오늘은 거진 하루 종일 웹 계층을 테스트했다. 어제 하루를 정리하고, 목표한 웹 계층의 개발을 해 나갔는데, 어제까지 내가 개발한 것은 하나의 유스케이스(카테고리 조회)를 제외하고 다른 유스케이스를 모두 개발하고서 잠에 들었었다. 개발하지 못하고 잠든 유스케이스는 입력값을 어떻게 받아야할지 감이 잡히지 않아, 일단은 자고(금새 시간이 한시 반을 넘어가고 있었다..) 내일 고민해보기로 하였었다.
웹 계층 개발
아침에 일어나, 오늘은 독서를 스킵했다. 알고리즘 문제풀이도 먼저하지 않고 잠시 미뤘다. 어제 개발하다만 문제를 빨리 해결하고 싶었다.
사실 어제 침대에 누워 하루를 돌이켜보며 생각해보니 나는 지금까지 웹계층에 대한 테스트를 작성해본적이 없었다. 웹 계층(API) 까지 개발하면, 실제 디버그 서버를 가동하고, 테스트를 했던것이 전부이다. 그리고 나는 그것을 E2E 테스트로 생각했다.
하지만 이게 잘못됬음을 나는 이제서야 깨달았다. 아니 알고서도 몰랐던 것같다.(왜지?) 그래서 나는 어제 못한 기능 개발을 하기에 앞서, 테스트를 작성해보고자 하였다.
그런데 또 생각을 해보니, 나는 하나의 Product 컨트롤러를 사용하는데, 이 컨트롤러에는 Command 를 처리하는 메서드와 Query를 담당하는 메서드가 공존했다. 나는 서비스도 Command 와 Query 로 나누었는데, 컨트롤러도 같은 기준으로 나누는 것이 올바른 것이 아닐까? 하는 생각이 들어 Command 와 Query 로 컨트롤러를 세분화 하였다.
드디어 나는 첫 웹계층의 테스트를 작성하기 시작했다. 그리고 문제가 시작되었다.
어노테이션
나는 스프링을 공부하면서, 가장 어려우면서도 유용하다 생각한 것이 어노테이션이다. 어노테이션은 모르면 끝없이 어렵고, 알면 너무나도 유용하다.
나는 지난 책을 독서하면서, 웹계층의 테스트에서 사용하는 어노테이션(@WebMvcTest)을 본 경험이 있어 처음에는 해당 코드를 참고했다. 그리고 이를 템플릿으로, 어떻게 테스트를 작성하면 좋을지 GPT에게 도움을 요청했다. 근데 왠걸? 다른 어노테이션으로 테스트를 바꿨다.
@AutoConfigureMockMvc Vs @WebMvcTest
이 둘은 이름도 비슷한데, 무슨 차이가 있는걸까.. 정말 궁금했다. 그래서 검색을 했다.
참고 블로그
#3-3_ 스프링부트 테스트코드 작성시 @WebMvcTest 와 @AutoConfigureMockMvc 의 차이점
스프링부트 테스트 코드를 작성할 때 java.lang.NullPointerException이 떴다. 그래서 @WebMvcTest 을 @AutoConfigureMockMvc으로 변경해줬다. 그렇다면 @WebMvcTest 와 @AutoConfigureMockMvc은 무엇일까? 우선 이 둘은 MockMVC
we1cometomeanings.tistory.com
@WebMvcTest | @AutoConfigureMockMvc | |
공통점 | 웹 애플리케이션에서 컨트롤러를 테스트 할 때, 서블릿 컨테이너를 모킹하기 위해서는 @WebMvcTest 또는 @AutoConfigureMockMvc를 사용하면 된다. | |
차이점 | - 웹에서 테스트하기 힘든 컨트롤러를 테스트 하는데 적합 - 웹 상에서 요청과 응답에 대해 테스트할 수 있을 뿐만 아니라 시큐리티 혹은 필터까지 자동으로 테스트하여 수동으로 추가/삭제 가능 - 일반적으로 @MockBean 또는 @Import와 함께 사용되어 @Controller 빈에 필요한 협력자를 생성한다. - 여러 스프링 테스트 어노테이션 중 Web에 집중할 수 있는 어노테이션 - 선언할 경우 @Controller,@ControllerAdvice등을 사용 가능 - 단, @Service, @Component, @Repository 등은 사용 불가 |
- @WebMvcTest와 비슷하게 사용할 수 있는 어노테이션이다. - @WebMvcTest와 가장 큰 차이점은 컨트롤러 뿐만 아니라 테스트 대상이 아닌 @Service나 @Repository사 붙은 객체들도 모두 메모리에 올린다. - 간단하게 테스트 하기 위해서는 @AutoConfigureMockMvc가 아닌 @WebMvcTest를 사용해야 한다. - MockMVC를 보다 세밀하게 제어하기 위해 사용하며,전체 애플리케이션 구성을 로드하고 MockMVC를 사용하려는 경우 @WebMvcTest보다 @AutoConfigureMockMvc와 결합된 @SpringBootTest를 고려해야 한다. |
둘다 MockMVC를 제어하는 어노테이션이라고 한다. MockMVC..? (이것도 뭔지 잘 모르겠다. 모르는게 너무많다..) 근데 무튼 간단하게는 둘의 차이를 알 수 있었다. 나는 테스트를 진행하는데 있어, SpringBootTest 어노테이션을 통해 TestConfig 설정을 사용한다. 그래서 WebMvcTest 는 사용할 수 없었다.
시작에서부터 모르는게 많았지만, 문제는 이제 시작이었다.
내가 지금 테스트를 작성하면서, 조회하는 Product 가 존재하지 않는다 는 에러 를 기대했다. 무엇보다 이렇게하면, 내가 개발한 API로 요청이 가나? 테스틀르 하고싶었다. 하지만 예상과는 전혀 다른 에러가 발생했다.
어..... 어.... 이게 뭐지..? acceptable representation 을 왜 찾지..? 그리고 또 왜 못찾지..? 아주 혼돈 그 자체였다. 그래서 나는 이전의 방식대로, 서버를 키고 포스트맨을 통해 테스트를 진행해보았다.
이상하다.. 포스트맨에서는 정상적으로 동작하는데.. 이때부터 내가 테스트를 작성하는 방법이 잘못됬나? 하는 생각이 들기 시작했다.
GPT의 조언은 산으로(?) 가기 시작했고, 나는 어떻게 웹 계층을 테스트를 해야하는가... 에 고민하게되었다. 하지만 내 혼자만의 고민만으로는 전혀 앞으로 나아갈 수가 없었다. 이렇게 하다보니 점심시간 먹을 시간도 지나서, 그냥 리프레시겸 운동을 다녀왔다.
운동을 다녀온 뒤에는, 웹 계층에 대한 테스트를 작성하는 것보다, 어제 개발하지 못한 부분을 먼저 해보자 라는 생각으로 바꾸었다. 이것이 정답은 아니지만, 오답도 아니라는 생각에 이렇게 개발하기로 하였다.
리스트를 조회하는 쿼리의 작성: 카테고리별 조회
TrackProductListQuery 는 필드 값으로, ProductCategory 리스트를 입력받는다. 나는 여러 카테고리에 해당하는 상품을 조회할 수 있도록 만들고 싶었다. 그래서 입력값으로 ProductCategory 리스트를 입력받고, 이를 활용하여 유스케이스를 구현하고자 하였다.
이제 해당 기능의 진입점(API) 를 개발하면되는데, 리스트 값을 어떻게 입력받아야하지? 하는 생각에 잠시 멈추게 되었다.
나는 방법을 2가지로 생각했다.
- PathVariable로 받는다. ex. /track/SHOES, CLOTH
- JSON으로 배열 값을 받는다.
나는 어느게 맞는것인지 잘 모르겠다. 테스트로 시나리오를 만들고 개발했다면 수월했을텐데.. 둘다 해본다는 생각으로 일단 1번을 먼저 선택하여 구현해보았다.
근데 지금 글을 작성하면서 보니, 문제점이 보인다.. 내일 개선하거나, 2번으로 개발해야겠다..
- products/track/공백 일경우, 에러처리가 안된다. (ControllerAdvice에서 다루지 않는 에러이다..)
- productCategoryList 를 만드는데 있어서, 없는 ProductCategory 로 입력이 들어올경우 처리가 없다.(나는 뒷단의 유스케이스를 다루는 Handler 에 해당 예외처리를 구현했다.)
그리고 저녁이되고서 아까 테스트의 문제점을 다시 해결해보고자 했다. 하도 많은 것을 해서, 지금 약간 앞으로 전진할 수 있었던 일을 이야기한다면, 지금 에러와 내가 하고싶은 것을 지피티에게 무진장 막 넣었다. 그리고 번역하고, 고민을 반복 하면서 한가지 힌트를 얻었다.
Unsupported Media Type: The client may be requesting a different media type (content type) in the "Accept" header of the request, which the server cannot provide. For example, the client might be requesting "application/json" but the controller is configured to produce "application/vnd.API.v1+json". In this case, the server cannot find a match, leading to the error.
Missing Implementation: There might be a missing implementation for handling the custom media type "application/vnd.API.v1+json". If the server is not set up to generate responses in this custom format, it will result in the "Could not find acceptable representation" error.
이 오류의 가능한 원인은 다음과 같습니다: 지원되지 않는 미디어 유형: 클라이언트가 요청의 '허용' 헤더에 서버가 제공할 수 없는 다른 미디어 유형(콘텐츠 유형)을 요청하고 있을 수 있습니다. 예를 들어 클라이언트는 "application/json"을 요청하지만 컨트롤러는 "application/vnd.API.v1+json"을 생성하도록 구성되어 있을 수 있습니다. 이 경우 서버가 일치하는 항목을 찾을 수 없어 오류가 발생합니다. 누락된 구현: 사용자 지정 미디어 유형 "application/vnd.API.v1+json"을 처리하기 위한 구현이 누락되었을 수 있습니다. 서버가 이 사용자 지정 형식의 응답을 생성하도록 설정되어 있지 않으면 '허용되는 표현을 찾을 수 없습니다' 오류가 발생합니다.
이말을 보고서, 나는 문득 테스트 코드의 한 부분과 내가 갖고 있던 책의 한 부분이 생각이 났고, 해당 부분을 수정해보았다.
위에 부분은 기존에 작성한 코드인데(지금보니, .header 부분도 없던것인데, 내가 지우지 못하고 캡쳐했다..), 나는 지피티의 미디어 유형과 헤더에 대한 인사이트를 얻어 이렇게 수정하고서는 기존의 에러에서 벗어날 수 있었다.
이제는 다른 문제에 직면했다. 왜 null 이 반환 됬는지를 해결하면된다.
이젠 다른 고민의 영역이다. 왜 productQueryService 가 null을 반환했을까.. 나는 이것이 웹 계층에서 사용한 어노테이션 때문이라고 현재 생각하고 있는데, 지금 이렇게 정리하면서 보니 컴포넌트 모두 올라가고.. 정상적으로 Bean 으로 등록이 됬기에.. Mocking 이 된듯한데, 좀 더 자세한것은 내일 알아봐야겠다.
중간 문제풀이도 했는데, 이 문제 때문인지 제대로 문제에 집중하지 못했다. 빨리하고 이 문제를 해결해야지 하는 생각에 문제 해결에 잘못된 아이디어에 빠지고, 잘못된 것에 내가 잘못된 것을 몰라 오히려 한참을 시간을 낭비했었다. 해당 문제는 도저히 내가 잘못한것을 모르겠어서 친구에게 도움을 요청했는데, 친구가 이상함을 느끼고(?) 어떤 의도로 문제를 접근했는지 설명해달라는 말에 내가 무엇이 잘못됬는지 스스로 깨우칠 수 있었다. 친구가 볼지 모르지만.. 수현아 바쁜데도 신경써줘서 정말 고마워!!
그리고 오늘 하루종일 한 문제를 해결하지 못했지만, 분명하게 새로운 지식을 공부하고 습득한다는 생각이 들어 기쁘다. 모르는 내용이 조금씩 조금씩 아는 내용으로 바뀌어 가는것이 재밌다. 내일은 더 재밌는 일이 가득하겠지?
그리고 내일은 책 꼭 읽자!! 안풀릴 때 리프레시로 책을 보자 !!
'회고 > TIL' 카테고리의 다른 글
에러 해결에 한걸음 한걸음, 다시 멈추고 이제야 발견한 오류, 해결 후 다시 앞으로 (0) | 2023.07.30 |
---|---|
알고리즘 문제풀이, 웹계층 테스트 에러 해결?, 도메인주도설계로 시작하는 마이크로서비스 개발 독서 (0) | 2023.07.27 |
알고리즘 문제풀이, 이슈 피드백(?), 클린 아키텍처 정리 (0) | 2023.07.24 |
독서, 문제풀이, 정리 (0) | 2023.07.23 |
이펙티브 자바 독서, 클린 아키텍처 정리, 프로젝트 템플릿 작성 (0) | 2023.07.20 |