[우아한테크코스] 프리코스 1주차 회고록

Jerry·2023년 10월 26일

우아한테크코스

목록 보기
1/3
post-thumbnail

1주차 미션 : 숫자 야구

이번 1주차 미션은 아래 브랜치명들로 요약할 수 있다.

브랜치가 만들어진 순서는 이렇다.

  • FollowTheRequirementsExactly
  • FollowTDD
  • TheBestICanDo

순서에 따라 각 브랜치들에 대해 설명하면서 회고록을 작성하려 한다.

📄FollowTheRequirementsExactly


처음 이 브랜치의 목표는 객체지향, TDD, MVC와 같은 개념들을 고려하지 않고 일단은 테스트를 통과하는 것이었다. 그럼 브랜치명을 PassTheTest로 하는 것이 더 적절할 것 같지만 FollowTheRequirementsExactly로 정한 이유가 있다.

처음 이 브랜치 코드를 완성했을 때는 테스트를 통과하지 못했다. node에서 실행해보면 기능적으로 잘 동작하는 것처럼 보였지만 이상하게 테스트는 통과하지 못했다. 원인을 찾을 수 없어서 결국 포기하고 FollowTDD 브랜치를 만들게 되었는데 FollowTDD에서 코드를 다시 작성하면서 실패 원인을 찾을 수 있었다.

원인은 간단했다.
매우

첫 번째, App.js 파일에서 await을 붙이지 않았다.

두 번째, 요구사항을 제대로 지키지 않았다.


저렇게 Random.pickNumberInRange()를 활용하라고 정확히 나와있는데 멋대로 Random.pickUniqueNumbersInRange()를 사용했다.

또한 프로그램 종료시 Process.exit()를 호출하지 말라고 명시되어 있음에도 아래 코드를 보면 아주 당당히 사용하고 있다.

그래서 브렌치 이름이 "요구사항을 정확히 따르자!"가 되었다.

📈 FollowTDD


FollowTheRequirementsCorretly 브렌치는 어떤 단위 테스트도 작성하지 않고 코드를 작성했다.
FollowTDD에서는 테스트 코드 작성 -> 기능 구현 -> 테스트 -> 리팩터링 -> 테스트의 반복된 과정으로 이루어지는 테스트 주도 개발의 기본 흐름을 따르려고 노력했다.
FollowTheRequirementsCorretly 브랜치에서 테스트 코드를 작성하지 않고 기능을 구현할 때는 기능을 구현할 때마다 node src/App.js로 게임을 실행해서 확인해봐야 했다. 처음에는 재밌었지만 계속하다보니 쓸데없는 노동으로 느껴졌다. 이렇게 간단한 게임에서도 귀찮다고 느끼는데 큰 프로젝트에서는 어떨지 상상하기조차 싫었다.

코드에 대한 자신감

테스트 코드를 작성하면서 느낀 가장 큰 장점은 코드에 대한 자신감이 생긴다는 것이다. TDD에 대해 알기 전에 코드를 작성할 때는 코드가 이상하게 작동해도 어디가 문제인지 찾는데 오랜 시간이 소요됐다. 하지만 테스트 코드를 작성하면서는 내가 작성한 코드에 대한 확신이 생기고 오류를 찾는데 적은 시간이 소요됐다.

개발 시간 단축

테스트 코드를 작성해보기 전에는 막연히 더 오랜 시간이 소요될 거 같다는 생각이 들었지만 그렇지 않다!!!! 물론 처음에는 익숙하지 않아 당연히 시간이 걸리겠지만 조금만 작성해봐도 테스트 코드를 작성하는 것이 훨씬 안정적이고 앞으로의 개발 시간을 단축시킬 거라는 것을 느낄 수 있다! 정말!! 테스트 코드를 통해 내가 작성한 코드의 함수가 제대로 기능하고 있다는 사실을 확신할 수 있고, 이는 분명 개발 시간을 단축시킨다!

