공부방/프리코스

우테코 프리코스 최종테스트 후기

EVO. 2023. 12. 20. 19:14
우테코 6기 4주간의 프리코스 이후 운좋게도 1차 합격 후 최종 코딩테스트를 보게되었다.

결론적으로 말하면 테스트코드를 통과하지 못했고 0점이기에 탈락이 거의 확정된 상태이다.(요구사항도 다 못지켰다)
탈락인데 쓸까말까 고민하다가 멘탈 회복. 그리고 그때 실수 했던 경험을 토대로 다시는 실수를 번복하지 않겠다는 의지와 머리 속에 복잡한 생각을 정리하기 위해 회고해본다.

 

이번 최종 코딩테스트의 문제는 온-콜 미션이었다.(문제는 아래에)

 

테스트코드 통과 못했던 이유

13시 30분에 시험이 시작되고 6기 4주차 크리스마스 미션 처럼 템플릿을 받아와 main 브랜치에 작업을 하는 형태로 시작했다. 이전 기수 시험과 다른 점은 how-to-solve 문서도 작성해야 하는 것인데, 핵심 로직을 어떻게 이해하고 구현을 했는 지 적는 문서를 필수적으로 작성해야 한다.

 

원인.  IndexOutOfBoundsException

 

모든 구현을 완료했다고 생각했을 때, 약 1시간의 여유가 생겼다. 당시에는 각각의 테스트 코드를 개별적으로 실행했을 때 문제가 없었다. 하지만 이들을 통합하여 실행했을 때, 앞서 언급한 예외가 발생했다. 이때, 디버깅을 통해 신속하게 오류를 해결할 수 있을 것이라고 생각했다. 문제가 발생한 것으로 예상되는 로직 부분에 집중하여, 그 부분에 빨간색 다트(중단점)를 여러 개 설정했다.

 

 

하지만 위 에러메세지와 같이 타임아웃 문제로 인해 디버깅이 생각처럼 쉽지 않았다. 결국, 테스트 코드의 디버깅을 포기했다. 4주간의 프리코스 기간 동안 로직 부분에만 집중하여 문제를 해결했기 때문에, 테스트 코드에서 오류를 경험한 적이 거의 없었다. 이로 인해 이러한 상황에 대한 충분한 연습을 하지 못한것은 내 부족함이다. 결론적으로 말하면 테스트코드를 디버깅 하는 것은 우테코 코드로는 불가능 한 것 같다(하지만 해결법이 존재한다). 그 이유를 살펴보면 다음과 같다. 

 

AssertionFailedError 뜯어보기

테스트코드는 다음과 같다.

 

우아한 테크코스에서 제공하는 assertSimpleTest라는 static 메서드는 두 개의 인자를 가진 assertTimeoutPreemtively라는 메서드를 호출한다. 아래와 같이 두번째 인자에는 함수형 인터페이스로 람다식을 받고 있고, 첫 번째 인자는 Duration.ofSeconds(1L)이라는 Duration 변수를 받고 있다.

 

그래서 이 SIMPLE_TEST_TIMEOUT이 어떻게 수행되고 있는 지 계속 들어가 보면 다음과 같다. 

 

두 번째 인자인 supplierExecutable이다. 즉, 테스트할 람다식이라고 볼 수 있다. Future 클래스는 비동기 작업의 결과를 표현하는 데 사용된다. 이 클래스는 처리된 데이터를 다른 스레드에 전달할 수 있는 장점을 가지고 있으며, synchronized 블록을 사용하지 않아도 되는 이점도 있다.

 

future.get() 메서드는 앞서 submit 메서드를 통해 전달된 Callable 함수가 비동기적으로 수행이 완료되어 값을 반환할 때, 그 값을 반환해준다. get 메서드에는 앞서 설정된 timeout 값인 1L (즉, 1000ms)을 인자로 함께 전달한다. 만약 이 timeout 기간 내에 실행이 완료되지 않으면, TimeoutException이 발생하며 에러 메시지가 출력된다.

 

이를 토대로 알 수 있는 점은 디버깅을 수행할 때 우테코에서 1000ms라는 타임아웃을 설정해서 무조건 테스트코드가 1초안에 실행되도록 테스트하게 구성했다.

 

