개요
이번에도 멘토링 프로젝트 개발을 하며 마주친 이슈입니다. 이번에는 스프링 프로젝트를 여러 번 하면서 별로 겪지 않았었지만, 프로젝트를 오랜만에 하다 보니 접하게 된 실수에 가까웠습니다. 그럼에도 다신 같은 실수를 하지 않기 위해 공유해보려 합니다!
문제 상황
어쩌면 순수 자바로 개발하다가 스프링으로 넘어갈 때 한 번쯤은 접하셨을 실수였을 것이라고 생각이 듭니다.
글 (Board) 쓰기 코드를 작성하며, 글 작성에 사용되는 요청 dto는 아래처럼 레코드로 작성했었습니다. (응답 dto는 여기에 id도 추가했습니다.)
public record BoardCreateRequest(String title, String content) {
}
그리고 생성 코드는 아래처럼 작성했었습니다.
@PostMapping
public ResponseEntity<BoardCreateResponse> create(final BoardCreateRequest request) {
Long newBoardId = boardService.save(request);
BoardCreateResponse response = BoardCreateResponse.from(newBoardId);
URI newBoardURI = URI.create("/boards" + newBoardId);
return ResponseEntity.created(newBoardURI)
.body(response);
}
}
해당 코드대로 실행해 보니, id는 잘 나오는 반면 (auto-increment), 제목과 글 내용은 제대로 나오지 않았습니다.
원인
원인은 위에서 작성했듯 @RequestBody를 사용하지 않아 벌어진 일이었습니다. 스프링 공식 문서를 보면, 스프링 MVC에서 @RequestBody를 사용해야 HttpMessageConverter를 통해 요청의 타입을 메서드 인수로 해결해 준다고 나와있습니다.
김영한 님의 스프링 MVC 1편에서도 관련된 내용이 있습니다.
@RequestBody
@RequestBody를 사용하면 HTTP 메시지 바디 정보를 편리하게 조회할 수 있다. 참고로 헤더 정보가 필요하다면 HttpEntity를 사용하거나 @RequestHeader를 사용하면 된다.
해결 이후
그래서 BoardCreateRequest 전, @RequestBody를 붙였더니 다행스럽게 잘 저장될 수 있었습니다.
추가적으로 알게 된 사실
보통 다른 글들을 보면, DTO를 레코드로 정의하지 않았을 때는 기본 생성자가 없을 경우 예외가 발생함을 확인할 수 있었습니다.
그래서 Lombok의 @NoArgsConstructor를 덧붙인 경우가 종종 있었는데요, 저는 DTO를 레코드로 설정해서 이것을 하지 않아도 문제가 발생되지 않았습니다.
이 점은 우아한테크코스 프리코스를 하며 레코드를 작성했던 글에서도 알지 못했던 내용인데, 레코드에 대한 코드를 까보니 기본 생성자가 protected로 선언되어 있음을 확인할 수 있었습니다.
이것을 알게 되어 더 DTO를 사용할 때는 자바 버전이 가능하다면 레코드로 관리하는 게 더 편한 것임을 알게 되었습니다. 짧은 과정이었지만 이번에도 새롭게 몰랐던 사실을 알게 되어 흥미로웠습니다 😀