Skip to content

Conversation

@YounaJ00
Copy link

@YounaJ00 YounaJ00 commented Oct 27, 2025

🚗 구현 내용

1. 입력 및 검증

  • Console.readLineAsync()로 사용자 입력 수신
  • 자동차 이름 검증
    • 빈 문자열 또는 공백 이름 입력 시 예외 발생
    • 이름은 5자 이하만 허용
  • 시도 횟수 검증
    • 숫자 이외의 입력, 0 이하 입력 시 [ERROR] 예외 발생
    • (input ?? "").trim()으로 null, undefined 입력도 안전하게 처리

2. 게임 로직

  • RacingGame 클래스
    • 자동차 리스트 초기화
    • 라운드 수만큼 반복하여 이동 시뮬레이션
    • 무작위 값(0~9) 중 4 이상일 때 전진 처리
  • 라운드별 위치 기록(board) 관리
  • 최대 이동 거리 비교로 우승자 계산

3. 출력

  • 실행 결과 헤더("실행 결과") 출력
  • 각 라운드별 자동차 위치 출력 (이름 : - 형식)
  • 최종 우승자 출력

4. 아키텍처

  • 입출력 로직(App) 과 비즈니스 로직(RacingGame) 완전 분리
  • private 메서드로 세부 로직 캡슐화
  • 예외 메시지 상수화 (ERROR = { ... }) 적용
  • Magic Number 상수화

5. 테스트

  • 정상 동작 테스트
  • 예외 테스트
  • 파라미터화 테스트(describe.each) 로 다양한 입력 자동 검증

6. 프로그래밍 요구사항

  • indent depth 2 이하
  • 3항 연산자 및 else 최소화
  • 함수 단일 책임 원칙 준수

💡 개선 및 피드백 반영

1차 미션에서 받은 피드백(함수 이름과 역할 명확화, 에러 메시지 상수화)을 이번 미션에 적극 반영했습니다.
이번 구현에서는 null/undefined 입력 처리, [ERROR] 메시지 일관성 유지, 상수 추출 등을 통해 가독성과 안정성을 개선했습니다.
또한 1주차 공통 피드백을 최대한 반영하여 구현하려고 노력했습니다.

특히 아래의 핵심 개선 포인트를 중점적으로 적용했습니다. ✨

  • 함수명과 역할의 일관성 강화
  • 테스트 구조 개선 (describe.each 활용으로 케이스 가독성 향상)
  • 예외 메시지 상수화로 유지보수성 및 통일성 확보

Copy link

@lucykim05 lucykim05 left a comment

Choose a reason for hiding this comment

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

private 필드를 굉장히 잘 사용하신 것 같아요! 다만 한 파일에 여러 책임이 있어서 단일 책임을 위해서 파일을 분리해보면 어떨까하는 생각이 듭니다! 2주차 너무 고생 많으셨습니다!!

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 방식으로 표현할 생각은 못했는데 훨씬 간결하네요 😮
좋은 코드 배웠습니다. 감사합니다!

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.

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

} 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가 맞는 방식이예요 제가 생각이 꼬였었나봐요ㅠㅠ 죄송합니다

Copy link

@manNomi manNomi left a comment

Choose a reason for hiding this comment

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

코드의 로직이 깔끔해서 보기 좋았습니다
적은 코드와 응집을 통해서 코드가 빠르게 읽힐 수 있어서 너무 좋았고요!!
좋은코드 많이배웠습니다 !

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로 묶으면 가독성이 좋아질 것 같다고 생각했어요!
테스트 의도를 더 명확하게 표현할 수 있어 자주 활용하게 될 것 같습니다 👩🏻‍💻

} catch (error) {
const message = error?.message ?? ERROR.UNKNOWN;
Console.print(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 가 필요하지 않나요 ?
제가 잘못알고 있는건지 궁금해서 답글 달았습니다

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 로 넘겨서 바로 출력하는 방식이 더 깔끔하겠네요. 감사합니다! 🙇🏻‍♀️

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

}

#parseAttempts(attemptLine) {
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이나 빈 문자열을 어떻게 처리하시나요?

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.

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

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.

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

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.

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

Copy link

@inseong01 inseong01 left a comment

Choose a reason for hiding this comment

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

코드가 깊지 않아서 읽기 수월했습니다~

지금 몇몇 커밋 메시지가 본문으로 넘어가는데 커밋 헤더, 본문으로 나눠서 작성하면 어떤 작업을 한 커밋인지 잘 보일 것 같아요.

2주 차 미션하시느라 수고하셨습니다.

Comment on lines +69 to +76
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);
}
}

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.

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

