Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
222 changes: 221 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,221 @@
# javascript-planetlotto-precourse
# 프리코스 최종 : 행성 로또
## 📌 문제 소개
우테코 로또 발매기인 행성 로또를 구현하는 것이며, 프리코스에서 학습한 문제 분해, 설계, TDD 등이 코드에 드러내도록 하는 것이 목적이다.

#### 도전 과제
기본 요구사항 충족 후, 아래 중 하나를 선택하여 도전하라
- 리팩터링: 작동은 그대로 유지하면서 코드 품질을 높이는 방향
- 기능 확장: 기본 기능 위에 새로운 기능을 추가하는 방향

이번 문제에서는 특히 입출력인 `view.js`는 기본 메서드를 수정/삭제가 불가하며 필요 시 추가는 가능하다.

로또 : 1 ~ 30

로또 발행 시 -> 중복 X 5개 숫자 뽑기

당첨 번호 추첨 시 -> 중복 X 숫자 5개 뽑기 + 보너스 번호 1개 뽑기

당첨 기준 (1~5등)
- 1등 : 5개 번호 일치 => 100,000,000원
- 2등 : 4개 번호 + 보너스 번호 일치 => 10,000,000원
- 3등 : 4개 번호 일치 => 1,500,000원
- 4등 : 3개 번호 일치 + 보너스 번호 일치 => 500,000원
- 5등 : 2개 번호 일치 + 보너스 번호 일치 => 5,000원

로또 가격 : 500원/장

<br>

## 🚀 실행 방법
```python
npm install
npm run start # 실행
npm run test # 테스트
```
<br>

## ✨ 입출력 요구사항
### 입력
로또 구입 금액을 입력받는다.
```
1000
```

당첨 번호를 입력받는다. 번호는 쉼표(,)를 기준으로 구분한다.
```
1,2,3,4,5
```

보너스 번호를 입력받는다.
```
6
```

<br>

### 출력
발행한 로또 수량 및 번호를 출력한다. 로또 번호는 오름차순으로 정렬하여 보여준다.
```
2개를 구매했습니다.
[8, 11, 13, 21, 22]
[1, 3, 6, 14, 22]
```

당첨 내역을 출력한다.
```
당첨 통계
---
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개
```

예외 상황 시 에러 문구를 출력해야 한다. 단, 에러 문구는 "[ERROR]"로 시작해야 한다.
```
[ERROR] 로또 번호는 1부터 30 사이의 숫자여야 합니다.
```

### 실행 결과 예시
```
구입금액을 입력해 주세요.
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개
```

<br>

## 프로그래밍 요구사항
- Node.js 22.19.0 버전에서 실행 가능해야 한다.
- 프로그램 실행의 시작점은 App.js의 run()이다.
- package.json 파일은 변경할 수 없으며, 제공된 라이브러리와 스타일 라이브러리 이외의 외부 라이브러리는 사용하지 않는다.
- 프로그램 종료 시 process.exit()를 호출하지 않는다.
- 프로그래밍 요구 사항에서 달리 명시하지 않는 한 파일, 패키지 등의 이름을 바꾸거나 이동하지 않는다.
- 자바스크립트 코드 컨벤션을 지키면서 프로그래밍한다.
- 기본적으로 JavaScript Style Guide를 원칙으로 한다.
- 기본으로 제공되는 테스트가 통과해야 한다.
- @woowacourse/mission-utils에서 제공하는 Random 및 Console API를 사용하여 구현해야 한다.
- Random 값 추출은 Random.pickUniqueNumbersInRange()를 활용한다.
- 사용자의 값을 입력 및 출력하려면 Console.readLineAsync()와 Console.print()를 활용한다.

## ✅ 구현 체크리스트

#### 1단계: 로또 구입 금액 입력 받기 (InputView class)
- [ ] 숫자인지 체크 (ERROR)
- [ ] 정수인지 체크 (ERROR)
- [ ] 500원 이상인지 체크 (ERROR)
- [ ] 500원으로 나누어 떨어지는 지 체크 (ERROR)

#### 2단계: 당첨 번호 입력 받기 (InputView class)
- [ ] SEPARATOR(,)가 있는지 체크 (ERROR)
- [ ] split된 배열 요소가 모두 숫자인지 체크 (ERROR)
- [ ] split된 배열 요소가 모두 정수인지 체크 (ERROR)
- [ ] split된 배열 요소가 모두 1~30인지 체크 (ERROR)
- [ ] split된 배열 요소 중 중복된 숫자가 있는지 체크 (ERROR)

