본문 바로가기

Programing/Framework

[스프링 부트] StringHttpMessageConverter 를 쓸 때 주의점

제목이 길어져서 생략했지만 2.1.2.RELEASE 부터 적용되는 이야기이다.
스프링부터 2.1.2.RELEASE 에서 의존하는 스프링 프레임워크 버전은 5.1.4.RELEASE 이다.

spring-web:5.1.4.RELEASE 에 들어있는 StringHttpMessageConverter 클래스의 코드가 아래와 같이 변경된 것을 알 수 있었다.

spring-web:5.1.4 .RELEASE 부터 바뀐 부분

getContentTypeCharset 메서드가 해주는 역할은 미디어타입에 따라 캐릭터셋을 돌려주는데,
크게 3가지 부분에서 사용이 된다.

  1. readInternal: 메세지를 읽을 때
  2. getContentLength: 컨텐츠의 길이를 계산할 때
  3. writeInternal: 메세지를 쓸 때

문제는 getContentTypeCharset의 로직의 분기가 추가됨에 따라 JSON 컨텐츠타입의 경우 charset이 특별히 지정되어 있지 않으면 기본적으로 UTF-8를 인코딩 타입을 사용하게 된다. 혹시나 defaultCharset 을 사용하여 인코딩을 바꾸는 경우 이전 동작이 정상적으로 동작하지 않을 가능성이 생긴 것이다.

이 문제를 발견하게 된 것은 요청은 EUC-KR로 인코딩된 JSON으로 보내고, 응답은 UTF-8로 인코딩된 JSON을 받는 특이한 API 때문이었다.

HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity entity = new HttpEntity<>(content, headers);

RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(0, new StringHttpMessageConverter(Charset.forName("EUC-KR"));
restTemplate.exchange(apiUrl, HttpMethod.POST, entity, Response.class);

이 경우에는 제일 마지막 else를 거쳐야 getDefaultCharset()을 통해 구해온 캐릭터셋을 반환한다.

하지만 spring-web이 5.1.4.RELEASE 부터는 상관없이 UTF-8을 돌려주게 된다는 사실.

해결책은

커밋 로그에도 나와 있지만, MappingJackson2HttpMessageConverter 이나 GsonHttpMessageConverter도 로직이 동일하다.
따라서 우선순위가 가장 높은 ContentType에 캐릭터셋을 명시하는 것이 가장 확실하다.

HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.valueOf("application/json;charset=EUC-KR");
HttpEntity entity = new HttpEntity<>(content, headers);

RestTemplate restTemplate = new RestTemplate();
restTemplate.exchange(apiUrl, HttpMethod.POST, entity, Response.class);

다른 해결책은?

나만 문제가 아니였는지 StringHttpMessageConverter 의 수정사항은 다시 Rollback이 되었다.

이 변화는 다시 Rollback 되었다!

Rossen Stoyanchev가 일을 저질러 놓고 Sebastien Deleuze가 해결해놓았다.

하지만 아직 2019-05-28일 커밋된 수정사항은 릴리즈되지 않은 것 같다.

아직 해결이 안되었다.

이후 언젠가 롤백이 된 버전으로 Jump를 하는 것이 다른 해결책이다.