@@ -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 자동완성과 타입 추론이 가능하도록 하였습니다!

Copy link

@Antoliny0919 Antoliny0919 left a comment

Choose a reason for hiding this comment

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

2주차 고생하셨습니다 ⭐

몇 가지 코멘트를 남겼습니다 :)

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
검증로직을 따로 분리하여 각 입력 이후에 바로 수행하면 해결할 수 있습니다.

return names;
}

#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한 형태로 제공되어도 좋을거 같습니다.

Comment on lines +19 to +27
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 };
}

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() {

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

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;
  }

Copy link

@MyungJiwoo MyungJiwoo left a comment

Choose a reason for hiding this comment

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

이미 좋은 리뷰들이 많이 남겨져 있어서 최대한 겹치지 않는 부분에서 리뷰 남겨보았습니다!
코드 구조나 가독성을 위해 많이 고민하신게 느껴졌습니다.
미션 수행하시느라 고생 많으셨습니다! 다음 미션도 화이팅입니다! 🍀

(시간 괜찮으실 때 제 코드 리뷰도 한 번 해주시면 감사하겠습니다 🙇🏻‍♀️ => PR)

Comment on lines +6 to +13
export const ERROR = {
EMPTY_NAMES: "[ERROR] 자동차 이름이 비어 있습니다.",
EMPTY_NAME_TOKEN: "[ERROR] 빈 이름은 허용되지 않습니다.",
NAME_TOO_LONG: "[ERROR] 자동차 이름은 5자 이하여야 합니다.",
ATTEMPTS_NAN: "[ERROR] 시도 횟수는 숫자여야 합니다.",
ATTEMPTS_POSITIVE: "[ERROR] 시도 횟수는 1 이상이어야 합니다.",
UNKNOWN: "[ERROR] 알 수 없는 오류가 발생했습니다.",
};

Choose a reason for hiding this comment

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

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

return nameTokens;
}

#parseAttempts(attemptLine) {

Choose a reason for hiding this comment

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

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

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 객체를 고려하셨었는지, 고려하셨었다면 현재 방식을 택하신 이유가 있으신지 궁금합니다!

Copy link

@KyeongJooni KyeongJooni left a comment

Choose a reason for hiding this comment

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

다른분들이 워낙 잘 달아주셔서 달게 없지만 부족한 실력으로 조금 달아봤습니다! 많이 배울 수 있는 코드인 것 같아요! 👍

Choose a reason for hiding this comment

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

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

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

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 이런식으로요!

Copy link

@lee-eojin lee-eojin left a comment

Choose a reason for hiding this comment

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

리뷰가 너무 상세히 해주신 분이 많아서 오히려 제가 배우고 갔습니다!! ㅎㅎㅎ
개인적으로 몇몇 코드가 인베디드 스타일(?)이 보였던거같아서 인상깊었습니다.
잘 배웠습니다! 고생하셨습니다!

#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같은 변수 명이면 위치를 나타내는 표시 라는 목적이 있어서, 나중에 출력 형식이 *나 >로 바뀌어도 변수명을 바꾸지 않아도 되는 장점이 있기에 의견 남깁니다!

ATTEMPTS_NAN: "[ERROR] 시도 횟수는 숫자여야 합니다.",
ATTEMPTS_POSITIVE: "[ERROR] 시도 횟수는 1 이상이어야 합니다.",
UNKNOWN: "[ERROR] 알 수 없는 오류가 발생했습니다.",
};

Choose a reason for hiding this comment

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

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants