Skip to content

feat: 입고 조회 기능 구현#84

Merged
JoonKyoLee merged 6 commits intomainfrom
feat/get-receipt
Nov 23, 2025
Merged

feat: 입고 조회 기능 구현#84
JoonKyoLee merged 6 commits intomainfrom
feat/get-receipt

Conversation

@JoonKyoLee
Copy link
Contributor

@JoonKyoLee JoonKyoLee commented Nov 23, 2025

✨ 작업 내용

  • 입고 조회 기능 구현

📝 적용 범위

  • /receipt

📌 참고 사항

Summary by CodeRabbit

새로운 기능

  • 입고 상세 조회 API 추가 - 특정 입고의 상세 정보 및 품목을 조회할 수 있습니다.

버그 수정

  • 입고 접근 권한 검증 강화 - 다른 상점의 입고에 대한 무단 접근을 방지하도록 개선되었습니다.

테스트

  • 입고 조회 기능에 대한 종합 테스트 추가 - 성공, 오류 시나리오 포함

✏️ Tip: You can customize this high-level summary in your review settings.

@JoonKyoLee JoonKyoLee self-assigned this Nov 23, 2025
@JoonKyoLee JoonKyoLee added the enhancement New feature or request label Nov 23, 2025
@coderabbitai
Copy link

coderabbitai bot commented Nov 23, 2025

Walkthrough

입고 조회 기능을 구현하며, 사용자 인증을 통한 상점별 접근 제어 검증을 포함한 새로운 GET 엔드포인트를 추가합니다. 성공/오류 응답 구조를 정의하고 엔드-투-엔드 테스트 커버리지를 확보합니다.

Changes

코호트 / 파일 변경 사항 요약
API 응답 정의
src/main/java/com/almang/inventory/global/api/SuccessMessage.java, src/main/java/com/almang/inventory/global/exception/ErrorCode.java
입고 조회 성공 메시지(GET_RECEIPT_SUCCESS) 및 접근 거부 오류 코드(RECEIPT_ACCESS_DENIED, HTTP 403) 추가
조회 엔드포인트 구현
src/main/java/com/almang/inventory/receipt/controller/ReceiptController.java
GET /api/v1/receipt/{receiptId} 엔드포인트 추가, 인증된 사용자의 요청 처리 및 서비스 호출
조회 로직 및 접근 제어
src/main/java/com/almang/inventory/receipt/service/ReceiptService.java
getReceipt(receiptId, userId) 공개 메서드 및 findReceiptByIdAndValidateAccess(receiptId, store) 비공개 헬퍼 메서드 추가로 상점 기반 접근 검증 구현
테스트 커버리지
src/test/java/com/almang/inventory/receipt/controller/ReceiptControllerTest.java
성공 케이스 및 3가지 오류 경로(사용자 미존재, 입고 미존재, 접근 거부) 테스트 추가
서비스 계층 테스트
src/test/java/com/almang/inventory/receipt/service/ReceiptServiceTest.java
기존 테스트 메서드명 재명명(발주기반_ 접두사) 및 조회 시나리오(성공, 미존재, 사용자 미존재, 접근 거부) 포괄적 테스트 추가

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Controller as ReceiptController
    participant Service as ReceiptService
    participant Repo as ReceiptRepository<br/>(JPA)
    participant DB as Database

    User->>Controller: GET /api/v1/receipt/{id}<br/>(with Auth)
    activate Controller
    
    Controller->>Service: getReceipt(receiptId, userId)
    activate Service
    
    Service->>Service: userStore = getUserStore(userId)
    alt User Not Found
        Service-->>Controller: throw USER_NOT_FOUND
    else User Found
        Service->>Repo: findById(receiptId)
        activate Repo
        Repo->>DB: SELECT receipt
        Repo-->>Service: Optional<Receipt>
        deactivate Repo
        
        alt Receipt Not Found
            Service-->>Controller: throw RECEIPT_NOT_FOUND
        else Receipt Found
            alt Receipt.store != userStore
                Service-->>Controller: throw RECEIPT_ACCESS_DENIED
            else Access Granted
                Service-->>Controller: ReceiptResponse
            end
        end
    end
    
    deactivate Service
    Controller-->>User: ApiResponse<ReceiptResponse>
    deactivate Controller
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12분

