우테코 8기 2주차 미션 회고

자바·2025년 10월 27일

우테코

목록 보기
4/10
post-thumbnail
@Override
public void start() {
	System.out.println("8기 2주차 미션");
}

새로운 도전과 기술적 성장을 이룬 일주일


🚗 2주차 미션 - 자동차 경주


2주차 미션은 자동차 경주 게임이었습니다. 1주차에서 배운 TDD와 도메인 중심 설계를 적용하려 했지만, 여전히 "어떤 순서로 개발해야 할까?"라는 고민이 남아있었습니다. 이번 주차에서는 코드 리뷰를 처음 받아보고 오프라인 스터디에도 참여하며 기술적으로나 협업적으로나 많은 성장을 경험했습니다.

초간단 자동차 경주 게임을 구현한다.

  • 주어진 횟수 동안 n대의 자동차는 전진 또는 멈출 수 있다.
  • 각 자동차에 이름을 부여할 수 있다. 전진하는 자동차를 출력할 때 자동차 이름을 같이 출력한다.
  • 자동차 이름은 쉼표(,)를 기준으로 구분하며 이름은 5자 이하만 가능하다.
  • 사용자는 몇 번의 이동을 할 것인지를 입력할 수 있어야 한다.
  • 전진하는 조건은 0에서 9 사이에서 무작위 값을 구한 후 무작위 값이 4 이상일 경우이다.
  • 자동차 경주 게임을 완료한 후 누가 우승했는지를 알려준다. 우승자는 한 명 이상일 수 있다.
  • 우승자가 여러 명일 경우 쉼표(,)를 이용하여 구분한다.
  • 사용자가 잘못된 값을 입력할 경우 IllegalArgumentException을 발생시킨 후 애플리케이션은 종료되어야 한다.

고민 내용

UML 다이어그램

PR 링크입니다 !
개발 내용을 확인하시려면 이 부분을 클릭해 주세요 !


📝 기술적 도전과 학습


1. 랜덤 값 테스트

문제 상황 : 랜덤 값을 테스트 할 때 랜덤 함수를 직접 사용하면 테스트 결과를 예측할 수 없어 어려움이 있었습니다. 처음에는 전진 여부만 구분하면 되지 않을까 생각해서 단순히 전진만 시키는 기능을 만들었습니다.

해결 과정 : 전진 기능을 만들고 나서 생각해보니, 다른 블로그나 코드를 보면서 여러 값을 입력받아 테스트할 수 있는 방법을 봤습니다. 그래서 @ValueSource를 활용하여 테스트를 분리했습니다.

@ParameterizedTest
@ValueSource(ints = {0, 1, 2, 3})
@DisplayName("자동차는 3 이하의 값이면 정지한다")
void 자동차는_3_이하의_값이면_정지한다(int randomValue) {
    Car car = new Car("pobi");
    int initialPosition = car.getPosition();
    
    car.move(randomValue);
    
    assertThat(car.getPosition()).isEqualTo(initialPosition);
}
@ParameterizedTest
@ValueSource(ints = {4, 5, 6, 7, 8, 9})
@DisplayName("자동차는 4 이상의 값이면 전진한다")
void 자동차는_4_이상의_값이면_전진한다(int randomValue) {
    Car car = new Car("pobi");
    int initialPosition = car.getPosition();
    
    car.move(randomValue);
    
    assertThat(car.getPosition()).isEqualTo(initialPosition + 1);
}

결과 : 이렇게 두 가지 경우로 나누어 테스트하니 랜덤한 값을 받지 않고 랜덤 값의 범위 내에 있는 값으로만 테스트해도 제대로 동작하는지 검증할 수 있었습니다.

추가 학습 : 미션이 끝나갈 때쯤 다른 방법은 없나 구글링해봤는데 Mockito라는 도구를 발견했습니다. 랜덤 값을 임의로 주입해서 진행하는 방법이 있는 것도 배울 수 있었고, 흐름 테스트에도 사용할 수 있다는 것을 알게 되어 다음 미션을 할 때 참고해서 작성해볼 예정입니다 !


2. TDD(테스트 주도 개발)와 DDD(도메인 주도 설계)

TDD 적용: 이전 주차에서는 TDD가 어떻게 돌아가는지 파악하고 사용법을 익히는 데 집중했다면, 이번 주차에는 작은 기능 하나를 만들 때마다 더 많이 테스트를 작성하기위해 노력했고, 기능 완성 후 리팩토링하는 흐름으로 진행했습니다.

