Skip to content

Conversation

@chasj0326
Copy link

@chasj0326 chasj0326 commented Jul 5, 2024

기본 추가 요구사항

  • 사용자는 몇 번의 이동을 할 것인지를 입력할 수 있어야 한다.
  • 사용자가 잘못된 입력 값을 작성한 경우 에러 메시지를 보여주고, 다시 입력할 수 있게 한다.

추가한 요구사항

  • 경주 규칙 추가 (미니게임)
  • 사용자는 미니게임에 직접 참여할 수 있다.
  • 함께 플레이할 봇의 이름들과, 참여할 본인의 이름들을 입력할 수 있다.
  • 한번에 갈 수 있는 거리가 1보다 클 수 있다.

RacingGame 클래스에서 MiniGame 들을 받아서, 랜덤으로 미니게임을 진행하면서 전진하는 형식입니다 !


🚕 레이싱 게임을 시작합니다 🚗
각 라운드마다 랜덤 미니 게임을 진행하여 이동할 수 있습니다.

직접 레이싱에 참여할 자동차들의 이름을 입력해주세요. (쉼표로 구분)
chasj,hello
봇으로 참여할 자동차들의 이름을 입력해주세요. (쉼표로 구분)
bot1, bot2
몇 라운드 플레이할 지 알려주세요.
4
--------------------------⭐ Round1⭐️️--------------------------

>> player chasj Turn!
가위바위보 대결 : 1.바위 2.가위 3.보
2

>> player hello Turn!
가위바위보 대결 : 1.바위 2.가위 3.보
1

chasj : ✌️  VS computer : ✋   ➡➡ win
hello : 👊  VS computer : 👊  ➡➡ draw
bot1  : ✌️  VS computer : ✌️  ➡➡ draw
bot2  : ✌️  VS computer : ✌️  ➡➡ draw

chasj : __⛳️ (+1 ➡ 1)
hello : ⛳️__ (+0 ➡ 0)
bot1  : ⛳️__ (+0 ➡ 0)
bot2  : ⛳️__ (+0 ➡ 0)

--------------------------⭐ Round2⭐️️--------------------------

>> player chasj Turn!
가위바위보 대결 : 1.바위 2.가위 3.보
1

>> player hello Turn!
가위바위보 대결 : 1.바위 2.가위 3.보
3

chasj : 👊  VS computer : ✋   ➡➡ lose
hello : ✋   VS computer : ✌️  ➡➡ lose
bot1  : 👊  VS computer : 👊  ➡➡ draw
bot2  : ✌️  VS computer : ✋   ➡➡ win

chasj : ⛳️__ (-1 ➡ 0)
hello : ⛳️__ (+0 ➡ 0)
bot1  : ⛳️__ (+0 ➡ 0)
bot2  : __⛳️ (+1 ➡ 1)

--------------------------⭐ Round3⭐️️--------------------------

>> player chasj Turn!
타이머 맞추기: 9 뒤에 Enter 누르기

>> player hello Turn!
타이머 맞추기: 5 뒤에 Enter 누르기

chasj : 7.94 VS computer : 9.00 ➡➡ lose
hello : 4.66 VS computer : 5.00 ➡➡ win
bot1  : 4.45 VS computer : 5.00 ➡➡ lose
bot2  : 4.96 VS computer : 5.00 ➡➡ win

chasj : ⛳️____ (+0 ➡ 0)
hello : __⛳️__ (+1 ➡ 1)
bot1  : ⛳️____ (+0 ➡ 0)
bot2  : ____⛳️ (+1 ➡ 2)

--------------------------⭐ Round4⭐️️--------------------------

>> player chasj Turn!
숫자 맞추기 대결 : 1 부터 10 까지의 숫자 중 하나를 입력
2

>> player hello Turn!
숫자 맞추기 대결 : 1 부터 10 까지의 숫자 중 하나를 입력
6

chasj : 2 VS computer : 1 ➡➡ lose
hello : 6 VS computer : 8 ➡➡ lose
bot1  : 7 VS computer : 4 ➡➡ lose
bot2  : 2 VS computer : 10 ➡➡ lose

chasj : ⛳️____ (+0 ➡ 0)
hello : __⛳️__ (+0 ➡ 1)
bot1  : ⛳️____ (+0 ➡ 0)
bot2  : ____⛳️ (+0 ➡ 2)

최종 우승자는 👑 bot2 입니다. 축하합니다!



미션을 수행하며 어려웠던 점

1. 게임 중간에 출력하기

  • 다른 분들의 코드를 보고 뷰, 모델을 나누는 방식이 이 미션에 적합하다는 생각이 들었어요. 따라서 모델에서 반환된 게임 결과를 뷰가 받아서 출력하는 구조를 생각했었습니다 !

  • 하지만 .. 제가 미니게임으로 일을 너무 크게 벌여놔서 중간 출력이 없으면 이상한 상황이 되었습니다 ! (가위바위보를 했는데 상대방이 뭘 냈는지 알 수가 없는..)

  • 따라서 EventEmitter 객체를 데려와서, 모델에서 이벤트를 던지면 컨트롤러에서 받아서 뷰에게 출력 명령을 내리는 구조로 변경했습니다

