문제의 배경
프로젝트 진행 중에 입력값의 유효성을 검사하기 위해 외부 API를 호출할 일이 있었다. 그런데 포스트맨이나 크롬 개발자 도구에서는 문제 없이 잘 호출되는 API가 막상 WebClient를 사용한 자바 프로덕션 코드로 호출을 하려고 해보니 자꾸 등록된 키가 아니라며 400 에러를 뿜어대서 일단은 동작하게 만들기 위해 Unirest로 구현을 한 상태였다.
원인 파악
return WebClient.create()
.post()
.uri("외부 API url")
.body(BodyInserters.fromValue(param))
.retrieve()
.BodyToMono(String.class)
.block();
에러 로그에서 계속 문제가 발생한다고 했던 부분이다. 처음에는 문제될 것이 없는데 왜 자꾸 에러가 나는 것인지 도저히 알 수 없었다. 그러다가 에러 로그에 나와있는 url과 포스트맨에서 사용한 url에 차이가 있다는 것을 알게 되었다. 자세히 보니 url에 포함된 특수문자가 다른 형태로 변환되어 있었다.
이 문제는 인증키를 url에 포함된 queryParam으로 받는 경우에 발생하는 문제인듯 하다. 기본적으로 WebClient의 인코딩 방식은 UriComponentsBuilder#encode()라는 옵션을 사용하여 url의 예약 문자들을 치환한다고 한다. 결국 WebClient의 기본 인코딩으로 인해 키값이 달라져서 발생한 문제였던 것이다.
문제 해결
인코딩으로 인해 발생한 문제라는 것을 파악했으니 url에 인코딩을 적용하지 않기로 했다. DefaultUriBuilderFactory 객체를 생성하여 인코딩 모드를 None으로 변경하고 이를 아래와 같이 WebClient에 적용시켰다.
DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory();
factory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.NONE);
return WebClient.builder() // create()를 빌더로 변경
.uriBuilderFactory(factory) // 추가
.build()
.post()
.uri("외부 Api url")
.body(BodyInserters.fromValue(param))
.retrieve()
.BodyToMono(String.class)
.block();
테스트도 정상적으로 통과된다!
결론
API의 인증키를 queryParam으로 받는 API를 WebClient로 호출하기 위해서는 인코딩을 적용하지 않아야 하는듯 하다. 사실 인증키를 queryParam으로 주고 받는 것보단 아예 header의 Authorization 쪽에 담아서 주고 받는 것이 더 좋을 것 같지만 이걸 가져다 쓰는 입장에서 바꿀 수는 없으니...
참조 링크
댓글