확실히 작은 단위로 테스트를 진행하니 장점이 많았습니다.

  • 구현해야 할 기능 명확
  • 리팩토링 시 안정망 확보
  • 개발 속도 향상, 디버깅 시간 감소

DDD 도전: 도메인 주도 설계에 대해 커뮤니티 글을 읽으면서 고민했습니다. 아직 완벽히 이해하지는 못했지만, "일단 도전하자"라는 마인드로 일단 도메인에게 역할을 부여를 중점으로 해야 된다는 생각으로 일단 진행을 했습니다.

확실히 완벽하게 이해하지는 못했지만, 코드 리뷰와 스터디를 통해 DDD에 대해 더 배울 수 있을 거라 생각했습니다. 다른 크루들은 어떻게 도메인을 설계했는지 어떤 기준으로 역할을 나눴는지 토론하면서 조금씩 이해의 폭을 넓혀갈 수 있을 것 같았습니다.

그래서 완벽하지 않더라도 일단 내가 이해한 방식대로 작성해보기로 했습니다.

// Car: 자동차 개별 객체
public class Car {
    private final String name;
    private int position;
    
    public void move(int randomValue) {
        if (randomValue >= 4) {
            position++;
        }
    }
    
    // default: Cars 외부에서는 개별 위치를 알 필요 없음
    int getPosition() {
        return position;
    }
}
// Cars: 자동차들을 관리하는 일급 컬렉션
public class Cars {
    private final List<Car> cars;
    
    public void moveAll(RandomNumberGenerator generator) {
        cars.forEach(car -> car.move(generator.generate()));
    }
    
    public List<String> findWinners() {
        int maxPosition = findMaxPosition();
        return cars.stream()
                   .filter(car -> car.getPosition() == maxPosition)
                   .map(Car::getName)
                   .toList();
    }
}

설계 포인트:

  • Car: 개별 자동차의 책임(이름, 위치, 이동)만 담당
  • Cars: 여러 자동차를 관리하고 우승자를 찾는 책임 담당
  • 접근 제어: getPosition()default로 설정해 Cars 외부에서는 개별 위치에 접근하지 못하도록 캡슐화

아직 부족한 부분이 많지만, 앞으로 코드 리뷰와 스터디를 통해 계속 개선해나갈 예정입니다!


3. SRP(단일 책임 원칙)

우아한테크코스 클린코드 체크리스트의 "메서드가 한 가지 일만 담당하도록 구현했는가?" 항목에 대해 많이 고민했습니다.

최대한 하나의 메서드가 하나의 일만 담당하도록 노력하며 개발을 진행했습니다. 그 과정에서 특히 고민이 깊었던 지점이 있습니다.

고민했던 지점 : 시도 횟수를 입력받을 때 String을 int로 변환하는 과정에서 많은 고민이 있었습니다. int 범위를 초과하면 NumberFormatException이 발생하는데, 이 검증 로직을 Validator에서 처리해야 할지 Parser에서 처리해야 할지 고민이었습니다.

첫 번째 시도: Validator에서 처리...?

public class InputValidator {
	public void validateIntRange(String input) {
    	// int 범위 검증 
    }
}

여기서 막혔습니다. int 범위를 검증하려면 Stringint로 변환하는 과정이 필요한데, 그럼 검증 단계에서 변환까지 하는 게 맞나? Validator의 역할이 너무 커지는 것 같았습니다.

최종 결정: Parser에서 처리

public class InputParser {
	public int parseRound(String input) {
    	try {
        	return Integer.parseInt(Input);
        } catch (NumberFormatException e) {
        	throw new IllegalArgumentException(ERROR_MESSAGES.INVALID_ROUND);
        }
    }
}

고민 끝에 이렇게 결론을 내렸습니다. 파싱은 "변환"의 책임이고, 변환 과정에서 발생하는 오류도 파싱의 일부라고 생각했습니다.

Validator"형식" 검증에 집중하는 것이 맞다고 판단했습니다. 숫자인지 아닌지, 빈 문자열인지 등 형식적인 부분만 Validator가 담당하고, int 범위 초과는 형식의 문제가 아니라 변환 과정의 문제이기 때문에 Parser에서 처리하는 게 맞다고 생각했습니다.

그래도 여전히 고민입니다.

이 결정이 완벽한 답은 아닐 수 있습니다. 혹시 이 부분에 대해 다른 의견이 있거나 더 나은 방법을 아신다면 댓글이나 코드 리뷰로 알려주시면 정말 감사하겠습니다!


💭 개인 소감문


