[우아한테크코스 4기 프리코스] 2주차 후기

NinjaJuunzzi·2021년 12월 8일
0

배달의민족

목록 보기
1/6
post-thumbnail

2주차 과제 제출 이후 피드백 문서와 다른 사람의 코드를 보고 느낀점을 정리하려고 한다. 마지막엔 성장한 점 또한 적어보았다.

2주차 과제로 성장한 점

  • 변수명, 메서드명, 함수명의 축약표현을 지양하고 명확한 의미를 갖도록 지어보았다. -> 1주차 과제에서 느낀점을 적용

  • lint와 code formatter 툴을 활용하여 프로그래밍 요구사항, 컨벤션 지키기 등을 잘 지킬 수 있었다. ( 기존에 들어가던 비용을 낮출 수 있었다 ) -> 1주차 과제에서 느낀점을 적용

  • MVC 패턴의 필요성을 느끼게 되었다. 비즈니스 로직을 어설프게 분리하니 클래스에 부여하였던 의미가 퇴색되는 느낌을 받았다. 또 프로그램이 커졌을 때 유지보수가 힘들어질 수 있겠다 느끼게 되었다.

  • javascript 객체의 가변성을 알게되었고, 객체 상수의 경우 불변 객체로 만들기 위해 Object.freeze를 사용할 수 있음을 알게되었다.

  • 클래스의 분리하고자 한다면 단일 책임을 갖도록 해야한다. 서로 간 의존도가 생길 수 있으니 이를 고려하고 분리하도록 해야함을 느꼈다.

  • ERROR 메세지를 각 상황별로 다르게, 또 어떤 문제가 발생하였는지 사용자가 인지할 수 있도록 하였다. 조금 더 사용자 지향적인 프로덕트에 대해 고민하게 되었다.

  • 커밋메세지를 작은 단위로 또 상세하게 적어 명확한 의미를 갖도록 하였다.

  • 불필요한 멤버들이 존재하였는데, 피드백을 통해 불필요한 변수를 제거해야함을 알게되었다

  • Boolean을 리턴하는 경우 간결하게 한다. 나도 명확하지 않은 코드를 사용하고 있었다.

  • 불필요한 공백라인은 추가하지 않았다. -> 1주차 과제에서 느낀점을 적용

1. 클래스의 분리와 MVC 패턴

이번 과제에서 클래스의 분리라는 목표를 보고, 가장 먼저 떠올린 것은 비즈니스 로직과 렌더링 파트를 분리하는 것이었다.

그렇기에 1주차 과제와 동일하게 클래스를 분리해보았다.

2주차 피드백에도 위와 같이 적혀있어서 뭔가 답을 맞춘 기분이 들긴하였으나, 신경쓰이는 부분은 '단일 책임의 원칙' 이란 말이었다.

과연 나는 단일책임의 원칙을 잘 지킨걸까 ?? 이번 프로젝트에서 클래스는 총 4개였다.

  • Car : 자동차 게임에서 사용되는 데이터 모델 클래스이다.
  • CarGame : 자동차 게임의 화면(뷰)를 담당한다.
  • CarGameController : 뷰와 데이터를 변경한다.
  • CarGameUtil : 자동차 게임 내부에서만 사용되는 유틸 함수들이 static 메소드로 포함되어 있다.

일단 이 과정에서 뷰를 담당하는 클래스와 데이터모델과 뷰를 변경하는 컨트롤러 클래스는 상속관계이다. 이 관계는 사실 맞지 않는 것 같다. 단일 책임의 원칙은 다음과 같다.

모든 클래스는 하나의 책임만 가지며, 클래스는 그 책임을 완전히 캡슐화해야 함을 일컫는다. 클래스가 제공하는 모든 기능은 이 책임과 주의 깊게 부합해야 한다.

뷰와 클래스는 상속관계가 아닌 분리된 두 개의 서로다른 클래스이다. 하나의 클래스로 묶이는 순간 단일 책임의 원칙에 위배된다. 의존도가 생기기 때문에 잘못된 설계라고 생각한다.

MVC 패턴

데이터, 컨트롤러, 뷰 세 개의 클래스로 어플리케이션을 구현한다. 하나의 아키텍처이다. 중요한 점은 서로 의존하지 않는 각기 다른 클래스라는 점이다. 기존의 방식을 MVC 패턴을 적용하여 각기 클래스 간 의존도를 낮춘다면 좋을 것 같다고 생각하였다.

Model : 데이터(자료구조)이다. 데이터를 관리하고 변경한다.

View : 모델의 상태에 따라 렌더링 되는 화면이다.

Controller : Model과 View를 변경한다.

    1. Model은 컨트롤러와 뷰에 의존하지 않아야 한다. 모델 내부에 컨트롤러 뷰와 관련된 코드가 있으면 안된다.
    1. 뷰는 모델에만 의존해야하고, 컨트롤러에는 의존하면 안된다. 모델의 코드는 있을 수 있으나 컨트롤러의 코드가 있으면 안된다.
    1. 뷰가 모델로부터 데이터를 받을 때는, 사용자마다 다르게 보여주어야하는 데이터에 대해서만 (변경되는 데이터에 대해서만)
    1. 컨트롤러는 모델과 뷰에 의존해도 된다. 컨트롤러 내부에는 모델과 뷰의 코드가 있을 수 있다.
    1. 뷰가 모델로부터 데이터를 받을 때는 반드시 컨트롤러에서 받아야 한다.

