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

2025. 10. 29. 00:29·우아한테크코스 8기

1주차에서는 작성한 기능 중 핵심 비즈니스 로직을 어떻게 구현할 것 인지를 많이 고민했다면, 2주차에서는 Java 기본 기능에 대해 공부하고 전체 시스템에 적용하는 것에 초점을 맞췄습니다.

 

[가장 오래 고민했던 부분 - 컬렉션 프레임워크와 Stream API 사용]

저번 주차 과제에서 코드 리뷰를 하며 Set을 사용하여 커스텀 구분자가 기본 구분자와 중복되지 않도록 처리한 코드를 보았습니다. 이를 통해 컬렉션 프레임워크에 대해 정확히 이해하고 사용하면 효율적으로 데이터를 조작할 수 있을 것 같다는 큰 깨달음을 얻었습니다.

마침 공통 피드백에서도 ‘배열 대신 컬렉션을 사용한다’라는 피드백이 있어 이번 과제에 들어가기 전 컬렉션 프레임워크를 공부했습니다.

자동차 입력값을 분리할 때 어떤 자료구조를 선택하는 것이 적절할 지 고민을 많이 했습니다. 특히 “입력값에서 사소한 문제가 발생한다면, 어떻게 처리할 것인가?”를 많이 고민했었는데요

 

사소한 문제는

  • 자동차 이름과 구분자 사이에 공백이 있는 경우
  • 자동차 이름을 중복해서 입력한 경우

두 가지 상황을 떠올렸습니다.

 

로그인을 할 때 아이디 뒤에 엔터를 잘못 눌러도 로그인이 되는 것처럼, 이런 사소한 실수는 프로그램이 유연하게 처리해주는 것이 사용자 경험 측면에서 더 낫다고 생각했습니다.

 

먼저, 자동차 이름과 구분자 사이에 공백이 있는 경우는 중복된 데이터를 자동으로 제거해주는 Set을 사용하기로 했습니다.

그중에서도 입력 순서를 유지하는 LinkedHashSet을 선택했습니다. 이 자료구조를 사용하면 자동차 이름이 입력된 순서대로 객체를 생성할 수 있어서 우승자 출력 시에도 순서를 유지한 처리가 가능하다는 장점이 있습니다.

 

다음으로 자동차 이름 중복 처리 과정은 Stream API를 사용해 해결해줬습니다. Stream을 처음 사용해서 고생을 했지만 공식문서와 번역글을 따라가다보니 결국 해냈습니다. 새로운 도전은 항상 깨달음을 가져다 주는 것 같습니다. 문자열을 쉼표(,) 단위로 분리한 뒤, 각 이름의 공백을 제거하고(trim), 정제된 데이터를 LinkedHashSet에 추가하여 중복을 제거했습니다.

 

private final Set<String> carNames = new LinkedHashSet<>();

private void parseNames(String inputCarNames) {
    Arrays.stream(inputCarNames.split(","))
            .map(String::trim)
            .forEach(carNames::add);
}

[테스트 코드를 작성하며 배운 점 - AssertJ 학습하기]

테스트 코드를 작성하는 과정에서 AssertJ에 대해 새롭게 학습했습니다. AssertJ는 JUnit의 기본 어설션보다 더 강력하고 직관적인 어설션 기능을 제공하는 라이브러리라는 것을 알게 되었습니다.

 

1주차에는 null 여부나 공백 체크를 직접 값으로 입력해 테스트를 작성했는데, 이번에는 @NullAndEmptySource 어노테이션을 활용해 훨씬 깔끔하고 가독성 높은 테스트 코드를 작성할 수 있었습니다. 이런 방식으로 테스트를 작성하니 테스트 의도가 더 명확하게 드러났고, 테스트 코드를 읽는 사람도 이 함수가 어떤 경우를 검증하고자 하는지 이해하기 쉬워졌습니다.

 

