우아한 테크코스[프리코스] 2주차 회고록

2025. 10. 28. 10:31·우아한테크벨로/프리코스 회고록
728x90

이번 과제는 내가 교내 전공 교과목 튜터로 일을하면서 과제를 만드는 것에 사용했던 자동차 경주가 2주차 과제로 나왔다.

후배들에게 과제를 주면서도 항상 고민하면서 작성하던 주제이고, 이번 기회를 삼아 다시 한 번 더 조금 더 객체지향적인 결과물을 만들어보려고 노력했다.

TDD의 필요성을 체감했다

 이번 초간단 자동차 경주 게임 구현 과제는 단순히 자바 문법을 활용하는 것을 넘어, 객체 지향적인 설계와 깨끗한 코드 작성의 중요성을 깊이 고민하게 하는 경험이었다.

특히 'indent depth 3 미만'과 '메서드의 단일 책임'이라는 제약 사항은 '어떻게' 기능을 구현할 것인가가 아니라 '무엇을' 기준으로 코드를 분리하고 객체에게 책임을 위임할 것인지 근본적인 질문을 던졌다

 

객체 분리, 그 어려움과 고민의 과정

과제를 시작하며 가장 중요하게 생각했던 부분은 역시 '객체 분리와 역할 세분화'다.

나는 요구 사항을 바탕으로 InputView, OutputView, RacingGame, Car, Referee 그리고 유효성 검증을 위한 NameValidator, NumberValidator 등 역할을 분리하려 노력했다.
 하지만 이 과정에서 가장 어려웠던 지점은 명확한 경계를 설정하는 일이다. 특히 입출력을 담당하는 뷰(View) 객체들과 실제 게임 로직을 처리하는 도메인(Domain) 객체들, 그리고 우승자를 판별하는 로직을 분리하는 것이 모호하게 느껴졌다.

예를 들어, InputView가 단순히 입력을 받는 것에서 그쳐야 하는지, 아니면 입력받은 값을 파싱하고 검증하는 책임까지 져야 하는지 고민이 되었다. 또한 RacingGame이 전체 게임을 총괄하면서 우승자 판별 로직까지 갖는 것이 맞는지, 아니면 Referee라는 별도 객체에게 완전히 위임하는 것이 맞는지에 대한 설계적 고민이 많았다.

그래서 나는 InputView에서는 입력을 받아서 validator에게 검증의 책임을 넘기는 방식으로 구현하고, 경주 진행은 RacingGame을 통해서 진행하고, 우승자의 판단은 Referee에게 넘기는 방식으로 구현을 하였다.

 

TDD의 부재가 가져온 비효율

 이번 과제에서 TDD(Test-Driven Development)가 아닌, 요구 사항 분석에 따른 전통적인 기능 구현 방식으로 개발을 진행했다. 먼저 README.md에 기능 목록을 정리하고, 이에 따라 클래스 구조를 설계한 뒤 세부 기능을 하나씩 구현해 나갔다. 이 방식은 초기 구현에는 속도를 낼 수 있었지만, 개발 과정에서 여러 비효율을 초래했다. 가장 큰 문제는 '변경에 대한 불안정성'이었다. 개발 도중 요구 사항이 일부 변경되거나, 미처 생각지 못한 혹은 까먹은 예외 처리를 추가해야 하는 상황이 발생했다. 예를 들어, '자동차 이름 5자 이하' 검증 로직을 수정하거나, '이동 횟수'에 대한 숫자 외 입력 값 예외 처리를 추가할 때, 이 변경이 기존에 다른 코드에 영향을 주지 않을까 하는 불안감이 항상 존재했다. 
수동으로 매번 테스트를 진행하며 확인하는 것은 번거로웠고, 모든 엣지 케이스를 놓치지 않고 확인하고 있다는 확신을 갖기 어려웠다. 만약 이 기능을 수정했을 때 다른 기능이 깨진다면, 그 원인을 찾는 데 또 다른 시간을 소모해야 했다.

 

TDD의 가치를 깨닫다

