Skip to content

[volume-4] 쿠폰 추가, 동시성 제어 적용#143

Open
hyejin0810 wants to merge 19 commits intoLoopers-dev-lab:hyejin0810from
hyejin0810:week4
Open

[volume-4] 쿠폰 추가, 동시성 제어 적용#143
hyejin0810 wants to merge 19 commits intoLoopers-dev-lab:hyejin0810from
hyejin0810:week4

Conversation

@hyejin0810
Copy link

📌 Summary

  • 목표: 쿠폰 도메인 신규 구현, 재고/쿠폰/좋아요 동시성 추가

🧭 Context & Decision

변경 사항
쿠폰 도메인 신규 구현

  • CouponTemplate — 정액(FIXED) /(RATE) 쿠폰 템플릿 엔티티
  • IssuedCoupon — 발급된 쿠폰 생명주기 관리 (AVAILABLE → USED / EXPIRED)
  • @Version 낙관적 락 적용 — 동일 쿠폰 동시 사용 방지
  • 할인 계산 로직을 CouponTemplate 도메인에 캡슐화 (calculateDiscount(Money))

동시성 제어

  • 재고: UPDATE ... WHERE stock >= quantity 방식을 통해 DB 레벨에서 원자적 차감을 수행하여 초과 차감을 방지.

  • 쿠폰 사용: 충돌 가능성이 낮은 비즈니스 특성을 고려하여 @Version을 활용한 낙관적 락 사용.

  • 좋아요 수: 별도의 락 없이 아토믹 업데이트를 사용하여 동시성 경쟁 상황에서도 카운트 보장.

  • 좋아요 중복: 애플리케이션의 유효성 검사와 DB Unique Constraint를 결합하여 중복 데이터 생성을 완벽히 차단.

    주문-쿠폰 통합

    • OrderFacade.createOrder()에 쿠폰 검증/사용/할인 계산 통합
    • Order에 originalAmount / discountAmount / totalAmount 스냅샷 추가
    • Facade 메서드에만 단일 @transactional 적용 — 재고·쿠폰·포인트 원자성 보장

    Admin / 대고객 API (8개 추가)

Method Endpoint 설명
POST /api-admin/v1/coupons 쿠폰 템플릿 등록
GET /api-admin/v1/coupons 쿠폰 템플릿 목록 조회
GET /api-admin/v1/coupons/{couponId} 쿠폰 템플릿 상세 조회
PUT /api-admin/v1/coupons/{couponId} 쿠폰 템플릿 수정
DELETE /api-admin/v1/coupons/{couponId} 쿠폰 템플릿 삭제
GET /api-admin/v1/coupons/{couponId}/issues 쿠폰별 발급 내역 조회
POST /api/v1/coupons/{couponId}/issue 쿠폰 발급
GET /api/v1/users/me/coupons 내 쿠폰 목록 조회

기존 주문 API에 couponId (요청), originalAmount / discountAmount / userCouponId (응답) 필드 추가.

테스트

테스트 클래스 테스트 수 검증 내용
CouponTemplateTest 12 정액/정률 할인 계산, 만료 검증, 최소주문금액 검증
IssuedCouponTest 8 발급/사용/소유자 검증 생명주기
CouponFacadeTest 7 쿠폰 발급·사용 서비스 흐름
OrderConcurrencyTest 2 비관적 락으로 동시 재고 차감 정확성 (재고 10, 10스레드 → 잔여 0)
LikeConcurrencyTest 2 유니크 제약으로 좋아요 중복 방지 (같은 회원 2번 좋아요 → 1건만 저장)

