From 410982c977dba956e90abe690775034e3cfd56f1 Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Mon, 28 Oct 2024 04:39:35 +0900 Subject: [PATCH 01/37] =?UTF-8?q?=F0=9F=93=9A=20docs(README):=20=EC=9A=94?= =?UTF-8?q?=EA=B5=AC=EC=82=AC=ED=95=AD=20=EB=B6=84=EC=84=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 과제의 요구사항을 분석한 README를 추가했습니다. `시작하기`, `입력받기`, `출력하기`, `게임하기`, `입력값 예외 처리하기` 의 단계로 나누어 각 단계 별로 구현해야할 기능을 명시하였습니다. --- README.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/README.md b/README.md index e078fd41f..2ec7d1954 100644 --- a/README.md +++ b/README.md @@ -1 +1,31 @@ # javascript-racingcar-precourse +## 🚀 요구 사항 분석 +### 시작하기 + +- [x] 요구사항 분석하기 `README` +- [ ] constant 작성하기 `constant` + +### 입력받기 `input` + +- [ ] 사용자에게 입력받는 함수 구현하기 +- [ ] 입력받은 값 쉼표(,) 기준으로 구분하기 +- [ ] 시도할 횟수 입력받기 + +### 출력하기 `output` + +- [ ] 출력하는 함수 구현하기 +- [ ] 실행 결과 출력하기 +- [ ] 최종 우승자 출력하기 + +### 게임하기 `game` + +- [ ] 0~9 무작위값 구하기 +- [ ] 전진 가능한지 확인하기 +- [ ] 최종 우승자 구하기 + +### 입력값 예외 처리하기 `error` + +- [ ] 입력값이 없는 경우 +- [ ] 이름이 5자 이하가 아닐 경우 +- [ ] 자동차 이름이 중복됐을 경우 +- [ ] 시도 횟수가 양수가 아닐 경우 \ No newline at end of file From 0bb590388f3cd749e8c546bd6ffaa1a29c3507f6 Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Mon, 28 Oct 2024 05:01:38 +0900 Subject: [PATCH 02/37] =?UTF-8?q?=E2=9C=A8=20feat(constant):=20=EB=A9=94?= =?UTF-8?q?=EC=8B=9C=EC=A7=80=20=EA=B4=80=EB=A0=A8=20=EC=83=81=EC=88=98=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 입출력 메시지 관련 상수, 에러 메시지 관련 상수를 추가했습니다. --- README.md | 2 +- src/constant.js | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 src/constant.js diff --git a/README.md b/README.md index 2ec7d1954..e133a3aa3 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ ### 시작하기 - [x] 요구사항 분석하기 `README` -- [ ] constant 작성하기 `constant` +- [x] constant 작성하기 `constant` ### 입력받기 `input` diff --git a/src/constant.js b/src/constant.js new file mode 100644 index 000000000..bd1dd51ac --- /dev/null +++ b/src/constant.js @@ -0,0 +1,14 @@ +export const MESSAGE = Object.freeze({ + PROMPT_NAME_USER_INPUT: + '경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)', + PROMPT_COUNT_USER_INPUT: '시도할 횟수는 몇 회인가요?', + EXECUTE_OUTPUT: '실행 결과', + FINAL_OUTPUT: '최종 우승자', +}); + +export const ERROR_MESSAGE = Object.freeze({ + EMPTY_INPUT: '[Error] 입력값이 없습니다.', + NAME_TOO_LONG: '[Error] 이름은 5자 이하로 입력해야 합니다.', + DUPLICATE_NAME: '[Error] 자동차 이름은 중복될 수 없습니다.', + INVALID_TRY_COUNT: '[Error] 시도 횟수는 양수여야 합니다.', +}); From bdefa60fc05a571d1818f4750d3bfb5a9c71ff2f Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Mon, 28 Oct 2024 05:30:24 +0900 Subject: [PATCH 03/37] =?UTF-8?q?=E2=9C=A8=20feat(input,=20output):=20?= =?UTF-8?q?=EC=9E=85=EB=A0=A5=20=EB=B0=8F=20=EC=B6=9C=EB=A0=A5=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Console API를 사용하여 한 줄의 입력을 받는 함수를 구현했습니다. - Console API를 사용하여 입력받은 값을 출력하는 함수를 구현했습니다. --- README.md | 4 ++-- src/util/io.js | 11 +++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 src/util/io.js diff --git a/README.md b/README.md index e133a3aa3..ad5d0ee40 100644 --- a/README.md +++ b/README.md @@ -7,13 +7,13 @@ ### 입력받기 `input` -- [ ] 사용자에게 입력받는 함수 구현하기 +- [x] 사용자에게 입력받는 함수 구현하기 - [ ] 입력받은 값 쉼표(,) 기준으로 구분하기 - [ ] 시도할 횟수 입력받기 ### 출력하기 `output` -- [ ] 출력하는 함수 구현하기 +- [x] 출력하는 함수 구현하기 - [ ] 실행 결과 출력하기 - [ ] 최종 우승자 출력하기 diff --git a/src/util/io.js b/src/util/io.js new file mode 100644 index 000000000..03ef45b64 --- /dev/null +++ b/src/util/io.js @@ -0,0 +1,11 @@ +import { Console } from '@woowacourse/mission-utils'; + +function input(message) { + return Console.readLineAsync(message); +} + +function output(message) { + Console.print(message); +} + +export { input, output }; From e7ccc48f18f2755931f4fc5cf973b59fcffcbbcd Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Mon, 28 Oct 2024 05:43:07 +0900 Subject: [PATCH 04/37] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor(input,=20ou?= =?UTF-8?q?tput):=20=ED=95=A8=EC=88=98=EB=AA=85=20readUserInput,=20printOu?= =?UTF-8?q?tput=20=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 이전: `input` 변경 `readUserInput` 입력의 의미보다는 사용자의 입력을 읽는 함수이기 때문에 함수의 기능을 명확하게 표현하기 위해 변경했습니다. - 이전: 'output' 변경: `printOutput` 단순한 출력의 의미보다는 결괏값을 Console에 print 하는 함수이기 때문에 함수의 기능을 명확하게 표현하기 위해 변경했습니다. --- src/util/io.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/util/io.js b/src/util/io.js index 03ef45b64..25ce9524c 100644 --- a/src/util/io.js +++ b/src/util/io.js @@ -1,11 +1,11 @@ import { Console } from '@woowacourse/mission-utils'; -function input(message) { +function readUserInput(message) { return Console.readLineAsync(message); } -function output(message) { +function printOutput(message) { Console.print(message); } -export { input, output }; +export { readUserInput, printOutput }; From 2b0f3d6fe249a423fcc503e485c9e3de3ed6e045 Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Mon, 28 Oct 2024 05:50:40 +0900 Subject: [PATCH 05/37] =?UTF-8?q?=E2=9C=A8=20feat(input):=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EC=9E=90=EC=97=90=EA=B2=8C=20=EC=9E=90=EB=8F=99?= =?UTF-8?q?=EC=B0=A8=20=EC=9D=B4=EB=A6=84=20=EC=9E=85=EB=A0=A5=EB=B0=9B?= =?UTF-8?q?=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `readUserInput` 함수를 사용하여 사용자로부터 자동차 이름을 입력받는 기능을 구현했습니다. - `MESSAGE.PROMPT_NAME_USER_INPUT` 상수를 통해 입력을 안내했습니다. - 입력받은 값을 `input` 변수에 저장하여 이후 로직에서 활용 가능하도록 구현했습니다. --- README.md | 1 + src/App.js | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ad5d0ee40..f66213209 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ ### 입력받기 `input` - [x] 사용자에게 입력받는 함수 구현하기 +- [x] 사용자에게 자동차 이름 입력받기 - [ ] 입력받은 값 쉼표(,) 기준으로 구분하기 - [ ] 시도할 횟수 입력받기 diff --git a/src/App.js b/src/App.js index 091aa0a5d..8d67af2d8 100644 --- a/src/App.js +++ b/src/App.js @@ -1,5 +1,10 @@ +import { MESSAGE } from './constant'; +import { readUserInput } from './util/io.js'; + class App { - async run() {} + async run() { + const input = await readUserInput(MESSAGE.PROMPT_NAME_USER_INPUT); + } } export default App; From df9cb0704c9441d8980fc15630d04a77193aa85c Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Mon, 28 Oct 2024 06:13:26 +0900 Subject: [PATCH 06/37] =?UTF-8?q?=E2=9C=A8=20feat(input):=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EC=9E=90=EC=97=90=EA=B2=8C=20=EC=8B=9C=EB=8F=84=20?= =?UTF-8?q?=ED=9A=9F=EC=88=98=EB=A5=BC=20=EC=9E=85=EB=A0=A5=EB=B0=9B?= =?UTF-8?q?=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `readUserInput` 함수를 사용하여 사용자로부터 시도 횟수를 입력받는 기능을 구현했습니다. - `MESSAGE.PROMPT_COUNT_USER_INPUT` 상수를 통해 입력을 안내했습니다. - 입력받은 값을 attemptCount 변수에 저장하여 이후 로직에서 활용 가능하도록 구현했습니다. --- README.md | 2 +- src/App.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f66213209..c223b92c1 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ - [x] 사용자에게 입력받는 함수 구현하기 - [x] 사용자에게 자동차 이름 입력받기 - [ ] 입력받은 값 쉼표(,) 기준으로 구분하기 -- [ ] 시도할 횟수 입력받기 +- [x] 시도할 횟수 입력받기 ### 출력하기 `output` diff --git a/src/App.js b/src/App.js index 8d67af2d8..3ec2ec133 100644 --- a/src/App.js +++ b/src/App.js @@ -4,6 +4,7 @@ import { readUserInput } from './util/io.js'; class App { async run() { const input = await readUserInput(MESSAGE.PROMPT_NAME_USER_INPUT); + const attemptCount = await readUserInput(MESSAGE.PROMPT_COUNT_USER_INPUT); } } From c07e4355110a45a87e1bb1df18d1671a6829c9b6 Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Mon, 28 Oct 2024 16:25:07 +0900 Subject: [PATCH 07/37] =?UTF-8?q?=E2=9C=A8=20feat(split):=20=EC=9E=85?= =?UTF-8?q?=EB=A0=A5=EB=B0=9B=EC=9D=80=20=EA=B0=92=EC=9D=84=20=EC=89=BC?= =?UTF-8?q?=ED=91=9C=20=EA=B8=B0=EC=A4=80=EC=9C=BC=EB=A1=9C=20=EA=B5=AC?= =?UTF-8?q?=EB=B6=84=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `splitByComma` 함수를 통해 문자열을 쉼표로 구분하여 배열로 반환하였습니다. --- README.md | 2 +- src/App.js | 2 ++ src/util/splitByComma.js | 5 +++++ 3 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 src/util/splitByComma.js diff --git a/README.md b/README.md index c223b92c1..e47174929 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ - [x] 사용자에게 입력받는 함수 구현하기 - [x] 사용자에게 자동차 이름 입력받기 -- [ ] 입력받은 값 쉼표(,) 기준으로 구분하기 +- [x] 입력받은 값 쉼표(,) 기준으로 구분하기 - [x] 시도할 횟수 입력받기 ### 출력하기 `output` diff --git a/src/App.js b/src/App.js index 3ec2ec133..4004b5de9 100644 --- a/src/App.js +++ b/src/App.js @@ -1,9 +1,11 @@ import { MESSAGE } from './constant'; import { readUserInput } from './util/io.js'; +import splitByComma from './util/splitByComma'; class App { async run() { const input = await readUserInput(MESSAGE.PROMPT_NAME_USER_INPUT); + const cars = splitByComma(input); const attemptCount = await readUserInput(MESSAGE.PROMPT_COUNT_USER_INPUT); } } diff --git a/src/util/splitByComma.js b/src/util/splitByComma.js new file mode 100644 index 000000000..a36d63aed --- /dev/null +++ b/src/util/splitByComma.js @@ -0,0 +1,5 @@ +function splitByComma(input) { + return input.split(','); +} + +export default splitByComma; From 57ebe3fe52af05034f17f7bcf54b3cb70af8801f Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Mon, 28 Oct 2024 16:40:32 +0900 Subject: [PATCH 08/37] =?UTF-8?q?=E2=9C=A8=20feat(random):=20=EB=B2=94?= =?UTF-8?q?=EC=9C=84=20=EB=82=B4=EC=97=90=EC=84=9C=20=EC=9E=84=EC=9D=98?= =?UTF-8?q?=EC=9D=98=20=EC=88=AB=EC=9E=90=20=EC=B6=94=EC=B6=9C=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `MissionUtils.Random.pickNumberInRange` 메서드를 사용하였습니다. - `startRange` 와 `endRange` 를 통해 숫자 범위를 입력받아 해당 범위 내에서 랜덤한 숫자를 생성하였습니다. --- README.md | 2 +- src/util/random.js | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 src/util/random.js diff --git a/README.md b/README.md index e47174929..7849ab889 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ ### 게임하기 `game` -- [ ] 0~9 무작위값 구하기 +- [x] 0~9 무작위값 구하기 - [ ] 전진 가능한지 확인하기 - [ ] 최종 우승자 구하기 diff --git a/src/util/random.js b/src/util/random.js new file mode 100644 index 000000000..4d1760e56 --- /dev/null +++ b/src/util/random.js @@ -0,0 +1,7 @@ +import { MissionUtils } from '@woowacourse/mission-utils'; + +function pickRandomNumberInRange(startRange, endRange) { + return MissionUtils.Random.pickNumberInRange(startRange, endRange); +} + +export default pickRandomNumberInRange; From 9cbf847e796c03c47b44e8fd2bbb0678955ff414 Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Mon, 28 Oct 2024 16:43:52 +0900 Subject: [PATCH 09/37] =?UTF-8?q?=F0=9F=A7=AA=20test(random):=20`pickRando?= =?UTF-8?q?mNumberInRange`=20=ED=95=A8=EC=88=98=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `pickRandomNumberInRange` 함수의 기능을 검증하는 테스트 코드를 작성했습니다. - `Random.pickNumberInRange` 메서드를 `jest.spyOn`으로 모의 처리하여 테스트의 일관성을 유지하였습니다. - 0-9 사이의 범위에서 반환되는 값이 지정된 범위 내에 있는지 테스트하였습니다 - mock을 사용하여 설정한 중간값이 반환되는지 확인하는 테스트를 추가하였습니다. - 테스트 전후로 mock을 설정 및 복원하여 다른 테스트에 영향을 주지 않도록 처리하였습니다. --- __tests__/random.js | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 __tests__/random.js diff --git a/__tests__/random.js b/__tests__/random.js new file mode 100644 index 000000000..1ba1f40f7 --- /dev/null +++ b/__tests__/random.js @@ -0,0 +1,32 @@ +import { Random } from '@woowacourse/mission-utils'; +import pickRandomNumberInRange from '../src/util/random'; + +describe('random function', () => { + beforeEach(() => { + jest + .spyOn(Random, 'pickNumberInRange') + .mockImplementation((startRange, endRange) => { + // 항상 중간값을 반환하도록 모의 처리 + return Math.floor((startRange + endRange) / 2); + }); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + test('0-9 사이의 랜덤한 숫자 추출하기', () => { + const startRange = 0; + const endRange = 9; + const result = pickRandomNumberInRange(startRange, endRange); + expect(result).toBeGreaterThanOrEqual(startRange); + expect(result).toBeLessThanOrEqual(endRange); + }); + + test('mock에서 설정한 중간값 반환되는지 확인하기', () => { + const startRange = 2; + const endRange = 8; + const result = pickRandomNumberInRange(startRange, endRange); + expect(result).toBe(5); + }); +}); From 30a7ac232df0138029031622f08287d1293008d4 Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Mon, 28 Oct 2024 16:55:36 +0900 Subject: [PATCH 10/37] =?UTF-8?q?=E2=9C=A8=20feat(simulate):=20`Simulator`?= =?UTF-8?q?=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EB=B0=8F=20=EC=83=81=ED=83=9C?= =?UTF-8?q?=20=EC=B4=88=EA=B8=B0=ED=99=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `Simulator` 클래스 생성자로 이름 배열을 받아 초기화하도록 구현하였습니다. - `names` 배열을 사용하여 각 이름을 키로 하고 값을 0으로 초기화한 객체 배열 생성하였습니다. - `state` 의 key는 자동차의 이름을 의미하고, value는 해당 자동차의 전진 척도를 의미합니다. --- src/App.js | 2 ++ src/Simulator.js | 11 +++++++++++ 2 files changed, 13 insertions(+) create mode 100644 src/Simulator.js diff --git a/src/App.js b/src/App.js index 4004b5de9..fa91acc25 100644 --- a/src/App.js +++ b/src/App.js @@ -1,4 +1,5 @@ import { MESSAGE } from './constant'; +import Simulator from './Simulator.js'; import { readUserInput } from './util/io.js'; import splitByComma from './util/splitByComma'; @@ -6,6 +7,7 @@ class App { async run() { const input = await readUserInput(MESSAGE.PROMPT_NAME_USER_INPUT); const cars = splitByComma(input); + const simulator = Simulator(cars); const attemptCount = await readUserInput(MESSAGE.PROMPT_COUNT_USER_INPUT); } } diff --git a/src/Simulator.js b/src/Simulator.js new file mode 100644 index 000000000..88d5efe6b --- /dev/null +++ b/src/Simulator.js @@ -0,0 +1,11 @@ +class Simulator { + constructor(names) { + this.names = names; + this.state = names.reduce((arr, name) => { + arr.push({ [name]: 0 }); + return arr; + }, []); + } +} + +export default Simulator; From 7bb7b2a7dbce01e8c352abec32ac2efb5fbb325d Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Mon, 28 Oct 2024 17:00:56 +0900 Subject: [PATCH 11/37] =?UTF-8?q?=F0=9F=9B=A0=EF=B8=8F=20chore:=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=EB=AA=85=20`random.js`=EB=A5=BC=20`pickRando?= =?UTF-8?q?mNumberInRange.js`=20=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 함수 이름과 파일 이름을 통일하여 가독성 및 코드 컨벤션을 준수하였습니다. --- __tests__/random.js | 2 +- src/util/{random.js => pickRandomNumberInRange.js} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/util/{random.js => pickRandomNumberInRange.js} (100%) diff --git a/__tests__/random.js b/__tests__/random.js index 1ba1f40f7..53777f963 100644 --- a/__tests__/random.js +++ b/__tests__/random.js @@ -1,5 +1,5 @@ import { Random } from '@woowacourse/mission-utils'; -import pickRandomNumberInRange from '../src/util/random'; +import pickRandomNumberInRange from '../src/util/pickRandomNumberInRange'; describe('random function', () => { beforeEach(() => { diff --git a/src/util/random.js b/src/util/pickRandomNumberInRange.js similarity index 100% rename from src/util/random.js rename to src/util/pickRandomNumberInRange.js From 49f1cac8dbfd28f87e30c32ac7f629057ce39434 Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Mon, 28 Oct 2024 17:04:56 +0900 Subject: [PATCH 12/37] =?UTF-8?q?=E2=9C=A8=20feat(simulate):=20=EC=A0=84?= =?UTF-8?q?=EC=A7=84=20=EA=B0=80=EB=8A=A5=ED=95=9C=EC=A7=80=20=EC=97=AC?= =?UTF-8?q?=EB=B6=80=20=ED=99=95=EC=9D=B8=ED=95=98=EB=8A=94=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 클래스 내에서만 사용 가능한 `#canMoveForward()` 메서드를 추가하였습니다. - `#`으로 private 메서드로 정의하여 캡슐화하고 외부에서 접근 불가하도록 설계하여 내부 로직을 보호했습니다. - `pickRandomNumberInRange` 함수를 사용해 0-9 범위의 랜덤 값이 4보다 큰 지를 확인하여 전진 가능 여부를 판단했습니다. --- README.md | 2 +- src/Simulator.js | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7849ab889..6d428e1cb 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ ### 게임하기 `game` - [x] 0~9 무작위값 구하기 -- [ ] 전진 가능한지 확인하기 +- [x] 전진 가능한지 확인하기 - [ ] 최종 우승자 구하기 ### 입력값 예외 처리하기 `error` diff --git a/src/Simulator.js b/src/Simulator.js index 88d5efe6b..b1e8f65d2 100644 --- a/src/Simulator.js +++ b/src/Simulator.js @@ -1,3 +1,5 @@ +import pickRandomNumberInRange from './util/pickRandomNumberInRange'; + class Simulator { constructor(names) { this.names = names; @@ -6,6 +8,11 @@ class Simulator { return arr; }, []); } + + #canMoveForward() { + if (pickRandomNumberInRange(0, 9) >= 4) return true; + return false; + } } export default Simulator; From a4411ba9bdfe19165ed2f26a3d04e930d2b2c618 Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Mon, 28 Oct 2024 17:16:07 +0900 Subject: [PATCH 13/37] =?UTF-8?q?=E2=9C=A8=20feat(simulate):=20`simulate`?= =?UTF-8?q?=20=EB=A9=94=EC=84=9C=EB=93=9C=EB=A5=BC=20=ED=86=B5=ED=95=B4=20?= =?UTF-8?q?=EC=9E=90=EB=8F=99=EC=B0=A8=20=EC=A0=84=EC=A7=84=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `simulate` 메서드를 구현하여 각 자동차의 전진 여부를 결정하고 상태를 업데이트 하였습니다. - 전진 시 `count` 를 증가시키도록 설정하였습니다. - `attemptCount`만큼 시뮬레이션을 반복 실행하는 로직을 추가하여, 사용자가 입력한 횟수만큼 시뮬레이션을 진행하도록 하였습니다. --- README.md | 1 + src/App.js | 3 +++ src/Simulator.js | 8 +++++++- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6d428e1cb..f8e614fe3 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ - [x] 0~9 무작위값 구하기 - [x] 전진 가능한지 확인하기 +- [x] 실행 횟수만큼 실행하기 - [ ] 최종 우승자 구하기 ### 입력값 예외 처리하기 `error` diff --git a/src/App.js b/src/App.js index fa91acc25..aaf8860b2 100644 --- a/src/App.js +++ b/src/App.js @@ -9,6 +9,9 @@ class App { const cars = splitByComma(input); const simulator = Simulator(cars); const attemptCount = await readUserInput(MESSAGE.PROMPT_COUNT_USER_INPUT); + for (let i = 0; i < attemptCount; i++) { + simulator.simulate(); + } } } diff --git a/src/Simulator.js b/src/Simulator.js index b1e8f65d2..2847d994c 100644 --- a/src/Simulator.js +++ b/src/Simulator.js @@ -2,7 +2,6 @@ import pickRandomNumberInRange from './util/pickRandomNumberInRange'; class Simulator { constructor(names) { - this.names = names; this.state = names.reduce((arr, name) => { arr.push({ [name]: 0 }); return arr; @@ -13,6 +12,13 @@ class Simulator { if (pickRandomNumberInRange(0, 9) >= 4) return true; return false; } + + simulate() { + this.state = this.state.map(({ name, count }) => { + if (this.#canMoveForward()) count++; + return { name, count }; + }); + } } export default Simulator; From 0acfc7bf626b8b93cbcd7bcb35990ffad76ab8a9 Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Mon, 28 Oct 2024 17:29:06 +0900 Subject: [PATCH 14/37] =?UTF-8?q?=E2=9C=A8=20feat(output):=20=EC=8B=A4?= =?UTF-8?q?=ED=96=89=20=EA=B2=B0=EA=B3=BC=20=EC=B6=9C=EB=A0=A5=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `printSimulate` 함수 구현하여 자동차 이름과 전진 상태를 '-'로 시각적으로 출력하였습니다. - 기존의 `simulate` 메서드에서 각 자동차의 전진 여부를 결정한 후, `printSimulate` 함수를 호출해 현재 상태를 출력하는 부분을 추가하였습니다. - 현재 상태를 모두 출력한 후 공백을 출력하여 과제의 출력 요구 사항을 충족시켰습니다. --- README.md | 2 +- src/Simulator.js | 6 ++++++ src/util/printSimulate.js | 8 ++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 src/util/printSimulate.js diff --git a/README.md b/README.md index f8e614fe3..3938911ab 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ ### 출력하기 `output` - [x] 출력하는 함수 구현하기 -- [ ] 실행 결과 출력하기 +- [x] 실행 결과 출력하기 - [ ] 최종 우승자 출력하기 ### 게임하기 `game` diff --git a/src/Simulator.js b/src/Simulator.js index 2847d994c..599268af0 100644 --- a/src/Simulator.js +++ b/src/Simulator.js @@ -1,7 +1,11 @@ +import { MESSAGE } from './constant'; +import { printOutput } from './util/io'; import pickRandomNumberInRange from './util/pickRandomNumberInRange'; +import printSimulate from './util/printSimulate'; class Simulator { constructor(names) { + printOutput(MESSAGE.EXECUTE_OUTPUT); this.state = names.reduce((arr, name) => { arr.push({ [name]: 0 }); return arr; @@ -16,8 +20,10 @@ class Simulator { simulate() { this.state = this.state.map(({ name, count }) => { if (this.#canMoveForward()) count++; + printSimulate(name, count); return { name, count }; }); + printOutput(''); } } diff --git a/src/util/printSimulate.js b/src/util/printSimulate.js new file mode 100644 index 000000000..437f11202 --- /dev/null +++ b/src/util/printSimulate.js @@ -0,0 +1,8 @@ +import { printOutput } from './io'; + +function printSimulate(name, count) { + const result = '-'.repeat(count); + printOutput(`${name} : ${result}`); +} + +export default printSimulate; From e10f48a28666e5b460dbb1ddb2841eb0b0b3b00a Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Mon, 28 Oct 2024 17:34:53 +0900 Subject: [PATCH 15/37] =?UTF-8?q?=F0=9F=90=9B=20fix(simulate):=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=EC=9D=B8=EC=8A=A4=ED=84=B4=EC=8A=A4=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=EC=8B=9C=20`new`=20=ED=82=A4=EC=9B=8C?= =?UTF-8?q?=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `Simulator` 클래스의 인스턴스를 생성할 때 `new` 키워드를 사용하도록 수정했습니다. --- src/App.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/App.js b/src/App.js index aaf8860b2..0fa505d0c 100644 --- a/src/App.js +++ b/src/App.js @@ -7,7 +7,7 @@ class App { async run() { const input = await readUserInput(MESSAGE.PROMPT_NAME_USER_INPUT); const cars = splitByComma(input); - const simulator = Simulator(cars); + const simulator = new Simulator(cars); const attemptCount = await readUserInput(MESSAGE.PROMPT_COUNT_USER_INPUT); for (let i = 0; i < attemptCount; i++) { simulator.simulate(); From 12f8d179498887881a1c6e30780fc69c5d7fd44a Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Mon, 28 Oct 2024 17:45:02 +0900 Subject: [PATCH 16/37] =?UTF-8?q?=F0=9F=A7=AA=20test(simulate):=20`Simulat?= =?UTF-8?q?or`=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `Simulator` 클래스의 기능을 검증하는 테스트 코드를 작성했습니다. - name, count 초기화 확인하기 - `names` 배열을 통해 초기 상태가 설정되고, 실행 메시지가 출력되는지 검증하였습니다. - `#canMoveForward` 가 true일 때 상태 업데이트를 확인하기 - `pickRandomNumberInRange`를 mock 처리하여 전진 조건을 만족하는 경우를 검증하였습니다. - 전진 시 `count` 가 증가하고, `printOutput`으로 올바른 결과가 출력되는지 확인하였습니다. - `#canMoveForward` 가 false일 때 상태 업데이트를 확인하기 - `pickRandomNumberInRange`를 mock 처리하여 전진 조건을 만족하지 않는 경우를 검증하였습니다. - 전진하지 않으면 `count`가 유지되고, `printOutput`으로 올바른 결과가 출력되는지 확인하였습니다. - 자동차가 여러 개일 때 상태 업데이트 확인하기 - `mockReturnValueOnce`를 사용해 각 자동차에 대해 다른 전진 조건을 설정하고, 결과를 검증하였습니다. - 여러 자동차의 상태를 확인하여 시뮬레이션 결과가 일관되게 출력되는지 검증하였습니다. --- __tests__/SimulateTest.js | 56 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 __tests__/SimulateTest.js diff --git a/__tests__/SimulateTest.js b/__tests__/SimulateTest.js new file mode 100644 index 000000000..5b088b19a --- /dev/null +++ b/__tests__/SimulateTest.js @@ -0,0 +1,56 @@ +import Simulator from '../src/Simulator'; // Simulator 클래스가 정의된 경로로 수정하세요. +import { MESSAGE } from '../src/constant'; +import { printOutput } from '../src/util/io'; +import pickRandomNumberInRange from '../src/util/pickRandomNumberInRange'; + +jest.mock('../src/util/io'); +jest.mock('../src/util/pickRandomNumberInRange'); + +describe('Simulator class', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + test('name, count 초기화 확인하기', () => { + const names = ['Car1', 'Car2', 'Car3']; + const simulator = new Simulator(names); + + expect(printOutput).toHaveBeenCalledWith(MESSAGE.EXECUTE_OUTPUT); + expect(simulator.state).toEqual([{ Car1: 0 }, { Car2: 0 }, { Car3: 0 }]); + }); + + test('#canMoveForward가 true일 때 상태 업데이트 확인하기', () => { + const names = ['Car1']; + const simulator = new Simulator(names); + pickRandomNumberInRange.mockReturnValue(5); + + simulator.simulate(); + console.log(simulator.state); + expect(simulator.state).toEqual([{ Car1: 1 }]); + expect(printOutput).toHaveBeenCalledWith('Car1 : -\n'); + }); + + test('#canMoveForward가 false일 때 상태 업데이트 확인하기', () => { + const names = ['Car1']; + const simulator = new Simulator(names); + pickRandomNumberInRange.mockReturnValue(3); + + simulator.simulate(); + + expect(simulator.state).toEqual([{ Car1: 0 }]); + expect(printOutput).toHaveBeenCalledWith('Car1 : \n'); + }); + + test('자동차가 여러 개일 때 상태 업데이트 확인하기', () => { + const names = ['Car1', 'Car2']; + const simulator = new Simulator(names); + pickRandomNumberInRange.mockReturnValueOnce(5).mockReturnValueOnce(3); + + simulator.simulate(); + + expect(simulator.state).toEqual([{ Car1: 1 }, { Car2: 0 }]); + expect(printOutput).toHaveBeenCalledWith('Car1 : -'); + expect(printOutput).toHaveBeenCalledWith('Car2 : '); + expect(printOutput).toHaveBeenCalledWith(''); + }); +}); From bad4934b95f9401c226716cb5ef19830006fe4e6 Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Mon, 28 Oct 2024 17:51:26 +0900 Subject: [PATCH 17/37] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor(test):=20?= =?UTF-8?q?=EC=83=81=ED=83=9C=20=EA=B2=80=EC=A6=9D=EC=97=90=EC=84=9C=20`to?= =?UTF-8?q?StrictEqual`=20=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - expect의 `toEqual`을 `toStrictEqual`로 변경했습니다. - 객체의 구조와 값뿐만 아니라 객체의 형까지 엄격하게 비교하도록 수정하였습니다. - 테스트의 신뢰성을 높이고, 객체의 구조적 일관성을 더 정확하게 검증하기 위해 변경했습니다. --- __tests__/SimulateTest.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/__tests__/SimulateTest.js b/__tests__/SimulateTest.js index 5b088b19a..bc65c7603 100644 --- a/__tests__/SimulateTest.js +++ b/__tests__/SimulateTest.js @@ -16,7 +16,11 @@ describe('Simulator class', () => { const simulator = new Simulator(names); expect(printOutput).toHaveBeenCalledWith(MESSAGE.EXECUTE_OUTPUT); - expect(simulator.state).toEqual([{ Car1: 0 }, { Car2: 0 }, { Car3: 0 }]); + expect(simulator.state).toStrictEqual([ + { Car1: 0 }, + { Car2: 0 }, + { Car3: 0 }, + ]); }); test('#canMoveForward가 true일 때 상태 업데이트 확인하기', () => { @@ -26,7 +30,7 @@ describe('Simulator class', () => { simulator.simulate(); console.log(simulator.state); - expect(simulator.state).toEqual([{ Car1: 1 }]); + expect(simulator.state).toStrictEqual([{ Car1: 1 }]); expect(printOutput).toHaveBeenCalledWith('Car1 : -\n'); }); @@ -37,7 +41,7 @@ describe('Simulator class', () => { simulator.simulate(); - expect(simulator.state).toEqual([{ Car1: 0 }]); + expect(simulator.state).toStrictEqual([{ Car1: 0 }]); expect(printOutput).toHaveBeenCalledWith('Car1 : \n'); }); @@ -48,7 +52,7 @@ describe('Simulator class', () => { simulator.simulate(); - expect(simulator.state).toEqual([{ Car1: 1 }, { Car2: 0 }]); + expect(simulator.state).toStrictEqual([{ Car1: 1 }, { Car2: 0 }]); expect(printOutput).toHaveBeenCalledWith('Car1 : -'); expect(printOutput).toHaveBeenCalledWith('Car2 : '); expect(printOutput).toHaveBeenCalledWith(''); From 759aac4232ea00d35a6fe98fef7474b492fb966c Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Mon, 28 Oct 2024 18:09:25 +0900 Subject: [PATCH 18/37] =?UTF-8?q?=F0=9F=90=9B=20fix(simulate,=20test):=20`?= =?UTF-8?q?simulate`=20=EB=A9=94=EC=84=9C=EB=93=9C=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EC=83=81=ED=83=9C=20=EB=B0=98=ED=99=98=20=ED=98=95=EC=8B=9D=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 - 이전: `{ [name]: count }` - 변경: `{ name, count }` - `simulate` 메서드와 `test` 코드의 상태 반환 형식을 맞추었습니다. --- __tests__/SimulateTest.js | 25 +++++++++++++++++-------- src/Simulator.js | 2 +- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/__tests__/SimulateTest.js b/__tests__/SimulateTest.js index bc65c7603..0c25482d8 100644 --- a/__tests__/SimulateTest.js +++ b/__tests__/SimulateTest.js @@ -17,9 +17,9 @@ describe('Simulator class', () => { expect(printOutput).toHaveBeenCalledWith(MESSAGE.EXECUTE_OUTPUT); expect(simulator.state).toStrictEqual([ - { Car1: 0 }, - { Car2: 0 }, - { Car3: 0 }, + { name: 'Car1', count: 0 }, + { name: 'Car2', count: 0 }, + { name: 'Car3', count: 0 }, ]); }); @@ -27,11 +27,14 @@ describe('Simulator class', () => { const names = ['Car1']; const simulator = new Simulator(names); pickRandomNumberInRange.mockReturnValue(5); + console.log(simulator.state); simulator.simulate(); console.log(simulator.state); - expect(simulator.state).toStrictEqual([{ Car1: 1 }]); - expect(printOutput).toHaveBeenCalledWith('Car1 : -\n'); + expect(simulator.state).toStrictEqual([{ name: 'Car1', count: 1 }]); + expect(printOutput).toHaveBeenCalledWith(MESSAGE.EXECUTE_OUTPUT); + expect(printOutput).toHaveBeenCalledWith('Car1 : -'); + expect(printOutput).toHaveBeenCalledWith(''); }); test('#canMoveForward가 false일 때 상태 업데이트 확인하기', () => { @@ -41,8 +44,10 @@ describe('Simulator class', () => { simulator.simulate(); - expect(simulator.state).toStrictEqual([{ Car1: 0 }]); - expect(printOutput).toHaveBeenCalledWith('Car1 : \n'); + expect(simulator.state).toStrictEqual([{ name: 'Car1', count: 0 }]); + expect(printOutput).toHaveBeenCalledWith(MESSAGE.EXECUTE_OUTPUT); + expect(printOutput).toHaveBeenCalledWith('Car1 : '); + expect(printOutput).toHaveBeenCalledWith(''); }); test('자동차가 여러 개일 때 상태 업데이트 확인하기', () => { @@ -52,7 +57,11 @@ describe('Simulator class', () => { simulator.simulate(); - expect(simulator.state).toStrictEqual([{ Car1: 1 }, { Car2: 0 }]); + expect(simulator.state).toStrictEqual([ + { name: 'Car1', count: 1 }, + { name: 'Car2', count: 0 }, + ]); + expect(printOutput).toHaveBeenCalledWith(MESSAGE.EXECUTE_OUTPUT); expect(printOutput).toHaveBeenCalledWith('Car1 : -'); expect(printOutput).toHaveBeenCalledWith('Car2 : '); expect(printOutput).toHaveBeenCalledWith(''); diff --git a/src/Simulator.js b/src/Simulator.js index 599268af0..6192ee2d8 100644 --- a/src/Simulator.js +++ b/src/Simulator.js @@ -7,7 +7,7 @@ class Simulator { constructor(names) { printOutput(MESSAGE.EXECUTE_OUTPUT); this.state = names.reduce((arr, name) => { - arr.push({ [name]: 0 }); + arr.push({ name, count: 0 }); return arr; }, []); } From 57b217451207a6b8b10ec66eb918d6762e97f787 Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Mon, 28 Oct 2024 18:12:10 +0900 Subject: [PATCH 19/37] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor(simulate):?= =?UTF-8?q?=20state=EB=A5=BC=20private=20=ED=95=84=EB=93=9C=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `Simulator` 클래스의 `state`를 private 필드 `#state` 로 변경했습니다. - 외부에서 직접 `state`에 접근할 수 없도록 하여 캡슐화를 강화했습니다. --- src/Simulator.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Simulator.js b/src/Simulator.js index 6192ee2d8..6a76dac4c 100644 --- a/src/Simulator.js +++ b/src/Simulator.js @@ -4,9 +4,11 @@ import pickRandomNumberInRange from './util/pickRandomNumberInRange'; import printSimulate from './util/printSimulate'; class Simulator { + #state; + constructor(names) { printOutput(MESSAGE.EXECUTE_OUTPUT); - this.state = names.reduce((arr, name) => { + this.#state = names.reduce((arr, name) => { arr.push({ name, count: 0 }); return arr; }, []); @@ -18,7 +20,7 @@ class Simulator { } simulate() { - this.state = this.state.map(({ name, count }) => { + this.#state = this.#state.map(({ name, count }) => { if (this.#canMoveForward()) count++; printSimulate(name, count); return { name, count }; From bff457a4333145e496a9d5450caf26c88fa7b54a Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Mon, 28 Oct 2024 18:22:03 +0900 Subject: [PATCH 20/37] =?UTF-8?q?=E2=9C=A8=20feat(simulate):=20=EC=B5=9C?= =?UTF-8?q?=EC=A2=85=20=EC=9A=B0=EC=8A=B9=EC=9E=90=20=EC=B6=9C=EB=A0=A5?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `printWinner` 메서드를 통해 자동차 경주 종료 시 가장 많이 이동한 우승자들을 출력하였습니다. - 최대 이동 횟수를 계산하고, 해당 이동 횟수를 가진 자동차들을 필터링하여 우승자 이름만 추출하였습니다. - 우승자들의 이름을 쉼표`.` 로 구분하여 출력 형식에 맞췄습니다. --- README.md | 4 ++-- src/App.js | 1 + src/Simulator.js | 8 ++++++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3938911ab..60c1a7df6 100644 --- a/README.md +++ b/README.md @@ -16,14 +16,14 @@ - [x] 출력하는 함수 구현하기 - [x] 실행 결과 출력하기 -- [ ] 최종 우승자 출력하기 +- [x] 최종 우승자 출력하기 ### 게임하기 `game` - [x] 0~9 무작위값 구하기 - [x] 전진 가능한지 확인하기 - [x] 실행 횟수만큼 실행하기 -- [ ] 최종 우승자 구하기 +- [x] 최종 우승자 구하기 ### 입력값 예외 처리하기 `error` diff --git a/src/App.js b/src/App.js index 0fa505d0c..ba4a7e68c 100644 --- a/src/App.js +++ b/src/App.js @@ -12,6 +12,7 @@ class App { for (let i = 0; i < attemptCount; i++) { simulator.simulate(); } + simulator.printWinner(); } } diff --git a/src/Simulator.js b/src/Simulator.js index 6a76dac4c..d5890433f 100644 --- a/src/Simulator.js +++ b/src/Simulator.js @@ -27,6 +27,14 @@ class Simulator { }); printOutput(''); } + + printWinner() { + const maxCount = Math.max(...this.#state.map((car) => car.count)); + const winner = this.#state + .filter((car) => car.count === maxCount) + .map((car) => car.name); + printOutput(`${MESSAGE.FINAL_OUTPUT} : ${winner.join(', ')}`); + } } export default Simulator; From e354100d2e1c070cb1a4a2cbaf670b7249e52444 Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Mon, 28 Oct 2024 18:36:11 +0900 Subject: [PATCH 21/37] =?UTF-8?q?=F0=9F=A7=AA=20test(simulate):=20printWin?= =?UTF-8?q?ner=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=EB=B0=8F=20private=20=ED=95=84=EB=93=9C=20=EC=A0=81=EC=9A=A9?= =?UTF-8?q?=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `Simulator` 클래스의 `printWinner` 메서드에 대한 테스트를 추가했습니다. - 우승자가 한 명일 경우, 여러 명일 경우, 아무도 전진하지 않았을 경우에 대한 시나리오별로 테스트를 작성했습니다. - `describe`로 테스트를 그룹화하여 `simulate`와 `printWinner` 테스트를 분리하였습니다. - `state`를 `private` 필드로 변경함에 따라, 외부에서 직접 `state`에 접근하는 테스트 코드를 삭제했습니다. - 상태 변경 검증은 `printOutput` 호출 여부를 통해 간접적으로 확인할 수 있습니다. --- __tests__/SimulateTest.js | 70 ++++++++++++++++++++++++++++----------- 1 file changed, 50 insertions(+), 20 deletions(-) diff --git a/__tests__/SimulateTest.js b/__tests__/SimulateTest.js index 0c25482d8..21ac16954 100644 --- a/__tests__/SimulateTest.js +++ b/__tests__/SimulateTest.js @@ -6,23 +6,11 @@ import pickRandomNumberInRange from '../src/util/pickRandomNumberInRange'; jest.mock('../src/util/io'); jest.mock('../src/util/pickRandomNumberInRange'); -describe('Simulator class', () => { +describe('Simulator simulate test', () => { beforeEach(() => { jest.clearAllMocks(); }); - test('name, count 초기화 확인하기', () => { - const names = ['Car1', 'Car2', 'Car3']; - const simulator = new Simulator(names); - - expect(printOutput).toHaveBeenCalledWith(MESSAGE.EXECUTE_OUTPUT); - expect(simulator.state).toStrictEqual([ - { name: 'Car1', count: 0 }, - { name: 'Car2', count: 0 }, - { name: 'Car3', count: 0 }, - ]); - }); - test('#canMoveForward가 true일 때 상태 업데이트 확인하기', () => { const names = ['Car1']; const simulator = new Simulator(names); @@ -30,8 +18,6 @@ describe('Simulator class', () => { console.log(simulator.state); simulator.simulate(); - console.log(simulator.state); - expect(simulator.state).toStrictEqual([{ name: 'Car1', count: 1 }]); expect(printOutput).toHaveBeenCalledWith(MESSAGE.EXECUTE_OUTPUT); expect(printOutput).toHaveBeenCalledWith('Car1 : -'); expect(printOutput).toHaveBeenCalledWith(''); @@ -44,7 +30,6 @@ describe('Simulator class', () => { simulator.simulate(); - expect(simulator.state).toStrictEqual([{ name: 'Car1', count: 0 }]); expect(printOutput).toHaveBeenCalledWith(MESSAGE.EXECUTE_OUTPUT); expect(printOutput).toHaveBeenCalledWith('Car1 : '); expect(printOutput).toHaveBeenCalledWith(''); @@ -57,13 +42,58 @@ describe('Simulator class', () => { simulator.simulate(); - expect(simulator.state).toStrictEqual([ - { name: 'Car1', count: 1 }, - { name: 'Car2', count: 0 }, - ]); expect(printOutput).toHaveBeenCalledWith(MESSAGE.EXECUTE_OUTPUT); expect(printOutput).toHaveBeenCalledWith('Car1 : -'); expect(printOutput).toHaveBeenCalledWith('Car2 : '); expect(printOutput).toHaveBeenCalledWith(''); }); }); + +describe('Simulator printWinner test', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + test('우승자가 한 명일 경우', () => { + const names = ['Car1', 'Car2', 'Car3']; + const simulator = new Simulator(names); + + pickRandomNumberInRange.mockReturnValueOnce(5); + pickRandomNumberInRange.mockReturnValue(3); + + simulator.simulate(); + simulator.printWinner(); + + expect(printOutput).toHaveBeenCalledWith(`${MESSAGE.FINAL_OUTPUT} : Car1`); + }); + + test('우승자가 여러 명일 경우', () => { + const names = ['Car1', 'Car2', 'Car3']; + const simulator = new Simulator(names); + + pickRandomNumberInRange.mockReturnValueOnce(5); + pickRandomNumberInRange.mockReturnValueOnce(5); + pickRandomNumberInRange.mockReturnValue(3); + + simulator.simulate(); + simulator.printWinner(); + + expect(printOutput).toHaveBeenCalledWith( + `${MESSAGE.FINAL_OUTPUT} : Car1, Car2`, + ); + }); + + test('아무도 전진하지 않았을 경우', () => { + const names = ['Car1', 'Car2', 'Car3']; + const simulator = new Simulator(names); + + pickRandomNumberInRange.mockReturnValue(1); + + simulator.simulate(); + simulator.printWinner(); + + expect(printOutput).toHaveBeenCalledWith( + `${MESSAGE.FINAL_OUTPUT} : Car1, Car2, Car3`, + ); + }); +}); From 0fb75f384899b12c60a3ce0dce9dbeb304e205c1 Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Mon, 28 Oct 2024 18:39:43 +0900 Subject: [PATCH 22/37] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor(constant):?= =?UTF-8?q?=20`#canMoveForward`=20=EB=A9=94=EC=84=9C=EB=93=9C=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EB=B2=94=EC=9C=84=20=EC=83=81=EC=88=98=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `pickRandomNumberInRange` 호출 시 하드코딩된 숫자(0, 9)를 `RANGE` 상수로 대체했습니다. - `RANGE.START` , `RANGE.END` , `RANGE.VALID` 상수를 사용하여 코드 가독성 및 유지보수성을 향상 시켰습니다. --- src/Simulator.js | 5 +++-- src/constant.js | 6 ++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Simulator.js b/src/Simulator.js index d5890433f..faa7d61a0 100644 --- a/src/Simulator.js +++ b/src/Simulator.js @@ -1,4 +1,4 @@ -import { MESSAGE } from './constant'; +import { MESSAGE, RANGE } from './constant'; import { printOutput } from './util/io'; import pickRandomNumberInRange from './util/pickRandomNumberInRange'; import printSimulate from './util/printSimulate'; @@ -15,7 +15,8 @@ class Simulator { } #canMoveForward() { - if (pickRandomNumberInRange(0, 9) >= 4) return true; + if (pickRandomNumberInRange(RANGE.START, RANGE.END) >= RANGE.VALID) + return true; return false; } diff --git a/src/constant.js b/src/constant.js index bd1dd51ac..b7ef78637 100644 --- a/src/constant.js +++ b/src/constant.js @@ -12,3 +12,9 @@ export const ERROR_MESSAGE = Object.freeze({ DUPLICATE_NAME: '[Error] 자동차 이름은 중복될 수 없습니다.', INVALID_TRY_COUNT: '[Error] 시도 횟수는 양수여야 합니다.', }); + +export const RANGE = Object.freeze({ + START: 0, + END: 9, + VALID: 4, +}); From d68f883ff167161cb9f7eefb23069ab51c407671 Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Mon, 28 Oct 2024 19:04:46 +0900 Subject: [PATCH 23/37] =?UTF-8?q?=E2=9C=A8=20feat(error):=20`Validator`=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - validate 메서드를 통해 입력값 검증 기능을 추가할 예정입니다. --- src/App.js | 7 ++++++- src/Validator.js | 5 +++++ 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 src/Validator.js diff --git a/src/App.js b/src/App.js index ba4a7e68c..6cfaf0eee 100644 --- a/src/App.js +++ b/src/App.js @@ -2,13 +2,18 @@ import { MESSAGE } from './constant'; import Simulator from './Simulator.js'; import { readUserInput } from './util/io.js'; import splitByComma from './util/splitByComma'; +import Validator from './Validator'; class App { async run() { const input = await readUserInput(MESSAGE.PROMPT_NAME_USER_INPUT); const cars = splitByComma(input); - const simulator = new Simulator(cars); + Validator.validate(cars); + const attemptCount = await readUserInput(MESSAGE.PROMPT_COUNT_USER_INPUT); + Validator.validate(attemptCount); + + const simulator = new Simulator(cars); for (let i = 0; i < attemptCount; i++) { simulator.simulate(); } diff --git a/src/Validator.js b/src/Validator.js new file mode 100644 index 000000000..b83d42bb2 --- /dev/null +++ b/src/Validator.js @@ -0,0 +1,5 @@ +class Validator { + static validate(value) {} +} + +export default Validator; From cd6b6239705d4ae630dc248e8a0a3ee72d99a384 Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Mon, 28 Oct 2024 19:15:43 +0900 Subject: [PATCH 24/37] =?UTF-8?q?=E2=9C=A8=20feat(error):=20=EC=8B=9C?= =?UTF-8?q?=EB=8F=84=20=ED=9A=9F=EC=88=98=EA=B0=80=20=EC=96=91=EC=88=98?= =?UTF-8?q?=EA=B0=80=20=EC=95=84=EB=8B=90=20=EA=B2=BD=EC=9A=B0=20=EC=98=88?= =?UTF-8?q?=EC=99=B8=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `Validator` 클래스에 시도 횟수가 양수가 아닐 경우 오류를 던지는 로직을 추가했습니다. - `#validatePositiveNumbers` 메서드에서 시도 횟수가 0 이하일 경우 Error를 던지도록 구현했습니다. - `validate` 메서드를 수정하여 외부에서 인스턴스를 생성하지 않고도 private 필드를 사용할 수 있도록 하였습니다. --- README.md | 2 +- src/App.js | 2 +- src/Validator.js | 20 +++++++++++++++++++- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 60c1a7df6..b2b892fd4 100644 --- a/README.md +++ b/README.md @@ -30,4 +30,4 @@ - [ ] 입력값이 없는 경우 - [ ] 이름이 5자 이하가 아닐 경우 - [ ] 자동차 이름이 중복됐을 경우 -- [ ] 시도 횟수가 양수가 아닐 경우 \ No newline at end of file +- [x] 시도 횟수가 양수가 아닐 경우 \ No newline at end of file diff --git a/src/App.js b/src/App.js index 6cfaf0eee..6eee5eba6 100644 --- a/src/App.js +++ b/src/App.js @@ -11,7 +11,7 @@ class App { Validator.validate(cars); const attemptCount = await readUserInput(MESSAGE.PROMPT_COUNT_USER_INPUT); - Validator.validate(attemptCount); + Validator.validate(+attemptCount); const simulator = new Simulator(cars); for (let i = 0; i < attemptCount; i++) { diff --git a/src/Validator.js b/src/Validator.js index b83d42bb2..bc2c2d625 100644 --- a/src/Validator.js +++ b/src/Validator.js @@ -1,5 +1,23 @@ +import { ERROR_MESSAGE } from './constant'; + class Validator { - static validate(value) {} + #value; + constructor(value) { + this.#value = value; + } + + #validatePositiveNumbers() { + if (this.#value <= 0) { + throw Error(ERROR_MESSAGE.INVALID_TRY_COUNT); + } + } + + static validate(value) { + const validator = new Validator(value); + if (typeof validator.#value === 'number') { + validator.#validatePositiveNumbers(); + } + } } export default Validator; From 42be5a82a40d2a0d59f1a9a81325429717cc5b32 Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Mon, 28 Oct 2024 19:22:13 +0900 Subject: [PATCH 25/37] =?UTF-8?q?=E2=9C=A8=20feat(error):=20=EC=9E=90?= =?UTF-8?q?=EB=8F=99=EC=B0=A8=20=EC=9D=B4=EB=A6=84=EC=9D=B4=20=EC=A4=91?= =?UTF-8?q?=EB=B3=B5=EB=90=A0=20=EA=B2=BD=EC=9A=B0=20=EC=98=88=EC=99=B8=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `#validateNoDuplicates` 메서드를 추가해 배열 내 중복 요소를 검사하도록 구현했습니다. - Set을 사용해 중복을 확인하고, 중복이 있을 경우 Error를 던지도록 처리했습니다. - `validate` 메서드에서 배열 타입의 값에 대해 #validateNoDuplicates 메서드를 호출하여 중복 검사를 수행했습니다. --- README.md | 2 +- src/Validator.js | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index b2b892fd4..761cdacff 100644 --- a/README.md +++ b/README.md @@ -29,5 +29,5 @@ - [ ] 입력값이 없는 경우 - [ ] 이름이 5자 이하가 아닐 경우 -- [ ] 자동차 이름이 중복됐을 경우 +- [x] 자동차 이름이 중복될 경우 - [x] 시도 횟수가 양수가 아닐 경우 \ No newline at end of file diff --git a/src/Validator.js b/src/Validator.js index bc2c2d625..3190b14f1 100644 --- a/src/Validator.js +++ b/src/Validator.js @@ -6,16 +6,25 @@ class Validator { this.#value = value; } - #validatePositiveNumbers() { + #validatePositiveNumber() { if (this.#value <= 0) { - throw Error(ERROR_MESSAGE.INVALID_TRY_COUNT); + throw new Error(ERROR_MESSAGE.INVALID_TRY_COUNT); + } + } + + #validateNoDuplicates() { + const hasDuplicates = new Set(value).size !== this.#value.length; + if (hasDuplicates) { + throw new Error(ERROR_MESSAGE.DUPLICATE_NAME); } } static validate(value) { const validator = new Validator(value); if (typeof validator.#value === 'number') { - validator.#validatePositiveNumbers(); + validator.#validatePositiveNumber(); + } else if (typeof validator.#value === 'object') { + validator.#validateNoDuplicates(); } } } From cd1516b6a52f7466bf0414c97d51ee04d271a621 Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Mon, 28 Oct 2024 19:27:18 +0900 Subject: [PATCH 26/37] =?UTF-8?q?=E2=9C=A8=20feat(error):=20=EC=9E=90?= =?UTF-8?q?=EB=8F=99=EC=B0=A8=20=EC=9D=B4=EB=A6=84=EC=9D=B4=205=EC=9E=90?= =?UTF-8?q?=20=EC=B4=88=EA=B3=BC=EC=9D=BC=20=EA=B2=BD=EC=9A=B0=20=EC=98=88?= =?UTF-8?q?=EC=99=B8=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `#validateMaxLength `메서드를 추가해 배열 내 각 문자열의 길이를 검사하도록 구현했습니다. - 각 문자열의 길이가 `MAX_INPUT_LENGTH` 를 초과할 경우 Error를 던지도록 처리했습니다. - `MAX_INPUT_LENGTH` 상수를 5로 설정하여 최대 글자 수 제한을 정의하였습니다. --- README.md | 2 +- src/Validator.js | 11 ++++++++++- src/constant.js | 10 ++++++---- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 761cdacff..56f6014da 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,6 @@ ### 입력값 예외 처리하기 `error` - [ ] 입력값이 없는 경우 -- [ ] 이름이 5자 이하가 아닐 경우 +- [x] 이름이 5자 이하가 아닐 경우 - [x] 자동차 이름이 중복될 경우 - [x] 시도 횟수가 양수가 아닐 경우 \ No newline at end of file diff --git a/src/Validator.js b/src/Validator.js index 3190b14f1..1e77bd056 100644 --- a/src/Validator.js +++ b/src/Validator.js @@ -1,4 +1,4 @@ -import { ERROR_MESSAGE } from './constant'; +import { ERROR_MESSAGE, MAX_INPUT_LENGTH } from './constant'; class Validator { #value; @@ -12,6 +12,14 @@ class Validator { } } + #validateMaxLength() { + this.#value.forEach((value) => { + if (value.length > MAX_INPUT_LENGTH) { + throw new Error(ERROR_MESSAGE.NAME_TOO_LONG); + } + }); + } + #validateNoDuplicates() { const hasDuplicates = new Set(value).size !== this.#value.length; if (hasDuplicates) { @@ -24,6 +32,7 @@ class Validator { if (typeof validator.#value === 'number') { validator.#validatePositiveNumber(); } else if (typeof validator.#value === 'object') { + validator.#validateMaxLength(); validator.#validateNoDuplicates(); } } diff --git a/src/constant.js b/src/constant.js index b7ef78637..354bfd7c8 100644 --- a/src/constant.js +++ b/src/constant.js @@ -7,10 +7,10 @@ export const MESSAGE = Object.freeze({ }); export const ERROR_MESSAGE = Object.freeze({ - EMPTY_INPUT: '[Error] 입력값이 없습니다.', - NAME_TOO_LONG: '[Error] 이름은 5자 이하로 입력해야 합니다.', - DUPLICATE_NAME: '[Error] 자동차 이름은 중복될 수 없습니다.', - INVALID_TRY_COUNT: '[Error] 시도 횟수는 양수여야 합니다.', + EMPTY_INPUT: '[ERROR] 입력값이 없습니다.', + NAME_TOO_LONG: '[ERROR] 이름은 5자 이하로 입력해야 합니다.', + DUPLICATE_NAME: '[ERROR] 자동차 이름은 중복될 수 없습니다.', + INVALID_TRY_COUNT: '[ERROR] 시도 횟수는 양수여야 합니다.', }); export const RANGE = Object.freeze({ @@ -18,3 +18,5 @@ export const RANGE = Object.freeze({ END: 9, VALID: 4, }); + +export const MAX_INPUT_LENGTH = 5; From 1dac794b9763ea94c017c70d42915d8c7d69f652 Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Mon, 28 Oct 2024 19:44:33 +0900 Subject: [PATCH 27/37] =?UTF-8?q?=E2=9C=A8=20feat(error):=20=EC=9E=85?= =?UTF-8?q?=EB=A0=A5=EA=B0=92=EC=9D=B4=20=EC=97=86=EB=8A=94=20=EA=B2=BD?= =?UTF-8?q?=EC=9A=B0=20=EC=98=88=EC=99=B8=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - #validateNotEmpty 메서드를 추가해 입력값이 비어 있는지 검사하도록 구현했습니다. --- README.md | 2 +- src/Validator.js | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 56f6014da..85b70f43b 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ ### 입력값 예외 처리하기 `error` -- [ ] 입력값이 없는 경우 +- [x] 입력값이 없는 경우 - [x] 이름이 5자 이하가 아닐 경우 - [x] 자동차 이름이 중복될 경우 - [x] 시도 횟수가 양수가 아닐 경우 \ No newline at end of file diff --git a/src/Validator.js b/src/Validator.js index 1e77bd056..d46233cfa 100644 --- a/src/Validator.js +++ b/src/Validator.js @@ -12,6 +12,12 @@ class Validator { } } + #validateNotEmpty() { + if (!this.#value) { + throw new Error(ERROR_MESSAGE.EMPTY_INPUT); + } + } + #validateMaxLength() { this.#value.forEach((value) => { if (value.length > MAX_INPUT_LENGTH) { @@ -21,7 +27,7 @@ class Validator { } #validateNoDuplicates() { - const hasDuplicates = new Set(value).size !== this.#value.length; + const hasDuplicates = new Set(this.#value).size !== this.#value.length; if (hasDuplicates) { throw new Error(ERROR_MESSAGE.DUPLICATE_NAME); } @@ -32,6 +38,7 @@ class Validator { if (typeof validator.#value === 'number') { validator.#validatePositiveNumber(); } else if (typeof validator.#value === 'object') { + validator.#validateNotEmpty(); validator.#validateMaxLength(); validator.#validateNoDuplicates(); } From b09c4922ac62b7318b1f103e2aa66bd760ab6141 Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Mon, 28 Oct 2024 19:47:02 +0900 Subject: [PATCH 28/37] =?UTF-8?q?=F0=9F=9B=A0=EF=B8=8F=20chore:=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=EB=AA=85=20`random.js`=20=EB=A5=BC=20`Random?= =?UTF-8?q?Test.js`=20=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `__test__` 폴더 내에 다른 테스트 파일명들과 일관성을 유지하기 위해 변경했습니다. --- __tests__/{random.js => RandomTest.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename __tests__/{random.js => RandomTest.js} (100%) diff --git a/__tests__/random.js b/__tests__/RandomTest.js similarity index 100% rename from __tests__/random.js rename to __tests__/RandomTest.js From 4a502f8e04822360be86a1d96d0baa4f0d807e38 Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Mon, 28 Oct 2024 20:28:43 +0900 Subject: [PATCH 29/37] =?UTF-8?q?=F0=9F=A7=AA=20test(error):=20Application?= =?UTF-8?q?=EC=9D=98=20=EC=98=88=EC=99=B8=20=EC=B2=98=EB=A6=AC=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `App` 클래스의 `run` 메서드에서 발생 가능한 다양한 예외 상황에 대한 테스트 코드를 추가했습니다. - `test.each` 를 사용해 빈 입력값, 중복된 이름, 이름 길이 초과, 시도 횟수의 유효성 검사 등의 케이스를 검증했습니다. - `Validator` 의 예외 메시지와 일치하는지 확인하여 입력값에 대한 유효성 검증 로직이 제대로 동작하는지 확인하였습니다. --- __tests__/ApplicationTest.js | 39 ++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/__tests__/ApplicationTest.js b/__tests__/ApplicationTest.js index 0260e7e84..7871d9058 100644 --- a/__tests__/ApplicationTest.js +++ b/__tests__/ApplicationTest.js @@ -1,5 +1,7 @@ -import App from "../src/App.js"; -import { MissionUtils } from "@woowacourse/mission-utils"; +import App from '../src/App.js'; +import { MissionUtils } from '@woowacourse/mission-utils'; +import { ERROR_MESSAGE } from '../src/constant.js'; +import Validator from '../src/Validator.js'; const mockQuestions = (inputs) => { MissionUtils.Console.readLineAsync = jest.fn(); @@ -19,18 +21,18 @@ const mockRandoms = (numbers) => { }; const getLogSpy = () => { - const logSpy = jest.spyOn(MissionUtils.Console, "print"); + const logSpy = jest.spyOn(MissionUtils.Console, 'print'); logSpy.mockClear(); return logSpy; }; -describe("자동차 경주", () => { - test("기능 테스트", async () => { +describe('자동차 경주', () => { + test('기능 테스트', async () => { // given const MOVING_FORWARD = 4; const STOP = 3; - const inputs = ["pobi,woni", "1"]; - const logs = ["pobi : -", "woni : ", "최종 우승자 : pobi"]; + const inputs = ['pobi,woni', '1']; + const logs = ['pobi : -', 'woni : ', '최종 우승자 : pobi']; const logSpy = getLogSpy(); mockQuestions(inputs); @@ -46,15 +48,22 @@ describe("자동차 경주", () => { }); }); - test("예외 테스트", async () => { - // given - const inputs = ["pobi,javaji"]; + test.each([ + { inputs: [], expectedError: ERROR_MESSAGE.EMPTY_INPUT }, + { inputs: ['pobi,javaji'], expectedError: ERROR_MESSAGE.NAME_TOO_LONG }, + { inputs: ['pobi,pobi'], expectedError: ERROR_MESSAGE.DUPLICATE_NAME }, + { inputs: ['pobi,java'], expectedError: ERROR_MESSAGE.EMPTY_INPUT }, + { + inputs: ['pobi,java', '-1'], + expectedError: ERROR_MESSAGE.INVALID_TRY_COUNT, + }, + { + inputs: ['pobi,java', '0'], + expectedError: ERROR_MESSAGE.INVALID_TRY_COUNT, + }, + ])('예외 테스트: %o', async ({ inputs, expectedError }) => { mockQuestions(inputs); - - // when const app = new App(); - - // then - await expect(app.run()).rejects.toThrow("[ERROR]"); + await expect(app.run()).rejects.toThrow(expectedError); }); }); From 8311733ec723e1fdad926958243999ab1b6871e8 Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Mon, 28 Oct 2024 20:31:19 +0900 Subject: [PATCH 30/37] =?UTF-8?q?=F0=9F=90=9B=20fix(error):=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EC=9E=90=20=EC=9E=85=EB=A0=A5=EA=B0=92=EC=97=90=20?= =?UTF-8?q?=EB=8C=80=ED=95=9C=20=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B2=80?= =?UTF-8?q?=EC=82=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 기존: `input` 을 `split` 한 부분만 유효성 검사 - 변경: `input`유효성 검사 후 `split` - `splitByComma` 로 자동차 이름을 나누기 전에, 입력값 자체가 유효한지 검증이 필요했습니다. - 그렇지 않으면, `split` 메서드 사용에서 에러가 발생했습니다. --- src/App.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/App.js b/src/App.js index 6eee5eba6..854ef9e9d 100644 --- a/src/App.js +++ b/src/App.js @@ -7,6 +7,7 @@ import Validator from './Validator'; class App { async run() { const input = await readUserInput(MESSAGE.PROMPT_NAME_USER_INPUT); + Validator.validate(input); const cars = splitByComma(input); Validator.validate(cars); From 181c549140f10110affe01f20778f7eca256c82a Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Mon, 28 Oct 2024 20:33:55 +0900 Subject: [PATCH 31/37] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor(error):=20`?= =?UTF-8?q?validate`=20=EB=A9=94=EC=84=9C=EB=93=9C=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EB=B9=88=20=EA=B0=92=20=EA=B2=80=EC=A6=9D=20=EB=A1=9C=EC=A7=81?= =?UTF-8?q?=20=EC=88=9C=EC=84=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 이전: object 일 때만 빈 값 검증 - 변경: type에 상관없이 빈 값 검증 - validate 메서드에서 `#validateNotEmpty()` 를 모든 타입의 값에 대해 우선적으로 검증하도록 수정했습니다. - 빈 값 검증을 가장 먼저 수행하여 이후의 유효성 검사 로직이 실행되기 전에 입력값의 존재를 확인할 수 있게 됐습니다. - 숫자 0이 빈 값으로 검증되지 않게 하기 위해 조건문을 추가했습니다. --- src/Validator.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Validator.js b/src/Validator.js index d46233cfa..36aa4d8f9 100644 --- a/src/Validator.js +++ b/src/Validator.js @@ -6,15 +6,15 @@ class Validator { this.#value = value; } - #validatePositiveNumber() { - if (this.#value <= 0) { - throw new Error(ERROR_MESSAGE.INVALID_TRY_COUNT); + #validateNotEmpty() { + if (this.#value !== 0 && !this.#value) { + throw new Error(ERROR_MESSAGE.EMPTY_INPUT); } } - #validateNotEmpty() { - if (!this.#value) { - throw new Error(ERROR_MESSAGE.EMPTY_INPUT); + #validatePositiveNumber() { + if (this.#value <= 0) { + throw new Error(ERROR_MESSAGE.INVALID_TRY_COUNT); } } @@ -35,10 +35,10 @@ class Validator { static validate(value) { const validator = new Validator(value); + validator.#validateNotEmpty(); if (typeof validator.#value === 'number') { validator.#validatePositiveNumber(); } else if (typeof validator.#value === 'object') { - validator.#validateNotEmpty(); validator.#validateMaxLength(); validator.#validateNoDuplicates(); } From c269b9b0ca44013db8a349e3f8680f0015928c24 Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Mon, 28 Oct 2024 21:17:22 +0900 Subject: [PATCH 32/37] =?UTF-8?q?=F0=9F=93=9A=20docs(README):=20=EC=B2=B4?= =?UTF-8?q?=ED=81=AC=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EB=B0=8F=20=ED=8F=B4?= =?UTF-8?q?=EB=8D=94=EA=B5=AC=EC=A1=B0=20=ED=99=95=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 85b70f43b..af6038284 100644 --- a/README.md +++ b/README.md @@ -30,4 +30,43 @@ - [x] 입력값이 없는 경우 - [x] 이름이 5자 이하가 아닐 경우 - [x] 자동차 이름이 중복될 경우 -- [x] 시도 횟수가 양수가 아닐 경우 \ No newline at end of file +- [x] 시도 횟수가 양수가 아닐 경우 + +
+ +## 🚨 체크리스트 +- [x] JavaScript Code Conventions 을 준수하였는가? +- [x] 한 메서드에 오직 한 단계의 들여쓰기만 허용했는가? +- [x] else 예약어를 쓰지 않았는가? +- [x] 모든 원시값과 문자열을 포장했는가? +- [x] 3개 이상의 인스턴스 변수를 가진 클래스를 구현하지 않았는가? +- [x] getter/setter 없이 구현했는가? +- [x] 메소드의 인자 수를 제한했는가? +- [ ] 코드 한 줄에 점(.)을 하나만 허용했는가? +- [x] 메소드가 한가지 일만 담당하도록 구현했는가? +- [x] 클래스를 작게 유지하기 위해 노력했는가? +- [x] 3항 연산자를 사용하지 않았는가? +- [x] AngularJS Commit Conventions 에 맞춰 Commit Message를 작성했는가? +- [x] 이름을 축약하지 않고 의도를 잘 드러냈는가? +- [x] JavaScript API를 적극 활용했는가? +- [x] Jest를 활용해 테스트 코드로 작동을 확인했는가? +- [x] 테스트 코드를 관심사별로 분리했는가? + +
+ +## 📄 폴더구조 +``` +📦 __tests__ + ┗━ 📜ApplicationTest.js +📦 src + ┣━ 📜App.js + ┣━ 📜index.js + ┣━ 📂util + ┃ ┣━ 📜io.js + ┃ ┣━ 📜pickRandomNumberInRange.js + ┃ ┣━ 📜printSimulate.js + ┃ ┗━ 📜splitByComma.js + ┣━ 📜Simulator.js + ┣━ 📜Validator.js + ┗━ 📜constant.js +``` \ No newline at end of file From 03d25085e4a3d68475ea1a4176726b82d91f3458 Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Mon, 28 Oct 2024 21:32:42 +0900 Subject: [PATCH 33/37] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor(simulate):?= =?UTF-8?q?=20=EB=B0=98=EB=B3=B5=20=EB=A1=9C=EC=A7=81=EC=9D=84=20`simulate?= =?UTF-8?q?`=20=EB=A9=94=EC=84=9C=EB=93=9C=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= =?UTF-8?q?=ED=95=98=EC=97=AC=20=EA=B4=80=EC=8B=AC=EC=82=AC=20=EA=B7=B8?= =?UTF-8?q?=EB=A3=B9=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 시도 횟수에 따른 시뮬레이션 반복 로직을 `App.run()`에서 `Simulator`의 `simulate` 메서드로 이동시켰습니다. - 시뮬레이션 횟수와 상태 업데이트가 동일한 관심사라고 판단했기 때문입니다. - 코드의 응집도를 높이고, `App.run()`의 책임을 줄여 가독성 및 유지보수성을 개선시켰습니다. --- __tests__/SimulateTest.js | 10 +++++----- src/App.js | 4 +--- src/Simulator.js | 16 +++++++++------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/__tests__/SimulateTest.js b/__tests__/SimulateTest.js index 21ac16954..0241f2692 100644 --- a/__tests__/SimulateTest.js +++ b/__tests__/SimulateTest.js @@ -17,7 +17,7 @@ describe('Simulator simulate test', () => { pickRandomNumberInRange.mockReturnValue(5); console.log(simulator.state); - simulator.simulate(); + simulator.simulate(1); expect(printOutput).toHaveBeenCalledWith(MESSAGE.EXECUTE_OUTPUT); expect(printOutput).toHaveBeenCalledWith('Car1 : -'); expect(printOutput).toHaveBeenCalledWith(''); @@ -28,7 +28,7 @@ describe('Simulator simulate test', () => { const simulator = new Simulator(names); pickRandomNumberInRange.mockReturnValue(3); - simulator.simulate(); + simulator.simulate(1); expect(printOutput).toHaveBeenCalledWith(MESSAGE.EXECUTE_OUTPUT); expect(printOutput).toHaveBeenCalledWith('Car1 : '); @@ -40,7 +40,7 @@ describe('Simulator simulate test', () => { const simulator = new Simulator(names); pickRandomNumberInRange.mockReturnValueOnce(5).mockReturnValueOnce(3); - simulator.simulate(); + simulator.simulate(1); expect(printOutput).toHaveBeenCalledWith(MESSAGE.EXECUTE_OUTPUT); expect(printOutput).toHaveBeenCalledWith('Car1 : -'); @@ -61,7 +61,7 @@ describe('Simulator printWinner test', () => { pickRandomNumberInRange.mockReturnValueOnce(5); pickRandomNumberInRange.mockReturnValue(3); - simulator.simulate(); + simulator.simulate(1); simulator.printWinner(); expect(printOutput).toHaveBeenCalledWith(`${MESSAGE.FINAL_OUTPUT} : Car1`); @@ -75,7 +75,7 @@ describe('Simulator printWinner test', () => { pickRandomNumberInRange.mockReturnValueOnce(5); pickRandomNumberInRange.mockReturnValue(3); - simulator.simulate(); + simulator.simulate(1); simulator.printWinner(); expect(printOutput).toHaveBeenCalledWith( diff --git a/src/App.js b/src/App.js index 854ef9e9d..584e21a02 100644 --- a/src/App.js +++ b/src/App.js @@ -15,9 +15,7 @@ class App { Validator.validate(+attemptCount); const simulator = new Simulator(cars); - for (let i = 0; i < attemptCount; i++) { - simulator.simulate(); - } + simulator.simulate(attemptCount); simulator.printWinner(); } } diff --git a/src/Simulator.js b/src/Simulator.js index faa7d61a0..b48011613 100644 --- a/src/Simulator.js +++ b/src/Simulator.js @@ -20,13 +20,15 @@ class Simulator { return false; } - simulate() { - this.#state = this.#state.map(({ name, count }) => { - if (this.#canMoveForward()) count++; - printSimulate(name, count); - return { name, count }; - }); - printOutput(''); + simulate(attemptCount) { + for (let i = 0; i < attemptCount; i++) { + this.#state = this.#state.map(({ name, count }) => { + if (this.#canMoveForward()) count++; + printSimulate(name, count); + return { name, count }; + }); + printOutput(''); + } } printWinner() { From 46fda478a94a595d53395f77887ddb6bdcd0840c Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Mon, 28 Oct 2024 21:38:40 +0900 Subject: [PATCH 34/37] =?UTF-8?q?=F0=9F=9B=A0=EF=B8=8F=20chore:=20?= =?UTF-8?q?=EB=B6=88=ED=95=84=EC=9A=94=ED=95=9C=20=EC=B6=9C=EB=A0=A5=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- __tests__/SimulateTest.js | 1 - 1 file changed, 1 deletion(-) diff --git a/__tests__/SimulateTest.js b/__tests__/SimulateTest.js index 0241f2692..ad9b478c8 100644 --- a/__tests__/SimulateTest.js +++ b/__tests__/SimulateTest.js @@ -15,7 +15,6 @@ describe('Simulator simulate test', () => { const names = ['Car1']; const simulator = new Simulator(names); pickRandomNumberInRange.mockReturnValue(5); - console.log(simulator.state); simulator.simulate(1); expect(printOutput).toHaveBeenCalledWith(MESSAGE.EXECUTE_OUTPUT); From cc5c795562c91b3bddc52d5c218236b12805196c Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Mon, 28 Oct 2024 23:55:11 +0900 Subject: [PATCH 35/37] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor(simulate):?= =?UTF-8?q?=20=EB=B6=88=ED=95=84=EC=9A=94=ED=95=9C=20=EB=B6=84=EA=B8=B0=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `#canMoveForward` 메서드를 간결하게 리팩토링했습니다. - 불필요한 if-else 구조를 제거하고, 조건문을 직접 반환하는 방식으로 코드를 간소화했습니다. --- src/Simulator.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Simulator.js b/src/Simulator.js index b48011613..317e7b1c0 100644 --- a/src/Simulator.js +++ b/src/Simulator.js @@ -15,9 +15,7 @@ class Simulator { } #canMoveForward() { - if (pickRandomNumberInRange(RANGE.START, RANGE.END) >= RANGE.VALID) - return true; - return false; + return pickRandomNumberInRange(RANGE.START, RANGE.END) >= RANGE.VALID; } simulate(attemptCount) { From 616654d083f88ac95cfe75b1b6f9e8720e03c4c9 Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Mon, 28 Oct 2024 23:56:33 +0900 Subject: [PATCH 36/37] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor(simulate):?= =?UTF-8?q?=20names=20=EB=B0=B0=EC=97=B4=20=EC=B4=88=EA=B8=B0=ED=99=94=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=EC=9D=84=20reduce=EC=97=90=EC=84=9C=20map?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `this.#state` 초기화 시 `reduce` 대신 `map`을 사용하여 간결하게 수정했습니다. --- src/Simulator.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Simulator.js b/src/Simulator.js index 317e7b1c0..025e7bd3f 100644 --- a/src/Simulator.js +++ b/src/Simulator.js @@ -8,10 +8,7 @@ class Simulator { constructor(names) { printOutput(MESSAGE.EXECUTE_OUTPUT); - this.#state = names.reduce((arr, name) => { - arr.push({ name, count: 0 }); - return arr; - }, []); + this.#state = names.map((name) => ({ name, count: 0 })); } #canMoveForward() { From 479e7531f984be3c1978181b3077d2dc9bb52c0e Mon Sep 17 00:00:00 2001 From: Lee jin <83453646+j-nary@users.noreply.github.com> Date: Mon, 28 Oct 2024 23:59:52 +0900 Subject: [PATCH 37/37] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor(error):=20f?= =?UTF-8?q?orEach=EC=97=90=EC=84=9C=20some=EC=9C=BC=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Validator.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Validator.js b/src/Validator.js index 36aa4d8f9..075b3d733 100644 --- a/src/Validator.js +++ b/src/Validator.js @@ -19,11 +19,9 @@ class Validator { } #validateMaxLength() { - this.#value.forEach((value) => { - if (value.length > MAX_INPUT_LENGTH) { - throw new Error(ERROR_MESSAGE.NAME_TOO_LONG); - } - }); + if (this.#value.some((value) => value.length > MAX_INPUT_LENGTH)) { + throw new Error(ERROR_MESSAGE.NAME_TOO_LONG); + } } #validateNoDuplicates() {