본문 바로가기
코딩 교육 기관/우아한테크캠프 프로

우아한 테크코스 프로 - 프리코스 1주차

by 성건희 2022. 10. 2.
반응형

좋은 개발자로 한 단계 더 성장하고 싶다는 니즈가 계속 있었는데, 운이 좋게도 해당 시기에 우아한 테크코스 프로 과정을 모집 중이어서 바로 신청했다.
지금은 프리코스 과정을 통해 과제 미션을 진행하고 있는데, 과제를 진행하면서 느낀 점을 적어보려고 한다.

느낀점

indent depth 를 2가 넘지 않도록 구현한다

과제를 진행하면서 요구되는 기본 사항이 있었는데, 그중에 depth를 1까지만 허용한다.는 규약을 지키기가 어려웠다.
사실 실무에서는 depth에 대해서 깊은 고민 없이 구현했었기 때문에 더 어려웠던 것 같다.
일반적인 상황에서는 메서드 분리를 통해서 depth를 줄일 수 있었지만, 아래 코드에서 고민을 정말 많이 했다.

Enum 의 값을 values() 로 순회하면서 state 가 일치하는 GameState enum 을 리턴하고, 해당하는 enum 이 없으면 예외를 던지는 코드다. if 문 때문에 depth가 2인 코드가 돼버렸다.
위 코드를 어떻게 메서드로 분리해서 depth를 줄일 수 있지? 를 계속 고민했다.
if 문을 따로 메서드로 분리하더라도 결국 리턴은 GameState 이므로, GameState가 있는지를 if 문으로 조건처리를 해야 한다. 결국 위와 다를 바 없어진다.

고민한 끝에 구조를 아래와 같이 바꿨다.

state를 key, GameState enum을 value로 갖는 Map 자료구조를 만들고, static으로 map에 넣어주도록 했다.

그 후 메서드 내에서는 map에서 state에 해당하는 enum을 가져오도록 수정했다.
만약 가져온 enum이 없다면 잘못 입력한 state 값이므로 예외를 던지도록 하였다.
위와 같이 수정하니 depth를 1로 줄일 수 있었다.
그런데 사실 depth를 줄이기 위해 Map을 추가할 필요가 있을까? 오히려 코드 자체는 depth 2일 때가 더 깔끔한 것 같은데.. 라는 생각도 들었다. 🤔

우테코 Console 테스트 코드 분석

우테코에서 만든 라이브러리를 통해서 테스트 코드가 작성된 부분이 있었는데,
어떻게 console 입력을 테스트 할 수 있는 걸까? 하는 궁금증에 내부를 분석해보았다.

위 dependency를 추가하면 assertRandomNumberInRangeTest 메서드를 통해 console 입력 테스트를 할 수 있었다.

해당 테스트 코드로 System.out.print 에 찍히는 내용을 검증하는 것인데, 내부를 들어가보자.


첫번째 파라미터에서는 Mockito 로 pickNumberInRanage 메서드를 사용했을 경우의 Verification 을 넘긴다.


assertTimeoutPreemptively 메서드를 사용하고 있는데, 해당 메서드는 timeout 시간이 지나면 테스트를 실패시키며 종료시키는 메서드다.
참고 ) 다른 메서드로 assertTimeout 메서드가 있는데, 실행할 메서드를 전부 완료한 뒤에 시간을 비교하기 때문에 assertTimeoutPreemptively 에 비해 느리다는 단점이 있다.

pickNumberInRanage 메서드를 사용하면 return 으로 value 와 values 를 넘기는데

stubbing 의 내부를 들어가보니 stubbed 라는 LinkedList 변수가 있어 값을 계속 추가해주고 있었다.
결국 pickNumberInRanage 메서드를 실행할 때마다 1, 3, 5, 5, 8, 9 값이 순차적으로 하나씩 나오는 구조이다.
그런데 테스트 코드에서 value = 1, values = 3, 5, 5, 8, 9 로 지정되어 있어서, 처음 코드를 봤을 때 value 와 values 를 따로 처리하는건가?
라고 생각했는데 하나의 LinkedList 로 처리되어서, 코드 내부를 들어가봐야지 이해가 되었던 점이 조금 아쉬웠다.
아마도 thenReturn 메서드가 2가지 밖에 없어서 이렇게 처리하신듯 싶다.

OngoingStubbing<T> thenReturn(T value);
OngoingStubbing<T> thenReturn(T value, T... values);

mock 으로 선언해준뒤 executable.execute(); 를 통해 assertRandomNumberInRangeTest 메서드에서 첫번째 파라미터를 람다로 지정했던, run() 과 assert 문을 실행하게 된다.



command 메서드를 통해 입력값을 세팅한다.
이렇게 하면 Console.readLine() 메서드를 호출할 때 마다 args 값에서 하나씩 꺼내온다.
그 후 runMain 메서드를 통해 Application.main 메서드를 실행하는 구조이다.


