-
Notifications
You must be signed in to change notification settings - Fork 206
[자동차 경주] 강성준 미션 제출합니다. #205
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
a651ed0
922135f
fdfacb9
0c0f891
8d347c3
3a32f8c
a8f602a
b95acb0
21d7292
fdf4494
d5d0950
22897e7
5a12cfb
de75d0d
4a08e8e
a5355f3
1feb6a2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,97 @@ | ||
| # javascript-racingcar-precourse | ||
| # 학습 목표 | ||
| - 여러 역할을 하는 큰 함수를 단일 역할을 하는 작은 함수로 분리한다. | ||
| - 테스트 도구를 사용하는 방법을 배우고 프로그램이 제대로 작동하는 지 테스트 한다. | ||
| - 1주차 피드백을 반영한다. | ||
| # 요구사항 정리 | ||
| ### 1주차 피드백 정리 | ||
| - git 명령어, git 자원, 관리 파일(package.json, package-lock.json 등) 역할 학습 | ||
| - 디버거 사용에 익숙해지기 | ||
| - 좋은 이름 짓기 | ||
| - 정확한 공백 사용 | ||
| - 의미 없는 주석 달지 않기 | ||
| - JavaScript API 적극 활용 | ||
| ### 기능 요구사항 | ||
| 자동차 경주 게임을 구현 | ||
| - 사용자는 자동차의 이름과 몇 번 이동할 것 인지 입력할 수 있다. | ||
| - 이름에 대한 입력은 쉼표를 기준으로 구분하며, 각 자동차 명은 5자 이하 | ||
| - 주어진 횟수 동안 전진/정지 할 수 있다. | ||
| - 0~9 무작위 값이 4 이상일 경우 전진 / 4 미만 정지 | ||
| - 자동차 경주 게임이 종료된 후 누가 우승했는지를 알려준다. | ||
| - 우승자가 여러 명일 경우 쉼표(,)를 이용하여 구분 | ||
| - 사용자가 잘못된 값을 입력할 경우 "[ERROR]"로 시작하는 메시지와 함께 Error를 발생시킨 후 애플리케이션은 종료된다. | ||
| ### 프로그래밍 요구사항 | ||
| - 함수가 한가지 일만 하도록 작게 만들기 | ||
| - 들어쓰기 깊이 2까지만 허용 | ||
| - 3항 연산자 사용 X | ||
| - Jest로 테스트 코드 작성하여 기능 정상 작동 확인 | ||
| - Jest 활용법 공부하기 | ||
|
|
||
| # 설계 | ||
| ## 함수 구조(구현 전 설계) | ||
| ```mermaid | ||
| flowchart TD | ||
| %% 최상위 (부수효과 담당) | ||
| main["App.run()"] | ||
| %% 핵심 로직 (순수) | ||
| main --> Race | ||
|
|
||
| subgraph Race["Race (순수 함수 계층)"] | ||
| direction TB | ||
| R1["runEntireRace()"] --> R2["moveCarsOneLap()"] | ||
| R2 --> R3["decideCarMoveOrNot()"] | ||
| R1 --> R4["applySingleLapResult()"] | ||
| R1 --> R6["selectWinningCars()"] | ||
| end | ||
|
|
||
| %% 입력 처리 | ||
| main --> input["getInputUsingMissionUtils()"] | ||
| %% 결과 출력 | ||
| main --> output["printOutputUsingMissionUtils()"] | ||
| %% 결과 렌더링 | ||
| main --> render["renderHistory()"] | ||
| render --> output | ||
|
|
||
| %% 외부 난수 API | ||
| main --> random["makeRandomNumberArray()"] | ||
| ``` | ||
| ## 기능 목록 | ||
| - [x] 입력 | ||
| - [x] 자동차 명 입력 받기 | ||
| - [x] `,` 기준으로 자동차명을 구분 | ||
| - [x] 진행 랩 수(몇번 진행할 것인지) 입력 받기 | ||
| - [x] 입력값 검증(예외) | ||
| - [x] 사용자가 잘못된 값을 입력할 경우 `[ERROR]`로 시작하는 문구와 함께 에러를 발생시키고 프로그램을 종료시킨다. | ||
| - [x] 각 자동차명은 공백을 포함하지 않은 5자 이하의 문자열이다. | ||
| - [x] 각 자동차명은 중복을 허용하지 않는다. | ||
| - [x] 랩 수는 양의 정수이다. | ||
| - [x] 랜덤 넘버 데이터 생성 | ||
| - [x] `자동차 수 * 랩 수` 만큼의 랜덤 숫자(0~9사이 정수) 생성 | ||
| - [x] 레이스 진행(전진 or 정지) | ||
| - [x] 랜덤 숫자가 4 이상(4,5,6,7,8,9)이면 전진, 4미만(0,1,2,3)이면 정지 | ||
| - [x] 우승자 결정 | ||
| - [x] 가장 많이 전진한 자동차가 우승자로 결정된다. | ||
| - [x] 공동 우승도 인정된다. | ||
| - [x] 레이스 히스토리 출력 | ||
| - 각 랩 순으로 레이스 기록을 출력한다. | ||
| - [x] 우승자명 출력 | ||
| - 동률의 경우 `,`로 구분하여 출력한다. | ||
|
|
||
| # 문제 해결 과정 | ||
| ### 설계 단계 | ||
| > OOP + FP(함수형 프로그래밍) | ||
| - 해결해야하는 문제: 프리코스 기간 동안 직접 함수형 패러다임을 공부하고 적용을 시도해보고 있습니다. 함수형 패러다임을 OOP에 융합 시키기 위한 구조 설계에 어려움을 겪고 있습니다. | ||
| - 어떻게 해결했는지: 큰 틀에서 보면 `App 클래스`가 `부수효과(입출력, API)`를 모두 담당하고 레이스를 진행하는 부분은 순수함수로 작성하려고 합니다. 이를 바탕으로 함수명 구조를 짜보았는데, 아마 구현을 진행하면서 많은 수정이 있을 것 같습니다. | ||
| > Car 클래스 분리 | ||
| - 해결해야하는 문제: Car을 객체로 만들어서 관리한다는 아이디어가 자연스레 떠올랐습니다. 의미적으로 확실하게 분리가 되니까 관리와 가독성에 장점이 있다고 생각합니다. 하지만 자동차가 복잡도 높은 기능을 수행하는 것도 아니기 때문에 고민이 되었습니다. | ||
| - 어떻게 해결했는지: 클래스보다는 객체와 같은 구조로 관리를 하여 함수형 중심적으로 설계를 했습니다. | ||
| > 랜덤 넘버 생성 | ||
| - 해결해야하는 문제: 함수형 패러다임의 적용과 연결되는 부분입니다. API를 통해서 생성하는 난수들을 어떻게 생성하고 사용할 지에 대한 문제입니다. 처음에는 별 생각없이 매 라운드마다 난수를 만들어서 판별하도록 하면 되겠다고 생각했지만 설계를 하다보니 레이스마다 매번 난수를 생성하면 순수성을 유지하지 못하고, 테스트에도 어려움이 있을 것이라 판단했습니다. | ||
| - 어떻게 해결했는지: App 단에서 필요한 개수의 난수를 모두 생성하여 넘겨주는 방식으로 문제를 해결해보려 합니다. 마찬가지로 매 랩읙 결과를 모두 끝이 나고 한 번에 순서대로 출력하는 방식으로 진행하려고 합니다. | ||
| ### 구현 단계 | ||
| > 데이터 구조 | ||
| - 해결해야하는 문제: 난수 테이프 부분에서 필요한 모든 난수를 미리 만들어서 레이스를 진행하는 흐름을 처음 구현하다보니 데이터 구조 결정에 문제있었습니다. 기능 중심으로만 설계하다 보니, 각 함수가 어떤 데이터를 주고받을지에 대한 구조적 고려가 부족했던 것이 원인이었습니다. | ||
| - 어떻게 해결했는지: 처음에는 단순히 HashMap을 사용하여 <자동차명-히스토리 배열>을 이루면 쉽게 구현애 가능하겠구나 생각했습니다. 여러 데이터를 파라미터로 넘겨주다 보니 시간복잡도와 메모리 비용이 너무 크게 발생함을 깨닫고, 2차원 배열로 히스토리를 저장하는 방식으로 문제를 해결했습니다. | ||
| > FIFO vs LIFO | ||
| - 해결해야하는 문제: 모든 테스트 코드 중 제공된 테스트만 통과하지 못하는 문제가 있었습니다. | ||
| - 어떻게 해결했는지: 난수를 생성해서 배열에 push함수를 사용해서 난수테이프를 만들었기에, 자연스레 pop()을 사용했던 것이 문제였습니다. 먼저 push된 것을 먼저 사용할 수 있도록 shift()를 사용하여 문제를 해결했습니다. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| 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 getLogSpy = () => { | ||
| const logSpy = jest.spyOn(MissionUtils.Console, "print"); | ||
| logSpy.mockClear(); | ||
| return logSpy; | ||
| }; | ||
|
|
||
| describe("woowacourse/mission-utils api 테스트", () => { | ||
| let app; | ||
| beforeEach(() => { | ||
| app = new App(); | ||
| }) | ||
|
|
||
| test.each([ | ||
| ["경주할 자동차 이름을 입력해주세요.", "a,b,c"], | ||
| ["경주할 자동차의 이름을 입력해주십쇼.","hihi, woowa, pre"], | ||
| ["시도할 횟수는 몇 회인가요?", "5"] | ||
| ])("readLineAsync가 입력값(자동차명 or 랩 수)을 정상적으로 처리되어 가져온다.", async (question, answer) => { | ||
| mockQuestions([answer]); | ||
| const userReply = await app.readInputAsyncUsingWoowaMissionApi(question); | ||
| expect(MissionUtils.Console.readLineAsync).toHaveBeenCalledWith(question); | ||
| expect(userReply).toBe(answer); | ||
| }); | ||
|
|
||
| test("Random.pickNumberInRange를 사용해 0 이상 9 이하의 정수를 생성한다", () => { | ||
| const results = Array.from({ length: 100 }, () => | ||
| app.pickRandomNumberInRangeUsingWoowaMissionApi(0, 9) | ||
| ); | ||
|
|
||
| for (const num of results) { | ||
| expect(Number.isInteger(num)).toBe(true); | ||
| expect(num).toBeGreaterThanOrEqual(0); | ||
| expect(num).toBeLessThanOrEqual(9); | ||
| } | ||
| }); | ||
|
|
||
| test("Console.print를 사용해 레이스의 히스토리를 출력한다.", () => { | ||
| const logs = ["a : -", "b : ", "a : --", "a : --\nb : -\nc : -"] | ||
| const logSpy = getLogSpy(); | ||
| app.printHistoryOfRace([[1, 0, 1], [1, 1, 1], [2, 1, 1]], ["a", "b", "c"]); | ||
| logs.forEach((log) => { | ||
| expect(logSpy).toHaveBeenCalledWith(expect.stringContaining(log)); | ||
| }); | ||
| }); | ||
|
|
||
| test("Console.print를 사용해 우승자를 출력한다.", () => { | ||
| const log = "최종 우승자 : a, b"; | ||
| const logSpy = getLogSpy(); | ||
| app.printWinnerOfRace(["a", "b"]); | ||
| expect(logSpy).toHaveBeenCalledWith(expect.stringContaining(log)); | ||
| }) | ||
| }) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| import { runEntireRace } from "../src/domains/race"; | ||
| import { determineWinnerOfRace, findMaxValue } from "../src/domains/queries"; | ||
|
|
||
| describe("레이스 진행 도메인 테스트", () => { | ||
| test.each([ | ||
| [["a", "b", "c"], 2, [4, 1, 2, 9, 0, 4], [[1, 0, 0], [2, 0, 1]]], | ||
| [["red", "blue", "black"], 3, [5, 2, 4, 9, 5, 9, 9, 4, 6], [[1, 0, 1], [2, 1, 2], [3, 2, 3]]] | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 테스트 코드에 작성해주신 숫자들이 너무나도 불규칙적이어서 다소 무슨 테스트를 하는지 헷갈리는것 같습니다
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 생각도 못해 본 부분이네요! 각 테스트의 규칙적이고 필요한 이유에 대해서 생각해보겠습니다. 새로운 방식 추천도 감사드립니다 ㅎㅎ |
||
| ])("레이스 진행하여 전체 히스토리를 기록한다.", (carnames, laps, randomTape, raceHistory) => { | ||
| const result = runEntireRace(carnames, laps, randomTape); | ||
| expect(result).toStrictEqual(raceHistory); | ||
| }) | ||
| test("숫자 배열에서 최댓값을 찾는다.", () => { | ||
| expect(findMaxValue([2, 3, 8])).toBe(8); | ||
| }) | ||
| test.each([ | ||
| [["a", "ab", "c"], [2, 5, 4], ["ab"]], | ||
| [["red", "black", "blue"], [9, 19, 19], ["black", "blue"]] | ||
| ])("최종 점수 배열에서 우승자를 선택한다.", (names, scores, expectWinner) => { | ||
| const winner = determineWinnerOfRace(names, scores); | ||
| expect(winner).toStrictEqual(expectWinner); | ||
| }) | ||
| }) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| import { parseByComma } from "../src/utils/parsing"; | ||
| import { validateCarNameRule, validateLapNumberRule } from "../src/utils/validator"; | ||
|
|
||
| describe("유틸 함수 테스트", () => { | ||
| test.each(([ | ||
| ["a,b,c", ["a", "b", "c"]], | ||
| ["ab,cd,e,f ", ["ab", "cd", "e", "f"]], | ||
| [" a , b, c, d ", ["a", "b", "c", "d"]], | ||
| ["a,b,,d", ["a", "b", "", "d"]] | ||
| ]))("쉼표를 기준으로 문자열을 파싱해주는 parseByComma 테스트", (input, parsed) => { | ||
| const parsingInput = parseByComma(input); | ||
| expect(parsingInput).toStrictEqual(parsed); | ||
| }); | ||
|
|
||
| test.each(([ | ||
| [["a", "aaaa", "bbbbbb"], "[ERROR] : 자동차 명은 5자 이하여야 합니다."], | ||
| [["a", "a", "ab"], "[ERROR] : 중복된 이름을 사용할 수 없습니다."] | ||
| ]))("자동차명 검증 테스트", (carNameHasErr, errMsg) => { | ||
| expect(() => validateCarNameRule(carNameHasErr)).toThrow(errMsg); | ||
| }); | ||
|
|
||
| test.each(([ | ||
| [2.1, "[ERROR] : 횟수는 양의 정수이어야 합니다."], | ||
| [-1, "[ERROR] : 횟수는 양의 정수이어야 합니다."], | ||
| [NaN, "[ERROR] : 횟수는 양의 정수이어야 합니다."], | ||
| [null, "[ERROR] : 횟수는 양의 정수이어야 합니다."], | ||
| [undefined, "[ERROR] : 횟수는 양의 정수이어야 합니다."], | ||
|
Comment on lines
+23
to
+27
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이런 부분들은 재사용되는 문구니까 상수화해도 좋을 것 같습니다!
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 3주차 미션 때 상수화 부분을 적용해보려고 합니다. 감사합니다!! |
||
| ]))("횟수 검증 테스트", (numberHasErr, errMsg) => { | ||
| expect(() => validateLapNumberRule(numberHasErr)).toThrow(errMsg); | ||
| }) | ||
| }) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,51 @@ | ||
| class App { | ||
| async run() {} | ||
| } | ||
| import { MissionUtils } from "@woowacourse/mission-utils"; | ||
| import { parseByComma, parseToHistoryFormat, parserToWinnerFormat } from "./utils/parsing"; | ||
| import { runEntireRace } from "./domains/race"; | ||
| import { determineWinnerOfRace } from "./domains/queries"; | ||
| import { validateCarNameRule, validateLapNumberRule } from "./utils/validator"; | ||
|
|
||
| class App { | ||
| async run() { | ||
| // 입력(자동차명, 횟수) | ||
| const stringOfCarNamesUserRequest = await this.readInputAsyncUsingWoowaMissionApi("경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)"); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 함수명을 조금은 줄여도 되지 않을까 싶습니다...
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 제가 봐도 조금 과한 부분인 것 같습니다. 타인이나 나중에 제가 코드를 다시 본다면 이해할 수 있을까를 고민하다가, 최대한 디테일하게 이름을 지어보자는 의도였습니다. |
||
| const arrayOfCarNamesUserRequest = parseByComma(stringOfCarNamesUserRequest); | ||
| validateCarNameRule(arrayOfCarNamesUserRequest); | ||
| const stringOfLapUserRequest = await this.readInputAsyncUsingWoowaMissionApi("시도할 횟수는 몇 회인가요?"); | ||
| const countOfLapUserRequest = Number(stringOfLapUserRequest); | ||
| validateLapNumberRule(countOfLapUserRequest); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 흐름 가장 이상적이네요 .. |
||
| // 레이스 진행 | ||
| const randomNumberTape = this.makeRandomNumbersTape(arrayOfCarNamesUserRequest, countOfLapUserRequest); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 미리 개수만큼 랜덤을 뽑는건가요 ? race 의 책임 경계에서 고민을 많이하셨을거 같습니다
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 레이스를 진행하는 부분을 순수함수로 구현하고자 함이었습니다. 그렇다 보니 입력, 랜덤과 같은 부분을 분리하려고 했습니다. 메모리에 대한 트레이드오프가 있지만 테스트할 때 매우 유용했습니다. |
||
| const historyOfRace = runEntireRace(arrayOfCarNamesUserRequest, countOfLapUserRequest, randomNumberTape); | ||
| const namesOfWinner = determineWinnerOfRace(arrayOfCarNamesUserRequest, historyOfRace[historyOfRace.length -1]); | ||
| // 결과 출력(레이스 히스토리, 우승자) | ||
| this.printHistoryOfRace(historyOfRace, arrayOfCarNamesUserRequest); | ||
| this.printWinnerOfRace(namesOfWinner); | ||
| } | ||
|
|
||
| async readInputAsyncUsingWoowaMissionApi(questionStr) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 명칭을 구체화하는 과정에서 오히려 가독성이 떨어진다고 느껴집니다! |
||
| return await MissionUtils.Console.readLineAsync(questionStr); | ||
| } | ||
| makeRandomNumbersTape(cars, laps) { // 부수효과를 없애기 위해 필요한 만큼의 난수를 만들고 레이스 진행 | ||
| let numberTape = []; | ||
| const countOfNeededNumber = cars.length * laps; | ||
| for(let cnt = 0; cnt < countOfNeededNumber; cnt++) { | ||
| numberTape.push(this.pickRandomNumberInRangeUsingWoowaMissionApi(0, 9)) | ||
| } | ||
| return numberTape; | ||
| } | ||
|
Comment on lines
+28
to
+35
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이렇게 진행하신 이유가 궁금합니다!
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 레이스를 진행하는 부분을 순수함수로 구현하고자 함이었습니다. 그렇다 보니 입력, 랜덤과 같은 부분을 분리하려고 했습니다. 메모리에 대한 트레이드오프가 있지만 테스트할 때 매우 유용했습니다. |
||
| pickRandomNumberInRangeUsingWoowaMissionApi(min, max) { | ||
| return MissionUtils.Random.pickNumberInRange(min, max); | ||
| } | ||
| printOutputUsingWoowaMissionApi(output) { | ||
| MissionUtils.Console.print(output); | ||
| } | ||
| printHistoryOfRace(history, name) { | ||
| history.forEach(log => { | ||
| this.printOutputUsingWoowaMissionApi(parseToHistoryFormat(log, name)) | ||
| }) | ||
| } | ||
| printWinnerOfRace(winner) { | ||
| this.printOutputUsingWoowaMissionApi(parserToWinnerFormat(winner)); | ||
| } | ||
| }; | ||
| export default App; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| export const findMaxValue = (values) => Math.max(...values); // 명확성과 재사용성이 높아보여 분리 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 코멘트 남기려고 보니 주석을 남기셨네요 |
||
|
|
||
| export function determineWinnerOfRace(carNames, finalScores) { | ||
| const scoreOfWinner = findMaxValue(finalScores); | ||
| const winner = carNames.filter((name, carIdx) => finalScores[carIdx] === scoreOfWinner) | ||
| return winner; | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| export const meetMoveCondition = (number, threshold = 4) => number >= threshold; // 요구사항: 4이상이면 전진, 아니면 정지 | ||
|
|
||
| export function runEntireRace(arrOfCarNames, cntOfLaps, randomTape) { | ||
| let randomNumbers = randomTape.slice(); // 불변성 방지 복사 | ||
| let scoreAfterCurrentLap = Array(arrOfCarNames.length).fill(0); | ||
| const raceHistoryByLap = []; // 최종결과(마지막랩)로 쉽게 활용할 수 있도록 랩을 기준으로 데이터 저장 | ||
| for(let currentLap = 0 ; currentLap < cntOfLaps ; currentLap++) { | ||
| scoreAfterCurrentLap = scoreAfterCurrentLap.slice().map(prev => { | ||
| if(meetMoveCondition(randomNumbers.shift())) return prev + 1; | ||
| return prev; | ||
| }); | ||
| raceHistoryByLap.push(scoreAfterCurrentLap.slice()); | ||
| } | ||
| return raceHistoryByLap; | ||
| }; | ||
|
|
||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| export function parseByComma(input) { | ||
| return input.split(",").map(n => n.trim()); | ||
| } | ||
|
|
||
| export function parseToHistoryFormat(oneRoundHistory, name) { // [1,1,1] ["a","b","c"] | ||
| return oneRoundHistory | ||
| .map((result, idx) => `${name[idx]} : ${"-".repeat(result)}`) | ||
| .join("\n"); | ||
| } | ||
|
|
||
| export function parserToWinnerFormat(winner) { | ||
| return `최종 우승자 : ${winner.join(", ")}`; | ||
| } | ||
|
Comment on lines
+5
to
+13
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이 부분들을 파서로 빼신게 인상깊습니다. 어디까지가 레이스와 아웃풋의 책임인지 생각할 여지를 주는 것 같아요 |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| export function validateCarNameRule(names) { | ||
| if(new Set(names).size != names.length) throw new Error("[ERROR] : 중복된 이름을 사용할 수 없습니다.") | ||
| names.forEach(n => { | ||
| if(n.length > 5 || n.length === 0) throw new Error("[ERROR] : 자동차 명은 5자 이하여야 합니다."); | ||
| }); | ||
| } | ||
|
Comment on lines
+1
to
+6
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 자동차 이름이 없어도 "[ERROR] : 자동차 명은 5자 이하여야 합니다." 라는 에러가 뜹니다!
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 앗! 검증 부분을 더 꼼꼼히 체크해야겠습니다. 찾아주셔서 감사합니다! |
||
|
|
||
| export function validateLapNumberRule(lap) { | ||
| if(lap <= 0 || !Number.isInteger(lap)) | ||
| throw new Error("[ERROR] : 횟수는 양의 정수이어야 합니다.") | ||
| } | ||
|
Comment on lines
+8
to
+11
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이것도 음수인 경우와 소수인 경우를 분리 처리가 가능할 것 같아요
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 분리 처리 후 다른 에러 메세지를 준다면 사용자 입장에서 더 편할 것 같네요! 감사합니다~ |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
당연하다고 생각한 의존성까지 테스트 하신 부분이 인상깊습니다 :)