[Baeg-won Clothing Gallery] 7. CART 페이지 구현

2023. 7. 24. 14:01·🚗 Backend Toy Project/Baeg-won Clothing Gallery

📝 Outline

  • 마침내 Cart 페이지 구현이 완료되었다.
  • 면접 준비로 인해 좀 늦어진 감이 있긴 하지만... (참 좋은 핑계거리야)
  • 이번에 해당 페이지를 구현하면서 삽질한 것도 좀 있고 그 과정에서 나름 배운 것도 있어서 이를 하나씩 정리해보려고 한다.
  • 구현 스타일은 다음과 같다.

  • 그럼 바로 리뷰로 들어가보자.

📝 Review

💬 연관관계 매핑

  • 이번에 Cart 페이지를 구현하면서 연관관계가 더 복잡해졌다.
  • 우선 한 명의 고객은 하나의 장바구니만 가질 수 있기 때문에 Cart와 Customer의 관계는 1:1로 설정해주었다.
  • 또한 하나의 장바구니에는 여러 개의 상품이 담길 수 있고, 하나의 상품은 여러 장바구니에 담길 수 있으므로, Cart와 Product의 관계는 N:M이 되어야 했다. 따라서 이는 1:N, N:1의 관계로 나누어 설계해주었다.
  • 이로써 추가된 엔티티는 Cart, Customer, ProductCart가 되시겠다.
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Cart {

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @OneToMany(mappedBy = "cart")
    private List<ProductCart> productCarts = new ArrayList<>();

    @OneToOne
    @JoinColumn(name = "customer_id")
    private Customer customer;
}
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Customer {

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String username;
    private String password;
    private String nickname;

    private String phone;
    private String address;

    @Column(name = "membership_level")
    private int membershipLevel;
    private String status;

    @OneToOne(mappedBy = "customer")
    private Cart cart;

    @Column(name = "register_date")
    private LocalDateTime registerDate;

    @Column(name = "modify_date")
    private LocalDateTime modifyDate;

    @PrePersist
    public void registerDate() {
        this.registerDate = LocalDateTime.now();
    }
}
@Entity
@Getter
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Builder
public class ProductCart {

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String size;
    private int quantity;

    @Column(name = "total_price")
    private int totalPrice;

    @ManyToOne
    @JoinColumn(name = "product_id")
    private Product product;

    @ManyToOne
    @JoinColumn(name = "cart_id")
    private Cart cart;
}
  • 보면 알겠지만 ProductCart 객체는 Product와 Cart에서 각각 @OneToMany로 매핑되는 것을 확인할 수 있다.
  • 위처럼 설계하는게 정답은 아닐 수 있지만 우선은 이렇게 진행해볼 예정이다.

 

💬 CascadeType.REMOVE

  • 이번에 장바구니 기능을 구현하면서 문득 떠오른 것이 있었다.
  • 기존에 존재하던 상품이 판매 중지될 경우, 그 상품이 어느 고객의 장바구니에 담겨있었다면 어떻게 해야 할까?
  • 당연히 그 장바구니에 담겨 있는 상품 정보도 함께 제거해야 할 것이다.
  • 아하! 그럼 CascadeType.REMOVE를 쓰면 되겠구나!

  • Product를 아래와 같이 수정하고, 장바구니에 상품을 담은 뒤, 데이터베이스에서 Product 개체를 삭제해보았다.
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Product {

    ...

    @OneToMany(mappedBy = "product", cascade = CascadeType.REMOVE)
    private List<Size> sizes = new ArrayList<>();

    @OneToMany(mappedBy = "product", cascade = CascadeType.REMOVE)
    private List<Image> images = new ArrayList<>();

    @OneToMany(mappedBy = "product", cascade = CascadeType.REMOVE)
    private List<Feature> features = new ArrayList<>();

    @OneToMany(mappedBy = "product", cascade = CascadeType.REMOVE)
    private List<ProductCart> productCarts = new ArrayList<>();

    ...
}
  • 결과는 다음과 같았다.
cannot delete or update a parent row a foreign key constraint fails
  • 음... 외래키 제약조건 때문에 부모 개체의 행을 지울 수 없다고?
  • 난 분명 @OneToMany에 cascade 옵션을 줬는데?!

  • 문제를 해결하기 위해 구글링을 하다보니 나의 멍청함을 깨달을 수 있었다.
  • cascade 옵션은 JPA에 의해 처리되는 것으로 데이터베이스에서 직접 개체를 삭제하려고 하는 경우에는 적용되지 않는 것이었다.
  • 즉, JPA를 통해 코드 상으로 delete() 또는 remove()를 수행해주어야 제대로 동작하는 것이었다.
  • 그것도 모르고 아무런 CASCADE 조건도 걸려 있지 않은 데이터베이스에서 DELETE FROM product WHERE id = 1; 이러고 있었으니 오류가 날 수 밖에
  • 그래도 확인은 해봐야 하니까 아래와 같이 메서드를 구현하고 테스트해보았다.
@RestController
public class ProductApiController {

    @Autowired
    private ProductService productService;

    @DeleteMapping("product/delete/{product_id}")
    public ResponseEntity<?> delete(@PathVariable Long product_id) {
        productService.deleteProduct(product_id);

        return new ResponseEntity<>(1, HttpStatus.OK);
    }
}
@Service
public class ProductService {

