Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
6d50306
doc: 요구 사항 정리
dannysir Jan 10, 2026
1c213e0
feat: input 기능 구현
dannysir Jan 10, 2026
4cb692a
test: Lotto.js 로또 숫자 랜덤 생성
dannysir Jan 10, 2026
b2f332a
test: 로또 숫자 정렬 테스트
dannysir Jan 10, 2026
52bb79d
test: 로또 숫자 정렬 테스트
dannysir Jan 10, 2026
90a1dca
refactor: 기존 테스트 유틸 함수 분리
dannysir Jan 10, 2026
f378253
test: User 객체 테스트
dannysir Jan 10, 2026
dec37bb
feat: User 객체 구현
dannysir Jan 10, 2026
63418a7
refactor: Lotto 관련 상수 값 정리
dannysir Jan 10, 2026
f34971b
test: Game 관련 예외처리 테스트 추가
dannysir Jan 10, 2026
9c66455
feat: 예외처리 로직 구현
dannysir Jan 10, 2026
efea380
refactor: 예외처리를 위한 문자열 추가
dannysir Jan 10, 2026
2be60ed
test: Game 정상입력 테스트 추가
dannysir Jan 10, 2026
8053daf
feat: Game 객체에 get 메서드 추가
dannysir Jan 10, 2026
97221ee
test: Calculator 테스트
dannysir Jan 10, 2026
4d4fb9a
feat: Calculator 객체 구현
dannysir Jan 10, 2026
d6046e2
feat: App.js에 주요 로직 조합
dannysir Jan 10, 2026
67cec0d
feat: 예외처리 해결을 위핸 input 메서드 추가
dannysir Jan 10, 2026
f9271b6
doc: 도전 목표 정립
dannysir Jan 10, 2026
ebd6bcd
refactor: 로또 단위가 아닌 금액 입력 가능 로직 추가
dannysir Jan 10, 2026
406f9f9
test: App.js 관련 테스트 코드 추가
dannysir Jan 10, 2026
1b13ad4
refactor: 음수 금액 입력시 예외 처리 추가
dannysir Jan 10, 2026
df29079
refactor: 코드 스타일 및 함수명 수정
dannysir Jan 10, 2026
e612e36
test: Random 모듈에 문제가 있을 경우 예외처리
dannysir Jan 10, 2026
c1b2e95
feat: Lotto.js 객체에서 Random 모듈에 관한 예외처리를 하도록 수정
dannysir Jan 10, 2026
c3d7dd7
doc: 도전 과제 완료 항목 정리
dannysir Jan 10, 2026
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
140 changes: 139 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,139 @@
# javascript-planetlotto-precourse
# 요구 사항 정리 내역 (13시 15분 작성)

## 예제

```text
구입금액을 입력해 주세요.
1000

2개를 구매했습니다.
[8, 11, 13, 21, 22]
[1, 3, 6, 14, 22]

당첨 번호를 입력해 주세요.
1, 2, 3, 4, 5

보너스 번호 번호를 입력해 주세요.
6

당첨 통계
---
5개 일치 (100,000,000원) - 0개
4개 일치, 보너스 번호 일치 (10,000,000원) - 0개
4개 일치 (1,500,000원) - 0개
3개 일치, 보너스 번호 일치 (500,000원) - 0개
2개 일치, 보너스 번호 일치 (5,000원) - 1개
0개 일치 (0원) - 1개
```

## 기능 요구 사항

- TDD 등 개발 방식이 드라나게
- `InputView` `OutputView`의 기존 메서드 수정 X

### 게임 로직

- 로또
- 번호는 1~30
- 중복되진 않는 5개의 숫자를 뽑음
- 당첨 번호는 중복되지 않는 5개의 숫자 + 보너스 번호 1개
- 개당 500원
- 당첨
- 1등 : 5개 번호 / 100_000_000
- 2등 : 4개 번호 + 보너스 번호 / 10_000_000
- 3등 : 4개 번호 / 1_500_000
- 4등 : 3개 번호 + 보너스 / 500_000
- 5등 : 2개 번호 + 보너스 / 5_000
- 예외 처리
- `[ERROR]` 메시지와 함께 에러 발생
- 해당 지점부터 다시 입력

## 기능 요구 사항

### 입력

- 1차 입력
- 구입 금액
- 2차 입력
- 당첨 번호
- `, 쉼표`로 구분
- 3차 입력
- 보너스 번호

> 예외 발생시 해당 지점부터 재입력

### 출력

