각종 후기/우아한테크코스

[우아한 테크코스 3기] LEVEL 1 회고 (37일차)

제이온 (Jayon) 2021. 3. 10.

안녕하세요? 제이온입니다.

 

오늘은 대부분의 시간을 블랙잭 2단계 미션을 수행하는데 사용하였습니다.

 

 

데일리 미팅

오전 10시에 케빈이 온라인 ZOOM으로 데일리 미팅을 진행해 주셨습니다. 케빈이 가져온 링크로 각자 MBTI 특징을 읽어보면서 서로의 MBTI를 공유하는 것이 이번 말하기 주제였습니다. 다양한 MBTI가 있었지만, 크루들 중에 저랑 같은 ESFJ가 많아서 신기했습니다. ESFJ가 그렇게 비교적(?) 흔한 편에 속하지만, 제가 기억나는 크루들만 해도 저 포함 4명이나 됐습니다.

 

시간 관계상 각자의 특징을 읽다가 끝나는 바람에 내일 이어서 하기로 하였습니다.

 

 

특강

우테코 2기 포코와 우테캠 출신 이찬호님이 특강을 진행해 주셨습니다. 하지만, 포코의 강의를 들을 때는 제 네트워크 상태가 갑자기 안 좋아져서 고치느라 거의 다 끝나가는 시점에 들어오게 되었습니다. 그래서 아쉽게도 많은 이야기는 듣지 못하였지만, 이번 우테코 기간동안 '본인에게 맞는 학습 방법'을 찾으라는 조언은 귀에 잘 새겨두기로 하였습니다.

 

다음으로, 프론트엔드 개발자이신 이찬호님은 더 나은 개발자로 성장하기 위해서는 "피드백"이 중요하다고 하였습니다. 자기 자신에게 피드백을 해 주되, 정성적인 평가가 아닌 정량적인 평가로 해야한다고 말씀해 주셨습니다. 예를 들어, 어떤 목표가 운동이라면 "오늘 운동을 열심히 하였다."로 피드백을 할 것이 아니라, "팔굽혀 펴기 xx회를 하였다."와 같이 구체적인 수치로 정량화 하는 것이 중요합니다.

 

하지만, 시험을 치는 것도 아니고 개발을 하면서 스스로에게 정량적으로 피드백을 하는 것은 어렵습니다. 따라서, 이찬호님은 매일 일기를 일단 정성적으로 쓰라고 조언해 주셨습니다. 정성적으로 글을 쓰다보면, 자기도 모르게 고민이 생기고 그 고민에 대해서 구체적으로 서술하게 된다는 것입니다.

 

저는 안 그래도 매일 매일 회고를 쓰고 있는데, 좀 더 하루 있었던 일 중에 고민이 되는 부분은 디테일하게 써야겠다는 생각이 들었습니다.

 

 

블랙잭 2단계 미션

어제 블랙잭 1단계 미션이 merge되면서 2단계 미션을 진행하게 되었습니다. 전체적인 틀은 같으나, 기능이 추가되고 사라진 것이 있습니다.

 

 

 

 

기존에는 단순히 승무패를 나타내는 것이지만, 이번에는 배팅 금액에 따른 수익을 출력하는 것이 목표입니다. 또한, 이번에는 승무패 로직이 디테일하게 주어져 있었습니다. 제가 이를 요약해 보았습니다.

 

 

<딜러 입장>

- 처음에 뽑은 카드 2장의 합이 21일 경우 블랙잭이다. (단, 플레이어도 블랙잭이면 무승부다.)

- 플레이어가 버스트일 경우 승리한다.

- 내가 버스트일 경우, 버스트가 아닌 플레이어에 대해서 패배한다.

- 나의 카드 합과 플레이어의 카드 합이 같으면 무승부다.

- 나의 카드 합이 플레이어의 카드 합보다 크면 승리하고, 작으면 패배한다.

 

 

<플레이어 입장>

- 처음에 뽑은 카드 2장의 합이 21일 경우 블랙잭이다. (단, 딜러도 블랙잭이면 무승부다.)

- 내가 버스트일 경우 패배한다.

- 딜러가 버스트일 경우, 내가 버스트가 아니면 승리한다.

- 나의 카드 합과 딜러의 카드 합이 같으면 무승부다.

- 나의 카드 합이 딜러의 카드 합보다 크면 승리하고, 작으면 패배한다.

 

 

제가 2단계 미션을 시작하면서 잘못 알고 있었던 사실이 있습니다. 바로, 카드의 합이 21이면 무조건 블랙잭이 아니라 처음 뽑은 카드 2장의 합이 21이어야 블랙잭인 것이죠. 따라서, 이 부분을 수정하는 것이 급선무였습니다.

 

가장 이상적인 것은 첫 번째 턴을 추상화하여 그것에 따른 여러 가지 행위를 상태 패턴으로 설계하는 것이 좋아 보였습니다. 하지만, 이미 짜 놓은 구조를 완전 엎어야해서 굳이 시도하지는 않고 타협을 보았습니다.

 

 

    public boolean isBlackjack() {
        if (isBust()) {
            return false;
        }
        return calculate() == BLACKJACK_SCORE && getCardCount() == INITIAL_CARDS_COUNT;
    }

 

 