    @Autowired
    private ProductRepository productRepository;

    @Transactional
    public void deleteProduct(Long product_id) {
        productRepository.deleteById(product_id);
    }
}
  • 결과는 성공적이었다.

 

💬 @OnDelete(action = OnDeleteAction.CASCADE)

  • 위에서 CascadeType.REMOVE를 통해 상품이 삭제되면 해당 상품과 연관되어 있는 모든 자식 개체를 지우도록 하였다.
  • 당연한 것이, 상품이 삭제될 경우 그 상품의 사이즈, 이미지, 특징 등과 같은 요소 역시 필요가 없어지기 때문이다.
  • 다만 나 같은 경우 위처럼 할 경우 삭제를 위한 쿼리문이 너무 많이 나가게 된다.
  • 그도 그럴것이 전부다 @OneToMany로 매핑되어 있기 때문에 상품 하나가 삭제됐을 때 그와 연관되어 있는 모든 Many가 삭제되기 때문이다.
  • 따라서 뭔가 다른 방법이 필요했고, 결국 찾은 방법이 바로 @OnDelete(action = OnDeleteAction.CASCADE)이다.
  • CascadeType.REMOVE와 @OnDelete(action = OnDeleteAction.CASCADE)의 가장 큰 차이는, JPA에 의해 처리되느냐, DDL에 의해 DB단에서 처리되느냐이다.
  • CascadeType.REMOVE
    • 전자의 방식을 취할 경우, JPA에 의해 외래 키를 찾아가며 참조하는 레코드를 제거해주게 된다.
    • 따라서, JPA 상에서는 참조하고 있는 레코드의 개수만큼 delete 쿼리가 생성된다.
  • @OnDelete(action = OnDeleteAction.CASCADE)
    • 후자의 방식을 취할 경우, 데이터베이스 자체에서 on delete cascade 제약조건이 걸리게 되며, 이를 통해 참조하는 레코드가 모두 제거되는 것이다.
    • 따라서, JPA 상에서는 한 개의 delete 쿼리가 생성되고, 데이터베이스에서 이를 처리해준다.
  • 자세한 내용은 여기에서..


 

GitHub - Baeg-won/Baeg-won-Mall

Contribute to Baeg-won/Baeg-won-Mall development by creating an account on GitHub.

github.com

 

저작자표시 (새창열림)

'🚗 Backend Toy Project > Baeg-won Clothing Gallery' 카테고리의 다른 글

[Baeg-won Clothing Gallery] 9. PROFILE 페이지 구현  (0) 2023.07.31
[Baeg-won Clothing Gallery] 8. WISH 페이지 구현  (0) 2023.07.28
[Baeg-won Clothing Gallery] 6. DETAIL 페이지 구현  (0) 2023.07.16
[Baeg-won Clothing Gallery] 5. SALE, CONTACT 페이지 구현  (0) 2023.07.15
[Baeg-won Clothing Gallery] 4. CLOTHING, FOOTWEAR, ACCESSORIES 페이지 구현  (0) 2023.07.14
'🚗 Backend Toy Project/Baeg-won Clothing Gallery' 카테고리의 다른 글
  • [Baeg-won Clothing Gallery] 9. PROFILE 페이지 구현
  • [Baeg-won Clothing Gallery] 8. WISH 페이지 구현
  • [Baeg-won Clothing Gallery] 6. DETAIL 페이지 구현
  • [Baeg-won Clothing Gallery] 5. SALE, CONTACT 페이지 구현
Baeg-won
Baeg-won
  • Baeg-won
    좋았다면 추억이고 나빴다면 경험이다.
    Baeg-won
  • 전체
    오늘
    어제
    • 분류 전체보기
      • 🍃 Spring, Spring Boot
        • 스프링 프레임워크 기초
        • 스프링 핵심 원리 - 기본편
        • 자바 ORM 표준 JPA 프로그래밍 - 기본편
        • 스프링 MVC
        • 실전! 스프링 부트와 JPA 활용1 - 웹 애플리..
      • 🥑 Web Technoloy
      • 🚗 Backend Toy Project
        • 스프링 부트 게시판
        • Photogram
        • Baeg-won Clothing Gallery
      • 🥇 Problem Solving
        • Breadth-First Search
        • Depth-First Search
        • Backtracking
        • Simulation
        • Two-pointer
        • Binary Search
        • Greedy
        • Dynamic Programming
        • Minimum Spanning Tree
        • Dijkstra
        • Floyd warshall
      • ☕ Java
        • 명품 자바 에센셜
        • Applications
      • 🍦 JavaScript
        • JavaScript 기초
      • 🐧 Linux
        • 이것이 리눅스다(CentOS 8)
      • 📟 Database
        • 혼자 공부하는 SQL
      • 🧬 Data Structure
      • 🎬 HTML
      • 🎤 Tech Interview
      • 📌 etc
        • Unity 2D Raising Jelly Game
        • C++
        • 영어 쉐도잉
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
Baeg-won
[Baeg-won Clothing Gallery] 7. CART 페이지 구현
상단으로

티스토리툴바