1. Feign에서 커스텀 Serializer/Deserializer 사용하기
Feign을 사용할 땐 외부 API의 응답에 따라 커스텀 Serializer/Deserializer가 필요한 경우가 있다. 이 경우 별도의 Config 클래스 내에서 @Bean 메서드를 통해 JacksonDecoder를 스프링 빈으로 등록하면 된다. 이때, JacksonDecoder의 생성자에 커스텀 Serializer/Deserializer가 등록된 ObjectMapper를 넘겨준다. 클래스 레벨에 @Configuration 애너테이션을 붙이면 전역 설정이 되므로 주의하자.
open class FeignConfig {
@Bean
open fun jacksonDecoder() = JacksonDecoder(ObjectMapper().apply {
this.registerModule(JavaTimeModule().also {
it.addSerializer(Instant::class.java, InstantSerializer())
it.addDeserializer(Instant::class.java, InstantDeserializer())
})
//...
})
class InstantSerializer : JsonSerializer<Instant?>() {
override fun serialize(instant: Instant?, jsonGenerator: JsonGenerator, serializerProvider: SerializerProvider?) {
//...
}
}
class InstantDeserializer : JsonDeserializer<Instant?>() {
override fun deserialize(jsonParser: JsonParser?, des: DeserializationContext?): Instant? {
//...
}
}
}
그리고 해당 클래스를 @FeignClient의 configuration 속성에 지정해주면 된다.
@FeignClient(
name = "my-feign-client",
url = "/my",
configuration = [FeignConfig::class]
)
interface MyFeignClient {
//...
}
2. Reactive Feign에서 커스텀 Serializer/Deserializer 사용하기
Reactive Feign은 약간 다르다. @JsonComponent를 이용해서 빈으로 등록된 ObjectMapper를 건드려줘야 한다. 사실 일반 Feign처럼 설정하는 게 왜 안 먹히는지는 정확히 모르겠지만... 기본적으로 Reactive Feign이 지원이나 관심도적인 측면에서 Feign에 비해 떨어지다보니 레퍼런스도 적고 여러모로 사용할 때 불편한 점이 있다.
스프링 부트는 기본적으로 Jackson ObjectMapper를 빈으로 등록하는데, @JsonComponent이 붙혀진 Serializer/Deserializer는 빈으로 등록된ObjectMapper에 자동으로 등록된다.
class FeignConfig {
@JsonComponent
class InstantDeserializer : JsonDeserializer<Instant?>() {
override fun deserialize(jsonParser: JsonParser?, des: DeserializationContext?): Instant? {
//...
}
}
@JsonComponent
class InstantSerializer : JsonSerializer<Instant?>() {
override fun serialize(instant: Instant?, jsonGenerator: JsonGenerator, serializerProvider: SerializerProvider?) {
//...
}
}
}
참고로 @JsonComponent 애너테이션은 @Component를 포함한다.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface JsonComponent {
@AliasFor(annotation = Component.class)
String value() default "";
Class<?>[] type() default {};
Scope scope() default Scope.VALUES;
enum Scope {
VALUES,
KEYS
}
}
3. 참고
'Spring > Spring' 카테고리의 다른 글
[Spring] @ContextConfiguration이란? (0) | 2023.07.16 |
---|---|
[Spring] feign + hystrix 사용 시 특정 예외 무시하도록 처리하기 (0) | 2023.05.15 |
[Spring] @Bean 애너테이션의 name 속성과 value 속성 (0) | 2023.03.07 |
[Spring] 빈 중복 시 해결방법(@Autowired, @Qualifier, @Primary) (0) | 2022.12.19 |
[Spring] 트랜잭션 전파 옵션 (0) | 2022.09.07 |