#### 3단계: 보너스 번호 입력 받기 (InputView class)
- [ ] 숫자인지 체크 (ERROR)
- [ ] 정수인지 체크 (ERROR)
- [ ] split된 당첨 번호 배열에 포함돼있는지 체크 (ERROR)

#### 4단계: 로또 발행 기계 만들기 (LottoPublisher class)
- [ ] 4-1. 발행할 로또 개수 정하기
- [ ] 로또 개수가 양의 정수인지 체크 (ERROR)

- [ ] 4-2. 로또 번호 계산하기
- [ ] 모든 번호가 1~30인지 체크 (ERROR)
- [ ] 중복된 번호가 있는지 체크 (ERROR)

#### 5단계: 발행한 로또 개수 및 번호 출력하기 (OutputView class)
- [ ] 출력하기

#### 6단계: 당첨 통계 게산하기 (LottoCalculator class)
- [ ] 발행된 로또 번호마다 계산
- [ ] 6-1. 발행 번호와 당첨번호 비교하여 일치하는 개수 계산
- [ ] 정수인지 체크 (ERROR)
- [ ] 0~5인지 체크 (ERROR)

- [ ] 6-2. 보너스 번호와 일치하는 지 계산
- [ ] return 타입이 `booelan`인 지 체크 (ERROR)

- [ ] 6-3. 등수 정하기

- [ ] 6-4. 전체 통계 계산하기

#### 7단계: 당첨 통계 출력하기 (OutputView class)
- [ ] 출력하기

<br>

## 🗂️ 아키텍처
**MVC + shared**
```
src/
├── App.js (Controller) # 전체적인 흐름 제어
├── services/ # 도메인 간 복잡한 흐름 제어
├── domains/ # 비즈니스 로직
├── views.js # 입출력
└── shared/ # 공통 유틸
```

<br>

## ❌ 에러 목록
### 공통

<br>

### 입력

<br>

### 출력

<br>

### 도메인

<br>

## 🧪 테스트
(도메인 별로 몇 개인지도 적기)
- **단위 테스트**: 30개
- **통합 테스트**: 6개 (정상 2개, 예외 4개)
- **재입력 테스트**: 12개

<br>

## 도전 목록
### 1. 에러 시 재입력받기

<br>


