본문 바로가기
백엔드/스프링

Swagger 테스트 시 - application/octet-stream is not supported

by ARlegro 2025. 3. 5.

오류 발생

application/octet-stream' is not supported 메시지를 남기는 상황
Postman으로 테스트했을 때는 문제 없었던 상황

 

HTTP매핑 속성도 'MULTIPART_FORM_DATA_VALUE"로 잘 했는데 왜 서버 측에서는 설정해주지도 않은 타입인 application/octet-stream으로 인식하고 있을까??

문제 원인 분석

Swagger UI의 JSON 직렬화 방식 차이

Swagger UI에서는 Multipart/form-data 요청 시 버그가 있다

  1. 객체(JSON) 요청일 경우 해당 데이터의 Content-Type을 제대로 지정하지 않고 전송하는 현상.
    • postman은 수동으로 설정이 가능해서 문제가 없었던 것
  2. 서버측에서는 이를, application/octet-stream (기본 바이너리 타입)으로 받아들이게 된다.
  3. 따라서 'application/octet-stream' is not supported" 가 발생

 

 

해결법 - 커스텀 컨버터 생성

조사하니까 해결하는 방법들은 여러 방법이 있다.
그 중 깔끔하고 괜찮은 방법 2개 선정(1번 방법 선호)

방법 1. 기존 컨버터 확장 or 추가 등록 🔓

Spring이 기본 적으로 등록하는 jackson기반 json 컨버터인 MappingJackson ~ 를 오버라이딩(또는 추가 등록) 할 빈을 등록하기

해당 컨버터의 지원 미디어 타입 목록에 application/octet-stream을 추가

@Configuration
public class JacksonConverterConfig {

    //@Primary => 자동 vs 수동 빈 충돌 두려우면 추가 
    @Bean
    public MappingJackson2HttpMessageConverter customJacksonConverter() {
		    // 1. 새로운 jackson 기반 HTTP 메시지 컨버터 생성 
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        // 2. converter가 지원하는 MediaType 리스트를 복사하여 만들기 
        List<MediaType> supportedMediaTypes = new ArrayList<>(converter.getSupportedMediaTypes());
        // 3. octet-stream도 지원하도록 추가하기 
        supportedMediaTypes.add(MediaType.APPLICATION_OCTET_STREAM);
        converter.setSupportedMediaTypes(supportedMediaTypes);
        return converter;
        
        // List<MediaType> supportedMediaTypes = converter.getSupportedMediaTypes();
        // 이게 불변 객체 반환이라 불가능
				// supportedMediaTypes.add(MediaType.APPLICATION_OCTET_STREAM);
    }

✅기존 Jackson 컨버터의 기능을 유지하면서 확장

  • Spring이 기본적으로 등록하는 MappingJackson2HttpMessageConverter의 기능을 변경하지 않고 추가적인 기능만 확장
  • 원래 지원하는 JSON 변환 기능은 그대로 유지하면서, 추가로 지원할 미디어 타입을 확장

✅충돌이 날 것 같지만, 수동으로 등록된 빈이 우선순위다

  • 일반적으로 수동 VS 자동 등록 빈이 충돌하면 수동 등록 빈이 우선이다
  • (수동 빈이 자동 빈을 오버라이딩)
  • But 의도치 않은 에러가 발생할까봐 걱정이 되면 @Primary 써서 2개 등록 가능

방법 2. 커스텀 컨버터 생성 - 상속

Swagger에서 전송하는 데이터를 application/octet-stream로 변환하기 때문에, Spring이 무시하도록 설정할 필요가 있다
해결방법 개요

  1. application/octet-stream을 JSON으로 변환하려는 시도를 차단
  2. 커스텀 메시지 컨버터(MultiPartConverterConfig)를 생성하여 application/octet-stream을 무시하도록 설정
  3. 커스텀 컨버터가 HTTP 응답 출력을 지원하지 않도록 canWrite() 메서드를 오버라이딩하여 false 반환

 

@Component  // 1. Abstract ~~ 상속 : 
public class MultiPartConverterConfig extends AbstractJackson2HttpMessageConverter {

		// 2. ObjectMapper 객체를 받아서 생성자로 받아들임
    protected MultiPartConverterConfig(ObjectMapper objectMapper) {
        super(objectMapper, MediaType.APPLICATION_OCTET_STREAM);
    }

		// 3. 모든 canWrite 메서드 다 false로 반환해서 변환 무력화 
    @Override
    protected boolean canWrite(MediaType mediaType) {
        return false;
    }

    @Override
    public boolean canWrite(Class<?> clazz, MediaType mediaType) {
        return false;
    }

    @Override
    public boolean canWrite(Type type, Class<?> clazz, MediaType mediaType) {
        return false;
    }
}

 
 
AbstractJackson2HttpMessageConverter를 상속받기

  • Spring의 JSON 변환기인 MappingJackson2HttpMessageConverter도 AbstractJackson2HttpMessageConverter를 상속받음.
  • 따라서 커스텀 클래스가 이것을 직접 상속한다면, application/octet-stream 처리 방식을 조정할 수 있다.

✅super(objectMapper, MediaType.APPLICATION_OCTET_STREAM);

  • application/octet-stream 타입의 요청을 받을 때, Spring이 이 컨버터를 사용하도록 등록함.
  • 이렇게 되면, octet-stream 타입을 받아들여도 예외가 발생하지 않음
  • 후에, 이 타입이 받아들여지면 기본 Jackson 컨버터가 그 부분을 json으로 간주하고 파싱해주게 된다.

✅canWrite( ~~ ) 오버라이딩 : 출력차단

  • 모두 false로 설정 → 이 컨버터가 HTTP 응답을 변환하는 기능을 하지 않음.
    • application/octet-stream을 JSON으로 직렬화하지 않도록 차단
    • octet-stream은 바이너리 데이터인데 직렬화 하는 것 자체를 막아야
  • 즉, 이 컨버터가 응답 변환에는 개입하지 않도록 제한

이로 인해, Spring이 application/octet-stream타입 요청을 기본 컨버터(MappingJackson2HttpMessageConverter)에서 처리하지 않도록 함

  • 처리를 커스텀 클래스에서 하고
  • 나중에 파싱은 MappingJackson2HttpMessageConverter