1주차에서 검증과 추출을 먼저 만들다가 막혔던 경험 덕분에 2주차에는 리드미에 개발 순서를 먼저 정리하고 '도메인을 중심으로 설계하자'고 다짐했습니다. 실패가 다음 도전 성장의 발판이 되었습니다.

1주차 미션 제출 후 처음으로 코드 리뷰를 받았습니다. 특히 "메서드가 한 가지 일만 담당하도록 구현했는가?"라는 질문이 인상 깊었습니다. 막연히 "코드를 나누면 좋겠다"라고만 생각했는데, 왜 나눠야 하는지 조금씩 이해가 되기 시작했습니다. 코드 리뷰를 읽으며 기술적인 부분뿐만 아니라 다른 크루들의 코드를 보고 배우며 소통하는 방식까지 익힐 수 있었습니다. 그래서 다른 크루들의 코드 리뷰에도 적극 참여하며 좋은 코드와 로직을 배웠습니다.

10월 24일, 지방에서 서울까지 가는 오프라인 스터디에 참여했습니다. "새로운 관점에서 문제를 바라보고 함께 리뷰하면 좋겠다"는 도전 정신으로 참여했는데 막상 가보니 다들 비슷한 고민을 하고 있었습니다. 같은 문제지만 각자의 접근 방식이 달랐고 누가 정답이고 오답이 아니라 각자의 방식으로 문제를 해결했습니다. 직접 대면하니 온라인보다 훨씬 더 깊은 토론이 오갔습니다. 스터디를 마치고 돌아오는 길에 "완벽하지 않아도 괜찮구나. 중요한 건 소통을 배우고 계속 새로운 것에 도전하는 거구나."라고 생각했습니다.

스터디 후 본격적으로 리팩토링을 시작했을 때, 1주차에서 헤맸던 부분이 이번에는 자연스럽게 해결되었습니다. "지난 주차보다 성장했구나"를 느낀 순간이었습니다. 2주차에서 가장 좋았던 것은 새로운 몰입의 즐거움을 다시 느낀 것입니다. 스터디에서 크루들과 고민을 나누고 그 깨달음을 코드로 구현하며 시간 가는 줄 몰랐습니다.

1주차에서 배운 것은 "도메인 중심 설계의 중요성", 2주차에서 배운 것은 "완벽하지 않아도 괜찮다는 것"과 "함께 성장하는 것의 가치"입니다. 완벽한 답은 아닐 수 있지만, 계속 고민하고 토론하고 생각하는 과정 자체가 성장이라고 생각합니다. 중요한 건 계속 도전하고, 실패를 두려워하지 않으며, 그 과정에서 배우는 것입니다.


😺 마무리하며


일주일 전 저는 도메인 중심 설계를 막연하게 개념만 알고 있었습니다. 지금의 저는 도메인이 좋은 설계의 출발점이라고 생각하게 되었습니다.

단순히 개념을 하나 더 알게 되었다고 생각하지 않습니다. 코드 리뷰를 통해 다른 사람의 생각과 스터디를 통해 다양한 접근 방식을 경험하고 실패를 성장의 발판으로 삼는 개발의 즐거움을 다시 느낄 수 있었습니다.

이제 2주차 코드 리뷰를 받으며 현재 코드의 문제점을 파악하고 Mockito를 활용한 테스트 방식을 고민하고, 도메인 중심 설계를 더 깊이 학습해볼 예정입니다.

코드 리뷰를 해주실 모든 분들께 미리 감사드립니다! 모두 함께 성장하고 열띤 토론을 하며 좋은 결과를 내는 개발자가 되면 좋겠습니다!

프리코스는 절반에 도달했습니다.

앞으로 더 어려운 과제와 복잡한 문제들이 기다리고 있을 것입니다.
하지만 두렵지 않습니다. 두려움보다 도전을, 완벽함보다 성장을, 혼자보다 함께.
이것이 제가 우테코에서 배우고 있는 가장 소중한 가치입니다.

profile
열띤 토론을 좋아하는 개발자입니다.

4개의 댓글

comment-user-thumbnail
2025년 10월 27일

처음에 작성하신 로직에 대한 고민 부분도 마크다운 명세로 작성해 보면
자연스럽게 다시 정리도 되고 다른 크루들과 공유하기도 편할 것 같아요!

스터디 멤버가 상당히 많네요 ㄷㄷ 열정들이 대단하십니다
2주차도 고생하셨고 3주차 화이팅!

1개의 답글
comment-user-thumbnail
2025년 10월 28일

두려움보다 도전을, 완벽함보다 성장을, 혼자보다 함께
멋있으십니다
역시 보법이 다르세요

1개의 답글