[우아한테크코스 6기] 프리코스 1주차 회고

조경찬 (Jo Gyungchan)·2023년 10월 30일
0

우아한테크코스

목록 보기
1/4
post-thumbnail

드디어 기다리던 프리코스 1주차를 10/19 ~ 10/25까지 진행하였습니다.

저번 기수인 5기부터 우테코에 지원을 하기만 하면 프리코스를 함께 참여할 수 있었기에 코딩 테스트를 하지 않고 프리코스에 참여할 수 있었습니다.

1주차


숫자 야구 게임

1주차의 미션은 숫자 야구 게임을 구현하는 것이였습니다.

미션을 진행하는 방식은 다음과 같이 나와있었습니다.

진행 방식

  • 미션은 기능 요구 사항, 프로그래밍 요구 사항, 과제 진행 요구 사항 세가지로 구성되어 있다.
  • 세 개의 요구 사항을 만족하기 위해 노력한다. 특히 기능을 구현하기 전에 기능 목록을 만든다.
  • 기능 요구 사항에 기재되지 않은 내용은 스스로 판단하여 구현한다.

요구 사항 분석


미션 진행 방식에 명시되어 있는대로 기능을 구현하기 전에 기능 목록을 작성하기 위해 요구 사항을 꼼꼼히 읽고 기능 목록을 작성하기 위해 노력했습니다.

이번 미션에 나와있는 기능 요구 사항은 다음과 같았습니다.

이를 토대로 최대한 기능을 작은 단위로 분리하였고, 아래 사진은 제가 작성한 구현 기능 목록입니다.

구현 기능 목록을 작성할때 구현할 클래스도 생각하며 분리하였고, 이를 통해 미션을 진행할 때는 클래스에 대한 고민을 줄일 수 있었습니다.
(물론 구현 기능 목록을 작성할때부터 클래스를 생각하는 것이 좋은 것인지 모르겠습니다.)

기능 구현


클래스와 패키지 분리

저는 다음과 같이 클래스와 패키지를 분리하였습니다.

테스트 코드

기능을 구현할 때 TDD 기반으로 진행하면서 대부분의 기능에 대해 테스트를 작성할 수 있었습니다.

TDD와 리팩토링에 대해 궁금하신 분들은 한번씩 보시는 것을 추천드립니다.
우테코의 대장이신 포비님이 강의하시는 영상입니다.
https://www.youtube.com/watch?v=bIeqAlmNRrA

테스트 코드를 작성할 때 귀찮기도 했지만, 발생할 수 있는 모든 예외 상황에 대해 생각하고 여러 입력 값에 대해 테스트를 진행하려고 노력했습니다. 특히, 에러가 발생할 확률이 높은 경곗값은 주의 깊게 테스트를 진행하였습니다.

위에 테스트 코드는 경곗값을 모두 비교하기 위해 제가 작성했던 코드입니다.

  • 1과 9에 대한 숫자를 입력했을 때는 예외가 발생 X
  • 0과 10에 대한 숫자를 입력했을 때는 예외가 발생 O

실제로 TDD 기반으로 구현을 시작하면서 작성한 모든 로직들에 대해 테스트 코드를 구현하고 잘못된 설계와 버그를 초기에 수정할 수 있었습니다.

보기만 해도 맘이 편안해지는 초록색과 총 38개의 테스트 코드를 통해 꼼꼼히 기능에 대한 결과를 확인할 수 있었습니다.

원시값 포장

숫자 야구 게임에서 공을 비교하기 위해 Ball이라는 객체를 만들었습니다.

// 처음에 작성했던 Ball 객체
public class Ball {
    private static final int MIN_BALL_NUMBER = 1;
    private static final int MAX_BALL_NUMBER = 9;
    private static final int MIN_POSITION = 1;
    private static final int MAX_POSITION = 3;

    private final int ballNumber;
    private final int position;

    public Ball(int ballNumber, int position) {
        validateBall(ballNumber, position);
        this.ballNumber = ballNumber;
        this.position = position;
    }