이제 대충 코드가 파악됐으니 우테코 코드를 거치지 않고 디버깅을 수행하는 방법은 다음과 같다.

우테코 테스트코드 디버깅 하는 방법

ApplicationTest 클래스를 복사했다. 그 후 Assertions.assertTimeOutPreemptively 메서드 호출로 수정하면 디버깅을 하루종일 수행해도 타임아웃 예외가 나지 않는다.

 

 

원인 해결 방법.

앞에서 디버깅을 해결했으니 정확한 원인 파악이 가능하다. IndexOutOfBounds는 보통 배열에 넘는 인덱스를 건들였을 때 일어난다. 배열을 쓰는 의심가는 곳에 빨간색 다트를 찍었다. 그리고 이상한 점이 발견되었다.

첫번째 테스트코드가 끝나고 다음 두번째 테스트가 처음 실행되고 있는 데 idx가 21를 가르키고 있다. 하지마 원래 무조건 0을 가르켜야 한다. 이 부분에서 원인을 발견할 수 있었다. 그리고 사원의 순서가 인덱스 끝에 도달하면 다시 0으로 돌아가야 하는 데 그냥 무조건 idx를 믿고 컬렉션의 인덱스를 접근하고 있다. 여기서 두번째 원인이 발견할 수 있었다.

 

두번째 원인은 다시 사원의 수에 도달하면 0으로 돌아가도록 구현하면 된다. 이 부분은 당시에 로직을 살펴보며 해결했었다.

 

 

그리고 첫번째 원인은 idx가 초기화되지 않고 다른 테스트코드에도 영향을 주고 있었다. 여기서 크게 실수한 부분이다. 

 

이렇게 사원 인덱스를 가르키는 변수를 static으로 선언한 것이다. JUnit5에서 테스트 클래스 내의 static 변수는 모든 테스트 메서드에서 공유된다. Junit 테스트 실행동안, 테스트 클래스의 인스턴스는 각 테스트 메서드 마다 새로 생성되지만, static 변수는 클래스 레벨에서 한 번만 초기화되고 모든 인스턴스에서 공유된다.

 

조금만 생각해본다면 당연한 사실이었지만, 당시에는 당황스러워서 이에 도달하지 못했다. 정적 변수를 생성하는 것 자체가 어이없는 일이었다. 말도 안 되는 코드를 무의식적으로 작성했던 것이다. 다음 글을 2주 전에 읽었음에도 불구하고, 완전히 잊고 있었다.

 

5시간 동안 못 지킨 것

5시간동안의 구현에서 못지킨 것은 다음과 같다.

  • 테스트코드 실패 
  • 근무자 닉네임은 최대 5자 예외처리 구현 
  • 평일이면서 공휴일일때만 (휴일) 출력 (공휴일이면 무조건 (휴일)이 출력하도록 구현했다. 다시 검토했으면 해결했을 문제.)
  • 중복으로 인해서 근무자의 순서가 바뀌면 출력되지 않았던 근무자 다음에 출력 (이전 근무자를 ArrayList에 저장하여 꺼내서 비교하고 닉네임이 중복이고 휴일->평일 혹은 평일->휴일로 넘어가면 다다음 근무자를 꺼내서 이전근무자인 ArrayList에 저장하는 식으로 구현했다. 하지만 이렇게 하면 다다음 근무자가 또 꺼내지는 상황이 생긴다. 이렇게 말고 근무자들을 큐에 넣어서 꺼내는 방식이 제일 괜찮은 풀이 같다.) 
  • 매직넘버 사용과 메서드 15라인 넘어가는 로직 존재 
  • 실력이 준건지 시간뗌에 그런건지 알 수 없는 형태의 서비스 설계
  • 테스트 코드 미작성

 

앞으로는.

후회되는 최종 시험이었지만, 그것은 내 실력이 그대로 드러난 시험이었다. 앞으로 더 많은 연습과 개선이 필요해 보인다.

 

 

탈락이지만 부족한 점도 알고 있고 앞으로 공부하고 싶은 것도 많아 금방 아쉬운 마음을 털어낼 수 있었다.