[Spring] Jackson : ResponseBody, RequestBody 꽉잡기

su_y2on·2022년 8월 8일
9

Spring

목록 보기
30/30
post-thumbnail

Response, Request DTO 꽉잡기

최근 프로젝트를 하면서 당연하게 사용하던 DTO 매핑이 어떻게 일어나는지 궁금해졌고, DTO에 붙여주는 여러가지 롬북 어노테이션들.. 이중에 정말 필요한 것만 내가 쓰고있는가?라는 의문이 생겨서 싹 정리를 해보고 싶어졌습니다. DTO에 대부분의 어노테이션은 Object Mapping을 위해 존재합니다.


지금부터 Mapping 과정을 꽉잡아보겠습니다!





1. Request Dto

먼저 spring-boot-starter-web으로 부터 Jackson라이브러리가 함께 추가됩니다. 보통 웹에서는 JSON으로 req body, res body를 교환합니다. 따라서 JSON <-> Java Object의 과정이 필요합니다. 이때 MappingJackson2HttpMessageConverter가 쓰입니다.

@ResponseBody @RequestBody ResponseEntity<>와 같은 곳에서 쓰이며 이외에도 JSON <-> Java Object가 필요한 곳이면 사용할 수 있습니다.




아래는 북마크 생성시에 받아온 정보를 매핑할 RequestDto입니다. URL, Title 그리고 소속 폴더의 id를 받아옵니다.

public class BookmarkRequest {


    @NotBlank(message = "url을 입력하세요")
    private String url;

    @NotBlank(message = "북마크이름을 입력하세요")
    private String title;

    private Long folderId;

    public Bookmark toEntity(Folder folder){
        return Bookmark.builder()
            .url(this.url)
            .title(this.title)
            .folder(folder)
            .build();
    }

}

그리고 Controller에서 @RequestBody부분에 넣어 mapping이 되도록 합니다. 이부분에서 Jackson이 쓰일 것 같네요

@PostMapping("/api/bookmarks")
@ResponseStatus(HttpStatus.OK)
public void create(@Valid @RequestBody BookmarkRequest bookmarkRequest) {
    bookmarkService.create(bookmarkRequest);
 }

요청은 대충 아래와 같을 것입니다. 그러면 requestDto에서 각각의 요소와 매칭을 해줄 것입니다.

{ 
	"url" : "https://naver.com",
 	"title" : "네이버",
 	"folderId" : 2
}



@Requestbody를 타고들어가면 아래와같이 request의 body가 HttpMessageConverter로 던져지고 그곳에서 content-type에 맞는 converter를 찾는다고 적혀있습니다. 즉 JSON에 맞는
MappingJackson2HttpMessageConverter를 찾게 될것입니다.

따라서 해당 구현체를 볼까요? 역시 HttpMessageConverter의 구현체중 하나로 JSON을 변환해준다고 써있네요.

JSON -> Java Object로 변환하는 과정은 먼저 이름을 기반으로 getter 또는 setter를 이용해서 DTO필드를 가져옵니다. 그리고 reflection을 이용해서 DTO에 값을 주입합니다. 이렇게 request body의 값들이 담긴 DTO가 되는 것입니다.

결과적으로 setter와 getter중에 하나 그리고 reflection을 위한 기본생성자가 필요합니다. 근데 기본적으로 setter는 사용을 지양하는 것이 좋으니 getter와 기본생성자가 request DTO에 필요합니다



완성본은 아래와 같습니다. 저는 @builder를 사용할 것이기 때문에 @AllArgsConstructor도 넣어줬습니다. 참고로 @builder는 아무생성자가 없으면 모든 필드를 포함한 생성자를 즉 AllArgusConstructor가 있다고 생각하고 동작하기때문에 생략해줘도 되지만 지금같이 기본생성자와 함께 사용하려면 @AllArgsConstructor가 필요합니다.

@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class BookmarkRequest {
				...
}





2. Response DTO

이번에는 폴더를 생성하고 그 id값을 반환해주는 Response Dto를 만들어보겠습니다. Long하나는 그냥 넘겨줘도 되는거아닌가 할 수도 있지만 그럴경우 data:1이런식으로 JSON형식이 아니게 넘어갑니다. 따라서 data : { "id" : 1 }가 될 수 있도록 DTO를 사용했습니다.

public class FolderIdResponse {

    private Long id;

    public static FolderIdResponse fromEntity(Long folderId) {
        return FolderIdResponse.builder()
            .id(folderId)
            .build();
    }

}

그리고 controller에서는 다른 것은 다 무시하고 마지막 반환하는 부분만 보면 됩니다. 여기선 ResponseEntity가 mapping이 일어나는 곳이 될 것같습니다.

 @PostMapping("/api/folders")
    public ResponseEntity<FolderIdResponse> create(
        @Valid @RequestBody CreateFolderRequest createFolderRequest,
        @AuthenticationPrincipal JwtAuthentication auth) {
        FolderIdResponse create = folderService.create(auth, createFolderRequest);
        return ResponseEntity.ok(create);
    }




스프링 공식문서에는 ResponseEntity도 MessageConverter로 변환이 된다고 되어있고 여기서 JSON으로 변환하기 위해 Jackson이 다시사용됩니다. 그리고 이때는 Getter를 사용해서 DTO값을 가져와 JSON으로 만들어줍니다.

따라서 완성본은 아래와 같이 비교적 깔끔하네요!

@Builder
@Getter
public class FolderIdResponse {
			...
}



3. 결론

Request DTO : getter 와 기본생성자 필요
Response DTO : getter 필요

이렇게 정리를 하고나니 DTO파일을 볼때 한결 깔끔한 마음이듭니다!!







참고

0개의 댓글