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
61 changes: 61 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,62 @@
# javascript-racingcar-precourse

# 🏎️ 자동차 경주

## 📋 구현 기능 목록

---

### 1️⃣ 입출력 기본 프롬프트

- [x] `Console.print("경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)")`
- [x] `Console.readLineAsync("")`로 자동차 이름 입력
- [x] `Console.print("시도할 횟수는 몇 회인가요?")`
- [x] `Console.readLineAsync("")`로 시도 횟수 입력

---

### 2️⃣ 입력 파싱 & 검증 — 자동차 이름

- [x] 쉼표 기준 분리, 각 이름 `trim()`
- [x] 빈 이름 금지, 전체 목록 빈 배열 금지
- [x] 각 이름 길이 1~5자 제한 (초과 시 에러)
- [x] 오류 시 `[ERROR]`로 시작하는 메시지와 함께 `Error` throw

---

### 3️⃣ 입력 파싱 & 검증 — 시도 횟수

- [x] 정수 문자열인지 확인
- [x] 양의 정수(>0)만 허용
- [x] 오류 시 `[ERROR]` 메시지와 함께 `Error` throw

---

### 4️⃣ 진행 규칙 구현

- [x] 라운드마다 각 자동차에 대해 `Random.pickNumberInRange(0, 9)`
- [x] 4 이상이면 전진, 미만이면 정지
- [x] 각 라운드 종료 후 `이름 : -...` 형식으로 상태 출력(진행도만큼 `-`)

---

### 5️⃣ 실행 결과 출력 형식

- [x] 입력 후 `\n실행 결과` 출력
- [x] 라운드 사이 빈 줄 출력(예시와 동일)
- [x] 모든 라운드 종료 후 우승자(복수 가능) 출력

---

### 6️⃣ 예외 처리와 종료 흐름

- [x] `run()`에서 모든 예외 `try/catch`
- [x] `[ERROR]`로 시작하는 메시지를 Console.print로 출력
- [x] rethrow하여 테스트의 `rejects.toThrow`를 만족
- [x] `process.exit()` 금지

### 7️⃣ 구조화/모듈화 & 스타일

- [x] I/O(`App.js`) ↔ 비즈니스 로직(`RacingGame.js`) 분리
- [x] 들여쓰기(depth) 2 이내, 3항 연산자 사용 금지
- [x] 함수는 한 가지 일만 하도록 분리
55 changes: 55 additions & 0 deletions __tests__/ApplicationTest.js

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

예외 케이스와 단일 라운드 시뮬레이션은 훌륭하게 커버되어있는 것 같아요!
그래도 제 생각이긴 하지만 attempts > 1일 때 누적 이동 결과를 검증하는 테스트가 추가되면 테스트케이스 완성도가 좀 더 올라가지 않을까 생각해봅니다...!

Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,59 @@ describe("자동차 경주", () => {
// then
await expect(app.run()).rejects.toThrow("[ERROR]");
});

// 파라미터화 테스트 추가
describe.each([
["", "[ERROR] 자동차 이름이 비어 있습니다."],
["pobi,javaji", "[ERROR] 자동차 이름은 5자 이하여야 합니다."],
["pobi, ", "[ERROR] 빈 이름은 허용되지 않습니다."],
[" ,woni", "[ERROR] 빈 이름은 허용되지 않습니다."],
])("이름 검증 - 입력: %s", (nameLine, errorMsg) => {
test("run()는 [ERROR]로 reject 되어야 한다", async () => {
mockQuestions([nameLine]);
const app = new App();
await expect(app.run()).rejects.toThrow(errorMsg);
});
});

