-
2주차의 목표는 readMe 깔끔하게 작성하기, 주석 적게 사용하기, 일급 컬렉션 사용하기, MVC 패턴 적용하기였다.
-
악취가 났던 1주차와는 달리, 만족할만한 퀄리티가 나왔다. 새 집으로 이사한 느낌도 들었다.
-
3주차의 3번째 프로그래밍 요구 사항인 15라인 넘어가지 않기와 else 예약어를 쓰지 않기도 벌써 달성해냈다!
controller의
run()메서드는 15라인을 넘어버렸는데, 어떻게 해결해야 할지 고민해봐야겠다.🤔 -
이번엔 이븐하게 익었어요
- 아쉬운 점으로는 테스트를 적극적으로 활용하지 않았다는 점이었다.
- 테스트 도구를 적극 활용하기(다양한 테스트 메서드 활용)
- 단위 테스트만으로 모든 메서드에 대해 커버리지 100% 달성하기
- 단위 테스트 후 통합 테스트 작성하기
ReadMe에 주요 메서드의 기능을 정리하기
- 변수 이름에 자료형은 사용하지 않는다
변수명에 대한 고민을 조금 더 해보기..
- 테스트를 작성하는 이유에 대해 본인의 경험을 토대로 정리해본다
가장 난이도가 높은 문제라고 생각한다. '아! 이거 때문이지!' 하면서도 다시 테스트에 회의감이 들어버리는 상황이 발생한다..
- 메서드의 길이가 15라인을 넘어가지 않도록, 한 가지 일만 하도록 구현한다.
else,switch/case를 사용하지 않는다.Enum을 사용한다.- UI 로직을 제외한 구현 기능에 대해 단위 테스트를 작성한다.
- 랜덤으로 로또를 뽑고 당첨 내역 및 수익률을 확인할 수 있는 로또 발매기
- 사용자는 로또 구입 금액을 입력 받는다.
- 구입 금액은 1000원 단위 이다.
- 금액만큼 로또를 발행한다.
- 1장 당 1000원이다.
- 랜덤값을 뽑을 때는
pickUniqueNumbersInRange()를 활용한다.// 1에서 45 사이의 중복되지 않은 정수 6개 반환 Randoms.pickUniqueNumbersInRange(1, 45, 6); - 로또 번호는 오름차순 정렬한다.
- 당첨 번호 6개와 보너스 번호 1개를 뽑는다.
- 번호는 중복되지 않는다.
- 당첨 번호는 쉼표(,)를 기준으로 구분한다.
- 로또 번호와 당첨 번호를 비교한다.
- 당첨 내역 및 수익률을 출력한다.
- 수익률은 소수점 둘째 자리에서 반올림한다.
당첨 기준과 금액 1등: 6개 번호 일치 / 2,000,000,000원 2등: 5개 번호 + 보너스 번호 일치 / 30,000,000원 3등: 5개 번호 일치 / 1,500,000원 4등: 4개 번호 일치 / 50,000원 5등: 3개 번호 일치 / 5,000원 - 예외 상황 시 에러 문구를 출력해야 한다.
- 에러 문구는
[ERROR]로 시작해야 한다. - 잘못된 값을 입력한 경우
IllegalArgumentException을 발생시킨다. - 명확한 에러 유형을 처리한다.
- 에러 문구는
- 사용자는 로또 구입 금액을 입력 받는다.
- 로또 구입 금액을 입력 받는다.
- 1000원으로 나누어 떨어지지 않는지 검증한다.
- 처리 가능 범위를 넘어서는 금액인 경우인지 검증한다.
- 잘못된 값을 입력한 경우 에러 메시지를 출력하고 재입력 받는다.
- 입력받은 로또 구입 금액을 저장한다.
- 당첨 번호를 입력 받는다.
- 쉼표, 숫자 외의 문자가 있는지 검증한다.
- 쉼표가 잘못 위치한 경우를 검증한다.
- 보너스 번호를 입력 받는다.
- 숫자가 1~45에 속하는지 검증한다.
- 입력받은 당첨 번호를 저장한다.
- 입력받은 보너스 번호를 저장한다.
사용자 입력은
camp.nextstep.edu.missionutils.Console의readLine()을 활용한다.
- 로또 구입 금액으로부터 구입 가능한 로또 개수를 구한다.
- 로또를 발행한다.
랜덤값을 뽑을 때는
pickUniqueNumbersInRange()를 활용한다. - 발행한 로또 정보를 출력한다.
- 발행한 로또들의 당첨 여부를 확인한다.
- 당첨 통계를 출력한다.
- 총 수익률을 출력한다.
- 명확한 유형을 지정하여 각 예외를 처리한다.
- 입력값으로 받은 구매금액을 처리하는 클래스입니다.
| 메서드 명 | 설명 |
|---|---|
| save | 로또 구매금액 입력값을 받아 검증, 파싱, 저장을 수행합니다. |
- 입력값으로 받은 당첨번호를 처리하는 클래스입니다.
| 메서드 명 | 설명 |
|---|---|
| save | 당첨번호 입력값을 받아 검증, 파싱, 저장을 수행합니다. |
- 입력값으로 받은 보너스번호를 처리하는 클래스입니다.
| 메서드 명 | 설명 |
|---|---|
| save | 보너스번호 입력값을 받아 검증, 파싱, 저장을 수행합니다. |
- 로또 발매기의 동작을 제어하는 객체로서, 입력 금액으로 로또를 발행하고, 당첨 여부를 조회하고, 결과를 전달합니다.
| 메서드 명 | 설명 |
|---|---|
| buy | 저장된 구매금액만큼 로또를 발행합니다. |
| getPurchaseCount | 구매금액으로 발행 가능한 로또 개수를 반환합니다. |
| issue | 로또 개수만큼 발행하고 로또 발매기에 저장합니다. |
| generate | 랜덤한 번호로 로또를 발행합니다. |
| check | 저장된 당첨번호, 보너스 번호, 발행된 로또를 조회하여 당첨 여부를 확인합니다. |
| compare | 당첨번호 개수, 보너스 번호 맞춤 여부를 확인하여 그에 맞는 상품 통계를 저장합니다. |
| savePrize | 당첨번호 개수, 보너스 번호 맞춤 여부에 맞는 상품을 조회하여 통계에 저장합니다. |
| getStatistic | 통계 정보를 조회합니다. |
| appendResult | 각 상품에 대한 통계 정보를 조회합니다. |
| getProfitRate | 수익률 정보를 조회합니다. |
| computeProfitRate | 수익률을 계산합니다. |
- 현실의 발매기와 대응되는 객체로서, 발매에 필요한 데이터들을 저장하고 조회합니다.
| 메서드 명 | 설명 |
|---|---|
| insertPurchaseAmount | 구매금액을 저장합니다.(돈을 투입합니다.) |
| settingWinnerNumber | 당첨번호를 저장합니다.(새로운 회차의 당첨번호를 뽑습니다.) |
| settingBonusNumber | 보너스번호를 저장합니다.(새로운 회차의 보너스번호를 뽑습니다.) |
| settingIssuedLotto | 발행된 로또를 저장합니다.(사용자의 로또 발행 번호를 저장합니다.) |
| consumePurchaseAmount | 구매금액을 조회하면서 0원으로 초기화합니다. |
- 발매기의 책임을 분리하여 통계 정보를 저장하는 객체입니다. 통계 데이터를 저장하고 조회합니다.
| 메서드 명 | 설명 |
|---|---|
| add | 인자로 받은 상품에 대해 개수를 1 증가시킵니다. |
- 이번 과제 목표 달성 여부
- 테스트 도구를 적극 활용하기(다양한 테스트 메서드 활용)
- ParameterizedTest를 익히고 완벽하게 활용해냈습니다.
- NsTest 메서드도 적극 활용하여 통합 테스트의 여러 케이스를 검증했습니다.
- 단위 테스트만으로 모든 메서드에 대해 커버리지 100% 달성하기
- 출력, 검증, 컨트롤러, 상수를 제외한 모든 메서드에 대해 커버리지를 100% 달성했습니다.
- 통합 테스트를 통해 상수를 제외한 모든 메서드에 대해서도 커버리지를 100% 달성했습니다.
-
ReadMe에 주요 메서드의 기능을 정리하기- 주요 메서드에 대한 클래스 정의서를 작성했습니다.
- 여러 컨트롤러들을 만들면서 동일한 모듈을 사용하기 위해 외부에서 주입이 필요했고, 이를 위해 의존성 주입 컨테이너
ObjectFactory를 만들었습니다. - 덕분에 클라이언트는 컨트롤러들의 세부 모듈을 알 필요 없이 호출할 수 있게 되었습니다.
- 여러 엔티티로 나누면서 중복되는 검증 메서드들이 생겨났습니다. 중복을 제거하기 위해 여러 방법을 고민했지만, 다음 이유들로 인해 적용하지 않았습니다.
- 상속 : 결합도가 강해져 확장이 어려워집니다.
- 유틸 클래스 : 엔티티의 책임을 외부 모듈로 분리하지 않고, 한 곳에 몰아넣어 응집도를 높이고 싶었습니다.
- 기존 테스트를 작성했을 때에는 다중 조건의 커버리지를 위해 비슷한 테스트 케이스가 여러 개 생겨났습니다.
- 하지만 @ParameterizedTest 를 학습하고 적용하면서, 중복되는 케이스들이 제거되었고 가독성이 향상되었습니다.
- TDD는 가성비 측면에서 적합하지 않다고 생각하여 적용하지 않기로 했습니다.
- 실제 프로젝트에서 시간은 넉넉하지 않고, 기획은 계속 변경되어 로직의 의도 자체가 바뀌는 경우가 잦았습니다.
- 프리코스에서도 마찬가지로 요구사항을 한 번에 이해하기 힘들고, 중간에 깨우쳐 로직이 바뀌는 부분이 존재하기 때문에 TDD는 가성비가 좋지 않다고 판단하였습니다.
