2023년 11월 2일 ~ 2023년 11월8일, 7일간 우아한 테크코스 프리코스 3주차 과제가 진행되었다.
이미 많은 사람들이 과제가 끝난 직후 회고록을 작성했지만...
졸업 작품과 오픽 시험 준비를 하느라 너무 바쁜 나머지 이제서야 작성하게 되었다...ㅜ
하지만 괜찮다!! 과제에 몰입했던 그 기억은 그대로 있으니까...
2023년 우테코 웹 백엔기 6기의 3주차 문제는 "Lotto" 이다!!
(사실.. 2022년 우테코 웹 백엔기 5기 3주차도 "Lotto"였다!!)
Honestly, to tell you the truth... 테스트는 통과했지만 내부 구현 코드는 스파게티 그 자체다...ㅜㅜ
그때는 로또 문제가 어려운지조차 몰랐다... 테스트 성공만으로도 나에게 벅찼으니...
https://www.youtube.com/watch?v=gBDzloyZfS4
과제 시작하기 직전 우연히 우아한테크 유튜브에서 이 영상을 봤고 로또 문제가 엄~~청 어려웠다고 듣게 되었다...
그래서 더더욱 요구사항을 꼼꼼히 그리고 면밀히 살펴보았다!!
3주차 미션 소개 & 나의 로직 설명
3주차 미션 : https://github.com/JHyun0302/java-lotto-6
GitHub - JHyun0302/java-lotto-6: 우아한 테크코스 프리코스 3주차 과제 (2023.11.2~2023.11.8)
우아한 테크코스 프리코스 3주차 과제 (2023.11.2~2023.11.8). Contribute to JHyun0302/java-lotto-6 development by creating an account on GitHub.
github.com
로또 문제.. 요구사항도 많고 검증 로직도 많이 필요했고 SRP를 지키기도 힘들었다!!
하지만 테스트 통과!! 올해는 작년과 다르다!!
그리고 3주차는 2주차 코드 리뷰를 통해 배운 README 작성법과 MVC 패턴을 적극 활용했다.
README 작성법 : 사람마다 README를 작성하는 방법은 천차만별이다. 가장 잘 작성한 README는 사용자가 보고 쉽게 이해 할 수 있는 README 라고 생각했다. 그래서 README 작성 요령에 대해 공부했다.
MVC 패턴 : 요구사항도 많아지고 어떤 방법으로 구현해야 할지 막막했다. 그래서 '나'의 의도를 드러내기 쉬운 정형화된 MVC 패턴을 적용하였다.
로또 게임에서 내가 정의한 Model은 다음과 같다.
PurchasePrice : 로또 구입 금액
Lotto : 로또 번호
BonusNumber : 보너스 번호
WinnerNubmer : 당첨 번호
CompareResult : 비교 결과 (몇 개 일치하지? 보너스 번호는 일치하는가?)
RankMoney : 로또 상금 계산에 사용하는 Map<Rank, prize>
View는 다음과 같다.
InputView
- 구입 금액 입력 받기
- 당첨 번호 입력 받기
- 보너스 번호 입력 받기
OutputView
- 모든 출력문 관리
그리고 Controller와 Service를 분리하여 비즈니스 로직은 Service, 화면을 그리는 역할은 Controller!!
Service는 다음과 같다. (Controller는 각각의 Service를 그리는 역할!)
InputPurchasePriceService : 구입 금액 입력
InputNumberService : 로또 번호 입력
BuyLottoService : 로또 구매
CompareService: 로또 번호와 당첨 번호 비교
StatisticsService : 로또 통계 (일치률, 상금 계산)
그 외에도 검증(validation), dto(구매한 로또 리스트 & 당첨 번호, 보너스 번호), constant(정적 메시지, 상금 Rank)을 정의했다.
고민 & 해결
1. 검증 로직 분리
- 고민 : 3주 차 과제는 요구사항에 대한 검증 로직이 많았다. 처음에는 모든 검증 로직을 Validation 패키지에서 하려고 했으나 요구사항에 제공된 Lotto 객체를 보고 검증 로직의 위치에 대해 고민하게 되었다.
- 해결 : 객체의 특징과 직접적으로 관련된 중요한 검증일 경우 객체를 생성하면서 검증했고 요구사항과 간접적으로 연관된 검증의 경우 Validation 패키지에서 처리하였다. 예를 들면 구입 금액의 경우 "null or Empty 값" 또는 "정수가 아닌 값"인 경우 Validation에서 처리했고 "구입 금액은 1,000원 단위이어야 한다."와 같은 요구 사항은 객체 생성 시에 검증했다.
2. 검증 로직 위치
- 고민 : 'Controller'와 'InputView' 중에 어디에 검증 로직을 위치할지 고민했었다.
- 해결 : Controller는 모든 검증이 끝난 상태로 화면을 그리는 것에 집중해야 한다고 생각한다. 그래서 'InputView'를 통해 입력값을 받고 검증까지 끝낸 상태로 다른 메서드에게 넘겨주기로 하였다!
왜냐하면 'InputView'에서 입력과 동시에 에러를 발견해야 다른 메서드까지 에러를 전파하지 않는다고 생각했기 때문!!
3. 출력문 위치
- 고민 : 테스트 코드를 작성하다가 출력을 담당하는 'OutputView'의 위치에 대해 고민했다.
① 화면을 그리는 Controller에만 집중시킬 것인가? VS ② Service 로직에 분산시킬 것 인가?
- 해결 : 이 문제에는 트레이드 오프가 있다.
②의 경우, Service 로직이 출력문을 찍는 일까지 해야 하므로 책임 분리가 제대로 되지 않습니다.
하지만 당첨 번호와 보너스 번호를 입력받는 Service 로직에서 예외가 발생한 후 재입력을 시도할 때 OutputView의 출력문이 다시 한 번 더 출력되므로 사용자 관점에서는 좋아집니다.
Controller에만 집중할 경우 책임의 분리는 제대로 되지만 무작위 로또 번호를 출력할 때 로또 번호를 List에 담고 담은 List를 for 문을 통해 재출력해야 한다. 즉 출력을 위해 for 문을 추가해야 하는데 이는 성능 측면에서 매우 안 좋다고 판단했다.
※ 결론 : 비록 Service의 책임이 모호해지더라도 Service에서 출력하기로 하였다.
성장 포인트
1. README 작성법 : 2주 차 공통 피드백에서 "README 을 상세히 작성한다"라는 피드백을 보고 2주 차 코드 리뷰를 하면서 다른 사람들의 README 파일을 읽고 README 의 작성법을 배워 적용하였다.
2. 객체 생성 위치 : 초기에는 'InputView'에서 검증이 끝난 직후 객체를 생성하여 Service 로직으로 반환하게끔 코드를 작성했다. 하지만 'InputView'의 책임인 "입력 & 최소한의 검증"에서 객체의 생성까지 더해져 책임이 모호짐을 느꼈다. 그래서 'InputView'의 책임을 한정 짓기 위해 객체의 생성은 Service 로직이 담당하도록 수정하였다!
3. 당첨 통계 출력 : 당첨 통계 출력 시 5개가 일치한 경우 보너스 볼이 일치하는 경우와 일치하지 않는 경우로 분리해서 출력했다. 고민 끝에 hash code, equals를 이용해 두 경우를 나눠서 출력하였다. (이 부분이 특히 어려웠...)
4. 더 섬세한 검증 : 기능 요구사항을 읽던 중 문득 구매 금액은 1,000단위마다 ','가 찍혀있는데 수익률은 1,000단위마다 콤마를 찍으라는 요구사항은 없었다. 이를 계기로 요구사항을 읽을 때 더 집중해서 작성자의 숨겨진 의도를 찾아야겠다고 생각했다!
5. Test Case 작성 : 테스트 케이스를 작성한 덕분에 3개, 4개, 6개의 로또 번호가 일치했을 때 보너스 번호의 일치/불일치 때문에 기대한 결과값이 나오지 않는 버그를 발견했다. 테스트 케이스 작성... 매우 중요함!!
느낀점
로또 문제.. 말도 안되게 어려웠다... 과연 5시간 안으로 짤 수 있을까..? (최종 코딩테스트 : 5시간)
4주 차는 더 노력해야겠다!!
'2023-woowacourse-precourse' 카테고리의 다른 글
일급 컬렉션이요?? 그게 뭐죠? (0) | 2023.11.26 |
---|---|
Getter를 지양하라고요? (0) | 2023.11.26 |
2023 우아한 테크코스 4주차 회고 (0) | 2023.11.17 |
2023 우아한 테크코스 2주차 회고 (0) | 2023.11.17 |
2023 우아한 테크코스 1주차 회고 (2) | 2023.10.27 |