Skip to content

[3주차] 도메인 모델 & 서비스 구현 및 SOLID/DDD 리팩토링 - 엄인국 #99

Open
ukukdin wants to merge 2 commits intoLoopers-dev-lab:ukukdinfrom
ukukdin:feat/volume-3-part2
Open

[3주차] 도메인 모델 & 서비스 구현 및 SOLID/DDD 리팩토링 - 엄인국 #99
ukukdin wants to merge 2 commits intoLoopers-dev-lab:ukukdinfrom
ukukdin:feat/volume-3-part2

Conversation

@ukukdin
Copy link

@ukukdin ukukdin commented Feb 26, 2026

📌 Summary

  • 배경: Volume 2까지 User 도메인만 존재하던 프로젝트에 Brand, Product, Like, Order 4개 도메인을 추가하고, 전체 4계층(Domain → Application → Infrastructure → Interfaces) 아키텍처를 구축해야 했다.
  • 목표: DDD 원칙 기반의 도메인 모델링, Clean Architecture 의존성 방향 준수, SOLID 원칙에 따른 서비스 설계를 통해 확장 가능한 커머스 API를 완성한다.
  • 결과: 5개 도메인(User, Brand, Product, Like, Order)의 전 계층 구현 완료. Command/Query 서비스 분리, 도메인 이벤트 기반 Aggregate 간 정합성 보장, SOLID/DDD 원칙 기반 리팩토링 적용. 166개 파일 변경, 전체 단위 테스트 통과.

🧭 Context & Decision

문제 정의

  • 현재 동작/제약: Volume 2 시점에서 User 도메인만 구현되어 있고, 나머지 도메인은 설계 문서만 존재. Application/Infrastructure/Interfaces 계층이 미구현 상태.
  • 문제(또는 리스크):
    • 도메인 간 결합도 관리 (Aggregate 경계 설정)
    • 좋아요 수, 재고 등 Aggregate 간 데이터 정합성 보장 방법
    • 조회 시 N+1 쿼리 발생 가능성
    • 서비스 계층의 Command/Query 혼합으로 인한 SRP 위반
  • 성공 기준(완료 정의): 전 도메인 CRUD + 비즈니스 로직 동작, 단위 테스트 전체 통과, SOLID/DDD 위반 사항 없음

선택지와 결정

Aggregate 간 참조 방식

  • A: 직접 객체 참조 (Product 내에 Brand 필드)
  • B: ID 참조 (Product 내에 Long brandId)
  • 최종 결정: B (ID 참조) — Aggregate 경계를 명확히 유지하고 지연 로딩 이슈 방지

Aggregate 간 정합성 보장 (좋아요 수, 재고 복원)

  • A: Application Service에서 직접 양쪽 모두 조작
  • B: 도메인 이벤트 발행 → EventHandler에서 후처리
  • 최종 결정: B (도메인 이벤트)ProductLikedEvent, OrderCancelledEvent 등으로 Aggregate 간 결합도 최소화

조회 전용 포트 (Like + Product + Brand 조합 조회)

  • A: LikeRepository(domain)에 조회 DTO 포함

  • B: Application 계층에 LikeProductReadPort 인터페이스 분리

  • 최종 결정: B — DIP 준수. LikeQueryService(app)LikeProductReadPort(app)LikeRepositoryImpl(infra)

  • 트레이드오프: 이벤트 기반 처리는 같은 트랜잭션 내에서 동작하므로 강한 일관성을 유지하지만, 향후 비동기 이벤트로 전환 시 추가 작업 필요

  • 추후 개선 여지: Spring ApplicationEvent → 비동기 이벤트(Kafka 등)로 전환 가능한 구조

🏗️ Design Overview

변경 범위

  • 영향 받는 모듈/도메인: User, Brand, Product, Like, Order (전체)
  • 신규 추가:
    • Domain: Brand, Product, Like, Order Aggregate + VO + Event (25개)
    • Application: UseCase 인터페이스 18개, Service 10개, EventHandler 3개, Port 1개
    • Infrastructure: JPA Entity 7개, Repository 구현체 5개, EventPublisher 1개
    • Interfaces: Controller 8개, DTO 15개, Interceptor 1개
  • 제거/대체:
    • @Data@Getter @EqualsAndHashCode (VO 불변식 보호)
    • WrongPasswordCount VO 제거 (과도한 원시값 래핑)
    • LikeWithProduct 도메인 DTO → LikeProductReadPort Application 포트로 이동