@ParameterizedTest
@DisplayName("자동차 이름이 null 혹은 공백인지 검사")
@NullAndEmptySource
void nameNullOrEmpty(String carNames) {
     assertThatThrownBy(() -> new Cars(carNames))
             .isInstanceOf(IllegalArgumentException.class);
}

 

시도 횟수가 음수인지 검사하는 테스트 코드에서는 예외 메시지까지 구체적으로 검증하려고 했습니다. 그 이유는 에러 메시지 또한 사용자에게 피드백을 주는 일종의 인터페이스라고 생각했기 때문입니다. 테스트 코드에서 메시지를 함께 확인하면, 기능 동작뿐 아니라 개발자가 의도한 예외 처리 흐름이 올바르게 전달되고 있는지까지 검증할 수 있습니다.

 

    @Test
    @DisplayName("시도 횟수가 음수인지 검사")
    void tryCountsIsNegative() {
        // Given
        RacingGameService service = new RacingGameService();
        Integer tryCounts = -5;

        // When & Then: validateTryCounts 호출 시 예외 발생
        IllegalArgumentException exception = assertThrows(IllegalArgumentException.class,
                () -> service.setTryCounts(tryCounts));

        // Then: 예외 메시지가 올바른지 검증
        assertEquals("시도 횟수는 0보다 커야 합니다.", exception.getMessage());
    }

 

지난주 코드 리뷰를 하는 과정에서 하나의 클래스에 여러 메서드의 테스트 코드를 몰아 넣는 것은 가독성을 떨어진다는 느낌을 받았습니다. 그래서 이번에는 도메인별로 테스트 클래스를 분리하여 작성했습니다. 이렇게 분리하니 나중에 특정 기능의 테스트만 확인할 때 훨씬 수월했고, 테스트 대상이 명확하게 구분되어 유지보수성도 좋아졌습니다. 테스트 코드도 유지 보수성이 좋게 작성해야겠다는 깨달음을 얻었습니다.

 

 

[컬렉션 프레임워크를 사용하며 배운 점 - 항상 오버 엔지니어링을 경계하자]

이번 과제를 하면서 가장 크게 느낀 점은 오버 엔지니어링을 경계해야 한다는 것입니다. 새로 배운 기술을 적용하는 것은 좋지만 그 과정에서 문제의 본질을 벗어나지 않는 것이 중요하다는 걸 깨달았습니다.

예를 들어, 자동차 이름을 처리할 때 중복을 제거하기 위해 Set을 사용하는 것은 좋은 선택이었습니다.

하지만, 이후 Car 객체를 생성할 때, List로 자료형을 다시 변환해 도메인으로 넘겨주는 과정이 생기면서

결국 불필요한 데이터 변환 로직이 추가되었습니다. 기술 자체를 적용하는 데에 집중한 나머지 변수의 재사용성이나 도메인 간 데이터 흐름의 단순함을 충분히 고려하지 못한 것입니다.

‘배운 것을 적극적으로 써보자’는 마음이 과했던 탓에, 결국 코드가 복잡해지고 유지보수성도 떨어졌습니다. 이번 경험을 통해 기술은 적용하는 것보다 언제, 어디에, 왜 적용할 것인가를 판단하는 능력이 더 중요하다는 것을 배웠습니다.

앞으로는 새로운 기능이나 라이브러리를 도입하기 전에 “이게 정말 필요한가?”, “더 단순한 방법은 없을까?”를 먼저 스스로에게 물어보는 습관을 들이려고 합니다. (엉엉)

 

public void createCars(Cars cars) {
        List<String> validatedNames = cars.getValidatedCarNames();
        for (String name : validatedNames) {
            carList.add(new Car(name));
        }
    }

 

[2주차를 마무리하며 느낀 점]

