[우아한테크코스 4기] Level 1 25일차 회고

Jihoon Oh·2022년 3월 1일
0

거의 1주만에 회고를 쓴다. 왜냐하면 아팠기 때문이다. 지난 주 초반부는 데일리도 죄다 빠지고 강의도 못들을 정도로 아파서 약먹고 페어 프로그래밍과 리팩토링 잠깐 잠깐 한 것 외에는 잠만 잘 정도였고, 후반부에는 회고보다는 스터디 준비하고 로또 미션 리팩토링을 하느라 시간을 보냈기 때문이다.

왜 아프냐고?

오미크론 확진자가 거의 20만명씩 나오는 이 시기에 놀라운 일은 아니다만, 뭐 어쨌든 그렇게 됐다. (다행히 이 글을 쓰고 있는 시점에서는 격리해제) 그간 우테코 하느라 밖에 잘 나가지도 않다가 졸업식 한번 나갔다 왔는데 거기서 걸려서 왔다. 운도 지지리도 없지...

오미크론으로 우세종이 변하고 나서 코로나가 안아프다는 소리를 들어서 걱정하지 않았었는데, 아니었다. 진짜 엄청 아팠다. 독감 한번 걸린적 없던 나라서 그런가 이런 증상에 익숙치가 않았다. 목이 미친듯이 부어서 침도 삼키기 힘들 정도... 여러분 몸관리 잘하세요 증말...

로또 1단계 미션으로 배운 것

캐싱

로또 번호는 1~45까지 총 45개다. 다른 나라 로또는 어떤지 모르겠고 우리 나라에서도 스피또는 다른 방식이고 그렇지만 뭐 여튼 로또 6/45의 규칙은 1~45의 숫자 6개를 고르는 것이다. 그렇다면, 어쨌든 간에 로또 미션에서 사용하는 LottoNumber(로또 번호 VO)의 개수는 45개라는 소리다. 처음에는 그냥 무작정 인스턴스를 만들었는데, 코드 리뷰 과정에서 45개의 객체를 미리 캐싱해놓고 가져다 쓰면 되지 않겠냐는 의견을 받았다.

오, 생각해보니 그렇다. 계속 LottoNumber의 인스턴스를 만드는 것 보다, 미리 1~45의 LottoNumber를 만들어 놓고 필요할 때 참조로 가져다 쓰는 것이 훨씬 빠르고 리소스 낭비도 적다. 처음에는 List의 형태로 만들었는데, 지금이야 1~45로 연속적인 숫자지만 연속적이지 않을 경우에 문제가 되고, 값을 찾아올 때 값-1 로 인덱싱을 해줘야 한다는 단점이 있어서 Map의 형태로 바꿔주었다.

캐싱에 대한 더 자세한 내용이 궁금하다면 이곳을 참고하면 좋을 것 같다.

상속보다는 조합을

나와 내 페어 필즈는 LottoTicket과 WinningLotto 클래스가 내부 구조가 거의 비슷하니까 LottoNumbers 라는 추상 클래스를 만들어놓고, 그걸 상속해서 구현하는 식으로 했었다. 하지만 리뷰로 받은 내용은, 상속도 좋지만 조합으로 풀어나가는 것이 더 좋다는 피드백이었다.

이 부분에는 이펙티브 자바에도 같은 내용이 있었고, 현재 진행중인 이펙티브 자바 스터디에서도 다룬 내용이어서 관련 내용을 첨부한다.

DTO 생성 방식

나는 DTO 사용을 좋아한다. 아무리 변경 가능성을 최소화하는 방향으로 도메인을 설계했더라도, 도메인에 들어있는 비즈니스 로직이 뷰로 넘어가는 것에 대해 경계하기 때문이다. (내 코드 작성이 조그만한 사이드 이펙트의 가능성도 방지하고 싶어하는, 약간 보수적인 감이 있긴 하다.) 그래서 이번에도 DTO를 사용하려고 했는데, 아직은 사용하지 않았다. 생성 방식에 대한 고민 때문이었다.