이외에도 작성하지 않은 수많은 장점이 있으니 이 글을 읽으시는 분 중 아직 테스트 코드를 작성해보지 않은 분들이 있다면 프리코스 기간 동안 꼭 시도해보시기를 추천하고 싶다. 정말 꼭!꼭!꼭!
코드를 다 작성해 놓고 나중에 테스트 코드를 작성하는 것을 말하는 것이 아니다. 정말 테스트 코드 작성 -> 기능 구현 -> 테스트 -> 리팩터링 -> 테스트의 반복된 과정으로 느낄 수 있는 TDD의 장점을 꼭 경험해보시길 바란다.

⌜테스트 주도 개발 시작하기⌟

만약 TDD가 무엇인지 생소하시다면 ⌜테스트 주도 개발 시작하기⌟ 책을 추천한다!! 이 책은 자바로 되어있긴 하지만 읽는데 무리가 가지는 않는다.
내 블로그에 자바스크립트 코드로 ⌜테스트 주도 개발 시작하기⌟ 시리즈로 작성해 놓은 자바스크립트 코드도 있다! 만약 참고할 분이 있다면 꼭 책과 함께 보시기를 추천한다!

👍🏻TheBestICanDo


FollowTDD 브랜치 코드를 완성하고 리팩터링을 좀 더 거친 후에 이 코드가 정말 내가 이번 주차에 할 수 있는 최선인지에 대해 수십 번 생각했다. 결국 followTDD 브렌치에서 리팩터링을 통해 코드를 발전시키지 않고 설계를 처음부터 하기로 마음 먹은 것이 아래 이유 때문이었다.

3자리가 아니라 4자리라면?

나는 학창시절 숫자 야구 게임을 자주 했었다. 그런데 그때는 3자리 숫자가 아니라 4자리 숫자로 게임을 했었어서 처음 이 미션을 보자마자 4자리로도 설정을 쉽게 바꿀 수 있으면 좋겠다는 생각을 했었다. FollowTDD를 하고 그렇게 설정하기 쉽도록 리팩터링을 해볼까 하고 보니 리팩터링이 아니라 다 갈아엎어야 할 것 같았다. 상수 처리 해놓은 메시지에도 3이라는 숫자가 들어갔다.

"3개의 숫자를 모두 맞히셨습니다! 게임 종료"

아래는 FollowTDD 브랜치의 App.js 파일이다.

import NumberBaseballGame from "./NumberBaseballGame.js";

class App {
  async play() {
    const numberBaseballGame = new NumberBaseballGame();
    await numberBaseballGame.start();
  }
}

// const app = new App(); // 개발용
// app.play(); // 개발용

export default App;

아래는 TheBestICanDo 브랜치의 App.js 파일이다.

import NumberBaseball from "./NumberBaseball.js";
import NumberBaseballConsole from "./NumberBaseballConsole.js";
import NumberBaseballUmpire from "./NumberBaseballUmpire.js";
import NumberBaseballUmpireIndicator from "./NumberBaseballUmpireIndicator.js";
import RandomNumbersGenerator from "./RandomNumbersGenerator.js";

class App {
  async play() {
    const numberBaseball = new NumberBaseball(
      new NumberBaseballConsole(1, 9, 3, 1, 2),
      new NumberBaseballUmpire(new NumberBaseballUmpireIndicator(3)),
      new RandomNumbersGenerator(1, 9, 3)
    );
    await numberBaseball.start();
  }
}

// const app = new App(); // 개발용
// app.play(); // 개발용

export default App;

시작할 때 숫자의 범위, 숫자의 개수, 재시작 숫자와 종료 숫자를 설정할 수 있도록 바꿨다.

자율적인 객체

⌜객체지향의 사실과 오해⌟ 책을 읽으면서 묻지 말고 시켜라라는 말이 잘 이해되지 않았다.