hyejin0810 and others added 18 commits February 23, 2026 21:40
- 장바구니 최대 100개 제한 제거
- likes_count 동시성: @Version 낙관적 락 명시 (시퀀스 다이어그램, 클래스 다이어그램, 요구사항)
- 주문번호 생성 전략: UUID 기반 (ORD-yyyyMMdd-{UUID 앞 8자리}) 명시
- 주문 취소 트랜잭션: @transactional 원자적 처리 범위 명시
- 물리적 FK: ORDERS→USERS, ORDER_ITEMS→ORDERS 구간 DDL FK 제약 명시
- OrderFacade 추가: OrderService + CartService 조율로 관심사 분리
- CartService 추가: 장바구니 관리 책임 분리
- OrderService.generateOrderNumber() 추가

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 장바구니(Cart) 도메인 전체 제거 (요구사항, 시퀀스, 클래스, ERD)
- User에 balance 필드 추가 (USERS 테이블 포함)
- 주문 생성 시퀀스: 잔액 확인 → 잔액 차감 흐름 추가
- 주문 취소 시퀀스: 잔액 복구 단계 추가 (UserRepo 참여자 추가)
- 기능 요구사항: 잔액 조회, 잔액 확인/차감/복구 항목 추가
- 시나리오 3 → 상품 주문으로 변경 (장바구니 시나리오 삭제)
- 핵심 제약: 잔액 부족 시 주문 실패 추가

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- User: balance 추가, deductBalance/restoreBalance, authenticate()
- Brand: 등록/조회/삭제 CRUD
- Product: @Version 낙관적 락, decreaseStock/increaseStock, likesCount
- Like: 중복 방지(CONFLICT), 취소(NOT_FOUND), Product.likesCount 연동
- Order/OrderItem: UUID 주문번호, PENDING/CONFIRMED/CANCELLED 상태 관리
- OrderFacade: 재고 차감 + 잔액 차감 단일 트랜잭션, 취소 시 원복
- 각 도메인 단위 테스트 (LikeTest, LikeServiceTest, OrderTest, OrderServiceTest 등)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- BrandFacade.deleteBrand에 @transactional 추가 (연쇄 삭제 원자성 보장)
- OrderFacade.cancelOrder에 주문 소유자 검증 추가 (타인 주문 취소 시 NOT_FOUND)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- cancelOrder(Long id) → cancelOrder(Order order)로 변경하여 중복 조회 제거
- Facade에서 조회한 Order를 직접 전달하도록 수정

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- UserServiceTest: isNotNull() 중복 제거 (isEqualTo로 충분)
- OrderTest: 테스트 이름과 무관한 필드 검증 제거 (status만 검증)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- UserFacade 생성: getMyInfo 유스케이스를 application 레이어로 이동
- UserService에서 getMyInfo 제거 및 UserInfo import 삭제 (domain → application 역방향 의존 해소)
- UserV1Controller가 getMyInfo를 UserFacade를 통해 호출하도록 수정
- UserFacadeTest 추가, UserServiceTest의 GetMyInfo 테스트 이동
- UserV1ControllerStandaloneTest를 UserFacade mock 기반으로 수정

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Service 레이어(User/Brand/Product/Like/Order/Member)의 @transactional 전부 제거
- BrandFacade: register(@transactional), getBrand/getBrands(@transactional readOnly) 추가
- ProductFacade: 모든 메서드에 @transactional 추가
- 트랜잭션 경계를 유스케이스 단위인 Facade로 일원화

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- User/Brand/Product/Like/Order/MemberService 메서드에 @transactional 복원
- 쓰기 메서드: @transactional, 읽기 메서드: @transactional(readOnly = true)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
상품 ID 오름차순 정렬 후 재고 차감/복구 처리

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link

coderabbitai bot commented Mar 5, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 13771b7e-cc53-4307-a808-672bd198aca9

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@hyejin0810 hyejin0810 changed the title 도메인 구현 및 주문 동시성 제어 [volume-4] 쿠폰 추가, 동시성 제어 적용 Mar 5, 2026
쿠폰 사용/복구 시 ObjectOptimisticLockingFailureException을 Facade에서 잡아
CoreException(CONFLICT)으로 변환하여 클라이언트에 의미있는 에러 메시지 반환

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant