본문 바로가기

Hub Development/Woowacourse

[우아한테크코스] 우테코 6기 프리코스 3주차 피드백 정리

728x90

📜 우테코 6기 프리코스 3주 차


🔹 3주 차 프리코스의 미션은 로또 게임을 만드는 미션이었다. 미션을 진행하면서 내가 고민했던 부분과 코드 리뷰를 통해서 받았던 피드백 내용을 공유해 보고자 글을 작성했다.

 

GitHub - noxknow/java-lotto-6: 우아한테크코스 6기 로또 미션을 진행하는 저장소

우아한테크코스 6기 로또 미션을 진행하는 저장소. Contribute to noxknow/java-lotto-6 development by creating an account on GitHub.

github.com

 

➡️ 이번 미션에서는 프로그램 구조의 변화가 있었다. 먼저 구현 기능 목록을 작성했지만 2주 차 때 배운 wrapper 클래스를 적용하면서 목록의 변화가 필요하다고 생각했다. 이전 미션들에 service package를 활용했던 이유는 domain package에서 유효성 검사와 데이터의 저장, 비즈니스 로직까지 담당하게 된다면 클래스 하나의 길이가 너무 길어지기 때문에 domain과 service를 나눴다.

 

하지만, wrapper 클래스를 사용함으로써 객체의 응집도를 높이고, 데이터의 저장과 유효성 검사 로직을 처리하는 게 가능해졌고 domain package의 다른 클래스들은 비즈니스 로직의 역할만을 담당하면 될 것이라는 생각을 했다. 이러한 상황을 바탕으로 구현 기능 목록을 수정하게 되었고 기존의 service package에서 어려움을 겪었던 테스트 코드 작성 역시 한층 수월해진 결과를 얻게 되었다.

 

또한 이번 미션에서 시도했던 부분은 ConsoleInput의 간소화였다. 이전의 미션에서는 다른 타입으로 들어오는 input 마다 메서드를 만들고 입력에 대한 예외는 view에서 처리하도록 코드를 작성했다. 하지만, 이번 미션은 타입이 다른 input도 모두 하나의 메서드를 통해서 입력받고 그 입력을 wrapper 클래스 내부에서 예외 처리를 하며 타입을 변환하도록 구현했다.

이를 통해, 테스트 코드를 작성할 시 input에 대한 테스트 없이 wrapper 클래스 하나의 객체에 대한 테스트를 한다면 그 객체의 모든 유효성 검사를 한곳에서 확인할 수 있다는 장점을 알게 되었다.

👻 문제 해결 과정 중 고민 사항

 

ConsoleInput의 책임과 유효성 검사의 위치


🔹이번 미션에서는 ConsoleInput 클래스가 하는 일이 극단적으로 줄어들었다. 이전 미션까지는 ConsoleInput 내에서도 간단한 유효성 검사와 타입 변환을 통해 입력값마다 메서드를 두고 값을 받아왔지만 이번에는 모든 입력을 inputValue 메서드 하나에 의지하고 유효성 검사와 타입 변환의 책임을 domain package로 넘기도록 했다. 처음에는 InputValidator를 사용해서 view package에서 유효성 검사를 하더라도 테스트 시 불편함이 없도록 하는 것이 목표였지만, 이런 식으로 바뀌게 된 계기는 아래와 같다.

 

먼저, 입력에 대한 예외를 어떻게 처리할지 고민했다.

 

    ◈ 입력 예외를 ConsoleInput 클래스에서 InputValidator없이 처리한다.

 

➡️ 위와 같은 경우 ConsoleInput 클래스에 try - catch 문을 활용해서 예외를 처리하게 될 것이다. 하지만, 이렇게 된다면 다른 클래스에서 사용하지 않을 stringToInt와 stringToList 메서드를 public으로 선언해야 하며, 테스트를 진행할 시 view에 대한 테스트를 따로 진행해야 한다는 불편함이 있다고 생각했다.

 

      입력 예외를 ConsoleInput 클래스에서 InputValidator를 활용해서 처리한다.

 