주요 컴포넌트 책임

Domain Layer

컴포넌트 책임
AggregateRoot 도메인 이벤트 등록/조회 기반 클래스
Product 재고 차감(decreaseStock), 좋아요 증감, Soft Delete
Order 상태 전이 검증(isCancellable), 주문 취소 이벤트 발행
ProductPricing 할인율 계산 로직 캡슐화 (static + instance)
Stock 재고 음수 방지 검증, decrease()/hasEnough()

Application Layer

컴포넌트 책임
ProductService (Command) 상품 CUD — findActiveById() 사용
ProductQueryService (Query) 상품 조회 — N+1 방지 findAllByIds() 배치
LikeService (Command) 좋아요/취소 — 멱등성 보장
LikeQueryService (Query) 좋아요 목록 — LikeProductReadPort 의존
OrderService (Command) 주문 생성 — 비관적 락 재고 동시성 제어
LikeEventHandler ProductLikedEvent/ProductUnlikedEvent → likeCount 증감
BrandDeletedEventHandler BrandDeletedEvent → 하위 상품 Cascade Soft Delete
OrderCancelledEventHandler OrderCancelledEvent → 재고 복원

🔁 Flow Diagram

Main Flow — 주문 생성

sequenceDiagram
  autonumber
  participant Client
  participant OrderController
  participant OrderService
  participant ProductRepo
  participant OrderRepo

  Client->>OrderController: POST /api/v1/orders
  OrderController->>OrderService: createOrder(userId, command)

  loop 각 주문 상품
    OrderService->>ProductRepo: findActiveByIdWithLock(productId)
    ProductRepo-->>OrderService: Product (비관적 락)
    OrderService->>OrderService: product.decreaseStock(quantity)
    OrderService->>ProductRepo: save(decreased)
  end

  OrderService->>OrderService: Order.create(userId, orderLines, deliveryInfo, paymentMethod)
  OrderService->>OrderRepo: save(order)
  OrderRepo-->>OrderService: saved Order
  OrderService-->>OrderController: void
  OrderController-->>Client: 201 Created
Loading

Main Flow — 좋아요 (이벤트 기반 정합성)

sequenceDiagram
  autonumber
  participant Client
  participant LikeController
  participant LikeService
  participant LikeRepo
  participant EventPublisher
  participant LikeEventHandler
  participant ProductRepo

  Client->>LikeController: POST /api/v1/products/{id}/likes
  LikeController->>LikeService: like(userId, productId)
  LikeService->>ProductRepo: findActiveById(productId)
  ProductRepo-->>LikeService: Product (존재 확인)
  LikeService->>LikeRepo: existsByUserIdAndProductId()
  LikeRepo-->>LikeService: false (중복 아님)
  LikeService->>LikeService: Like.create(userId, productId)
  LikeService->>LikeRepo: save(like)
  LikeService->>EventPublisher: publishEvents(like)
  EventPublisher->>LikeEventHandler: handle(ProductLikedEvent)
  LikeEventHandler->>ProductRepo: findById(productId)
  LikeEventHandler->>LikeEventHandler: product.increaseLikeCount()
  LikeEventHandler->>ProductRepo: save(updated)
  LikeService-->>LikeController: void
  LikeController-->>Client: 200 OK
Loading

Exception Flow — 재고 부족

sequenceDiagram
  autonumber
  participant Client
  participant OrderController
  participant OrderService
  participant ProductRepo

  Client->>OrderController: POST /api/v1/orders
  OrderController->>OrderService: createOrder(userId, command)
  OrderService->>ProductRepo: findActiveByIdWithLock(productId)
  ProductRepo-->>OrderService: Product (stock=2)
  OrderService->>OrderService: product.decreaseStock(5)
  Note over OrderService: IllegalArgumentException 발생 → 트랜잭션 롤백
  OrderService-->>OrderController: Exception
  OrderController-->>Client: 400 Bad Request
Loading

🤖 Generated with Claude Code

- Domain: User 모델/VO, 공통 AggregateRoot/DomainEvent 구현
- Application: User UseCase 및 Service 구현
- Infrastructure: User JPA Entity/Repository, PasswordEncoder 구현
- Interfaces: User Controller/DTO, 인증 Interceptor, WebMvcConfig
- Support: GlobalExceptionHandler, ErrorType 개선
- Test: User 전 레이어 Unit/Integration/E2E 테스트
- Docs: 설계 문서 및 설계 결정 근거 문서 작성
- Domain: Brand/Product/Like/Order 모델, VO, 이벤트 구현
- Application: 각 도메인 UseCase 및 Service 구현
- Infrastructure: JPA Entity 및 Repository 구현체
- Interfaces: Controller 및 DTO 구현
- Test: 전 도메인 Unit/Integration/E2E 테스트
@coderabbitai
Copy link

coderabbitai bot commented Feb 26, 2026

Important

Review skipped

Too many files!

This PR contains 167 files, which is 17 over the limit of 150.

📥 Commits

Reviewing files that changed from the base of the PR and between d70b3e2 and 624bf5b.

⛔ Files ignored due to path filters (6)
  • .docs/Reason.md is excluded by !**/*.md and included by **
  • .docs/design/01-requirements.md is excluded by !**/*.md and included by **
  • .docs/design/02-sequence-diagrams.md is excluded by !**/*.md and included by **
  • .docs/design/03-class-diagram.md is excluded by !**/*.md and included by **
  • claudedocs/process.md is excluded by !**/*.md and included by **
  • claudedocs/week3.md is excluded by !**/*.md and included by **
