본문 바로가기
Java & Spring Boot

[Java] WebClient queryParam uri 인코딩 문제

by 향각산 2024. 10. 17.

WebClient를 사용하여 Get Method를 요청할때 사용하는 QueryParam에 인코딩 문제

 

다음과 같은 데이터 포맷이 있을때

    private static final DateTimeFormatter OUTPUT_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");

    public static RequsetDto of(String start) {
        // start = 2024-10-15
        RequsetDto dto = new RequsetDto();

        // Parse start and end dates
        LocalDate startDate = LocalDate.parse(start, INPUT_FORMATTER);

        // Format to the desired output format
        dto.lastChangedFrom = startDate.atStartOfDay().atOffset(ZoneOffset.ofHours(9)).format(OUTPUT_FORMATTER);

        return dto;
    }

 

이런 코드로 데이터 데이트 포맷을 만들면 dto.lastChangedFrom은 2024-10-15T00:00:00.000+09:00 가 나온다.

내가 보내고 싶었던 queryParam은 lastChangedFrom=2024-10-15T00%3A00%3A00.000%2B09%3A00 데이터 였다.

하지만 WebClient로 전송시 인코딩이 되지않고 lastChangedFrom=2024-10-15T00:00:00.000+09:00 형태로 전송되는 문제가 발생헀다.

 

처음 시도한 방법은 dto.lastChangedFrom에 들어있는 값 자체를 직접 인코딩을 했다.

dto.lastChangedFrom = URLEncoder.encode(dto.lastChangedFrom);

 

이렇게 작성하면 내가 원하는 포맷 2024-10-15T00%3A00%3A00.000%2B09%3A00 데이터가 나타나고 이걸 WebClient에 QueryParam으로 전송하면 다음과 같은 포맷이 나온다.

2024-10-15T00%253A00%253A00.000%252B09%253A00

내가 원했던 데이터 포맷보다 훨씬 긴 문자열이 나왔다.

 

WebClient에서 인코딩하지 않았을땐 WebClient도 인코딩하지 않는데, 한번 인코딩한 데이터를 보내면 WebClient도 인코딩을 해버리는 문제가 발생.

 

해결방법은 WebClientConfig에서 DefaultUriBulderFactory를 설정해주는 것

다른 부분은 필요없고 defaultUriBuilderFactory 설정만 추가하면 된다.

다만, 다른 부분에 영향이 갈 수 있으니, 기존에 WebClient를 사용하는 코드가 있다면 @Qualifier 사용을 고려할 것

    @Bean
    public WebClient webClient() {
        return WebClient.builder()
                .uriBuilderFactory(defaultUriBuilderFactory())
                .exchangeStrategies(exchangeStrategies())
                .clientConnector(reactorClientHttpConnector())
                .filter(logRequest())
                .filter(logResponse())
                .build();
    }

    private DefaultUriBuilderFactory defaultUriBuilderFactory() {
        DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory();
        factory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.NONE);
        return factory;
    }

    private ExchangeStrategies exchangeStrategies() {
        return ExchangeStrategies.builder()
                .codecs(configurer -> configurer
                        .defaultCodecs()
                        .maxInMemorySize(MAX_MEMORY))
                .build();
    }

    private ExchangeFilterFunction logRequest() {
        return ExchangeFilterFunction.ofRequestProcessor(clientRequest -> {
            log.info("Request: {} {}", clientRequest.method(), clientRequest.url());
            clientRequest.headers().forEach((name, values) -> values.forEach(value -> log.info("{}={}", name, value)));
            return Mono.just(clientRequest);
        });
    }

    private ExchangeFilterFunction logResponse() {
        return ExchangeFilterFunction.ofResponseProcessor(clientResponse -> {
            log.info("Response: {}", clientResponse.statusCode());
            clientResponse.headers().asHttpHeaders().forEach((name, values) -> values.forEach(value -> log.info("{}={}", name, value)));
            return Mono.just(clientResponse);
        });
    }

    private ReactorClientHttpConnector reactorClientHttpConnector() {
        return new ReactorClientHttpConnector(httpClient());
    }

    private HttpClient httpClient() {
        return HttpClient.create()
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, MAX_TIMEOUT_MS);
    }