개발바닥곰발바닥
728x90

Spring DTO 파일 깔끔하게 관리하기

Spring Boot를 사용해서 Restful API를 개발하다 보니 DTO 파일이 어느새 중구난방 하게 많아져 있는 모습이 찜찜했었는데, 다른 사람들은 이런 경우에 어떻게 파일을 관리하나 찾아보던 중 좋은 글을 발견해서 적용해 보았다.

DTO 파일이 많아지는 이유

예를 들어 쇼핑몰에서 상품에 대한 Entity가 있을 때, 이에 대한 DTO를 만든다고 하면,

Request에 대한 DTO, Response에 대한 DTO 등 하나의 Entity에 대해서도 여러 개의 DTO가 생기게 되고 이게 각각의 클래스로 만들다 보면 DTO 파일이 많아질 수밖에 없다.

해결 방안

이렇게 많아지는 Product 도메인에 대한 DTO 클래스를 하나의 ProductDto 클래스 안에서 Inner Class로 관리하게 되면 하나의 파일에서 여러 개의 DTO 클래스를 깔끔하게 관리할 수 있다.

이렇게 Inner Class로 만들어본 ProductDto 클래스는 아래와 같다.

본래처럼 각각의 파일로 만들었다면 벌써 3개의 파일이 만들어졌겠지만 이 방식으로는 하나의 파일에서 모두 편하게 관리할 수 있다.

public class ProductDto {
    @Getter
    @Builder
    @AllArgsConstructor
    public static class Detail {
        private Long id;
        private String name;
        private Long startPrice;
        private Long categoryId;
        private String content;
        private Long instantPrice;
        private LocalDateTime startDate;
        private String successBidderId;
        private Long successBid;
        private LocalDateTime endDate;
        private Long bidUnit;
        private String sellerId;
        private Long bidderCnt;
        private Long imgCnt;
    }

    @Getter
    @Builder
    @AllArgsConstructor
    public static class Summary {
        private Long id;
        private String name;
        private Long startPrice;
        private Long instantPrice;
        private LocalDateTime endDate;
        private String successBidderId;
        private Long successBid;
        private String sellerId;
        private Long bidderCnt;
        private Long imgCnt;
    }

    @Getter
    @Setter
    @AllArgsConstructor
    public static class Request {
        private String name;
        private Long startPrice;
        private Long categoryId;
        private String content;
        private Long instantPrice;
        @DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
        private LocalDateTime endDate;
        private Long bidUnit;
        private Long sellerId;
        private Long imgCnt;

        public Product toProduct() {
            return Product.builder()
                    .name(name)
                    .startPrice(startPrice)
                    .category(Category.builder()
                            .id(categoryId)
                            .build())
                    .content(content)
                    .instantPrice(instantPrice)
                    .endDate(endDate)
                    .bidUnit(bidUnit)
                    .seller(Member.builder()
                            .id(sellerId)
                            .build())
                    .imgCnt(imgCnt)
                    .build();
        }
    }

}

Service단에서 사용 방법

@Transactional(rollbackFor = Exception.class) //TODO: 확장자 체크
    public ProductDto.Detail addProduct(ProductDto.Request productDto, List<MultipartFile> multipartFiles) throws IOException, IllegalStateException {
        if(multipartFiles == null) {
            throw new IllegalStateException("상품 이미지가 없습니다.");
        }

        productDto.setImgCnt(multipartFiles.size() + 0L);
        Product product = productDto.toProduct();
        productRepository.save(product);
        for (MultipartFile multipartFile : multipartFiles) {
            multipartFile.transferTo(new File("product", "product-" + product.getId().toString() + "-" + multipartFiles.indexOf(multipartFile) + ".png"));
        }

        return toProductDetail(product);
    }

return은 ProductDto.Detail 클래스로 반환해주고 인자값은 ProductDto.Request로 받아오는 방식으로 편하고 깔끔하게 DTO 클래스들을 관리할 수 있는 모습을 확인할 수 있다.

 

참고 블로그 글

https://velog.io/@ausg/Spring-Boot%EC%97%90%EC%84%9C-%EA%B9%94%EB%81%94%ED%95%98%EA%B2%8C-DTO-%EA%B4%80%EB%A6%AC%ED%95%98%EA%B8%B0

728x90
profile

개발바닥곰발바닥

@bestinu

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!