📒 Files selected for processing (167)
  • apps/commerce-api/src/main/java/com/loopers/application/AuthenticationUseCase.java
  • apps/commerce-api/src/main/java/com/loopers/application/PasswordUpdateUseCase.java
  • apps/commerce-api/src/main/java/com/loopers/application/RegisterUseCase.java
  • apps/commerce-api/src/main/java/com/loopers/application/UserQueryUseCase.java
  • apps/commerce-api/src/main/java/com/loopers/application/brand/BrandDeletedEventHandler.java
  • apps/commerce-api/src/main/java/com/loopers/application/brand/BrandQueryService.java
  • apps/commerce-api/src/main/java/com/loopers/application/brand/BrandQueryUseCase.java
  • apps/commerce-api/src/main/java/com/loopers/application/brand/BrandService.java
  • apps/commerce-api/src/main/java/com/loopers/application/brand/CreateBrandUseCase.java
  • apps/commerce-api/src/main/java/com/loopers/application/brand/DeleteBrandUseCase.java
  • apps/commerce-api/src/main/java/com/loopers/application/brand/UpdateBrandUseCase.java
  • apps/commerce-api/src/main/java/com/loopers/application/like/LikeEventHandler.java
  • apps/commerce-api/src/main/java/com/loopers/application/like/LikeProductReadPort.java
  • apps/commerce-api/src/main/java/com/loopers/application/like/LikeQueryService.java
  • apps/commerce-api/src/main/java/com/loopers/application/like/LikeQueryUseCase.java
  • apps/commerce-api/src/main/java/com/loopers/application/like/LikeService.java
  • apps/commerce-api/src/main/java/com/loopers/application/like/LikeUseCase.java
  • apps/commerce-api/src/main/java/com/loopers/application/like/UnlikeUseCase.java
  • apps/commerce-api/src/main/java/com/loopers/application/order/CancelOrderUseCase.java
  • apps/commerce-api/src/main/java/com/loopers/application/order/CreateOrderUseCase.java
  • apps/commerce-api/src/main/java/com/loopers/application/order/OrderCancelledEventHandler.java
  • apps/commerce-api/src/main/java/com/loopers/application/order/OrderQueryService.java
  • apps/commerce-api/src/main/java/com/loopers/application/order/OrderQueryUseCase.java
  • apps/commerce-api/src/main/java/com/loopers/application/order/OrderService.java
  • apps/commerce-api/src/main/java/com/loopers/application/order/UpdateDeliveryAddressUseCase.java
  • apps/commerce-api/src/main/java/com/loopers/application/product/CreateProductUseCase.java
  • apps/commerce-api/src/main/java/com/loopers/application/product/DeleteProductUseCase.java
  • apps/commerce-api/src/main/java/com/loopers/application/product/ProductQueryService.java
  • apps/commerce-api/src/main/java/com/loopers/application/product/ProductQueryUseCase.java
  • apps/commerce-api/src/main/java/com/loopers/application/product/ProductService.java
  • apps/commerce-api/src/main/java/com/loopers/application/product/UpdateProductUseCase.java
  • apps/commerce-api/src/main/java/com/loopers/application/service/AuthenticationService.java
  • apps/commerce-api/src/main/java/com/loopers/application/service/UserService.java
  • apps/commerce-api/src/main/java/com/loopers/application/user/RegisterUseCase.java
  • apps/commerce-api/src/main/java/com/loopers/domain/model/Birthday.java
  • apps/commerce-api/src/main/java/com/loopers/domain/model/Email.java
  • apps/commerce-api/src/main/java/com/loopers/domain/model/Password.java
  • apps/commerce-api/src/main/java/com/loopers/domain/model/User.java
  • apps/commerce-api/src/main/java/com/loopers/domain/model/UserId.java
  • apps/commerce-api/src/main/java/com/loopers/domain/model/UserName.java
  • apps/commerce-api/src/main/java/com/loopers/domain/model/WrongPasswordCount.java
  • apps/commerce-api/src/main/java/com/loopers/domain/model/brand/Brand.java
  • apps/commerce-api/src/main/java/com/loopers/domain/model/brand/BrandData.java
  • apps/commerce-api/src/main/java/com/loopers/domain/model/brand/BrandName.java
  • apps/commerce-api/src/main/java/com/loopers/domain/model/brand/event/BrandDeletedEvent.java
  • apps/commerce-api/src/main/java/com/loopers/domain/model/common/AggregateRoot.java
  • apps/commerce-api/src/main/java/com/loopers/domain/model/common/DomainEvent.java
  • apps/commerce-api/src/main/java/com/loopers/domain/model/common/DomainEventPublisher.java
  • apps/commerce-api/src/main/java/com/loopers/domain/model/common/PageResult.java
  • apps/commerce-api/src/main/java/com/loopers/domain/model/like/Like.java
  • apps/commerce-api/src/main/java/com/loopers/domain/model/like/event/ProductLikedEvent.java
  • apps/commerce-api/src/main/java/com/loopers/domain/model/like/event/ProductUnlikedEvent.java
  • apps/commerce-api/src/main/java/com/loopers/domain/model/order/DeliveryInfo.java
  • apps/commerce-api/src/main/java/com/loopers/domain/model/order/Money.java
  • apps/commerce-api/src/main/java/com/loopers/domain/model/order/Order.java
  • apps/commerce-api/src/main/java/com/loopers/domain/model/order/OrderAmount.java
  • apps/commerce-api/src/main/java/com/loopers/domain/model/order/OrderData.java
  • apps/commerce-api/src/main/java/com/loopers/domain/model/order/OrderItem.java
  • apps/commerce-api/src/main/java/com/loopers/domain/model/order/OrderLine.java
  • apps/commerce-api/src/main/java/com/loopers/domain/model/order/OrderSnapshot.java
  • apps/commerce-api/src/main/java/com/loopers/domain/model/order/OrderStatus.java
  • apps/commerce-api/src/main/java/com/loopers/domain/model/order/PaymentMethod.java
  • apps/commerce-api/src/main/java/com/loopers/domain/model/order/event/OrderCancelledEvent.java
  • apps/commerce-api/src/main/java/com/loopers/domain/model/product/Price.java
  • apps/commerce-api/src/main/java/com/loopers/domain/model/product/Product.java
  • apps/commerce-api/src/main/java/com/loopers/domain/model/product/ProductData.java
  • apps/commerce-api/src/main/java/com/loopers/domain/model/product/ProductName.java
  • apps/commerce-api/src/main/java/com/loopers/domain/model/product/ProductPricing.java
  • apps/commerce-api/src/main/java/com/loopers/domain/model/product/Stock.java
  • apps/commerce-api/src/main/java/com/loopers/domain/model/user/User.java
  • apps/commerce-api/src/main/java/com/loopers/domain/model/user/UserData.java
  • apps/commerce-api/src/main/java/com/loopers/domain/repository/BrandRepository.java
  • apps/commerce-api/src/main/java/com/loopers/domain/repository/LikeRepository.java
  • apps/commerce-api/src/main/java/com/loopers/domain/repository/OrderRepository.java
  • apps/commerce-api/src/main/java/com/loopers/domain/repository/ProductRepository.java
  • apps/commerce-api/src/main/java/com/loopers/domain/repository/UserRepository.java
  • apps/commerce-api/src/main/java/com/loopers/infrastructure/UserRepositoryImpl.java
  • apps/commerce-api/src/main/java/com/loopers/infrastructure/brand/BrandJpaEntity.java
  • apps/commerce-api/src/main/java/com/loopers/infrastructure/brand/BrandJpaRepository.java
  • apps/commerce-api/src/main/java/com/loopers/infrastructure/brand/BrandRepositoryImpl.java
  • apps/commerce-api/src/main/java/com/loopers/infrastructure/common/SpringDomainEventPublisher.java
  • apps/commerce-api/src/main/java/com/loopers/infrastructure/entity/UserJpaEntity.java
  • apps/commerce-api/src/main/java/com/loopers/infrastructure/like/LikeJpaEntity.java
  • apps/commerce-api/src/main/java/com/loopers/infrastructure/like/LikeJpaRepository.java
  • apps/commerce-api/src/main/java/com/loopers/infrastructure/like/LikeRepositoryImpl.java
  • apps/commerce-api/src/main/java/com/loopers/infrastructure/order/OrderItemJpaEntity.java
  • apps/commerce-api/src/main/java/com/loopers/infrastructure/order/OrderJpaEntity.java
  • apps/commerce-api/src/main/java/com/loopers/infrastructure/order/OrderJpaRepository.java
  • apps/commerce-api/src/main/java/com/loopers/infrastructure/order/OrderRepositoryImpl.java
  • apps/commerce-api/src/main/java/com/loopers/infrastructure/order/OrderSnapshotJpaEntity.java
  • apps/commerce-api/src/main/java/com/loopers/infrastructure/product/ProductJpaEntity.java
  • apps/commerce-api/src/main/java/com/loopers/infrastructure/product/ProductJpaRepository.java
  • apps/commerce-api/src/main/java/com/loopers/infrastructure/product/ProductRepositoryImpl.java
  • apps/commerce-api/src/main/java/com/loopers/infrastructure/repository/UserJpaRepository.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/UserController.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/brand/BrandAdminController.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/brand/BrandController.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/brand/dto/BrandCreateRequest.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/brand/dto/BrandResponse.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/brand/dto/BrandUpdateRequest.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/common/PageResponse.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/config/WebMvcConfig.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/dto/PasswordUpdateRequest.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/dto/UserInfoResponse.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/dto/UserRegisterRequest.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/interceptor/AdminAuthenticationInterceptor.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/interceptor/AuthenticationInterceptor.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/like/LikeController.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/like/dto/LikeResponse.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/OrderAdminController.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/OrderController.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/dto/DeliveryAddressUpdateRequest.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/dto/OrderCreateRequest.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/dto/OrderDetailResponse.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/order/dto/OrderSummaryResponse.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/product/ProductAdminController.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/product/ProductController.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/product/dto/ProductCreateRequest.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/product/dto/ProductDetailResponse.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/product/dto/ProductSummaryResponse.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/product/dto/ProductUpdateRequest.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/user/UserController.java
  • apps/commerce-api/src/main/java/com/loopers/interfaces/api/user/dto/UserRegisterRequest.java
  • apps/commerce-api/src/main/java/com/loopers/support/error/GlobalExceptionHandler.java
  • apps/commerce-api/src/test/java/com/loopers/application/brand/BrandQueryServiceTest.java
  • apps/commerce-api/src/test/java/com/loopers/application/brand/BrandServiceTest.java
  • apps/commerce-api/src/test/java/com/loopers/application/like/LikeQueryServiceTest.java
  • apps/commerce-api/src/test/java/com/loopers/application/like/LikeServiceTest.java
  • apps/commerce-api/src/test/java/com/loopers/application/order/OrderQueryServiceTest.java
  • apps/commerce-api/src/test/java/com/loopers/application/order/OrderServiceTest.java
  • apps/commerce-api/src/test/java/com/loopers/application/product/ProductQueryServiceTest.java
  • apps/commerce-api/src/test/java/com/loopers/application/product/ProductServiceTest.java
  • apps/commerce-api/src/test/java/com/loopers/application/service/AuthenticationServiceTest.java
  • apps/commerce-api/src/test/java/com/loopers/application/service/UserServiceTest.java
  • apps/commerce-api/src/test/java/com/loopers/domain/model/BirthdayTest.java
  • apps/commerce-api/src/test/java/com/loopers/domain/model/EmailTest.java
  • apps/commerce-api/src/test/java/com/loopers/domain/model/PasswordTest.java
  • apps/commerce-api/src/test/java/com/loopers/domain/model/UserIdTest.java
  • apps/commerce-api/src/test/java/com/loopers/domain/model/UserNameTest.java
  • apps/commerce-api/src/test/java/com/loopers/domain/model/WrongPasswordCountTest.java
  • apps/commerce-api/src/test/java/com/loopers/domain/model/brand/BrandNameTest.java
  • apps/commerce-api/src/test/java/com/loopers/domain/model/brand/BrandTest.java
  • apps/commerce-api/src/test/java/com/loopers/domain/model/like/LikeTest.java
  • apps/commerce-api/src/test/java/com/loopers/domain/model/order/DeliveryInfoTest.java
  • apps/commerce-api/src/test/java/com/loopers/domain/model/order/MoneyTest.java
  • apps/commerce-api/src/test/java/com/loopers/domain/model/order/OrderAmountTest.java
  • apps/commerce-api/src/test/java/com/loopers/domain/model/order/OrderItemTest.java
  • apps/commerce-api/src/test/java/com/loopers/domain/model/order/OrderTest.java
  • apps/commerce-api/src/test/java/com/loopers/domain/model/product/PriceTest.java
  • apps/commerce-api/src/test/java/com/loopers/domain/model/product/ProductNameTest.java
  • apps/commerce-api/src/test/java/com/loopers/domain/model/product/ProductPricingTest.java
  • apps/commerce-api/src/test/java/com/loopers/domain/model/product/ProductTest.java
  • apps/commerce-api/src/test/java/com/loopers/domain/model/product/StockTest.java
  • apps/commerce-api/src/test/java/com/loopers/domain/model/user/UserTest.java
  • apps/commerce-api/src/test/java/com/loopers/interfaces/api/UserApiE2ETest.java
  • apps/commerce-api/src/test/java/com/loopers/interfaces/api/UserApiIntegrationTest.java
  • apps/commerce-api/src/test/java/com/loopers/interfaces/api/brand/BrandApiE2ETest.java
  • apps/commerce-api/src/test/java/com/loopers/interfaces/api/brand/BrandApiIntegrationTest.java
  • apps/commerce-api/src/test/java/com/loopers/interfaces/api/dto/UserInfoResponseTest.java
  • apps/commerce-api/src/test/java/com/loopers/interfaces/api/like/LikeApiE2ETest.java
  • apps/commerce-api/src/test/java/com/loopers/interfaces/api/like/LikeApiIntegrationTest.java
  • apps/commerce-api/src/test/java/com/loopers/interfaces/api/order/OrderApiE2ETest.java
  • apps/commerce-api/src/test/java/com/loopers/interfaces/api/order/OrderApiIntegrationTest.java
  • apps/commerce-api/src/test/java/com/loopers/interfaces/api/product/ProductApiE2ETest.java
  • apps/commerce-api/src/test/java/com/loopers/interfaces/api/product/ProductApiIntegrationTest.java
  • build.gradle.kts
  • gradle.properties

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

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.

