(Sparta_WIL_06) 트랜잭션
(Sparta_WIL_06) 트랜잭션
1. Log
1. Keyword
- DataSource
- 조금더 고민과 생각이 필요할듯
- JPA가 항상 정답은 아니고, 그렇다면 JPA나 마이바티스는 어느 레벨에 있는가?
- Datasource로 쪼개서 사용가능하다고 하는데
- 트랜잭션은 어떤 계층에서 어떻게 잡아서 이루어지는가?
- JDBC 직접 한번 해보기
- 옵셔널
- 어떻게 사용해야할지 고민 더 필요할듯
- 어떤 메소드를 사용해도 비슷한 결과를 만들 수는 있는데,
- 이름에 따라서 용도와 목적이 쪼금식 다르기 때문에 고민이 더필요
- 언어라는 명칭으로 불리는게 이런 느낌 같음(☆)
- JPA
- 이제 QueryDSL과 영속성, N+1등 어떻게 처리해야하는지 조금더 투자하기.
- 대략적으로 어떻게 써먹어야하는지 기본적인거는 파악된듯.
2. 문제의 기록
- 시간부족으로 과제 중간 진행X
3. 6주차 피드백
쿠폰 도메인을 엔티티·컨트롤러·서비스로 분리해 끝까지 구현하려는 흐름이 잘 보였고, 특히 발급 기능에 트랜잭션과 락을 붙여보려 한 점이 좋았습니다. 다만, 요구사항 구현 중 비즈니스 로직에서 누락된 부분(유효 쿠폰 조회와 최대 할인 계산)은 보완이 필요합니다.
📢 주요 피드백
- [쿠폰 테이블 설계] ✔️: coupons 테이블에 할인 유형, 값, 최소 주문 금액, 최대 할인 금액, 기간, 발급/사용 수량을 담아 핵심 정보를 비교적 충실하게 반영했습니다.
- [쿠폰 단일 조회 API] ✔️: ID 기반 상세 조회와 DTO 변환 흐름이 단순해서 Spring MVC 요청 처리 구조를 이해하고 구현한 흔적이 보입니다.
- [쿠폰 목록 조회 API] ❌: QueryDSL과 페이지네이션은 적용했지만 isActive를 현재 유효 쿠폰 여부로 해석한 것이 아니라 단순 플래그/기간 조건 조회 수준이라 요구사항 의도와 다소 어긋납니다.
- [쿠폰 생성 API] ❌: 생성 자체는 되지만 정액/정률 타입별 값 검증, maxDiscountAmount 규칙, 할인율 범위 같은 요청 유효성 검증이 충분하지 않습니다.
- [쿠폰 삭제 API] ✔️: 물리 삭제 대신 비활성화로 처리해 히스토리를 보존하려는 방향은 요구사항과 잘 맞습니다.
- [상품별 최대 할인율 조회 API] ❌: 엔드포인트는 연결했지만 서비스가 아직 null을 반환해서 핵심 계산 기능이 비어 있습니다.
- [쿠폰 대량 발급 API] ✔️: 비관적 락으로 대상 쿠폰을 잡고 saveAll과 수량 증가를 한 트랜잭션 안에서 처리해 정합성을 챙기려는 접근이 좋습니다.
- [쿠폰 등록 API] ❌: 코드 존재 확인과 상태 변경 흐름은 있지만 요청 계약이 userId를 본문으로 받는 형태와 다르고, 이미 사용된 쿠폰을 409로 구분하는 예외 설계도 아직 맞춰지지 않았습니다.
👨🏫 개선 포인트
- [목록 조회 의미 해석] 개선: 현재 getCoupons는 BooleanBuilder를 잘 사용했지만, isActive를 단순 컬럼 필터로만 쓰고 있어 “지금 시점에 실제로 유효한 쿠폰”을 찾는 요구와는 차이가 있습니다. 이 값은 now가 startDate와 endDate 사이에 있고, isActive가 true이며, 필요하다면 발급 가능 수량도 남아 있는지를 함께 묶어 해석하는 것이 더 자연스럽습니다. QueryDSL에서 BooleanBuilder로 비즈니스 의미를 한 번에 표현해보면 요구사항을 코드로 번역하는 힘이 더 좋아집니다.
- [최대 할인 계산 로직] 개선: ProductService의 getProductPricing가 비어 있어서 가장 중요한 학습 포인트 하나가 빠져 있습니다. 이 기능은 상품 가격을 기준으로 적용 가능한 쿠폰만 먼저 걸러내고, FIXED는 정액 할인, PERCENT는 비율 계산 후 maxDiscountAmount 상한을 적용해 비교하는 식으로 단계 분리가 필요합니다. 이렇게 계산 로직을 메서드로 분리해두면 JPA 조회, 비즈니스 규칙, 응답 DTO 구성이 분명해져 이후 유지보수도 쉬워집니다.
- [오프라인 쿠폰 상태/예외 정리] 개선: coupon_users 테이블 기본값은 ISSUED인데 자바 Enum은 AVAILABLE, CLAIMED, USED, EXPIRED라서 상태 모델이 서로 다르게 표현되고 있습니다. 이런 불일치는 운영 중 예외 원인을 찾기 어렵게 만들기 때문에 DB와 Enum 이름을 먼저 맞추고, 잘못된 코드와 이미 사용된 쿠폰을 서로 다른 예외 코드와 HTTP 상태로 나누는 것이 좋습니다. 특히 등록 API는 @Transactional 안에서 조회, 상태 검증, 사용자 연결, 상태 변경의 순서를 더 명확히 두면 코드 읽기가 훨씬 좋아집니다.
🛎️ 추가 팁
- QueryDSL을 이미 사용하고 있으니 BooleanBuilder와 LocalDateTime now를 함께 써서 “현재 유효” 같은 비즈니스 조건을 한 메서드로 표현하는 연습을 해보세요. 단순 필터 조합보다 요구사항 해석 능력이 더 빨리 늘어납니다.
- CouponUserInfoResponse를 만들 때 couponUser.getCoupon()에 접근하므로, 나중에 목록 조회로 확장되면 LAZY 로딩 때문에 조회 수가 늘 수 있습니다. 이후 단계에서는 fetch join이나 @EntityGraph 같은 키워드를 같이 보면 JPA 조회 최적화 감각을 익히는 데 도움이 됩니다.
- 생성 요청 검증은 @NotNull만으로 끝내지 말고 타입 의존 규칙을 별도 검증 메서드나 커스텀 Validator로 분리해보세요. 예를 들어 PERCENT일 때 할인율 범위와 maxDiscountAmount 필수 여부를 나누면 컨트롤러보다 도메인 규칙이 더 선명해집니다.
This post is licensed under CC BY 4.0 by the author.