-
Notifications
You must be signed in to change notification settings - Fork 192
[로또] 김예안 미션 제출합니다. #159
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
[로또] 김예안 미션 제출합니다. #159
Changes from all commits
9520db8
3007883
77751f1
c0f7eff
d9c0adc
da9aee7
438809b
135c9b2
0d3758f
8e29c27
59fe725
fde8f5a
be434ca
deb3e6b
59b020a
4154dc9
cf7772b
9f4d022
f52ed68
1822d80
e641f4e
7747feb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,67 @@ | ||
| # javascript-lotto-precourse | ||
| ## 기능 요구사항 분석 | ||
|
|
||
| ### 입력/출력 | ||
|
|
||
| **입력** | ||
|
|
||
| - [x] 로또 구입 금액을 입력받는다. | ||
| - [x] 당첨 번호와 보너스 번호를 입력받는다. | ||
| - [x] 잘못된 값을 입력했을 경우 메시지와 함께 에러를 발생시킨 후 | ||
| 해당 지점부터 다시 입력 받는다. | ||
|
|
||
| **출력** | ||
|
|
||
| - [x] 발행할 로또 개수를 바탕으로 로또 수량 및 번호 리스트들을 출력한다. | ||
| - [x] 당첨 내역을 출력한다. | ||
| - [x] 수익률을 출력한다. | ||
| - [x] 당첨 내역 및 수익률을 출력하고 로또 게임을 종료한다. | ||
|
|
||
| <br> | ||
|
|
||
| ### 기능 | ||
|
|
||
| **로또 발행** | ||
|
|
||
| - [x] 로또 구입 금액만큼 발행할 로또 개수를 정한다. | ||
| - [x] 로또 개수만큼 로또를 발행한다. | ||
| - [x] 발행한 로또 번호를 오름차순으로 정렬한다. | ||
|
|
||
| **결과 계산** | ||
|
|
||
| - [x] 발행된 로또와 당첨 번호를 비교하여 당첨 내역을 계산한다. | ||
| - [x] 당첨 내역을 바탕으로 총 수익률을 계산한다. | ||
|
|
||
| 수익률은 소수점 둘째 자리에서 반올림한다. | ||
|
|
||
|
|
||
| <br> | ||
|
|
||
| ### 에러 | ||
|
|
||
| **입력값** | ||
|
|
||
| - [x] 공백인 경우 | ||
| - 로또 구입 금액 | ||
| - [x] 구입 금액의 단위가 1,000으로 나누어 떨어지지 않을 경우 | ||
| - [x] 숫자가 아닌 문자가 포함된 경우 | ||
| - 당첨 번호 | ||
| - [x] 구분자가 쉼표(,)가 아닌 경우 | ||
| - [x] 숫자가 아닌 문자가 포함된 경우 | ||
| - [x] 구분자 형식이 잘못된 경우 (e.g. ‘1,2,3,’ ‘1,2,,3’) | ||
| - [x] 구분자 사이에 공백이 포함된 경우 (e.g. ‘1, 2,3’ ‘1,2 ,3’) | ||
| - [x] 입력된 숫자가 6개가 아닌 경우 | ||
| - [x] 중복된 번호가 입력된 경우 | ||
| - [x] 1~45 범위의 숫자가 아닌 경우 | ||
| - 보너스 번호 | ||
| - [x] 숫자가 아닌 문자가 포함된 경우 | ||
|
|
||
| **로또 발행** | ||
|
|
||
| - [x] 중복된 번호가 발행된 경우 | ||
| - [x] 발행된 번호가 6개가 아닌 경우 | ||
|
|
||
| <br> | ||
|
|
||
| ### 엣지 케이스 | ||
|
|
||
| - [x] 당첨되지 않았을 때 (2개 이하로 번호 일치) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| import { ERROR_MESSAGE, TERMS } from '../src/utils/constants'; | ||
| import InputValidator from '../src/utils/InputValidator'; | ||
|
|
||
| describe('입력값 검증 테스트 (로또 구입 금액)', () => { | ||
| it.each([ | ||
| ['공백이 입력된 경우', ' ', ERROR_MESSAGE.BLANK_INPUT], | ||
| [ | ||
| '숫자가 아닌 문자가 포함된 경우', | ||
| 'lotto', | ||
| ERROR_MESSAGE.PURCHASE_AMOUNT_TYPE, | ||
| ], | ||
| [ | ||
| '1,000원으로 나누어 떨어지지 않는 경우', | ||
| '900', | ||
| ERROR_MESSAGE.PURCHASE_AMOUNT_UNIT, | ||
| ], | ||
| ])('%s', (_, input, expectedError) => { | ||
| expect(() => | ||
| InputValidator.runValidate(TERMS.PURCHASE_AMOUNT, input) | ||
| ).toThrow(expectedError); | ||
| }); | ||
| }); | ||
|
|
||
| describe('입력값 검증 테스트 (당첨 번호)', () => { | ||
| it.each([ | ||
| ['공백이 입력된 경우', ' ', ERROR_MESSAGE.BLANK_INPUT], | ||
| [ | ||
| '구분자가 쉼표(,)가 아닌 경우', | ||
| '1,2;3,4,5,6', | ||
| ERROR_MESSAGE.WINNING_NUMBER_TYPE, | ||
| ], | ||
| [ | ||
| '구분자 형식이 잘못된 경우', | ||
| ',1,2,3,4,5,6', | ||
| ERROR_MESSAGE.WINNING_NUMBER_TYPE, | ||
| ], | ||
| [ | ||
| '숫자가 아닌 문자가 포함된 경우', | ||
| '1,2,h,4,5,6', | ||
| ERROR_MESSAGE.WINNING_NUMBER_TYPE, | ||
| ], | ||
| [ | ||
| '숫자가 6개가 아닌 경우', | ||
| '1,2,3,4,5', | ||
| ERROR_MESSAGE.WINNING_NUMBER_LENGTH, | ||
| ], | ||
| [ | ||
| '중복된 숫자가 포함된 경우', | ||
| '1,2,3,4,5,5', | ||
| ERROR_MESSAGE.WINNING_NUMBER_DUPLICATE, | ||
| ], | ||
| [ | ||
| '1~45 범위 밖의 숫자가 포함된 경우', | ||
| '1,2,3,4,5,50', | ||
| ERROR_MESSAGE.WINNING_NUMBER_RANGE, | ||
| ], | ||
| ])('%s', (_, input, expectedError) => { | ||
| expect(() => | ||
| InputValidator.runValidate(TERMS.WINNING_NUMBER, input) | ||
| ).toThrow(expectedError); | ||
| }); | ||
| }); | ||
|
|
||
| describe('입력값 검증 테스트 (보너스 번호)', () => { | ||
| it.each([ | ||
| ['공백이 입력된 경우', ' ', ERROR_MESSAGE.BLANK_INPUT], | ||
| [ | ||
| '숫자가 아닌 문자가 포함된 경우', | ||
| 'lotto', | ||
| ERROR_MESSAGE.BONUS_NUMBER_TYPE, | ||
| ], | ||
| ])('%s', (_, input, expectedError) => { | ||
| expect(() => InputValidator.runValidate(TERMS.BONUS_NUMBER, input)).toThrow( | ||
| expectedError | ||
| ); | ||
| }); | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,18 +1,15 @@ | ||
| import Lotto from "../src/Lotto"; | ||
| import Lotto from '../src/model/Lotto'; | ||
|
|
||
| describe("로또 클래스 테스트", () => { | ||
| test("로또 번호의 개수가 6개가 넘어가면 예외가 발생한다.", () => { | ||
| describe('로또 클래스 테스트', () => { | ||
| test('로또 번호의 개수가 6개가 넘어가면 예외가 발생한다.', () => { | ||
| expect(() => { | ||
| new Lotto([1, 2, 3, 4, 5, 6, 7]); | ||
| }).toThrow("[ERROR]"); | ||
| }).toThrow('[ERROR]'); | ||
| }); | ||
|
|
||
| // TODO: 테스트가 통과하도록 프로덕션 코드 구현 | ||
| test("로또 번호에 중복된 숫자가 있으면 예외가 발생한다.", () => { | ||
| test('로또 번호에 중복된 숫자가 있으면 예외가 발생한다.', () => { | ||
| expect(() => { | ||
| new Lotto([1, 2, 3, 4, 5, 5]); | ||
| }).toThrow("[ERROR]"); | ||
| }).toThrow('[ERROR]'); | ||
| }); | ||
|
|
||
| // TODO: 추가 기능 구현에 따른 테스트 코드 작성 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. index 9abfb89..622caa7 100644
--- a/__tests__/LottoTest.js
+++ b/__tests__/LottoTest.js
@@ -1,6 +1,8 @@
import Lotto from '../src/model/Lotto';
describe('로또 클래스 테스트', () => {
+ const lotto = new Lotto([1, 2, 3, 4, 5, 6]);
+
test('로또 번호의 개수가 6개가 넘어가면 예외가 발생한다.', () => {
expect(() => {
new Lotto([1, 2, 3, 4, 5, 6, 7]);
@@ -12,4 +14,13 @@ describe('로또 클래스 테스트', () => {
new Lotto([1, 2, 3, 4, 5, 5]);
}).toThrow('[ERROR]');
});
+
+ test('로또 번호와 당첨 번호를 비교하여 일치하는 숫자 갯수를 반환합니다.', () => {
+ expect(lotto.calculateMatchCount([4, 5, 6, 7, 8, 9])).toBe(3);
+ });
+
+ test('로또 번호에 보너스 번호가 포함되었는지 확인합니다.', () => {
+ expect(lotto.hasBonusNumber(8)).toBe(false);
+ expect(lotto.hasBonusNumber(3)).toBe(true);
+ });
});테스트코드가 더 있다면 좋았을거 같습니다. |
||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,80 @@ | ||
| import { Console } from '@woowacourse/mission-utils'; | ||
| import LottoMachine from './model/LottoMachine.js'; | ||
| import { IO_MESSAGE, SEPERATOR, TERMS } from './utils/constants.js'; | ||
| import Input from './view/Input.js'; | ||
| import Parser from './utils/Parser.js'; | ||
| import RankCalculator from './model/RankCalculator.js'; | ||
| import StatisticsView from './view/StatisticsView.js'; | ||
|
|
||
| class App { | ||
| async run() {} | ||
| #issuedLottos = []; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 필드로 구현하신 이유가있을까요 ? run 내부에서 메소드 흐름이 그대로 가서 파라미터로 넘겨주어도 직관적으로 보였을것 같아서요 ! |
||
|
|
||
| /** | ||
| * 1. 구입 금액을 입력받고 숫자로 변환 | ||
| */ | ||
| async getPurchaseAmount() { | ||
| const purchaseAmountStr = await Input.readInputValues( | ||
| TERMS.PURCHASE_AMOUNT, | ||
| IO_MESSAGE.PURCHASE_AMOUNT_INPUT | ||
| ); | ||
| return Number(purchaseAmountStr); | ||
| } | ||
|
|
||
| /** | ||
| * 2. 로또 발행 | ||
| */ | ||
| issueLottos(purchaseAmount) { | ||
| this.#issuedLottos = LottoMachine.run(purchaseAmount); | ||
| } | ||
|
|
||
| /** | ||
| * 3. 발행된 로또 번호를 정렬하여 출력 | ||
| */ | ||
| printIssuedLottos() { | ||
| this.#issuedLottos.forEach((lotto) => { | ||
| const numbers = lotto.getNumbers(); | ||
| numbers.sort((a, b) => a - b); | ||
| Console.print(`[${numbers.join(', ')}]`); | ||
| }); | ||
| } | ||
|
|
||
| /** | ||
| * 4. 당첨 번호와 보너스 번호를 입력받고 파싱 | ||
| */ | ||
| async getWinningNumbers() { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| const winningNumber = await Input.readInputValues( | ||
| TERMS.WINNING_NUMBER, | ||
| IO_MESSAGE.WINNING_NUMBER_INPUT | ||
| ); | ||
| const parsedWinningNumber = Parser.convertToNumberArray( | ||
| winningNumber, | ||
| SEPERATOR.COMMA | ||
| ); | ||
|
|
||
| const bonusNumber = await Input.readInputValues( | ||
| TERMS.BONUS_NUMBER, | ||
| IO_MESSAGE.BONUS_NUMBER_INPUT | ||
| ); | ||
|
|
||
| return { winningNumber: parsedWinningNumber, bonusNumber }; | ||
| } | ||
|
|
||
| // 전체 프로세스 실행 | ||
| async run() { | ||
| const purchaseAmount = await this.getPurchaseAmount(); | ||
| this.issueLottos(purchaseAmount); | ||
| this.printIssuedLottos(); | ||
|
|
||
| const { winningNumber, bonusNumber } = await this.getWinningNumbers(); | ||
|
|
||
| const rankCounts = RankCalculator.calculate( | ||
| this.#issuedLottos, | ||
| winningNumber, | ||
| bonusNumber | ||
| ); | ||
|
|
||
| StatisticsView.printResult(rankCounts, purchaseAmount); | ||
| } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 함수의 선언 순서도 중요할수 있다고 들었던것 같습니다.. run 함수가 전반적인 코드의 시작점이라 가장 상단에 존재해야 아무래도 보기 좋을것 같긴합니다 !! 흐름을 읽고 내부 구현을 들여다 보기 마련인데 최하단까지 내려가야하니까요! |
||
| } | ||
|
|
||
| export default App; | ||
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| import { ERROR_MESSAGE, LOTTO_RULES } from '../utils/constants.js'; | ||
|
|
||
| class Lotto { | ||
| #numbers; | ||
|
|
||
| constructor(numbers) { | ||
| this.#validate(numbers); | ||
| this.#numbers = numbers; | ||
| } | ||
|
|
||
| #validate(numbers) { | ||
| if (numbers.length !== LOTTO_RULES.TICKET_NUMBER_COUNT) { | ||
| throw new Error(ERROR_MESSAGE.GENERATED_LOTTO_COUNT); | ||
| } | ||
|
|
||
| if (new Set(numbers).size !== numbers.length) { | ||
| throw new Error(ERROR_MESSAGE.GENERATED_LOTTO_DUPLICATE); | ||
| } | ||
| } | ||
|
|
||
| getNumbers() { | ||
| return [...this.#numbers]; | ||
| } | ||
|
|
||
| calculateMatchCount(winningNumber) { | ||
| return this.#numbers.filter((number) => winningNumber.includes(number)) | ||
| .length; | ||
| } | ||
|
|
||
| hasBonusNumber(bonusNumber) { | ||
| return this.#numbers.includes(Number(bonusNumber)); | ||
| } | ||
| } | ||
|
|
||
| export default Lotto; |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,49 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||
| import { Console, MissionUtils } from '@woowacourse/mission-utils'; | ||||||||||||||||||||||||||||||||||||||||||||||||
| import { IO_MESSAGE, LOTTO_RULES } from '../utils/constants.js'; | ||||||||||||||||||||||||||||||||||||||||||||||||
| import Parser from '../utils/Parser.js'; | ||||||||||||||||||||||||||||||||||||||||||||||||
| import Lotto from './Lotto.js'; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| class LottoMachine { | ||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||
| * 구입 가능한 로또 티켓 수 계산 | ||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||
| static #determineLottoNumber(purchaseAmount) { | ||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||||||||||||||||||||||||||||||||||||||||||||
| return Parser.getPurchaseCount(purchaseAmount); | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||
| * 한 개의 로또 번호 생성 (e.g. [8, 21, 23, 41, 42, 43]) | ||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||
| static #generateLottoNumbers() { | ||||||||||||||||||||||||||||||||||||||||||||||||
| return MissionUtils.Random.pickUniqueNumbersInRange( | ||||||||||||||||||||||||||||||||||||||||||||||||
| LOTTO_RULES.MIN_NUMBER, | ||||||||||||||||||||||||||||||||||||||||||||||||
| LOTTO_RULES.MAX_NUMBER, | ||||||||||||||||||||||||||||||||||||||||||||||||
| LOTTO_RULES.TICKET_NUMBER_COUNT | ||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||
| * 로또 티켓 발행 | ||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||
| static #issueLottoTickets(purchaseCount) { | ||||||||||||||||||||||||||||||||||||||||||||||||
| const issuedLottos = []; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| Array.from({ length: purchaseCount }).forEach(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||
| const lottoNumbers = this.#generateLottoNumbers(); | ||||||||||||||||||||||||||||||||||||||||||||||||
| const lotto = new Lotto(lottoNumbers); | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| issuedLottos.push(lotto); | ||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| return issuedLottos; | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+28
to
+39
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| static run(purchaseAmount) { | ||||||||||||||||||||||||||||||||||||||||||||||||
| const purchaseCount = this.#determineLottoNumber(purchaseAmount); | ||||||||||||||||||||||||||||||||||||||||||||||||
| Console.print(IO_MESSAGE.PURCHASE_COUNT_OUTPUT(purchaseCount)); | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| return this.#issueLottoTickets(purchaseCount); | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+41
to
+47
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 해당 메서드는 구매 개수를 출력하고, 로또를 반환해주는 동작을 하는 것 같습니다 이에 더해서, run이라는 메서드 명이 해당 메서드의 동작을 완전히 설명해주지는 못하는 것 같습니다 출력하는 로직은 해당 클래스가 아니라 다른 곳에서 관리하는 것은 어떠신가요?! |
||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| export default LottoMachine; | ||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
체크리스트를 만들고 꼼꼼히 작성하셨네요!!
꼼꼼하신 성격이 코드에도 느껴집니다!