다시 처음 테스트 코드로 돌아가보자.

output 메서드는 아래와 같이 printStream 의 내용. 즉, System.out.print 를 실행하는 텍스트가 captor 에 쌓이게 된다.

captor.toString() 호출 시 아래 내용이 포함되게 되어 contains 로 지정한 값이 포함되는지 체크하게 된다.

숫자를 입력해주세요 : 낫싱
숫자를 입력해주세요 : 3스트라이크
3개의 숫자를 모두 맞히셨습니다! 게임 종료
게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요.
숫자를 입력해주세요 : 1볼 1스트라이크
숫자를 입력해주세요 : 3스트라이크
3개의 숫자를 모두 맞히셨습니다! 게임 종료
게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요.

그런데 단순히 텍스트가 포함되었는지만 체크하므로 실제 게임 순서에 해당하는 텍스트인지는 검증이 힘들다는 단점이 있을 것 같다.

코드 라인을 띄우는 기준의 모호함

실무에서 개발을 할 때도 어느 기준으로 라인을 띄워야 할까? 는 항상 고민이었던 부분이었다.
예시 코드를 살펴보자.

위 코드를 정리하면 다음과 같다.

  • computer : 변수 선언
  • playGame() : 비즈니스 로직 수행
  • inputWhetherRestartGame() : 재시작 여부 입력

각 역할이 다르기 때문에 아래와 같이 라인을 띄웠다.

나의 경우에는 이런식의 기준으로 라인을 띄우는데, 더 나은 방식은 무엇일까? 이렇게 해도 되는걸까? 에 대한 고민이 계속 있었다. 🤔

메서드의 라인수는 10라인을 넘어가면 안된다

나는 게임 결과 화면 출력을 OutputView 에서 담당하도록 구현했다.

printGameResult 메서드를 통해서 1턴의 게임 결과를 출력하는데, 메서드 라인이 10라인을 넘어가버렸다.
그래서 게임이 끝났을 때와 게임이 진행중인 상황으로 분리를 했다.

isPlayingGame 메서드 안에서 if 문 안에 print 문을 출력하게 되면 10라인이 넘어가버리게 된다.
그래서 각각의 경우를 메서드로 또 분리했다.

그런데 위처럼 하면 문제가 있다.
strike 와 ball 을 체크하는 역할과 결과를 출력하는 역할. 즉 한 메서드에서 2가지 역할을 하게되는거 아닐까?
코드를 처음보는 개발자가 isNotMatch 메서드만 봤을 때, 매치되는 값이 있는지 체크하는구나 라고 생각했는데,
출력도 된다면 혼동이 올 수 있을 것 같다는 생각이 들었다..
메서드가 하나의 일만 수행하게 하기위해 10라인을 넘어가면 메서드로 분리하게 했는데, 오히려 2가지 일을 하게 되버린 느낌..?
그런데 이렇게 안하면 어떻게 10라인 안으로 짤 수 있는거지? 라는 생각도 들어서 고민이 많았다.. 저런 경우는 어떻게 해결해야 하는걸까?

Service 비즈니스 로직

나의 경우는 프로젝트 구성을 Spring MVC 구조 처럼 만들어서 처리했다.
그래서 아래처럼 게임을 진행하는 비즈니스 로직은 service 단에서 처리하도록 했다.


게임 결과를 나타내는 비즈니스 로직은 GameResult 객체를 생성하면서 처리하도록 구현했다.

여기서 고민이 되었던 부분은, 서비스 단은 로직이 하나도 없이 객체 생성만 하고 리턴하는 형식으로 해도 되는건지 의문이 들었다.

이름 짓기

이름 짓는 것에도 고민이 정말 많았는데, 예를 들면 야구 게임 숫자에 대한 부분을 BaseballNumber로 지을지, Number로 지을지 고민이었다.
모든 것에 prefix로 Baseball을 넣게 되는 경우가 많았는데, 어차피 최상단 패키지가 baseball인데 이름 앞에 Baseball을 넣는게 맞을까? 라는 생각이 들기도 했다. 추후에 다른 기능이 추가될 수 있으니 Baseball을 붙이긴 했는데 정답을 잘 모르겠다.

정리

테스트 코드에서 @ParameterizedTest 어노테이션이 있다는 건 몰랐는데, 테스트 코드를 짜면서 새롭게 알게 된 부분이 많아 좋았다.
우테코에서 지정한 컨벤션 요구사항 제약이 있어서 그걸 지키기 위해 메서드 분리하는 연습을 많이 했던 것 같다.
사실 현업에서는 이렇게 극단적으로 메서드를 분리한 적은 없었는데, 이번 기회에 많이 배웠던 것 같다.
힘들긴 했지만 되게 재미있고 유익한 시간이었다. 느낀점에서 남긴 궁금증에 대한 힌트를 과정이 끝나고 얻을 수 있으면 좋겠다.

미션 코드

참고

반응형

댓글