From 82460f608447a32b81a6799df3531569f0a4e8db Mon Sep 17 00:00:00 2001 From: hjkim0905 Date: Sat, 10 Jan 2026 13:31:45 +0900 Subject: [PATCH 01/13] docs: add feature implementation list --- README.md | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/README.md b/README.md index b168a180..4295cca3 100644 --- a/README.md +++ b/README.md @@ -1 +1,52 @@ # javascript-planetlotto-precourse + +## 최종 테스트 - <🌏 행성 로또 🎱> 기능 구현 목록 + +### 입력 처리 + +- [ ] 사용자로부터 로또 구입 금액 입력받기 (`askAmount()` 사용) + - [ ] return -> number +- [ ] 사용자로부터 당첨 번호 입력받기 (`askWinningLotto()` 사용) + - [ ] return -> number[] + - [ ] Lotto class 만들어서 사용 (멤버 => numbers) +- [ ] 사용자로부터 보너스 번호 입력받기 (`askBonusNumber()` 사용) + - [ ] return -> number + +### 결과 계산 + +- [ ] 입력 받은 로또 구입 금액 / 500 계산하여 로또 수량 도출 + - [ ] 각 발행한 로또에 중복되지 않은 5개의 정수(1부터 30 사이) 부여 (`MissionUtils.Random.pickUniqueNumbersInRange(1, 30, 5)` 사용) + - [ ] Lotto class instance 생성 + - [ ] 로또 번호 오름차순 정렬 +- [ ] 입력 받은 당첨 번호와 발행된 로또의 6개 정수를 비교 + - [ ] 5개 모두 일치하지 않고 4개만 일치했을 경우 발행된 로또의 4개 정수와 보너스 번호를 비교 + - [ ] 4개 모두 일치하지 않고 3개만 일치했을 경우 발행된 로또의 3개 정수와 보너스 번호를 비교 + - [ ] 3개 모두 일치하지 않고 3개만 일치했을 경우 발행된 로또의 2개 정수와 보너스 번호를 비교 +- [ ] 당첨 내역 계산 + - [ ] 1등부터 6등 각각의 개수 + +### 예외 처리 + +- [ ] Error 발생 시 `process.exit()`을 호출하지 않고 `[ERROR]` 메시지와 함께 Error throw +- [ ] 구입 금액 입력 + - [ ] 빈 문자열 입력 시 + - [ ] 숫자가 아닌 값 입력 시 + - [ ] 500 미만 값 입력 시 + - [ ] 500으로 나눴을 때 정수형 숫자로 나눠떨어지지 않는 경우 +- [ ] 당첨 번호 입력 + - [ ] 1부터 30 사이 외의 로또 번호 입력 시 + - [ ] 정수가 아닌 값 입력 시 + - [ ] 5개 숫자가 아닌 갯수 입력 시 (빈 문자열도 같이 검증 역할) + - [ ] 중복된 번호 포함 시 +- [ ] 보너스 번호 입력 + - [ ] 1부터 30 사이 외의 로또 번호 입력 시 + - [ ] 빈 문자열 입력 시 + - [ ] 정수가 아닌 값 입력 시 + - [ ] 당첨 번호와 중복 시 + +### 출력 + +- [ ] 발행한 로또 수량 출력 (`printPurchasedLottos(lottos)` 사용) + - [ ] lottos -> `number[][]` e.g. numbers[[1,2,3,4,5],[6,7,8,9,10]] +- [ ] 당첨 내역 출력 (`printResult(countsByRank)` 사용) + - [ ] countsByRank -> `Map` e.g. [ 1, firstCount ], [ 2, secondCount ] From 74ec11fcf4889a572eff1d8a19682d3a5c992f8e Mon Sep 17 00:00:00 2001 From: hjkim0905 Date: Sat, 10 Jan 2026 14:09:44 +0900 Subject: [PATCH 02/13] feat: get lotto purchase amount & lotto quantity with validation --- README.md | 26 +++++++++++++------------- src/App.js | 7 ++++++- src/controller/LottoController.js | 25 +++++++++++++++++++++++++ src/model/Lotto.js | 29 +++++++++++++++++++++++++++++ src/model/LottoFactory.js | 3 +++ src/model/LottoStatistics.js | 3 +++ src/model/RankFinder.js | 3 +++ src/utils/lottoUtils.js | 3 +++ src/utils/validator.js | 9 +++++++++ src/{ => view}/view.js | 0 10 files changed, 94 insertions(+), 14 deletions(-) create mode 100644 src/controller/LottoController.js create mode 100644 src/model/Lotto.js create mode 100644 src/model/LottoFactory.js create mode 100644 src/model/LottoStatistics.js create mode 100644 src/model/RankFinder.js create mode 100644 src/utils/lottoUtils.js create mode 100644 src/utils/validator.js rename src/{ => view}/view.js (100%) diff --git a/README.md b/README.md index 4295cca3..73863678 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,8 @@ ### 입력 처리 -- [ ] 사용자로부터 로또 구입 금액 입력받기 (`askAmount()` 사용) - - [ ] return -> number +- [x] 사용자로부터 로또 구입 금액 입력받기 (`askAmount()` 사용) + - [x] return -> number - [ ] 사용자로부터 당첨 번호 입력받기 (`askWinningLotto()` 사용) - [ ] return -> number[] - [ ] Lotto class 만들어서 사용 (멤버 => numbers) @@ -14,7 +14,7 @@ ### 결과 계산 -- [ ] 입력 받은 로또 구입 금액 / 500 계산하여 로또 수량 도출 +- [x] 입력 받은 로또 구입 금액 / 500 계산하여 로또 수량 도출 - [ ] 각 발행한 로또에 중복되지 않은 5개의 정수(1부터 30 사이) 부여 (`MissionUtils.Random.pickUniqueNumbersInRange(1, 30, 5)` 사용) - [ ] Lotto class instance 생성 - [ ] 로또 번호 오름차순 정렬 @@ -29,19 +29,19 @@ - [ ] Error 발생 시 `process.exit()`을 호출하지 않고 `[ERROR]` 메시지와 함께 Error throw - [ ] 구입 금액 입력 - - [ ] 빈 문자열 입력 시 - - [ ] 숫자가 아닌 값 입력 시 - - [ ] 500 미만 값 입력 시 - - [ ] 500으로 나눴을 때 정수형 숫자로 나눠떨어지지 않는 경우 + - [x] 빈 문자열 입력 시 + - [x] 숫자가 아닌 값 입력 시 + - [x] 500 미만 값 입력 시 + - [x] 500으로 나눴을 때 정수형 숫자로 나눠떨어지지 않는 경우 - [ ] 당첨 번호 입력 - - [ ] 1부터 30 사이 외의 로또 번호 입력 시 - - [ ] 정수가 아닌 값 입력 시 - - [ ] 5개 숫자가 아닌 갯수 입력 시 (빈 문자열도 같이 검증 역할) - - [ ] 중복된 번호 포함 시 + - [x] 1부터 30 사이 외의 로또 번호 입력 시 + - [x] 정수가 아닌 값 입력 시 + - [x] 5개 숫자가 아닌 갯수 입력 시 (빈 문자열도 같이 검증 역할) + - [x] 중복된 번호 포함 시 - [ ] 보너스 번호 입력 - [ ] 1부터 30 사이 외의 로또 번호 입력 시 - - [ ] 빈 문자열 입력 시 - - [ ] 정수가 아닌 값 입력 시 + - [x] 빈 문자열 입력 시 + - [x] 정수가 아닌 값 입력 시 - [ ] 당첨 번호와 중복 시 ### 출력 diff --git a/src/App.js b/src/App.js index 091aa0a5..24cc8f10 100644 --- a/src/App.js +++ b/src/App.js @@ -1,5 +1,10 @@ +import LottoController from './controller/LottoController.js'; + class App { - async run() {} + async run() { + const lottoController = new LottoController(); + await lottoController.run(); + } } export default App; diff --git a/src/controller/LottoController.js b/src/controller/LottoController.js new file mode 100644 index 00000000..df117141 --- /dev/null +++ b/src/controller/LottoController.js @@ -0,0 +1,25 @@ +import { InputView, OutputView } from '../view/view.js'; +import { validatePurchaseAmount } from '../utils/validator.js'; +import { getLottoQuantity } from '../utils/lottoUtils.js'; +import { Console } from '@woowacourse/mission-utils'; + +class LottoController { + async run() { + let inputErrorFlag = true; + let lottoPurchaseAmount = 0; + while (inputErrorFlag) { + try { + lottoPurchaseAmount = await InputView.askAmount(); + validatePurchaseAmount(lottoPurchaseAmount); + inputErrorFlag = false; + } catch (error) { + Console.print(error.message); + } + } + inputErrorFlag = true; + + const lottoQuantity = getLottoQuantity(lottoPurchaseAmount); + } +} + +export default LottoController; diff --git a/src/model/Lotto.js b/src/model/Lotto.js new file mode 100644 index 00000000..210e2197 --- /dev/null +++ b/src/model/Lotto.js @@ -0,0 +1,29 @@ +class Lotto { + #numbers; + + constructor(numbers) { + this.#validate(numbers); + this.#numbers = numbers; + } + + #validate(numbers) { + if (numbers.length !== 5) { + throw new Error('[ERROR] 로또 번호는 5개여야 합니다.'); + } + + const uniqueArr = new Set(numbers); + if (numbers.length !== uniqueArr.size) { + throw new Error('[ERROR] 로또 번호에 중복된 숫자가 있습니다.'); + } + + // if (!numbers.every((number) => Number.isInteger(number))) { + // throw new Error('[ERROR] 로또 번호에 정수가 아닌 값이 있습니다.'); + // } + + if (!numbers.every((number) => number <= 30 && number >= 1)) { + throw new Error('[ERROR] 로또 번호에 1부터 30 사이가 아닌 값이 있습니다.'); + } + } +} + +export default Lotto; diff --git a/src/model/LottoFactory.js b/src/model/LottoFactory.js new file mode 100644 index 00000000..803e400e --- /dev/null +++ b/src/model/LottoFactory.js @@ -0,0 +1,3 @@ +class LottoFactory {} + +export default LottoFactory; diff --git a/src/model/LottoStatistics.js b/src/model/LottoStatistics.js new file mode 100644 index 00000000..ad6feb55 --- /dev/null +++ b/src/model/LottoStatistics.js @@ -0,0 +1,3 @@ +class LottoStatistics {} + +export default LottoStatistics; diff --git a/src/model/RankFinder.js b/src/model/RankFinder.js new file mode 100644 index 00000000..edabdefe --- /dev/null +++ b/src/model/RankFinder.js @@ -0,0 +1,3 @@ +class RankFinder {} + +export default RankFinder; diff --git a/src/utils/lottoUtils.js b/src/utils/lottoUtils.js new file mode 100644 index 00000000..c1af09c6 --- /dev/null +++ b/src/utils/lottoUtils.js @@ -0,0 +1,3 @@ +export function getLottoQuantity(lottoPurchaseAmount) { + return lottoPurchaseAmount / 500; +} diff --git a/src/utils/validator.js b/src/utils/validator.js new file mode 100644 index 00000000..b8116730 --- /dev/null +++ b/src/utils/validator.js @@ -0,0 +1,9 @@ +export function validatePurchaseAmount(lottoPurchaseAmount) { + if (lottoPurchaseAmount < 500) { + throw new Error('[ERROR] 500 미만 값이 입력되었습니다.'); + } + + if (!Number.isInteger(lottoPurchaseAmount / 500)) { + throw new Error('[ERROR] 입력값이 500으로 나눴을 때 정수형 숫자로 나눠떨어지지 않습니다.'); + } +} diff --git a/src/view.js b/src/view/view.js similarity index 100% rename from src/view.js rename to src/view/view.js From 295091ce8d8327cd9558d6cb3e47a3f58f31895e Mon Sep 17 00:00:00 2001 From: hjkim0905 Date: Sat, 10 Jan 2026 14:35:24 +0900 Subject: [PATCH 03/13] feat: create lottos according to the purchased quantity and print it using OutputView --- README.md | 13 +++++++------ src/controller/LottoController.js | 11 ++++++++++- src/model/Lotto.js | 14 +++++++------- src/model/LottoFactory.js | 13 ++++++++++++- src/utils/validator.js | 4 ++-- src/view/view.js | 4 ++-- 6 files changed, 40 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 73863678..1ec89100 100644 --- a/README.md +++ b/README.md @@ -15,9 +15,9 @@ ### 결과 계산 - [x] 입력 받은 로또 구입 금액 / 500 계산하여 로또 수량 도출 - - [ ] 각 발행한 로또에 중복되지 않은 5개의 정수(1부터 30 사이) 부여 (`MissionUtils.Random.pickUniqueNumbersInRange(1, 30, 5)` 사용) - - [ ] Lotto class instance 생성 - - [ ] 로또 번호 오름차순 정렬 + - [x] 각 발행한 로또에 중복되지 않은 5개의 정수(1부터 30 사이) 부여 (`MissionUtils.Random.pickUniqueNumbersInRange(1, 30, 5)` 사용) + - [x] Lotto class instance 생성 + - [x] 로또 번호 오름차순 정렬 - [ ] 입력 받은 당첨 번호와 발행된 로또의 6개 정수를 비교 - [ ] 5개 모두 일치하지 않고 4개만 일치했을 경우 발행된 로또의 4개 정수와 보너스 번호를 비교 - [ ] 4개 모두 일치하지 않고 3개만 일치했을 경우 발행된 로또의 3개 정수와 보너스 번호를 비교 @@ -28,7 +28,8 @@ ### 예외 처리 - [ ] Error 발생 시 `process.exit()`을 호출하지 않고 `[ERROR]` 메시지와 함께 Error throw -- [ ] 구입 금액 입력 + - [ ] 'OutputView의 `printErrorMessage(message)` 사용' +- [x] 구입 금액 입력 - [x] 빈 문자열 입력 시 - [x] 숫자가 아닌 값 입력 시 - [x] 500 미만 값 입력 시 @@ -46,7 +47,7 @@ ### 출력 -- [ ] 발행한 로또 수량 출력 (`printPurchasedLottos(lottos)` 사용) - - [ ] lottos -> `number[][]` e.g. numbers[[1,2,3,4,5],[6,7,8,9,10]] +- [x] 발행한 로또 수량 출력 (`printPurchasedLottos(lottos)` 사용) + - [x] lottos -> `number[][]` e.g. numbers[[1,2,3,4,5],[6,7,8,9,10]] - [ ] 당첨 내역 출력 (`printResult(countsByRank)` 사용) - [ ] countsByRank -> `Map` e.g. [ 1, firstCount ], [ 2, secondCount ] diff --git a/src/controller/LottoController.js b/src/controller/LottoController.js index df117141..df8864de 100644 --- a/src/controller/LottoController.js +++ b/src/controller/LottoController.js @@ -1,10 +1,12 @@ import { InputView, OutputView } from '../view/view.js'; import { validatePurchaseAmount } from '../utils/validator.js'; import { getLottoQuantity } from '../utils/lottoUtils.js'; +import LottoFactory from '../model/LottoFactory.js'; import { Console } from '@woowacourse/mission-utils'; class LottoController { async run() { + // 로또 금액 입력 받기 let inputErrorFlag = true; let lottoPurchaseAmount = 0; while (inputErrorFlag) { @@ -13,12 +15,19 @@ class LottoController { validatePurchaseAmount(lottoPurchaseAmount); inputErrorFlag = false; } catch (error) { - Console.print(error.message); + OutputView.printErrorMessage(error.message); } } inputErrorFlag = true; + // 로또 갯수 도출하기 const lottoQuantity = getLottoQuantity(lottoPurchaseAmount); + + // 로또 배열 생성하기(팩토리 패턴) + const lottos = LottoFactory.createLotto(lottoQuantity); + + Console.print(''); + OutputView.printPurchasedLottos(lottos); } } diff --git a/src/model/Lotto.js b/src/model/Lotto.js index 210e2197..609d313e 100644 --- a/src/model/Lotto.js +++ b/src/model/Lotto.js @@ -8,22 +8,22 @@ class Lotto { #validate(numbers) { if (numbers.length !== 5) { - throw new Error('[ERROR] 로또 번호는 5개여야 합니다.'); + throw new Error('로또 번호는 5개여야 합니다.'); } const uniqueArr = new Set(numbers); if (numbers.length !== uniqueArr.size) { - throw new Error('[ERROR] 로또 번호에 중복된 숫자가 있습니다.'); + throw new Error('로또 번호에 중복된 숫자가 있습니다.'); } - // if (!numbers.every((number) => Number.isInteger(number))) { - // throw new Error('[ERROR] 로또 번호에 정수가 아닌 값이 있습니다.'); - // } - if (!numbers.every((number) => number <= 30 && number >= 1)) { - throw new Error('[ERROR] 로또 번호에 1부터 30 사이가 아닌 값이 있습니다.'); + throw new Error('로또 번호에 1부터 30 사이가 아닌 값이 있습니다.'); } } + + getNumbers() { + return this.#numbers; + } } export default Lotto; diff --git a/src/model/LottoFactory.js b/src/model/LottoFactory.js index 803e400e..7a0a62f7 100644 --- a/src/model/LottoFactory.js +++ b/src/model/LottoFactory.js @@ -1,3 +1,14 @@ -class LottoFactory {} +import { Random } from '@woowacourse/mission-utils'; + +class LottoFactory { + static createLotto(lottoQuantity) { + const lottoArray = Array.from({ length: lottoQuantity }, () => { + const numbers = Random.pickUniqueNumbersInRange(1, 30, 5); + return numbers.sort((a, b) => a - b); + }); + + return lottoArray; + } +} export default LottoFactory; diff --git a/src/utils/validator.js b/src/utils/validator.js index b8116730..1f54a59b 100644 --- a/src/utils/validator.js +++ b/src/utils/validator.js @@ -1,9 +1,9 @@ export function validatePurchaseAmount(lottoPurchaseAmount) { if (lottoPurchaseAmount < 500) { - throw new Error('[ERROR] 500 미만 값이 입력되었습니다.'); + throw new Error('500 미만 값이 입력되었습니다.'); } if (!Number.isInteger(lottoPurchaseAmount / 500)) { - throw new Error('[ERROR] 입력값이 500으로 나눴을 때 정수형 숫자로 나눠떨어지지 않습니다.'); + throw new Error('입력값이 500으로 나눴을 때 정수형 숫자로 나눠떨어지지 않습니다.'); } } diff --git a/src/view/view.js b/src/view/view.js index ae6afd9c..5989899a 100644 --- a/src/view/view.js +++ b/src/view/view.js @@ -1,4 +1,4 @@ -import { MissionUtils } from "@woowacourse/mission-utils"; +import { MissionUtils } from '@woowacourse/mission-utils'; const InputView = { /** @@ -51,7 +51,7 @@ const OutputView = { printPurchasedLottos(lottos) { const lines = [ `${lottos.length}개를 구매했습니다.`, - ...lottos.map(lotto => `[${lotto.join(', ')}]`), + ...lottos.map((lotto) => `[${lotto.join(', ')}]`), ]; MissionUtils.Console.print(lines.join('\n')); }, From d60c3d0c251163365364910e570a11c21c9446a1 Mon Sep 17 00:00:00 2001 From: hjkim0905 Date: Sat, 10 Jan 2026 14:47:39 +0900 Subject: [PATCH 04/13] feat: get winningNumber input with validation --- README.md | 8 ++++---- src/controller/LottoController.js | 20 +++++++++++++++++++- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 1ec89100..326cc694 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,9 @@ - [x] 사용자로부터 로또 구입 금액 입력받기 (`askAmount()` 사용) - [x] return -> number -- [ ] 사용자로부터 당첨 번호 입력받기 (`askWinningLotto()` 사용) - - [ ] return -> number[] - - [ ] Lotto class 만들어서 사용 (멤버 => numbers) +- [x] 사용자로부터 당첨 번호 입력받기 (`askWinningLotto()` 사용) + - [x] return -> number[] + - [x] Lotto class 만들어서 사용 (멤버 => numbers) - [ ] 사용자로부터 보너스 번호 입력받기 (`askBonusNumber()` 사용) - [ ] return -> number @@ -34,7 +34,7 @@ - [x] 숫자가 아닌 값 입력 시 - [x] 500 미만 값 입력 시 - [x] 500으로 나눴을 때 정수형 숫자로 나눠떨어지지 않는 경우 -- [ ] 당첨 번호 입력 +- [x] 당첨 번호 입력 - [x] 1부터 30 사이 외의 로또 번호 입력 시 - [x] 정수가 아닌 값 입력 시 - [x] 5개 숫자가 아닌 갯수 입력 시 (빈 문자열도 같이 검증 역할) diff --git a/src/controller/LottoController.js b/src/controller/LottoController.js index df8864de..868a2749 100644 --- a/src/controller/LottoController.js +++ b/src/controller/LottoController.js @@ -2,6 +2,7 @@ import { InputView, OutputView } from '../view/view.js'; import { validatePurchaseAmount } from '../utils/validator.js'; import { getLottoQuantity } from '../utils/lottoUtils.js'; import LottoFactory from '../model/LottoFactory.js'; +import Lotto from '../model/Lotto.js'; import { Console } from '@woowacourse/mission-utils'; class LottoController { @@ -18,7 +19,6 @@ class LottoController { OutputView.printErrorMessage(error.message); } } - inputErrorFlag = true; // 로또 갯수 도출하기 const lottoQuantity = getLottoQuantity(lottoPurchaseAmount); @@ -26,8 +26,26 @@ class LottoController { // 로또 배열 생성하기(팩토리 패턴) const lottos = LottoFactory.createLotto(lottoQuantity); + // 구매한 로또 출력 Console.print(''); OutputView.printPurchasedLottos(lottos); + Console.print(''); + + // 당첨 번호 입력 + inputErrorFlag = true; + const winningLotto = []; + while (inputErrorFlag) { + try { + const winningNumbers = await InputView.askWinningLotto(); + + winningLotto.push(new Lotto(winningNumbers.sort((a, b) => a - b))); + inputErrorFlag = false; + } catch (error) { + OutputView.printErrorMessage(error.message); + } + } + + Console.print(winningLotto); } } From 27342b1bf62f051f925f55d12a3953fbbcc8f7c9 Mon Sep 17 00:00:00 2001 From: hjkim0905 Date: Sat, 10 Jan 2026 14:57:06 +0900 Subject: [PATCH 05/13] feat: get bonus number input with validation --- README.md | 14 +++++++------- src/controller/LottoController.js | 17 +++++++++++++++-- src/model/Lotto.js | 4 ++++ src/utils/validator.js | 10 ++++++++++ 4 files changed, 36 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 326cc694..644da1a8 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,8 @@ - [x] 사용자로부터 당첨 번호 입력받기 (`askWinningLotto()` 사용) - [x] return -> number[] - [x] Lotto class 만들어서 사용 (멤버 => numbers) -- [ ] 사용자로부터 보너스 번호 입력받기 (`askBonusNumber()` 사용) - - [ ] return -> number +- [x] 사용자로부터 보너스 번호 입력받기 (`askBonusNumber()` 사용) + - [x] return -> number ### 결과 계산 @@ -27,8 +27,8 @@ ### 예외 처리 -- [ ] Error 발생 시 `process.exit()`을 호출하지 않고 `[ERROR]` 메시지와 함께 Error throw - - [ ] 'OutputView의 `printErrorMessage(message)` 사용' +- [x] Error 발생 시 `process.exit()`을 호출하지 않고 `[ERROR]` 메시지와 함께 Error throw + - [x] 'OutputView의 `printErrorMessage(message)` 사용' - [x] 구입 금액 입력 - [x] 빈 문자열 입력 시 - [x] 숫자가 아닌 값 입력 시 @@ -39,11 +39,11 @@ - [x] 정수가 아닌 값 입력 시 - [x] 5개 숫자가 아닌 갯수 입력 시 (빈 문자열도 같이 검증 역할) - [x] 중복된 번호 포함 시 -- [ ] 보너스 번호 입력 - - [ ] 1부터 30 사이 외의 로또 번호 입력 시 +- [x] 보너스 번호 입력 + - [x] 1부터 30 사이 외의 로또 번호 입력 시 - [x] 빈 문자열 입력 시 - [x] 정수가 아닌 값 입력 시 - - [ ] 당첨 번호와 중복 시 + - [x] 당첨 번호와 중복 시 ### 출력 diff --git a/src/controller/LottoController.js b/src/controller/LottoController.js index 868a2749..68171ddf 100644 --- a/src/controller/LottoController.js +++ b/src/controller/LottoController.js @@ -1,5 +1,5 @@ import { InputView, OutputView } from '../view/view.js'; -import { validatePurchaseAmount } from '../utils/validator.js'; +import { validatePurchaseAmount, validateBonusNumber } from '../utils/validator.js'; import { getLottoQuantity } from '../utils/lottoUtils.js'; import LottoFactory from '../model/LottoFactory.js'; import Lotto from '../model/Lotto.js'; @@ -45,7 +45,20 @@ class LottoController { } } - Console.print(winningLotto); + // 보너스 번호 입력 + inputErrorFlag = true; + let bonusNumber = 0; + + while (inputErrorFlag) { + try { + bonusNumber = await InputView.askBonusNumber(); + validateBonusNumber(bonusNumber, winningLotto[0]); + + inputErrorFlag = false; + } catch (error) { + OutputView.printErrorMessage(error.message); + } + } } } diff --git a/src/model/Lotto.js b/src/model/Lotto.js index 609d313e..3106741f 100644 --- a/src/model/Lotto.js +++ b/src/model/Lotto.js @@ -24,6 +24,10 @@ class Lotto { getNumbers() { return this.#numbers; } + + hasBonusNumber(bonusNumber) { + return this.#numbers.includes(bonusNumber); + } } export default Lotto; diff --git a/src/utils/validator.js b/src/utils/validator.js index 1f54a59b..3651baf4 100644 --- a/src/utils/validator.js +++ b/src/utils/validator.js @@ -7,3 +7,13 @@ export function validatePurchaseAmount(lottoPurchaseAmount) { throw new Error('입력값이 500으로 나눴을 때 정수형 숫자로 나눠떨어지지 않습니다.'); } } + +export function validateBonusNumber(bonusNumber, winningLotto) { + if (!(bonusNumber <= 30 && bonusNumber >= 1)) { + throw new Error('보너스 번호에 1부터 30 사이가 아닌 값이 있습니다.'); + } + + if (winningLotto.hasBonusNumber(bonusNumber)) { + throw new Error('보너스 번호가 당첨 번호와 중복됩니다.'); + } +} From d2e753e4439a7334f62cd8fa4098754451371c74 Mon Sep 17 00:00:00 2001 From: hjkim0905 Date: Sat, 10 Jan 2026 15:37:03 +0900 Subject: [PATCH 06/13] feat: calculate ranks by using Lotto class methods --- README.md | 12 +++++------ src/constants/ranks.js | 7 +++++++ src/controller/LottoController.js | 15 ++++++++++++++ src/model/Lotto.js | 9 ++++++++ src/model/LottoStatistics.js | 31 +++++++++++++++++++++++++++- src/model/RankFinder.js | 34 ++++++++++++++++++++++++++++++- 6 files changed, 100 insertions(+), 8 deletions(-) create mode 100644 src/constants/ranks.js diff --git a/README.md b/README.md index 644da1a8..9bb25504 100644 --- a/README.md +++ b/README.md @@ -18,12 +18,12 @@ - [x] 각 발행한 로또에 중복되지 않은 5개의 정수(1부터 30 사이) 부여 (`MissionUtils.Random.pickUniqueNumbersInRange(1, 30, 5)` 사용) - [x] Lotto class instance 생성 - [x] 로또 번호 오름차순 정렬 -- [ ] 입력 받은 당첨 번호와 발행된 로또의 6개 정수를 비교 - - [ ] 5개 모두 일치하지 않고 4개만 일치했을 경우 발행된 로또의 4개 정수와 보너스 번호를 비교 - - [ ] 4개 모두 일치하지 않고 3개만 일치했을 경우 발행된 로또의 3개 정수와 보너스 번호를 비교 - - [ ] 3개 모두 일치하지 않고 3개만 일치했을 경우 발행된 로또의 2개 정수와 보너스 번호를 비교 -- [ ] 당첨 내역 계산 - - [ ] 1등부터 6등 각각의 개수 +- [x] 입력 받은 당첨 번호와 발행된 로또의 5개 정수를 비교 + - [x] 5개 모두 일치하지 않고 4개만 일치했을 경우 발행된 로또의 4개 정수와 보너스 번호를 비교 + - [x] 4개 모두 일치하지 않고 3개만 일치했을 경우 발행된 로또의 3개 정수와 보너스 번호를 비교 + - [x] 3개 모두 일치하지 않고 3개만 일치했을 경우 발행된 로또의 2개 정수와 보너스 번호를 비교 +- [x] 당첨 내역 계산 + - [x] 1등부터 6등 각각의 개수 ### 예외 처리 diff --git a/src/constants/ranks.js b/src/constants/ranks.js new file mode 100644 index 00000000..bb02f32c --- /dev/null +++ b/src/constants/ranks.js @@ -0,0 +1,7 @@ +export const RANKS = [ + { rank: 1, matchingCount: 5, hasBonusNumber: false, winningAmount: 100000000 }, + { rank: 2, matchingCount: 4, hasBonusNumber: true, winningAmount: 10000000 }, + { rank: 3, matchingCount: 4, hasBonusNumber: false, winningAmount: 1500000 }, + { rank: 4, matchingCount: 3, hasBonusNumber: true, winningAmount: 500000 }, + { rank: 5, matchingCount: 2, hasBonusNumber: true, winningAmount: 5000 }, +]; diff --git a/src/controller/LottoController.js b/src/controller/LottoController.js index 68171ddf..3cf7e3a2 100644 --- a/src/controller/LottoController.js +++ b/src/controller/LottoController.js @@ -2,6 +2,7 @@ import { InputView, OutputView } from '../view/view.js'; import { validatePurchaseAmount, validateBonusNumber } from '../utils/validator.js'; import { getLottoQuantity } from '../utils/lottoUtils.js'; import LottoFactory from '../model/LottoFactory.js'; +import LottoStatistics from '../model/LottoStatistics.js'; import Lotto from '../model/Lotto.js'; import { Console } from '@woowacourse/mission-utils'; @@ -59,6 +60,20 @@ class LottoController { OutputView.printErrorMessage(error.message); } } + + const lottoInstances = []; + lottos.forEach((lotto) => { + lottoInstances.push(new Lotto(lotto)); + }); + + const lottoRankResults = LottoStatistics.calculateRankResults( + lottoInstances, + winningLotto[0], + bonusNumber, + ); + + Console.print(lottoInstances); + Console.print(lottoRankResults); } } diff --git a/src/model/Lotto.js b/src/model/Lotto.js index 3106741f..496f6043 100644 --- a/src/model/Lotto.js +++ b/src/model/Lotto.js @@ -21,6 +21,15 @@ class Lotto { } } + getMatchingCount(winningLotto) { + const winningNumbers = winningLotto.getNumbers(); + const matchingCount = winningNumbers.filter((winningNumber) => + this.#numbers.includes(winningNumber), + ); + + return matchingCount.length; + } + getNumbers() { return this.#numbers; } diff --git a/src/model/LottoStatistics.js b/src/model/LottoStatistics.js index ad6feb55..6b847468 100644 --- a/src/model/LottoStatistics.js +++ b/src/model/LottoStatistics.js @@ -1,3 +1,32 @@ -class LottoStatistics {} +import RankFinder from './RankFinder.js'; + +class LottoStatistics { + static calculateRankResults(lottos, winningLotto, bonusNumber) { + const lottoRankResults = { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 0: 0 }; + + lottos.forEach((lotto) => { + this.updateRankResult(lotto, winningLotto, bonusNumber, lottoRankResults); + }); + + return lottoRankResults; + } + + static updateRankResult(lotto, winningLotto, bonusNumber, lottoRankResults) { + const matchingCount = lotto.getMatchingCount(winningLotto); + const hasBonusNumber = lotto.hasBonusNumber(bonusNumber); + const rank = RankFinder.getRank(matchingCount, hasBonusNumber); + + if (!rank) { + this.addRankCount(lottoRankResults, 0); + return; + } + + this.addRankCount(lottoRankResults, rank.rank); + } + + static addRankCount(lottoRankResults, rank) { + lottoRankResults[rank] += 1; + } +} export default LottoStatistics; diff --git a/src/model/RankFinder.js b/src/model/RankFinder.js index edabdefe..15a986c6 100644 --- a/src/model/RankFinder.js +++ b/src/model/RankFinder.js @@ -1,3 +1,35 @@ -class RankFinder {} +import { RANKS } from '../constants/ranks.js'; + +class RankFinder { + static getRank(matchingCount, hasBonusNumber) { + if (matchingCount === 4) { + const secondRank = RANKS.find( + (rank) => rank.matchingCount === matchingCount && rank.hasBonusNumber === hasBonusNumber, + ); + + return secondRank; + } + + if (matchingCount === 3) { + const fourthRank = RANKS.find( + (rank) => rank.matchingCount === matchingCount && rank.hasBonusNumber === hasBonusNumber, + ); + + return fourthRank; + } + + if (matchingCount === 2) { + const fifthRank = RANKS.find( + (rank) => rank.matchingCount === matchingCount && rank.hasBonusNumber === hasBonusNumber, + ); + + return fifthRank; + } + + const rank = RANKS.find((rank) => rank.matchingCount === matchingCount); + + return rank; + } +} export default RankFinder; From 7bcbf6cb0df1ecc536a81b9b5c6e740e8fd2b154 Mon Sep 17 00:00:00 2001 From: hjkim0905 Date: Sat, 10 Jan 2026 15:51:10 +0900 Subject: [PATCH 07/13] feat: complete feature test --- README.md | 4 ++-- src/controller/LottoController.js | 8 +++++--- src/model/LottoStatistics.js | 8 +++++++- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 9bb25504..7917c1ec 100644 --- a/README.md +++ b/README.md @@ -49,5 +49,5 @@ - [x] 발행한 로또 수량 출력 (`printPurchasedLottos(lottos)` 사용) - [x] lottos -> `number[][]` e.g. numbers[[1,2,3,4,5],[6,7,8,9,10]] -- [ ] 당첨 내역 출력 (`printResult(countsByRank)` 사용) - - [ ] countsByRank -> `Map` e.g. [ 1, firstCount ], [ 2, secondCount ] +- [x] 당첨 내역 출력 (`printResult(countsByRank)` 사용) + - [x] countsByRank -> `Map` e.g. [ 1, firstCount ], [ 2, secondCount ] diff --git a/src/controller/LottoController.js b/src/controller/LottoController.js index 3cf7e3a2..bc8b448c 100644 --- a/src/controller/LottoController.js +++ b/src/controller/LottoController.js @@ -61,19 +61,21 @@ class LottoController { } } + // 구매한 로또 인스턴스 생성 후 로또 랭크 갯수 도출 const lottoInstances = []; lottos.forEach((lotto) => { lottoInstances.push(new Lotto(lotto)); }); - const lottoRankResults = LottoStatistics.calculateRankResults( + const countsByRank = LottoStatistics.calculateRankResults( lottoInstances, winningLotto[0], bonusNumber, ); - Console.print(lottoInstances); - Console.print(lottoRankResults); + // 로또 당첨 통계 출력 + Console.print(''); + OutputView.printResult(countsByRank); } } diff --git a/src/model/LottoStatistics.js b/src/model/LottoStatistics.js index 6b847468..58762f64 100644 --- a/src/model/LottoStatistics.js +++ b/src/model/LottoStatistics.js @@ -8,7 +8,13 @@ class LottoStatistics { this.updateRankResult(lotto, winningLotto, bonusNumber, lottoRankResults); }); - return lottoRankResults; + const countsByRank = new Map(); + + for (let i = 0; i < Object.keys(lottoRankResults).length; i++) { + countsByRank.set(i, lottoRankResults[i]); + } + + return countsByRank; } static updateRankResult(lotto, winningLotto, bonusNumber, lottoRankResults) { From a8f82ea8a4e1e724360bf6d9f038626f799766d0 Mon Sep 17 00:00:00 2001 From: hjkim0905 Date: Sat, 10 Jan 2026 15:58:33 +0900 Subject: [PATCH 08/13] refactor: remove magic numbers using lotto constants --- src/constants/lotto.js | 6 ++++++ src/model/Lotto.js | 6 ++++-- src/utils/validator.js | 8 +++++--- 3 files changed, 15 insertions(+), 5 deletions(-) create mode 100644 src/constants/lotto.js diff --git a/src/constants/lotto.js b/src/constants/lotto.js new file mode 100644 index 00000000..8566b4fc --- /dev/null +++ b/src/constants/lotto.js @@ -0,0 +1,6 @@ +export const LOTTO = { + PRICE: 500, + NUMBER_COUNT: 5, + MIN_NUMBER: 1, + MAX_NUMBER: 30, +}; diff --git a/src/model/Lotto.js b/src/model/Lotto.js index 496f6043..767a93a1 100644 --- a/src/model/Lotto.js +++ b/src/model/Lotto.js @@ -1,3 +1,5 @@ +import { LOTTO } from '../constants/lotto.js'; + class Lotto { #numbers; @@ -7,7 +9,7 @@ class Lotto { } #validate(numbers) { - if (numbers.length !== 5) { + if (numbers.length !== LOTTO.NUMBER_COUNT) { throw new Error('로또 번호는 5개여야 합니다.'); } @@ -16,7 +18,7 @@ class Lotto { throw new Error('로또 번호에 중복된 숫자가 있습니다.'); } - if (!numbers.every((number) => number <= 30 && number >= 1)) { + if (!numbers.every((number) => number <= LOTTO.MAX_NUMBER && number >= LOTTO.MIN_NUMBER)) { throw new Error('로또 번호에 1부터 30 사이가 아닌 값이 있습니다.'); } } diff --git a/src/utils/validator.js b/src/utils/validator.js index 3651baf4..f14183e7 100644 --- a/src/utils/validator.js +++ b/src/utils/validator.js @@ -1,15 +1,17 @@ +import { LOTTO } from '../constants/lotto.js'; + export function validatePurchaseAmount(lottoPurchaseAmount) { - if (lottoPurchaseAmount < 500) { + if (lottoPurchaseAmount < LOTTO.PRICE) { throw new Error('500 미만 값이 입력되었습니다.'); } - if (!Number.isInteger(lottoPurchaseAmount / 500)) { + if (!Number.isInteger(lottoPurchaseAmount / LOTTO.PRICE)) { throw new Error('입력값이 500으로 나눴을 때 정수형 숫자로 나눠떨어지지 않습니다.'); } } export function validateBonusNumber(bonusNumber, winningLotto) { - if (!(bonusNumber <= 30 && bonusNumber >= 1)) { + if (!(bonusNumber <= LOTTO.MAX_NUMBER && bonusNumber >= LOTTO.MIN_NUMBER)) { throw new Error('보너스 번호에 1부터 30 사이가 아닌 값이 있습니다.'); } From 1cfbfac12e2837661a6002755be34fc4f2554220 Mon Sep 17 00:00:00 2001 From: hjkim0905 Date: Sat, 10 Jan 2026 16:06:34 +0900 Subject: [PATCH 09/13] refactor: replace error messages with constants --- src/constants/messages.js | 9 +++++++++ src/model/Lotto.js | 7 ++++--- src/utils/validator.js | 9 +++++---- 3 files changed, 18 insertions(+), 7 deletions(-) create mode 100644 src/constants/messages.js diff --git a/src/constants/messages.js b/src/constants/messages.js new file mode 100644 index 00000000..846f3c07 --- /dev/null +++ b/src/constants/messages.js @@ -0,0 +1,9 @@ +export const ERROR_MESSAGES = { + INVALID_LOTTO_COUNT: '로또 번호는 5개여야 합니다.', + DUPLICATE_LOTTO_NUMBERS: '로또 번호에 중복된 숫자가 있습니다.', + LOTTO_NUMBER_OUT_OF_RANGE: '로또 번호에 1부터 30 사이가 아닌 값이 있습니다.', + BELOW_MINIMUM_PURCHASE: '500 미만 값이 입력되었습니다.', + INVALID_PURCHASE_UNIT: '입력값이 500으로 나눴을 때 정수형 숫자로 나눠떨어지지 않습니다.', + BONUS_NUMBER_OUT_OF_RANGE: '보너스 번호에 1부터 30 사이가 아닌 값이 있습니다.', + BONUS_NUMBER_DUPLICATE: '보너스 번호가 당첨 번호와 중복됩니다.', +}; diff --git a/src/model/Lotto.js b/src/model/Lotto.js index 767a93a1..a2d1d070 100644 --- a/src/model/Lotto.js +++ b/src/model/Lotto.js @@ -1,4 +1,5 @@ import { LOTTO } from '../constants/lotto.js'; +import { ERROR_MESSAGES } from '../constants/messages.js'; class Lotto { #numbers; @@ -10,16 +11,16 @@ class Lotto { #validate(numbers) { if (numbers.length !== LOTTO.NUMBER_COUNT) { - throw new Error('로또 번호는 5개여야 합니다.'); + throw new Error(ERROR_MESSAGES.INVALID_LOTTO_COUNT); } const uniqueArr = new Set(numbers); if (numbers.length !== uniqueArr.size) { - throw new Error('로또 번호에 중복된 숫자가 있습니다.'); + throw new Error(ERROR_MESSAGES.DUPLICATE_LOTTO_NUMBERS); } if (!numbers.every((number) => number <= LOTTO.MAX_NUMBER && number >= LOTTO.MIN_NUMBER)) { - throw new Error('로또 번호에 1부터 30 사이가 아닌 값이 있습니다.'); + throw new Error(ERROR_MESSAGES.LOTTO_NUMBER_OUT_OF_RANGE); } } diff --git a/src/utils/validator.js b/src/utils/validator.js index f14183e7..c784153c 100644 --- a/src/utils/validator.js +++ b/src/utils/validator.js @@ -1,21 +1,22 @@ import { LOTTO } from '../constants/lotto.js'; +import { ERROR_MESSAGES } from '../constants/messages.js'; export function validatePurchaseAmount(lottoPurchaseAmount) { if (lottoPurchaseAmount < LOTTO.PRICE) { - throw new Error('500 미만 값이 입력되었습니다.'); + throw new Error(ERROR_MESSAGES.BELOW_MINIMUM_PURCHASE); } if (!Number.isInteger(lottoPurchaseAmount / LOTTO.PRICE)) { - throw new Error('입력값이 500으로 나눴을 때 정수형 숫자로 나눠떨어지지 않습니다.'); + throw new Error(ERROR_MESSAGES.INVALID_PURCHASE_UNIT); } } export function validateBonusNumber(bonusNumber, winningLotto) { if (!(bonusNumber <= LOTTO.MAX_NUMBER && bonusNumber >= LOTTO.MIN_NUMBER)) { - throw new Error('보너스 번호에 1부터 30 사이가 아닌 값이 있습니다.'); + throw new Error(ERROR_MESSAGES.BONUS_NUMBER_OUT_OF_RANGE); } if (winningLotto.hasBonusNumber(bonusNumber)) { - throw new Error('보너스 번호가 당첨 번호와 중복됩니다.'); + throw new Error(ERROR_MESSAGES.BONUS_NUMBER_DUPLICATE); } } From 417dc614be827c51892ded3aac0a3251acf0ab58 Mon Sep 17 00:00:00 2001 From: hjkim0905 Date: Sat, 10 Jan 2026 16:11:11 +0900 Subject: [PATCH 10/13] refactor: create service folder and re-name & re-place quantity calculator function --- src/controller/LottoController.js | 2 +- src/{utils/lottoUtils.js => service/LottoQuantityCalculator.js} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/{utils/lottoUtils.js => service/LottoQuantityCalculator.js} (100%) diff --git a/src/controller/LottoController.js b/src/controller/LottoController.js index bc8b448c..674a833f 100644 --- a/src/controller/LottoController.js +++ b/src/controller/LottoController.js @@ -1,6 +1,6 @@ import { InputView, OutputView } from '../view/view.js'; import { validatePurchaseAmount, validateBonusNumber } from '../utils/validator.js'; -import { getLottoQuantity } from '../utils/lottoUtils.js'; +import { getLottoQuantity } from '../service/LottoQuantityCalculator.js'; import LottoFactory from '../model/LottoFactory.js'; import LottoStatistics from '../model/LottoStatistics.js'; import Lotto from '../model/Lotto.js'; diff --git a/src/utils/lottoUtils.js b/src/service/LottoQuantityCalculator.js similarity index 100% rename from src/utils/lottoUtils.js rename to src/service/LottoQuantityCalculator.js From 68745b77ed9431b8b487c19a80a9a00210945127 Mon Sep 17 00:00:00 2001 From: hjkim0905 Date: Sat, 10 Jan 2026 16:14:05 +0900 Subject: [PATCH 11/13] test: create LottoFactoryTest file --- __tests__/LottoFactoryTest.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 __tests__/LottoFactoryTest.js diff --git a/__tests__/LottoFactoryTest.js b/__tests__/LottoFactoryTest.js new file mode 100644 index 00000000..33be3fae --- /dev/null +++ b/__tests__/LottoFactoryTest.js @@ -0,0 +1,15 @@ +import LottoFactory from '../src/model/LottoFactory.js'; + +describe('로또 팩토리 테스트', () => { + test('로또를 구매한 개수만큼만 생성한다.', () => { + const lottos = LottoFactory.createLotto(5); + + expect(lottos).toHaveLength(5); + }); + + test('정상적으로 에러없이 로또가 생성된다.', () => { + expect(() => { + LottoFactory.createLotto(5); + }).not.toThrow(); + }); +}); From fdc221ac6f0bab373fe003890f5b78b4c4d726c9 Mon Sep 17 00:00:00 2001 From: hjkim0905 Date: Sat, 10 Jan 2026 16:35:45 +0900 Subject: [PATCH 12/13] test: add test cases for LottoStaticsTeest(main function) --- __tests__/LottoStatisticsTest.js | 118 +++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 __tests__/LottoStatisticsTest.js diff --git a/__tests__/LottoStatisticsTest.js b/__tests__/LottoStatisticsTest.js new file mode 100644 index 00000000..08296ee3 --- /dev/null +++ b/__tests__/LottoStatisticsTest.js @@ -0,0 +1,118 @@ +import LottoStatistics from '../src/model/LottoStatistics.js'; +import Lotto from '../src/model/Lotto.js'; + +describe('LottoStatistics 테스트(calculateRankResults Map 테스트)', () => { + test('당첨된 로또가 없으면 꽝이 2개이다. (2개 구매시)', () => { + const lottos = [new Lotto([1, 2, 3, 4, 5]), new Lotto([7, 8, 9, 10, 11])]; + const winningLotto = new Lotto([13, 14, 15, 16, 17]); + const bonusNumber = 19; + + const result = LottoStatistics.calculateRankResults(lottos, winningLotto, bonusNumber); + + const expectedMap = new Map([ + [1, 0], + [2, 0], + [3, 0], + [4, 0], + [5, 0], + [0, 2], + ]); + + expect(result).toEqual(expectedMap); + }); + + test('1등이 1개 있으면 1등 개수가 1이고 꽝 개수가 1개이다. (2개 구매시)', () => { + const lottos = [new Lotto([1, 2, 3, 4, 5]), new Lotto([7, 8, 9, 10, 11])]; + const winningLotto = new Lotto([1, 2, 3, 4, 5]); + const bonusNumber = 7; + + const result = LottoStatistics.calculateRankResults(lottos, winningLotto, bonusNumber); + + const expectedMap = new Map([ + [1, 1], + [2, 0], + [3, 0], + [4, 0], + [5, 0], + [0, 1], + ]); + + expect(result).toEqual(expectedMap); + }); + + test('2등이 1개 있으면 2등 개수가 1이고 꽝 개수가 1개이다. (2개 구매시)', () => { + const lottos = [new Lotto([1, 2, 3, 4, 5]), new Lotto([8, 9, 10, 11, 12])]; + const winningLotto = new Lotto([1, 2, 3, 4, 6]); + const bonusNumber = 5; + + const result = LottoStatistics.calculateRankResults(lottos, winningLotto, bonusNumber); + + const expectedMap = new Map([ + [1, 0], + [2, 1], + [3, 0], + [4, 0], + [5, 0], + [0, 1], + ]); + + expect(result).toEqual(expectedMap); + }); + + test('3등이 1개 있으면 3등 개수가 1이고 꽝 개수가 1개이다. (2개 구매시)', () => { + const lottos = [new Lotto([1, 2, 3, 4, 5]), new Lotto([9, 10, 11, 12, 13])]; + const winningLotto = new Lotto([1, 2, 3, 4, 8]); + const bonusNumber = 7; + + const result = LottoStatistics.calculateRankResults(lottos, winningLotto, bonusNumber); + + const expectedMap = new Map([ + [1, 0], + [2, 0], + [3, 1], + [4, 0], + [5, 0], + [0, 1], + ]); + + expect(result).toEqual(expectedMap); + }); + + test('4등이 1개 있으면 4등 개수가 1이고 꽝 개수가 1개이다. (2개 구매시)', () => { + const lottos = [new Lotto([1, 2, 3, 4, 8]), new Lotto([10, 11, 12, 13, 14])]; + const winningLotto = new Lotto([1, 2, 3, 6, 7]); + const bonusNumber = 4; + + const result = LottoStatistics.calculateRankResults(lottos, winningLotto, bonusNumber); + + const expectedMap = new Map([ + [1, 0], + [2, 0], + [3, 0], + [4, 1], + [5, 0], + [0, 1], + ]); + + expect(result).toEqual(expectedMap); + }); + + test('5등이 1개 있고 4등이 1개 있으면 5등, 4등 개수가 각각 1이다. (2개 구매시)', () => { + const lottos = [new Lotto([1, 2, 3, 8, 9]), new Lotto([1, 2, 8, 20, 25])]; + const winningLotto = new Lotto([1, 2, 3, 4, 5]); + const bonusNumber = 8; + + const result = LottoStatistics.calculateRankResults(lottos, winningLotto, bonusNumber); + + const expectedMap = new Map([ + [1, 0], + [2, 0], + [3, 0], + [4, 1], + [5, 1], + [0, 0], + ]); + + expect(result).toEqual(expectedMap); + }); +}); From f5f9365f6f701b9616257858328f87a4bc20cb6a Mon Sep 17 00:00:00 2001 From: hjkim0905 Date: Sat, 10 Jan 2026 16:52:40 +0900 Subject: [PATCH 13/13] refactor: create newline printer function --- src/controller/LottoController.js | 8 ++++---- src/utils/newline_printer.js | 5 +++++ 2 files changed, 9 insertions(+), 4 deletions(-) create mode 100644 src/utils/newline_printer.js diff --git a/src/controller/LottoController.js b/src/controller/LottoController.js index 674a833f..c0109ac2 100644 --- a/src/controller/LottoController.js +++ b/src/controller/LottoController.js @@ -4,7 +4,7 @@ import { getLottoQuantity } from '../service/LottoQuantityCalculator.js'; import LottoFactory from '../model/LottoFactory.js'; import LottoStatistics from '../model/LottoStatistics.js'; import Lotto from '../model/Lotto.js'; -import { Console } from '@woowacourse/mission-utils'; +import { newLinePrinter } from '../utils/newline_printer.js'; class LottoController { async run() { @@ -28,9 +28,9 @@ class LottoController { const lottos = LottoFactory.createLotto(lottoQuantity); // 구매한 로또 출력 - Console.print(''); + newLinePrinter(); OutputView.printPurchasedLottos(lottos); - Console.print(''); + newLinePrinter(); // 당첨 번호 입력 inputErrorFlag = true; @@ -74,7 +74,7 @@ class LottoController { ); // 로또 당첨 통계 출력 - Console.print(''); + newLinePrinter(); OutputView.printResult(countsByRank); } } diff --git a/src/utils/newline_printer.js b/src/utils/newline_printer.js new file mode 100644 index 00000000..907417ad --- /dev/null +++ b/src/utils/newline_printer.js @@ -0,0 +1,5 @@ +import { Console } from '@woowacourse/mission-utils'; + +export function newLinePrinter() { + Console.print(''); +}