From 20bfa55aa55fe5bd83c30d4b56d3b89e9d726354 Mon Sep 17 00:00:00 2001 From: JinwooKim Date: Wed, 30 Oct 2024 19:04:16 +0900 Subject: [PATCH 01/19] =?UTF-8?q?docs:=20README=EC=97=90=20=ED=95=99?= =?UTF-8?q?=EC=8A=B5=20=EB=AA=A9=ED=91=9C=20=EB=B0=8F=20=EC=B2=B4=ED=81=AC?= =?UTF-8?q?=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/README.md b/README.md index 15bb106b5..02085e2c8 100644 --- a/README.md +++ b/README.md @@ -1 +1,30 @@ # javascript-lotto-precourse +# 우아한 프리코스 3주차 과제 +## 학습 목표 +- 관련 함수를 묶어 클래스를 만들고, 객체들이 협력하여 하나의 큰 기능을 수행하도록 한다. +- 클래스와 함수에 대한 단위 테스트를 통해 의도한 대로 정확하게 작동하는 영역을 확보한다. +- 2주 차 공통 피드백을 최대한 반영한다. + +## 📝 요구사항분석 + + +## ⚠️ 예외처리 + +## ✅ 체크리스트 +- [ ] 클래스 설계와 구현, 메서드 설계와 구현 같은 상세한 내용은 기능 목록에 포함하지 않았는가? +- [ ] JavaScript Code Conventions 을 준수하였는가? +- [ ] 한 메서드에 오직 한 단계의 들여쓰기만 허용했는가? +- [ ] 함수(또는 메서드)의 길이가 15라인은 넘어가지 않았는가? +- [ ] else 예약어를 쓰지 않았는가? +- [ ] 모든 원시값과 문자열을 포장했는가? +- [ ] 3개 이상의 인스턴스 변수를 가진 클래스를 구현하지 않았는가? +- [ ] getter/setter 없이 구현했는가? +- [ ] 메소드의 인자 수를 제한했는가? +- [ ] 코드 한 줄에 점(.)을 하나만 허용했는가? +- [ ] 메소드가 한가지 일만 담당하도록 구현했는가? +- [ ] 클래스를 작게 유지하기 위해 노력했는가? +- [ ] 3항 연산자를 사용하지 않았는가? +- [ ] AngularJS Commit Conventions 에 맞춰 Commit Message를 작성했는가? +- [ ] 이름을 축약하지 않고 의도를 잘 드러냈는가? +- [ ] Jest를 활용해 테스트 코드로 작동을 확인했는가? +- [ ] 구현한 기능에 대한 단위 테스트를 작성하였는가? \ No newline at end of file From 62dc197836fea9bd682b4bb43d9841a04f1df738 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 1 Nov 2024 17:48:35 +0900 Subject: [PATCH 02/19] =?UTF-8?q?docs:=20=EC=9A=94=EA=B5=AC=EC=82=AC?= =?UTF-8?q?=ED=95=AD=EB=B6=84=EC=84=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 02085e2c8..b16645158 100644 --- a/README.md +++ b/README.md @@ -6,12 +6,25 @@ - 2주 차 공통 피드백을 최대한 반영한다. ## 📝 요구사항분석 +### 입력 +- [ ] 로또 구입 금액 입력 받기 +- [ ] 로또 당첨 번호 입력 받기 +- [ ] 보너스 번호 입력 받기 +### 출력 +- [ ] 당첨 통계 출력 +- [ ] 총 수익률 출력 + +### 로또 +- [ ] 랜덤한 6개의 숫자 발행 +- [ ] 랜덤한 보너스 숫자 발행 +- [ ] 로또 당첨 번호와 구매한 로또 번호 비교하여 일치 개수 산출 +- [ ] 구매 금액과 총 당첨 금액을 비교하여 수익률 계산 ## ⚠️ 예외처리 ## ✅ 체크리스트 -- [ ] 클래스 설계와 구현, 메서드 설계와 구현 같은 상세한 내용은 기능 목록에 포함하지 않았는가? +- [x] 클래스 설계와 구현, 메서드 설계와 구현 같은 상세한 내용은 기능 목록에 포함하지 않았는가? - [ ] JavaScript Code Conventions 을 준수하였는가? - [ ] 한 메서드에 오직 한 단계의 들여쓰기만 허용했는가? - [ ] 함수(또는 메서드)의 길이가 15라인은 넘어가지 않았는가? From b9f9d706cb40573312d9b0aed2539e7a55e10d81 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 2 Nov 2024 02:29:02 +0900 Subject: [PATCH 03/19] =?UTF-8?q?feat:=20=EA=B5=AC=EC=9E=85=20=EA=B8=88?= =?UTF-8?q?=EC=95=A1=20=EC=9E=85=EB=A0=A5=20=ED=9B=84=20=EB=A1=9C=EB=98=90?= =?UTF-8?q?=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.js | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/App.js b/src/App.js index 091aa0a5d..14b9239ab 100644 --- a/src/App.js +++ b/src/App.js @@ -1,5 +1,26 @@ +import { Console, MissionUtils } from "@woowacourse/mission-utils"; +import Lotto from "./Lotto.js" + class App { - async run() {} + async run() { + const purchaseMoney = await Console.readLineAsync( + "구입금액을 입력해 주세요." + ); + const tickets = Array.from({ length: purchaseMoney / 1000 }, () => + MissionUtils.Random.pickUniqueNumbersInRange(1, 45, 6) + ); + const lottos = tickets.map((ticket) => new Lotto(ticket)); + + Console.print(`${tickets.length}개를 구매했습니다.`); + + // const winningNumber = await Console.readLineAsync( + // "당첨 번호를 입력해 주세요." + // ); + + // const bonusNumber = await Console.readLineAsync( + // "보너스 번호를 입력해 주세요." + // ); + } } export default App; From 8a6e4559b7546fdf35737c6b351838b342b20710 Mon Sep 17 00:00:00 2001 From: JINWOOKIM Date: Sat, 2 Nov 2024 19:08:17 +0900 Subject: [PATCH 04/19] =?UTF-8?q?feat:=20=EA=B5=AC=EC=9E=85=ED=95=9C=20?= =?UTF-8?q?=EB=A1=9C=EB=98=90=20=EB=B2=88=ED=98=B8=20=EC=B6=9C=EB=A0=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 8 ++++---- src/App.js | 13 ++++++++----- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index b16645158..9298f7114 100644 --- a/README.md +++ b/README.md @@ -7,16 +7,16 @@ ## 📝 요구사항분석 ### 입력 -- [ ] 로또 구입 금액 입력 받기 -- [ ] 로또 당첨 번호 입력 받기 -- [ ] 보너스 번호 입력 받기 +- [x] 로또 구입 금액 입력 받기 +- [x] 로또 당첨 번호 입력 받기 +- [x] 보너스 번호 입력 받기 ### 출력 - [ ] 당첨 통계 출력 - [ ] 총 수익률 출력 ### 로또 -- [ ] 랜덤한 6개의 숫자 발행 +- [x] 랜덤한 6개의 숫자 발행 - [ ] 랜덤한 보너스 숫자 발행 - [ ] 로또 당첨 번호와 구매한 로또 번호 비교하여 일치 개수 산출 - [ ] 구매 금액과 총 당첨 금액을 비교하여 수익률 계산 diff --git a/src/App.js b/src/App.js index 14b9239ab..81be55fd9 100644 --- a/src/App.js +++ b/src/App.js @@ -1,18 +1,21 @@ import { Console, MissionUtils } from "@woowacourse/mission-utils"; -import Lotto from "./Lotto.js" +import Lotto from "./Lotto.js"; class App { async run() { const purchaseMoney = await Console.readLineAsync( "구입금액을 입력해 주세요." ); - const tickets = Array.from({ length: purchaseMoney / 1000 }, () => + let lottos = Array.from({ length: purchaseMoney / 1000 }, () => MissionUtils.Random.pickUniqueNumbersInRange(1, 45, 6) ); - const lottos = tickets.map((ticket) => new Lotto(ticket)); - Console.print(`${tickets.length}개를 구매했습니다.`); - + Console.print(`${lottos.length}개를 구매했습니다.`); + lottos = lottos.map((lotto) => { + Console.print(lotto); + return new Lotto(lotto); + }); + // const winningNumber = await Console.readLineAsync( // "당첨 번호를 입력해 주세요." // ); From 5a289d6b9ed638f09e5be41e273a37d75cab8410 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 3 Nov 2024 22:12:20 +0900 Subject: [PATCH 05/19] =?UTF-8?q?feat:=20=EC=8B=A4=ED=96=89=20=EA=B2=B0?= =?UTF-8?q?=EA=B3=BC=20=EC=98=88=EC=8B=9C=EC=97=90=20=EB=A7=9E=EA=B2=8C=20?= =?UTF-8?q?=EC=9E=85=EC=B6=9C=EB=A0=A5=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/App.js b/src/App.js index 81be55fd9..4b396e01b 100644 --- a/src/App.js +++ b/src/App.js @@ -4,25 +4,25 @@ import Lotto from "./Lotto.js"; class App { async run() { const purchaseMoney = await Console.readLineAsync( - "구입금액을 입력해 주세요." + "구입금액을 입력해 주세요.\n" ); let lottos = Array.from({ length: purchaseMoney / 1000 }, () => MissionUtils.Random.pickUniqueNumbersInRange(1, 45, 6) ); - Console.print(`${lottos.length}개를 구매했습니다.`); + Console.print(`\n${lottos.length}개를 구매했습니다.`); lottos = lottos.map((lotto) => { Console.print(lotto); return new Lotto(lotto); }); - // const winningNumber = await Console.readLineAsync( - // "당첨 번호를 입력해 주세요." - // ); + const winningNumber = await Console.readLineAsync( + "\n당첨 번호를 입력해 주세요.\n" + ); - // const bonusNumber = await Console.readLineAsync( - // "보너스 번호를 입력해 주세요." - // ); + const bonusNumber = await Console.readLineAsync( + "\n보너스 번호를 입력해 주세요.\n" + ); } } From 4e3842031ea1a9a5c42e995cb4af24b4b44e8410 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 3 Nov 2024 23:11:53 +0900 Subject: [PATCH 06/19] =?UTF-8?q?docs:=20=EC=9A=94=EA=B5=AC=EC=82=AC?= =?UTF-8?q?=ED=95=AD=EB=B6=84=EC=84=9D=20=EC=88=98=EC=A0=95=20(=EB=B3=B4?= =?UTF-8?q?=EB=84=88=EC=8A=A4=20=EB=B2=88=ED=98=B8=20=EA=B0=9C=EB=B3=84=20?= =?UTF-8?q?=ED=8C=8C=EC=95=85)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9298f7114..b74e236cb 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,8 @@ ### 로또 - [x] 랜덤한 6개의 숫자 발행 -- [ ] 랜덤한 보너스 숫자 발행 -- [ ] 로또 당첨 번호와 구매한 로또 번호 비교하여 일치 개수 산출 +- [x] 로또 당첨 번호와 구매한 로또 번호 비교하여 일치 개수 산출 +- [x] 보너스 번호와 로또 번호 일치 여부 저장 - [ ] 구매 금액과 총 당첨 금액을 비교하여 수익률 계산 ## ⚠️ 예외처리 From 7867b5303e5d0a1fe157b1506bdb87e7c7dff5b4 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 3 Nov 2024 23:20:28 +0900 Subject: [PATCH 07/19] =?UTF-8?q?feat:=20=EB=A1=9C=EB=98=90=20=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20=EC=9D=BC=EC=B9=98=20=EC=97=AC=EB=B6=80=20=ED=99=95?= =?UTF-8?q?=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.js | 21 +++++++++++++++++++++ src/Lotto.js | 13 +++++++++++++ 2 files changed, 34 insertions(+) diff --git a/src/App.js b/src/App.js index 4b396e01b..dfcf47500 100644 --- a/src/App.js +++ b/src/App.js @@ -23,7 +23,28 @@ class App { const bonusNumber = await Console.readLineAsync( "\n보너스 번호를 입력해 주세요.\n" ); + + const matchNumbers = lottos.map((lotto) => + lotto.confirmMatches(winningNumber + bonusNumber) + ); + const matchBonus = confirmBonus(bonusNumber); + + // 통계 출력 + Console.print("\n당첨 통계\n---"); + // winningDetails(matchNumbers, matchBonus); } + + // winningDetails(numbers, bonus) { + // const winningStats = { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0 }; + + // numbers.forEach((number) => (winningStats[number] += 1)); + + // console.log(`3개 일치 (5,000원) - ${winningStats[3]}개`); + // console.log(`4개 일치 (50,000원) - ${winningStats[4]}개`); + // console.log(`5개 일치 (1,500,000원) - ${winningStats[5]}개`); + // console.log(`5개 일치, 보너스 볼 일치 (30,000,000원) - ${winningStats['5_bonus']}개`); + // console.log(`6개 일치 (2,000,000,000원) - ${winningStats[6]}개`); + // } } export default App; diff --git a/src/Lotto.js b/src/Lotto.js index cb0b1527e..30fff9c3a 100644 --- a/src/Lotto.js +++ b/src/Lotto.js @@ -13,6 +13,19 @@ class Lotto { } // TODO: 추가 기능 구현 + confirmMatches(winNum) { + let matches = 0; + this.#numbers.forEach((number) => { + if (winNum.includes(number)) { + matches += 1; + } + return matches; + }); + } + confirmBonus(bonusNum) { + const match = this.#numbers.includes(bonusNum); + return match; + } } export default Lotto; From d6b6b50424f413376ccd3fde180a61437e89c19f Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 4 Nov 2024 19:45:43 +0900 Subject: [PATCH 08/19] =?UTF-8?q?docs:=20=ED=83=80=EC=9D=B4=ED=8B=80=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b74e236cb..e66f01aff 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # javascript-lotto-precourse -# 우아한 프리코스 3주차 과제 +# 🏫 우아한테크코스 7기 프리코스 3주차 미션: 로또 발매기 ## 학습 목표 - 관련 함수를 묶어 클래스를 만들고, 객체들이 협력하여 하나의 큰 기능을 수행하도록 한다. - 클래스와 함수에 대한 단위 테스트를 통해 의도한 대로 정확하게 작동하는 영역을 확보한다. From bfa02b1ce1f0813de18aeb644a74151e75d79100 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 4 Nov 2024 19:58:53 +0900 Subject: [PATCH 09/19] =?UTF-8?q?refactor:=20Lotto=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=EC=9D=98=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=A7=81=EA=B4=80=EC=A0=81=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EA=B0=80=EB=8F=85=EC=84=B1=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Lotto.js | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/Lotto.js b/src/Lotto.js index 30fff9c3a..e9862106d 100644 --- a/src/Lotto.js +++ b/src/Lotto.js @@ -14,17 +14,10 @@ class Lotto { // TODO: 추가 기능 구현 confirmMatches(winNum) { - let matches = 0; - this.#numbers.forEach((number) => { - if (winNum.includes(number)) { - matches += 1; - } - return matches; - }); + return this.#numbers.filter(number => winNum.includes(number)).length; } confirmBonus(bonusNum) { - const match = this.#numbers.includes(bonusNum); - return match; + return this.#numbers.includes(bonusNum); } } From f43e3280b0504a24f0fbc81e87b2bdb482ad1519 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 4 Nov 2024 20:57:25 +0900 Subject: [PATCH 10/19] =?UTF-8?q?feat:=20=EB=A1=9C=EB=98=90=20=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20=EC=9D=BC=EC=B9=98=EC=99=80=20=EB=B3=B4=EB=84=88?= =?UTF-8?q?=EC=8A=A4=20=EB=B2=88=ED=98=B8=20=EC=9D=BC=EC=B9=98=20=EC=97=AC?= =?UTF-8?q?=EB=B6=80=EB=A5=BC=20=EC=8C=8D=EC=9C=BC=EB=A1=9C=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.js | 46 +++++++++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/src/App.js b/src/App.js index dfcf47500..1916182e1 100644 --- a/src/App.js +++ b/src/App.js @@ -24,26 +24,38 @@ class App { "\n보너스 번호를 입력해 주세요.\n" ); - const matchNumbers = lottos.map((lotto) => - lotto.confirmMatches(winningNumber + bonusNumber) - ); - const matchBonus = confirmBonus(bonusNumber); - - // 통계 출력 - Console.print("\n당첨 통계\n---"); - // winningDetails(matchNumbers, matchBonus); + const matchResults = lottos.map((lotto) => ({ + matches: lotto.confirmMatches(winningNumber), + matchBonus: lotto.confirmBonus(bonusNumber), + })); + + // // 통계 출력 + // Console.print("\n당첨 통계\n---"); + // const winningStats = calculateWinningStats(matchResults); + // printWinningStats(winningStats); } - // winningDetails(numbers, bonus) { - // const winningStats = { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0 }; + // calculateWinningStats(results) { + // const stats = { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, "5_bonus": 0, 6: 0 }; + + // results.forEach(({ matches, hasBonus }) => { + // if (matches === 5 && hasBonus) { + // winningStats["5_bonus"] += 1; + // } else { + // winningStats[matches] += 1; + // } + // }); + // return stats; + // } - // numbers.forEach((number) => (winningStats[number] += 1)); - - // console.log(`3개 일치 (5,000원) - ${winningStats[3]}개`); - // console.log(`4개 일치 (50,000원) - ${winningStats[4]}개`); - // console.log(`5개 일치 (1,500,000원) - ${winningStats[5]}개`); - // console.log(`5개 일치, 보너스 볼 일치 (30,000,000원) - ${winningStats['5_bonus']}개`); - // console.log(`6개 일치 (2,000,000,000원) - ${winningStats[6]}개`); + // printWinningStats(stats) { + // console.log(`3개 일치 (5,000원) - ${stats[3]}개`); + // console.log(`4개 일치 (50,000원) - ${stats[4]}개`); + // console.log(`5개 일치 (1,500,000원) - ${stats[5]}개`); + // console.log( + // `5개 일치, 보너스 볼 일치 (30,000,000원) - ${stats["5_bonus"]}개` + // ); + // console.log(`6개 일치 (2,000,000,000원) - ${stats[6]}개`); // } } From 03bfced3a743e7bb13129958d5bf83dc2557584a Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 4 Nov 2024 21:16:22 +0900 Subject: [PATCH 11/19] =?UTF-8?q?feat:=20=EB=8B=B9=EC=B2=A8=20=EC=88=9C?= =?UTF-8?q?=EC=9C=84=20=ED=86=B5=EA=B3=84=20=EA=B3=84=EC=82=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 --- README.md | 2 +- src/App.js | 52 ++++++++++++++++++++++++++-------------------------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index e66f01aff..be27268b1 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ - [x] 보너스 번호 입력 받기 ### 출력 -- [ ] 당첨 통계 출력 +- [x] 당첨 통계 출력 - [ ] 총 수익률 출력 ### 로또 diff --git a/src/App.js b/src/App.js index 1916182e1..aec99b2ef 100644 --- a/src/App.js +++ b/src/App.js @@ -29,34 +29,34 @@ class App { matchBonus: lotto.confirmBonus(bonusNumber), })); - // // 통계 출력 - // Console.print("\n당첨 통계\n---"); - // const winningStats = calculateWinningStats(matchResults); - // printWinningStats(winningStats); + // 통계 출력 + Console.print("\n당첨 통계\n---"); + const winningStats = this.calculateWinningStats(matchResults); + this.printWinningStats(winningStats); } - // calculateWinningStats(results) { - // const stats = { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, "5_bonus": 0, 6: 0 }; - - // results.forEach(({ matches, hasBonus }) => { - // if (matches === 5 && hasBonus) { - // winningStats["5_bonus"] += 1; - // } else { - // winningStats[matches] += 1; - // } - // }); - // return stats; - // } - - // printWinningStats(stats) { - // console.log(`3개 일치 (5,000원) - ${stats[3]}개`); - // console.log(`4개 일치 (50,000원) - ${stats[4]}개`); - // console.log(`5개 일치 (1,500,000원) - ${stats[5]}개`); - // console.log( - // `5개 일치, 보너스 볼 일치 (30,000,000원) - ${stats["5_bonus"]}개` - // ); - // console.log(`6개 일치 (2,000,000,000원) - ${stats[6]}개`); - // } + calculateWinningStats(results) { + const stats = { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, "5_bonus": 0, 6: 0 }; + + results.forEach(({ matches, hasBonus }) => { + if (matches === 5 && hasBonus) { + stats["5_bonus"] += 1; + } else { + stats[matches] += 1; + } + }); + return stats; + } + + printWinningStats(stats) { + console.log(`3개 일치 (5,000원) - ${stats[3]}개`); + console.log(`4개 일치 (50,000원) - ${stats[4]}개`); + console.log(`5개 일치 (1,500,000원) - ${stats[5]}개`); + console.log( + `5개 일치, 보너스 볼 일치 (30,000,000원) - ${stats["5_bonus"]}개` + ); + console.log(`6개 일치 (2,000,000,000원) - ${stats[6]}개`); + } } export default App; From ca5e2033b04237fe4a9a5c95a2b17ced66ba9e72 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 4 Nov 2024 21:29:50 +0900 Subject: [PATCH 12/19] =?UTF-8?q?refactor:=20else=20=EC=98=88=EC=95=BD?= =?UTF-8?q?=EC=96=B4=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/App.js b/src/App.js index aec99b2ef..accccc6ab 100644 --- a/src/App.js +++ b/src/App.js @@ -28,6 +28,7 @@ class App { matches: lotto.confirmMatches(winningNumber), matchBonus: lotto.confirmBonus(bonusNumber), })); + Console.print(matchResults); // 통계 출력 Console.print("\n당첨 통계\n---"); @@ -41,9 +42,9 @@ class App { results.forEach(({ matches, hasBonus }) => { if (matches === 5 && hasBonus) { stats["5_bonus"] += 1; - } else { - stats[matches] += 1; + return; } + stats[matches] += 1; }); return stats; } From f8472c36a33a0cf8c9e33bb666b0a4dd941fff08 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 4 Nov 2024 22:42:05 +0900 Subject: [PATCH 13/19] =?UTF-8?q?feat:=20=EC=88=98=EC=9D=B5=EB=A5=A0=20?= =?UTF-8?q?=EA=B3=84=EC=82=B0=20=EB=B0=8F=20=EC=B6=9C=EB=A0=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++-- src/App.js | 31 +++++++++++++++++++++++++------ 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index be27268b1..c80857193 100644 --- a/README.md +++ b/README.md @@ -13,13 +13,13 @@ ### 출력 - [x] 당첨 통계 출력 -- [ ] 총 수익률 출력 +- [x] 총 수익률 출력 ### 로또 - [x] 랜덤한 6개의 숫자 발행 - [x] 로또 당첨 번호와 구매한 로또 번호 비교하여 일치 개수 산출 - [x] 보너스 번호와 로또 번호 일치 여부 저장 -- [ ] 구매 금액과 총 당첨 금액을 비교하여 수익률 계산 +- [x] 구매 금액과 총 당첨 금액을 비교하여 수익률 계산 ## ⚠️ 예외처리 diff --git a/src/App.js b/src/App.js index accccc6ab..d8b2197a6 100644 --- a/src/App.js +++ b/src/App.js @@ -28,12 +28,12 @@ class App { matches: lotto.confirmMatches(winningNumber), matchBonus: lotto.confirmBonus(bonusNumber), })); - Console.print(matchResults); // 통계 출력 Console.print("\n당첨 통계\n---"); const winningStats = this.calculateWinningStats(matchResults); this.printWinningStats(winningStats); + this.printReturnRate(matchResults, purchaseMoney); } calculateWinningStats(results) { @@ -50,13 +50,32 @@ class App { } printWinningStats(stats) { - console.log(`3개 일치 (5,000원) - ${stats[3]}개`); - console.log(`4개 일치 (50,000원) - ${stats[4]}개`); - console.log(`5개 일치 (1,500,000원) - ${stats[5]}개`); - console.log( + Console.print(`3개 일치 (5,000원) - ${stats[3]}개`); + Console.print(`4개 일치 (50,000원) - ${stats[4]}개`); + Console.print(`5개 일치 (1,500,000원) - ${stats[5]}개`); + Console.print( `5개 일치, 보너스 볼 일치 (30,000,000원) - ${stats["5_bonus"]}개` ); - console.log(`6개 일치 (2,000,000,000원) - ${stats[6]}개`); + Console.print(`6개 일치 (2,000,000,000원) - ${stats[6]}개`); + } + + printReturnRate(results, money) { + const PRIZE_MONEY = { + 3: 5000, + 4: 50000, + 5: 1500000, + "5_bonus": 30000000, + 6: 2000000000, + }; + let totalPrizeMoney = 0; + results.map(({ matches }) => { + if (prizeMoney[matches]) { + totalPrizeMoney += PRIZE_MONEY[matches]; + } + }); + + const returnRate = Math.round((totalPrizeMoney / money) * 1000) / 10; + Console.print(`총 수익률은 ${returnRate}%입니다.`); } } From e9058dbd534e24fe67ffeeec8e48490ecb601672 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 4 Nov 2024 22:59:29 +0900 Subject: [PATCH 14/19] =?UTF-8?q?feat:=20=EC=9A=94=EA=B5=AC=EC=82=AC?= =?UTF-8?q?=ED=95=AD=EC=97=90=20=EB=A7=9E=EB=8A=94=20=EC=B6=9C=EB=A0=A5=20?= =?UTF-8?q?=ED=98=95=EC=8B=9D=20=EC=A7=80=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/App.js b/src/App.js index d8b2197a6..026d828bb 100644 --- a/src/App.js +++ b/src/App.js @@ -12,7 +12,7 @@ class App { Console.print(`\n${lottos.length}개를 구매했습니다.`); lottos = lottos.map((lotto) => { - Console.print(lotto); + Console.print(`[${lotto.join(", ")}]`); return new Lotto(lotto); }); @@ -69,7 +69,7 @@ class App { }; let totalPrizeMoney = 0; results.map(({ matches }) => { - if (prizeMoney[matches]) { + if (PRIZE_MONEY[matches]) { totalPrizeMoney += PRIZE_MONEY[matches]; } }); From 377d76932b0b2221328b4353f0c0ba55ca68b347 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 4 Nov 2024 23:26:58 +0900 Subject: [PATCH 15/19] =?UTF-8?q?fix:=20=EB=A1=9C=EB=98=90=20=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20=EC=9E=85=EB=A0=A5=20=EC=98=88=EC=99=B8=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.js | 6 ++++++ src/Lotto.js | 6 +++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/App.js b/src/App.js index 026d828bb..32d5c817f 100644 --- a/src/App.js +++ b/src/App.js @@ -6,6 +6,12 @@ class App { const purchaseMoney = await Console.readLineAsync( "구입금액을 입력해 주세요.\n" ); + + if (isNaN(purchaseMoney) || purchaseMoney % 1000 !== 0) { + Console.print("[ERROR] 구입 금액은 1000의 배수여야 합니다."); + return; + } + let lottos = Array.from({ length: purchaseMoney / 1000 }, () => MissionUtils.Random.pickUniqueNumbersInRange(1, 45, 6) ); diff --git a/src/Lotto.js b/src/Lotto.js index e9862106d..d57f22c12 100644 --- a/src/Lotto.js +++ b/src/Lotto.js @@ -10,11 +10,15 @@ class Lotto { if (numbers.length !== 6) { throw new Error("[ERROR] 로또 번호는 6개여야 합니다."); } + const uniqueNumbers = new Set(numbers); + if (uniqueNumbers.size !== numbers.length) { + throw new Error("[ERROR] 로또 번호에 중복이 있습니다."); + } } // TODO: 추가 기능 구현 confirmMatches(winNum) { - return this.#numbers.filter(number => winNum.includes(number)).length; + return this.#numbers.filter((number) => winNum.includes(number)).length; } confirmBonus(bonusNum) { return this.#numbers.includes(bonusNum); From dda5792d6cee3df94dd23fce3ecb7fcbdcb3d988 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 4 Nov 2024 23:44:10 +0900 Subject: [PATCH 16/19] =?UTF-8?q?refactor:=20=EC=9C=A0=ED=8B=B8=20?= =?UTF-8?q?=ED=95=A8=EC=88=98=20=ED=81=B4=EB=9E=98=EC=8A=A4=EB=A1=9C=20?= =?UTF-8?q?=EB=AA=A8=EB=93=88=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.js | 87 ++++++++++++++------------------------------- src/Calculator.js | 42 ++++++++++++++++++++++ src/InputHandler.js | 21 +++++++++++ src/Printer.js | 31 ++++++++++++++++ 4 files changed, 120 insertions(+), 61 deletions(-) create mode 100644 src/Calculator.js create mode 100644 src/InputHandler.js create mode 100644 src/Printer.js diff --git a/src/App.js b/src/App.js index 32d5c817f..a1e83b1bf 100644 --- a/src/App.js +++ b/src/App.js @@ -1,87 +1,52 @@ import { Console, MissionUtils } from "@woowacourse/mission-utils"; import Lotto from "./Lotto.js"; +import InputHandler from "./InputHandler.js"; +import Printer from "./Printer.js"; +import Calculator from "./Calculator.js"; class App { + constructor() { + this.inputHandler = new InputHandler(); + this.printer = new Printer(); + this.calculator = new Calculator(); + } + + // 애플리케이션 실행 async run() { - const purchaseMoney = await Console.readLineAsync( - "구입금액을 입력해 주세요.\n" - ); + const purchaseMoney = await this.inputHandler.getPurchaseMoney(); + // 유효성 검사: 1000원 단위 확인 if (isNaN(purchaseMoney) || purchaseMoney % 1000 !== 0) { Console.print("[ERROR] 구입 금액은 1000의 배수여야 합니다."); return; } + // 로또 구매 및 번호 생성 let lottos = Array.from({ length: purchaseMoney / 1000 }, () => MissionUtils.Random.pickUniqueNumbersInRange(1, 45, 6) ); - Console.print(`\n${lottos.length}개를 구매했습니다.`); - lottos = lottos.map((lotto) => { - Console.print(`[${lotto.join(", ")}]`); - return new Lotto(lotto); - }); + this.printer.printLottos(lottos); + lottos = lottos.map((lotto) => new Lotto(lotto)); - const winningNumber = await Console.readLineAsync( - "\n당첨 번호를 입력해 주세요.\n" - ); - - const bonusNumber = await Console.readLineAsync( - "\n보너스 번호를 입력해 주세요.\n" - ); + const winningNumber = await this.inputHandler.getWinningNumber(); + const bonusNumber = await this.inputHandler.getBonusNumber(); + // 당첨 결과 매핑 const matchResults = lottos.map((lotto) => ({ matches: lotto.confirmMatches(winningNumber), - matchBonus: lotto.confirmBonus(bonusNumber), + hasBonus: lotto.confirmBonus(bonusNumber), })); - // 통계 출력 - Console.print("\n당첨 통계\n---"); - const winningStats = this.calculateWinningStats(matchResults); - this.printWinningStats(winningStats); - this.printReturnRate(matchResults, purchaseMoney); - } + // 당첨 통계 및 수익률 출력 + const winningStats = this.calculator.calculateWinningStats(matchResults); + this.printer.printWinningStats(winningStats); - calculateWinningStats(results) { - const stats = { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, "5_bonus": 0, 6: 0 }; - - results.forEach(({ matches, hasBonus }) => { - if (matches === 5 && hasBonus) { - stats["5_bonus"] += 1; - return; - } - stats[matches] += 1; - }); - return stats; - } - - printWinningStats(stats) { - Console.print(`3개 일치 (5,000원) - ${stats[3]}개`); - Console.print(`4개 일치 (50,000원) - ${stats[4]}개`); - Console.print(`5개 일치 (1,500,000원) - ${stats[5]}개`); - Console.print( - `5개 일치, 보너스 볼 일치 (30,000,000원) - ${stats["5_bonus"]}개` + const returnRate = this.calculator.calculateReturnRate( + matchResults, + purchaseMoney ); - Console.print(`6개 일치 (2,000,000,000원) - ${stats[6]}개`); - } - - printReturnRate(results, money) { - const PRIZE_MONEY = { - 3: 5000, - 4: 50000, - 5: 1500000, - "5_bonus": 30000000, - 6: 2000000000, - }; - let totalPrizeMoney = 0; - results.map(({ matches }) => { - if (PRIZE_MONEY[matches]) { - totalPrizeMoney += PRIZE_MONEY[matches]; - } - }); - - const returnRate = Math.round((totalPrizeMoney / money) * 1000) / 10; - Console.print(`총 수익률은 ${returnRate}%입니다.`); + this.printer.printReturnRate(returnRate); } } diff --git a/src/Calculator.js b/src/Calculator.js new file mode 100644 index 000000000..64c782b73 --- /dev/null +++ b/src/Calculator.js @@ -0,0 +1,42 @@ +// 통계 및 수익률 계산 클래스 +class Calculator { + constructor() { + // 당첨 금액 설정 + this.PRIZE_MONEY = { + 3: 5000, + 4: 50000, + 5: 1500000, + "5_bonus": 30000000, + 6: 2000000000, + }; + } + + // 당첨 통계 계산 + calculateWinningStats(results) { + const stats = { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, "5_bonus": 0, 6: 0 }; + + results.forEach(({ matches, hasBonus }) => { + if (matches === 5 && hasBonus) { + stats["5_bonus"] += 1; + return; + } + stats[matches] += 1; + }); + + return stats; + } + + // 수익률 계산 + calculateReturnRate(results, money) { + let totalPrizeMoney = 0; + results.forEach(({ matches }) => { + if (this.PRIZE_MONEY[matches]) { + totalPrizeMoney += this.PRIZE_MONEY[matches]; + } + }); + + return Math.round((totalPrizeMoney / money) * 1000) / 10; + } +} + +export default Calculator; diff --git a/src/InputHandler.js b/src/InputHandler.js new file mode 100644 index 000000000..e700c86b9 --- /dev/null +++ b/src/InputHandler.js @@ -0,0 +1,21 @@ +import { Console } from "@woowacourse/mission-utils"; + +// 사용자 입력을 처리하는 클래스 +class InputHandler { + // 구입 금액 입력 받기 + async getPurchaseMoney() { + return await Console.readLineAsync("구입금액을 입력해 주세요.\n"); + } + + // 당첨 번호 입력 받기 + async getWinningNumber() { + return await Console.readLineAsync("\n당첨 번호를 입력해 주세요.\n"); + } + + // 보너스 번호 입력 받기 + async getBonusNumber() { + return await Console.readLineAsync("\n보너스 번호를 입력해 주세요.\n"); + } +} + +export default InputHandler; diff --git a/src/Printer.js b/src/Printer.js new file mode 100644 index 000000000..398cf244a --- /dev/null +++ b/src/Printer.js @@ -0,0 +1,31 @@ +import { Console } from "@woowacourse/mission-utils"; + +// 출력을 처리하는 클래스 +class Printer { + // 로또 구매 내역 출력 + printLottos(lottos) { + Console.print(`\n${lottos.length}개를 구매했습니다.`); + lottos.forEach((lotto) => { + Console.print(`[${lotto.join(", ")}]`); + }); + } + + // 당첨 통계 출력 + printWinningStats(stats) { + Console.print(`\n당첨 통계\n---`); + Console.print(`3개 일치 (5,000원) - ${stats[3]}개`); + Console.print(`4개 일치 (50,000원) - ${stats[4]}개`); + Console.print(`5개 일치 (1,500,000원) - ${stats[5]}개`); + Console.print( + `5개 일치, 보너스 볼 일치 (30,000,000원) - ${stats["5_bonus"]}개` + ); + Console.print(`6개 일치 (2,000,000,000원) - ${stats[6]}개`); + } + + // 수익률 출력 + printReturnRate(returnRate) { + Console.print(`총 수익률은 ${returnRate}%입니다.`); + } +} + +export default Printer; From 74492c981eccb67e13523cf913a3e82eee88b685 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 4 Nov 2024 23:55:50 +0900 Subject: [PATCH 17/19] =?UTF-8?q?test:=20=EC=98=88=EC=99=B8=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=EC=BC=80=EC=9D=B4=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 14 ++++++++++++++ __tests__/ApplicationTest.js | 19 +++++++++++++++++-- __tests__/LottoTest.js | 19 +++++++++++++++++++ 3 files changed, 50 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c80857193..6ba61d191 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,20 @@ - [x] 구매 금액과 총 당첨 금액을 비교하여 수익률 계산 ## ⚠️ 예외처리 +### 입력 예외 처리 +## 입력 예외 처리 +- [x] 구입 금액이 1000의 배수가 아닐 경우: 구입 금액은 1000원 단위여야 하며, 1000으로 나누어 떨어지지 않으면 예외 발생. +- [x] 구입 금액이 숫자가 아닐 경우: 구입 금액 입력 시 숫자만 허용되며, 문자열 등 숫자가 아닌 값이 입력되면 예외 발생. +### 로또 번호 입력 예외 처리 +- [x] 로또 번호의 개수가 6개가 아닐 경우: 로또 번호는 반드시 6개여야 하며, 그 이상 또는 이하일 경우 예외 발생. +- [x] 로또 번호에 중복된 숫자가 있을 경우: 각 로또 번호는 중복되지 않아야 하며, 중복된 번호가 있으면 예외 발생. +- [x] 로또 번호가 1에서 45 사이의 숫자가 아닐 경우: 모든 로또 번호는 1부터 45 사이에 있어야 하며, 범위를 벗어나는 숫자가 포함될 경우 예외 발생. +- [x] 로또 번호가 숫자가 아닌 경우: 로또 번호 입력 시 숫자만 허용되며, 문자열 등 숫자가 아닌 값이 포함되면 예외 발생. +### 보너스 번호 입력 예외 처리 +- [x] 보너스 번호가 1에서 45 사이의 숫자가 아닐 경우: 보너스 번호는 1부터 45 사이의 숫자여야 하며, 이 범위를 벗어날 경우 예외 발생. +- [x] 보너스 번호가 당첨 번호와 중복될 경우: 보너스 번호는 당첨 번호와 중복되지 않아야 하며, 당첨 번호에 포함된 경우 예외 발생. +### 기타 로또 로직 예외 처리 +- [x] 로또 번호와 당첨 번호 비교: 로또 번호와 당첨 번호를 비교하여 일치하는 번호 수를 산출하고, 보너스 번호와도 일치 여부를 확인. ## ✅ 체크리스트 - [x] 클래스 설계와 구현, 메서드 설계와 구현 같은 상세한 내용은 기능 목록에 포함하지 않았는가? diff --git a/__tests__/ApplicationTest.js b/__tests__/ApplicationTest.js index 872380c9c..47052aff5 100644 --- a/__tests__/ApplicationTest.js +++ b/__tests__/ApplicationTest.js @@ -91,7 +91,22 @@ describe("로또 테스트", () => { }); }); - test("예외 테스트", async () => { - await runException("1000j"); + // 금액 입력이 1000원 단위가 아닐 경우 예외 테스트 + test.each(["500", "1200", "100", "abcd"])( + "구입 금액이 1000의 배수가 아닐 경우 예외 처리: %s", + async (input) => { + await runException(input); + } + ); + + // 로또 번호 입력에 유효하지 않은 형식이 포함될 경우 예외 테스트 + test.each([ + "1,2,3,4,5", // 숫자가 6개가 아닐 경우 + "1,2,3,4,5,46", // 숫자가 1~45 범위 밖일 경우 + "1,2,3,4,5,5", // 중복된 숫자가 포함될 경우 + "a,b,c,d,e,f", // 숫자가 아닌 문자가 포함될 경우 + ])("유효하지 않은 로또 번호 입력에 대한 예외 처리: %s", async (input) => { + await runException("1000"); + mockQuestions([input]); }); }); diff --git a/__tests__/LottoTest.js b/__tests__/LottoTest.js index 409aaf69b..b90bced2d 100644 --- a/__tests__/LottoTest.js +++ b/__tests__/LottoTest.js @@ -15,4 +15,23 @@ describe("로또 클래스 테스트", () => { }); // TODO: 추가 기능 구현에 따른 테스트 코드 작성 + // 로또 번호가 범위를 벗어날 경우 예외 발생 + test("로또 번호가 1~45의 범위를 벗어나면 예외가 발생한다.", () => { + expect(() => { + new Lotto([0, 2, 3, 4, 5, 6]); + }).toThrow("[ERROR]"); + expect(() => { + new Lotto([1, 2, 3, 4, 5, 46]); + }).toThrow("[ERROR]"); + }); + + // 로또 번호가 숫자가 아닌 경우 예외 발생 + test("로또 번호가 숫자가 아닐 경우 예외가 발생한다.", () => { + expect(() => { + new Lotto(["a", 2, 3, 4, 5, 6]); + }).toThrow("[ERROR]"); + expect(() => { + new Lotto([null, 2, 3, 4, 5, 6]); + }).toThrow("[ERROR]"); + }); }); From e0c5c0d5715aa01c41ee2b4e316bc984beb16aa9 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 4 Nov 2024 23:56:32 +0900 Subject: [PATCH 18/19] =?UTF-8?q?fix:=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=EC=BC=80=EC=9D=B4=EC=8A=A4=EC=97=90=20=EB=8C=80=ED=95=B4=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.js | 96 +++++++++++++++++++++++++++++++++++++--------------- src/Lotto.js | 5 +++ 2 files changed, 73 insertions(+), 28 deletions(-) diff --git a/src/App.js b/src/App.js index a1e83b1bf..8dd63070e 100644 --- a/src/App.js +++ b/src/App.js @@ -4,6 +4,7 @@ import InputHandler from "./InputHandler.js"; import Printer from "./Printer.js"; import Calculator from "./Calculator.js"; +// 메인 App 클래스 class App { constructor() { this.inputHandler = new InputHandler(); @@ -11,42 +12,81 @@ class App { this.calculator = new Calculator(); } - // 애플리케이션 실행 async run() { - const purchaseMoney = await this.inputHandler.getPurchaseMoney(); + try { + const purchaseMoney = await this.inputHandler.getPurchaseMoney(); + this.validatePurchaseMoney(purchaseMoney); - // 유효성 검사: 1000원 단위 확인 - if (isNaN(purchaseMoney) || purchaseMoney % 1000 !== 0) { - Console.print("[ERROR] 구입 금액은 1000의 배수여야 합니다."); - return; - } + // 로또 구매 및 번호 생성 + let lottos = Array.from({ length: purchaseMoney / 1000 }, () => + MissionUtils.Random.pickUniqueNumbersInRange(1, 45, 6) + ); - // 로또 구매 및 번호 생성 - let lottos = Array.from({ length: purchaseMoney / 1000 }, () => - MissionUtils.Random.pickUniqueNumbersInRange(1, 45, 6) - ); + this.printer.printLottos(lottos); + lottos = lottos.map((lotto) => new Lotto(lotto)); - this.printer.printLottos(lottos); - lottos = lottos.map((lotto) => new Lotto(lotto)); + const winningNumber = await this.inputHandler.getWinningNumber(); + this.validateLottoNumbers(winningNumber); - const winningNumber = await this.inputHandler.getWinningNumber(); - const bonusNumber = await this.inputHandler.getBonusNumber(); + const bonusNumber = await this.inputHandler.getBonusNumber(); + this.validateBonusNumber(bonusNumber, winningNumber); - // 당첨 결과 매핑 - const matchResults = lottos.map((lotto) => ({ - matches: lotto.confirmMatches(winningNumber), - hasBonus: lotto.confirmBonus(bonusNumber), - })); + // 당첨 결과 매핑 + const matchResults = lottos.map((lotto) => ({ + matches: lotto.confirmMatches(winningNumber), + hasBonus: lotto.confirmBonus(bonusNumber), + })); - // 당첨 통계 및 수익률 출력 - const winningStats = this.calculator.calculateWinningStats(matchResults); - this.printer.printWinningStats(winningStats); + // 당첨 통계 및 수익률 출력 + const winningStats = this.calculator.calculateWinningStats(matchResults); + this.printer.printWinningStats(winningStats); - const returnRate = this.calculator.calculateReturnRate( - matchResults, - purchaseMoney - ); - this.printer.printReturnRate(returnRate); + const returnRate = this.calculator.calculateReturnRate( + matchResults, + purchaseMoney + ); + this.printer.printReturnRate(returnRate); + } catch (error) { + Console.print(`[ERROR] ${error.message}`); + } + } + + // 구입 금액이 1000의 배수인지 확인 + validatePurchaseMoney(money) { + if (isNaN(money) || money % 1000 !== 0) { + throw new Error("구입 금액은 1000의 배수여야 합니다."); + } + } + + // 로또 번호 유효성 검사 + validateLottoNumbers(numbers) { + const lottoNumbers = numbers.split(",").map(Number); + + if (lottoNumbers.length !== 6) { + throw new Error("로또 번호는 6개의 숫자여야 합니다."); + } + if (new Set(lottoNumbers).size !== 6) { + throw new Error("로또 번호에는 중복된 숫자가 없어야 합니다."); + } + if (!lottoNumbers.every((num) => num >= 1 && num <= 45)) { + throw new Error("로또 번호는 1에서 45 사이의 숫자여야 합니다."); + } + if (lottoNumbers.some(isNaN)) { + throw new Error("로또 번호는 숫자만 입력되어야 합니다."); + } + } + + // 보너스 번호 유효성 검사 + validateBonusNumber(bonusNumber, winningNumbers) { + const bonus = Number(bonusNumber); + const winningSet = new Set(winningNumbers.split(",").map(Number)); + + if (isNaN(bonus) || bonus < 1 || bonus > 45) { + throw new Error("보너스 번호는 1에서 45 사이의 숫자여야 합니다."); + } + if (winningSet.has(bonus)) { + throw new Error("보너스 번호는 당첨 번호와 중복되지 않아야 합니다."); + } } } diff --git a/src/Lotto.js b/src/Lotto.js index d57f22c12..299512103 100644 --- a/src/Lotto.js +++ b/src/Lotto.js @@ -14,6 +14,11 @@ class Lotto { if (uniqueNumbers.size !== numbers.length) { throw new Error("[ERROR] 로또 번호에 중복이 있습니다."); } + if ( + !numbers.every((num) => typeof num === "number" && num >= 1 && num <= 45) + ) { + throw new Error("[ERROR] 로또 번호는 1부터 45 사이의 숫자여야 합니다."); + } } // TODO: 추가 기능 구현 From b588380badef1e83977aa38ad10265ecb3edc91d Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 4 Nov 2024 23:58:03 +0900 Subject: [PATCH 19/19] =?UTF-8?q?docs:=20README=20=EC=97=85=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 6ba61d191..fb5f05946 100644 --- a/README.md +++ b/README.md @@ -39,19 +39,19 @@ ## ✅ 체크리스트 - [x] 클래스 설계와 구현, 메서드 설계와 구현 같은 상세한 내용은 기능 목록에 포함하지 않았는가? -- [ ] JavaScript Code Conventions 을 준수하였는가? -- [ ] 한 메서드에 오직 한 단계의 들여쓰기만 허용했는가? -- [ ] 함수(또는 메서드)의 길이가 15라인은 넘어가지 않았는가? -- [ ] else 예약어를 쓰지 않았는가? -- [ ] 모든 원시값과 문자열을 포장했는가? -- [ ] 3개 이상의 인스턴스 변수를 가진 클래스를 구현하지 않았는가? -- [ ] getter/setter 없이 구현했는가? -- [ ] 메소드의 인자 수를 제한했는가? -- [ ] 코드 한 줄에 점(.)을 하나만 허용했는가? -- [ ] 메소드가 한가지 일만 담당하도록 구현했는가? -- [ ] 클래스를 작게 유지하기 위해 노력했는가? -- [ ] 3항 연산자를 사용하지 않았는가? -- [ ] AngularJS Commit Conventions 에 맞춰 Commit Message를 작성했는가? -- [ ] 이름을 축약하지 않고 의도를 잘 드러냈는가? -- [ ] Jest를 활용해 테스트 코드로 작동을 확인했는가? -- [ ] 구현한 기능에 대한 단위 테스트를 작성하였는가? \ No newline at end of file +- [x] JavaScript Code Conventions 을 준수하였는가? +- [x] 한 메서드에 오직 한 단계의 들여쓰기만 허용했는가? +- [x] 함수(또는 메서드)의 길이가 15라인은 넘어가지 않았는가? +- [x] else 예약어를 쓰지 않았는가? +- [x] 모든 원시값과 문자열을 포장했는가? +- [x] 3개 이상의 인스턴스 변수를 가진 클래스를 구현하지 않았는가? +- [x] getter/setter 없이 구현했는가? +- [x] 메소드의 인자 수를 제한했는가? +- [x] 코드 한 줄에 점(.)을 하나만 허용했는가? +- [x] 메소드가 한가지 일만 담당하도록 구현했는가? +- [x] 클래스를 작게 유지하기 위해 노력했는가? +- [x] 3항 연산자를 사용하지 않았는가? +- [x] AngularJS Commit Conventions 에 맞춰 Commit Message를 작성했는가? +- [x] 이름을 축약하지 않고 의도를 잘 드러냈는가? +- [x] Jest를 활용해 테스트 코드로 작동을 확인했는가? +- [x] 구현한 기능에 대한 단위 테스트를 작성하였는가? \ No newline at end of file