
3주차 회고 — 바쁨 속에서도 얻어간 성장의 흔적
3주차는 유난히 빠르게 지나갔다. 일정은 빡빡했고 집중 시간이 부족했지만, 돌아보면 여러 도전과 선택을 통해 확실히 성장한 한 주였다. 특히 기술적인 부분뿐 아니라, 학습 태도와 협업 방식 같은 소프트 스킬의 중요성을 뚜렷하게 체감할 수 있었다.
리뷰를 통해 깨달은 관점의 다양성
2주차 미션의 결과물을 꽤 만족스럽게 생각하고 있었지만, 코드 리뷰가 시작되자 예상보다 많은 피드백이 들어왔다. 내가 보지 못한 허점들이 드러났고, 스스로에 대한 평가가 과연 정확했는지 되돌아보게 됐다.
하지만 곧 관점을 바꾸게 됐다. 피드백은 ‘틀렸다’가 아니라 ‘다른 시각’이었다. 그래서 리뷰를 받을 때마다 내 의도와 리뷰어의 논리를 비교하고, 그 사이의 간극을 기록하며 이해하려고 노력했다. 이 과정이 실제로 문제 해결과 설계 사고에 큰 도움이 됐다.
비교가 아닌 관찰로 성장하기
리뷰 과정에서는 자연스럽게 잘하는 사람들의 코드도 접하게 된다. 처음엔 비교하며 조급함을 느꼈지만, 점차 관찰의 태도로 바뀌었다. 잘 설계된 책임 분리, 구조적 패턴, 일관된 추상화 방식 등을 분석하며 “저 사람처럼”이 아니라 “저 사람이 보는 방식”을 내 것으로 만드는 데 집중했다. 덕분에 학습 과정이 훨씬 즐거워졌다.
학습과 일상의 균형 찾기
이번 학기는 학교 일정도 가장 바쁜 시기라 시간적으로 여유가 많지 않았다. 그래서 무언가를 포기하기보다는 회복과 리듬을 유지하는 방향을 선택했다. 운동 시간에 죄책감을 느끼던 예전과 달리, 이제는 집중력과 사고력을 회복시키는 중요한 루틴으로 바라보고 있다. 체력과 정신적 에너지가 학습 효율에 직결된다는 걸 체감한 주였다.
함께 성장하는 경험
프리코스를 함께 하는 사람들과 주 1회 대면 스터디를 진행 중인데, 이 시간이 큰 힘이 됐다. 혼자 오래 끌 고민도 스터디에서 공유하면 빠르게 해소됐다. 코드뿐 아니라 학습 태도, 불안, 방향성 등 다양한 이야기가 오갔고, 그 덕분에 부담이 훨씬 가벼워졌다.
테스트 코드가 주는 안정감
이번 주가 테스트의 가치를 제대로 체감한 시점이었다. 테스트는 회귀 오류를 빠르게 잡아주고, 리팩터링 과정에서 강력한 안전망 역할을 했다. 도메인의 최소 단위에서 비즈니스 로직을 검증하기 때문에, 테스트가 깨지면 즉시 문제를 확인하고 수정할 수 있었다. 테스트가 없다면 기능 검증을 매번 처음부터 다시 해야 했을 것이다.
이번 주 설계에서 만족스러웠던 점
InputView와 OutputView의 책임을 명확하게 나누었다는 점이 가장 큰 수확이었다. 처음엔 View라는 이름이 어색했지만, MVC 패턴의 관점에서 역할을 정의하고 적용하니 구조가 훨씬 깔끔해졌다. 안내 메시지 처리 여부와 관계없이 책임이 분리되고, 전체 코드의 일관성이 높아졌다.
public class OutputView {
public void printPurchaseCount(int count) {
System.out.printf(InfoMessage.PURCHASED_COUNT.getMessage(), count);
}
public void printErrorMessage(IllegalArgumentException e) {
System.out.println(e.getMessage());
}
public void printIssuedTicket(List<IssuedLottoDto> issuedLottoDtos) {
issuedLottoDtos.forEach(dto -> System.out.println(dto.numbers()));
}
public void printStatics(WinningStaticsDto dto) {
System.out.println(InfoMessage.STATISTICS_HEADER.getMessage());
System.out.println(dto.message());
System.out.printf(InfoMessage.YIELD.getMessage(), dto.yield());
}
}
또한 Getter 사용을 지양하면서 “묻지 말고 시켜라”라는 객체지향 원칙을 체감했다. WinningLotto 내부로 비교 로직을 옮기며 응집도가 높아지고, 외부에서 객체 상태를 끌어다 쓰는 일이 줄었다. 출력 영역을 제외하면 Getter를 거의 사용하지 않게 되었고, 객체가 스스로 책임을 수행하도록 설계하는 방향을 몸으로 익혔다.
그리고 제공된 Lotto 클래스를 상속해 IssuedLotto와 WinningLotto를 정의했다. 로직 재사용과 검증을 자연스럽게 녹일 수 있었다. 다만 상속과 다형성의 적절한 활용에 대한 고민이 남아 있어 다음 주 더 공부할 예정이다.
public class WinningLotto extends Lotto {
public WinningLotto(List<Integer> winnings) {
super(winnings);
}
public boolean isWinningNumber(int number) {
return super.numbers().contains(number);
}
public void checkBonusNumberDuplicate(int bonusNumber) {
if (super.numbers().contains(bonusNumber)) {
throw new IllegalArgumentException(LottoErrorCode.DUPLICATE_WINNER.getMessage());
}
}
}
public class IssuedLotto extends Lotto {
public IssuedLotto(List<Integer> issued) {
super(issued);
verifyIsSorted(issued);
}
public int countMatching(WinningLotto winningLotto) {
return (int) super.numbers().stream()
.filter(winningLotto::isWinningNumber)
.count();
}
public List<Integer> getIssuedLotto() {
return List.copyOf(super.numbers());
}
private void verifyIsSorted(List<Integer> numbers) {
if (!isSorted(numbers)) {
throw new IllegalArgumentException(LottoErrorCode.INVALID_SORT.getMessage());
}
}
private boolean isSorted(List<Integer> numbers) {
return IntStream.range(0, numbers.size() - 1)
.allMatch(i -> numbers.get(i) <= numbers.get(i + 1));
}
}
이번 주의 고민 — 서비스 계층 도입
점점 도메인이 늘어나고 요구사항이 복잡해지면서 서비스 계층을 도입했다. 하지만 서비스는 상태를 가질 수 없기 때문에 설계의 제약이 컸다. 구매 금액 입력 → 로또 발행 → 당첨 비교의 흐름에서 발행된 로또를 어떻게 유지할까 고민했고 다음 두 가지 방안이 있었다.
- 도메인을 컨트롤러까지 전달해 상태 유지
- 상태가 필요한 객체마다 리포지토리 생성
첫 번째는 단순하지만 결합도가 높아졌고, 두 번째는 설계가 깔끔하지만 클래스 수와 복잡도가 늘어났다. 두 장점의 균형을 고려해 두 번째 방식을 선택했고 세 개의 리포지토리를 추가했다.
하지만 돌아보니 콘솔 기반 미션에서는 서비스 계층이 반드시 필요한가에 대한 의문이 남았다. 입력 → 객체 생성 → 활용이라는 단순한 흐름에서는 컨트롤러가 도메인과 직접 상호작용하는 쪽이 더 자연스러워 보인다. 그래서 다음 주에는 서비스를 제거하고 도메인 중심 설계를 다시 시도할 계획이다.
public class LottoService {
private final IssuedLottoRepository issuedLottoRepository;
private final BonusNumberRepository bonusNumberRepository;
private final WinningLottoRepository winningLottoRepository;
public LottoService(BonusNumberRepository bonusNumberRepository,
IssuedLottoRepository issuedLottoRepository,
WinningLottoRepository winningLottoRepository) {
this.bonusNumberRepository = bonusNumberRepository;
this.issuedLottoRepository = issuedLottoRepository;
this.winningLottoRepository = winningLottoRepository;
}
.
.
.
다음 주의 목표
4주차부터는 새 미션이 시작된다. 설계 방식이 바뀌더라도 객체 협력과 응집도 높은 구조는 계속 유지하고 싶다. 또한 이번 주에 적용해본 Runnable 기반 위임 구조가 매우 편리했기 때문에, 함수형 인터페이스를 더 적극적으로 활용해볼 생각이다. 입력 처리뿐 아니라 다양한 로직 위임에도 확장해볼 예정이다.
'외부활동 > 우아한테크코스 8기 프리코스' 카테고리의 다른 글
| 우아한테크코스 프리코스 4,5주차 회고 (0) | 2025.11.25 |
|---|---|
| 우아한테크코스 프리코스 8기 2주차 회고 (0) | 2025.11.01 |
| 우아한테크코스 프리코스 8기 1주차 회고 (0) | 2025.10.20 |