describe.each([
["pobi,woni", "0", "[ERROR] 시도 횟수는 1 이상이어야 합니다."],
["pobi,woni", "-1", "[ERROR] 시도 횟수는 1 이상이어야 합니다."],
["pobi,woni", "abc", "[ERROR] 시도 횟수는 숫자여야 합니다."],
["pobi,woni", " ", "[ERROR] 시도 횟수는 숫자여야 합니다."],
])("시도 횟수 검증 - 입력: %s / %s", (nameLine, attemptLine, errorMsg) => {
test("run()는 [ERROR]로 reject 되어야 한다", async () => {
mockQuestions([nameLine, attemptLine]);
const app = new App();
await expect(app.run()).rejects.toThrow(errorMsg);
});
});

describe.each([
[
[4, 3],
["pobi : -", "woni : ", "최종 우승자 : pobi"],
],
[
[3, 4],
["pobi : ", "woni : -", "최종 우승자 : woni"],
],
[
[4, 4],
["pobi : -", "woni : -", "최종 우승자 : pobi, woni"],
],
])("한 라운드 시뮬레이션 - 랜덤: %j", (randoms, expectedLogs) => {
test("보드/우승자 출력이 형식에 맞아야 합니다.", async () => {
const logSpy = getLogSpy();
mockQuestions(["pobi,woni", "1"]);
mockRandoms(randoms);

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

expectedLogs.forEach((log) => {
expect(logSpy).toHaveBeenCalledWith(expect.stringContaining(log));
});
});
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

describe.each를 통해서 한번에 테스트를 묶으니까 코드가 더 간결하네요
좋은 코드 배웠습니다 !

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

반복되는 케이스가 많아서 describe.each로 묶으면 가독성이 좋아질 것 같다고 생각했어요!
테스트 의도를 더 명확하게 표현할 수 있어 자주 활용하게 될 것 같습니다 👩🏻‍💻

});
43 changes: 42 additions & 1 deletion src/App.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,46 @@
import { Console } from "@woowacourse/mission-utils";
import { RacingGame } from "./RacingGame.js";
import { ERROR, PROMPT } from "./constants/messages.js";

class App {
async run() {}
async run() {
Console.print(PROMPT.ASK_NAMES);
try {
const nameLine = await Console.readLineAsync("");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

readLienAsync에 PROMPT.ASK_NAMES을 작성하면 콘솔에 출력되고 입력을 받을 수 있습니다!!

따로 Print 안해도 될것 같아서 좋을것 같네요!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

출력 형식을 명시적으로 구분하고 싶어서 Console.print로 분리했는데,
readLineAsync 로 넘겨서 바로 출력하는 방식이 더 깔끔하겠네요. 감사합니다! 🙇🏻‍♀️

Console.print(PROMPT.ASK_ATTEMPTS);
const attemptLine = await Console.readLineAsync("");

const game = new RacingGame(nameLine, attemptLine);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

입력된 값을 RacingGame객체를 생성할때 검증합니다.
이와 같은 구조에서는 첫 번째 입력이 유효하지 않더라도 두 번째 입력을 시도하게 됩니다.
Screenshot 2025-10-31 at 5 05 27 PM
검증로직을 따로 분리하여 각 입력 이후에 바로 수행하면 해결할 수 있습니다.

const result = game.playAllRounds();

this.#printExecutionHeader();
this.#printBoards(result.boards);
this.#printWinners(result.winners);
} catch (error) {
const message = error?.message ?? ERROR.UNKNOWN;
Console.print(message);
throw error;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

위에서 error message 처리해서 throw error는 삭제해도 될 것 같습니다

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

error를 발생시키고 종료해야해서 throw error 가 필요하지 않나요 ?
제가 잘못알고 있는건지 궁금해서 답글 달았습니다

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

위에서 error message 처리해서 throw error는 삭제해도 될 것 같습니다

테스크 환경에서 await expect(app.run()).rejects.toThrow("[ERROR]"); 이 코드가 동작하려면 예외를 다시 던져야하지 않나요? 🤔

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

error를 발생시키고 종료해야해서 throw error 가 필요하지 않나요 ? 제가 잘못알고 있는건지 궁금해서 답글 달았습니다

테스트 코드에서 rejects.toThrow()로 검증을 하기 위해 throw error 를 사용했는데, 실제 실행만 고려하면 생략해도 괜찮은지 궁금하네요!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

앗 제가 생각이 여기서 꼬였었나봐요... 왜 메세지를 단순히 error.message로 안하셨을까? 하고 뜯어보다가 message 출력하고 다시 throw?하면서 삭제해도 될 것 같다고 생각했어요.... throw하는게 맞습니다ㅠㅠ
근데 일단 실제 실행만 고려하면 throw error가 없어도 catch문 실행으로 실행 종료되긴 해서 실행에는 문제가 없다고 생각합니다. 다만 test에서는 문제가 생겨요...
throw error가 맞는 방식이예요 제가 생각이 꼬였었나봐요ㅠㅠ 죄송합니다

}
}

#printExecutionHeader() {
Console.print("");
Console.print(PROMPT.EXEC_HEADER);
}