➡️ 다른 경우와 비교했을 때 stringToInt와 stringToList 메서드를 private으로 지정이 가능하고, 테스트 시에도 유효성 검사를 하기 편하다는 장점이 있지만, 여전히 view package에 유효성 검사와 타입의 변환 책임이 있는 것 같아 다른 방식을 생각하게 되었다.

 

      입력 예외를 domain에서 처리한다.

 

➡️ domain에서 입력에 대한 예외를 처리하기 위해서는 ConsoleInput 클래스에서 List 혹은 Int 등의 타입으로 변환하지 않고 String 상태 그대로 Controller에 전달을 하게 되고, 이렇게 된다면 Controller에서 필요한 타입으로 변환해서 domain 객체로 전달을 해야 한다. 이때, 필연적으로 Controller 클래스에서 변환 과정에 대한 예외 처리가 필요하고 이는 Controller package의 목적과 맞지 않다고 생각했었다. 하지만, Controller에서 변환을 하지 않고 string 값 그대로 domain 객체로 들어가 그 안에서 예외 처리와 타입 변환을 한다면 테스트, SRP, 접근 제어자 등의 모든 부분을 해결할 수 있다고 생각했다.

 

🧐 Service package 사용의 변화


🔹 3주 차 미션에서는 저번 주차 미션과는 다르게 service package를 사용하지 않게 되었다. 처음 구현 기능 목록을 작성하면서는 service를 사용할 생각이었지만, 코드를 작성하면서 wrapper 클래스가 유효성 검사 부분을 담당한다면 service에 비즈니스 로직을 둘 필요 없이 domain 객체에 로직을 작성하면 된다고 생각했다.

 

👻 테스트의 중요성


🔹 미션 중 생겼던 실수를 테스트를 실행해 보면서 알게 되었다. ApplicationTest의 예외_테스트 메서드를 진행하면서 테스트 실패가 발생했고, 원인을 찾아본 결과 요구사항을 제대로 읽지 못했던 것이 문제가 되었다. 사용자가 잘못된 값을 입력할 경우 IllegalArgumentException를 발생시키고, "[ERROR]"로 시작하는 에러 메시지를 출력 후 그 부분부터 입력을 다시 받는다. 이 요구 사항의 가장 마지막 부분을 보면 에러 메시지를 출력 후 그 부분부터 입력을 다시 받는다 라는 부분이 있는데 이 요구사항을 평소와 같은 예외 처리 방식으로 처리하다 문제가 발생했다. 테스트 덕분에 이러한 사실을 알 수 있었고, 이번 주차에 지키지 못했던 기능 이후 바로 테스트 작성하기를 다음 미션에서 꼭 지켜야 하는 부분이라고 생각했다.


🧐 피드백 및 리뷰 내용

 

🧪 Enum을 통한 매직 넘버 관리


➡️ 다음은 첫 번째 피드백 내용이다. 이러한 피드백 내용에 따라 처음에는 enum으로 작성하지 않았던 ConstantsHandler 를 enum 클래스로 리팩토링을 진행했다.

 

🧪 재귀와 반복문


➡️ 3주 차 미션부터 잘못된 입력값을 받은 경우 그 부분부터 입력을 다시 받는다는 내용이 명시되어 있다. 이러한 요구사항을 해결하기 위해 재귀와 반복문을 고민했고, 그 중 반복문을 활용하여 코드를 구현했다. 반복문을 활용한다면 조건을 두고 반복을 빠져나올 수 있기 때문에 했던 선택이었는데 다른 분들이 서로 리뷰한 내용을 통해 재귀와 반복문에 대해 다른 의견도 알 수 있었다.

 

🔸 (리뷰어) : 재귀를 사용한다면 반복문을 제거해줄 수 있어서 가독성에 더 좋을 것 같아요!

물론 성능상은 조금 안좋지만 가독성 측면에서 이득을 취할 수 있습니다! 😁

 

🔹 (답변) : 좋은 말씀 감사합니다 : ) 사실 저도 처음에는 재귀로 구현했는데요, 자바 특성 상 1000번이 넘어가면 StackOverFlow가 발생하더라구요! 여러 예외 상황에 대해서 깊게 생각하다 보니 자연스럽게 while로 구현하게 되었습니다!

 

📸 참조