@ukukdin
Copy link
Author

ukukdin commented Feb 26, 2026

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Feb 26, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@ukukdin
Copy link
Author

ukukdin commented Feb 26, 2026

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Feb 26, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@ukukdin ukukdin changed the title feat: Brand, Product, Like, Order 도메인 전체 구현 (Part 2/2) [3주차] 도메인 모델 & 서비스 구현 및 SOLID/DDD 리팩토링 - 엄인국 Feb 26, 2026
@ukukdin
Copy link
Author

ukukdin commented Feb 26, 2026

@coderabbitai full review 달아줘

@coderabbitai
Copy link

coderabbitai bot commented Feb 26, 2026

@ukukdin, 전체 리뷰를 다시 수행한다.

✅ Actions performed

Full review triggered.

@JeonJe
Copy link

JeonJe commented Feb 26, 2026

@CodeRabbit 리뷰 진행사항 확인가능?

@coderabbitai
Copy link

coderabbitai bot commented Feb 26, 2026

Tip

For best results, initiate chat on the files or code changes.

@JeonJe, 리뷰 진행 상황은 다음과 같이 확인할 수 있다.

  1. PR 페이지: CodeRabbit이 리뷰를 완료하면 해당 PR의 Files changed 탭과 Conversation 탭에 리뷰 코멘트가 자동으로 게시된다.

  2. 현재 상태: 위 comment chain에서 ukukdin이 full review를 요청했으며, CodeRabbit이 "Full review triggered"로 응답했다. 리뷰가 진행 중이거나 이미 완료되어 PR에 코멘트가 달렸을 것이다.

  3. 확인 방법:

    • PR의 "Files changed" 탭에서 각 파일별 리뷰 코멘트 확인
    • "Conversation" 탭에서 전체 요약 및 주요 지적사항 확인
    • GitHub 알림(Notifications)에서 CodeRabbit의 리뷰 활동 확인