    private void validateBall(int ballNumber, int position) {
        validateBallNumber(ballNumber);
        validatePosition(position);
    }

    private void validateBallNumber(int ballNumber) {
        if (ballNumber < MIN_BALL_NUMBER || ballNumber > MAX_BALL_NUMBER) {
            throw new IllegalArgumentException("공의 숫자는 1에서 9사이의 값이어야 합니다.");
        }
    }

    private void validatePosition(int position) {
        if (position < MIN_POSITION || position > MAX_POSITION) {
            throw new IllegalArgumentException("공의 위치는 1에서 3사이의 값이어야 합니다.");
        }
    }
}

하지만 대충 봐도 알 수 있듯이, 절반 이상의 코드가 공의 숫자와, 위치를 검증하는 코드로 이루어져있습니다.

이를 해결하기 위해 저는 공의 숫자와 위치를 각각 BallNumber, Position이라는 객체로 포장을 해주었습니다.

public class Ball {
    private final BallNumber ballNumber;
    private final Position position;

    public Ball(int ballNumber, int position) {
        this.ballNumber = new BallNumber(ballNumber);
        this.position = new Position(position);
    }
}

public class BallNumber {
	private static final int MIN_BALL_NUMBER = 1;
    private static final int MAX_BALL_NUMBER = 9;

    private final int ballNumber;

    public BallNumber(int ballNumber) {
        validateBallNumber(ballNumber);
        this.ballNumber = ballNumber;
    }

    private void validateBallNumber(int ballNumber) {
        if (ballNumber < MIN_BALL_NUMBER || ballNumber > MAX_BALL_NUMBER) {
            throw new IllegalArgumentException("공의 숫자는 1에서 9사이의 값이어야 합니다.");
        }
    }
}

public class Position {
    private static final int MIN_POSITION = 1;
    private static final int MAX_POSITION = 3;

    private final int position;

    public Position(int position) {
        validatePosition(position);
        this.position = position;
    }

    private void validatePosition(int position) {
        if (position < MIN_POSITION || position > MAX_POSITION) {
            throw new IllegalArgumentException("공의 위치는 1에서 3사이의 값이어야 합니다.");
        }
    }
}

원시값을 포장함으로써 Ball 객체가 가지던 많은 유효성 검사와 관련된 코드들을 다른 객체로 위임할 수 있었고, Ball 객체는 그와 관련된 책임만을 가질 수 있었습니다.

리팩토링


요구사항에 맞게 모든 기능을 구현하고 난 뒤에는 리팩토링에 많은 시간을 투자하였습니다.

중심적으로 본 부분들은 다음과 같습니다.

  • 단일 책임의 원칙을 지키도록 리팩토링한다.
  • 메서드를 작은 단위로 분리할 수 있는 부분을 찾아 분리한다.
  • 변수명, 메서드명, 클래스명 등 가독성을 높일 수 있는 이름으로 바꾼다.
  • 객체 필드에 직접 접근하는 것이 아니라 객체에 메시지를 보내 기능을 수행한다.
  • 람다 함수를 활용 가능한 부분을 찾아 적용한다.

끊임없이 더 나은 코드가 있을지 고민하며 리팩토링해보면서, 시간을 많이 투자하긴 했지만 다양한 개념들을 새롭게 알고 습득하면서 굉장히 의미 있는 시간이라 느꼈다.

오히려 리팩토링을 하며 더 많이 느끼고, 배울 수 있어서 뿌듯하기도 했다.

마치며


굉장히 오래 기다리고 목표로 삼았던 우테코가 시작하면서 떨리기도 하고, 기쁘기도 했다. 이번 1주차 미션을 통해 새로운 개념들을 알 수 있었고, 많은 부분들을 배울 수 있었다.

이를 통해 스스로 성장하길 바라는 욕심이 더욱 커져 가기도 했고, 항상 고민하고 끊임없이 성장하는 개발자가 될 수 있길 바란다...!!

자세한 코드는 여기서 확인 가능합니다!!
https://github.com/jcoding-play/java-baseball-6/tree/gyungchan

profile
한걸음씩 성장하는 개발자

0개의 댓글