- 1차 출력
- 로또 구매 갯수
- 로또 번호
- 숫자를 오름차순으로 정렬
- 배열로 보여줌
- 2차 출력
- 당첨 통계

## 도전 과제

### 리펙토링

- 작동은 유지
- 코드 품질을 높이는 방향
- 기능 확장에 용이한 구조

# 개인 도전 목표 (15시 11분 작성)

## 현재 상황

15시 현재 우선 테스트 코드를 모두 통과하는 기능을 구현했습니다.

그러나 현재 기능 추가에 용이한 구조가 아니라고 생각합니다.

### 고려되는 부분

- `만약 로또 금액이 500원이 아니라 1000원으로 바뀌어도 괜찮은가`
- `사용자가 로또 금액 단위의 금액을 입력하지 않으면 예외처리가 과연 맞는가`
- `로또 숫자가 0~100까지 가능하게 바뀔 수 있는가`
- `로또가 5개의 숫자가 아니라 6개의 숫자를 뽑아도 괜찮은가`

여기에 더해서 현재 코드가 그 동안 지켜왔던 규칙을 지키고 있는지도 확인 해야 한다.

- `함수의 길이가 15줄을 넘어가는가`
- `들여쓰기 depth가 3을 넘어가는가`
- `각각의 모듈의 테스트 코드를 잘 작성했는가`
- `테스트 코드에서 잡지 못하는 엣지 케이스가 있는가`

## 도전 목표

### 목표 1

로또 조건이 변경되도 실행될 대응이 가능한 코드를 만들자

`constants.js`의 수정을 통해 변경에 용이한 구조를 만들자

- 로또 숫자 범위 변경 가능
- 로또 가격 변경 가능
- 사용자가 로또 범위 외의 돈을 입력했을 때 정상 작동 로직 추가

### 목표 2

그 동안 지켜왔던 JavaScript 코드 규칙을 지켰는지 확인하자

- 함수 길이
- `if/else` 지양
- 들여쓰기 depth 3 이하

### 목표 3

- 각각의 비즈니스 로직에 해당하는 테스트 코드를 잘 작성했는지 확인
- 테스트 하지 못한 엣지 케이스 확인

### 완료 항목 (16:30 작성)

- `Random` 모듈이 문제가 생길 경우 예외 처리 진행
- 로또 관련 상수값 분리 완료
- 로또 단위가 아닌 금액을 입력해도 가능하도록 수정
- 부족한 테스트 코드 추가
- `App.test.js` - 정상 입력 엔드 투 엔드 테스트 추가
- `User.test.js` - 부족한 금액의 경우 테스트 추가
- `Lotto.test.js` - `Random`모듈이 문제가 생겼을 경우를 위한 테스트 추가
- `if/else` 문 제거
43 changes: 1 addition & 42 deletions __tests__/ApplicationTest.js
Original file line number Diff line number Diff line change
@@ -1,46 +1,5 @@
import App from "../src/App.js";
import { MissionUtils } from "@woowacourse/mission-utils";

const mockQuestions = (inputs) => {
MissionUtils.Console.readLineAsync = jest.fn();

MissionUtils.Console.readLineAsync.mockImplementation(() => {
const input = inputs.shift();

return Promise.resolve(input);
});
};

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

const getLogSpy = () => {
const logSpy = jest.spyOn(MissionUtils.Console, "print");
logSpy.mockClear();
return logSpy;
};

const runException = async (input) => {
// given
const logSpy = getLogSpy();

const RANDOM_NUMBERS_TO_END = [1, 2, 3, 4, 5];
const INPUT_NUMBERS_TO_END = ["500", "1,2,3,4,5", "6"];

mockRandoms([RANDOM_NUMBERS_TO_END]);
mockQuestions([input, ...INPUT_NUMBERS_TO_END]);

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

// then
expect(logSpy).toHaveBeenCalledWith(expect.stringContaining("[ERROR]"));
};
import {getLogSpy, mockQuestions, mockRandoms, runException} from "../testUtils.js";

