-
DTO를 통해 mapping된 엔티티 데이터 묶어서 보내기 ( + 순환참조 방지 DTO)Spring 2021. 10. 26. 14:17
DTO를 통해 엔티티 정보와 함께 참조된 데이터를 백엔드에서 프론트엔드로 보내고자 할 때,
일일이 나열하여 하나씩 데이터를 보내는 것이 아닌 json객체의 형식과 같이 데이터 안에 데이터 묶음으로 전하고자 하였다.
엔티티 mapping 상태
@Entity @Table(name = "user") @Getter @Setter @NoArgsConstructor public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; @Column(name = "firstname", nullable = false, length = 45) private String firstname; @Column(name = "lastname", nullable = false, length = 45) private String lastname; @Column(length = 128, nullable = false, unique = true) private String email; @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true) private Set<Product> products = new HashSet<>();
@Entity @Table(name = "product") @Getter @Setter @NoArgsConstructor public class Product extends BaseTimeEntity{ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; @Column(nullable = false, length = 45) private String name; private String description; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id") private User user;
다음과 같이 User-Product는 1대다 즉, one-to-many, Product-User는 다대1 즉, many-to-one 의 관계를 지니고 있다.
N:1의 데이터 DTO로 데이터 전하기
N:1의 관계에서 ProductDTO를 만들어 백엔드에서 프론트엔드로 전달하고자 할 때, 다음과 같이 DTO안의 클래스를 만들어주고 정보를 저장하도록 설정해주면 된다. Product 하나당 User정보는 하나씩 만 가지고 있으므로 데이터를 넣어 전달하면 된다.
@Getter @Setter public class ProductDTO { private Integer id; private String name; private String description; private ProductUser user; public ProductDTO(Product product) { this.id = product.getId(); this.name = product.getName(); this.description = product.getDescription(); this.user = new ProductUser(product.getUser()); } @Getter static class ProductUser{ private Integer id; private String fullname; private String email; private String imageUrl; private String description; public ProductUser(User user){ this.id = user.getId(); this.fullname = user.getFullname(); this.email = user.getEmail(); this.imageUrl = user.getImageUrl(); this.description = user.getDescription(); } } }
DTO안의 또다른 클래스 ProductUser는 생성자 를 가지고 있으며, User 정보를 넣어주면 유저의 정보를 클래스정보에 저장하여 데이터를 넘겨줄 수 있게 된다.
1:N의 데이터 DTO로 데이터 전하기
1:N의 관계에서 참조된 데이터를 전달하고자 한다면 List형식의 타입으로 데이터를 넣게 된다. Product와 ProductImage는 1대다, one-to-many관계이고 즉 Product 하나당 전달해야할 ProductImage의 갯수는 여러개로 List타입이 된다. DTO에서 참조된 리스트형식의 데이터를 보낼때는 다음과 같이 작성해준다.
@Getter @Setter public class ProductDTO { private Integer id; private String name; private String description; private List<ProductImage> productImages; public ProductDTO(Product product) { this.id = product.getId(); this.name = product.getName(); this.description = product.getDescription(); this.productImages = ProductImage.imageList(product.getProductImages()); } @Getter static class ProductImage{ private String name; private String imageUrl; static List<ProductImage> imageList(List<com.yunhalee.walkerholic.entity.ProductImage> productImages){ List<ProductImage> productImageList = new ArrayList<>(); productImages.forEach(productImage -> productImageList.add(new ProductImage(productImage))); return productImageList; } public ProductImage(com.yunhalee.walkerholic.entity.ProductImage productImage){ this.name = productImage.getName(); this.imageUrl = productImage.getFilePath(); } } }
ProductDTO클래스 안에 ProductImage 클래스를 작성해주고, ProductDTO는 이 클래스를 List타입으로 갖게 된다.
DTO내에서 List<ProductImage>를 그대로 사용하려고 하면 양방향 관계에서의 지연로딩으로 인해 순환참조(아직 연관관계의 데이터를 알지 못하는 것과 더불어 Product <-> ProductImage의 양방향때문에 같은 데이터를 중복시키게 되어 발생하는 infinite recursion오류 발생)가 발생하게 되므로, DTO를 사용하여 새로운 imageList를 만들어 static class안의 생성자를 이용해 productImage.getName()과 같은 특정 데이터를 지정하여 추가해도록 해준다.
참고로 순환참조는 @JsonIgnore(해당 어노테이션을 가지고 있을 시에 json에서 호출하지 않는다)를 사용하여 해결할 수 도 있지만, 화면이나 로직이 엔티티에 침범하게 되므로 사용을 권장하지는 않는다.
(참고한 사이트)
https://tlqckd0.tistory.com/m/13
https://velog.io/@hyunho058/JPA-%EC%88%9C%ED%99%98%EC%B0%B8%EC%A1%B0
'Spring' 카테고리의 다른 글
JPA delete 쿼리가 실행되지 않을 때 (0) 2021.10.26 Controller와 RestController ( + ResponseEntity) (0) 2021.10.26 controller에서 파라미터를 받는 방법 : @RequestParam, @RequestBody, @PathVariable (+ DTO를 포함하는 DTO) (0) 2021.10.26 hibernate lazy initialization exception could not initialize proxy - no session 에러 발생시 (0) 2021.09.05 DTO 사용범위 (0) 2021.09.05