@RequestBody Json 데이터 바인딩
Frontend에서 Http Body에 Json 데이터를 담아서 보내면,
Spring Boot Controller의 @RequestBody로 객체로 바인딩된다.
결론부터 말하자면 아래의 방법 중 하나로 바인딩 한다.
- 기본 생성자 + Setter
- 전체 필드 생성자 + @JsonCreator
- 필드 직접 접근
HttpMessageConverter
Spring은 @RequestBody가 붙은 파라미터를 처리할 때 내부적으로 HttpMessageConverter를 사용한다.
- HandlerMethodArgumentResolver → RequestResponseBodyMethodProcessor → HttpMessageConverter 순으로 요청을 처리한다.
- JSON String은 HttpServletRequest.getInputStream()을 통해 읽히고, 적절한 MessageConverter가 선택된다.
Spring Boot는 기본적으로 MappingJackson2HttpMessageConverter를 사용한다.
- 이 컨버터는 Jackson의 ObjectMapper를 사용하여 JSON을 Java 객체로 역직렬화(deserialize) 한다.
- 설정 파일이나 @JsonProperty, @JsonIgnore 등으로 커스터마이징 할 수 있다.
직렬화: 객체들의 데이터를 연속적인 데이터(스트림)로 변형하여 전송 가능한 형태로 만드는 것
역직렬화: 직렬화된 데이터를 다시 객체의 형태로 만드는 것
Jackson 직렬화 / 역직렬화
실험 환경
public class UserDto {
private String username;
private UUID id;
}
아래처럼 debug를 찍어볼 것이다.
postman으로 요청을 보내본다.
JSON -> DTO 역직렬화
역직렬화는 직렬화된 Json 데이터를 Java 객체로 바인딩하는 것을 말한다.
아래 우선순위에 따라 Json 데이터를 Java 객체로 만들기를 시도한다.
- @NoArgsConstructor + setter
- @AllArgsConstructor와 @JsonCreator
- 필드에 직접 접근
case 1) 아무것도 없을 때
public class UserDto {
private String username;
private UUID id;
}
Json 데이터를 Java 객체로 바인딩하지 못해 필드가 null이다.
case 2) @NoArgsConstructor 만 있을 때
@NoArgsConstructor
public class UserDto {
private String username;
private UUID id;
}
마찬가지로 Setter가 없어서 null이다.
case 3) @NoArgsConstructor + @Setter
@NoArgsConstructor
@Setter
public class UserDto {
private String username;
private UUID id;
}
바인딩에 성공했다.
case 4) @AllArgsConstructor
@AllArgsConstructor
public class UserDto {
private String username;
private UUID id;
}
@JsonCreator가 없지만 바인딩에 성공했다.
Jackson은 생성자가 하나만 존재하면, @JsonCreator 없이도 해당 생성자를 자동으로 사용하여 역직렬화를 시도한다.
단, JSON 키 이름이 생성자 파라미터 이름과 정확히 일치해야 한다.
case 5) 아무것도 없지만, 필드가 public
public class UserDto {
public String username;
public UUID id;
}
아무것도 없지만 필드 접근자가 public이면 필드에 직접 접근하여 바인딩할 수 있다.
그러나 Java에서 이러면 안 된다.
DTO -> JSON 직렬화
직렬화는 Java의 객체를 JSON String으로 변환하는 것을 말한다.
Jackson은 기본적으로 Java Bean 규약에 따라 Getter 메서드를 탐색하여 해당 값을 JSON으로 변환한다.
( getFieldName() 또는 isFieldName() 메서드를 탐색 )
Getter가 없으면?
406 Not Acceptable 에러가 발생한다.
WARN 16494 --- [nio-8080-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.HttpMediaTypeNotAcceptableException: No acceptable representation]
406 Not Acceptable 에러는 클라이언트가 요청한 콘텐츠 형식이 서버에서 제공할 수 없는 경우 발생한다.
즉, 서버가 클라이언트가 요청한 Accept 헤더에 표시된 콘텐츠 형식을 제공할 수 없다는 뜻이다.
직렬화를 하지 못해서 발생하는 문제이다.
추가) Getter만 있으면 직렬화 / 역직렬화가 가능하다?
Jackson 2.17부터는 Getter만으로 객체 추론이 가능하여 역직렬화가 가능하도록 변경되었다고 한다.
Jackson 2.2부터 INFER_PROPERTY_MUTATORS라는 옵션이 추가되었다. 해석하면 아래와 같다.
멤버 변경자 (필드 접근 또는 setters)에 직접적으로 접근할 수 없더라도, 같은 이름의 Getter가 보이는 경우 Setter를 추론하여 사용할 수 있다.
즉, public으로 지정된 getter가 있다면 이 getter를 기반으로 setter, 필드명을 추론하여 매핑할 수 있다는 것이다.
2.17에 위의 옵션이 잘 동작하지 않던 버그가 수정되었다고 한다.
즉, 2.17 이상의 Jackson 버전을 사용하면 Getter 만으로도 직렬화 / 역직렬화가 가능하다.
'Spring' 카테고리의 다른 글
[Spring] @Transactional 전파수준 정리 - Propagation 예제코드로 테스트해보기 (0) | 2025.06.19 |
---|---|
[Spring/Redis] Spring @Cacheable과 Redis를 이용한 메서드 레벨 캐싱 (0) | 2025.04.02 |
[DB / Spring ] @Transactional 세부 설정 - 격리 수준 / 전파 수준 설정 (0) | 2024.03.19 |
[Spring] 빈 스코프 (Bean Scope) / 싱글톤, 프로토타입 등 (0) | 2024.02.27 |
[Spring] 빈 생명 주기 콜백 (Bean LifeCycle) @PostConstruct / @PreDestroy (0) | 2024.02.23 |