describe("로또 테스트", () => {
beforeEach(() => {
Expand Down
5 changes: 5 additions & 0 deletions doc/checkList.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
## View 관련 추가 조건

당첨 결과는 `Map` 자료 구조 이용

에러의 경우 `printErrorMessage` 메서드 이용
43 changes: 42 additions & 1 deletion src/App.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,46 @@
import {InputView, OutputView} from "./view.js";
import {User} from "./User/User.js";
import {Game} from "./Game/Game.js";
import {Calculator} from "./Calculator/Calculator.js";

class App {
async run() {}
#user;
#game;
#calculator;

async run() {
this.#user = await this.#inputMoney();
OutputView.printPurchasedLottos(this.#user.getMyLottos());
this.#game = await this.#inputLottoNumbers();
this.#calculator = new Calculator(...this.#game.getWinNumInfo(), this.#user.getMyLottos());
OutputView.printResult(this.#calculator.getResult());
}

async #inputMoney() {
while (true) {
try{
const money = await InputView.askAmountMoney();
return new User(money);
}catch (e) {
OutputView.printErrorMessage(e.message);
}
}
}

async #inputLottoNumbers() {
while (true) {
try {
const winNum = await InputView.askWinningLotto();
const bonusNum = await InputView.askBonusNumber();

return new Game(winNum, bonusNum);
}catch (e) {
OutputView.printErrorMessage(e.message);
}
}
}
}

export default App;

console.log(parseInt('100m', 10));
45 changes: 45 additions & 0 deletions src/App.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import App from "../src/App.js";
import {getLogSpy, mockQuestions, mockRandoms} from "../testUtils.js";
import {LOTTO} from "./constants.js";

describe("로또 테스트", () => {
beforeEach(() => {
jest.restoreAllMocks();
});

test("기능 테스트", async () => {
const logSpy = getLogSpy();

mockRandoms([
[1, 2, 3, 4, 5],
[1, 2, 3, 4, 6],
[1, 2, 3, 6, 7],
[1, 2, 6, 7, 8],
]);
mockQuestions([String(LOTTO.PRICE * 4), "1,2,3,4,5", "6"]);

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

const logs = [
"4개를 구매했습니다.",
"[1, 2, 3, 4, 5]",
"[1, 2, 3, 4, 6]",
"[1, 2, 3, 6, 7]",
"[1, 2, 6, 7, 8]",
"당첨 통계",
"5개 일치 (100,000,000원) - 1개",
"4개 일치, 보너스 번호 일치 (10,000,000원) - 1개",
"4개 일치 (1,500,000원) - 0개",
"3개 일치, 보너스 번호 일치 (500,000원) - 1개",
"2개 일치, 보너스 번호 일치 (5,000원) - 1개",
"0개 일치 (0원) - 0개",
];

logs.forEach((log) => {
expect(logSpy).toHaveBeenCalledWith(expect.stringContaining(log));
});
});


});
57 changes: 57 additions & 0 deletions src/Calculator/Calculator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
export class Calculator {
#winNum;
#bonus;
#lottos;
#result = new Map();

constructor(winNum, bonus, lottos) {
this.#winNum = new Set(winNum);
this.#bonus = bonus;
this.#lottos = lottos;

this.#calcLottos();
}

getResult() {
const copyResult = new Map();
for (const [key, value] of this.#result.entries()) {
copyResult.set(key, value);
}
return copyResult;
}

#calcLottos() {
this.#lottos.forEach(lotto => {
const result = this.#calcLottoPrice(lotto);
if (this.#result.has(result)) {
this.#result.set(result, this.#result.get(result) + 1);
return;
}
this.#result.set(result, 1);
});
}

#calcLottoPrice(lottoNum) {
const [cnt, bonus] = this.#countLottoNum(lottoNum);
if (cnt === 5) return 1;
if (cnt === 4 && bonus) return 2;
if (cnt === 4) return 3;
if (cnt === 3 && bonus) return 4;
if (cnt === 2 && bonus) return 5;
return 0;
}

#countLottoNum(lottoNums) {
let cnt = 0;
let bonus = false;
lottoNums.forEach(num => {
if (this.#winNum.has(num)) {
cnt++;
}
if (num === this.#bonus) {
bonus = true;
}
});
return [cnt, bonus];
}
}
21 changes: 21 additions & 0 deletions src/Calculator/Calculator.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import {Calculator} from "./Calculator.js";

describe('Calculator 정상 테스트', () => {
test('1등 3등 0등', () => {
const winNum = [1, 2, 3, 4, 5];
const bonus = 6;
const myLottos = [
[1, 2, 3, 4, 5],
[1, 2, 3, 4, 10],
[1, 11, 12, 13, 14]
];
const answer = new Map();
answer.set(0, 1);
answer.set(1, 1);
answer.set(3, 1);

const calculator = new Calculator(winNum, bonus, myLottos);

expect(calculator.getResult()).toEqual(answer);
});
});
Loading