Sqs Listener - @Payload DTO 바인딩시 에러

Bobby·2023년 2월 13일
1

오답노트

목록 보기
3/6
post-thumbnail

🔥 문제 발생

@SqsListener(value = "${cloud.aws.sqs.queue.name}", deletionPolicy = SqsMessageDeletionPolicy.NEVER)
public void listen(@Payload SqsMessage message, @Headers Map<String, String> headers, Acknowledgment ack) {
	log.info("header ===> {}", headers);
	log.info("message ===> {}", message);
	ack.acknowledge();
}

@SqsListener 에서 @Payload 값을 SqsMessage 타입으로 받을 때 에러가 발생했다.

org.springframework.messaging.converter.MessageConversionException: Cannot convert from [java.lang.String] to [com.example.sqs.dto.SqsMessage] for GenericMessage

  • String을 SqsMessage객체로 변환 할 수 없다..
    -> Converter의 문제임을 알 수 있다.

바인딩 받을 객체의 예시

@Getter
@Setter
@ToString
public class SqsMessage {

    private String message;
    private User user;

    @Getter
    @Setter
    @ToString
    public static class User {
        private String userId;
        private String email;
    }
}

🪓 문제 해결

  1. PayloadMethodArgumentResolver 에서 @Payload 어노테이션을 resolve 한다.
  2. resolve 할 때 사용하는 컨버터는 MappingJackson2MessageConverter 이다.
  3. MappingJackson2MessageConverter 는 초기값이 세팅될 때 옵션은 다음과 같다.
private MappingJackson2MessageConverter getDefaultMappingJackson2MessageConverter() {
	MappingJackson2MessageConverter jacksonMessageConverter = new MappingJackson2MessageConverter();
	jacksonMessageConverter.setSerializedPayloadClass(String.class);
	jacksonMessageConverter.setStrictContentTypeMatch(true);
	return jacksonMessageConverter;
}
  1. 여기서 에러가 발생하는 원인인 옵션은 jacksonMessageConverter.setStrictContentTypeMatch(true) 이다. 이 옵션은 content type을 엄격하게 검사한다.
  2. MappingJackson2MessageConverter는 application/json 타입이 들어왔을 때 컨버팅 한다.
public MappingJackson2MessageConverter() {
	super(new MimeType("application", "json"));
	this.objectMapper = initObjectMapper();
}
  1. Sqs 리스너의 메시지의 헤더를 확인해보자.
    -> Content-Type이 없다.
  2. MappingJackson2MessageConverter가 컨버팅 할때 지원가능한 MimeType을 체크하는데 헤더에 Content-Type이 없다.
    Null일 경우 StrictContentTypeMatch의 값을 확인한다. 초기 세팅 값이 true이기 때문에 payload의 메시지 타입이 json이지만 MappingJackson2MessageConverter가 동작하지 않아서 에러가 발생 했다.
  3. MappingJackson2MessageConverter를 다시 등록해 주었다.
@Bean
public MappingJackson2MessageConverter mappingJackson2MessageConverter() {
    MappingJackson2MessageConverter jackson2MessageConverter = new MappingJackson2MessageConverter();
    jackson2MessageConverter.setStrictContentTypeMatch(false);
    return jackson2MessageConverter;
}
  1. 정상적으로 바인딩 되었다.

🔫 문제 해결 과정

일단 @Payload의 resolver를 찾았다.

PayloadMethodArgumentResolver

  • resolveArgument() 메소드에 컨버팅 하는 부분이 있다.
  • Json을 object로 바꿔주는 컨버터는 MappingJackson2MessageConverter이다. 이 컨버터는 언제 생성이 되어 이 객체에 담길까?
  • SqsConfiguration가 빈으로 등록될 때 메시지를 처리하는 핸들러를 생성한다.

  • 그때 MappingJackson2MessageConverter를 등록한다.

  • MappingJackson2MessageConverterSmartMessageConverter 를 상속받고 있다.

  • 다음 메소드에서 메시지를 해석한다.

  • 여러 컨버터들 중 찾아 실행한다.

  • 컨버팅 가능한지 확인

  • 헤더에서 MimeType을 찾고 해결가능한지 확인한다.

  • 헤더에 MimeType이 지정되지 않을 경우 isStrictContentTypeMatch옵션을 확인한다.

  • MappingJackson2MessageConverter의 초기 옵션은 true 이므로 해결 불가능하다고 판단 하고 컨버팅을 하지 않아서 에러가 발생한다.

  • isStrictContentTypeMatch옵션을 false로 바꾸고 실행하여 해결~!


profile
물흐르듯 개발하다 대박나기

0개의 댓글