삽질 주도 개발
article thumbnail

Spring boot에서 통합 테스트를 진행하다가 아래 코드에서 에러가 발생했다.

Error while extracting response for type [class com.tistory.rjvv.learntest.exception.format.ErrorResponse] and content type [application/json]
org.springframework.web.client.RestClientException: Error while extracting response for type [class com.tistory.rjvv.learntest.exception.format.ErrorResponse] and content type [application/json]

내가 만든 ErrorResponse 타입과 application/json 타입의 응답을 추출하는 과정에서 에러가 발생했다고 한다.

 

문제는 예상할 수 있듯이, ErrorResponse 객체를 응답으로 받기 위해 역직렬화가 제대로 되지 않았다는 것을 예상할 수 있었다.

 

현재 위의 코드는 ObjectMapper가 직렬화할 수 있는 조건(getter)은 만족되었지만, 역직렬화할 수 있는 조건이 만족되지 않는다. 그렇다면 역직렬화의 조건이 무엇일까?

 

jackson-module-parameter-names을 사용하고 있는가에 따라 구분할 수 있다.

해당 모듈을 사용하지 않는 경우는 기본 생성자와 getter를 반드시 가지고 있어야 한다.

해당 모듈을 사용하는 경우는 위의 경우를 포함하고, 기본 생성자 없이 full-Delegate-based creator method 혹은 full-property-based creator method를 사용할 수 있다.

 

스프링은 해당 모듈이 자동 구성되어 있다.

 

ObjectMapper의 동작 방식에 대해서 찾아보면 관련 글이 많으니 참고하면 좋다.

 

나는 모든 인자가 있는 생성자는 아니지만, 인자를 받아서 객체 내의 모든 필드를 채웠기 때문에 문제가 없을 거라고 생각을 했던 것이다.

 

코드를 조금 더 뜯어보니 확인할 수 있었다.

BaseDeserializerBase.class의 deserializeFromObject() 메서드를 살펴보자.

위의 _nonStandardCreation 의 선언부를 확인해 보자.

기본이 아닌 생성자를 표시하는 플래그 ("full" delegation or properties-based creator)를 의미한다고 작성되어 있다.

  • delegate-based creator method는 하나의 인수를 전달해서 전달된 인수로 모든 필드에 값을 바인딩하는 방식을 의미한다.
    (보통 @JsonCreator와 함께 사용)
  • property-baesd creator method는 각 필드에 맞는 인수를 전달해서 각각의 필드에 바인딩하는 방식을 의미한다.
    (보통 @JsonProperty와 함께 사용)

즉, 나의 코드는 nonStandardCreation이 true가 되어 생성자로 역직렬화를 수행한다. 하지만, 위의 생성자 방식 중 어떠한 것도 따르지 않고 있기 때문에 에러가 발생했고 추가적인 조치가 필요했다.

 


 

위의 문제의 해결 방법은 아주 많다고 생각되지만, 내가 확인한 방법은 다음과 같다.

  1. 기본 생성자와 getter를 사용한다.
  2. delegate-based or property-based creator method 사용하기
  3. ErrorResponse를 커스텀 역직렬 객체를 적용한다.

1번은 기본 ObjectMapper의 사용 방식이니 2번과 3번의 방법으로 테스트를 진행한다.

 

1-1. Delegate-based creator method

@JsonCreator에서는 delegate-based creator를 다음과 같이 정의한다.

단수 인자의 생성자 혹은 팩토리 메서드를 사용해서 객체를 생성한다.

 

1-2. Property-based creator method

각 필드에 대한 프로퍼티를 가지는 생성자를 기반으로 객체를 생성한다.

주의해야 할 점은 필드의 개수와 생성자의 개수를 일치시켜야 한다.

 

2. Custom Deserializer

json에 포함된 "code"는 Error Enum을 구분하기 넣어둔 enum의 이름이다. 해당 키를 찾아 String으로 바인딩을 진행한 후, enum에 해당 코드가 존재하는지 확인해서 새로운 ErrorResponse를 반환한다.

 

테스트 코드로 확인해보았다.

 

예제는 https://github.com/rxjw95/object-mapper-custom-deserialize에서 확인할 수 있습니다.

 

GitHub - rxjw95/object-mapper-custom-deserialize: ParameterNamesModule과 StdDeserializer를 활용해서 기본 생성자없이

ParameterNamesModule과 StdDeserializer를 활용해서 기본 생성자없이 커스텀 생성자로 역직렬화하기 - GitHub - rxjw95/object-mapper-custom-deserialize: ParameterNamesModule과 StdDeserializer를 활용해서 기본 생성자없이 커

github.com

 


 

 

Reference

https://www.baeldung.com/jackson-deserialization

https://www.cowtowncoder.com/blog/archives/2011/07/entry_457.html

 

틀렸거나 개선할 점은 댓글 부탁드립니다. 저에게 큰 도움이 됩니다 :)

'🧑🏻‍💻Dev' 카테고리의 다른 글

@MappedSuperclass  (0) 2023.02.07
[리눅스 명령어] grep  (0) 2023.01.19
[리눅스 명령어] 파일 찾기  (0) 2023.01.18
상속관계 매핑  (0) 2022.12.06
연관관계 매핑 종류  (0) 2022.12.05