2. 출력 내용 관리하기

  • 출력하는 부분이 이제 뷰에 다 몰려있긴 하지만, console.log 안에 문자열이 산재되어 있으면 수정하기 어렵다는 생각이 들었어요.

  • 상수로 관리할 수도 있지만, 다른 방법이 없을까.. 에 대해 고민을 했습니다.

  • 따라서 printWithTemplate 이라는 메소드를 만들어서, Printer 객체를 생성할 때 템플릿 객체를 넣어줄 수 있도록 했어요 ! (맞는 방법인지는 모르겠습니다... ! 도전정신? 으로 해본 내용입니다)

  • 예를 들면 아래와 같습니다.

printer.templates = { error: 'this is error! %[1]: %[2]' }
printer.printWithTemplate('error', [429, 'too many request'])

// 출력 : this is error! 429 : too many request

3. validation & test

  • 사실 앞전 구현 내용(미니게임, 구조변경, 출력..) 에 시간을 너어어어무 뺏겨서 이 부분을 꼼꼼히 마무리하지 못했습니다 ㅠㅠ

  • 객체에 검사 메소드인 check 와 에러때 출력할 errorMessage 를 작성하고, 이걸로 유효성 검사를 실행시켜주는 validator 를 반환하는 createValidator 를 구현했습니다. (사실 시간이 없어서... 객체만 작성하면 에러처리 함수를 일일히 안써도 되도록 하기 위함이었어요)

// 미니게임에 대한 검사 실행하기

this.validate = createValidator(racingValidations);
this.validate(this.miniGames, ['miniGameInterface', 'miniGameSize']);
  • 테스트 코드는 어렵네요 ㅠ 저도 모르게 무의식적으로 함수가 돌아가는 순서?시나리오?를 그대로 테스트 코드로 옮기고 있습니다 ! 그렇다 보니 mock 같은 친구들을 많이 사용하는데.. 이게 좋다고 생각하지 않습니다 ..ㅠㅠ 간단하게 작성할 수 있는 팁이 있다면.. 알려주세요

리뷰 받고 싶은 점 & 궁금한 점

  • 이벤트를 활용하는 방식이 뷰와 로직을 분리한다는 관점에서 유의미한 방법인지 궁금합니다 !

  • 이 외에도, 위에 작성한 부분들에 대해서 자유롭게 의견 주시면 감사하겠습니다 !

  • 게임... 시간되면 다들 해보세요 .. ! 나름 재미있어요 🥹


내가 생각하는 클린코드의 원칙 한 가지

이 코드의 사용자를 고려했는가? 라고 생각합니다 ! 여기서 코드의 사용자란, 작성한 코드를 보고 수정하는 개발자입니다
결국 개발할 때의 여러가지 비용들을 줄여야 하기 때문에,
사용자에게 쉬운 서비스를 만드는 것 처럼 코드 작성도 개발자가 쉽게 사용할 수 있는 방향으로 해야한다고 생각합니다
구체적으로는, 코드를 수정할 때 스크롤 + 클릭 횟수 + 생각하는 비용을 적게 만들어야 된다 생각해요 !

@chasj0326 chasj0326 self-assigned this Jul 5, 2024
@chasj0326 chasj0326 added the team1 label Jul 6, 2024
Copy link

@suyeon1218 suyeon1218 left a comment

Choose a reason for hiding this comment

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

안녕하세요 세진님...!! 코드 잘 봤습니다...! 세진님께서 구현에 힘을 주셨다는 얘기를 듣고 저도 코드 자체에 에러가 있는지 보다는 세진님이 어떻게 구현했는지를 파악하려고 노력하면서 우와 우와< 같은 리뷰 위주로 남기게 된 것 같아요... ㅋㅋㅋ

노션 클로닝을 할 때 막히는 부분이 있으면 세진님 코드를 참고하며 놀랐던 부분이 이만저만이 아니었는데 이번 미션도 역시 세진님의 문제해결능력이 돋보이는 코드였습니다...!! 세진님의 코드를 보면 더 시야가 넓어지는 기분이라 꼭 참고하려고 하고 저도 더 열심히 하게 되는 것 같아요. 흑흑... 제 코드 리뷰가 뭔가 인사이트를 얻으실 수 있는 리뷰라기 보단 세진님의 코드를 보며 감탄 & 궁금한 점만 적은 코드 같아 부끄럽네요 ㅠ_ㅠ 아무튼 이번주 미션도 수고 많으셨습니다...!!

DiceDiffGame,
},
}),
viewer: new RacingGameViewer(),

Choose a reason for hiding this comment

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

저는 main 을 컨트롤러의 역할로 분리했는데 세진님 처럼 Controller 를 따로 만들고, 컨트롤러에 View 를 전달해주는 방식이 가독성 측면이나 재활용 측면에서 좋은 것 같아요! 고민을 많이 하신 흔적이 보여서 세진님의 코드를 보는 게 매번 기대가 됩니다 ㅎㅎ

Comment on lines +19 to +21
get results() {
return this.#results;
}

Choose a reason for hiding this comment

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