바로 이 지점에서 TDD의 진정한 가치를 깨닫게 되었다. 만약 TDD 방식으로 개발을 진행했다면, 개발 경험은 완전히 달라졌을 것이다.
첫째, TDD는 '명확한 목표 지향적 개발'을 가능하게 한다. 기능을 구현하기 전, "자동차 이름이 6자일 경우 IllegalArgumentException을 발생시킨다" 또는 "무작위 값이 4일 때 자동차는 전진한다"와 같은 구체적인 테스트 케이스를 먼저 작성했을 것이다. 이는 '무엇을 만들어야 하는지'에 대한 명확한 목표가 되어, 불필요한 고민 없이 딱 필요한 만큼의 코드만 작성하도록 이끌었을 것이다.
둘째, 테스트 코드는 '견고한 안전망' 역할을 한다. 과제 중반에 예외 처리 로직을 추가하거나 기존 로직을 리팩토링할 때, 미리 작성해둔 테스트 코드를 실행(Run)하는 것만으로 '변경으로 인한 버그'가 발생했는지 즉각적으로 피드백받을 수 있다. 이는 "기존 기능은 안전하다"라는 심리적 안정감을 주어, 과감하고 적극적인 코드 개선을 가능하게 한다.
셋째, TDD는 '다양한 예외 처리와 엣지 케이스'를 자연스럽게 고려하도록 유도한다. "사용자가 자동차 이름을 입력하지 않았을 때", "쉼표(,)만 입력했을 때", "이동 횟수로 0이나 음수를 입력했을 때" 등 다양한 비정상적 상황에 대한 테스트 코드를 미리 작성함으로써, 훨씬 더 견고하고 안정적인 애플리케이션을 완성할 수 있었을 것이다. 

 

미래의 개발을 위한 다짐

나의 새로운 개발 순서이번 과제를 통해 저는 '테스트 코드는 단순히 버그를 잡는 도구가 아니라, 설계를 명확하게 하고 개발의 방향을 이끄는 나침반'임을 깨달았다. TDD의 편리함과 중요성을 절실히 느낀 만큼, 향후 개발 프로세스를 다음과 같이 정립하고자 한다.

[1순위] 주요 기능 요구사항 정의 (README.md 작성): 프로젝트의 큰 그림과 핵심 기능을 명확히 정의
[2순위] 테스트 코드 작성 (TDD): 정의된 기능을 바탕으로 성공 케이스와 실패 케이스(예외 상황)에 대한 테스트 코드를 먼저 작성
[3순위] 클래스 구조 설계: 테스트 코드를 통과하기 위한 최소한의 책임을 가진 객체들을 설계하고, 이들 간의 관계를 정립
[4순위] 세부 기능 구현: 테스트 코드를 하나씩 통과시켜 나가는 방식으로 실제 코드를 구현하고, 모든 테스트가 통과하면 리팩토링을 통해 코드를 개선

자동차 경주 게임이라는 작은 프로젝트였지만, 객체 지향 설계에 대한 고민과 TDD의 필요성이라는 큰 배움을 얻었다. 이번에 느낀 점들을 바탕으로 다음 프로젝트에서는 더욱 효율적이고 견고한 코드를 작성하는 개발자로 성장하겠다.

 

과제의 전체 코드는 아래 깃허브 링크에 올려두었다.

https://github.com/juntae6942/java-racingcar-8/tree/juntae6942

 

GitHub - juntae6942/java-racingcar-8

Contribute to juntae6942/java-racingcar-8 development by creating an account on GitHub.

github.com

 

 

1주차 과제 코드 리뷰 후기

2주차 과제 회고록에 추가로 다른 분들께서 내 코드를 보고 해주신 1주차 과제 코드 리뷰 후기를 써보려고한다.

먼저 첫 번째 코멘트다. 나는 입력으로 사용자 지정 패턴이 들어오는 경우에 패턴이 틀리는 경우를 생각하지 못했었다😂

이러한 경우에 예시 입력으로 아래와 같이 들어온다면

//;2;3;4

이 경우, 패턴으로 인식되지 않아 사용자 지정 구분자가 없다고 판단하면서 다음 단계로 넘어가게 된다.

그 결과 parse 단계에서는 사용자 지정 구분자가 없다고 가정하고 코드를 그대로 진행하게 되고, 결국 NumberValidator 단계에 도달한다. 이 단계에서는 입력값이 숫자인지를 검증하기 때문에, 제대로 파싱되지 않은 "//;2;3;4"가 하나의 토큰으로 전달되어 숫자가 아닌 값으로 인식되어 예외가 발생한다.

