Skip to content

feat: 입고 삭제 기능 구현#90

Merged
JoonKyoLee merged 6 commits intomainfrom
feat/delete-receipt
Nov 24, 2025
Merged

feat: 입고 삭제 기능 구현#90
JoonKyoLee merged 6 commits intomainfrom
feat/delete-receipt

Conversation

@JoonKyoLee
Copy link
Contributor

@JoonKyoLee JoonKyoLee commented Nov 24, 2025

✨ 작업 내용

  • 입고 삭제 기능 구현

📝 적용 범위

  • /receipt

📌 참고 사항

Summary by CodeRabbit

  • New Features

    • 입고 삭제 기능이 추가되었습니다. 사용자는 이제 자신이 소유한 입고 기록을 삭제할 수 있습니다.
    • 삭제 성공 시 표시되는 성공 메시지가 추가되었습니다.
  • Tests

    • 입고 삭제 기능에 대한 성공/실패 시나리오 테스트가 추가되었습니다. 권한 검증, 존재 확인 및 접근 제어 오류 처리를 포함합니다.

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

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

coderabbitai bot commented Nov 24, 2025

Walkthrough

입고(Receipt) 삭제 기능을 추가했습니다: 새로운 DELETE 엔드포인트, 서비스의 삭제 로직, 응답 DTO와 성공 메시지 열거형 상수, 및 관련 컨트롤러·서비스 단위/통합 테스트가 도입되었습니다.

Changes

응집군 / 파일(들) 변경 요약
응답 타입
src/main/java/com/almang/inventory/receipt/dto/response/DeleteReceiptResponse.java
삭제 응답용 public record DeleteReceiptResponse(boolean success) 추가
성공 메시지 열거형
src/main/java/com/almang/inventory/global/api/SuccessMessage.java
enum에 DELETE_RECEIPT_SUCCESS("입고 삭제 성공") 상수 추가
서비스 계층
src/main/java/com/almang/inventory/receipt/service/ReceiptService.java
public DeleteReceiptResponse deleteReceipt(Long receiptId, Long userId) 메서드 추가: 사용자 조회, 접근 검증, receipt 비활성화 및 응답 반환
컨트롤러
src/main/java/com/almang/inventory/receipt/controller/ReceiptController.java
@DeleteMapping("/{receiptId}") 엔드포인트 추가: 인증 정보 사용, 로깅, 서비스 호출 및 ApiResponse 반환
컨트롤러 테스트
src/test/java/com/almang/inventory/receipt/controller/ReceiptControllerTest.java
DELETE 엔드포인트 관련 4개 통합 테스트 추가(성공, 사용자 없음, 입고 없음, 접근 거부)
서비스 테스트
src/test/java/com/almang/inventory/receipt/service/ReceiptServiceTest.java
deleteReceipt 관련 4개 단위 테스트 추가(성공 상태 변경 검증 및 예외 경로)

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client
    participant Controller as ReceiptController
    participant Service as ReceiptService
    participant Repo as Repository / DB

    Client->>Controller: DELETE /api/v1/receipt/{receiptId} (Auth)
    Note right of Controller `#dff0d8`: CustomUserPrincipal에서 userId 추출
    Controller->>Service: deleteReceipt(receiptId, userId)
    
    Service->>Repo: findUserById(userId)
    Repo-->>Service: User
    
    Service->>Repo: findReceiptByIdAndValidateAccess(receiptId, user.storeId)
    alt 접근 허용
        Repo-->>Service: Receipt
        Service->>Repo: receipt.deactivate() / save()
        Repo->>DB: UPDATE receipt (activated=false, status=CANCELED)
        DB-->>Repo: OK
        Repo-->>Service: updated Receipt
        Service-->>Controller: DeleteReceiptResponse(true)
    else 접근 거부 / 미존재
        Repo-->>Service: 예외(BaseException)
        Service-->>Controller: 예외 전파
    end

    Controller->>Client: ApiResponse(code=200, message="입고 삭제 성공", data=DeleteReceiptResponse)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 분

추가 주의 포인트:

  • findReceiptByIdAndValidateAccess 호출 경계(매장 식별자 vs. 사용자 권한)가 정확히 매핑되는지 확인하세요.
  • 트랜잭션 범위(@Transactional)와 비활성화(activated 필드) 변경이 관련 엔티티 일관성(예: 재고, 연관 로그)에 영향을 주지 않는지 검토하세요.
  • 컨트롤러 레벨의 HTTP 상태/에러 매핑과 ApiResponse 포맷(코드·메시지 일관성)을 확인하세요.

권장 문서: Spring @Transactional 가이드, REST API 에러 핸들링 모범 사례 문서.

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 PR의 모든 코드 변경사항이 이슈 #79의 '입고 삭제 기능 구현' 요구사항을 충족합니다. 엔드포인트, 서비스, 응답 DTO, 그리고 포괄적인 테스트가 모두 구현되었습니다.
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/delete-receipt

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b8f0f43 and 02647c3.

