[FE] 우테코 6기 프리코스 3주차 회고

DAN·2023년 11월 17일

우테코

목록 보기
3/9
post-thumbnail

🚀 미션을 수행하기 전에

공통 피드백과 새롭게 추가된 학습 목표를 정리하고, 다른 분들의 코드를 보면서 정리한 부분을 추가해 나만의 학습 목표를 만들었다.


2주 차 공통 피드백

  • README.md를 상세히 작성한다. 어떤 프로젝트이며, 어떤 기능을 담고 있는지 기술한다.
  • 기능 목록을 재검토한다. 정상적인 경우도 중요하지만, 예외적인 상황도 기능 목록에 정리한다.
  • 기능 목록을 업데이트한다. 기능을 구현하면서 문서를 계속 업데이트해 살아있는 문서를 만들기 위해 노력한다.
  • 값을 하드 코딩하지 않는다.
  • 구현 순서도 코딩 컨벤션이다.
  • 한 함수가 한 가지 기능만 담당하게 한다.
  • 함수가 한 가지 기능을 하는지 확인하는 기준을 세운다. 중복 코드를 줄이고, 15라인을 넘어가지 않도록 한다.
  • JavaScript에서 객체를 만드는 다양한 방법을 이해하고 사용한다.
  • 테스트를 작성하는 이유에 대해 본인의 경험을 토대로 정리해본다.
  • 처음부터 큰 단위의 테스트를 만들지 않는다. 문제를 작게 나누고, 그 중 핵심 기능에 가까운 부분부터 작게 테스트를 만들어 나간다.

새롭게 추가된 학습 목표

  1. 클래스(객체)를 분리하는 연습
  2. 도메인 로직에 대한 단위 테스트를 작성하는 연습
  3. else를 지양한다.

나만의 학습 목표

1. 클래스 문법 활용

  • 클래스 문법(private 변수 사용, 접근자 프로퍼티)을 활용하자.
  • 클래스를 '잘' 분리하자.

2. 테스트

  • 도메인 로직에 대해서 학습하고 단위 테스트를 작성하자.
  • 테스트 코드의 가독성을 향상시키자.

3. 가독성과 함수 역할 분배

  • 에러 핸들링 로직을 함수로 분리하자.
  • 값을 하드 코딩하지 않고 상수로 만들자.


클래스

클래스 문법 활용

이번 미션의 특징은 Lotto 클래스가 주어졌다는 것이었다. 그리고 유일한 private 멤버 변수인 numbers를 수정하지 않고 프로그램을 구성하는 것이 중요한 미션이었다.
다른 분들의 코드를 보면서 이번 미션에서는 private 선언 + 접근자 프로퍼티 등 클래스 문법을 적극적으로 사용해 캡슐화를 지켜야겠다는 목표를 세웠는데, 이에 부합하는 조건이라서 좋았다.

그래서 Lotto를 아주 심플하게 유지했다. 다만 Lotto에 필드를 추가하지 말라는 조건이 있어서 접근자 프로퍼티는 사용하지 않고 getter 함수를 따로 만들어 사용했다. 다른 클래스에는 위의 내용을 적용했다.

class Lotto {
  #numbers;

  constructor(numbers) {
    this.#validate(numbers);
    this.#numbers = numbers.sort((a, b) => a - b);
  }

  #validate(numbers) {
    //... validator 로직
  }

  getNumbers() {
    return this.#numbers;
  }
}

🥲 아쉬운 점
3주 차 피드백에 '객체는 객체스럽게 사용한다'는 내용이 있었다. 객체에 상태 값만 넣지 않고 로직에 대한 구현이 있어야 한다는 내용이었는데, 딱 내 코드여서 매우 뜨끔했다.
로또 번호를 비교하는 로직이 WinningLotto에 있어서 Lotto는 값만을 가지도록 만들어지게 됐는데, 피드백을 보니 해당 내용을 Lotto 클래스에 넣었다면 더 좋았을 것 같다는 생각을 했다.

객체를 '잘' 분리하자.

이번 미션 또한 MVC 패턴을 사용해서 구현했는데, 지금까지와는 다르게 Model 코드가 여러개가 필요했다. 대표적으로 Lotto 클래스, 상금을 판단하는 Prize 클래스 등이 있었는데, 위 클래스들을 컨트롤러에서 직접 관리하자니 필요 이상으로 많은 정보를 알고 있어야 한다는 생각이 들었다. 그래서 컨트롤러와 모델들을 중개해 주는 모델을 만들었고, 그것이 LottoMachine이 되었다.

class LottoMachine {
  // 로또 발행 완료 여부
  isIssueOver() {}

  // 일반 로또 발행
  issueLotto() {}

  // 당첨 로또 발행
  issueWinningLotto(winningNumbers, bonusNumber) {}

  // 당첨 결과
  async getResults() {}

  // Prize 클래스를 활용해서 상금 계산
  async calculatePrizeResult() {}
}

이렇게 클래스를 분리하고 나니 도메인 로직과 서비스 로직이 분리가 되어 테스트하기 더욱 쉬워졌다.


테스트

도메인 로직에 대해 단위 테스트를 작성하자!

