[F-Lab 모각코 챌린지 56일차] DTO Enum으로 받기 (Jackson)

부추·2023년 7월 26일
0

F-Lab 모각코 챌린지

목록 보기
56/66

DTO ENUM으로 받기

레시피 업로드 기능을 이용하면서, Recipe라는 엔티티에 Value 타입으로 RecipeInfo, RecipeStatistics를 두고 싶었다. Entity 객체 안에 또다른 Reference type의 필드를 두고자 한다는 소리이다.

RecipeInfo는 레시피에 대한 설명, RecipeStatics은 레시피의 통계 관련한 필드(조회수, 평점, 북마크 개수)를 저장할 수 있도록 했다. 각각의 클래스 코드를 까보면 다음과 같다.

@Embeddable
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class RecipeInfo {
    private String description;

    private String cookingTime;

    @Enumerated(EnumType.STRING)
    private Difficulty difficulty;

    @Enumerated(EnumType.STRING)
    private Serving serving;
}

@Embeddable
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class RecipeStatistics {
    // 평점 : 실제로는 10.0으로 나눈 값이 쓰임
    private int ratings;

    private int viewCount;

    private int bookMarkCount;
}

RecipeInfo의 enum타입은 더욱 별 거 없다.

@RequiredArgsConstructor
@Getter
public enum Difficulty {
    EASY("쉬움"),
    NORMAL("보통"),
    HARD("어려움"),
    UNSELECTED("선택안함");

    private final String description;
}

@Getter
@RequiredArgsConstructor
public enum Serving {
    ONE("1인분"),
    TWO("2인분"),
    THREE("3인분"),
    FOUR("4인분"),
    MORE("5인분"),
    UNSELECTED("선택안함");

    private final String description;
}

위 밸류 객체들을 DTO로 어떻게 받아야할까? 그 전에 받아지긴 할까?

궁금했기 때문에, 바로 DTO 클래스를 만들어 실험해봤당.

public class CreateRecipeDto {
    @Getter
    @NoArgsConstructor
    @AllArgsConstructor
    @Builder
    @ToString
    public static class Request {
        @NotNull
        @NotBlank
        private String title;

        private String description = "";
        private CookingTime cookingTime = CookingTime.UNDEFINED;
        private Difficulty difficulty = Difficulty.UNDEFINED;
        private Serving serving = Serving.UNDEFINED;
        
        private List<CreateStepDto.Request> steps = new ArrayList<>();
}

Reference 타입의 데이터들에 대해 "", CookingTime.UNDEFINED 등의 기본 객체를 저런 식으로 할당하면 Builder나 생성자에 null값이 들어와도 할당된 기본 객체가 바인딩된다. @ToString을 통해 DTO 바인딩 상태도 살펴볼 것이다!

이제 @RequestBody가 handler argument에 붙은 Controller 메소드를 살펴보자!

@RestController
@RequestMapping("/recipe")
@RequiredArgsConstructor
@Slf4j
public class RecipeController {
    private final RecipeService recipeService;

    @PostMapping
    public ResponseEntity<CreateRecipeDto.Request> postRecipe(
            @RequestBody @Valid CreateRecipeDto.Request request) {
        log.info("CreateRecipeDto : {}", request);
        return ResponseEntity.ok(request);
    }
}

그냥 @Slf4jlog.info를 이용해 input request dto를 로그로 남기는 작업과, Enum이 다시 JSON 응답으로 deserialize되는지까지를 확인하기 위해 ok()안의 body까지 저렇게 담았다.ㅎㅎ

POSTMAN으로 위와 같은 JSON body를 /recipe URL로 전송해보자. difficultyDifficulty.NORMAL, servingServing.TWO enum 객체가 매핑되길 기대하는 상황이다. coockingTime필드는 보내지 않은 상태다. null값으로 보내졌다고 생각해도 무방하다. 이는 앞서 설정했던, enum을 포함한 reference type의 초기화가 잘 이뤄졌는지 확인하기 위함이다.

이제 응답으로 온 JSON body를 보자! 두근두근
Request의 모든 필드를 보내지 않았음에도 불구하고("steps""cookingTime"), 해당 필드가 존재한다. 심지어 cookingTime의 경우 우리가 최초로 설정했던 CookingTime.UNDEFINED가 할당된 것을 볼 수 있다. 그리고 정상적으로 넘겨준 "NORMAL", "TWO"에 대해서도 오류 없이 응답이 잘 deserialized된 것을 보면 문자열이 enum type에 잘 바인딩되었다고 예상할 수 있다!

이제 콘솔창에 찍혔을 로그를 확인하자.
JSON 응답을 확인했으면 더 볼 필요도 없는 내용이긴 하지만(..) 로그도 잘 찍혔다.


멘토링 회?고

별개로 한 건 완전 많은데(프로젝트 코드 작성, 관련 레퍼런스 공부, JPA 공부..) 프로젝트 코드랑 엮어서 블로그 정리하려고 하니 시간이 너무 부족하다. 일단 오늘 멘토링 하면서 함께 토론했던 내용들 캡처를 보면서 회고해보자..
다대다 연관관계를 사용할 때 제3의 연관테이블을 작성하게 되는데, @ManyToMany를 쓰지 않냐고 여쭤보았다. 확장성이 떨어지기도 하고, 연관 테이블 자체에 필요한 필드 추가가 까다롭다는 점을 얘기해주셨다. 사실 메인은 성능이라고..

그리고 한쪽 엔티티에서 연관관계를 가진 다른 엔티티를 불러와야할 상황이 있을 땐 결국 양방향으로 연관관계를 매핑하는 것이 좋을 것이라고 말씀도 들었다.

또! 개인적으로 고민했던것. 프로젝트 기능 구현만 한다고 하면 크게 오래걸리지 않는데, 주변적인 것에 시간을 쏟는게 좀 걱정이 되서 말씀을 드렸다. 그냥 기능 구현이나 빨리 하겠다고 일갈했더니 그러라고 하셨다(?).

profile
부추튀김인지 부추전일지 모를 정도로 빠싹한 부추전을 먹을래

0개의 댓글