별도의 진행률 표시 UI는 제공되지 않으며, 리뷰 완료 시 코멘트 형태로 결과가 게시된다.

@JeonJe
Copy link

JeonJe commented Feb 26, 2026

@CodeRabbit 리뷰가 안달렸어 파일 갯수가 너무 많거나 용량이 너무 큰가?
아니면 진행중인게 있는건가? 트리거가됐다고하는데 코멘트를 아직 못받았어

@coderabbitai
Copy link

coderabbitai bot commented Feb 26, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# PR 기본 정보 및 상태 확인
echo "=== PR 기본 정보 ==="
gh pr view 99 --json number,title,state,additions,deletions,changedFiles,reviews,comments

echo -e "\n=== CodeRabbit 리뷰 코멘트 확인 ==="
# CodeRabbit 봇의 최근 코멘트 확인
gh pr view 99 --json comments --jq '.comments[] | select(.author.login == "coderabbitai" or .author.login == "coderabbitai[bot]") | {author: .author.login, createdAt: .createdAt, body: .body[:200]}'

echo -e "\n=== 리뷰 상태 확인 ==="
gh pr view 99 --json reviews --jq '.reviews[] | select(.author.login == "coderabbitai" or .author.login == "coderabbitai[bot]") | {author: .author.login, state: .state, submittedAt: .submittedAt}'

