Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
48b38d0
docs(README.md): 요구사항 정리
seonjeong Nov 3, 2025
b7dda72
feat(App#run): 기본 테스트 통과를 위한 input, output에 대한 하드 코딩
seonjeong Nov 3, 2025
364005c
feat(InputView#readCost): 구입금액 입력
seonjeong Nov 3, 2025
cfe5bfd
feat(App#run): 변수로부터 출력 메시지 얻기
seonjeong Nov 3, 2025
4ae17de
feat(OutputView): 출력기능 OutputView 로 이동
seonjeong Nov 3, 2025
45f1411
test(Cost): 입력받은 구입 금액 확인
seonjeong Nov 3, 2025
b2af7c1
feat(Cost): 입력 받은 구입 금액 확인
seonjeong Nov 3, 2025
84e8536
test(Cost): 문자열 포함
seonjeong Nov 3, 2025
f244411
feat(Cost): 문자열 포함 예외 테스트
seonjeong Nov 3, 2025
601cd7c
refactor(Cost): 문자열 예외 테스트
seonjeong Nov 3, 2025
4584bc8
test(Cost): 입력 받은 구입 금액으로부터 추첨 횟수 처리
seonjeong Nov 3, 2025
ccc4ec9
feat(Cost): 입력 받은 구입 금액으로부터 추첨 횟수 처리
seonjeong Nov 3, 2025
920d631
test(Cost): 예외 테스트: 추첨 횟수로 딱 나누어지지 않는 경우
seonjeong Nov 3, 2025
292f99b
feat(Cost): 투입금액이 추천 횟수에 맞춰 입급되지 않으면 예외처리
seonjeong Nov 3, 2025
cea16bb
feat(App): count 를 Cost 로 부터 얻기
seonjeong Nov 3, 2025
33841bb
test(Lottos): n 번 추천 횟수를 넣으면 로또 번호이 n개 나온다
seonjeong Nov 3, 2025
8830299
feat(Lottos): n 번 추천 횟수를 넣으면 로또 번호이 n개 나온다
seonjeong Nov 3, 2025
eee8f77
refactor(Lotto): 1-45까지의 숫자중에서 6개를 뽑아서 lottos 변수, get numbers를만든다
seonjeong Nov 3, 2025
98ed51a
feat(Lotto): numbers 변수에 접근하게 get 추가
seonjeong Nov 3, 2025
85ef2b5
feat(Lotto): 로또 번호에 중복된 숫자가 있으면 예외가 발생한다.
seonjeong Nov 3, 2025
3de7855
feat(App): Lottos 이용해서 numbers 얻기
seonjeong Nov 3, 2025
764ee5e
refactor(App): 로또 구매 수 출력 코드 위치 조정
seonjeong Nov 3, 2025
5ab3104
test(Draw): 당첨 번호와 보너스 번호를 넣으면 값을 얻는다
seonjeong Nov 3, 2025
a29028b
feat(Draw): 당첨 번호와 보너스 번호를 넣으면 값을 얻는다
seonjeong Nov 3, 2025
6025b80
refactor(Draw): winningNumbers와 bonusNumber 받은 값을 파싱한다
seonjeong Nov 3, 2025
7173733
test(Draw#validateWinningNumbers): 입력된 당첨 번호에 대한 검증을 처리한다
seonjeong Nov 3, 2025
1ed28b0
feat(Draw#validateWinningNumbers): 입력된 당첨 번호에 대한 검증을 처리한다
seonjeong Nov 3, 2025
5184813
test(Draw#bonusNumbers): 입력된 보너스 번호에 대한 검증을 한다
seonjeong Nov 3, 2025
2d6ea29
feat(Draw#bonusNumbers): 입력된 보너스 번호에 대한 검증을 한다
seonjeong Nov 3, 2025
b291aea
feat(InputView): 당첨번호, 보너스 번호를 입력 받기를 App.run 에서 실행
seonjeong Nov 3, 2025
45d757b
feat(App): 당첨 결과 및 수익금 계산을 Draw 에서 처리하는 것을 하드코딩하여 임시처리
seonjeong Nov 3, 2025
60985f2
test(Draw): 당첨 결과를 뽑는다
seonjeong Nov 3, 2025
2468d75
feat(Draw.draw): draw method 에서 처리하도록 하드코딩
seonjeong Nov 3, 2025
e2fc35a
test(Draw): 당첨번호, 보너스 번호를 Draw에 넘겨주는 형태변경
seonjeong Nov 3, 2025
0b1a975
feat(Draw): 당첨번호와 보너스 번호를 받는 함수 형태를 변경함
seonjeong Nov 3, 2025
554ef8f
test(Draw): 뽑은 로또 번호를 set 한다
seonjeong Nov 3, 2025
eef4751
feat(Draw): 뽑은 로또 번호를 set 한다
seonjeong Nov 3, 2025
f2ba185
feat(Draw#draw): 등수에 따른 추첨 함수 체크하여 winningResult 변수에 반영
seonjeong Nov 3, 2025
ee7d8a7
refactor(Draw#places): 각 등수에 대한 당첨 조건 함수를 따로 뺌
seonjeong Nov 3, 2025
687ad6b
refactor(Draw#calculateWinningResults): winningResults 값 계산 함수 추출
seonjeong Nov 3, 2025
c68802d
feat(Draw#calculateReturnOnInvestment): 수익금 계산하기
seonjeong Nov 3, 2025
4c3b54b
refactor(Draw#prizes): prizes 데이터 타입 변경
seonjeong Nov 3, 2025
9c669bf
feat(App.run): Draw 에 얻은 lotto 번호들 넘여서 계산함
seonjeong Nov 3, 2025
d06101f
test(LottoController): 예외 테스트: 재실행 테스트
seonjeong Nov 3, 2025
d46a214
feat(LottoController): 예외 테스트: 재실행 테스트
seonjeong Nov 3, 2025
3e3030c
refactor(App): 예외처리시 무한 반복해서 다시 시작
seonjeong Nov 3, 2025
fbba9fe
refactor(App): 예외 처리 후 재시작을 lottoController instance 를 새로 만들지 않고 처리
seonjeong Nov 3, 2025
14af3ba
test(App): 무한 반복 오류 수정
seonjeong Nov 3, 2025
678e786
feat(LottoController): 구입금액, 뽑은 로또 번호까지 예외 처리의 경우 완료된 곳까지 데이터 유지
seonjeong Nov 3, 2025
c7446fc
test(App): 예외 테스트: 재실행 테스트 - 당첨 번호, 보너스 번호 오류:
seonjeong Nov 3, 2025
05b3868
test(Draw): 당첨 번호, 보너스 번호 설정을 인스턴스 생성시가 아닌 개별 처리
seonjeong Nov 3, 2025
a7f79e8
feat(LottoController): 당첨번호, 보너스 번호 오류시 재실행을 위해 코드 수정
seonjeong Nov 3, 2025
65f7b29
feat(Draw): 당첨번호, 보너스 번호 개별 처리
seonjeong Nov 3, 2025
0b4f358
fix(LottoController): 보너스 번호 오류시 보너스 번호만 받도록 하기 위해 draw 인스턴스 재생성 금지
seonjeong Nov 3, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,31 @@
# javascript-lotto-precourse
- 입력/출력/예외 처리 기본 기능
- 입력받기
- 출력하기
- 예외처리

- 입/출력 값 처리
- 입력: 입력 파싱 & Validating & 예외처리
- 금액 -> 횟수
- 로또 번호
- 보너스 번호
- 출력

- 로또 기능
- 로또 번호 뽑기
- 1회차
- n회차
- 당첨 결과 계산

- prize
- 몇등까지 있는가?
- 각 등수에 따른 당첨 기준 & 수령액

- 로또 번호 맞추기
- 1회차
- n회차 반복

- 수익금 계산
-


70 changes: 70 additions & 0 deletions __tests__/ApplicationTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,74 @@ describe("로또 테스트", () => {
test("예외 테스트", async () => {
await runException("1000j");
});

test("예외 테스트: 재실행 테스트 - 구입금액 오류", async () => {
try {
mockRandoms([
[8, 21, 23, 41, 42, 43],
[3, 5, 11, 16, 32, 38],
[7, 11, 16, 35, 36, 44],
[1, 8, 11, 31, 41, 42],
[13, 14, 16, 38, 42, 45],
[7, 11, 30, 40, 42, 43],
[2, 13, 22, 32, 38, 45],
[1, 3, 5, 14, 22, 45],
]);
mockQuestions(["8a", "8000", "1,2,3,4,5,6", "7"]);

const app = new App();
await app.run();

expect(MissionUtils.Console.readLineAsync).toHaveBeenCalledWith('구입금액을 입력해 주세요.');
expect(MissionUtils.Console.readLineAsync).toHaveBeenCalledTimes(4);
} catch(e) {
expect(() => app.toThrow());
}
});

test("예외 테스트: 재실행 테스트 - 당첨 번호 오류", async () => {
try {
mockRandoms([
[8, 21, 23, 41, 42, 43],
[3, 5, 11, 16, 32, 38],
[7, 11, 16, 35, 36, 44],
[1, 8, 11, 31, 41, 42],
[13, 14, 16, 38, 42, 45],
[7, 11, 30, 40, 42, 43],
[2, 13, 22, 32, 38, 45],
[1, 3, 5, 14, 22, 45],
]);
mockQuestions(["8000", "1,2,3,4,5,a", "1,2,3,4,5,6", "7"]);

const app = new App();
await app.run();

expect(MissionUtils.Console.readLineAsync).toHaveBeenCalledTimes(4);
} catch(e) {
expect(() => app.toThrow());
}
});

test("예외 테스트: 재실행 테스트 - 보너스 번호 오류", async () => {
try {
mockRandoms([
[8, 21, 23, 41, 42, 43],
[3, 5, 11, 16, 32, 38],
[7, 11, 16, 35, 36, 44],
[1, 8, 11, 31, 41, 42],
[13, 14, 16, 38, 42, 45],
[7, 11, 30, 40, 42, 43],
[2, 13, 22, 32, 38, 45],
[1, 3, 5, 14, 22, 45],
]);
mockQuestions(["8000", "1,2,3,4,5,6", "a", "7"]);

const app = new App();
await app.run();

expect(MissionUtils.Console.readLineAsync).toHaveBeenCalledTimes(4);
} catch(e) {
expect(() => app.toThrow());
}
});
});
49 changes: 49 additions & 0 deletions __tests__/CostTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { Console } from '@woowacourse/mission-utils';

import Cost from '../src/Cost';

const mockCost = (input) => {
Console.readLineAsync = jest.fn();

Console.readLineAsync.mockImplementation(() => {
return Promise.resolve(input);
});
};

describe('Cost 클래스 테스트', () => {
test('입력 받은 구입 금액 확인', () => {
const costMessage = '3000';
mockCost(costMessage);

const costInstance = new Cost(costMessage);
const { cost } = costInstance;

expect(cost).toBe(3000);
});

test('입력 받은 구입 금액으로부터 추첨 횟수 처리', () => {
const costMessage = '3000';
mockCost(costMessage);

const costInstance = new Cost(costMessage);
const { count } = costInstance;

expect(count).toBe(3);
});

test("예외 테스트: 문자열 포함", () => {
const costMessage = '1000j';

mockCost(costMessage);

expect(() => new Cost(costMessage)).toThrow();
});

test("예외 테스트: 추첨 횟수로 딱 나누어지지 않는 경우", () => {
const costMessage = '1100';

mockCost(costMessage);

expect(() => new Cost(costMessage)).toThrow();
});
});
107 changes: 107 additions & 0 deletions __tests__/DrawTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import Draw from "../src/Draw.js";

describe("Draw 클래스 테스트", () => {
test("당첨 번호와 보너스 번호를 넣으면 값을 얻는다", () => {
const drawnInstance = new Draw({
tickets: [],
});

drawnInstance.setWinning("1,2,3,4,5,6");
drawnInstance.setBonus("7");

drawnInstance.draw();

const { drawnNumber: { winning, bonus } } = drawnInstance;

expect(winning).toEqual([1, 2, 3, 4, 5, 6]);
expect(bonus).toBe(7);
});

test("당첨 결과를 뽑는다", () => {
const drawnInstance = new Draw({
tickets: [
[1, 2, 3, 14, 15, 16],
[1, 2, 3, 4, 5, 9],
[1, 2, 3, 4, 5, 7],
[1, 22, 33, 44, 15, 19],
],
});
drawnInstance.setWinning("1,2,3,4,5,6");
drawnInstance.setBonus("7");

drawnInstance.draw();

const { winningResults } = drawnInstance;

expect(winningResults).toEqual([1, 0, 1, 1, 0]);
});

test("뽑은 로또 번호를 set 한다", () => {
const drawnInstance = new Draw({
tickets: [[1, 2, 3, 4, 5, 6]],
});

drawnInstance.setWinning("1,2,3,4,5,6");
drawnInstance.setBonus("7");

drawnInstance.draw();

const { tickets } = drawnInstance;

expect(tickets).toEqual([
[1, 2, 3, 4, 5, 6],
]);
});

test("예외 테스트: wiinningNumbers - 로또 번호는 숫자여야 합니다.", () => {
expect(() => {
const drawnInstance = new Draw({
tickets: [[1, 2, 3, 4, 5, 6]],
});

drawnInstance.setWinning("1,2,3,4,5,a");
drawnInstance.setBonus("7");

drawnInstance.draw();
}).toThrow();
});

test("예외 테스트: wiinningNumbers - 로또 번호는 6개여야 합니다.", () => {
expect(() => {
const drawnInstance = new Draw({
tickets: [[1, 2, 3, 4, 5, 6]],
});

drawnInstance.setWinning("1,2,3,4,5,6,7");
drawnInstance.setBonus("7");

drawnInstance.draw();
}).toThrow();
});

test("예외 테스트: wiinningNumbers - 로또 번호는 1과 45 사이의 숫자여야 합니다.", () => {
expect(() => {
const drawnInstance = new Draw({
tickets: [[1, 2, 3, 4, 5, 6]],
});

drawnInstance.setWinning("1,2,3,4,5,60");
drawnInstance.setBonus("7");

drawnInstance.draw();
}).toThrow();
});

test("예외 테스트: bonusNumbers - 보너스 번호는 숫자여야 합니다.", () => {
expect(() => {
const drawnInstance = new Draw({
tickets: [[1, 2, 3, 4, 5, 6]],
});

drawnInstance.setWinning("1,2,3,4,5,60");
drawnInstance.setBonus("a");

drawnInstance.draw();
}).toThrow();
});
});
34 changes: 34 additions & 0 deletions __tests__/LottosTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Random } from '@woowacourse/mission-utils';

import Lottos from "../src/Lottos";

const mockRandoms = (numbers) => {
Random.pickUniqueNumbersInRange = jest.fn();
numbers.reduce((acc, number) => {
return acc.mockReturnValueOnce(number);
}, Random.pickUniqueNumbersInRange);
};

describe("Lottos 클래스 테스트", () => {
test("n 번 추천 횟수를 넣으면 로또 번호이 n개 나온다", () => {
const count = 8;
const randoms = [
[8, 21, 23, 41, 42, 43],
[3, 5, 11, 16, 32, 38],
[7, 11, 16, 35, 36, 44],
[1, 8, 11, 31, 41, 42],
[13, 14, 16, 38, 42, 45],
[7, 11, 30, 40, 42, 43],
[2, 13, 22, 32, 38, 45],
[1, 3, 5, 14, 22, 45],
];

mockRandoms(randoms);

const lottosInstance = new Lottos(count);
const { numbers } = lottosInstance;

expect(numbers.length).toBe(8);
expect(numbers).toEqual(randoms);
});
});
16 changes: 15 additions & 1 deletion src/App.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
import LottoController from './LottoController.js';

class App {
async run() {}
lottery;

constructor(){
this.lottery = new LottoController();
}
async run() {
const lottery = this.lottery;
try {
await lottery.start();
} catch (err) {
await this.run();
}
}
}

export default App;
46 changes: 46 additions & 0 deletions src/Cost.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
class Cost {
#cost;
#count;

constructor(cost){
cost = this.#validate(cost);
const parsedValue = this.#parse(cost);

this.#cost = parsedValue.cost;
this.#count = parsedValue.count;
}

#isNumber(value){
return !Number.isNaN(Number(value))
}

#validate(cost){
if (!this.#isNumber(cost)) {
throw Error('문자열 포함안됨');
}

const remainder = Number(cost) % 1000;
if (remainder) {
throw Error('1000원 단위로 입력 필요');
}

return cost;
}

#parse(cost){
cost = Number(cost);
const count = cost / 1000;

return { cost, count };
}

get cost() {
return this.#cost;
}

get count() {
return this.#count;
}
}

export default Cost;
Loading