본문 바로가기
Development/Java

[SpringBoot] @RequestBody 에 들어가는 Object 는 No-Argument Constructor 가 존재해야 한다.

by 폴피드 2019. 10. 21.
728x90
반응형

아래와 같이 @RestController  에 메소드가 있다고 생각해보자.

@PostMapping
public ResponseEntity<AccountResDto> getResDto(@RequestBody AccountReqDto dto){
    return ResponseEntity.ok(AccountResDto.builder().email("test@test.com")
    .loginId("test")
    .username("tester")
    .build()
  );
}

Post 요청에 대해서 AccountReqDto 라는 Object 형태의 파라메터를 받아서 Response 를 리턴하는 메소드 이다.

AccountReqDto 는 다음과 같이 되어있다.

@Getter
public class AccountReqDto {

    private String loginId;

    @Builder
    public AccountReqDto(String loginId) {
        this.loginId = loginId;
    }
}

그리고 나서 난 아무 생각없이 테스트 케이스를 돌렸다.

@Test
public void getResDtoTest() throws Exception {
  MvcResult mvcResult = this.mockMvc.perform(
      post("/api/v1/accounts")
      	.contentType(MediaType.APPLICATION_JSON_UTF8)
      	.content(objectMapper.writeValueAsString(reqDto)))
      .andExpect(status().isOk())
      .andDo(print())
      .andReturn();

  String contentAsString = mvcResult.getResponse().getContentAsString();
  AccountResDto accountResDto = objectMapper.readValue(contentAsString, AccountResDto.class);
  Assert.assertEquals("test", accountResDto.getLoginId());
}

당연히 성공할줄 알았던 테스트 케이스는 실패를 한다. 실패의 원인은 다음과 같다. 

 

Status expected:<200> but was:<400>
Expected :200
Actual   :400

 

아니 왜??? 왜 400 error 가 났을까??

결론부터 말하자면 AccountReqDto 클래스에 기본 생성자가 없기 때문에 위와 같은 에러가 발생 했다. 그렇다면 왜 기본 생성자가 있어야 하나??? 그걸 알기 위해서 찾아보다 보니 Serialization 이 연관이 있다는 것을 찾게 되었다. 

 

Serialization

Serialization 은 Object 의 상태를 byte stream 형태로 변환하는 것을 말한다. Deserialization 은 반대의 경우를 말한다. java  에서 Serialization 을 이용하면 객체를 네트워크를 통해 전송이 가능해진다. 

 

그럼 왜 Serialization에는 No-Argument Constructor 가 필요한걸까? 

To allow subtypes of non-serializable classes to be serialized, the subtype may assume responsibility for saving and restoring the state of the supertype's public, protected, and (if accessible) package fields. The subtype may assume this responsibility only if the class it extends has an accessible no-arg constructor to initialize the class's state. It is an error to declare a class Serializable if this is not the case. The error will be detected at runtime.
During deserialization, the fields of non-serializable classes will be initialized using the public or protected no-arg constructor of the class. A no-arg constructor must be accessible to the subclass that is serializable. The fields of serializable subclasses will be restored from the stream.

(출처 : https://docs.oracle.com/javase/10/docs/api/java/io/Serializable.html)

 

요약을 해보면 클래스의 상태를 초기화 할 때 No-Argument Constructor 를 이용한다는 의미이다. 만약 선언되어있지 않으면 에러가 발생한다고 되어있다. 

 

결과적으로 Serialization 을 위해서는 No-Argument Constructor  가 필요하고 그것을 사용하고 있는 Controller 의 RequestBody 에 들어가는 Object 에도 No-Argument Constructor 가 존재 해야 한다는 의미이다.

 

그래서 위 소스에서 테스트가 성공하기 위해서는 No-Argument Constructor 를 만들어 주거나 Lombok 을 사용한다면 @NoArgsConstructor 를 넣어주면 된다.

 

728x90
반응형