#printBoards(boards) {
for (const board of boards) {
for (const line of board) {
Console.print(line);
}
Console.print("");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Console.print("")가 반복되는데
방법이 없을까요 ...?

음.. print함수를 따로 만들어서 증복을 줄이는 방법은 어떨까요 ?

그러면 출력 요소가 전부 분리되어있는데 이또한 줄어들 수 있을것 같네요!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

print 함수를 헬퍼로 추출해서 중복을 줄이는 방향이 깔끔하겠네요!
많이 배우고 있습니다. 좋은 피드백 감사합니다 👍🏻

}
}

#printWinners(winners) {
const line = `최종 우승자 : ${winners.join(", ")}`;
Console.print(line);
}
}

export default App;
95 changes: 95 additions & 0 deletions src/RacingGame.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { Random } from "@woowacourse/mission-utils";
import { ERROR } from "./constants/messages";

const MOVE_THRESHOLD = 4;
const RANDOM_MIN = 0;
const RANDOM_MAX = 9;
Comment on lines +4 to +6

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이미 충분히 좋지만 MOVE_THRESHOLD = 4 상수의 의미가 ‘전진 조건 최소값’ 임을 이름으로 드러내면 좀 더 확실하게 알 수 있을 것 같아요! FORWARD_THRESHOLD 또는 MOVE_CONDITION_MIN_VALUE 이런식으로요!