## 📚 참고 자료
[MissionUtils 라이브러리 분석](https://quirky-streetcar-a17.notion.site/mission-utils-28c523184d3c80d8904fe0870e5e4181)

[우테코 Clean Code](https://github.com/woowacourse/woowacourse-docs/blob/main/cleancode/pr_checklist.md)

[Commit Convention 정리](https://velog.io/@gaiogo2/Git-%EC%BB%A4%EB%B0%8B-%EC%BB%A8%EB%B2%A4%EC%85%98)

[최종 테스트 대비 개인 참고 자료](https://quirky-streetcar-a17.notion.site/2e1523184d3c805f8e11da2dab1019c1)
26 changes: 26 additions & 0 deletions __tests__/LottoPublisherTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { checkErrorMessage, LOTTO_ERROR } from "../src/shared/index.js";
import { LottoPublisher } from "../src/domains/index.js";

describe("로또 발행기 클래스 테스트", () => {
describe("로또 벌향가 정상 테스트", () => {
test("1. 로또 발행이 정상적으로 작동한다.", () => {
expect(() => {
new LottoPublisher(1000);
}).not.toThrow();
});
});

describe("로또 예외 테스트", () => {
test("1. 로또 금액이 숫자가 아니다.", () => {
expect(() => {
new LottoPublisher('wrong str');
}).toThrow(checkErrorMessage(LOTTO_ERROR.NON_NUMBER));
});

test("2. 로또 금액이 양의 정수가 아니다.", () => {
expect(() => {
new LottoPublisher(500.2);
}).toThrow(checkErrorMessage(LOTTO_ERROR.NOT_POSITIVE_INTEGER));
});
});
});
2 changes: 2 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

64 changes: 63 additions & 1 deletion src/App.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,67 @@
import { InputView, OutputView } from "./views/view.js";
import { LottoPublisher, LottoCalculator, LOTTO_RANKING } from "./domains/index.js";

class App {
async run() {}
async run() {


// 1. 로또 금액 입력받기
const amount = await InputView.askAmount();
OutputView.printSpace();

// 2. 당첨 번호 입력 받기
const winningLotto = await InputView.askWinningLotto();
OutputView.printSpace();

// 3. 보너스 번호 입력 받기
const bonusNumber = await InputView.askBonusNumber();
OutputView.printSpace();

// 4. 로또 발행 기계 만들기
const lottoPublisher = new LottoPublisher(amount);
const lottoArray = lottoPublisher.getLottoArray();

// 5. 발행한 로또 및 개수 번호 출력하기
OutputView.printPurchasedLottos(lottoArray);

OutputView.printSpace();
// 6. 당첨 통계 계산하기
const lottoCalculator = new LottoCalculator();

for (let lotto of lottoArray) {
lottoCalculator.calculateRank(lotto, winningLotto, bonusNumber);
}

const entries = Object.entries(LOTTO_RANKING);


const map = new Map(entries);

let newMap = new Map();
newMap.set(1, 0);
newMap.set(2, 0);
newMap.set(3, 0);
newMap.set(4, 0);
newMap.set(5, 0);
newMap.set(0, 0);

const map1Value = map.get('ONE');
const map2Value = map.get('TWO');
const map3Value = map.get('THREE');
const map4Value = map.get('FOUR');
const map5Value = map.get('FIVE');
const map0Value = map.get('ZERO');
newMap.set(1, map1Value);
newMap.set(2, map2Value);
newMap.set(3, map3Value);
newMap.set(4, map4Value);
newMap.set(5, map5Value);
newMap.set(0, map0Value);


OutputView.printResult(newMap);

}
}

export default App;
5 changes: 5 additions & 0 deletions src/domains/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export { LottoPublisherValidator } from './lottoPublisher/utils/LottoPublisherValidator.js';
export { LottoPublisher } from './lottoPublisher/LottoPublisher.js';

export { LottoCalculator } from './lottoCalculator/LottoCalculator.js';
export { LOTTO_RANKING } from './lottoCalculator/utils/LottoCalculatorConstants.js';
40 changes: 40 additions & 0 deletions src/domains/lottoCalculator/LottoCalculator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// import { LOTTO } from "../../shared/index.js";
import { LOTTO_RANKING } from "./utils/LottoCalculatorConstants.js";

class LottoCalculator {

constructor() {}

calculateRank(lotto, winningLotto, bonusNumber) {
const matchingNumbersLength = this.#calculateWinningNumbersLength(lotto, winningLotto);
const bonusNumberMatching = this.#calculateBonusNumberMatching(lotto, bonusNumber);

if (matchingNumbersLength === 5) { LOTTO_RANKING['ONE']++; }
if (matchingNumbersLength === 4 && bonusNumberMatching) { LOTTO_RANKING['TWO']++; }
if (matchingNumbersLength === 4) { LOTTO_RANKING['THREE']++; }
if (matchingNumbersLength === 3 && bonusNumberMatching) { LOTTO_RANKING['FOUR']++; }
if (matchingNumbersLength === 2 && bonusNumberMatching) { LOTTO_RANKING['FIVE']++; }
if (matchingNumbersLength === 0) { LOTTO_RANKING['ZERO']++; }
}


getWinningNumbersLength(lotto, winningLotto) {
return this.#calculateWinningNumbersLength(lotto, winningLotto);
}

#calculateBonusNumberMatching(lotto, bonusNumber) {
return lotto.includes(bonusNumber);
}

#calculateWinningNumbersLength(lotto, winningLotto) {
/**
* @param {number[]} lotto
* @param {number[]} winningLotto
*/
const matchingNumbers = lotto.filter(lottoNumber => winningLotto.includes(lottoNumber));

return matchingNumbers.length;
}
}

export { LottoCalculator };
10 changes: 10 additions & 0 deletions src/domains/lottoCalculator/utils/LottoCalculatorConstants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
let LOTTO_RANKING = {
'ONE' : 0,
'TWO' : 0,
'THREE' : 0,
'FOUR' : 0,
'FIVE' : 0,
'ZERO' : 0
};

export { LOTTO_RANKING };
Loading