본문 바로가기
Java & Kotlin/Spring

Feign에서 multipart/form-data json 조금 더 잘 다루기

by heekng 2024. 3. 10.
반응형

업무 중 외부 API 호출이 필요한 상황이 생겼습니다.

이미 사용하고 있던 ClientAPI는 FeignClient였고, 평소 Get, Post 그리고 application/json 형식의 요청을 주로 사용했습니다.

하지만 이번 경우에는 Multipart/form-data 형태의 요청 그리고 File과 json을 함께 전송하는 것을 필요로 했습니다.

 

(예시) 외부 API의 컨트롤러 형태는 다음과 같습니다.

Post 요청의 consumes는 Multipart/form-data 이며, 각각의 mapVlues, longValues, fileValue는 모두 @RequestPart로 요청받고 있습니다.

 

단순히 생각했을 때, 이 API를 feignClient로 요청한다면 아마 다음과 같은 interface를 구성할 것입니다.

이렇게 작성된 API에는 어떤 문제가 생길까요?

위처럼 적절한 Encoder가 존재하지 않는다는 feign.coder.EncodeException이 발생합니다.

 

많은 외부 자료에서는 해당 에러를 해결하기 위해 SpringFormEncoder와 같이 인코더를 별도로 생성해서 config에 주입시키는 방식을 사용하고는 합니다.

 

Encoder을 정상적으로 생성한 후 다시 테스트를 해본다면

이렇게 Unsupported Media Type 에러가 발생합니다.

 

요청받는 Vendor api는 어떤 로그가 출력될까요?

저는 분명히 List를 보냈는데 content-type이 'text/plain'으로 전송되었다고 합니다.

 

https://github.com/OpenFeign/feign-form/issues/118

 

POST JSON with File not working (multipart/form-data) · Issue #118 · OpenFeign/feign-form

Using these deps: feign-core and feign-gson 12.3. feign-form 3.8.0. JDK 19. I can't get JSON serialisation working when using POST multipart/form-data for POJO->JSON and File. Example: interface Ex...

github.com

위 이슈와 같이 File과 Json을 함꼐 전송할 때, json은 application/json으로 전송되길 원하지만 text/plain으로 전송되는 문제가 있습니다.

 

그래서 Map, List와 같이 RequestPart로 전송해야하는 데이터를 MultipartFile로 변경하고, content-type을 직접 명시하는 방법을 선택하게 되었습니다.

 

첫 번째 시도는 MockMultipartFile을 사용하는 방법인데요.

주로 테스트 의존성에 추가되는 org.springframework.boot:spring-boot-starter-test 내에 있는 MockMultipartFile을 사용합니다.

테스트도 잘 통과되고, vender-api의 로그도 정상적으로 출력됩니다.

 

하지만 test에서 사용하는 의존성을 메인 소스에 추가한다는 것이 완전히 잘못된 것은 아니지만 조금은 부담스럽기도 합니다.

때문에 MutipartFile 인터페이스 구현한 CustomMultipartFile을 직접 작성하고, byteArray를 입력받아 multipartFile을 생성하기로 했습니다.

위와 같이 CustomMultipartFile을 작성하고, Client class에서 MultipartFile을 생성한 후 Feign API 호출을 진행했습니다.

당연히 테스트와 request log 또한 정상적으로 출력됩니다.

 

많은 글에서 MockMultipartFile를 메인 소스코드에서 작성하고 사용하는 예시가 있는데요.

개인적으로 테스트를 위한 의존성에 포함되어있는 테스트관련 클래스와 기능들을 무심코 사용할 수 있다는 면에서 지양하고 있어서 구현 클래스를 생성해 사용하게 되었습니다.

 

전체 소스코드는 여기서 확인할 수 있습니다.

반응형