2주차 과제가 끝났다. 2주차 과제에서는 앞선 과제의 피드백을 최대한 반영하고자 노력했다. 과제는 자동차 경주 시뮬레이터를 구현하는 것으로, 주어진 시도 횟수만큼 랜덤한 값을 통해서 자동차를 전진시켜 가장 멀리 이동한 자동차가 우승자가 되는 프로그램을 만드는 것이다. 이때 우테코에서 제시한 기본적인 기능 요구 사항은 다음과 같았다.

기능 요구 사항

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

내가 정의한 예외 상황

  • 시도 횟수 입력 범위 설정

위 요구사항을 고려했을 때 몇몇 의문인 점이 있었다. 앞선 1주차 과제에 대해서는 입력되는 숫자의 값이 매우 커도 이를 다룰 수 있는 숫자의 범위를 고려해야 했다면, 이번 과제에서는 입력값이 과도한 경우에는 어떻게 처리해야 할지에 대한 의문이 있었다. 나는 이 의문을 최대 시도 횟수가 100을 초과하면 오류가 발생하도록 설계했다.

  • 유효한 자동차 이름

자동차 이름을 구분자로 파싱하고 유효성을 검증할 때 한글을 어떻게 처리할 것인지 고민했었다. 나는 이를 완전한 한글(’가’, ‘남’ 같은 자모의 완전한 조합)은 허용하는 것으로 정의했다. 즉 ‘caㅏ’는 오류가 발생하지만 ‘짱car’ 같은 허용된다.

위 두 상황을 포함하여 예외를 다음과 같이 정의하여 표로 명시했다.

코드 상황 예시 오류 메시지
V1 아무런 입력이 없거나 공백 입력 "", " " -
V2 한 자동차의 이름이 아무런 입력이 없거나 공백 입력 "", pobi, " ", pobi -
V3 5글자를 초과한 입력 sehako, pobi -
V4 한글과 알파벳을 제외한 특수 문자 또는 공백 입력 seh2k, p.obi, j un -
N1 아무런 입력이 없거나 공백 입력 "", " " -
N2 소수점 입력 1.5 -
N3 숫자가 아니거나 문자와 혼용하여 입력 five -
N4 음수 입력 -5 -

회고를 적는 이 시점에 깨달은 건데 내가 정의한 오류 메시지를 개발 완료 이후에 표기해두는 것을 깜빡했다… 아무튼… 이번 버전은 코드를 좀 구체화 하였다. 자동차 이름 관련된 건 Vehicle의 V를, 시도 횟수 관련된 것은 Number의 N을 이니셜로 활용했다.

회고

Keep

팩토리 패턴 적용

메인 클래스에서 여러 객체를 생성하여 컨트롤러를 구성하다보니 메인 클래스의 코드가 너무 더러워졌다. 나는 이 부분을 컨트롤러를 만드는 팩토리를 설계하여서 메인에서는 팩토리를 만들고, 여기서 컨트롤러를 생성하여 넘겨주는 방식으로 처리하니 메인 클래스가 깔끔해졌다.

컬렉션 사용

저번 주차에서는 입력값 파싱 이후 값들을 배열로 관리했지만 공통 피드백에서 컬렉션을 활용하면 다양한 API를 활용하여 데이터를 조작할 수 있기에 컬렉션 사용을 지향하라는 피드백을 받았다. 이 부분을 참고해서 파싱 이후에 값들을 List 형식으로 관리하였다. 이렇게 관리하니 확실히 바로 컬렉션에서 stream() 메서드를 호출해서 검증이나 변환 등 다양한 처리를 할 수 있었다.

Problem

예외 메시지 미작성

개발이 끝난 후 리드미에 오류 상황에 대한 예외 메시지를 작성하는 것을 잊었다. 항상 마감 전 문서를 검토하도록 하자.

중복값 입력 처리

자동차 이름이 중복되는 경우에 대해서 고려하지 못했다. 이 부분을 Set<String>으로 관리했다면 오류 처리가 가능했을 것 같다.

일급 컬렉션 미사용

나는 파싱 이후 자동차를 만드는 과정에서 List<RacingCar> 객체를 그대로 사용했는데, 이렇게 단순한 컬렉션으로 관리하면 해당 객체를 담은 리스트가 어떤 역할을 하는 지 모른다. 이 부분 1주차 공통 피드백을 통해 알았던 내용이었는데,

잘못된 클래스의 역할 설정

입출력 클래스를 설계할 때, 단순히 ‘콘솔에 값을 출력한다’는 의미로만 간주해서 너무 책임 범위가 작게 설정된 느낌이다. 실제로 이 부분을 코드리뷰에서 많이 피드백 받았다.

또한 개인적으로 시뮬레이터가 과연 생성 시점에 외부로부터 시뮬레이션에 필요한 값들을 주입받아 필드로 관리해야 하는건가? 생각이 들었다. 실생활에서도 어떤 시뮬레이터를 실행할 때 사용자가 필요한 값들을 입력하지 않나? 생각이 들어서 차라리 메서드 인자로 전달했어야 하나 싶었다.