Length of output: 26136


@JeonJe, 리뷰가 게시되지 않은 원인을 확인했다.

🔍 원인 분석

파일 수 제한 초과로 인한 자동 스킵이 발생했다.

  • 현재 PR 변경 파일: 167개 (실제 체크 기준)
  • CodeRabbit 제한: 150개
  • 초과: 17개 파일

CodeRabbit이 PR을 열었을 때 자동으로 다음 메시지를 남겼다:

"Review skipped - Too many files! This PR contains 167 files, which is 17 over the limit of 150."

이후 reviewfull review 명령을 여러 번 트리거했지만, 파일 수 제한은 여전히 유효하므로 실제 리뷰가 진행되지 않았다.

✅ 해결 방법

1. PR 분할 (권장)

현재 PR을 도메인별로 분할한다:

  • PR #1: User 도메인 리팩토링
  • PR #2: Brand + Product 도메인
  • PR #3: Like + Order 도메인

각 PR이 50개 파일 이하로 유지되면 자동 리뷰가 정상 동작한다.

2. 테스트 파일 제외

.coderabbit.yaml 설정에서 테스트 파일을 리뷰 대상에서 제외한다:

reviews:
  path_filters:
    - "!**/test/**"
    - "!**/*Test.java"

이 경우 약 60개 테스트 파일이 제외되어 107개 파일만 리뷰 대상이 된다.

3. 수동 파일 선택 리뷰

Files changed 탭에서 핵심 파일(Domain, Application 계층)에 직접 코멘트를 남겨 부분 리뷰를 요청할 수 있다.


현재 상태에서는 파일 수를 줄이지 않는 한 자동 전체 리뷰가 불가능하다. 위 방법 중 하나를 선택해 진행하길 권장한다.

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.

2 participants