[우테코 프리코스] 3주차 미션 회고 (java)

3Beom's 개발 블로그·2022년 11월 27일

본 글은 우아한 테크코스 3주 차 미션을 수행하면서 기록했던 회고록이다.


지난 2주차 미션에 이어 3주차 미션도 정말 열심히 했다 ㅎ
-> 2주차 미션 회고

3주차 미션


-> 우아한 테크코스 프리코스 3주차 미션 java-lotto
-> 필자가 제출한 코드

3주차 미션에서 주어진 목표는 다음과 같았다.

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

목표의 1번을 처음 보고 클래스를 잘 분리할 수 있는 방법이 있지 않을까 생각하며 구글링을 해보았고, 다들 하나같이 MVC 패턴에 대해 언급하고 있었다.

예전에 멋사 활동할 때 Django에서 쓰이던 MVT 패턴이 떠올랐고, MVC 패턴의 개념을 이해하는 것은 크게 어렵지 않았다. 하지만 직접 적용해 보는 것은 또 다른 얘기기 때문에 3주 차 미션에서 써보고자 했다.
-> 새로 배운 내용 - MVC 패턴

그리고 새로 주어진 목표를 이루는 것도 중요하지만, 전 주차에서 좋았던 점은 유지하고, 부족했던 점은 개선하는 것 또한 매우매우매우매우 중요하다고 생각했다.

그래서 2주 차 미션을 수행하며 작성했던 회고록과, 우테코 코치님이 공지해 주신 공통 피드백 내용을 같이 보면서 유지 및 개선 점을 정리했다.

이렇게 이것저것 정리하면서 '나만의 미션'을 만들게 되었고, 본 회고록은 이를 최대한 실천해 보고자 나름(?) 노력했던 기록이다.

나만의 미션


나만의 미션은 '전 주차의 유지 및 개선할 내용' 과 '본 주차에서 새로 시도할 내용' 두 가지로 구성했다.

2주 차 유지 및 개선

  • TDD 방식 유지하기
  • 커밋 메세지의 scope를 패키지 단위로 채우기
    • controller, model, view, exception, constants 등
  • 최대한 작은 단위로 테스트 코드 작성하기

3주 차의 새로운 시도

  • 함수가 한 가지 기능을 하는지 확인하는 기준에 대해 고민해보기
  • 기능 범주에 맞추어 클래스 분리에 대해 고민해보기
  • MVC 패턴 적용해보고 장단점에 대해 생각해보기
    • MVC 규칙 잘 지키기

이 외에도 '값을 하드 코딩하지 않는다.', '변수 이름에 자료형은 쓰지 않는다.', '기능 목록을 업데이트 한다.' 등등... 2주 차 미션 공통 피드백 내용은 최대한 다 지키려 했다.

그리고 개발하면서 중간에 새롭게 시도해 본 내용들도 있었는데, 이는 다음과 같다.

개발 중 추가한 미션

  • 인터페이스 및 익명 클래스를 통해 반복 코드 줄이기
  • 기능 목록 양식 만들기
  • 기능 목록 작성 방식 재고

개발 회고


1. 2주 차 유지 및 개선

<TDD 방식 유지>

  • 2주 차에 처음으로 시도해 보았던 TDD 방식을 3주 차에도 유지하면서 구현해 보았다.
  • red - green - refactor 과정을 다시 한번 시도해 보았고, 이 과정에 좀 더 익숙해질 수 있었다.
  • 비록 규모가 있는 실제 개발 프로젝트와는 차이가 있겠지만, 이렇게 계속 시도해보며 과정 자체에 익숙해지면, 후에 분명 다른 이로운 효과도 얻을 수 있을 것이라 기대한다.

<커밋 메세지 scope>

  • 2주 차 미션에서 다짐(?)한대로 커밋 메세지의 scope 부분에 클래스 이름이 아닌, 좀 더 큰 범주로 설정하였다.
  • 본 3주차 미션에서 시도한 MVC 패턴에 맞추어 model, view, controller, exception 단위로 설정하였다.
  • 확실히 중복되는 내용이 사라지고, 길이도 줄어들었다.
  • 또한, 깃 사이트의 커밋 내역에서 어떤 파일의 어떤 로직이 수정되었는지 확인할 수 있기 때문에, 개발자의 의도를 드러내기에 충분하겠다고 생각되었다.
  • 이후에도 scope 내용을 본 방식과 동일하게 적용할 예정이다.

<최대한 작은 단위의 테스트>

  • 2주 차 미션에서는 '기능' 단위로 테스트를 수행했었다.
    • Ex) 1볼부터 3스트라이크까지 반환 가능한 모든 경우들을 하나의 테스트 메소드에 모아두고 테스트를 수행했었다.
  • 3주차 미션에서는 하나의 기능도 여러가지 경우들에 대해 세세하게 나누어 테스트 메소드를 작성하였다.
  • 본 방식대로 개발을 진행해 보니, 확실히 어느 기능에서 문제가 발생하는지 바로바로 피드백을 받을 수 있었다.
    • 2주차 미션 때는 큰 기능 단위로 테스트를 실패하니, 어느 구간에서 문제가 발생했는지 추가로 알아볼 필요가 있었는데, 좀 더 작은 단위로 나누어 테스트를 수행해보니 문제 구간을 바로 파악할 수 있었다.

2. 3주 차의 새로운 시도

<메소드 한가지 기능 기준>

  • 리팩토링 중 메소드 분리 과정에서 메소드가 한가지 기능을 수행하고 있는지에 대한 기준에 대해 생각해 보았다.

  • 기준을 설정하기 전, 먼저 메소드를 분리하는 과정에 대해 생각해 보았고, 그 과정 속에서 메소드가 한 가지 기능을 하는지에 대한 기준을 설정하였다.

  • 과정은 다음과 같다.

    (1) 우선 메소드를 **간략하게 하나의 문장으로 작성**한다.
    - 로또 각각을 당첨 로또와 비교하며 통계에 반영한다.
    
    (2) 문장을 **좀 더 디테일하게 수정**한다.
    - 로또 각각에 대해 로또 번호와 당첨 번호를 비교한다. 일치하는 숫자의 개수를 센 후, 일치하는 숫자의 개수에 따라 당첨 통계에 반영한다.
    
    (3) 문장을 **동작 단위**로 나눈다.
    - 로또 각각에 대해 로또 번호와 당첨 번호를 비교한다. 
      - 일치하는 숫자의 개수를 센다.
      - 일치하는 숫자의 개수에 따라 당첨 통계에 반영한다.
    
    (3) **동작 단위에서 좀 더 세세하게** 나눌 수 있는지 고민한다.
    - 로또 각각에 대해 로또 번호와 당첨 번호를 비교한다.
      - 일치하는 숫자의 개수를 센다.
      - 일치하는 숫자의 개수에 따라 당첨 통계에 반영한다.
        - 해당하는 순위의 통계 값을 1씩 증가시킨다.
          - 일치하는 숫자가 5개일 경우 보너스 번호가 일치하는지 여부를 고려한다.
    
    (4) 이렇게 나누었을 때, **두 문장이 붙어있진 않는지** 확인한다.
    
    (5) 메소드 1차 완성
    - 로또 각각에 대해 로또 번호와 당첨 번호를 비교한다. : compareOneLotto()
      - 일치하는 숫자의 개수를 센다. : countMatchingNumbers()
      - 일치하는 숫자의 개수에 따라 당첨 통계에 반영한다. : reflectStatistics()
        - 해당하는 순위의 통계 값을 1씩 증가시킨다. : increaseCount()
          - 일치하는 숫자가 5개일 경우 보너스 번호가 일치하는지 여부를 고려한다. : checkBonusNumber()
    
    (6) 메소드 **개발 중 한 메소드 내에 여러가지 기능을 포함하게 될 수 있다.** 이를 또 분리한다.
    - countMatchingNumbers() : retainAll() 메소드를 활용하여 일치하는 번호만 남기는 과정이 포함된다.
      => 해당 기능 leaveOnlyDuplicated()  분리
  • 본 과정을 통해 수행되는데, 여기서 중요한 기준은 다음과 같다.

    • 메소드를 문장으로 표현했을 때, 두 문장이 이어진 형태는 아닌지 확인한다.
      • Ex) 중복되는 번호만 남겨서개수를 세어 일치하는 숫자의 개수를 구한다.
    • indent 깊이가 너무 깊어지거나, 코드 길이가 너무 길어질 경우, 문장을 더 세세하게 나누지 않았을 확률이 높다.

<클래스 분리>

  • 먼저 MVC 패턴에 맞추어 클래스를 분리하였다.
    • Model
      • Lotto : 로또 번호 저장 모델
      • WinningLotto : 당첨 로또 저장 모델
    • View
      • LottoGuide : 사용자 입출력 담당
    • Controller
      • LottoGame : Model과 View 및 기능 클래스들을 연결하는 중간 Controller 역할
      • LottoAnalyst : 로또 분석 기능
      • LottoIssuer : 로또 발행 기능
  • 2주 차 미션에서 더 발전시켜 하나의 클래스 내에서 여러 범주의 기능들이 활용되지 않도록, 기능 범주에 맞추어 클래스를 분리하였다.
    • 로또 발행 기능 클래스 : LottoIssuer
    • 로또 분석 기능 클래스 (당첨 통계 및 수익률) : LottoAnalyst
    • 로또 게임 운영 클래스 : LottoGame
  • 클래스를 분리해 보니, 확실히 각자 고유의 기능만을 담고 있을 수 있었다.
  • 가독성에도 더 도움이 되었고, 로직이 깔끔하게 정리된 느낌이 들었다.
  • 이를 통해 유지 보수에도 더 도움이 될 것이라 생각되었고, 테스트도 보다 더 정리되어 기능 정상 동작 여부에 대해 좀 더 효율적으로 피드백 받을 수 있어 매우 좋았다.
  • 2주 차 미션에서는 메소드들이 각자의 역할을 수행하는 공장 역할을 하는 느낌이었는데, 3주 차 미션에서 클래스를 분리해보니, 공장이었던 메소드들이 클래스로 묶이면서 하나의 기업이 된 듯한 느낌이었다.

<MVC 패턴>

  • 확실히 MVC 패턴에 맞추어 개발을 진행해 보니, 백엔드 개발 방식에 매우 적합한 패턴이라는 생각이 들었다.
  • ModelDB와의 소통을 담당하기 위한 기본적인 로직을 갖추고, View가 사용자와 직접 맞닿아있는 프론트엔드 역할을, Controller가 DB와 프론트엔드 사이에서 로직을 수행하는 서버 역할을 수행하도록 구현하니, 각자의 뚜렷한 역할이 그려져 유지보수에 상당히 도움이 될 수 있을 것이라는 생각이 들었다.

3. 개발 중 추가한 새로운 시도

<인터페이스 및 익명 클래스를 통해 반복 코드 줄이기>

-> [Java] 인터페이스+익명 클래스 활용 반복 코드 줄이기

  • 2주 차 미션 당시, 테스트 코드에서 중복되는 코드가 너무 많아 상당히 거슬렸지만 방법을 찾아 적용하기에는 시간이 부족해 아쉬움으로 남았었다.
  • 3주 차 역시 테스트 코드에서 중복되는 코드가 다수 발생하였고, "객체 지향"적으로 중복을 줄일 수 없을까 고민해 보았다.
  • 테스트 코드의 중복되는 코드가 변수가 아닌 메소드가 달라지다 보니, 단순히 메소드 분리로는 어려움이 예상되었다.
    • 메소드 파라미터로 메소드 자체를 전달해야 하기 때문이다.
      (Method 객체를 활용하는 방법도 알아보았지만, 보다 객체 지향적인 방법을 적용해 보고 싶었다.)
  • 그러던 중, 인터페이스와 익명 클래스를 활용하는 방법에 대해 확인할 수 있었다.
  • 덕분에 `LottoExceptionHandler 라는 인터페이스를 생성하고, 각 예외처리 기능 테스트마다 익명 클래스 객체를 생성하여 메소드를 파라미터로 전달하듯이 구현해 낼 수 있었다.
  • 지금까지 인터페이스, 추상 클래스에 대해 개념만 모호하게 알고 있었는데 이번 기회에 직접 활용해 보니 진짜 대박이었다.
  • 이후 반복되는 코드에 대해 고려할 수 있는 방안이 더 늘어났다!

<기능 목록 양식 만들기>

  • 1, 2주 차 미션을 수행하며 기능 목록을 즉흥적으로 작성했었는데, 그러다 보니 놓치는 내용이 있었다.
  • 따라서 이번 3주 차 미션을 통해 '나만의 기능 목록 양식'을 만들고, 직접 활용해 보며 부족한 내용을 채워보았다.
  • 이를 통해 '기능 목록 양식'을 만들어 낼 수 있었다.
    • 기능 전체 목록, 예외 사항 전체 목록 : 분류되지 않은 상태로 기능과 예외 사항 전체를 줄줄이 작성한다.
    • 기능 정리, 예외 사항 정리 : 유사한 범주의 내용들을 묶어 기능과 예외 사항들을 정리하고, 이를 토대로 클래스 분리를 수행한다.
    • 개발 순서 : 작성된 기능 정리, 예외 사항 정리 내용을 토대로 개발 순서를 결정한다.
    • 요구 사항 체크 리스트 : 주어진 요구사항에 대해 체크박스 형태로 정리해둔다. 꼭 개발 전에 작성하며 고려해야 한다. 개발 후 하나씩 체크하며 요구 사항을 만족했는지 확인한다.

<기능 목록 작성 방식>

  • 원래는 2주 차 미션에서 작성하던 방식을 좀 더 심화시켜 진행하려 했지만, 공통 피드백 내용과 맞지 않는 부분이 있었다.
    • 기능 목록을 재검토 한다.
    • 기능 목록을 업데이트 한다.
      • 죽은 문서가 아니라 살아있는 문서를 만들기 위해 노력한다.
  • 따라서 기능 목록 초안을 지나칠 정도로 자세하게 작성하지 않았고, 살아있는 문서로 만들기 위해 노력하였다.
  • 역시나 개발하면서 기능 목록의 많은 내용들이 수정되었고, 피드백에 따르길 잘 했다는 생각이 들었다.
  • 또한, 기능 목록 내용에 너무 의존하지 않게 되었다.
    • 기능 목록을 처음부터 너무 세세하게 작성하니, 기능 목록의 내용만을 너무 신뢰하여 의존하게 되는 경향이 있었다. 그러다 보니 생각이 기능 목록 안에 갇혀있게 되는 느낌을 받았다.
    • 하지만 기능 목록을 계속 수정하면서 '살아있는' 형태로 두니 생각이 뻗칠 수 있는 범위가 더 넓어지는 느낌을 받았다.
  • 이후에도 기능 목록을 처음부터 너무 상세히 작성하기 보다, 필요한 기능만을 자세히 작성하여 개발 방안에 대해서는 열어둔 상태로 유지하는 것이 좋을 것 같다.

profile
경험과 기록으로 성장하기

0개의 댓글