멍청멍청기록/프로젝트 일지

기본 생성자 없이 직렬화하기 feat. java Object to JSON

  • -
728x90
반응형

현재 개인 프로젝트를 진행하기 위해서 인강을 보면서 약간 의문점이 남아서 해당 부분을 기록합니다.

 

테스트케이스를 작성할 때

Object형태를 JSON형식으로 RequestBody에 요청하는 케이스가 생겼습니다.

 

아래는 위의 설명을 코드화한 것입니다.

@Test
    @DisplayName("/posts 요청")
    void test() throws Exception {

        PostCreate request = PostCreate.builder() // 빌더패턴을 이용했습니다.
                .title("제목입니다.")
                .content("내용입니다.")
                .build();

        ObjectMapper objectMapper = new ObjectMapper(); // 오브젝트메퍼를 이용해 직렬화합니다.
        String json = objectMapper.writeValueAsString(request);

        mockMvc.perform(
                        post("/posts")
                                .contentType(APPLICATION_JSON)
                                .content(json)
                )
                .andExpect(status().isOk())
                .andExpect(content().string("{}"))
                .andDo(print());
    }

생각보다 간단한 테스트 케이스입니다.

근데 여기 테스트 시 실패를 계속합니다.

cannot deserialize from Object value

이 와 같은 에러가 계속 뜨는데요 해당 부분을 검색하면

기본 생성자가 없어서 기본 생성자를 넣어줘야 한다고 합니다.

 

결과 적으로 아래 PostCreate라는 클래스에는 기본 생성자가 없어서 발생한 에러입니다.

@ToString
@Getter
public class PostCreate {

    @NotBlank(message = "제목은 필수입니다!")
    public String title;
    @NotBlank(message = "내용은 필수입니다!")
    public String content;

    @Builder
    public PostCreate(String title, String content) {
        this.title = title;
        this.content = content;
    }
}

 

근데 해당 인강에서는 기본 생성자 없이 테스트가 동작이 잘 됩니다...?

 

뭐지...??

 

 

원인은?

ObjectMapper 이용 시 직렬화 객체에 대한 것을 모르기 때문에 발생한 것입니다.

우리는 소스를 까보면서 아 생성자에는 어떤게 있고 없고를 판단할 수 있는데 ObjectMapper는 모릅니다.

 

해당 내용은 이 블로그에서 정말 잘 설명해줬다

https://da-nyee.github.io/posts/woowacourse-why-the-default-constructor-is-needed/

 

무튼 해결 방법은?

1. 기본 생성자를 넣어준다.

@ToString
@Getter
public class PostCreate {

    @NotBlank(message = "제목은 필수입니다!")
    public String title;
    @NotBlank(message = "내용은 필수입니다!")
    public String content;
    
    public PostCreate() {
        
    }

    @Builder
    public PostCreate(String title, String content) {
        this.title = title;
        this.content = content;
    }
}

 

2. @NoArgsConstructor를 달아준다 (어차피 1번의 방법을 어노테이션으로 대체한 것 뿐)

 

3. 기본 생성자 없이 해당 인자값을 다 지정해준다

@Builder
@JsonCreator
public PostCreate(@JsonProperty("title") String title, @JsonProperty("content") String content) {
    this.title = title;
    this.content = content;
}

 

여기서 배운 점

1) ObjectMappter는 Object to JSON 역직렬화는 setter가 없어도 된다.

왜? java reflection 패키지를 활용하기 때문에.

 

2) 자바 reflection은 컴파일 시점에서 객채 타입을 결정하지만 가져올 수 없는 정보 중 하나가 생성자의 인자 정보들이다.

그렇기 때문에 기본 생성자는 무조건 필수다.

 

3) 기본 생성자 없이 활용하는 다양한 방법들

JsonCreator를 이용한 방법, 기본 생성자를 private로 변경해 불변성을 보장하게끔 하는 방법

 

그리고 이 부분에 대해서는 생각보다 많은 개발자들이 선호하는게 각자 다 다른 것 같다.

 

 

무튼 해결되어 뿌듯...

하지만 뭔가 찜찜...허허..

반응형
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.