이번 2주차 과제는 스스로도 아쉬움이 많이 남는 결과물이었다. 리팩터링을 반복하다 보니 1주차 회고에서 다짐했던 TDD를 제대로 적용하지 못했고, 학습 목표였던 테스트 도구의 적극적인 활용도 충분히 이루지 못했다. 그 점이 너무 아쉽고 속상했다.

 

그런데 오늘 우연히 본 웹툰 ‘도무지 그애는’에서 인상적인 장면이 있었다. 주인공의 친구가 글 공모전에 당선된 장면이었다. 그 친구는 제출하기 전까지 자신감이 없어 망설였다고 했다. 탈고를 여러 번 반복하면서 이런 형편없는 글을 정말 내도 될까 고민했지만, 그렇게 고치다 보면 끝이 없다는 걸 깨달았다고 한다. 그 이야기를 들은 주인공은 완벽을 추구하다 보면 끝이 없다는 말에 공감했다.

 

 

이 장면을 보며 개발도 똑같다는 생각이 들었다. 나 역시 완벽한 구조를 만들고 싶다는 욕심에 리팩터링을 계속하다 보니 끝이 보이지 않고 오히려 복잡해지는 순간이 있었다. 그때 주인공 친구의 말처럼 ‘여기까지면 됐다’고 스스로 정리할 줄 아는 결단력이 필요하다는 것을 깨달았다. 완벽을 향한 집착보다 지금의 한계를 인정하고 멈출 줄 아는 용기가 더 중요하다는 걸 느꼈다.

 

https://comic.naver.com/webtoon/detail?titleId=761496&no=140&week=fri

 

도무지 그애는 - 139화 - 완벽한 타이밍

 

comic.naver.com

+ 다들 한 번 읽어보세요.. 눈물 줄줄 샙니다..

 

모든 것이 완벽하게 준비된 순간은 절대 오지 않는다. 성장은 언제나 현재진행형이며, 언젠가 내가 지금 작성한 코드를 돌아봤을 때 미숙하다고 느낄 날이 분명히 올 것이다. 하지만 그것은 부끄러운 일이 아니라, 내가 성장했기에 가능한 감정일 것이다.

그래서 나는 이 회고를 쓰는 지금 ‘여기까지면 됐다’고 스스로에게 말하며 용기있게 코드를 제출한다.

개발적 지식보다도 개발을 대하는 태도와 마음가짐에서 더 큰 배움을 얻은 주였다.

'우아한테크코스 8기' 카테고리의 다른 글

[우아한테크코스 8기] 최종 코딩테스트 후기  (3) 2026.02.03
[우아한테크코스 8기] 1차 합격 했어요!  (1) 2025.12.30
[우아한테크코스 8기] 프리코스 오픈미션 회고  (0) 2025.12.02
[우아한테크코스 8기] 프리코스 3주차 회고  (0) 2025.11.10
[우아한테크코스 8기] 프리코스 1주차 회고  (0) 2025.10.21
'우아한테크코스 8기' 카테고리의 다른 글
  • [우아한테크코스 8기] 1차 합격 했어요!
  • [우아한테크코스 8기] 프리코스 오픈미션 회고
  • [우아한테크코스 8기] 프리코스 3주차 회고
  • [우아한테크코스 8기] 프리코스 1주차 회고
kcnsmoothie
kcnsmoothie
얼레벌레
  • kcnsmoothie
    청산가리스무디
    kcnsmoothie
  • 전체
    오늘
    어제
    • 분류 전체보기 (17)
      • 우아한테크코스 8기 (6)
      • 아인슈타인클래스 17기 (3)
  • 블로그 메뉴

    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    우아한테크코스
    CI/CD
    K8S
    우아한테크코스8기
    grpc
    docker
    Prometheus
    observability
    JVM
    ttrpc
    Grafana
    kata-container
    kvm
    아인슈타인클래스
    Java
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
kcnsmoothie
[우아한테크코스 8기] 프리코스 2주차 회고
상단으로

티스토리툴바