영호

[JPA] 엔티티 구조 변경을 통한 로직 복잡성 낮추기, 쿼리 개선기 본문

JPA

[JPA] 엔티티 구조 변경을 통한 로직 복잡성 낮추기, 쿼리 개선기

0h0 2023. 11. 11. 15:47

들어가면서

이번 글에서는 프로젝트를 진행하며 엔티티 구조를 변경해 로직 복잡성을 낮추고, 쿼리를 개선한 경험을 작성하려고 합니다.

상황 및 엔티티 구조 설명

상황 설명

저희 프로젝트는 개인 카페의 쿠폰을 한 곳에서 관리해주는 서비스 입니다. 여기에 더불어 카페 사장님은 쿠폰의 디자인, 쿠폰에 찍히는 스탬프의 위치를 모두 커스텀 할 수 있습니다. 그리고 발급된 쿠폰은 발급 당시의 이미지 정보를 토대로 보여지게 됩니다.

A쿠폰 발급 후 사장님이 카페의 쿠폰 이미지를 변경해도 A쿠폰은 발급 당시의 이미지로 보여집니다. 즉, 쿠폰은 발급 당시 이미지, 스탬프 위치 정보를 적용합니다.

엔티티 구조 설명

위 요구사항을 만족하기 위해 카페 별 쿠폰 정보를 저장하는 복사 테이블을 만들었습니다. 그 이유는 당시 저희는 사장님이 쿠폰 이미지 정보를 변경하면 update 를 통해 최신화를 했습니다. 그래서 발급 당시의 정보를 관리하기 위해 복사 테이블을 만들었습니다.

구조를 간단하게 도식화 하면 아래와 같습니다.

복사본은 CouponDesign, StampCoordinate 에서 cafe 에 대한 참조만 없는 형태입니다.

기존 문제점

복사본으로 인한 로직 복잡성 증가

이 상황에서 복사본을 만드는 책임을 각 CouponDesign, StampCoordinate 에 부여했습니다.

아래 코드는 쿠폰 디자인 복사 로직입니다. 이와 같은 코드가 StampCoordinate 에도 존재합니다.

public CouponDesign copy() {
    CouponDesign couponDesign = new CouponDesign(frontImageUrl, backImageUrl, stampImageUrl);
    for (CafeStampCoordinate cafeStampCoordinate : cafeStampCoordinates) {
        CouponStampCoordinate couponStampCoordinate = cafeStampCoordinate.copy(couponDesign);
        couponDesign.addCouponStampCoordinate(couponStampCoordinate);
    }
    return couponDesign;
}

아래 코드는 쿠폰 발급 service 로직 일부분입니다. 쿠폰 발급을 하는데 복사본 생성 → 복사본 저장 등의 로직이 더 들어가있습니다.

private Coupon issueCoupon(Customer customer, Cafe cafe, CafePolicy cafePolicy, CafeCouponDesign cafeCouponDesign) {
    CouponDesign couponDesign = cafeCouponDesign.copy();
    couponDesignRepository.save(couponDesign);
    CouponPolicy couponPolicy = cafePolicy.copy();
    couponPolicyRepository.save(couponPolicy);

    LocalDate expiredDate = LocalDate.now().plusMonths(couponPolicy.getExpiredPeriod());

    return new Coupon(expiredDate, customer, cafe, couponDesign, couponPolicy);
}

쿠폰 발급 비즈니스 로직의 흐름은 아래와 같습니다.

  1. service 단에서 CouponDesign, StampCoordinate의 복사 메서드를 호출해서 복사본 객체를 만든다.
  2. 각 복사본 객체를 DB에 저장한다.
  3. 이를 참조하는 Coupon 객체를 저장한다.

다수의 insert 쿼리 발생

쿠폰 하나를 저장하기 위해 해당 쿠폰의 이미지 정보, 각 스탬프 위치 정보를 추가로 저장해야했습니다. 이로 인해 실제로 발생하는 insert 쿼리는 아래와 같습니다.

  • StampCoordinate 정보 저장 (N개 - 최대 10개)
  • CouponDesign 저장 (1개)
  • Coupon 저장 (1개)

개선점

위 상황에서 쿠폰 디자인 변경 시 update 가 아닌 insert 를 통해 관리하면 쿠폰 발급 시 3번 작업만으로 쿠폰 발급 로직을 간단하게 풀어낼 수 있다고 판단했습니다. 그리고 쿠폰은 현재 카페의 사용중인 CouponDesign, StampCoordinate 에 대한 참조를 가지면 쿠폰 발급 당시 이미지 정보를 적용할 수 있다고 판단했습니다.

update 가 아닌 insert 를 통해 쿠폰 디자인 변경을 관리하는 방법은 아래 근거를 통해 결정했습니다.

  1. 매우 간단하게 쿠폰 발급 비즈니스 로직을 풀어낼 수 있다.
  2. 사장님에게 쿠폰 디자인 변경 이력을 제공해 줄 수 있다.
  3. 불필요한 복사본 저장 쿼리가 줄어든다.

개선 후 엔티티 구조

개선 결과

쿼리 개선

  • 기존 쿠폰 발급 시 발생하던 insert 쿼리 13번 → 1번으로 감소

로직 복잡성 개선

private Coupon issueCoupon(Customer customer, Cafe cafe, CafePolicy cafePolicy, CafeCouponDesign cafeCouponDesign) {
    LocalDate expiredDate = LocalDate.now().plusMonths(cafePolicy.getExpirePeriod());
    return new Coupon(expiredDate, customer, cafe, cafeCouponDesign, cafePolicy);
}
  • 기존의 issueCoupon 메서드보다 로직이 매우 간결해졌습니다.
  • CouponDesign, StampCoordinate 도 copy() 메서드가 사라지면서 보다 가벼워졌습니다.
Comments