본문 바로가기
우아한테크코스 4기/레벨2

[Spring] 컨트롤러 테스트 시 한글 깨짐 문제 해결

by 나는후니 2022. 4. 24.

이번 미션에서 MockMvc를 이용하여 컨트롤러 테스트를 진행하던 중 아래와 같은 예외를 볼 수 있었습니다.

@DisplayName("진행 중인 모든 방을 찾아온다.")
@Test
void findRooms() throws Exception {
  RoomsResponseDto roomsResponseDto = RoomsResponseDto.of(List.of(createRoomEntity(1L)
                                                                  , createRoomEntity(2L)));
  String response = objectMapper.writeValueAsString(roomsResponseDto);

  given(chessService.findRooms())
    .willReturn(roomsResponseDto);
  mockMvc.perform(get(DEFAULT_API))
    .andExpect(status().isOk())
    .andExpect(content().string(response));
}

그 이유는 Spring 5.2.0 버전부터 더이상 UTF-8로 기본 인코딩 캐릭터를 제공하지 않기 때문인데요.

웹 요청에서는 UTF-8 설정을 하지 않더라도 한글이 깨지는 현상이 없지만, MockMvc를 이용하여 테스트 하기 위해서는 테스트용 기본 인코딩 캐릭터를 UTF-8로 변경해야 합니다.

 

문제점을 탐색하고, 해당 문제만 해결할 수 있는 방법을 알아보겠습니다.

1. @RequestMapping 의 produce 키워드

첫 번째로 시도한 방법은 produce 키워드를 이용해 메서드 자체에서 캐릭터 셋을 설정하는 방법이었습니다.

@GetMapping(produce = "application/json; charset=utf-8" )

해당 방법을 통해 문제를 해결할 수 있었지만 매 코드마다 반환하는 charset을 지정해줘야 한다는 측면에서 이 문제를 해결하기 위한 적절한 방법은 아니었습니다.

2. 프로그램 전체 MessageConverter 설정 변경

두 번째로 시도한 방법은 프로그램 전체 MessageConverter 기본 charSet을 UTF-8로 변경하는 것이었습니다.

전체 설정을 커스텀하여 사용하는 방법 중에는 @Bean을 이용한 의존성 주입도 있지만 스프링의 WebMvcConfigurer에 존재하는 MessageConverter를 커스텀하여 사용할 수도 있습니다.

@Configuration
public class WebConfig implements WebMvcConfigurer {

      // 1번 방법 - responseHeader의 반환 medialType을 Json utf-8로 설정한다.
      @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        configurer.defaultContentType(MediaType.APPLICATION_JSON_UTF8);
    }

      // 2번 방법 - Json으로 변환해주는 MappingJackson2HttpMessageConverter의 설정을 UTF-8로 변경한다.
      @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.stream()
            .filter(converter -> converter instanceof MappingJackson2HttpMessageConverter)
            .findFirst()
            .ifPresent(converter -> ((MappingJackson2HttpMessageConverter) converter).setDefaultCharset(UTF_8));
    }
}

하지만 위 두가지 방법 또한 테스트 코드에서 반환되는 response만 수정하는 방법이 아니기 때문에 이 문제의 해결방법과는 거리가 있습니다.

3. MockMvc custom

현재 저의 컨트롤러 테스트 코드는 컨텍스트에 컨트롤러를 캐싱하고 @Autowired 키워드를 통해 MockMvc를 주입받는 형태입니다.

@WebMvcTest(ChessController.class)
class ChessControllerTest {

    @Autowired
    private MockMvc mockMvc;
}

따라서 MockMvcBuilder를 이용한 filter 설정이 어렵습니다. 하지만 @Autowired를 사용할 수 있다는 것은 스프링에서 제공하는 기본 MockMvc를 커스텀하여 사용할 수 있다는 뜻입니다.

public class MockMvcConfig implements MockMvcBuilderCustomizer {

    @Override
    public void customize(ConfigurableMockMvcBuilder<?> builder) {
        builder.alwaysDo(result -> result.getResponse().setCharacterEncoding("UTF-8"));
    }
}

위 코드처럼 MockMvcBuilderCustomizer를 상속받아 result의 기본 인코딩은 UTF-8로 설정하여 MockMvc의 기본 설정을 커스터마이징합니다.

그리고 사용하고자 하는 테스트 코드에서 해당 코드를 @Import하여 사용합니다.

@Import(MockMvcConfig.class)
@WebMvcTest(ChessController.class)
class ChessControllerTest {

    @Autowired
    private MockMvc mockMvc;
}

이제 기존에 인코딩 이슈로 실패했던 테스트 코드를 돌려보면 아래와 같이 테스트를 성공합니다.