From 1cdc04d91d4e10e4a6f84c63c71e3ff39ece8dde Mon Sep 17 00:00:00 2001 From: yundoll Date: Sat, 10 Jan 2026 13:19:47 +0900 Subject: [PATCH 01/18] =?UTF-8?q?chore:=20=EB=A6=AC=EB=93=9C=EB=AF=B8=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1=20=EB=B0=8F=20=ED=8C=8C=EC=9D=BC=20=EA=B5=AC?= =?UTF-8?q?=EC=A1=B0=20=EC=84=B8=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 62 +++++++++++++++++++++++++++++++++++++++++++- src/model/index.js | 0 src/service/index.js | 0 src/util/index.js | 0 4 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 src/model/index.js create mode 100644 src/service/index.js create mode 100644 src/util/index.js diff --git a/README.md b/README.md index b168a180..8c8c546b 100644 --- a/README.md +++ b/README.md @@ -1 +1,61 @@ -# javascript-planetlotto-precourse +## 1️⃣ 과제 개요 + +**우아한 테크코스 프리코스 최종 테스트** + +**과제명 : 행성 로또** + +**기간 : 01.10 (4시간)** + +**작성자 : 윤돌** + +
+ +## 2️⃣ 기능 목록 + +**(0) 기본 구조 세팅** + +**(1) 로또 구입 금액 입력 기능 구현** + +**(2) 구입 금액 검증 로직 구현** + +- 숫자가 아닌 경우 예외처리 +- 0보다 작거나 같을 경우 예외처리 +- 500 단위로 떨어지지 않을 경우 예외처리 + +**(3) 로또 발행 기능 구현** + +**(4) 저장 및 조회 가능한 LottoBundle 클래스 생성** + +**(5) 발행된 로또 번호 출력 기능 구현** + +**(6) 당첨 번호 입력 기능 구현** + +**(7) 당첨 번호 검증 로직 구현** + +- 5개가 아닌 경우 예외처리 +- 중복될 경우 예외처리 +- 숫자가 아닐 경우 예외처리 +- 1~30 사이가 아닐 경우 예외처리 + +**(8) 보너스 번호 입력 기능 구현** + +**(9) 보너스 번호 검증 기능 구현** + +- 숫자가 아닐 경우 예외처리 +- 1~30 사이가 아닐 경우 예외처리 + +**(10) 규칙 및 상금 정의** + +**(11) 당첨 결과 계산 기능 구현** + +**(12) 결과 구현(결과 집계 및 수익률 계산)** + +**(13) 결과 출력** + +**(14) 통합 테스트 확인** + +**(15) 단일 테스트 추가 및 확인** + +**(16) 추가 기능 구현** + +
diff --git a/src/model/index.js b/src/model/index.js new file mode 100644 index 00000000..e69de29b diff --git a/src/service/index.js b/src/service/index.js new file mode 100644 index 00000000..e69de29b diff --git a/src/util/index.js b/src/util/index.js new file mode 100644 index 00000000..e69de29b From 4e2e48bb4cc47f484a679dcf48e5b0cef03d9838 Mon Sep 17 00:00:00 2001 From: yundoll Date: Sat, 10 Jan 2026 13:36:21 +0900 Subject: [PATCH 02/18] =?UTF-8?q?feat(app):=20=EB=A1=9C=EB=98=90=20?= =?UTF-8?q?=EA=B5=AC=EC=9E=85=20=EA=B8=88=EC=95=A1=20=EC=9E=85=EB=A0=A5=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit view의 함수를 이용해 구입 금액을 입력 받는 기능 연결 --- src/App.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/App.js b/src/App.js index 091aa0a5..21267615 100644 --- a/src/App.js +++ b/src/App.js @@ -1,5 +1,14 @@ +import { InputView, OutputView } from "./view.js"; + class App { - async run() {} + async run() { + try { + const purchaseAmount = await InputView.askAmount(); + console.log(purchaseAmount); + } catch (error) { + OutputView.printErrorMessage(error.message); + } + } } export default App; From 863118765683479b4991f30f0ef2238d609e3a7b Mon Sep 17 00:00:00 2001 From: yundoll Date: Sat, 10 Jan 2026 13:41:59 +0900 Subject: [PATCH 03/18] =?UTF-8?q?feat(util):=20=EA=B5=AC=EC=9E=85=20?= =?UTF-8?q?=EA=B8=88=EC=95=A1=20=EA=B2=80=EC=A6=9D=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 검증을 위한 유틸 함수 객체인 Validator 구현 - 0 이하일 경우, 1000단위가 아닐 경우 예외 처리 - readPurchaseAmount에서 받은 입력값 검증 --- src/App.js | 5 ++++- src/util/Validate.js | 17 +++++++++++++++++ src/util/index.js | 0 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 src/util/Validate.js delete mode 100644 src/util/index.js diff --git a/src/App.js b/src/App.js index 21267615..9ad2b086 100644 --- a/src/App.js +++ b/src/App.js @@ -1,10 +1,13 @@ +import LottoMachine from "./model/LottoMachine.js"; +import Validator from "./util/Validate.js"; import { InputView, OutputView } from "./view.js"; class App { async run() { try { const purchaseAmount = await InputView.askAmount(); - console.log(purchaseAmount); + const validatePurchaseAmount = + Validator.validatePurchaseAmount(purchaseAmount); } catch (error) { OutputView.printErrorMessage(error.message); } diff --git a/src/util/Validate.js b/src/util/Validate.js new file mode 100644 index 00000000..1262deb0 --- /dev/null +++ b/src/util/Validate.js @@ -0,0 +1,17 @@ +const Validator = { + validatePurchaseAmount(input) { + const amount = Number(input); + + if (amount <= 0) { + throw new Error("구입 금액은 0보다 커야 합니다."); + } + + if (amount % 500 !== 0) { + throw new Error("구입 금액은 500원 단위로 입력해야 합니다."); + } + + return amount; + }, +}; + +export default Validator; diff --git a/src/util/index.js b/src/util/index.js deleted file mode 100644 index e69de29b..00000000 From 59c5daaf5735699c208f5ee1b6e4a4016baf3928 Mon Sep 17 00:00:00 2001 From: yundoll Date: Sat, 10 Jan 2026 13:54:33 +0900 Subject: [PATCH 04/18] =?UTF-8?q?feat(model):=20=EB=A1=9C=EB=98=90=20?= =?UTF-8?q?=EB=B0=9C=ED=96=89=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 각 랜덤한 번호로 생성하는 LottoMachine 클래스 구현 - 구입 금액을 바탕으로 발행할 로또 개수를 계산 - 발행된 로또 번호를 2차원 배열로 저장 후 반환 --- src/App.js | 2 ++ src/model/LottoMachine.js | 31 +++++++++++++++++++++++++++++++ src/model/index.js | 0 3 files changed, 33 insertions(+) create mode 100644 src/model/LottoMachine.js delete mode 100644 src/model/index.js diff --git a/src/App.js b/src/App.js index 9ad2b086..86f60798 100644 --- a/src/App.js +++ b/src/App.js @@ -8,6 +8,8 @@ class App { const purchaseAmount = await InputView.askAmount(); const validatePurchaseAmount = Validator.validatePurchaseAmount(purchaseAmount); + + const lottoMachine = new LottoMachine(purchaseAmount); } catch (error) { OutputView.printErrorMessage(error.message); } diff --git a/src/model/LottoMachine.js b/src/model/LottoMachine.js new file mode 100644 index 00000000..5f50dbea --- /dev/null +++ b/src/model/LottoMachine.js @@ -0,0 +1,31 @@ +import { MissionUtils } from "@woowacourse/mission-utils"; + +class LottoMachine { + #lottoBundle; + + constructor(purchaseAmount) { + this.lottoCount = purchaseAmount / 500; + this.#lottoBundle = this.#generateLottos(); + } + + #generateLottos() { + const lottoBundle = []; + for (let i = 0; i < this.lottoCount; i++) { + const numbers = this.#generateRandomNumbers(); + lottoBundle.push(Array(numbers)); + } + + return lottoBundle; + } + + #generateRandomNumbers() { + const numbers = MissionUtils.Random.pickUniqueNumbersInRange(1, 30, 5); + return numbers.sort((a, b) => a - b); + } + + getLottos() { + return this.#lottoBundle; + } +} + +export default LottoMachine; diff --git a/src/model/index.js b/src/model/index.js deleted file mode 100644 index e69de29b..00000000 From 6f0f391917d399a6c230387f0fdbf371ef0883c5 Mon Sep 17 00:00:00 2001 From: yundoll Date: Sat, 10 Jan 2026 14:35:20 +0900 Subject: [PATCH 05/18] =?UTF-8?q?feat(model):=20=EC=A0=80=EC=9E=A5=20?= =?UTF-8?q?=EB=B0=8F=20=EC=A1=B0=ED=9A=8C=20=EA=B0=80=EB=8A=A5=ED=95=9C=20?= =?UTF-8?q?LottoBundle=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 발행된 로또 인스턴스를 묶어 관리하는 LottoBundle 클래스 구현 - 개수 조회, 전체 조회, 순회 기능 추가 - 내부 로또 묶음 데이터를 #을 사용하여 보호 --- src/model/LottoBundle.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 src/model/LottoBundle.js diff --git a/src/model/LottoBundle.js b/src/model/LottoBundle.js new file mode 100644 index 00000000..5e201902 --- /dev/null +++ b/src/model/LottoBundle.js @@ -0,0 +1,21 @@ +class LottoBundle { + #lottoBundle; + + constructor(lottoBundle) { + this.#lottoBundle = lottoBundle; + } + + size() { + return this.#lottoBundle.length; + } + + getAll() { + return [...this.#lottoBundle]; + } + + forEach(callback) { + this.#lottoBundle.forEach(callback); + } +} + +export default LottoBundle; From 08812377f42b675d28d4b1978649919f11ffa100 Mon Sep 17 00:00:00 2001 From: yundoll Date: Sat, 10 Jan 2026 14:35:55 +0900 Subject: [PATCH 06/18] =?UTF-8?q?feat(app):=20=EB=B0=9C=ED=96=89=EB=90=9C?= =?UTF-8?q?=20=EB=A1=9C=EB=98=90=20=EB=B2=88=ED=98=B8=20=EC=B6=9C=EB=A0=A5?= =?UTF-8?q?=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit view를 활용해 lottoBundle을 요구사항에 맞게 출력 --- src/App.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/App.js b/src/App.js index 86f60798..035b7e03 100644 --- a/src/App.js +++ b/src/App.js @@ -1,3 +1,4 @@ +import LottoBundle from "./model/LottoBundle.js"; import LottoMachine from "./model/LottoMachine.js"; import Validator from "./util/Validate.js"; import { InputView, OutputView } from "./view.js"; @@ -9,7 +10,11 @@ class App { const validatePurchaseAmount = Validator.validatePurchaseAmount(purchaseAmount); - const lottoMachine = new LottoMachine(purchaseAmount); + const lottoMachine = new LottoMachine(validatePurchaseAmount); + const lottoBundle = new LottoBundle(lottoMachine.getLottos()); + + console.log(""); + OutputView.printPurchasedLottos(lottoBundle.getAll()); } catch (error) { OutputView.printErrorMessage(error.message); } From e033f030e147602c038f6438563899f2cfc0d72d Mon Sep 17 00:00:00 2001 From: yundoll Date: Sat, 10 Jan 2026 14:46:31 +0900 Subject: [PATCH 07/18] =?UTF-8?q?feat(app):=20=EB=8B=B9=EC=B2=A8=20?= =?UTF-8?q?=EB=B2=88=ED=98=B8=20=EC=9E=85=EB=A0=A5=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit view의 함수를 이용해 당첨 번호 입력받는 기능 구현 --- src/App.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/App.js b/src/App.js index 035b7e03..9a58741f 100644 --- a/src/App.js +++ b/src/App.js @@ -15,6 +15,9 @@ class App { console.log(""); OutputView.printPurchasedLottos(lottoBundle.getAll()); + console.log(""); + + const winningNumbers = await InputView.askWinningLotto(); } catch (error) { OutputView.printErrorMessage(error.message); } From cc443ef7da508ad4ada97f1525c2fefb8092b639 Mon Sep 17 00:00:00 2001 From: yundoll Date: Sat, 10 Jan 2026 14:48:34 +0900 Subject: [PATCH 08/18] =?UTF-8?q?feat(util):=20=EB=8B=B9=EC=B2=A8=20?= =?UTF-8?q?=EB=B2=88=ED=98=B8=20=EA=B2=80=EC=A6=9D=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Validator에서 당첨 번호를 검증하는 validateWinningNumbers() 구현 5개가 아닐 경우, 중복될 경우, 1~30 사이가 아닐 경우 예외 처리 사용자에게 받은 입력값 검증 --- src/App.js | 2 ++ src/util/Validate.js | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/App.js b/src/App.js index 9a58741f..0489ef9b 100644 --- a/src/App.js +++ b/src/App.js @@ -18,6 +18,8 @@ class App { console.log(""); const winningNumbers = await InputView.askWinningLotto(); + const validateWinningNumbers = + Validator.validateWinningNumbers(winningNumbers); } catch (error) { OutputView.printErrorMessage(error.message); } diff --git a/src/util/Validate.js b/src/util/Validate.js index 1262deb0..036c8d16 100644 --- a/src/util/Validate.js +++ b/src/util/Validate.js @@ -12,6 +12,25 @@ const Validator = { return amount; }, + + validateWinningNumbers(winningNumbers) { + if (winningNumbers.length !== 5) { + throw new Error("로또 번호는 5개여야 합니다."); + } + + const uniqueNumbers = new Set(winningNumbers); + if (uniqueNumbers.size !== winningNumbers.length) { + throw new Error("로또 번호는 중복될 수 없습니다."); + } + + winningNumbers.forEach((num) => { + if (num < 1 || num > 30) { + throw new Error("로또 번호는 1부터 30 사이의 숫자여야 합니다."); + } + }); + + return winningNumbers; + }, }; export default Validator; From 139c9467bcd7a1d3708f2eb525172602e1d9916d Mon Sep 17 00:00:00 2001 From: yundoll Date: Sat, 10 Jan 2026 14:50:33 +0900 Subject: [PATCH 09/18] =?UTF-8?q?feat(app):=20=EB=B3=B4=EB=84=88=EC=8A=A4?= =?UTF-8?q?=20=EB=B2=88=ED=98=B8=20=EC=9E=85=EB=A0=A5=20=EA=B8=B0=EB=8A=A5?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit view의 함수를 이용해 보너스 번호 입력받는 기능 구현 --- src/App.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/App.js b/src/App.js index 0489ef9b..67e4f6c4 100644 --- a/src/App.js +++ b/src/App.js @@ -20,6 +20,9 @@ class App { const winningNumbers = await InputView.askWinningLotto(); const validateWinningNumbers = Validator.validateWinningNumbers(winningNumbers); + console.log(""); + + const bonusNumber = await InputView.askBonusNumber(); } catch (error) { OutputView.printErrorMessage(error.message); } From 4661005d37099521b1b7ddb88ded7ea5ca9485bc Mon Sep 17 00:00:00 2001 From: yundoll Date: Sat, 10 Jan 2026 14:53:14 +0900 Subject: [PATCH 10/18] =?UTF-8?q?feat(util):=20=EB=B3=B4=EB=84=88=EC=8A=A4?= =?UTF-8?q?=20=EB=B2=88=ED=98=B8=20=EA=B2=80=EC=A6=9D=20=EB=A1=9C=EC=A7=81?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Validator에서 보너스 번호를 검증하는 validateBonusNumber() 구현 - 1~30 사이가 아닐 경우 예외 처리 - 사용자에게 받은 입력값 검증 --- src/App.js | 2 ++ src/util/Validate.js | 14 ++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/src/App.js b/src/App.js index 67e4f6c4..dc4eb38e 100644 --- a/src/App.js +++ b/src/App.js @@ -23,6 +23,8 @@ class App { console.log(""); const bonusNumber = await InputView.askBonusNumber(); + const validateBonusNumber = Validator.validateBonusNumber(bonusNumber); + console.log(""); } catch (error) { OutputView.printErrorMessage(error.message); } diff --git a/src/util/Validate.js b/src/util/Validate.js index 036c8d16..e830978c 100644 --- a/src/util/Validate.js +++ b/src/util/Validate.js @@ -31,6 +31,20 @@ const Validator = { return winningNumbers; }, + + validateBonusNumber(bonusNumber) { + const number = Number(bonusNumber); + + if (isNaN(number)) { + throw new Error("[ERROR] 보너스 번호는 숫자여야 합니다."); + } + + if (number < 1 || number > 45) { + throw new Error("[ERROR] 로또 번호는 1부터 45 사이의 숫자여야 합니다."); + } + + return bonusNumber; + }, }; export default Validator; From 24fa2e3b00026b653554122a72191c5c08391b94 Mon Sep 17 00:00:00 2001 From: yundoll Date: Sat, 10 Jan 2026 14:56:14 +0900 Subject: [PATCH 11/18] =?UTF-8?q?feat(util):=20=EA=B7=9C=EC=B9=99=20?= =?UTF-8?q?=EB=B0=8F=20=EC=83=81=EA=B8=88=20=EC=A0=95=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 일치 개수와 보너스 여부를 기반으로 등수와 상금을 반환하는 getRank() 구현 - 숫자 값을 하드코딩 하지 않고, 의미있는 이름 사용 --- src/util/Rank.js | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 src/util/Rank.js diff --git a/src/util/Rank.js b/src/util/Rank.js new file mode 100644 index 00000000..b85a825c --- /dev/null +++ b/src/util/Rank.js @@ -0,0 +1,30 @@ +export const RANKS = { + FIRST: 1, + SECOND: 2, + THIRD: 3, + FOURTH: 4, + FIFTH: 5, +}; + +export const PRIZES = { + [RANKS.FIRST]: 100000000, + [RANKS.SECOND]: 10000000, + [RANKS.THIRD]: 1500000, + [RANKS.FOURTH]: 500000, + [RANKS.FIFTH]: 5000, +}; + +export function getRank(matchCount, hasBonus) { + if (matchCount === 6) + return { rank: RANKS.FIRST, prize: PRIZES[RANKS.FIRST] }; + if (matchCount === 5 && hasBonus) + return { rank: RANKS.SECOND, prize: PRIZES[RANKS.SECOND] }; + if (matchCount === 5) + return { rank: RANKS.THIRD, prize: PRIZES[RANKS.THIRD] }; + if (matchCount === 4) + return { rank: RANKS.FOURTH, prize: PRIZES[RANKS.FOURTH] }; + if (matchCount === 3) + return { rank: RANKS.FIFTH, prize: PRIZES[RANKS.FIFTH] }; + + return null; +} From 39c11393e17429b5971deb5859cfdb9ad62f105a Mon Sep 17 00:00:00 2001 From: yundoll Date: Sat, 10 Jan 2026 15:51:32 +0900 Subject: [PATCH 12/18] =?UTF-8?q?test:=20=EB=A1=9C=EB=98=90=20=ED=8C=90?= =?UTF-8?q?=EB=8B=A8=ED=95=98=EB=8A=94=20=ED=95=A8=EC=88=98=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?(TDD)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 추가할 LottoJudge 클래스의 기능인 judge() 함수에 대한 테스트 케이스 추가 --- __tests__/LottoJudgeTest.js | 41 +++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 __tests__/LottoJudgeTest.js diff --git a/__tests__/LottoJudgeTest.js b/__tests__/LottoJudgeTest.js new file mode 100644 index 00000000..4f683638 --- /dev/null +++ b/__tests__/LottoJudgeTest.js @@ -0,0 +1,41 @@ +import LottoJudge from "../src/service/LottoJudge"; +import { PRIZES } from "../src/util/Rank"; + +describe("로또 판단하는 클래스 테스트", () => { + test("로또 판단하는 judge 함수 정상 작동하는지 확인", () => { + expect(() => { + const lottoJudge = new LottoJudge([1, 2, 3, 4, 5], 6); + return lottoJudge + .judge([1, 2, 3, 4, 5]) + .toStictEqual({ rank: 1, prize: PRIZES[1] }); + }); + + expect(() => { + const lottoJudge = new LottoJudge([1, 2, 3, 4, 5], 6); + return lottoJudge + .judge([1, 2, 3, 4, 6]) + .toStictEqual({ rank: 2, prize: PRIZES[2] }); + }); + + expect(() => { + const lottoJudge = new LottoJudge([1, 2, 3, 4, 5], 6); + return lottoJudge + .judge([1, 2, 3, 4, 8]) + .toStictEqual({ rank: 3, prize: PRIZES[3] }); + }); + + expect(() => { + const lottoJudge = new LottoJudge([1, 2, 3, 4, 5], 6); + return lottoJudge + .judge([1, 2, 3, 8, 9]) + .toStictEqual({ rank: 4, prize: PRIZES[4] }); + }); + + expect(() => { + const lottoJudge = new LottoJudge([1, 2, 3, 4, 5], 6); + return lottoJudge + .judge([1, 2, 8, 9, 10]) + .toStictEqual({ rank: 5, prize: PRIZES[5] }); + }); + }); +}); From 06862b0a58f57ac445b49935277919fea1834dbc Mon Sep 17 00:00:00 2001 From: yundoll Date: Sat, 10 Jan 2026 15:53:27 +0900 Subject: [PATCH 13/18] =?UTF-8?q?feat(service):=20=EB=8B=B9=EC=B2=A8=20?= =?UTF-8?q?=EA=B2=B0=EA=B3=BC=20=EA=B3=84=EC=82=B0=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - LottoJudge에서 당첨 번호, 보너스 번호, 로또 번호를 기반으로 결과를 반환하는 judge() 메소드 구현 - 일치 개수와 보너스 여부에 따라 getRank() 결과 반환 - 테스트 통과 여부 확인 --- src/service/LottoJudge.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 src/service/LottoJudge.js diff --git a/src/service/LottoJudge.js b/src/service/LottoJudge.js new file mode 100644 index 00000000..82929b9a --- /dev/null +++ b/src/service/LottoJudge.js @@ -0,0 +1,21 @@ +import { getRank } from "../util/Rank.js"; + +class LottoJudge { + constructor(winningNumbers, bonusNumber) { + this.winningNumbers = winningNumbers; + this.bonusNumber = bonusNumber; + } + + judge(lotto) { + const matchCount = this.winningNumbers.filter((it) => + lotto[0].includes(it) + ).length; + const hasBonus = lotto[0].includes(this.bonusNumber); + + console.log(matchCount, hasBonus); + + return getRank(matchCount, hasBonus); + } +} + +export default LottoJudge; From 136ac20e78ec65664c124d1f62ee810521602ff6 Mon Sep 17 00:00:00 2001 From: yundoll Date: Sat, 10 Jan 2026 16:37:32 +0900 Subject: [PATCH 14/18] =?UTF-8?q?fix(util):=20=EA=B7=9C=EC=B9=99=20?= =?UTF-8?q?=EB=B0=8F=20=EC=83=81=EA=B8=88=20=EC=9E=AC=EC=A0=95=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit -getRank() 함수의 조건부를 문제 조건에 맞게 수정 --- src/util/Rank.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/util/Rank.js b/src/util/Rank.js index b85a825c..94c5eec9 100644 --- a/src/util/Rank.js +++ b/src/util/Rank.js @@ -15,15 +15,15 @@ export const PRIZES = { }; export function getRank(matchCount, hasBonus) { - if (matchCount === 6) + if (matchCount === 5) return { rank: RANKS.FIRST, prize: PRIZES[RANKS.FIRST] }; - if (matchCount === 5 && hasBonus) + if (matchCount === 4 && hasBonus) return { rank: RANKS.SECOND, prize: PRIZES[RANKS.SECOND] }; - if (matchCount === 5) - return { rank: RANKS.THIRD, prize: PRIZES[RANKS.THIRD] }; if (matchCount === 4) - return { rank: RANKS.FOURTH, prize: PRIZES[RANKS.FOURTH] }; + return { rank: RANKS.THIRD, prize: PRIZES[RANKS.THIRD] }; if (matchCount === 3) + return { rank: RANKS.FOURTH, prize: PRIZES[RANKS.FOURTH] }; + if (matchCount === 2) return { rank: RANKS.FIFTH, prize: PRIZES[RANKS.FIFTH] }; return null; From b19a896a50494714deaa12f3fe62f1f875166402 Mon Sep 17 00:00:00 2001 From: yundoll Date: Sat, 10 Jan 2026 16:39:07 +0900 Subject: [PATCH 15/18] =?UTF-8?q?feat(model):=20=EA=B2=B0=EA=B3=BC=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20(=EA=B2=B0=EA=B3=BC=20=EC=A7=91=EA=B3=84?= =?UTF-8?q?=20=EB=B0=8F=20=EC=88=98=EC=9D=B5=EB=A5=A0=20=EA=B3=84=EC=82=B0?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 각 로또별 등수 결과를 집계하여 변수(rankCounts)에 저장 등수별 상금과 당첨 개수를 곱해 총 상금 계산 결과 출력을 위한 getter 메서드 추가 --- src/service/Result.js | 22 ++++++++++++++++++++++ src/service/index.js | 0 2 files changed, 22 insertions(+) create mode 100644 src/service/Result.js delete mode 100644 src/service/index.js diff --git a/src/service/Result.js b/src/service/Result.js new file mode 100644 index 00000000..1ebca1c0 --- /dev/null +++ b/src/service/Result.js @@ -0,0 +1,22 @@ +class Result { + constructor() { + this.rankCounts = { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0 }; + } + + addRank(rank) { + if (rank) this.rankCounts[rank.rank]++; + } + + calculate(lottoBundle, lottoJudge) { + lottoBundle.forEach((lotto) => { + const rank = lottoJudge.judge(lotto); + this.addRank(rank); + }); + } + + getRankCounts() { + return this.rankCounts; + } +} + +export default Result; diff --git a/src/service/index.js b/src/service/index.js deleted file mode 100644 index e69de29b..00000000 From 96884a91653088aeb4f0714ab232a6ddd97fdf7e Mon Sep 17 00:00:00 2001 From: yundoll Date: Sat, 10 Jan 2026 16:40:27 +0900 Subject: [PATCH 16/18] =?UTF-8?q?feat(app):=20=EA=B2=B0=EA=B3=BC=20?= =?UTF-8?q?=EC=B6=9C=EB=A0=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 결과를 비교하고 출력하는 기능 구현 --- src/App.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/App.js b/src/App.js index dc4eb38e..d59411d8 100644 --- a/src/App.js +++ b/src/App.js @@ -1,5 +1,7 @@ import LottoBundle from "./model/LottoBundle.js"; import LottoMachine from "./model/LottoMachine.js"; +import LottoJudge from "./service/LottoJudge.js"; +import Result from "./service/Result.js"; import Validator from "./util/Validate.js"; import { InputView, OutputView } from "./view.js"; @@ -25,6 +27,22 @@ class App { const bonusNumber = await InputView.askBonusNumber(); const validateBonusNumber = Validator.validateBonusNumber(bonusNumber); console.log(""); + + const lottoJudge = new LottoJudge(winningNumbers, bonusNumber); + const result = new Result(); + + result.calculate(lottoBundle, lottoJudge); + const rankCounts = result.getRankCounts(); + + OutputView.printResult( + new Map([ + [1, rankCounts[1]], + [2, rankCounts[2]], + [3, rankCounts[3]], + [4, rankCounts[4]], + [5, rankCounts[5]], + ]) + ); } catch (error) { OutputView.printErrorMessage(error.message); } From df9905471af04ca7db2ff2779d95765fa1a69f0a Mon Sep 17 00:00:00 2001 From: yundoll Date: Sat, 10 Jan 2026 16:46:43 +0900 Subject: [PATCH 17/18] =?UTF-8?q?fix:=20=ED=86=B5=ED=95=A9=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=ED=99=95=EC=9D=B8=20=EB=B0=8F=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 로그용으로 출력한 console.log 제거 - 판단하는 함수에서 리턴값 제거 후 호출만 하도록 변경 --- src/App.js | 10 ++++------ src/service/LottoJudge.js | 2 -- src/util/Validate.js | 6 ------ 3 files changed, 4 insertions(+), 14 deletions(-) diff --git a/src/App.js b/src/App.js index d59411d8..adeac4ae 100644 --- a/src/App.js +++ b/src/App.js @@ -9,10 +9,9 @@ class App { async run() { try { const purchaseAmount = await InputView.askAmount(); - const validatePurchaseAmount = - Validator.validatePurchaseAmount(purchaseAmount); + Validator.validatePurchaseAmount(purchaseAmount); - const lottoMachine = new LottoMachine(validatePurchaseAmount); + const lottoMachine = new LottoMachine(purchaseAmount); const lottoBundle = new LottoBundle(lottoMachine.getLottos()); console.log(""); @@ -20,12 +19,11 @@ class App { console.log(""); const winningNumbers = await InputView.askWinningLotto(); - const validateWinningNumbers = - Validator.validateWinningNumbers(winningNumbers); + Validator.validateWinningNumbers(winningNumbers); console.log(""); const bonusNumber = await InputView.askBonusNumber(); - const validateBonusNumber = Validator.validateBonusNumber(bonusNumber); + Validator.validateBonusNumber(bonusNumber); console.log(""); const lottoJudge = new LottoJudge(winningNumbers, bonusNumber); diff --git a/src/service/LottoJudge.js b/src/service/LottoJudge.js index 82929b9a..c982935c 100644 --- a/src/service/LottoJudge.js +++ b/src/service/LottoJudge.js @@ -12,8 +12,6 @@ class LottoJudge { ).length; const hasBonus = lotto[0].includes(this.bonusNumber); - console.log(matchCount, hasBonus); - return getRank(matchCount, hasBonus); } } diff --git a/src/util/Validate.js b/src/util/Validate.js index e830978c..0fd7e08a 100644 --- a/src/util/Validate.js +++ b/src/util/Validate.js @@ -9,8 +9,6 @@ const Validator = { if (amount % 500 !== 0) { throw new Error("구입 금액은 500원 단위로 입력해야 합니다."); } - - return amount; }, validateWinningNumbers(winningNumbers) { @@ -28,8 +26,6 @@ const Validator = { throw new Error("로또 번호는 1부터 30 사이의 숫자여야 합니다."); } }); - - return winningNumbers; }, validateBonusNumber(bonusNumber) { @@ -42,8 +38,6 @@ const Validator = { if (number < 1 || number > 45) { throw new Error("[ERROR] 로또 번호는 1부터 45 사이의 숫자여야 합니다."); } - - return bonusNumber; }, }; From 48fb2de3f6582784cfbef8ca7f8ef48e5bd0f960 Mon Sep 17 00:00:00 2001 From: yundoll Date: Sat, 10 Jan 2026 16:55:52 +0900 Subject: [PATCH 18/18] =?UTF-8?q?fix:=20=EB=8B=B9=EC=B2=A8=EC=9D=B4=20?= =?UTF-8?q?=EB=90=98=EC=A7=80=20=EC=95=8A=EC=95=98=EC=9D=84=EB=95=8C=200?= =?UTF-8?q?=EA=B0=9C=EB=A1=9C=20=EC=B6=9C=EB=A0=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.js | 1 + src/service/Result.js | 2 +- src/util/Rank.js | 4 +++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/App.js b/src/App.js index adeac4ae..81a8d609 100644 --- a/src/App.js +++ b/src/App.js @@ -39,6 +39,7 @@ class App { [3, rankCounts[3]], [4, rankCounts[4]], [5, rankCounts[5]], + [0, rankCounts[6]], ]) ); } catch (error) { diff --git a/src/service/Result.js b/src/service/Result.js index 1ebca1c0..cf5b2129 100644 --- a/src/service/Result.js +++ b/src/service/Result.js @@ -1,6 +1,6 @@ class Result { constructor() { - this.rankCounts = { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0 }; + this.rankCounts = { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0 }; } addRank(rank) { diff --git a/src/util/Rank.js b/src/util/Rank.js index 94c5eec9..bdcdb988 100644 --- a/src/util/Rank.js +++ b/src/util/Rank.js @@ -4,6 +4,7 @@ export const RANKS = { THIRD: 3, FOURTH: 4, FIFTH: 5, + SIXTH: 6, }; export const PRIZES = { @@ -12,6 +13,7 @@ export const PRIZES = { [RANKS.THIRD]: 1500000, [RANKS.FOURTH]: 500000, [RANKS.FIFTH]: 5000, + [RANKS.SIXTH]: 0, }; export function getRank(matchCount, hasBonus) { @@ -26,5 +28,5 @@ export function getRank(matchCount, hasBonus) { if (matchCount === 2) return { rank: RANKS.FIFTH, prize: PRIZES[RANKS.FIFTH] }; - return null; + return { rank: RANKS.SIXTH, prize: PRIZES[RANKS.FIFTH] }; }