results 가 배열이라서 복사등을 사용하지 않으면 밖에서 데이터를 조작할 수 있지 않을까요?!

Comment on lines +23 to +25
get miniGames() {
return this.#miniGames;
}

Choose a reason for hiding this comment

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

마찬가지로 객체 형태이기 때문에 밖에서 조작할 수 있을 것 같아요!!

Comment on lines +7 to +10
this.name = name.replaceAll(' ', '');

this.validate()
this.#position = 0
this.validate();
this.#position = 0;

Choose a reason for hiding this comment

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

name private 속성이 아니니까 밖에서 접근하고 변경할 수 있을 것 같아요...! 그럼 이 경우 name 의 유효성 검사는 어디서 진행해줄 수 있나요?

Comment on lines +20 to +21
name => positions[name] === this.maxPosition,
);

Choose a reason for hiding this comment

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

maxPosition 정보가 필요해서 이걸 프로퍼티로 저장해서 관리할지 고민하다가 그냥 저는 하나의 함수안에 합쳐버렸는데 분리해놓으니 훨씬 더 읽기 편해지네요...! 인스턴스로 관리하는 변수만 get 프로퍼티를 사용할 수 있다고 무의식적으로 생각했는데 세진님 덕에 고정관념을 깨고 갑니다 😀

Comment on lines +30 to +31
this.cars = [];
this.players = [];

Choose a reason for hiding this comment

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

this.carsthis.players 를 여기서 처음으로 초기화시켜주는군요...! 사용되는 프로퍼티를 위에 미리 선언해 놓지 않은 이유가 있으실까요?

this.players.push(newCar.name);
}
});
this.validate(this.cars, ['leastCarCount', 'uniqueCarName']);

Choose a reason for hiding this comment

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

여기서 자동차의 이름에 대한 유효성 검사가 진행되기 때문에 Car 클래스에선 따로 유효성 검사를 안 해주는 거군요...! 세진님은 유효성 검사가 어느 수준만큼 이뤄져야 한다고 생각하시나요?

Choose a reason for hiding this comment

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

이런식으로 validator 를 만들수도 있군요...!

Choose a reason for hiding this comment

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

객체 다루기 마스터!

Copy link

@seeyoujeong seeyoujeong left a comment

Choose a reason for hiding this comment

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

코드 잘봤습니다!
세진님 코드는 항상 흥미롭습니다 ㅎㅎ
이번엔 미니게임에 진심을 다하신 모습 보기좋았어요 ㅋㅋㅋ
템플릿이나 유효성 검사하는 코드에서 많이 배워갑니다!


  • 이벤트를 활용하는 방식이 뷰와 로직을 분리한다는 관점에서 유의미한 방법인지 궁금합니다 !
    저도 잘몰라서 유의미한 방법인지는 모르겠지만 관리하기 편해보여서 좋았어요!

Comment on lines +18 to +21
if (this.#position + diff < 0) {
this.#position = 0;
} else {
this.#position += diff;

Choose a reason for hiding this comment

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

삼항 연산자로 바꿔도 좋을듯합니다!

selectRandomMiniGame() {
const miniGameNames = Object.keys(this.miniGames);
this.currentMiniGame =
miniGameNames[getRandomNumber(0, miniGameNames.length - 1)];

Choose a reason for hiding this comment

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

가독성을 높이기위해 변수에 할당하고 사용하면 좋지않을까요..?

const miniGameNames = ...;
const randomIndex = ...;
this.currentMiniGame = miniGameNames[randomIndex];

이렇게요!

Comment on lines +25 to +26
this.maxRound = maxRound;
this.validate(this.maxRound, ['maxRoundNumber', 'maxRoundRange']);

Choose a reason for hiding this comment

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

유효성 검사를 하고 프로퍼티에 할당하는게 좋을듯합니다!
검사를 통과 못하면 할당할 필요가 없으니깐요!

Comment on lines +57 to +63
positions: this.cars.reduce(
(acc, car) => ({
...acc,
[car.name]: car.position,
}),
{},
),

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 +16
this.printer = new ConsolePrinter({
roundStart:
'--------------------------⭐ Round%{1}⭐️️--------------------------',
carPosition: '%{1} : %{2} (%{3}%{4} ➡ %{5})',
gameLog: '%{1} : %{2} VS computer : %{3} ➡➡ %{4}',
miniGameStart: '>> player %{1} Turn!',
winner: '최종 우승자는 👑 %{1} 입니다. 축하합니다!',
error: '⚠️ %{1}',
divider:
'---------------------------------------------------------------',
});

Choose a reason for hiding this comment

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

템플릿 만드신건 정말 대단..!!!

Comment on lines +40 to +43
this.printer.printWithTemplate('gameLog', [
name.padEnd(5, ' '),
...Object.values(log),
]);

Choose a reason for hiding this comment

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

displayGameLog 메서드를 만들면 가독성에 좋을것같아요!

Comment on lines +49 to +51
const { positions } = results[currentRound - 1];
const prevPositions =
currentRound > 1 ? results[currentRound - 2].positions : {};

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.

객체 다루기 마스터!

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants