우테코 5기 프리코스 3주차 회고

우테코 프리코스 3주차 회고

이번 3주 차는 정말 너무나도 바쁜 주였다.. 졸작 마무리에, 학교 과제 폭탄에 예비군까지.. 너무 바빠서 나도 모르게 기절하는 시간이 많았다ㅠㅠ 그 와중에 프리코스 2주 차를 하고 나서 아직도 배울 것이 많고 피드백할 것도 많다고 느꼈기 때문에 더 미친 듯이 살았던 것 같다.

🧑‍🤝‍🧑 피어 리뷰 스터디

2주 차 숫자야구에서 코드를 작성하고 제출할 때까지만 해도 그전 피드백(리뷰, 공통 등)을 바탕으로 많은 개선도 하고 성장도 했기 때문에 상당히 자신감에 차있었던 것 같고 이번 피어 리뷰 스터디를 하기 전까지 설렘에 가득 차 있었다.

하지만 역시 성장을 한 것은 나뿐 아니라 다른 사람도 마찬가지였다. 스터디 팀원의 코드들을 리뷰하는데 정말 깔끔했고 배울 점이 여전히 많았다. 이전 주 차의 피드백을 모조리 흡수해서 더 강해진 팀원들이였다.

주마다 팀원들의 코드 퀄리티가 바뀌는데 그 노력이 코드들과 커밋에서 잘 보였다. 팀원들의 코드를 리뷰하면서 자동으로 2주 차에서 내가 뭐가 부족했었는지 3주 차에는 뭘 더 공부해 적용해 보면 좋을지 큰 틀이 바로 잡혔다.

  • private static final 상수 -> Enum으로 분리
  • 변수명, 메소드명 좀 더 생각해보기
  • Converter 분리, View 분리 등 역활 분리하기
  • MVC 적용

리뷰를 하기 전 자신감은 다시 열정으로 바뀌었고 이것을 바탕으로 더 열심히 해 팀원끼리 서로 시너지를 내려고 한다. 2주 차 피어 리뷰 스터디를 하고 느낀 건데 이 스터디가 없었다면 여기까지 이렇게 빠르게 피드백을 할 수 있었을까 하고 생각이 든다. 다시 한번 함께 자라기의 중요성을 느꼈던 시간이었다.

2주차 공통 피드백

이번 공통 피드백에는 글로 된 피드백뿐 아니라 숫자 야구에 대한 피드백 강의도 있었다. 저번에는 깃 관련 영상도 있었는데 이런 귀한 영상들까지 주시다니.. 하나도 안 남기고 싹 긁어먹겠습니다^^7 직접 멘토님의 라이브 코딩을 볼 수 있다니 너무나 좋은 기회였다.

멘토님께서는 하신 과정은 아래와 같다.

  1. 요구사항 바탕으로 기능 작성
  2. 뼈대(skeleton code) 생성
  3. 구체적 코드 작성
    • 도메인 코드를 작성하면서
    • 그에 맞는 흐름제어 코드도 동시에 작성

+추가적으로 기능마다 커밋까지

하시는 걸 보면 추출한 요구 기능에 따라서 정말 스무스하게 완성하시는 걸 볼 수 있다. 지금까지 커밋을 하면서 어떤 기준에 맞춰야 되나 고민을 많이 했다. 기준을 엄격하고 딱딱하게 정해서 할려 했기 때문에 스트레스를 많이 받고 고치는 일도 잦았는데 이 영상을 보고 좀 더 유연성 있게 넣을 수 있도록 바뀌었고 편하게 생각하게 되었다.

그리고 처음에 작성한 기능 목록 같은 경우도 후에 웬만하면 건드리지 않도록 하고 있었는데 이것도 편하게 고치도록 바뀌게 되었다. 공통 피드백에서도 기능 목록은 계속해서 변할 수 있기 때문에 처음에 완벽한 문서를 정리하기보다는 기능을 구현하면서 문서를 계속 업데이트하라고 하신다.

죽은 문서가 아니라 살아있는 문서를 만들기 위해 노력한다.

static, final

영상을 보면서 난 아직 사용 언어에 대해서도 잘 활용하지 못한다는 걸 깨달았다. 둘을 이용해서 선언하면 뭔가 변하지 않는다는 건 어렴풋이 알고 있었는데 둘의 차이를 정확하게 설명해 보라 하면 하지 못했을 것 같다.

private static final, private final 선언된 변수를 사용하면 둘 다 재할당하지 못하는 건 같다. 하지만 private static final 같은 경우는 아래와 같이 선언과 동시에 초기화해주어야 하고, private final 같은 경우는 객체 생성 시에도 초기화가 가능하다.

private static final String test = "test";
private final int number;

public 생성자(int number){
  this.number = number;
}

그리고 private static final 같은 경우 메모리에 한 번 올라가면 같은 값을 클래스 내부 전체 필드, 메서드에서 공유하고, private final은 새로운 객체가 생성될 때마다 새로운 값이 할당된다.

그래서 왜 사용할까?

static 같은 경우 잘 사용하면 메모리 측면에서도 효율적이고 속도 측면에서도 빠를 수 있다. 매번 인스턴스를 생성할 필요도 없고 객체를 생성하지 않고도 사용 가능하기 때문이다. 그리고 final 같은 경우 readOnly로 만드는 특성 때문에 얘가 불변한다는 것을 보장해 주기 때문에 나중에 유지 보수 관점에서도 추적하기 쉽다.

하지만 그만큼 조심해서 사용해야 한다. static 같은 경우 프로그램이 종료할 때까지 메모리에 할당된 채로 존재하기 때문에 많이 사용하면 성능에 좋지 않다. 그리고 static은 객체를 생성하지 않고 여러 곳에서 데이터를 사용할 수 있기 때문에 객체 지향 프로그래밍 원칙(캡슐화)을 위반한다.

그렇기 때문에 막 사용하는 게 아니고 적절한 곳에 사용하면 된다

테스트 코드의 중요성

테스트 코드를 작성하는 것이 귀찮게 느껴진 적이 누구라도 있을 것이다. 나도 기능을 다 구현하고 나서 지친 상태에서 테스트 코드를 생략하고 바로 그다음 기능을 작성했던 적이 있다. 하지만 다음 기능을 구현할 때도 이전에 구현했던 것이 제대로 돌아갈까 불안한 마음으로 다음 기능을 작성했던 기억이 난다.

그 기능을 완벽하게 짰다고 자신할 수 있으면 모를까 하지만 어떤 사람도 실수를 안 할 수는 없을 것이다. 그렇기 때문에 그 실수한 상태를 모르고 넘어가서 추가 기능을 구현하다 치명적인 에러가 발생하면 앞으로 되돌리는데 더 오랜 시간을 쏟게 될 것이다.

나도 프로젝트에서 진행하던 중 시간이 촉박해 빠르게 기능부터 연달아 구현했던 적이 있는데 오히려 어느 부분에 막혔을 때 어디부터 잘못된 건지 계속 되돌려 찾아봐야 하는 딜레마 상태에 빠졌던 적이 있다. 오히려 그 테스트 코드 작성 안 한 시간 보다 시간을 배로 날려먹는 낭비를 겪었다. 그렇기 때문에 전부터 테스트 코드가 중요하다는것은 머리로 알고 있었는데 이번에 제대로 적용해보면서 더욱 중요성을 체감해 볼 수 있었다.

이번에 도메인마다 단위 테스트를 진행하면서 꼼꼼히 한 결과, 테스트 코드를 작성하기 위해 약간의 시간이 추가 되었지만 나중에 통합적으로 테스트를 했을 때 오히려 깔끔하게 한 번에 통과가 되어 깜짝 놀랐다. 이처럼 테스트 코드로 각각의 기능 확인에 대한 빠른 피드백으로 신뢰성을 증가시키며 개발을 이어나갈 수 있는 것 같다.

그리고 매번 직접 수동으로 번거롭게 돌릴 필요 없이 컴퓨터가 자동으로 test 할 수 있도록 중요 자산이 남는 것이다. 이 자산은 후에도 유지 보수 혹은 인수인계 할 때도 유용하게 쓰일 수 있기 때문에 앞으로도 테스트 코드는 꼭 작성하도록 하자.

MVC 도입

지난주 목표로 책임을 좀 더 나누어 결합성을 낮추는 방법으로 MVC 패턴을 도입했다. 하지만 막상 처음부터 MVC 패턴으로 코드를 구현하려고 하니 막막했고 뭐부터 시작해야 될지 감이 안 잡혔다.

그래서 고민하던 도중 프로그램 설계 도식화에 관한 글에 대해 읽게 되었는데 어떤 일을 하기 전에 전체적인 계획 및 도식화하는 작업을 하여 명확화 하는 것이다. 그렇게 지금 하려는 로또 프로그램에 대한 흐름도를 먼저 작성하여 적용해 보게 되었고 흐름도를 작성하고 나니깐 어떤 걸 먼저 작성할지 어떻게 나누어야 할지 조금씩 감이 잡히게 되어 코드를 작성하기 시작했다.

또한 한참 구현하던 중간에 검증은 view, controller, domain 어디서 하는 게 좋은지 비즈니스 로직은 domain이 좋은지 service 가 좋은지 등 머릿속에 고민이 많아지니깐 이 mvc의 기본적인 개념들에 대해 점점 헷갈리기 시작했는데 다음과 같은 글들을 통해 많이 도움을 받았다. 검색하다 보면 우테코의 10분 톡이 자주 나오는데 정말 알기 쉽게 설명들을 잘해주셔서 강추한다.

3주 차 진행 과정

  1. 구현 기능 작성
  2. 흐름도 작성
  3. 코드 구현
  4. 리팩토링 및 제출

1. 구현 기능 작성


구현 기능 목록

🚀 구현할 기능 목록


  • 로또 발행
    • 로또 번호 범위는 1 ~ 45까지이다.
      • 1보다 작거나 45보다 크면 예외 처리
    • 중복되지 않는 6개의 숫자를 뽑는다.
      • 중복 되면 예외 처리
      • 6개가 아니면 예외 처리
    • 구입 금액만큼 해당하는 로또 발행
    • 로또 1장의 가격은 1000원
  • 당첨 번호와 로또 발행 번호 비교
    • 6개 번호 일치 -> 1등(2,000,000,000)
    • 5개 번호 + 보너스 번호 일치 -> 2등(30,000,000)
    • 5개 번호 일치 -> 3등(1,500,000)
    • 4개 번호 일치 -> 4등(50,000)
    • 3개 번호 일치 -> 5등(5,000)

입력

  • 로또 구입금액 입력
    • 1000원 단위로 입력
    • 입력이 3자리 수 이하이면 예외 처리
    • 숫자가 아닐 시 예외 처리
    • 1000원으로 나누어 떨어지지 않는 경우 예외 처리
  • 당첨 번호 입력
    • 쉼표를 기준으로 6개의 당첨 번호 입력
    • 6개가 아니면 예외 처리(콤마(,)로 나눴을 때)
    • 1 ~ 45 범위 숫자가 아닐 시 예외 처리
    • 중복 일시 예외 처리
  • 보너스 번호 입력
    • 1 ~ 45 범위 숫자가 아닐 시 예외 처리
    • 당첨번호와 중복 일시 예외 처리

출력

  • 발행한 로또 수량 및 번호 출력
    • 로또 번호는 오름차순으로 출력
  • 당첨 내역 출력
  • 수익률 출력
    • 수익률은 소수점 둘째 자리에서 반올림한다.

예외처리

  • 사용자가 잘못된 값 입력한 경우
    • IllegalArgumentException 발생시키고
    • 에러메시지 출력 후 종료


2. 흐름도 작성

위에 작성했기 때문에 생략

3. 코드 구현

Enum 사용

1주 차의 매직넘버에서 2주 차의 private static final을 거쳐 드디어 Enum을 사용해 보게 됐는데 확실히 Enum을 통해 구현함으로 코드가 더 단순해지고 가독성이 좋아졌다. 그리고 아래처럼 다양하게 활용할 수 있어 편리한 것 같다.

public enum LottoStatus {
    START(1),
    END(45),
    SIZE(6),
    LIMIT(1),
    PRICE(1000),
    PERCENT(100),
    DELIMITER_LENGTH(3);

    private final int value;

    LottoStatus(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }

    ...
}
public enum NoticeMessage {
    PURCHASING_AMOUNT{
        @Override
        public String toString() {
            return "구입금액을 입력해 주세요.";
        }
    },
    LOTTO_COUNT{
        @Override
        public String toString() {
            return "개를 구매했습니다.";
        }
    },
    LUCKY_NUMBER{
        @Override
        public String toString() {
            return "당첨 번호를 입력해 주세요.";
        }
    }

    ...
}

https://github.com/woowacourse-precourse/java-lotto/pull/528

thumbnail

4. 리팩토링 및 제출

이번에 새롭게 추가된 요구사항이 있어서 그것들을 중점으로 기존 요구사항까지 다시 확인해 보며 추가적으로 리팩토링하여 제출했다.

  • 함수(또는 메서드)의 길이가 15라인을 넘어가지 않도록 구현한다.
  • 함수(또는 메서드)가 한 가지 일만 잘 하도록 구현한다.
  • else 예약어를 쓰지 않는다.
  • Java Enum을 적용한다.
  • 도메인 로직에 단위 테스트를 구현해야 한다. 단, UI(System.out, System.in, Scanner) 로직은 제외한다.
  • 핵심 로직을 구현하는 코드와 UI를 담당하는 로직을 분리해 구현한다.

정리 및 후기

이번 3주 차도 Enum, 도식화, MVC 등 여러 가지로 성장할 수 있었던 뜻깊은 주차였다. MVC를 적용해 처음부터 설계하고 하나하나 분리하여 구현해 봄으로 써 뭔가 퍼즐 조각을 맞추는 듯한 느낌이 들어서 매우 재밌게 몰두했던 것 같다.

특히 제일 처음에 뭐부터 하면 좋을지 아무것도 모르겠을 때 Flow chart를 그리고 나서 딱 명확해졌는데 그때 전율이 왔다 ㅋㅋ 마치 미로에서 지도를 발견한 것처럼 어디로 가야 할지 딱 보였던 것 같다.

벌써 다음 주가 4주 차로 마지막 미션이다ㅠㅠ.. 그동안 많은 피드백으로 열심히 성장해왔으나 과연 그동안 배웠던 것을 나는 잘 적용하고 있는가라고 생각하면 막상 또 고민이 된다.

지금까지 많은 것들을 단기간에 압축해서 배웠기 때문에 익숙하지 않아 잘 다듬지 않으면 체득한 것이라 할 수 없다고 생각한다. 그렇기 때문에 다음 주의 최종 목표는 지금까지 한 것들을 종합하여 정리해 적용해 보며 잘 마무리하는 것이다.

이번 코수타(코치들의 수다 타임)에서도 포비가 이런 말을 했던 기억이 난다.

“욕심을 버려라. 누군가 tdd, oop, 클린 코드를 하더라도 내가 그전 걸 소화하지 않고 따라 한다면 그건 의미 없다”

“이런 쓰레기(tdd) 같은 거 안 하면 어때” 라고 한건 안 비밀ㅋㅋㅋㅋㅋㅋ


*틀린 부분이 있으면 언제든지 말씀해 주시면 공부해서 수정하겠습니다.


© 2022. All rights reserved.

Powered by 애송이