영호
[JPA] 엔티티 구조 변경을 통한 로직 복잡성 낮추기, 쿼리 개선기 본문
들어가면서
이번 글에서는 프로젝트를 진행하며 엔티티 구조를 변경해 로직 복잡성을 낮추고, 쿼리를 개선한 경험을 작성하려고 합니다.
상황 및 엔티티 구조 설명
상황 설명
저희 프로젝트는 개인 카페의 쿠폰을 한 곳에서 관리해주는 서비스 입니다. 여기에 더불어 카페 사장님은 쿠폰의 디자인, 쿠폰에 찍히는 스탬프의 위치를 모두 커스텀 할 수 있습니다. 그리고 발급된 쿠폰은 발급 당시의 이미지 정보를 토대로 보여지게 됩니다.
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);
}
쿠폰 발급 비즈니스 로직의 흐름은 아래와 같습니다.
- service 단에서 CouponDesign, StampCoordinate의 복사 메서드를 호출해서 복사본 객체를 만든다.
- 각 복사본 객체를 DB에 저장한다.
- 이를 참조하는 Coupon 객체를 저장한다.
다수의 insert 쿼리 발생
쿠폰 하나를 저장하기 위해 해당 쿠폰의 이미지 정보, 각 스탬프 위치 정보를 추가로 저장해야했습니다. 이로 인해 실제로 발생하는 insert 쿼리는 아래와 같습니다.
- StampCoordinate 정보 저장 (N개 - 최대 10개)
- CouponDesign 저장 (1개)
- Coupon 저장 (1개)
개선점
위 상황에서 쿠폰 디자인 변경 시 update 가 아닌 insert 를 통해 관리하면 쿠폰 발급 시 3번 작업만으로 쿠폰 발급 로직을 간단하게 풀어낼 수 있다고 판단했습니다. 그리고 쿠폰은 현재 카페의 사용중인 CouponDesign, StampCoordinate 에 대한 참조를 가지면 쿠폰 발급 당시 이미지 정보를 적용할 수 있다고 판단했습니다.
update 가 아닌 insert 를 통해 쿠폰 디자인 변경을 관리하는 방법은 아래 근거를 통해 결정했습니다.
- 매우 간단하게 쿠폰 발급 비즈니스 로직을 풀어낼 수 있다.
- 사장님에게 쿠폰 디자인 변경 이력을 제공해 줄 수 있다.
- 불필요한 복사본 저장 쿼리가 줄어든다.
개선 후 엔티티 구조
개선 결과
쿼리 개선
- 기존 쿠폰 발급 시 발생하던 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() 메서드가 사라지면서 보다 가벼워졌습니다.
'JPA' 카테고리의 다른 글
[JPA] 상속관계 사용 시 주의점 (1) | 2023.10.05 |
---|---|
[JPA] 엔티티 delete 시 발생하는 N+1 개선 (6) | 2023.10.04 |
[JPA] service 테스트 코드 개선하기 (3) | 2023.08.13 |
[JPA] 테스트에서 repository.save()해도 createdAt이 null일 때 (0) | 2023.08.06 |
[JPA] 다양한 기본 키 자동 생성 전략 (6) | 2023.07.02 |