🌱 ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [회고] 우아한테크코스 프리코스를 회고하며
    Daily-Life 2023. 11. 20. 23:14
    🍀 목차
    프리코스란?
    1주 차
    2주 차
    3주 차
    4주 차
    1주 차 ➡️4주 차

     

    프리코스란?

     우아한테크코스는 우아한형제들에서 진행하는 웹 개발 교육과정이다. 프리코스는 해당 교육과정의 선발 과정 중 하나이며, 각 모집분야에 명시된 언어를 활용하여 약 한 달간 주어진 미션을 수행한다. 지원서를 제출하면 모두가 참여 가능하며 커뮤니티(디스코드)를 통해 지원자들끼리 다양한 공유활동(지난 과제 리뷰/개발 지식 공유/모각코 등)이 가능하다.

     

     내가 프리코스에 참여하게 된 이유는 총 세 가지였다.

     

    1. 무언가를 만들어 본 적이 너무 옛날이다. 프로젝트도 계속 흐지부지됐고, 그러다 보니 내가 정말 무언가를 만들 수 있을까? 에 대한 두려움이 생겼다. 간단한 것이라도 주어진 문제에 도전해보고자 했다.

    2. 테스트 코드를 맛볼 수 있다.

    3. 코드에 대한 다양한 피드백을 받을 수 있다.

     

     

    1주 차

     숫자 야구 미션이었다. 첫 주차이기에 제출 가이드나 GitHub 관련 설정, Node.js 버전 설정 등을 놓치지 않기 위해 노력했다.

     간단해 보였으나 막상 해보니 지정된 코드 컨벤션 지키기, 라이브러리 이해, 테스트 코드의 통과 등 꽤 헤맸다. 부끄럽지만 1주 차에는 분리에 대한 고민을 크게 하지 않았다. 기능 목록 작성도 간단하게 진행, 평소에 하던 대로 상수만을 분리하였다.

     

    • 1주 차의 기능 목록
    # 구현 기능 목록
    
    ## 기능 요구 사항
    
    ### 힌트
    
    - 스트라이크 : 같은 수가 같은 자리에 있다.
    - 볼 : 같은 수가 다른 자리에 있다.
    - 낫싱 : 같은 수가 전혀 없다.
    
    ### 과정
    
    - [x] 0. 게임 시작 전 문구를 출력한다.
    - [x] 1. 컴퓨터는 1에서 9까지 서로 다른 임의의 수 3개를 선택한다.
      - [x] 0이 포함되지 않았는가?
      - [x] 서로 다른 임의의 수인가? `Random.pickNumberInRange()`를 활용한다.
      - [x] 3개의 수인가?
    - [x] 2. 플레이어는 컴퓨터의 수를 예측하여 _서로 다른 3개의 숫자를 입력_ 한다.
      - [x] 입력은 `Console.readLineAsync`를 활용한다.
      - [x] 사용자가 잘못된 값을 입력한 경우 `throw`문을 사용해 예외를 발생시킨 후 애플리케이션은 종료되어야 한다.
      - [x] 예외 사항 시 에러 문구를 출력해야 한다. 단, `[ERROR]`로 시작한다.
    - [x] 3. 컴퓨터는 입력한 숫자에 대한 결과를 출력한다.
      - [x] 출력은 `Console.print`를 활용한다.
      - [x] _볼, 스트라이크 개수로 출력_ 한다.
      - [x] 힌트의 모든 경우를 구현했는가?
    - [x] 4. 반복한다.
    - [x] 5. 플레이어가 컴퓨터가 선택한 3개의 숫자를 모두 맞히면 종료된다.
    - [x] 6. 게임 종료 후, 다시 시작하거나 완전히 종료할 수 있다.
      - [x] _1 입력 : 재시작_
      - [x] _2 입력 : 종료_
        - [x] 종료 시, `process.exit()`를 호출하지 않는다.

    과제에 대한 파일 분리 이미지
    상수만 분리했다.

     

    그때의 나는 나름 분리를 하지 않았던 이유가 있었다. 프로그램이 굉장히 작은 단위라고 판단하였고, 처음부터 분리를 하기보다 필요하게 될 경우 분리를 하자 생각했다. 그래서 후에 다른 사람들의 코드를 보며 꽤 충격을 받았다. MVC 패턴으로 분리를 하신 경우도 많았고, 예외처리 분리 등 높은 퀄리티의 코드가 많았다.

     지금 보면 적어도 예외처리나 출력은 분리를 하는 것이 좋아 보인다. 모든 코드가 App.js에 있으니 로직과 뷰가 혼합되어 있어 특정 부분을 찾는 데 굉장히 오래 걸린다.

     

    1주 차 느낀 점

    • 처음부터 분리를 생각하기보다 돌아가는 코드 구현 ➡️ 함수 분리가 더 구현 시간이 빨랐다.
    • 함수의 역할에 대해 이름 붙이는 것이 어려웠다. 직관적인 네이밍인가?에 대한 고민이 길었다.
      • 지금 생각해 보니 함수가 한 가지 역할만 하는 것이 아니었기 때문이다. getApple인데 Apple만 얻어오는 코드가 아닌 Apple값에 대한 예외처리... 상자에 담고... 하는 등 다양한 것들이 혼재해있다 보니 함수가 뚱뚱하다. 

    출처 : 네이버 지식인

    • 기능 목록을 정리하며 편리함을 느꼈다. 다만 commit 단위로 작성해야 할지? 해당 단위가 너무 커진다면? 기능 목록을 아무 생각 없이 작성하기보다 보다 큰 그림을 보며 분리하는 것이 좋아 보였다.
    • 테스트 코드가 존재하여 리팩토링 후에 기존 기능에 영향을 주진 않았는지 파악할 수 있어 좋았다.

     

     

    모두에게 전달된 공통 피드백과 다른 사람과의 코드 리뷰, 코드들을 보며 반성한 점은 다음과 같다.

    • MVC 패턴을 사람들이 왜 썼는지 고민하자. 내 코드와의 차이점을 위주로 생각한다.
    • 변수, 함수, 클래스 이름을 짓는데 시간을 투자하자. 불용어(info, data, a, an, the)는 적절하지 않다.
    • linter, code formatter의 기능을 활용하자. 코드 컨벤션을 매번 확인하기보다 애초에 강제를 주자.
    • 숫자 야구 피드백 강의
      • 객체 지향 프로그래밍
        • 기능을 가지고 있는 클래스를 인스턴스화(=객체)한다.
        • 필요한 기능을 역할에 맞는 인스턴스가 수행하게 한다.
        • 각 결과를 종합한다.
      • 기능 목록을 잘 작성한다. 미리 함수 이름을 작성해 본다거나, 테스트로 작성해야 할 요소를 생각한다.

    기능목록에 대한 고민이 담긴 이미지

     

    +) 코치와 수다 타임(코수타)을 통해 외면받고 있는(리뷰 요청이 매우 많기에) 코드를 먼저 코드리뷰해 드리는 것도 좋다하셔서 용기를 내어 먼저 코드 리뷰를 작성드렸다. 후에 한 분께서 리뷰를 남겨주셨고, 2주 차에는 두 분께 리뷰를 받고 싶은 목표가 생겼다.


    2주 차

     자동차 경주 미션이었다. 1주 차보다 2가지의 요구 사항이 추가되었다.

     

    추가된 요구 사항에 대한 내용이 담긴 이미지

     

    2주 차부터는 목표를 세워 계속 확인하며 리마인드 했다.

     

     

    2주 차의 목표가 담긴 이미지
    2주 차의 목표

     

     1주 차 종료 후, 공통 피드백에 숫자 야구 강의가 있었고 그를 수강하며 객체 지향에 대한 감을 잡았다.

    그 과정에서 자연스레 가장 유명한 MVC 패턴에 대해 학습하였고 2주 차에 적용하게 되었다.

    적용한 이유는 리팩토링 하기 편해 보여서, 그리고 자동차 경주 미션의 내용 때문이었다.

    n대의 자동차를 전진 또는 멈추게 할 수 있는데, 이 n개를 재사용하고 관리하려면 객체, 모델로 만들어 관리하는 것이 좋아 보였기 때문이다. 또한, 1주 차에 로직과 출력이 혼재되어 헷갈렸던 부분을 고치고 싶었다.

     

    MVC 패턴
     소프트웨어 아키텍처 디자인 패턴 중 하나이다.
    디자인 패턴이 개발 과정에서 자주 발생하는 문제에 대한 일종의 패턴을 만들어 해결하려 하는 것이라면, 아키텍처 디자인 패턴은 전체 시스템 구조, 구성 요소 간의 관계를 설계하는 방법이다. MVC가 가져가는 패턴은 모델-뷰-컨트롤러로 사용자에게 보이는 인터페이스와 비즈니스 로직, 데이터를 분리시키는 것이다. 입/출력과 로직이 혼재되어 있어 유지보수가 어려웠던 점을 관심사를 분리시켜 보다 나은 보수와 확장이 가능하게 도운다.

    1. 모델(Model) : 데이터를 관리한다.
    2. 뷰(View) : 사용자에게 보이는 부분을 관리한다.
    3. 컨트롤러(Controller) : 모델과 뷰로 명령을 전달한다. 모델과 뷰를 잇는다.

     생각해야 될 점은 각자의 역할을 분리했기에 하는 일이 혼재되면 안 된다는 것이다.

     컨트롤러는 모델과 뷰를 잇기에 의존성이 생긴다.
    모델의 경우 내부에 컨트롤러, 뷰에 관련된 코드가 있으면 안 된다. 
    뷰 또한 마찬가지이다. 뷰는 받은 데이터를 보여주는 화면이기에 모델을 출력할 때 필요한 코드는 있을 수 있으나, 컨트롤러의 코드는 있으면 안 된다. 모델로부터 데이터를 받을 때는 반드시 컨트롤러를 통해 받는다.

     

    2주 차의 폴더 구조가 담긴 이미지
    2주 차의 폴더 구조

     

      추가로 스타일 가이드를 강제시켜보고자 했다. 프리코스 커뮤니티에서 ESLint와 Prettier 세팅에 관한 공유가 있었고, 그를 통해 쉽게 적용할 수 있었다(감사합니다...). 

     

     마지막으로 기능 목록 작성을 전보다 시간을 들여 작성했다. 매 번 상수 확인을 위해 README.md 파일을 왔다 갔다 했었어서 상수도 미리 정의했다.

    # 자동차 경주
    
    ## 기능 목록
    
    - [x] 0에서 9까지의 수를 생성한다. `NumberGenerator#createRandomNumbers()`
    - [x] n대의 자동차가 있다. `Car`
      - [x] 자동차는 전진 또는 멈출 수 있다. => 상태를 가지고 있다. `level`, `win` 등...
        - [x] 전달받은 랜덤 수를 기준으로 변화한다. => `setter` 필요?!
        - [x] 0 ~ 3 : 멈춤
        - [x] 4 ~ : 전진 `moveForward`
      - [x] 자동차 이름은 쉼표(,) 를 기준으로 구분한다. + 쉼표를 parser하는 것도 분리할 지 고민해보자. => 상수 분리하면 한 번 더 확인해야하는 문제가 있는 것 같다(파일에 들어가 separator가 뭔지 확인 한 번 더함).
      - [x] 이름은 5자 이하만 가능하다. `InputValidator#checkCarName`
    - [x] 사용자는 시도 횟수(이동 횟수)를 입력한다. `InputProcessor#moveCountInput`
    - [x] 경주 게임 완료 후 우승자를 알려준다. `OutputProcessor#printWinner`
      - [x] 우승자는 한 명 이상일 수도 있다. 이 경우, 쉼표(,)를 이용하여 구분한다.
    - [x] 사용자가 잘못된 값을 입력한 경우 `throw`문을 사용해 `"[ERROR]"`로 시작하는 메시지를 가지는 예외를 발생시킨 후, 애플리케이션은 종료된다.
    
    ### 입출력 요구 사항
    
    - 사용자 입력
    
      - 자동차 이름 : `pobi,woni,jun` 같이 쉼표로 구분하여 입력
      - 시도 횟수 입력 : 숫자로 입력
    
    - 입력 상수 string 목록
    
      - 게임 시작 : `경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)`
      - 시도 횟수 입력 : `시도할 횟수는 몇 회인가요?`
    
    - 출력 상수 string 목록
      - 실행 결과 : `실행 결과`
      - 자동차 별 결과 : `pobi : -`
      - 우승자 결과(단독) : `최종 우승자 : pobi`
      - 우승자 결과(공동) : `최종 우승자 : pobi, jun`
      - 에러 메시지 : `[ERROR] 에러 케이스 1`

     

     

     보다 전체적인 그림을 그리게 되니 개발 전부터 신났었다. 혼자 새벽에 "찢었다..."라고 말했던 게 기억이 난다...(지금 보면 대체 뭘 찢었는지는 모르겠음(입고 있는 잠옷인듯)).

     

     그리고 크나큰 실수를 했다. 기능 목록 작성에는 시간을 들였으면서 정작 요구 사항 분석은 시간을 들이지 않은 것이다(?). 자동차는 전진 혹은 멈춤인데 전진 혹은 후진이라고 이해하여 잘못된 설계를 하게 되었다. 

    생각에 빠져 다른 가장 중요한 것을 놓치고 있어 반성을 많이 했다.

     

     

    잘못된 설계가 담긴 이미지
    왜 후진합니까?
    잘못된 로직이 담긴 이미지

     

     테스트 코드를 처음 작성해 본 주 차이기도 하다. Jest는 무엇이며 메서드들은 뭐 하는 것이며... 공식 문서를 참고하며 작성한 기능에 대해 다양한 메서드를 사용하며 재밌게 작성했던 것 같다. 이 때는 약간 자문자답?하는 느낌이어서 장점을 크게 느끼지 못한 주 차이기도 하다. 당연한 게 기능 개발을 모조리 완료한 다음에 테스트 코드를 작성했기에... 수많은 오류와 고침을 반복하여 완성된 코드를 후에 작은 단위로 테스트하려니...?

     

    +) 두 분께 코드 리뷰를 받을 수 있었다. 커뮤니티에서 코드 리뷰를 요청하는 데에 있어 생각보다 부담을 받아서(너무 많은 요청 게시글로 인해 묻힌다거나) 3주 차부터는 크게 신경 쓰지 않기로 했다.

     


    3주 차

     

     로또 미션이었다. 시작부터 잡담 채널에 사람들이 "악명 높은", "가장 어렵다던" 미션이라고 하셨었는데 실제로 3주 차 미션이 지금 생각해도 가장 어려웠던 것 같다. 복잡하고 꼬여있는 느낌.

     

    3주 차에 추가된 요구 사항이 담겨있는 이미지

     

    else 사용 지양에 관한 요구 사항이 추가되었다.

     

    3주 차는 클래스(객체)를 분리하는 연습도메인 로직에 대한 단위 테스트를 작성하는 연습을 했으면 한다고 전달되었다. 

     

     

    3주 차 목표

     

     

    2주 차 공통 피드백에서 다음과 같은 내용이 존재했다.

    • 기능 목록을 재검토한다.
      • 클래스 설계 구현, 함수(메서드) 설계와 구현같이 너무 상세하게 작성하지 않는다. 언제든지 변경될 수 있기 때문이다. 
      • 너무 세세한 부분까지 정리하기보다 구현해야 할 기능 목록을 정리하는 데 집중한다. 정상적인 경우도 중요하지만, 예외적인 상황도 기능 목록에 정리한다(이는 시작 단계에서 모두 찾기 힘들기에 기능 구현을 진행하며 추가해 나간다).
    • 기능 목록을 업데이트한다.
      • 기능 구현을 하며 변경될 수 있기에 모든 기능 목록을 완벽하게 정리하기보다는 기능을 구현하며 문서를 계속 업데이트한다.
      • 죽은 문서가 아니라 살아있는 문서를 만들기 위해 노력한다.

     

     2주 차 때 요구 사항을 잘 못 읽어 실수를 범했기에 공감이 많이 되었다. 요구 사항은 시간을 들여 분석하되 해당 분석 내용을 정리하는 데는 부담을 갖지 않도록 했다. 특히 네이밍에 서툴기에 2주 차 때 기능 목록 작성에서 진행했던 네이밍 설계도 하지 않기로 했다. 요구 사항 분석도 완벽하지 않으면서 네이밍을 미리 설계하는 건 위험해 보였다.

     

     

    3주 차는 2주 차 때 좋았던 점을 유지하고자 했다. 입/출력 상수에 대한 정리가 생각보다 편리하여 이를 유지하였다. 

     

    # 로또
    
    - **프로젝트 설명 :** 로또 게임을 구현한다. 프로젝트에는 [기능 목록](#기능-목록)에 작성된 기능들을 포함하며, 실행 결과는 다음과 같다.
    
    ```
    구입금액을 입력해 주세요.
    8000
    
    8개를 구매했습니다.
    [8, 21, 23, 41, 42, 43]
    [3, 5, 11, 16, 32, 38]
    [7, 11, 16, 35, 36, 44]
    [1, 8, 11, 31, 41, 42]
    [13, 14, 16, 38, 42, 45]
    [7, 11, 30, 40, 42, 43]
    [2, 13, 22, 32, 38, 45]
    [1, 3, 5, 14, 22, 45]
    
    당첨 번호를 입력해 주세요.
    1,2,3,4,5,6
    
    보너스 번호를 입력해 주세요.
    7
    
    당첨 통계
    ---
    3개 일치 (5,000원) - 1개
    4개 일치 (50,000원) - 0개
    5개 일치 (1,500,000원) - 0개
    5개 일치, 보너스 볼 일치 (30,000,000원) - 0개
    6개 일치 (2,000,000,000원) - 0개
    총 수익률은 62.5%입니다.
    ```
    
    ## 기능 목록
    
    - 로또
      - [x] 로또 1장의 가격은 1,000원이다.
      - [x] 숫자 범위는 1부터 45까지이다.
      - [x] 1개의 로또는 중복되지 않는 6개의 숫자로 이루어져 있다.
      - [x] 사용자가 입력한 당첨 번호(6개) + 보너스 번호(1개) 총 7개의 숫자로 당첨을 결정한다.
        - 당첨은 1등부터 5등까지 있다. 기준과 금액은 아래와 같다.
        - 1등 : 6개 번호 일치 / 2,000,000,000원
        - 2등: 5개 번호 + 보너스 번호 일치 / 30,000,000원
        - 3등: 5개 번호 일치 / 1,500,000원
        - 4등: 4개 번호 일치 / 50,000원
        - 5등: 3개 번호 일치 / 5,000원
    - [x] 사용자는 로또 구입 금액을 입력한다.
      - [x] 컴퓨터는 구입 금액에 해당하는 만큼 로또를 발행한다.
    - [x] 사용자는 당첨 번호와 보너스 번호를 입력한다.
    - [x] 당첨 번호와 로또 번호를 비교하여 당첨 내역 및 수익률을 출력한다.
    - [x] 사용자가 잘못된 값을 입력한 경우 throw문을 사용해 `"[ERROR]"`로 시작하는 에러 메시지를 출력한 후, 해당 부분부터 입력을 다시 받는다.
    
    ### 입출력 요구 사항
    
    - 사용자 입력
      - 구입 금액 : 1,000원 단위(로또 1장 가격)로 입력 받으며 나누어 떨어지지 않으면 예외 처리한다.
      - 당첨 번호 : 쉼표로 구분된 6개의 숫자
      - 보너스 번호 : 숫자 1개
    - 프로그램 출력
      - 사용자 입력 > 구입 금액 : 발행한 로또 수량 및 번호를 출력한다. 오름차순으로 정렬한다.
      - 사용자 입력 > 당첨 번호 > 보너스 번호 : 당첨 내역을 출력한다.
      - 최종 : 수익률을 줄력한다. 소수점 둘째 자리에서 반올림한다.
      - 예외 상황의 에러 문구를 출력한다.
    - 입력 상수 string 목록
      - 게임 시작 : `구입금액을 입력해 주세요.`
      - 당첨 번호 : `당첨 번호를 입력해 주세요.`
      - 보너스 번호 : `보너스 번호를 입력해 주세요.`
    - 출력 상수 string 목록
    
      - 로또 발행 수량 : `n개를 구매했습니다.`
      - 로또 번호 : `[8, 21, 23, 41, 42, 43]`
      - 당첨 통계
    
      ```
      당첨 통계
      ---
      3개 일치 (5,000원) - 1개
      4개 일치 (50,000원) - 0개
      5개 일치 (1,500,000원) - 0개
      5개 일치, 보너스 볼 일치 (30,000,000원) - 0개
      6개 일치 (2,000,000,000원) - 0개
      총 수익률은 62.5%입니다.
    
      ```

     

     

     

     또한 뷰를 통해 입/출력을 관리하는 것이 정말 좋았다. 이도 유지하였다.

     

     

    3주 차의 입출력 관리 관련 구조 이미지

     

     

     전 추자에서 헷갈렸던 부분은 컨트롤러다. 모델이나 뷰는 명확하단 느낌이 들었지만 컨트롤러는 모델과 뷰에 관련된 코드뿐 아니라 대다수의 로직을 다 때려 넣어 하는 일이 너무 많았다. 이를 LottoMaker라는 별도의 클래스로 로직만 관리하도록 했다.

     

     

    3주 차에서 헤맸던 부분은

    1. 주어진 class Lotto 사용하기

    2. 15줄 제한이 있다 보니 함수가 많이 생겼다. 네이밍이 어려웠다.

    => 계산은 calc~(), 리턴은 get~()등 나름의 규칙은 있었으나 모델을 받아 가공하여 뷰로 출력하는 부분은 대체 어떻게 네이밍을 해야 할지 감을 못 잡았다.

     

    App 클래스에 대한 이미지

     

    명확하지 않은 네이밍이 담겨있는 이미지
    명확하지 않은 네이밍

     

     입/출력에 대한 분리를 했으면서도 컨트롤러에서 입력을 관리했다. 개인적으로 많이 헤맸고 많이 만족스럽지 못한 코드가 완성되었다.

     

    또한 클래스에 setter, getter가 너무 많았다. 외부에서 마음껏 내부 데이터를 변경시키고 조회도 가능했다. 후에 피드백이나 다른 사람의 코드를 보며 많이 반성했다.

     

    불필요한 setter, getter이 존재하는 이미지
    😭

     

    그럼에도 좋았던 부분을 꼽아보자면 커스텀 에러를 만든 것이다. 매번 [ERROR]라는 prefix를 붙였어야 했는데 이 부분이 만약 바뀐다면? error 관련 string 상수들의 해당 부분을 다 고쳐야 할 것이다. 나는 이를 개선하고자 1주 차부터 아래 방식으로 에러 메시지를 관리하였다.

     

    export const ERROR_TEXT = Object.freeze({
      print(message) {
        return `[ERROR] ${message}`;
      },
      NOT_MATCH_LENGTH: '3개의 숫자가 아닙니다.',
      INVALID_NUMBER: '숫자가 잘못된 형식입니다.',
      DUPLICATE_NUMBER: '중복된 수가 있습니다.',
      IS_NEGATIVE_NUMBER: '음수는 입력할 수 없습니다.',
      CONTAIN_ZERO_NUMBER: '0은 포함될 수 없습니다.',
      NOT_MATCH_CHOICE: '1 또는 2만 입력해주세요.',
    });

     

    그러다 보니 호출은 길어졌다.

     

    에러 상수 관련 호출 이미지

     

     

    3주 차는 보다 편하게 호출하고 싶어 ValidationError를 만들었다.

    import { ERROR_PRIFIX } from '../constants/messages.js';
    
    class ValidationError extends Error {
      #prifix = ERROR_PRIFIX;
    
      constructor(message) {
        super(message);
        this.name = 'ValidationError';
      }
    
      toString() {
        return this.#prifix + this.message;
      }
    }
    
    export default ValidationError;

     

    개선된 호출 로직 이미지

     

    개인적으로 가독성은 개선하면서 관리가 편해져서 좋았다.

     

    3주 차는 2주 차에 이어서 테스트 코드에 익숙해지도록 노력했다. 유용함을 느끼기도 했다.

     

     

    3주 차의 회고록 이미지
    회고록

     

     4주 차에 목표가 추가되었다. 테스트 코드 먼저 작성하기!

     

     

    +) 신기하게도 코치와 수다 타임에서 TDD관련 내용이 나왔다. 그 외에도 좋았던 내용을 정리했다.

    TDD(🗣️ 준 코치)
     본질적인 이유는 "내 결정에 대한 피드백을 최대한 빠르게 받을 수 있게 하는 프로그래밍"입니다.
    불확실한 문제를 해결하기 위한 시간이 오래 걸릴 때 도움이 됩니다. 
    양치도 치과의사한테 1년 주기로 양치질에 대한 피드백을 받으며 고쳐나가고 있지는 않나요?
    TDD가 막연하더라도 스스로 빠르게 받는 피드백의 연습을 할 수 있습니다.

    치면 착색재를 도포한 후 그걸 양치질로 지워내는 연습을 하다 보면 현재의 내 양치 테크닉으로 닿지 않는 곳이 보입니다.
    TDD는 이와 비슷하게 더 자주, 더 빠른, 꾸준한 피드백이 오기에 성장하는 데 유용합니다.

     

    4주 차에 관하여
    많이 복잡할 수 있다. 어려울 수도 있다.

    지금까지의 미션을 수행하며 보통 무엇부터 구현했었나?
    - 기능 요구 사항 1번?
    - 객체?
    - 입력?

    한 단계 성장하려면 무엇부터 구현을 해야 하는 가에 대한 프로세스를 정립하라.
    역량이 뛰어난 사람은 핵심 기능의 정의를 진행한다. 그 후, 동작 가능한 가장 작은 버전부터 만든다. 

     그 핵심 기능이 동작하기 위한 가장 작은 버전은 무엇일까 고민하자. 그리고 그에 대한 피드백을 빠르게 받자(TDD).

    - 계산기 앱이라면?
     계산 기능(사칙 연산)이 핵심 기능이 될 수 있다.
    이의 가장 작은 버전은 숫자 두 개와 연산자를 입력받아 계산하는 것이다. 이를 구현 -> 테스트 코드 -> 피드백하자.

    제일 만만하고 쉬운 것부터 풀 수 있지만 결국 핵심에 시간을 많이 써야 한다. 핵심 기능을 먼저 구현하면 가장 어려운 것을 끝냈기에 탄력이 생긴다.

    4주 차의 요구 사항은 길다. 그를 여러 번 읽어 작게 쪼갠다. 
    맨 위에 핵심 기능을 한 줄로 적어보자.

     

    4주 차

     크리스마스 프로모션 미션이었다. 새로운 미션이었기에 비공개 저장소를 만드는 형식으로 진행되었다.

     

     프리코스의 좋은 점은 주차가 진행되며 공통 피드백이나 스켈레톤 코드를 제시하여 아주 헤매지는 않도록 한다는 것이다. 

    export default InputView = {
        async readDate() {
            const input = await Console.readLineAsync("12월 중 식당 예상 방문 날짜는 언제인가요? (숫자만 입력해 주세요!)");
            // ...
        }
        // ...
    }

     

    해당 스켈레톤 코드를 보며 InputView라 하여 Console창만 띄우는 것이 아닌 추가적인 입력 로직을 수행해도 되는 거구나 감을 잡았다.

     

    그리고 미리 겁 주신 대로(?) 요구 사항이 매우 길었다. 그렇지만 실제 기획 팀에게 메일을 받은 것처럼 진행되어 두근거렸다. 복잡하다 보다는 재밌겠다가 더 컸다.

    요구 사항을 잘못 읽는 경우를 줄이고 싶어 5번 이상은 정독한 것 같다. 그리고 정리 내용을 README.md에 정리를 더 잘하고자 했다.

     

    # 크리스마스 프로모션
    
    - 프로젝트 설명 : 우테코 식당의 크리스마스 프로모션을 개발한다. 프로젝트에는 [기능 목록](#기능-목록)에 작성된 기능들을 포함한다.
    - 핵심 기능 : 중복된 할인과 증정을 적용하여 결제 금액을 계산한다.
    
    ## 정보
    
    - 메뉴
    
    |    유형    |      메뉴명      |  가격  |
    | :--------: | :--------------: | :----: |
    | 에피타이저 |    시저샐러드    | 8,000  |
    |            |    양송이수프    | 6,000  |
    |            |      타파스      | 5,500  |
    |    메인    |   티본스테이크   | 55,000 |
    |            |     바비큐립     | 54,000 |
    |            |   해산물파스타   | 35,000 |
    |            | 크리스마스파스타 | 25,000 |
    |   디저트   |    초코케이크    | 15,000 |
    |            |    아이스크림    | 5,000  |
    |    음료    |     레드와인     | 60,000 |
    |            |      샴페인      | 25,000 |
    |            |     제로콜라     | 3,000  |
    
    - 이벤트 달력(2023년 12월)
    
    | 일(평일) | 월(평일) | 화(평일) | 수(평일) | 목(평일) | 금(주말) | 토(주말) |
    | :-: | :-: | :-: | :-: | :-: | :-: | :-: |
    |  |  |  |  |  | 1 디데이 할인 시작(1,000) 포함 모든 이벤트 시작 | 2 |
    | 3 🌟 | 4 | 5 | 6 | 7 | 8 | 9 |
    | 10 🌟 | 11 | 12 | 13 | 14 | 15 | 16 |
    | 17 🌟 | 18 | 19 | 20 | 21 | 22 | 23 |
    | 24 🌟 | 25 🌟 디데이 할인 자정 종료(3,400) | 26 | 27 | 28 | 29 | 30 |
    | 31 🌟 모든 이벤트 자정 종료 |  |  |  |  |  |  |
    
    - 할인 종류
      - 디데이 할인 : 2023.12.1 ~ 2023.12.25, 날마다 할인 금액에 100원씩 증가, 총주문 금액에서 해당 금액만큼 할인
      - 평일 할인(일-목) : 디저트 메뉴 1개당 2,023원 할인
      - 주말 할인(금,토) : 메인 메뉴 1개당 2,023원 할인
      - 특별 할인(🌟) : 총주문 금액에서 1,000원 할인
      - 증정 이벤트 : 할인 전 총주문 금액이 12만 원 이상일 때, 샴페인([음료] 25,000원) 1개 증정
    
    ## 기능 목록
    
    - [x] 결과를 출력한다.
      - [x] 주문 메뉴
      - [x] 할인 전 총주문 금액
      - [x] 증정 메뉴 (이벤트 해당 아닐 시, "없음")
      - [x] 혜택 내역 (하나도 없다면 "없음")
        - [x] 크리스마스 디데이
        - [x] 평일 할인
        - [x] 주말 할인
        - [x] 특별 할인
        - [x] 증정 이벤트
      - [x] 총혜택 금액
      - [x] 할인 후 예상 결제 금액 (할인 전 총주문 금액 - 할인 금액)
    - [x] 배지를 발급한다. (총혜택 금액 = 할인 금액의 합 + 증정 메뉴의 가격)(없다면 "없음")
      - [x] 총혜택 금액 5천 원 이상 : 별
      - [x] 총혜택 금액 1만 원 이상 : 트리
      - [x] 총혜택 금액 2만 원 이상 : 산타
    - [x] 예상 방문 날짜를 입력받는다.
    - [x] 메뉴와 메뉴 갯수를 입력받는다.
    - [x] 총주문 금액 10,000원 이상부터 이벤트가 적용된다.
    
    ### 예외 사항
    
    - [x] [날짜] 1 이상 31 이하 숫자만 입력 가능하다.
      - `"[ERROR] 유효하지 않은 날짜입니다. 다시 입력해 주세요."`
    - [x] [주문] 메뉴판에 있는 메뉴만 입력 가능하다.
    - [x] [주문] 메뉴 형식은 메뉴명-갯수,메뉴명-갯수... 이다.
    - [x] [주문] 메뉴의 개수는 1 이상의 숫자만 가능하다.
    - [x] [주문] 중복 메뉴는 입력 불가하다.
    - [x] [주문] 음료만 주문 시, 주문할 수 없다.
    - [x] [주문] 메뉴는 한 번에 최대 20개까지만 주문할 수 있다. (개수별 카운트)
      - `"[ERROR] 유효하지 않은 주문입니다. 다시 입력해 주세요."`
    
    ### 입출력 상수
    
    - 입력
      - `12월 중 식당 예상 방문 날짜는 언제인가요? (숫자만 입력해 주세요!)`
      - `주문하실 메뉴를 메뉴와 개수를 알려 주세요. (e.g. 해산물파스타-2,레드와인-1,초코케이크-1)`
    - 출력
    
      - `안녕하세요! 우테코 식당 12월 이벤트 플래너입니다.`
      - `12월 ${날짜}일에 우테코 식당에서 받을 이벤트 혜택 미리 보기!`
      - `<주문 메뉴>`
      - `${메뉴명} ${개수}개`
      - `<할인 전 총주문 금액>`
      - `${가격(천 단위 ,)}원`
      - `<증정 메뉴>`
      - (중복) `${샴페인} ${1}개` (현재 고정)
      - `<혜택 내역>`
      - `${이벤트 명} : -${금액(천 단위 ,)}원`
      - `없음`
      - `<총혜택 금액>`
      - `-${가격(천 단위 ,)}원`
      - `<할인 후 예상 결제 금액>`
      - `<12월 이벤트 배지>`
      - `산타` , `트리` , `별`

     

     

    미리 메뉴와 이벤트 특성을 깔끔하게 정리하려고 노력했다.

     

    4주 차의 목표 이미지 1
    4주 차의 목표 이미지 2

     

     목표는 위와 같다. TDD와 핵심 기능 파악하기, getter 줄여보기로 줄일 수 있겠다.

    getter를 줄여야 하는 이유에 대해 getter 쓰지 말라고만 하고 가버리면 어떡해요 자료와 getter를 사용하는 대신 객체에 메시지를 보내자 자료가 도움이 많이 되었다. 요점은 객체 외부에서 객체 내부의 값을 가져와 변경시키기보다 해당 객체에서 관련 로직을 수행하도록 하는 것이다. 메시지는 행위와 같다. 객체 스스로의 행동에 의해서만 상태를 변경시키자. 객체는 자율적이다. 특히 테스트 케이스 작성을 위해서만 존재하는 getter는 없애는 것이 좋다. '객체의 행위는 객체에서'를 리마인드 했다. 결과적으로 이번 과제에서는 불필요한 setter, getter가 없어졌다.

     

     추가로 언급했던 TDD를 도입해 보았다.

     

     

    테스트 코드 작성을 먼저 진행한 이미지

     

     

      요구 사항을 우선순위 별로, 작게 쪼개니 테스트 코드 작성도 쉬워졌다. 

    내가 뽑은 4주 차의 핵심 기능은 "중복된 할인과 증정을 적용하여 결제 금액을 계산한다."였기에 각 할인과 증정 이벤트를 분리하여 개발하였다.

     하나의 이벤트 혜택의 결과를 예상하고 코드 작성 후, 바로 테스트 결과로 피드백을 받았다. 이 단계에서도 오류가 많이 났었는데 만약 지난 주차들처럼 8~90% 완성된 상태에서 고치려고 했다면 리팩토링이나 설계를 수정하는 등 시간이 많이 소요됐을 것 같다.

     

    느꼈던 다른 장점

    • 자연스러운 분리가 된다. 테스트하기 좋은 코드를 위해 외부와의 의존을 최대한 줄이기 때문이다.
    • 가장 복잡해 보였음에도 다른 주차들보다 매끄럽게 진행되었었는데, 아마 테스트 코드를 미리 작성한 것이 큰 도움이 된 것 같다. 핵심 기능부터 작은 단위가 잘 돌아가게 만드니 나중에는 조립만 하면 프로그램이 돌아갔다.

     

    최종 제출 과제 링크

     


    1주 차 ➡️ 4주 차

     처음에는 한 달이라는 시간 동안 내가 가능할까? 란 막막함도 들었는데 한 달이 지난 이 시점은 안 했으면 평생 후회했을 것 같다.

     무엇보다 다른 사람들을 보며 자극을 많이 받았다. 커뮤니티는 정말 대단하다. 처음에는 비슷한 것을 고민하다가도, 주 차가 진행되며 관심 있어하시는 것들이 넓게 펴져가는 게 느껴졌다. 그 과정에서 각자의 집을 짓고, 비슷한 고민을 하는 분들끼리 마을을 짓고... 그 모습을 보며 정말 정답은 없구나 느꼈다. 다들 좋은 고민을 하시는 것 같았다. 그리고 알게 된 것들을 적극적으로 공유해 주셔서 좋았다.

     

     함수 15라인 제한도 한 줄 차이로 분리를 해야 했거나 얼렁뚱땅 분리하기도 했었는데 확실히 4주 차에 들어서며 자연스러워졌다. 한 가지 일만 시키면 크게 길어질 일은 없다.

     

     테스트 케이스도 점점 익숙해지는 모습이 보여서 좋았다. 

     

    2주 차의 테스트 케이스 결과 이미지
    2주 차
    4주 차의 테스트 결과 이미지
    4주 차

     

     

    앞서 프리코스에 참여하게 된 이유는 총 세 가지라고 밝혔는데, 프리코스가 끝난 지금은 어떤 것들을 이뤘을까?

     

    1. 한 달간 다양한 미션을 설계하고 구현하며 자신감을 얻었다. 개발은 재미있다.

    2. 테스트 코드에 대해 더 공부하고 싶어졌다.

    3. 공통 피드백, 다른 분들의 코드, 코드 리뷰를 통해 좋은 고민을 더 하고 싶어졌다. 

     

    혹시라도 지인이 참여를 망설이고 있다면 100% 추천해주고 싶다. 놀랄 만큼 재밌고 두근대는 과정이었다.

     

     

    참고자료

    https://developer.mozilla.org/ko/docs/Glossary/MVC

    댓글

🍀 Y0ungZ dev blog.
스크롤 버튼