인프런 커뮤니티 질문&답변

열공하짱님의 프로필 이미지

작성한 질문수

[개정판 2023-11-27] Spring Boot 3.x 를 이용한 RESTful Web Services 개발

사용자 등록을 위한 API 구현 - HTTP POST method

상태 200이 안나옵니다.

21.04.06 11:17 작성

·

551

2

안녕하세요 post요청으로 user 저장하는 것을 그대로 따라했는데 postman으로 post요청을 그대로 보내니 200이 아니라  500에러가 납니다.

@PostMapping("/users")

public void createUser(@RequestBody User user){

User savedUser = service.save(user);

}

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `hello.hellospring.user.User` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)

 at [Source: (PushbackInputStream); line: 2, column: 5]

이런 에러가 나는데 찾아보니 Jackson @JsonCreator 같은 걸 추가하라고 하는군요..??

import lombok.AllArgsConstructor;

import lombok.Data;

@Data

@AllArgsConstructor

public class User {

private Integer id;

private String name;

private Date joinDate;

}

User클래스에는 이렇게 추가되어 있는데 생성자가 없다니 무슨 말인지 제가 빼먹은 부분이 있는지 궁금합니다.

답변 3

5

제이크서님의 프로필 이미지

2021. 09. 15. 09:21

@NoArgsConstructor 애노테이션을 추가했을 때 작동이 정상적으로 되고 없었을 때는 안 됐던 이유는 요약하자면, Jackson2HttpMessageConverter 때문입니다.

 

처음에 스프링 부트를 로드하시면, 로깅 시에  Creating shared instance of singleton bean 'mappingJackson2HttpMessageConverter' 과 같은 것이 보일 것입니다. 요약하자면 해당 빈이 등록되었다는 이야기인데, 해당 빈은 스프링 부트 설정에 의해 자동으로 등록되어 쓰입니다.

 

해당 빈은 우리가 HTTP BODY에 JSON 문자열을 담았을 때, 그것을 자바 객체로 변환해주거나, 아니면 자바 객체로 만든 것을 컨트롤러에서 반환했을 때 JSON 문자열로 변환해주는 역할을 하고 있습니다.

 

또 'mappingJackson2HttpMessageConverter' 빈은 내부적으로 'ObjectMapper'라는 객체를 사용해서 JSON 문자열을 자바 객체로 바꾸거나, 자바 객체를 JSON 문자열로 바꾸는데, 'ObjectMapper' 객체는 바로 변환을 시작하는 게 아니라 처음에 일단  변경할 수 있는지 검증부터 합니다. 

 

왜냐하면, 사용자가 잘못된 (제대로 시작되거나 끝나지 않은 혹은 쉼표가 부족한 경우 등) JSON 문자열을 넘기는 경우에 대해서도 대비를 해야하고, 자바 객체에 대해서도 올바른 Getter, Setter가 있는지, 어떤 생성자가 있는지 검증을 해야 변환 시 예측하지 못한 방향으로 로직이 흘러가지 않기 때문입니다. 이는 올바른 예외처리의 일환입니다.

 

ObjectMapper 내부에 BeanDeserializer가 존재하고, deserializeFromObject() 라는 메소드가 존재하는데, 이 메소드에서 기본 생성자가가 없으면 에러가 날 것입니다.

 

기본생성자가 없으면 에러가 나는 이유는 내부적으로 자바 리플렉션을 사용해서 입니다. 자바 리플렉션에 대해서 설명하기는 너무 장황하니 검색을 추천드립니다.

 

자바 리플렉션의 특성상 접근 제어자에 상관 없이 기본 생성자를 사용 가능하기 때문에 기본 생성자의 접근 제어자는 private이어도 무관합니다.

 

참고로 JPA에서도 같은 이유(리플렉션 사용)로 기본 생성자가 필수이니 참고하시면 나중에 도움이 되실 겁니다.

wcsjinn님의 프로필 이미지

2021. 10. 26. 00:42

혹시 그럼 강사님께서는 allargs~로 생성자 어노테이션을 다셨는데 되는 이유는 뭔가요?

3

codesweaver님의 프로필 이미지

2021. 04. 06. 17:50

@AllArgsConstructor 는 현재 클래스가 가지고 있는 모든 멤버변수를 파라미터로 받는 생성자를 자동으로 생상해달라는 어노테이션 입니다. 지금 클래스의 예에서는 아래와 같은 생성자가 자동으로 추가 되었을 것입니다.

public User(Integer id, String name, Date joinDate) {
    this.id = id;
    this.name = name;
    this.joinDate = joinDate;
}

.

문제는 자바의 스펙은 '사용자가 단 한개의 생성자라도 제공하면, 기본 생성자를 제공하지 않는다' 라고 규정하고 있습니다. 그래서 해당 코드에는 '기본 생성자'가 없습니다.

.

추측컨데 JSON 라이브러리에서 User 클래스를 처리하기 위해 '기본 생성자'를 이용해 개체를 생성하는 부분이 있고, 그래서 문제가 있다고 오류를 던졌습니다.

.

답글로 남겨주신 @NoArgsConstructor 는 파라미터가 없는 빈 생성자(기본 생성자)를 자동으로 만들어 달라는 어노테이션 입니다. 아래와 같은 생성자가 자동으로 추가 되니 문제가 해결 된 것입니다.

public User() {
    super();
}

1

열공하짱님의 프로필 이미지
열공하짱
질문자

2021. 04. 06. 11:22

User 클래스에 @NoArgsConstructor 어노테이션을 추가하니까 되긴했습니다!