@Test
@DisplayName("이름조회")
public void getMemberByName() throws Exception{
Member member1 = new Member(1l, "홍길동", "1234", "남", "대성");
Member member2 = new Member(2l, "김민희", "5678", "여", "마라");
Member member3 = new Member(3l, "성대리", "9011", "여", "아발론");
ResultActions actions = mockMvc.perform(
get("/v1/members/find?name={name}", member1.getName())
.accept(MediaType.APPLICATION_JSON)
);
MvcResult result = actions
.andExpect(status().isOk())
.andReturn();
}
이름으로 회원 조회하는 기능을 만들고 테스팅을 하던 중 원하던 200번이 아닌 406번 에러가 발생했다.
2022-08-22 14:25:49.425 WARN 40047 --- [ Test worker] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation]
MockHttpServletRequest:
HTTP Method = GET
Request URI = /v1/members/find
Parameters = {name=[홍길동]}
Headers = [Accept:"application/json"]
Body = null
Session Attrs = {}
(중략)
MockHttpServletResponse:
Status = 406
Error message = null
Headers = []
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
에러 메시지를 보면 Could not find acceptable representation
라며 HttpMediaTypeNotAcceptableException 에러를 냈다. HTTP 응답도 처음 보는 406이 왔다. 먼저 이 두 가지에 대해 알아보자.
요청 핸들러가 허용된 응답을 만들어낼 수 없을 때 발생하는 에러라고 한다. 여기서 허용된 응답이란 헤더에 정해놓은 허용 타입이다. 내 코드의 경우 테스트 내에 .accept(MediaType.APPLICATION_JSON)라고 정해놓았기 때문에 JSON 타입만 허용한다.
The HyperText Transfer Protocol (HTTP) 406 Not Acceptable client error response code indicates that the server cannot produce a response matching the list of acceptable values defined in the request's proactive content negotiation headers, and that the server is unwilling to supply a default representation.
406 에러도 마찬가지로 서버가 허용된 타입의 응답을 생성하지 못할 때 발생하는 통신 에러이다.
DTO 클래스에 @Getter
추가기능이 동작하는 과정을 살펴보면 컨트롤러에 구현한 getMemberByName
핸들러 메서드로 member1.getName()
의 값인 홍길동
이 흘러갈 것이다.
@GetMapping("/find")
public ResponseEntity getMemberByName(@RequestParam(value = "name") String name) {
Member member = memberService.getMemberByName(name);
return new ResponseEntity<>(new SingleResponseDto<>(member), HttpStatus.OK);
}
이후 홍길동
이라는 값은 memberService.getMemberByName()
메서드에 인자로 들어가 레파지토리에서 결과를 찾은 후 member
라는 값으로 돌아와 설정된다. 그후 SingleResponseDto
라는 DTO 클래스를 거쳐 응답이 전해진다.
요청과 응답 간의 문제이니 여기서부터 문제가 발생했을 거라는 생각이 들었다.
public class SingleResponseDto<T>{
private T data;
public SingleResponseDto(T data) {
this.data = data;
}
}
DTO 클래스를 보면 생성자만 있고 게터가 없다. 따라서 클래스에 @Getter
애너테이션을 붙여줬다.
@Getter // 추가
public class SingleResponseDto<T>{
private T data;
public SingleResponseDto(T data) {
this.data = data;
}
}
붙여주니 테스트가 통과되었다. DTO에서 게터가 없으면 응답 내용에 값이 포함되지 않는 문제가 발생한다. (data
가 현재 private
변수 때문.) 검색해보니 이 방법 외에도 @JsonProperty
라는 애너테이션을 쓰거나 jackson 라이브러리 쓰면 해결할 수 있는 에러라고 한다. (spring-boot-starter-web를 쓴다면 jackson 라이브러리가 디폴트로 포함되어 있다고 한다.)
참고 자료