기존 클래스 분리 코드의 문제점을 정리하자면

  1. 비즈니스 로직 클래스와 뷰 클래스를 분리하지 못하였다. 두 개의 클래스로 분리하였으나, 상속관계를 줌으로써 단일 책임의 원칙에 위배되었다.

  2. 의존성을 갖는다. 뷰가 컨트롤러를 알고 직접 데이터를 변경하고 참조하게 되므로 클래스간 의존도가 높다고 할 수 있다.

2. submit 이벤트 핸들링 vs submit button의 click이벤트 핸들링

본인은 form 태그의 submit 이벤트를 핸들링하기위해 form 내부의 버튼에 클릭이벤트핸들러를 달기보단, form 태그에 submit 핸들러만 달아 이벤트를 핸들링하고 있다.

// src/app/index.js
    this.formCars.addEventListener('submit', this.onCarsFormSubmit);
    this.formCount.addEventListener('submit', this.onCountFormSubmit);

하지만 다른 사람의 코드를 보면 form 태그에 submit 이벤트 핸들러를 다는게 아니라 submit buttonclick 이벤트를 제어하는 모습을 보이곤 한다.

 $('#car-names-submit').addEventListener('click', (e) =>
      this.handleCarNamesSubmit(e)
    );
    $('#racing-count-submit').addEventListener('click', (e) =>
      this.handleRacingCountSubmit(e)
    );

사실 원래 알고 있는 차이정도밖에 없는 것 같다.

 <form id="car-names">
        <input type="text" id="car-names-input" />
        <button id="car-names-submit">확인</button>
 </form>

위 마크업의 경우 onSubmit 핸들러를 form태그의 submit 이벤트에 등록한다면 버튼을 클릭하여도, enter 키를 통해 submit 이벤트를 트리거 하여도 핸들러가 이벤트를 핸들링한다.

버튼에 클릭이벤트만 등록한다면 버튼을 클릭하는 행위 이외에는 동작하지 않게 된다.

결국 버튼에 click 핸들러, 폼에 submit 핸들러 두 가지 이벤트를 핸들링해야하는데 하는 일은 같다. 그러므로 두 개의 핸들러를 할당하기 보단 formsubmit 핸들러만 바인딩하여 클릭과 엔터클릭에 발생하는 이벤트를 핸들링 하는 것이 낫다고 보인다.

eslint - prettier를 사용하자

NHN의 코딩 컨벤션을 따르겠다고는 하였지만, 프로그램이 커질 수록 라인이 길어지고 따라서 코딩 컨벤션 문서에 맞게 리팩토링 하는데 시간이 오래걸리게 되었다. 특히 우테코 프리코스의 요구대로 15라인이 넘지않는 함수 등을 작성하고자 할 때 이를 일일히 세어보아야 문제를 알 수 있었는데 이러한 점들을 개선하기 위해 lint 도구와 code formatter 도구를 사용하고자 한다.

대게 lint 도구로는 eslint, code formatter 도구로는 prettier 를 사용한다. 오류를 잡고자 한다면 eslint를, 코드 스타일을 맞추려면 prettier를 사용한다.

이 두개를 적용할 때의 목표는 NHN의 코드 스타일을 따라가자였다. 그렇기 때문에, NHN의 lint option를 따르면서 프리코스가 요구하는 사항 및 내가 안지켜도 된다라고 판단한 것들을 커스텀하여 lint option을 세팅하였다.

NHN을 따르기 위해 다음 패키지를 설치한다.

npm install eslint-config-tui

옵션은 다음과 같이 커스텀해보았다.

{
  "env": {
    "browser": true,
    "es2021": true,
    "es6": true
  },
   
  "extends": ["tui", "prettier"],
  "parserOptions": {
    "sourceType": "module"
  },
  "rules": {
    // 사용되지 않는 변수는 에러를 노출하여, 불필요한 변수의 생성 및 import를 제어한다.
    "no-var": "error",
    // indent 조건을 지키기 위해 다음 라인을 추가
    "max-depth": ["error", 2],
    // 함수라인은 15라인 이하로
    "max-lines-per-function": ["error", 15],
    // 피드백에 의하면 불필요한 console.log는 제거하라고 함
    "no-console": "warn",
    // param을 재할당하는 것은 anti-pattern이라고 생각함  
    "no-param-reassign": "error"
  }
}

lint 옵션을 위와 같이 설정하였으므로 이제 컨벤션을 따르지 않거나 옵션에서 벗어나는 코드가 작성되면 에러가 노출된다. 하지만 prettier를 NHN에 맞게 설정하지 않았다면, 기본 확장을 설치해두었기 때문에 충돌이 발생할 수 있다.

이 충돌을 방지하기 위해 다음 패키지를 설치한다.

npm install eslint-config-prettier

위 패키지를 설치하여 lint위주로 prretier와 충돌할 수 있는 룰을 꺼버리도록 한다. 둘의 구분을 명확히 하여 코드 오류를 잡는데는 eslint - 코드 포맷팅에는 prettier를 사용할 수 있다. 하지만 NHN 컨벤션을 지키자는 나의 목표에는 사실 옵션을 꺼버리는 행위는 맞지 않다. 그렇기 때문에 NHN이 사용하는 prettier 옵션을 사용하고자 한다.

module.exports = {
  singleQuote: true,
  printWidth: 100,
  tabWidth: 2,
  useTabs: false,
  semi: true,
  quoteProps: 'as-needed',
  jsxSingleQuote: false,
  trailingComma: 'es5',
  arrowParens: 'always',
  endOfLine: 'lf',
  bracketSpacing: true,
  jsxBracketSameLine: false,
  requirePragma: false,
  insertPragma: false,
  proseWrap: 'preserve',
  vueIndentScriptAndStyle: false,
};

Ref

profile
Frontend Ninja

0개의 댓글