검토 포인트:

  • 접근 제어 검증 로직: findReceiptByIdAndValidateAccess() 메서드에서 RECEIPT_NOT_FOUNDRECEIPT_ACCESS_DENIED 예외의 발생 순서 확인 필요. 비존재 입고를 먼저 검사한 후 접근 제어를 검증하는 현재 순서가 정보 유출 방지 측면에서 최적인지 재검토 권장합니다.
  • 테스트 픽스처 일관성: ReceiptServiceTest와 ReceiptControllerTest의 Mock 데이터 설정 및 검증 로직이 일치하는지 확인하세요.
  • 로깅: 서비스 계층의 로깅이 민감한 정보(예: 상점 ID 비교 실패)를 노출하지 않는지 검토하세요.

Possibly related PRs

Poem

🎟️ 입고를 조회할 때,
세상은 좁아져, 너의 상점만 보인다.
접근 제어, 우아한 방패 🛡️
테스트 네 개가 모든 길을 지킨다.
기능은 완성, 리뷰는 순탄하리라! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목은 주요 변경사항인 '입고 조회 기능 구현'을 명확하고 간결하게 요약하고 있습니다.
Description check ✅ Passed PR 설명은 템플릿의 모든 필수 섹션(작업 내용, 적용 범위, 참고 사항)을 포함하고 있으며 정보가 충분합니다.
Linked Issues check ✅ Passed 변경사항들이 Issue #76의 요구사항인 '입고 조회 기능 구현'을 완전히 충족합니다.
Out of Scope Changes check ✅ Passed 모든 변경사항이 입고 조회 기능 구현에 관련되어 있으며, 범위를 벗어난 변경은 없습니다.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/get-receipt

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

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (5)
src/main/java/com/almang/inventory/receipt/controller/ReceiptController.java (1)

56-68: 단일 입고 조회 엔드포인트 구현이 기존 패턴과 잘 정렬되어 있습니다.

GET /api/v1/receipt/{receiptId} 가 기존 /from-order/{orderId} 핸들러와 동일한 구조(로그 → userId 추출 → 서비스 위임 → ApiResponse.success)를 따르고 있어 유지보수성이 좋습니다.
교육적 관점에서 한 가지 제안만 드리면, Swagger 문서를 더 풍부하게 만들고 싶을 때 @Operation(summary = "...", description = "...") 정도를 메서드에 추가하면 API 문서에서 조회 용도를 한눈에 파악하기 더 좋아집니다(이미 다른 컨트롤러에서 사용 중이라면 특히 일관성 측면에서 유리).

src/test/java/com/almang/inventory/receipt/controller/ReceiptControllerTest.java (1)

298-400: 입고 조회 컨트롤러 테스트 커버리지가 깔끔하게 완성되었습니다.

성공 케이스 + USER_NOT_FOUND + RECEIPT_NOT_FOUND + RECEIPT_ACCESS_DENIED까지 HTTP 상태, 메시지, 데이터 필드를 모두 검증하고 있어 컨트롤러 레이어의 행위가 충분히 보호되고 있습니다. 잘 뽑으셨습니다.
개선 아이디어를 하나만 덧붙이면, ReceiptItemResponse/ReceiptResponse 생성 코드가 from‑order/단일 조회 테스트들 사이에 거의 동일하게 반복되므로, 별도 헬퍼 메서드나 테스트 전용 fixture 클래스로 추출하면 중복이 줄고 이후 필드가 변경되었을 때 테스트 수정 범위도 줄일 수 있습니다.

src/main/java/com/almang/inventory/receipt/service/ReceiptService.java (1)

71-81: 입고 단건 조회 로직은 적절하지만, 트랜잭션 옵션·로그 개선 여지가 있습니다.

구현 자체는 findUserByIdStore 추출 → findReceiptByIdAndValidateAccessReceiptResponse.from 흐름이 명확하고, RECEIPT_NOT_FOUND / RECEIPT_ACCESS_DENIED 매핑도 의도에 잘 맞습니다. 다만 다음 두 가지를 고민해보시면 좋겠습니다.

  1. 읽기 전용 트랜잭션으로 정렬

    • getReceiptFromOrder@Transactional(readOnly = true) 인데, getReceipt는 일반 @Transactional입니다.
    • 둘 다 읽기 전용 조회이므로, getReceipt@Transactional(readOnly = true)로 맞추면 JPA의 dirty checking 부담을 줄이고 의도를 더 분명히 표현할 수 있습니다. (참고: Spring Framework Reference - Transaction Management 섹션의 read-only 옵션 설명)
  2. 로그 정보 확장

    • 요청 로그에서 receiptId가 빠져 있어, 404/403 발생 시 어떤 ID에 대한 요청이었는지 추적이 살짝 어려울 수 있습니다.
    • 예를 들어 아래처럼 receiptId도 함께 남기면 운영 중 디버깅 가독성이 좋아집니다.