export class RacingGame {
#cars;
#attempts;

constructor(nameLine, attemptLine) {
const names = this.#parseNames(nameLine);
const attempts = this.#parseAttempts(attemptLine);
this.#cars = names.map((name) => ({ name, position: 0 }));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재 cars의 각 car는 { name, position }의 형태로 관리되고 있는 것 같아요. 저는 자동차 하나인 Car와 자동차 경주인 Race를 나눠 각각 model로 만들었고, 전진에 관련된 내용은 Car가 담당하도록 했더니 객체 간 역할이 명확해진다는 느낌을 받았습니다.
그래서 혹시 Car 객체를 고려하셨었는지, 고려하셨었다면 현재 방식을 택하신 이유가 있으신지 궁금합니다!

this.#attempts = attempts;
}

playAllRounds() {
const boards = [];
for (let round = 0; round < this.#attempts; round += 1) {
this.#playOneRound();
boards.push(this.#formatBoard());
}
const winners = this.#determineWinners();
return { boards, winners };
}
Comment on lines +19 to +27

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재 매 라운드 자동차가 이동한 거리를 배열에 저장하고 모든 라운드가 다 끝났을때 한 번에 출력하는 방식입니다.
굳이 저장할 필요없이 매 라운드마다 이동 거리를 출력하면 필요없는 메서드를 많이 줄일 수 있습니다.

index 9c34162..2921c0b 100644
--- a/src/App.js
+++ b/src/App.js
@@ -11,11 +11,10 @@ class App {
       const attemptLine = await Console.readLineAsync("");
 
       const game = new RacingGame(nameLine, attemptLine);
-      const result = game.playAllRounds();
+      const winners = game.playAllRounds();
 
       this.#printExecutionHeader();
-      this.#printBoards(result.boards);
-      this.#printWinners(result.winners);
+      this.#printWinners(winners);
     } catch (error) {
       const message = error?.message ?? ERROR.UNKNOWN;
       Console.print(message);
@@ -28,15 +27,6 @@ class App {
     Console.print(PROMPT.EXEC_HEADER);
   }
 
-  #printBoards(boards) {
-    for (const board of boards) {
-      for (const line of board) {
-        Console.print(line);
-      }
-      Console.print("");
-    }
-  }
-
   #printWinners(winners) {
     const line = `최종 우승자 : ${winners.join(", ")}`;
     Console.print(line);
diff --git a/src/RacingGame.js b/src/RacingGame.js
index d6ccf70..5c32b74 100644
--- a/src/RacingGame.js
+++ b/src/RacingGame.js
@@ -1,5 +1,5 @@
-import { Random } from "@woowacourse/mission-utils";
-import { ERROR } from "./constants/messages";
+import { Random, Console } from "@woowacourse/mission-utils";
+import { ERROR } from "./constants/messages.js";
 
 const MOVE_THRESHOLD = 4;
 const RANDOM_MIN = 0;
@@ -17,13 +17,11 @@ export class RacingGame {
   }
 
   playAllRounds() {
-    const boards = [];
     for (let round = 0; round < this.#attempts; round += 1) {
       this.#playOneRound();
-      boards.push(this.#formatBoard());
     }
     const winners = this.#determineWinners();
-    return { boards, winners };
+    return winners
   }
 
   #playOneRound() {
@@ -32,16 +30,9 @@ export class RacingGame {
       if (randomValue >= MOVE_THRESHOLD) {
         car.position += 1;
       }
+      Console.print(`${car.name} : ${'-'.repeat(car.position)}`);
     }
-  }
-
-  #formatBoard() {
-    const lines = [];
-    for (const car of this.#cars) {
-      const hyphens = "-".repeat(car.position);
-      lines.push(`${car.name} : ${hyphens}`);
-    }
-    return lines;
+    Console.print('');
   }
 
   #determineWinners() {


#playOneRound() {
for (const car of this.#cars) {
const randomValue = Random.pickNumberInRange(RANDOM_MIN, RANDOM_MAX);
if (randomValue >= MOVE_THRESHOLD) {
car.position += 1;
}
}
}

#formatBoard() {
const lines = [];
for (const car of this.#cars) {
const hyphens = "-".repeat(car.position);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재 변수명이 직관적이고 명확하다는 장점이 있습니다만, 변수명이 hyphens는 살짝 specific 하지않나? 라고 생각했습니다. positionIndicator같은 변수 명이면 위치를 나타내는 표시 라는 목적이 있어서, 나중에 출력 형식이 *나 >로 바뀌어도 변수명을 바꾸지 않아도 되는 장점이 있기에 의견 남깁니다!

lines.push(`${car.name} : ${hyphens}`);
}
return lines;
}

#determineWinners() {
const max = this.#cars.reduce((maxDistance, car) => {
if (car.position > maxDistance) return car.position;
return maxDistance;
}, 0);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분은 간단하게 const max = Math.max(...this.cars.map(car=>car.position)으로 할 수 있을 것 같습니다

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

우와 Math.max 방식으로 표현할 생각은 못했는데 훨씬 간결하네요 😮
좋은 코드 배웠습니다. 감사합니다!

const names = [];
for (const car of this.#cars) {
if (car.position === max) {
names.push(car.name);
}
}
return names;
Comment on lines +52 to +58

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재 코드도 동작에는 문제가 없지만 filter, map 을 사용하면 코드가 더 깔끔할거 같습니다.

  #determineWinners() {
    const max = this.#cars.reduce((maxDistance, car) => {
      if (car.position > maxDistance) return car.position;
      return maxDistance;
    }, 0);
    const winners = this.#cars.filter((car) => car.position === max);
    const winnerNames = winners.map((car) => car.name);
    return winnerNames;
  }

}