원래는 당연히 도메인 외부에서 DTO를 생성했었는데, 생각해보니 도메인 안에 DTO를 반환하는 toDTO와 같은 메서드가 있으면(실제로 DTO -> entity의 경우 toEntity 메서드를 사용하는 예제들을 보았다.) getter를 쓰지 않고 좋지 않을까? 라는 생각을 해서 질문을 남겼다. 그리고 돌아오는 답변은...

그렇다. 도메인 내에서 DTO를 만들어주는 방식은 안티 패턴인 것 같다. "도메인 객체는 순수한 상태여야 합니다." 라는 생각을 하지 못해서 저런 방법을 생각했던 것 같다. 도메인과 뷰를 분리하기 위해서 DTO를 사용하는데 DTO 자체가 뷰와 연관이 있는 만큼(뷰가 달라지면 DTO의 모양도 달라져야 하므로) 도메인에서 DTO를 사용하는 것은 목적에 부합하지 않는다 라고 정리할 수 있을 것 같다.

로또 2단계 미션

1단계가 merge되고, 2단계 미션도 진행했다. 2단계 미션은 자동 로또 뿐 아니라 수동 로또를 생성하기. 이 부분에서 가장 애먹었던 부분이 수동 로또 구매 개수와 수동 로또 번호를 어떤 식으로 관리할건가였다. 온갖 클래스를 다 설계해보면서 고민해보았는데, 너무 많은 클래스 추가는 난잡한 것 같고 추가한 클래스는 LottoOrder.

public class LottoOrder {
    static final String TOO_MANY_MANUAL = "[ERROR] 수동 로또 구매 수는 전체 구매 수를 넘을 수 없습니다.";

    private final int manualLottoCount;
    private final int autoLottoCount;

    public LottoOrder(LottoPurchasingMoney lottoPurchasingMoney, int manualLottoCount) {
        validateManualCount(lottoPurchasingMoney, manualLottoCount);
        this.manualLottoCount = manualLottoCount;
        this.autoLottoCount = lottoPurchasingMoney.getPurchasableCount() - manualLottoCount;
    }

    public int getManualLottoCount() {
        return manualLottoCount;
    }

    public int getAutoLottoCount() {
        return autoLottoCount;
    }

    private void validateManualCount(LottoPurchasingMoney lottoPurchasingMoney, int manualLottoCount) {
        if (lottoPurchasingMoney.getPurchasableCount() < manualLottoCount) {
            throw new IllegalArgumentException(TOO_MANY_MANUAL);
        }
    }
}

수동 구매와 자동 구매 장 수를 저장하는 로또 주문 클래스를 만들었다. 이 부분을 객체로 만들게된 영감은 유튜브에서 본 '우아한객체지향' 이라는 세미나에서, 예제 코드에 주문, 배달 같은 모든 상태가 객체로 관리되고 있던 부분에서 따왔다. 처음에는 사용자가 입력한 수동 로또 번호도 필드로 넣어주려고 했는데, 그러면 유효성 검증 등의 부분에서 복잡해서 번호의 유효성 검증은 로또 생성시에 될 수 있도록 따로 객체를 만들지 않고 뷰에서 이중 리스트의 형태로 받아와서 처리하도록 했다.

그 외에는 DTO를 추가하는 등 자잘한 리팩토링을 하고 PR을 날렸다. 작성한 코드가 많지는 않았지만 수동 기능을 추가하는 부분은 쉽지 않았다...

아마 내일 쯤 코드 리뷰가 오지 않을 까 싶은데, 이번 주는 내가 만족할 수 있을 때 까지 최대한 코드 리팩토링 위주로 진행하는 것을 목표로 해야할 것 같다.


대학교 은사께서 작고하셔서 장례식 참석을 위해 내일은 회고 쉽니다.
삼가 고인의 명복을 빕니다.

profile
Backend Developeer

0개의 댓글