하지만 이 예외의 본질은 숫자 형식 오류가 아니라 사용자 지정 구분자 패턴 오류다. 따라서 NumberValidator에서 걸러지는 것이 아니라, 그 이전 단계에서 사용자 지정 구분자 패턴을 검증하는 로직이 있었어야 했다. 이 부분을 고려하지 못한 점을 통해, 앞으로는 요구사항에 맞는 예외 상황을 보다 세밀하게 구분하고 처리해야 한다는 점을 깨달았다.

 

다음으로 인상 깊었던 코멘트는 'Pattern Matching'의 효율성에 관한 것이었다.

과제를 진행하면서 아직 코드의 '성능 효율성'을 깊게 고려하는 습관이 익숙하지 않아, 저는 Pattern 클래스의 compile() 방식과 String 클래스의 matches() 메서드를 명확한 기준 없이 혼용했다. 이전에 다른 지원자분께서 두 방식의 성능을 비교 테스트한 공유 글을 본 기억이나서, 그것을 실제 코드에 적용하지 못했다는 점이 스스로 아쉬웠다.

이번 리뷰를 계기로 String.matches()의 내부 구현을 직접 살펴보았다. 이 메서드는 호출될 때마다 내부적으로 Pattern.matches()를 호출하고, 이 Pattern.matches()는 다시 Pattern.compile()을 호출하여 정규식 패턴을 '매번 새롭게 컴파일'하는 구조였다.

String.matches()
Pattern.matches()

즉, String.matches()를 반복적으로 사용하는 것은 호출할 때마다 불필요한 컴파일 비용을 지불하는 것과 같았다. 반면, Pattern 객체를 미리 컴파일하여 (가령 static final 필드로) 생성해두고 재사용하는 방식이, 특히 반복문 등에서 사용될 경우 성능 면에서 훨씬 효율적일 수밖에 없다는 것을 명확히 깨달았다.

이 리뷰는 큰 교훈을 주었다. 앞으로는 단순히 기능을 구현하는 것을 넘어, 자주 사용되는 메서드나 라이브러리의 내부 구현을 한 번쯤 파악해보고, '왜' 이 방식을 선택했는지 성능과 효율성을 따져가며 코드를 작성하고 리팩토링하는 개발 습관을 들여야겠다고 다짐했다.

728x90

'우아한테크벨로 > 프리코스 회고록' 카테고리의 다른 글

우아안 테크코스[프리코스] 최종 코딩 테스트 회고록  (1) 2026.01.23
우아한테크코스[프리코스] 5주차 회고록  (0) 2025.11.20
우아한 테크코스[프리코스] 4주차 회고록  (0) 2025.11.12
우아한 테크코스[프리코스] 3주차 회고록  (0) 2025.11.04
우아한 테크코스[프리코스] 1주차 회고록  (0) 2025.10.21
'우아한테크벨로/프리코스 회고록' 카테고리의 다른 글
  • 우아한테크코스[프리코스] 5주차 회고록
  • 우아한 테크코스[프리코스] 4주차 회고록
  • 우아한 테크코스[프리코스] 3주차 회고록
  • 우아한 테크코스[프리코스] 1주차 회고록
Bello's
Bello's
개발하는 벨로
  • Bello's
    벨로의 개발일지
    Bello's
  • 전체
    오늘
    어제
    • 분류 전체보기 (199) N
      • 노예 일지 (7)
        • 스타트업 노예일지 (3)
      • CS 이론 (81)
        • 학과 수업 (4)
        • 알고리즘 (64)
        • 시스템 프로그래밍 (3)
        • 데이터 통신 (1)
        • 운영체제 (2)
        • 데이터베이스 (1)
      • project (3)
      • 나는 감자다. (4)
      • Spring (27)
      • 모각코 (45)
        • 절개와지조(모각코) (7)
        • 어쩌다보니 박준태가 조장이조 (11)
        • 어쩌다보니 박준태가 또 조장이조 (12)
      • LikeLion🦁 (20)
      • 캘리포니아 감자 (4)
      • OpenSource Contribute (1)
      • 우아한테크벨로 (1) N
        • 프리코스 회고록 (6) N
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    절개와지조
    누적합
    그래프 순회
    백준
    Spring
    티스토리챌린지
    어렵다
    회고록
    8기
    BFS
    JPA
    모각코
    감자
    뛰슈
    프리코스
    자바
    DFS
    나는 감자
    타임리프
    오블완
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
Bello's
우아한 테크코스[프리코스] 2주차 회고록
상단으로

티스토리툴바