#parseNames(nameLine) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재 parseNames는 입력값을 검증, 파싱 두가지를 수행합니다.
이 두 가지를 분리해서 진행하면 더 좋을거 같습니다.

index d6ccf70..3c7f32b 100644
--- a/src/RacingGame.js
+++ b/src/RacingGame.js
@@ -1,5 +1,5 @@
 import { Random } from "@woowacourse/mission-utils";
-import { ERROR } from "./constants/messages";
+import { ERROR } from "./constants/messages.js";
 
 const MOVE_THRESHOLD = 4;
 const RANDOM_MIN = 0;
@@ -10,7 +10,8 @@ export class RacingGame {
   #attempts;
 
   constructor(nameLine, attemptLine) {
-    const names = this.#parseNames(nameLine);
+    const carNames = this.#parseCarNames(nameLine);
+    this.#validateCarNames(carNames);
     const attempts = this.#parseAttempts(attemptLine);
     this.#cars = names.map((name) => ({ name, position: 0 }));
     this.#attempts = attempts;
@@ -58,24 +59,20 @@ export class RacingGame {
     return names;
   }
 
-  #parseNames(nameLine) {
-    const raw = (nameLine ?? "").trim();
-    if (raw === "") {
-      throw new Error(ERROR.EMPTY_NAMES);
-    }
-
-    const nameTokens = raw.split(",").map((s) => s.trim());
-
-    for (const nameToken of nameTokens) {
-      if (nameToken === "") {
+  #validateCarNames(carNames) {
+    for (const name of carNames) {
+      if (name === '') {
         throw new Error(ERROR.EMPTY_NAME_TOKEN);
       }
       if (nameToken.length > 5) {
         throw new Error(ERROR.NAME_TOO_LONG);
       }
     }
+  }
 
-    return nameTokens;
+  #parseCarNames(nameLine) {
+    const carNames = nameLine.split(",").map((el) => el.trim());
+    return carNames;
   }
 
   #parseAttempts(attemptLine) {

최종적으로는 이 부분이 RacingGame 메서드가 아닌 다른 방향으로 제공되면 더 좋을거 같습니다.
Car 클래스 객체를 생성할때 이름속성으로 검증을 수행해도 괜찮고 따로 static한 형태로 제공되어도 좋을거 같습니다.

const raw = (nameLine ?? "").trim();
if (raw === "") {
throw new Error(ERROR.EMPTY_NAMES);
}

const nameTokens = raw.split(",").map((s) => s.trim());

for (const nameToken of nameTokens) {
if (nameToken === "") {
throw new Error(ERROR.EMPTY_NAME_TOKEN);
}
if (nameToken.length > 5) {
throw new Error(ERROR.NAME_TOO_LONG);
}
}
Comment on lines +69 to +76

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

반복문에서 여러 조건을 한 번에 검증하고 있는데 js 내장 매서드를 사용해서 한 번에 한 조건으로 검증하면 들여쓰기를 줄일 수 있을 것 같아요.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

제안해주신 방향으로 리팩토링하면 훨씬 깔끔한 코드가 될 것 같아요! 감사합니다 👍🏻


return nameTokens;
}

#parseAttempts(attemptLine) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

함수명이 전반적으로 일관적이어서 이해가 좋습니다!
허나 이함수는 다소 parse와 매치가 안되는 느낌이 있긴합니다..
parsing보다는 검증에 가까운것 같아서 validate 함수로 함수명을 바꾸는건 어떨까요 ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const randomValue = Number(raw); 이 코드를 생각하고 그렇게 지었는데,
검증 로직의 비중이 크기 떄문에 말씀주신 부분에 대해서 고려해보겠습니다!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저도 비슷한 느낌을 받았는데, 지금은 파싱 로직과 유효성 검사 로직이 함께 포함되어 있다보니 두 관심사를 나눠서 각각 메서드로 사용하는 것도 또 다른 방법이 될 것 같습니다!

const raw = (attemptLine ?? "").trim();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?? "" 사용도 좋네요 !
저는 대부분 default 파라미터를 사용했는데 이것도 한번 사용해보세요!!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

감사합니다! 😊
하나 궁금한게 default parameter를 사용하실 때,
null이나 빈 문자열을 어떻게 처리하시나요?


if (!/^-?\d+$/.test(raw)) {
throw new Error(ERROR.ATTEMPTS_NAN);
}

const randomValue = Number(raw);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

어차피 Number 처리를 했다면 위에서 정규표현식을 굳이 사용하지 않고 isNaN(randomValue)를 해서 확인해도 될 것 같습니다. 정규표현식으로 확인하는데에도 한계가 있을 것 같아요! (예를 들면 -0은 통과)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이번에는 음수 입력 케이스 잡아내고 싶어서 정규식으로 확인했습니다!
말씀주신 방법도 참고해서 케이스별로 간결한 로직을 고민해봐야겠습니다


if (!Number.isSafeInteger(randomValue) || randomValue <= 0) {
throw new Error(ERROR.ATTEMPTS_POSITIVE);
}
return randomValue;
}
}
13 changes: 13 additions & 0 deletions src/constants/messages.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export const PROMPT = {
ASK_NAMES: "경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)",
ASK_ATTEMPTS: "시도할 횟수는 몇 회인가요?",
EXEC_HEADER: "실행 결과",
};
export const ERROR = {
EMPTY_NAMES: "[ERROR] 자동차 이름이 비어 있습니다.",
EMPTY_NAME_TOKEN: "[ERROR] 빈 이름은 허용되지 않습니다.",
NAME_TOO_LONG: "[ERROR] 자동차 이름은 5자 이하여야 합니다.",
ATTEMPTS_NAN: "[ERROR] 시도 횟수는 숫자여야 합니다.",
ATTEMPTS_POSITIVE: "[ERROR] 시도 횟수는 1 이상이어야 합니다.",
UNKNOWN: "[ERROR] 알 수 없는 오류가 발생했습니다.",
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

상수 분리 너무 깔끔하네요 최고!!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

상수 분리에 대한 리뷰가 있었어서 적용해봤습니다 😁

Comment on lines +6 to +13

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저도 에러 메시지를 상수로 분리해 사용하는게 좋은 것 같아요! ㅎㅎ
다만 [ERROR]가 반복되어서, PREFIX와 같은 이름으로 분리해 재사용하는건 어떨까요?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

맞습니다 윗분님들 말씀대로 상수 분리 깔끔하게 잘 하셔서 인상적입니다! 추가로Object.freeze()를 적용하면 불변성을 보장할 수 있어 좋은 방법이 있어 남기고 갑니다 ㅎㅎ

6 changes: 6 additions & 0 deletions types/woowacourse__mission-utils.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
declare module "@woowacourse/mission-utils" {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ts 파일이 있는데 생성한 이유가 있을까요?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@woowacourse/mission-utils 모듈 타입이 인식되지 않아서 경고가 발생했었습니다.
declare module을 통해 타입을 직접 정의하여 해결했고,
.d.ts 로 선언하여 IDE 자동완성과 타입 추론이 가능하도록 하였습니다!

export const Console: {
readLineAsync(prompt?: string): Promise<string>;
print(message: string): void;
};
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

타입 선언 너무 좋습니다!!
모듈 활용 최강이죠

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이번 미션에서 계속 활용할 것 같습니다! 감사합니다 😊