- log.info("[ReceiptService] 입고 조회 요청 - userId: {}, storeId: {}", user.getId(), store.getId());
+ log.info("[ReceiptService] 입고 조회 요청 - userId: {}, storeId: {}, receiptId: {}",
+        user.getId(), store.getId(), receiptId);
  • 그리고 findReceiptByIdAndValidateAccess 쪽의 Receipt receipt = receiptRepository... (공백 두 칸)는 사소하지만 한 번 정리해 두면 코드 스타일도 더 깨끗해집니다.

위 두 가지는 기능 동작에는 영향 없고, 성능·운영/가독성 측면에서의 추천 사항입니다.

Also applies to: 145-153

src/test/java/com/almang/inventory/receipt/service/ReceiptServiceTest.java (2)

268-320: 발주기반 입고 조회 예외 시나리오를 잘 커버하고 있습니다.

발주기반_입고_조회시_입고가_존재하지_않으면_..., ...다른_상점의_발주면_..., ...사용자가_존재하지_않으면... 세 테스트가 각각 RECEIPT_NOT_FOUND, ORDER_ACCESS_DENIED, USER_NOT_FOUND를 정확하게 검증하고 있어서 서비스 로직의 예외 플로우가 안정적으로 보호되고 있습니다.
조금 더 견고하게 만들고 싶다면, BaseExceptiongetErrorCode() 같은 접근자가 있다면 현재처럼 hasMessageContaining(...) 대신 에러 코드 자체를 비교하도록 바꾸는 것을 고려해 보세요. 메시지 리팩터링에 덜 민감하고, 로직 의도를 더 직접적으로 표현할 수 있습니다.


322-428: 입고 단건 조회 서비스 테스트 구성도 탄탄합니다.

입고_조회에_성공한다에서 실제 영속화된 ReceiptReceiptItem을 기반으로 응답 필드를 검증하고, 이어서 세 개 테스트에서 RECEIPT_NOT_FOUND, USER_NOT_FOUND, RECEIPT_ACCESS_DENIED 각각을 체크해 주셔서 새로 추가된 getReceipt 경로가 거의 완전히 커버되고 있습니다. 아주 교과서적인 서비스 단 테스트 구성입니다.
위의 발주기반 테스트와 마찬가지로, 가능하다면 예외 검증을 메시지 포함 대신 에러 코드 비교 방식으로 통일하면 이후 메시지 변경(번역, 문구 수정 등)에 더 유연해질 수 있습니다. 또한 Receipt + ReceiptItem 생성 패턴이 여러 테스트에서 반복되니, 별도 private 헬퍼 메서드로 추출하면 테스트 본문이 “시나리오 설명”에만 집중할 수 있어 가독성이 한층 좋아집니다.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 35ded17 and 5565894.

📒 Files selected for processing (6)
  • src/main/java/com/almang/inventory/global/api/SuccessMessage.java (1 hunks)
  • src/main/java/com/almang/inventory/global/exception/ErrorCode.java (1 hunks)
  • src/main/java/com/almang/inventory/receipt/controller/ReceiptController.java (1 hunks)
  • src/main/java/com/almang/inventory/receipt/service/ReceiptService.java (2 hunks)
  • src/test/java/com/almang/inventory/receipt/controller/ReceiptControllerTest.java (1 hunks)
  • src/test/java/com/almang/inventory/receipt/service/ReceiptServiceTest.java (4 hunks)
🔇 Additional comments (2)
src/main/java/com/almang/inventory/global/api/SuccessMessage.java (1)

50-54: 입고 조회 성공 메시지 추가 일관성 좋습니다.

GET_RECEIPT_SUCCESS("입고 조회 성공") 이름/메시지가 기존 GET_*_SUCCESS 패턴과 잘 맞고, 컨트롤러/테스트에서도 동일하게 사용되어 응답 규격이 깔끔하게 맞춰졌습니다. 별도 수정 필요 없어 보입니다.

src/main/java/com/almang/inventory/global/exception/ErrorCode.java (1)

48-52: RECEIPT_ACCESS_DENIED 코드 추가 방향 적절합니다.

RECEIPT_ACCESS_DENIED(HttpStatus.FORBIDDEN, "해당 상점의 입고가 아닙니다.") 가 기존 *_ACCESS_DENIED들과 상태 코드/메시지 스타일이 통일되어 있고, 서비스/컨트롤러/테스트에서 잘 활용되고 있어 예외 설계가 명확합니다. 현재 수준에서 추가 변경은 없어도 되겠습니다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEAT] 입고 조회 기능 구현

1 participant