객체는 다른 객체의 상태를 묻지 말아야 한다. 객체가 다른 객체의 상태를 묻는다는 것은 메시지를 전송하기 이전에 객체가 가져야 하는 상태에 관해 너무 많이 고민하고 있었다는 증거다. 고민을 연기하라. 단지 필요한 메시지를 전송하기만 하고 메시지를 수신하는 객체가 스스로 메시지의 처리 방법을 고민하게 하라.

이번 미션에서 가장 많은 고민을 한 객체가 있다면 UmpireIndicator 객체였다.
스트라이크와 볼 개수를 판단하는 객체를 Umpire 그리고 메서드를 umpire()로 설정했었다. 코드를 작성하다보니 심판들이 보면서 체크하는 기계가 생각났고 그 기계를 Umpire 객체가 가지고 있게 하면 되겠다는 게 기본 생각이었다.
아래 기계가 Umpire Indicator다.

FollowTDD 브렌치의 UmpireIndicator 객체 코드는 이렇다.

class UmpireIndicator {
  #strike = 0;
  #ball = 0;

  get strike() {
    return this.#strike;
  }
  set strike(number) {
    this.#strike = number;
  }

  get ball() {
    return this.#ball;
  }
  set ball(number) {
    this.#ball = number;
  }
}

export default UmpireIndicator;

메시지는 없고 상태를 설정하고 묻는 코드만 있다. 근데 나는 이런 객체까지 메시지를 날리게 해야 하나라는 의문이 들었다. 내가 생각하는 좋은 코드는 일반적인 상식과 같게 동작하는 코드라고 생각했기 때문이다.
이 문제뿐 아니라 큰 욕심 탓에 과부화가 걸려 코드를 설계하는 것을 포기하고 예약해둔 책을 대출할 수 있다는 문자에 책을 빌리러 도서관에 가 그 책의 챕터1을 읽었다.

앞에서는 실세계에서의 생물처럼 스스로 생각하고 행동하도록 소프트웨어 객체를 설계하는 것이 이해하기 쉬운 코드를 작성하는 것이라고 설명했다. 하지만 이제 말을 조금 바꿔야겠다. 훌륭한 객체지향 설계란 소프트웨어를 구성하는 모든 객체들이 자율적으로 행동하는 설계를 가리킨다. 그 대상이 비록 실세계에서는 생명이 없는 수동적인 존재라고 하더라도 객체지향의 세계로 넘어오는 순간 그들은 생명과 지능을 지닌 싱싱한 존재로 다시 태어난다.
따라서 이해하기 쉽고 변경하기 쉬운 코드를 작성하고 싶다면 차라리 한 편의 애니메이션을 만든다고 생각하라. 다른 사람의 코드를 읽고 이해하는 동안에는 애니메이션을 보고 있다고 여러분의 뇌를 속여라. 그렇게 하면 코드 안에서 웃고, 떠들고, 화내는 가방 객체를 만나더라도 당황하지 않을 것이다.

⌜오브젝트⌟

유레카까지는 아니어도 조금은 머리가 조금은 개운해지는 기분이었다. 제출까지 시간이 촉박했지만 그래도 의문이 풀린 이상 최선을 다한 더 좋은 코드로 제출하고 싶었다.

바뀐 UmpireIndicator 코드는 이렇다.

class NumberBaseballUmpireIndicator {
  #strike = 0;
  #ball = 0;
  #count;
  constructor(count) {
    this.#count = count;
  }

  indicateAbout(array1, array2) {
    this.#click(array1, array2);
    return { strike: this.#strike, ball: this.#ball };
  }

  isOut() {
    return this.#strike === this.#count;
  }

  #reset() {
    this.#strike = 0;
    this.#ball = 0;
  }

  #click(array1, array2) {
    this.#reset();
    for (let i = 0; i < this.#count; i++) {
      if (array1[i] === array2[i]) this.#strike += 1;
      else if (array1.includes(array2[i])) this.#ball += 1;
    }
  }
}

export default NumberBaseballUmpireIndicator;

