본문 바로가기
프로그래밍/webflux

[webflux] webflux 환경에서 locale 커스터마이징하기

by 사바라다 2021. 10. 24.

안녕하세요. 저는 개인적으로 그리고 팀에서 webflux를 사용하고 있습니다. 관련하여 어느정도 사용을 해보았기 때문에 이제는 webflux 관련된 포스팅도 한번씩 올려보려고합니다. 오늘은 그 첫번째로 webflux 환경에서 언어 정보를 커스터마이징하는 방법에 대해서 알아보도록 하겠습니다.

locale 정보

글로벌 앱을 개발하다보면 로컬 앱을 개발할 때와는 다른 고려해야할 부분이 생기게됩니다. 대표적인 예가 시간, 그리고 언어입니다. 지역에 따라서 언어와 시간이 다르기 때문에 지역에 따라 다른 시간과 언어 정보를 데이터로 활용할 수 있기 때문입니다.

서버에서는 클라이언트에서 요청할 때의 특정 정보를 해석하여 그에 맞는 언어를 내려줍니다. 어디에 정보를 담아줄지는 개발하는 설계에 따라서 달라질 수 있습니다. 하지만 http를 사용한다고 하면 이를 위한 정보를 담는 header가 이미 존재하고 있습니다. 바로 Accept-Language라는 헤더입니다. 클라이언트에서 Accept-Language를 보내면 그것을 해석해서 서버에서 맞는 Contents를 Contents-Language와 함께 보내준다고 이해하시면 되겠습니다.

환경 및 의존성

webflux에서 Locale 정보를 가져오는 것을 확인하기 위한 테스트 환경은 아래와 같습니다.

  • java 버전 : 11
  • spring boot 버전 : 2.5.5

그리고 webflux 환경이기 때문에 아래의 의존성은 필수적으로 가져야합니다.

implementation("org.springframework.boot:spring-boot-starter-webflux")

webfux에서 locale 정보 취득 방법

webflux에서 locale 정보를 취득해서 사용하는 방법은 webMvc와 다르게 별도의 설정이 필요하지 않습니다. 아래 처럼 Locale 객체를 파라미터로 사용하면 바로 Locale 정보를 서비스 코드에서 활용할 수 있게 됩니다.

@RestController
@RequestMapping("/v1/voice-diary/question")
class QuestionController {

    @GetMapping
    public Mono<String> readRandomQuestion(locale: Locale) {
        return Mono.just(locale.toString());
    }
}

webflux에서 locale 정보 커스터마이징 방법

webflux 서버에서 Locale을 처리하는 handler를 global 수준에서 커스터마이징 하고싶다면 DelegatingWebFluxConfiguration을 상속 받은 뒤 LocaleContextResolver를 오버라이딩하면됩니다. 아래의 예제 코드를 참조하시면 많은 도움이 되실 것 같습니다. 아래 코드는 Accept-Language 헤더를 가지고 와서 locale을 입력하고 만약 Accept-Language에 값이 없거나 정상적이지 않다면 서버의 JVM에 설정된 Locale 정보를 이용한다는 코드입니다.

@Component
class LocaleResolver extends LocaleContextResolver {

    @Override
    public LocaleContext resolveLocaleContext(ServerWebExchange exchange) { // Locale 정보에 대해서 커스터마이징 로직처리
        String language = exchange.request.headers.getFirst("Accept-Language");

        Locale targetLocale = Locale.getDefault();
        if (language != null && language.isNotEmpty()) {
            targetLocale = Locale.forLanguageTag(language);
        }
        return SimpleLocaleContext(targetLocale);
    }

    @Override
    public void setLocaleContext(ServerWebExchange exchange, LocaleContext localeContext) {
        throw new UnsupportedOperationException("Not Supported");
    }
}

아래 코드는 커스터 마이징 된 global Locale 처리 로직을 적용하는 코드입니다. DelegatingWebFluxConfiguration에서 오버로딩함으로써 적용할 수 있습니다.

@Configuration
class LocaleSupportConfig extends DelegatingWebFluxConfiguration {

    @Override
    public LocaleContextResolver createLocaleContextResolver() {
        return LocaleResolver();
    }
}

테스트

아래는 위의 설정된 코드를 테스트하는 코드입니다. 아래의 테스트 코드로 위의 설정이 정상동작하는 것을 확인할 수 있습니다.

@ExtendWith({SpringExtension.class})
@WebFluxTest({QuestionController.class})
public final class QuestionControllerTest {

   @Autowired
   public WebTestClient webTestClient;

   @Test
   public void testGet_en() {
      String response = webTestClient.get()
            .uri("/v1/voice-diary/question")
            .header(HttpHeaders.ACCEPT_LANGUAGE, "en-US")
            .exchange()
            .returnResult(String.class)
            .responseBody
            .blockFirst()

        println("response = $response")
        Assertions.assertEquals(response, "en_US")
   }

   @Test
   public void testGet_default() {
       String response = webTestClient.get()
            .uri("/v1/voice-diary/question")
            .header(HttpHeaders.ACCEPT_LANGUAGE, "abcd")
            .exchange()
            .returnResult(String.class)
            .responseBody
            .blockFirst()

        println("response = $response")
        Assertions.assertEquals(response, "ko_KR")
   }
}

마무리

오늘은 이렇게 webflux 환경에서 locale을 커스터마이징하는 방법에 대해서 알아보았습니다.

감사합니다.

참조

mozilla_Accept-Language

josdem_spring_webflux_internationalization

stackoverflow_how-to-get-locale-in-spring-webflux

댓글