랩실 서비스 서버를 개발하는 중, 책을 등록하면 ‘책이 등록되었습니다.’라는 문자열을 Response 바디에 담아 보내기 위해 ResponseEntity.body()
로 문자열을 담아 응답하도록 구현했다. 구현을 마치고 Postman으로 책을 등록/하는 요청을 전송해 보았다.
?? ?? ????..?? 한글이 깨져서 물음표로 나왔다.
하지만 당황하지 않았다. 난 Character Encoding이란 것을 알고 있으니까. 바로 Headers로 들어가 보았다. 역시 Character Encoding이 심상치 않은 녀석으로 설정되어 있었다. ISO-8859-1이라는 낯선 친구가 보였다.
아래와 같이 Content-Type 헤더에 charset을 UTF-8로 설정하니 잘 오는 것을 확인했다.
...
return ResponseEntity.created(URI.create("/api/v1/books/" + bookId))
.header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN_VALUE + ";charset=" + StandardCharsets.UTF_8)
.body("도서 등록 완료");
...
왜 설정을 안 하면 기본이 ISO-8859-1로 설정되는지 궁금해서 코드를 뒤적거렸다. DispatcherServlet
이 요청을 받으면 우선 컨트롤러를 매핑한다. 그리고 해당 컨트롤러를 처리할 수 있는 HandlerAdapter
를 찾는다. 그리고 그 HandlerAdapter
가 컨트롤러의 메서드를 대신 호출하는데, 필요한 파라미터를 HandlerMethodArgumentResolver
를 통해 생성하고 전달한다. 그리고 컨트롤러가 응답을 반환할 때는 HandlerMethodReturnValueHandler
가 응답 데이터를 처리하는데 이때 HttpMessageConverter
를 사용한다. 이 HttpMessageConverter
에서 문자 인코딩을 설정할 것 같아서 구현체를 살펴보았다. 구현체가 워낙 다양해서 찾기가 좀 힘들었다.
그중에 AbstractHttpMessageConverter
추상 클래스를 상속받는 StringHttpMessageConverter
를 찾았다.
‘문자열을 읽고 쓸 수 있는 HttpMessageConverter
의 구현체’라고 한다. 해당 컨버터는 모든 미디어 타입을 지원하고 Content-Type을 text/plain으로 작성한다고 한다.
이 구현체의 상단에 DEFAULT_CHARSET
이 ISO-8859-1로 설정되어 있는 것을 확인할 수 있다.
기본 Charset
이 정해지지 않고 기본 생성자가 호출되면 기본 Charset
을 ISO-8859-1로 하여 상위 클래스인 AbstractHttpMessageConverter
의 생성자를 호출하며 전달한다.