현재 구조는 항상 외부로부터 입력을 받는 애플리케이션으로 확장한다면 매번 시뮬레이터를 생성하는 구조이므로 낭비로도 보여졌다. 도메인 위주로 생각했어야 했는데 패턴 위주로 설계를 생각해서 발생한 문제라고 보인다.

애매한 커밋 범위

검증에 대한 부분을 리스트에 대한 들여쓰기로 처리하다보니 커밋 범위가 들여쓰기된 리스트는 모두 포함하는 방식으로 개발했는데 이러다보니 새롭게 추가된 예외 처리에 대해서 feat로 하다보니 좀 이상했다. 그렇다고 refactor나 fix는 또 아닌 것 같아서 결국 feat로 작성하긴 했는데 통일감이 없었다.

Try

역할을 고려하자

클래스가 어떤 책임까지 담당할지 역할을 항상 생각하면서 설계하도록 하자. 객체지향을 항상 설명할 때 자동차를 설명하듯이, 나도 실생활에서 비슷한 책임을 수행하는 것들을 생각하면서 이 객체는 무엇이고 어떤 것을 해야 하는지 적어서 정리하겠다. 그리고 이를 기반으로 필요할때만 디자인 패턴을 적용하거나 정적 클래스 또는 유틸 클래스로 설계를 결정하겠다.

그리고 다음 과제에서는 View는 오직 표현만, Domain은 오직 로직만, Controller는 흐름만 담당하도록 설계하겠다.

객체지향 생활체조 원칙을 준수하자

객체지향 생활체조 원칙은 다음과 같다.

  • 한 메서드에 오직 한 단계의 들여쓰기(indent)만 한다.
  • else 예약어를 쓰지 않는다.
  • 모든 원시 값과 문자열을 포장한다.
  • 일급 컬렉션을 쓴다.
  • 한 줄에 점을 하나만 찍는다.
  • 줄여 쓰지 않는다(축약 금지).
  • 모든 엔티티를 작게 유지한다.
  • 3개 이상의 인스턴스 변수를 가진 클래스를 쓰지 않는다.
  • getter/setter/프로퍼티를 쓰지 않는다.

모든 부분이 부족하다고 생각하지만 특히나 지금까지의 과제에서 일급 컬렉션을 항상 고려하지 못했던 것 같다. 따라서 여러 도메인에 대해서 복수형을 붙여서 일급 컬렉션으로 관리하겠다.

오류 메시지를 리드미에 미리 명시하자

1주차 떄 오류 메시지를 리드미에 미리 명시해도 수정을 조금씩 하다보니 차라리 개발 이후 리드미에 한 번에 작성하자고 방향을 바꿔봤는데 바로 휴먼 에러가 일어났다. 리드미를 작성할 때 오류 메시지를 미리 명시해두고 이에 따라서 개발을 하도록 해야겠다.

커밋 범위에 예외도 추가하자

코드 변경사항이 작더라도 예외 하나하나에 대해서 feat 커밋으로 설정하여서 나중에 예외를 추가해도 feat로 묶어 통일성 있게 커밋 범위를 관리할 수 있게 만들려고 한다. 그리고 예외에 대한 feat는 리드미에 작성하는 오류 코드를 적극적으로 활용해야겠다.

회고 외 고민사항

검증에 대한 정적 클래스 설계

저번 주차에서는 검증에 대한 클래스 설계 시, 내부적으로 private static 메서드가 여러 개 생성되기 때문에 검증기를 인스턴스 클래스로 설계했었다. 이번에는 반대로 상태가 없다는 측면에 집중해서 정적 메서드만을 가진 유틸 클래스로 설계하긴 했는데 잘 모르겠다. 정책의 변동 가능성 측면에서 바라보아야 하는걸까?

내가 정의한 오류에 대한 고민

나는 명시되지 않은 최대 시도 횟수에 대해서 100을 초과하지 못하게 설계했지만, 차라리 int 범위 내에서는 허용되게 했어야 했나? 생각이 들기도 하다. 특히 이 부분에 대해서 왜 꼭 기준이 100이지? 라는 질문에 대해서 막연하게 ‘매 라운드의 진행상황이 출력되는데 100은 너무 커서 UX가 좋지 않을 것 같은데…’같은 생각으로 100으로 설정해서 설득력이 떨어지는 것 같다.


이런저런 일들을 하다보니 회고가 좀 늦었다. 항상 좋은 설계를 하고 싶지만 참가자분들과 코드 리뷰를 주고받다보면 내가 간과했던 부분이나 부족했던 부분을 발견하는 것 같다.

참고자료

객체지향 생활 체조 원칙 9가지 정리!

일급컬렉션의 정의와 필요성

카테고리:

업데이트:

댓글남기기