자신의 데이터를 스스로 처리하는 자율적인 객체를 만들려고 노력했다.
사실 그렇게 되면서 Umpire이 하는 일이 너무 없어진 게 아닌가 하는 생각이 들어서 내가 작성한 코드가 옳은 방향인지 아닌지 아직도 확신은 없다....(난 요즘 내 모든 코드에 대한 확신이 없다....)

하지만 그럼에도 마지막 브랜치 이름을 TheBestICanDo로 설정한 이유는 내가 할 수 있는 최선의 최선의 최선의 최선의 코드였기 때문이다. 정말.

사실 하면서도 시간 안에 이 설계대로 테스트를 통과할 수 있는 코드를 작성할 수 있다는 확신은 없었는데 이십여분 정도를 남겨두고 통과를 했고 통과된 코드를 이미 내가 작성해 높은 테스트 코드에도 통과될 수 있게 바꾸고 싶었지만 시간이 너무 부족했다. 그래서 그냥 FollowTDD 브렌치로 제출할까 싶기도 했지만... 그래도 정말 최선을 다한 코드라 내가 작성한 테스트에는 통과하지 못해도 제출했다.

급하게 제출한 탓에 아래 코드와 같은 말도 안 되는 실수를 하며 제출했지만...

또 크게 아쉬운 건 설계를 변경하고 급하게 작성하는 탓에 커밋을 하지 않고 그냥 쭉 작성해서 제출한 브랜치에서 마지막 커밋과 직전 커밋의 코드 차이가 매우 큰 게 아쉽다.

정리


TheBestICanDo 브랜치를 처음 생성할 때 객체지향적인 설계를 하고 코드를 작성하고 싶었는데 막상 설계를 하려고 하니 너무 막막하고 이렇게 하면 이런 문제가 이렇게 하면 이런 문제가 보였다. 누가 나한테 이 코드가 맞아! 라고 말해줬으면 좋겠는 기분이랄까...

코드에도 설계에도 정답은 없다지만 누군가 '이런 방향으로 가는 게 좋다'라고 피드백해줬으면 좋겠는 마음... 피드백 받을 주위 친구나 멘토가 없다는 게 속상했다. 그래서 2주차에는 커뮤니티를 더 잘 활용해보고자 한다!!!

사실 1주차 때는 일부로 커뮤니티를 보지 않았다. 초반 커뮤니티 올라오는 처음 들어보는 수많은 용어들에 머리는 혼란스럽고 자신감은 떨어졌기 때문이다.

그러고 있던 와중 이 글을 보자마자 일단 커뮤니티를 껐다. 그리고 내가 무엇에 집중하고자 하는지를 생각했다.

집중

FollowTheRequirementsExactly

  • 기능 구현
  • 함수 분리
  • 테스트 통과

FollowTDD

  • TDD
  • 객체 분리

TheBestICanDo

  • 코드의 재활용성
  • 자율적인 객체

항상 '이 코드가 최선일까?'라는 생각을 많이 한다. 이번 미션에는 확실하게 얘기할 수 있을 거 같다.

내가 할 수 있는 최선을 다했다.

테스트를 통과하자마자 코드를 제출한 탓에 다시 보니 고칠 부분이 너무 많이 보여서 속상하지만... 아 심지어 과제 제출 소감은 "과제 제출 소감"이라고 임시로 적어서 제출한 이후 수정하지도 못했다😂.

이미 지나간 일에 대한 후회는 접어두고 2주차 미션에서는 더 좋은 코드를 작성하겠노라 다짐하며 글을 마친다.

글은 코드처럼 설계하지 않고 작성해서 두서가 없었을텐데 마지막까지 읽으신 분이 계시다면 감사하다는 말도 전하고 싶다.


혹시 피드백 해주실 사항이 있다면 꼭 남겨주세요 :)
아주 사소한 것이라 해도 상관 없습니다!

PR 링크
https://github.com/woowacourse-precourse/javascript-baseball-6/pull/828

profile
I'm jerry

0개의 댓글