바로, 위 메소드를 Participant 클래스에 추가해 놓는 것이죠. 현재 카드가 정확히 2장이 있고, 카드의 합이 21일 때만 블랙잭인 것입니다. 또한, 기존의 Result의 상수는 승무패 뿐이었는데, 블랙잭 상수를 추가하였습니다. 왜냐하면, 블랙잭으로 승리하면 배팅 금액의 1.5배를 추가로 돌려받기 때문이죠.

 

 

다음으로, 딜러와 플레이어의 승무패 로직입니다. 먼저, 딜러의 승무패 메소드 코드를 보겠습니다.

 

 

    @Override
    public Result decideWinner(final Participant player) {
        if ((this.isBlackjack() && player.isBlackjack()) || this.isSameScore(player)) {
            return Result.DRAW;
        }
        if (this.isBlackjack()) {
            return Result.BLACKJACK;
        }
        if (player.isBust() || (!this.isBust() && (this.calculate() > player.calculate()))) {
            return Result.WIN;
        }
        return Result.LOSE;
    }

 

 

다음으로는 플레이어의 승무패 메소드 코드를 보겠습니다.

 

 

    @Override
    public Result decideWinner(final Participant participant) {
        if (this.isBust() || (!participant.isBust() && (this.calculate() < participant.calculate()))) {
            return Result.LOSE;
        }
        if ((this.isBlackjack() && participant.isBlackjack()) || this.isSameScore(participant)) {
            return Result.DRAW;
        }
        if (this.isBlackjack()) {
            return Result.BLACKJACK;
        }
        return Result.WIN;
    }

 

 

플레이어는 자신이 버스트일 경우 무조건 패배하기때문에 딜러와는 미묘하게 로직이 다른 것을 알 수 있습니다. 하지만, 반복된 조건은 존재하지만 이것을 어떻게 추상화할 수 있을지는 잘 모르겠습니다. 그래서 이 부분은 게이츠에게 질문하였습니다.

 

 

마지막으로 중복되는 테스트 코드 구조입니다. 아래는 플레이어의 승무패 로직을 테스트하는 코드입니다.

 

 

    @ParameterizedTest
    @DisplayName("딜러가 블랙잭이 아닌 상황에서 참가자의 경기 결과 구하기")
    @CsvSource(value = {"KING,KING,KING:LOSE", "ACE:LOSE", "KING,NINE:DRAW", "ACE,TEN:BLACKJACK",
        "KING,KING:WIN"}, delimiter = ':')
    void checkPlayerGameResult(final String input, final Result result) {
        final Dealer dealer = new Dealer();
        dealer.receiveCard(new Card(CardNumber.KING, CardType.CLOVER));
        dealer.receiveCard(new Card(CardNumber.NINE, CardType.CLOVER));

        final String[] inputs = input.split(",");
        for (final String number : inputs) {
            final CardNumber cardNumber = CardNumber.valueOf(number);
            player.receiveCard(new Card(cardNumber, CardType.CLOVER));
        }
        assertThat(player.decideWinner(dealer)).isEqualTo(result);
    }

    @ParameterizedTest
    @DisplayName("딜러가 블랙잭인 상황에서 참가자의 경기 결과 구하기")
    @CsvSource(value = {"ACE,TEN:DRAW", "ACE:LOSE"}, delimiter = ':')
    void checkPlayerGameResultWhenDealerBlackjack(final String input, final Result result) {
        final Dealer dealer = new Dealer();
        dealer.receiveCard(new Card(CardNumber.ACE, CardType.CLOVER));
        dealer.receiveCard(new Card(CardNumber.KING, CardType.CLOVER));

        final String[] inputs = input.split(",");
        for (final String number : inputs) {
            final CardNumber cardNumber = CardNumber.valueOf(number);
            player.receiveCard(new Card(cardNumber, CardType.CLOVER));
        }
        assertThat(player.decideWinner(dealer)).isEqualTo(result);
    }

 

 

보시면 딜러를 생성하고 입력값에 맞춰서 카드를 넣어주는 로직이 상당히 비슷합니다. 그리고 딜러의 승무패 로직도 위의 dealer가 player가 되었을 뿐이지 구조는 동일합니다. 따라서 이 부분도 리팩토링이 필요한데, 어떻게 수정하는 것이 좋을지 게이츠에게 질문하였습니다.

 

 

정리

미흡한 구조가 있지만, 그럼에도 오늘 제가 계획한대로 2단계 미션을 pr 날려서 만족스럽습니다. 또한, '개발자가 반드시 정복해야 할 객체 지향과 디자인 패턴' 책을 읽고 전략 패턴을 정리하였습니다.

 

내일은 루터 회관을 가야하므로 책을 조금만 읽다가 자야겠습니다.

댓글

추천 글