📒 Files selected for processing (1)
  • src/main/java/com/almang/inventory/receipt/service/ReceiptService.java (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-22T18:12:13.161Z
Learnt from: JoonKyoLee
Repo: almang2/inventory-server PR: 66
File: src/main/java/com/almang/inventory/order/domain/OrderItem.java:41-53
Timestamp: 2025-11-22T18:12:13.161Z
Learning: In the almang2/inventory-server repository, OrderItem entity update methods (updateQuantity, updatePrice in src/main/java/com/almang/inventory/order/domain/OrderItem.java) do not require null checks because OrderService will validate parameters before calling these update methods, following the same pattern as Product entity updates.
<!--

Applied to files:

  • src/main/java/com/almang/inventory/receipt/service/ReceiptService.java
🔇 Additional comments (2)
src/main/java/com/almang/inventory/receipt/service/ReceiptService.java (2)

16-16: 새로운 응답 DTO import 추가 확인!

삭제 기능에 필요한 DeleteReceiptResponse import가 적절하게 추가되었습니다.


134-145: 추가 검증이 필요합니다. 다음 항목들을 확인하겠습니다:

이제 Receipt 엔티티 정의를 직접 확인하겠습니다:

멱등성 검증 완료 — 안전합니다! ✓

idempotency와 orphanRemoval 패턴에 대한 우려는 완전히 해소되었습니다. deactivate() 메서드는 단순 할당 연산(activated = false, status = CANCELED)을 수행하므로 멱등성을 완벽하게 보장합니다. 동일한 입고에 대해 여러 번 삭제를 요청해도 같은 결과를 반환하므로 REST API 설계 원칙을 만족합니다.

다만, 한 가지 선택적 개선사항이 있습니다:

상태 기반 삭제 로직 추가 검토 (선택사항)

  • 현재 구현에서는 어떤 상태의 입고든 삭제 가능합니다
  • 비즈니스 요구사항에 따라 특정 상태(예: 이미 CANCELED인 입고)에서의 재삭제 시도를 명시적으로 거부하거나, COMPLETED 상태의 입고는 삭제 불가능하도록 제한하는 검증 로직을 추가하면 더 견고한 설계가 될 수 있습니다.

예: updateReceipt 메서드처럼 상태 검증을 앞단에 추가하는 것을 고려해보세요.



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: 1

🧹 Nitpick comments (1)
src/test/java/com/almang/inventory/receipt/service/ReceiptServiceTest.java (1)

896-924: 반환값 검증을 추가하세요.

테스트가 삭제 후 엔티티 상태(activated가 false, status가 CANCELED)를 잘 검증하고 있습니다. 하지만 deleteReceipt의 반환값(DeleteReceiptResponse)도 검증하는 것이 좋습니다.

다음과 같이 개선할 수 있습니다:

 // when
-receiptService.deleteReceipt(receipt.getId(), user.getId());
+DeleteReceiptResponse response = receiptService.deleteReceipt(receipt.getId(), user.getId());

 // then
+assertThat(response).isNotNull();
+assertThat(response.success()).isTrue();
 Receipt deleted = receiptRepository.findById(receipt.getId()).orElseThrow();
 assertThat(deleted.isActivated()).isFalse();
 assertThat(deleted.getStatus()).isEqualTo(ReceiptStatus.CANCELED);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 69843d3 and b8f0f43.

📒 Files selected for processing (6)
  • src/main/java/com/almang/inventory/global/api/SuccessMessage.java (1 hunks)
  • src/main/java/com/almang/inventory/receipt/controller/ReceiptController.java (3 hunks)
  • src/main/java/com/almang/inventory/receipt/dto/response/DeleteReceiptResponse.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 (3 hunks)
  • src/test/java/com/almang/inventory/receipt/service/ReceiptServiceTest.java (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-22T18:12:13.161Z
Learnt from: JoonKyoLee
Repo: almang2/inventory-server PR: 66
File: src/main/java/com/almang/inventory/order/domain/OrderItem.java:41-53
Timestamp: 2025-11-22T18:12:13.161Z
Learning: In the almang2/inventory-server repository, OrderItem entity update methods (updateQuantity, updatePrice in src/main/java/com/almang/inventory/order/domain/OrderItem.java) do not require null checks because OrderService will validate parameters before calling these update methods, following the same pattern as Product entity updates.
<!--

Applied to files:

  • src/test/java/com/almang/inventory/receipt/controller/ReceiptControllerTest.java
  • src/main/java/com/almang/inventory/receipt/service/ReceiptService.java
🔇 Additional comments (9)
src/main/java/com/almang/inventory/global/api/SuccessMessage.java (1)

56-56: 잘 구현되었습니다! 👍

성공 메시지가 기존 패턴과 일관되게 추가되었고, 적절한 섹션(RECEIPT)에 배치되었습니다.

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

926-979: 예외 케이스 테스트가 잘 구현되었습니다!

세 가지 예외 시나리오(사용자 없음, 입고 없음, 교차 상점 접근)가 모두 적절히 테스트되었습니다. 특히 라인 952-979의 교차 상점 접근 테스트는 보안 관점에서 중요한 검증입니다.

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

7-7: 필요한 임포트가 올바르게 추가되었습니다.

새로운 삭제 기능을 위한 임포트가 적절히 추가되었습니다.

Also applies to: 22-22


714-780: 컨트롤러 테스트가 체계적으로 구현되었습니다!

DELETE 엔드포인트에 대한 4가지 시나리오(성공, 사용자 없음, 입고 없음, 접근 거부)가 모두 테스트되었습니다. 각 테스트는 적절한 HTTP 상태 코드와 응답 구조를 검증하고 있습니다.

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

16-16: 임포트가 적절히 추가되었습니다.

새로운 응답 DTO를 위한 임포트입니다.


141-141: 현재 구현은 RESTful DELETE의 멱등성 원칙을 올바르게 따르고 있습니다.

검증 결과:

  • Receipt.deactivate(): 멱등적 설계 (activated=false, status=CANCELED로 단순 설정)
  • deleteReceipt(): 이미 삭제된 입고도 접근 가능하면 성공 처리
  • 현재 동작이 바로 리뷰 코멘트에서 "일반적"이라고 언급한 멱등성 패턴입니다

RESTful API 설계 관례상, DELETE 요청은 멱등성을 가져야 하므로 이미 삭제된 리소스에 대한 재삭제 요청도 성공으로 처리하는 것이 표준입니다. 추가 검증이나 명시적 에러 코드는 불필요하며, 현재 구현이 권장 사항을 충족하고 있습니다.

src/main/java/com/almang/inventory/receipt/controller/ReceiptController.java (2)

9-9: 필요한 임포트가 올바르게 추가되었습니다.

새로운 삭제 엔드포인트를 위한 임포트가 적절히 추가되었습니다.

Also applies to: 20-20


122-135: 삭제 엔드포인트가 잘 구현되었습니다!

엔드포인트 구현이 기존 컨트롤러의 패턴과 일관되게 작성되었습니다:

  • 적절한 인증 처리
  • 일관된 로깅
  • 표준 응답 구조 사용

참고: 현재는 200 OK를 반환하고 있지만, REST API 관례상 DELETE 요청이 성공하면 204 No Content를 반환하는 것도 고려해볼 수 있습니다. 다만 프로젝트 전체의 일관성을 위해 현재 패턴을 유지하는 것도 합리적인 선택입니다.

src/main/java/com/almang/inventory/receipt/dto/response/DeleteReceiptResponse.java (1)

3-5: 프로젝트 전체 삭제 응답 설계의 재검토가 필요합니다.

검증 결과, DeleteReceiptResponse는 고립된 문제가 아니라 프로젝트 전체의 일관된 패턴입니다. DeleteOrderResponse, DeleteUserResponse도 동일한 boolean success 구조를 사용하고 있습니다.

당신의 우려는 타당합니다:

  • 모든 삭제 작업은 new DeleteXxxResponse(true)를 반환합니다
  • 실패 시 예외가 발생하므로 success boolean은 실질적으로 항상 true입니다
  • 응답에 삭제된 리소스의 식별 정보가 없어 클라이언트가 어떤 리소스가 삭제되었는지 명시적으로 확인할 수 없습니다

권장 개선 방안 (프로젝트 전체에 일관되게 적용):

  1. 삭제 ID 반환 (권장):

    public record DeleteReceiptResponse(
        Long receiptId  // 또는 timestamp도 함께
    ) {}
    • 클라이언트가 멱등성(idempotence) 처리 시 유용합니다
    • 작업 추적성을 명확히 합니다
  2. 204 No Content 상태 코드:

    • 응답 본문 없이 성공 여부를 상태 코드로만 전달
    • REST 모범 사례와 더 부합합니다

현재 설계는 ApiResponse<DeleteReceiptResponse> 래퍼로 인해 비용이 증가하는 반면 정보 가치가 낮습니다. DeleteOrderResponse, DeleteUserResponse와 함께 리팩토링하시기 바랍니다.

참고 자료: RFC 7231 - DELETE 메서드, REST API 설계 가이드

@JoonKyoLee JoonKyoLee merged commit 439554c into main Nov 24, 2025
1 check passed
@JoonKyoLee JoonKyoLee deleted the feat/delete-receipt branch November 24, 2025 14:58
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