MockMvc 테스트 시 인코딩 문제로 검증 실패 | MockMvcBuilderCustomizer

gibeom·2022년 11월 9일
0

삽질 저장소

목록 보기
2/5

문제 상황

  • MockMvc를 사용하여 아래의 웹 테스트 진행 (상품 생성하기 Create API)
  • 위에서 content로 집어넣은 것을 그대로 검증하는 구문인데도 불구하고 검증 실패
@Test
@DisplayName("201 코드를 반환한다")
void it_responses_201() throws Exception {
    mockMvc.perform(
                    post("/products")
                            .contentType(MediaType.APPLICATION_JSON)
                            .content(JsonUtil.writeValue(TOY_1.요청_데이터_생성()))
            )
            .andExpect(status().isCreated())
            .andExpect(content().string(containsString(TOY_1.NAME())))
            .andExpect(content().string(containsString(TOY_1.MAKER())));
}


// TOY_1.요청_데이터_생성() 내부로직
ProductRequest.builder()
        .name(name)
        .maker(maker)
        .price(price)
        .imageUrl(imageUrl)
        .build();

문제 원인

  • 위 코드에서 .content(JsonUtil.writeValue(TOY_1.요청_데이터_생성()))를 통해 테스트 데이터를 집어넣고 반환하는 과정에서 반환된 데이터의 한글 값들이 깨지는 현상

  • 반환된 데이터가 깨져있기 때문에 아래의 .andExpect(content().string(containsString("범냐옹"))) 검증 구문에서 “범냐옹”이 포함된 문자가 존재하지 않아 검증 실패


Why?

https://stackoverflow.com/questions/58525387/mockmvc-no-longer-handles-utf-8-characters-with-spring-boot-2-2-0-release

  • 스프링 5.2 버전 이후로 기본 인코딩 문자는 더이상 UTF-8이 아니라고 한다
    • 한글깨짐 발생
    • Charset 을 UTF-8 변경등록해야함

해결 과정

[시도 1]

  • 강의에서 작성한 .accept(MediaType.APPLICATION_JSON_UTF8) 으로 진행하였더니 해결되었다.
  • 하지만 해당 static 상수(APPLICATION_JSON_UTF8)는 @Deprecated 되어있는 상태.

[Why?]

Public constant media type for application/json;charset=UTF-8.
Deprecated : as of 5.2 in favor of APPLICATION_JSON since major browsers like Chrome now comply with the specification and interpret correctly UTF-8 special characters without requiring a charset=UTF-8 parameter.

Chrome과 같은 주요 브라우저에서는 이전과 다르게 이제는 사양을 준수하기 때문에, charset=UTF-8 매개변수 없이도 UTF-8 특수문자를 제대로 해석할 수 있다고 한다.
또한 위와 같은 이유로 Spring에서는 더 이상 charset 부분을 사용하지 않는다는 목적을 가지게 되었다고 한다.
따라서 charset을 사용하지 않으니 자연스럽게 MediaType.APPLICATION_JSON_UTF8의 사용 중단(Deprecated)을 결정하고 MediaType.APPLICATION_JSON 사용을 권장하는것 같다.

  • MediaType.APPLICATION_JSON_UTF8는 내부에 2개의 속성을 가지고 있음
    (application/json; charset=UTF-8)

해당 결정은 Spring 5.2(Boot 2.2) 버전부터 적용된 것으로 보인다.


[생각 정리]

  • 스프링의 의도는 메인 브라우저들이 UTF-8을 제대로 알아 들으니까, 더 이상 Content-Type에 charset을 설정할 필요가 없어 보여서 charset 부분을 사용하지 않는 방향으로 감
  • 근데 charset 설정을 따로 안해주면 UTF-8이 아니기 때문에 한글이 깨져서 테스트가 실패함
  • 그럼 스프링 의도대로 charset 설정을 안해주면서 문제를 해결하려면 테스트 반환 타입을 수정해줘야겠네?

[시도 2]

  • 스택오버플로우 답변들에서는 생각보다 많은 해결 책을 제시했고, 제일 많은 추천수를 받은 답변의 대댓글이 눈에 들어왔다.
    • 베스트 추천 답변은 다음과 같다.
    • WebMvcConfigurer를 구현하여 메시지 컨버터 중에 MappingJackson2HttpMessageConverter일 때 setDefaultCharset(UTF_8)); 설정을 통해 charset을 설정해주는 방법이다.
    • 우리는 HTTP 요청을 하게되면 Request Body 데이터를 보통 RequestDto 객체를 통해 받는데, 이 때 MappingJackson2HttpMessageConverter가 DTO 객체에 데이터를 매핑해주는 역할을 해준다.
    • 따라서 DTO 객체에 매핑해주는 컨버터에 추가적으로 charset을 적용해주는 방식이다.

아래 대댓글을 보면 이 방법은 곧 Header에 application/json;charset=UTF-8 설정을 하는 것이나 다름 없으니 Spring의 본래 목적(charset 미사용)을 무효화 시킨다고 한다.
따라서 테스트 값 자체를 수정하는 것이 좋지 않겠냐고 제안한다.

해당 대댓글의 의견에 공감이 되었으므로 나는 아래와 같이 구현함으로써 문제를 해결하였다.

import org.springframework.boot.test.autoconfigure.web.servlet.MockMvcBuilderCustomizer;
import org.springframework.stereotype.Component;
import org.springframework.test.web.servlet.setup.ConfigurableMockMvcBuilder;

import java.nio.charset.StandardCharsets;

// @AutoconfigureMockMvc를 사용하여 MockMvc를 주입하는 경우에 한해서만 적용
@Component
class MockMvcCharacterEncodingCustomizer implements MockMvcBuilderCustomizer {
    @Override
    public void customize(ConfigurableMockMvcBuilder<?> builder) {
        builder.alwaysDo(result -> result.getResponse().setCharacterEncoding(StandardCharsets.UTF_8.name()));
    }
}

위와 같이 적용할 경우 MockMvc의 테스트 결과 값의 문자를 UTF-8로 세팅하여 한글이 깨지지 않아 정상적으로 테스트 검증을 할 수 있다.


해결!


Reference

profile
꾸준함의 가치를 향해 📈

1개의 댓글

comment-user-thumbnail
2023년 10월 15일

덕분에 좋은 내용 잘 보고 갑니다.
정말 감사합니다.

답글 달기