위와 같은 피드백이 도착했을 때 '도메인 로직이 뭔데?..'라는 궁금증이 생겼다. 그러다가 이 글을 발견했다.

위 블로그에서 말하는 도메인 로직이란 '비즈니스에 대한 의사결정을 하는 코드'이다. '송금'이라는 기능이 있을 때 계좌 잔액을 확인하는 코드, 수수료를 결정하는 코드, 사용자의 잔액을 DB에 저장하는 코드 등이 있을 것이다. 여기서 의사결정을 하는 코드는 계좌 잔액이 충분한지 확인하고, 수수료를 계산하는 코드로 볼 수 있다. 즉, 해당 로직이 도메인 로직이 된다.
그렇다면 도메인 로직이 아닌 로직은 뭘까? 바로 서비스 로직이다. 이 코드들은 도메인 로직이 의사결정을 할 수 있도록 입력을 제공하고, 결과를 DB/UI에 업데이트하는 역할을 맡는다.

이번 로또 미션의 도메인 로직과 서비스 로직을 분리하면 다음과 같을 수 있다.

  • 도메인 로직

    • 로또 숫자 6개를 랜덤으로 생성하는 코드.
    • 당첨 여부를 확인하는 코드.
    • 수익률을 계산하는 코드.
  • 서비스 로직

    • 당첨 내역과 수익률을 출력하는 코드.
    • 유효하지 않은 값이 입력됐을 경우 다시 입력받는 코드.

나의 경우 도메인 로직은 모델에서, 서비스 로직은 Controller와 View에서 처리하도록 했다. 따라서 모델에 대해서만 테스트 코드를 작성했는데, 클래스 분리 + 도메인 로직 분리가 같이 이루어지니 더욱 작은 단위의 함수를 작성하는 것이 가능해져 수월하게 테스트할 수 있었다.

given-when-then

테스트를 위해 필요한 변수나 상수가 많아질 수록 코드는 뚱뚱해지고 정작 중요한 테스트 내용이 잘 보이지 않는 걸 경험했다.. 그래서 테스트 코드의 가독성을 향상시킬 수 있는 방법을 찾아봤는데, 바로 given-when-then이었다! (이것도 우테코 테코톡의 도움을 받았다. 감사해요 테코톡~)

가독성을 향상시키자

handleException

이번 미션에서는 유효하지 않은 입력이 들어왔을 때 Error를 던지는 것에 그치지 않고 다시 입력을 받아야 하는 요구사항이 추가됐었다. 이 부분을 컨트롤러에서 try/catch 문을 사용해 처리했는데, 함수 안에 코드 블록이 겹치니 indent depth가 하나 더 늘어나고 코드가 반복되는 것이 만족스럽지 않았다.

어떻게 하면 코드의 반복을 줄일 수 있을까 고민하다가 자바스크립트에서 함수가 일급 객체이기 때문에 매개변수로 전달될 수 있다는 걸 떠올렸다! 그래서 에러가 반환됐을 때 재시도하는 로직을 handleExeption이라는 유틸리티 함수로 분리하고, 실행할 함수를 매개변수로 전달하는 구조를 만들어 컨트롤러의 코드를 간단하게 유지할 수 있었다.

// utils.js
export const handleException = async (action) => {
  while (true) {
    try {
      await action();
      break;
    } catch (err) {
      Console.print(err.message);
    }
  }
};

// LottoController.js
await handleException(async () => this.#generateLottoMachine());

개인적으로 가장 뿌듯한 부분..!

🤔 왜 while문으로 작성했나요?

코드 리뷰를 받으면서 while(true)로 작성한 이유를 질문 받았었는데, 재귀호출보다 스택 오버플로우가 발생할 가능성이 낮고, 루프를 명시적으로 제어할 수 있다는 점에서 장점이 있다고 생각했다!


📒 미션 진행 소감

매번 새로운 미션을 받을 때 마다 추가되는 학습 목표를 보며 내 머릿속을 들여다 보고 설계한게 아닐까 의심한다..
이전 미션에서 아쉽다고 느껴진 로직을 개선할 수 있는 방법을 생각해 놓으면, 곧바로 다음 미션의 학습 목표로 제시된다. 정말이지 기획한 분들의 노하우가 응집된 좋은 프로그램이라고 다시 한번 감탄한다.

이번 미션은 이전 미션들과 다르게 모델 클래스가 여러개 생길 수 밖에 없어서 이것들을 어떻게 하면 잘 관리할 수 있을까.. 고민하며 보냈다. 또 클래스를 분리하면서 최대한 필드에 직접 접근하는 경우를 만들지 않으려고 했는데, 아직 이 부분에서 미흡해서 몇 개는 퍼블릭으로 선언했다.😅 4주차 미션을 통해서 더 많이 고민하고 발전시키려 한다.

그리고 3주 차는 정말 많은 분들이 좋은 피드백을 주셔서 이것만 따로 정리하는 글을 만들어 보려고 한다! 지금까지 딱 한 분에게 코드 리뷰를 받았던 것을 생각하면 매우 감격이었다..🥹 이 에너지로 4주 차엔 더 좋은 코드를 작성해야지!

profile
블로그 옮겼습니다 | dan.io.kr

0개의 댓글