👾 문제식별
RequestDto 클래스 어노테이션 수정을 했다.
데이터 수정의 위험이 있는 @setter 대신 @builder로 변경한 뒤, 컨트롤러 테스트를 해봤는데 원인 모를 400 에러 발생.
{
"timestamp": "2024-08-24T09:56:57.523+00:00",
"status": 400,
"error": "Bad Request",
"path": "/notebook"
}
🔍 원인분석
우선 HttpMessageNotReadableException 가 무슨 에러인지 알아보자.
HttpMessageConverter.read가 실패할 때 던져지는 exception이라는데
HttpMessageConverter.read를 자세히 살펴보면
HttpMessageNotReadableException은 inputMessage를 clazz타입으로 변환하려다가 오류가 난거라고 한다.
나의 케이스는 json형식의 text를 RequestDto로 변환하다가 문제가 생긴것이다.
🤔 해결 방법 시도
controller
dto
- 가장 나중에 추가한 @Builder 삭제 : 저장 서비스 테스트할 때 필요해서 삭제 불가.
- 혹시나 역직렬화할때 필요한건가? 하고 @Setter 추가 : 동일오류(HttpMessageNotReadableException)
- 또 혹시나? @NotBlank, @Valid 제거 : 동일오류
- @RequestBody를 쓸 때에는 기본 생성자가 필요하다고 해서 @NoArgsConstructor를 붙이려 했으나 @Builder와 충돌나면서 실패
- 그럴때에는 보통 @AllArgsConstructor를 같이 붙여주면 오류가 안나기 때문에 @NoArgsConstructor, @AllArgsConstructor 추가 : 성공
✅ 최종 해결 방법
dto 수정 : @NoArgsConstructor 추가, 빌더 생성자 추가
테스트 결과 : 200 저장 성공
💡 해결 방법 설명
결론은 Json 데이터의 역직렬화에 대해서 잘 알지못해서 발생한 문제이다.
1. @RequestBody를 사용하여 JSON 데이터를 객체로 변환할 때, Jackson은 기본적으로 기본 생성자를 사용하여 객체를 생성한다.
하지만 생성자가 없어도 역직렬화는 가능하다.
매개변수 이름을 사용하여 Java 개체를 직렬화 및 역직렬화하는 방법을 제공하는 Java 라이브러리인 ParameterNamesModule을
사용하면 된다. (스프링부트를 사용한다면 대개 추가 하는 spring-boot-starter-web에 해당 모듈이 주입되어있다.)
* json 역직렬화 참고 : https://weights-learn-develop.tistory.com/41
2. 기본생성자가 없는 객체에 @Builder를 붙이면
Jackson은 생성자를 호출하지 않고 build() 메서드를 사용하여 객체를 생성하므로, 매개변수 이름을 사용할 수 없다.
매개변수 이름 기반으로 JSON 필드를 매핑하는 ParameterNamesModule은 동작이 불가하다.
3. 기본생성자를 추가해서 빌더패턴으로 객체 생성되는 걸 방지해야했다.
하지만 @builder와 @NoArgsConstructor를 동시에 사용하면 객체 생성 방식이 충돌되어 컴파일 오류가 발생한다.
그렇다고 이 오류를 해결하기 위해 @AllArgsConstructor를 붙이는 건 불필요하다고 생각했다.
4. @NoArgsConstructor를 붙이고 수동으로 필요한 필드를 매개변수로 하는 생성자를 만든 다음, 빌더로 지정해주었다.
📝 재발 방지 대책
- jackson의 역직렬화 하는 방법에 대해서 알아놓자
- 원래는 기본 생성자를 통하여 객체생성을 하였지만, 기본 생성자가 없어도 가능(공식문서에서는 @JsonProperty, 빌더 패턴 등 기본생성자 없이도 매핑되는 방법을 제시하고 있다.)
* jackson 공식문서 : https://github.com/FasterXML/jackson-databind/
'개발 공부 > 트러블슈팅' 카테고리의 다른 글
AWS EC2 상태검사가 1/2만 검사 통과 상태 일때 (0) | 2024.10.24 |
---|---|
@OneToMany 관계 정의 후, getter 호출 시 NullPointerException이 발생하는 원인과 문제 해결 (0) | 2024.09.29 |
H2 datasource 정보 입력이후, NullPointerException이 발생하는 원인과 해결방법 (2) | 2024.09.14 |
[Spring Security] permit을 적용한 API를 테스트 했을때 401 에러가 발생하는 원인과 해결방법 (2) | 2024.08.31 |
@Valid 가 작동하지 않는 원인과 해결방법 (0) | 2024.08.26 |