Skip to content

[Feat] 컬렉션 관리 API 구현 - #31

Merged
kangcheolung merged 5 commits into
developfrom
feature/29
Jul 16, 2026
Merged

[Feat] 컬렉션 관리 API 구현#31
kangcheolung merged 5 commits into
developfrom
feature/29

Conversation

@kangcheolung

@kangcheolung kangcheolung commented Jul 16, 2026

Copy link
Copy Markdown
Member

🔍 작업 내용

✨ 상세 설명

컬렉션 목록 조회, 컬렉션 삭제(soft delete), 컬렉션에서 문서 제거 API를 구현했습니다.

  • GET /collections — 로그인한 사용자가 소유한 ACTIVE 상태 컬렉션 목록 반환
  • DELETE /collections/{collectionId} — 소유자만 가능, soft delete + 관련 권한 및 USER 캐시 일괄 무효화
  • DELETE /collections/{collectionId}/documents/{documentId} — 소유자만 가능, 해당 문서에 대한 캐시만 선택적으로 무효화

컬렉션 삭제와 문서 제거 시 캐시 무효화 범위를 다르게 처리했습니다.

  • 컬렉션 삭제: 소속된 모든 문서의 캐시를 권한 단위로 일괄 무효화
  • 문서 제거: 제거된 문서에 대한 캐시만 단일 쿼리로 무효화 (나머지 문서 캐시 유지)

설계 문서: docs/chelung-#29-collection-management.md

🛠️ 추후 리팩토링 및 고도화 계획

  • 컬렉션 삭제 시 하위 컬렉션(parentCollection) 처리 정책 결정 필요

📸 스크린샷 (선택)

💬 리뷰 요구사항

  • 컬렉션 삭제 시 소유자만 허용하고 ADMIN 권한자는 제외한 설계 방향이 적절한지 확인 부탁드립니다.
  • 문서 제거 시 캐시 무효화를 별도 메서드(bulkRevokeBySourcesForDocument)로 분리한 구조가 괜찮은지 봐주세요.

Summary by CodeRabbit

  • 새 기능

    • 내 컬렉션 목록을 조회할 수 있습니다.
    • 컬렉션을 삭제하거나 컬렉션 내 문서를 제거할 수 있습니다.
    • 삭제 및 문서 제거 시 관련 권한과 접근 정보가 안전하게 정리됩니다.
    • 컬렉션에 없는 문서에 대한 오류 안내가 추가되었습니다.
  • 문서화

    • 컬렉션 관리와 권한 부여·조회·검증 기능의 설계 문서를 추가했습니다.
    • PR 및 테스트 결과 문서 작성 규칙을 정리했습니다.
  • 버그 수정

    • 컬렉션 삭제와 문서 제거의 소유자 권한 및 예외 처리를 보완했습니다.

kangcheolung and others added 4 commits July 16, 2026 22:35
@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@kangcheolung, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 53 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 4e55ce9d-da0a-490f-b8be-8e1df60782a2

📥 Commits

Reviewing files that changed from the base of the PR and between 188b428 and a042412.

📒 Files selected for processing (2)
  • CLAUDE.md
  • docs/chelung-#24-document-permission-check.md
📝 Walkthrough

Walkthrough

컬렉션 목록 조회, 컬렉션 soft delete, 컬렉션 내 문서 제거 API가 추가되었다. 삭제 작업은 소유자 검증과 USER 권한 캐시 무효화를 수행하며, 관련 리포지토리·오류 코드·단위 테스트와 설계 문서가 함께 보강되었다.

Changes

컬렉션 관리 기능

Layer / File(s) Summary
문서 관리 규칙
.claude/rules/deploy.md, CLAUDE.md
PR 설계 문서와 테스트 결과 문서의 저장 위치, 파일명, 작성 규칙과 프로젝트 구조가 문서화되었다.
컬렉션 API 설계 문서
docs/chelung-#16-collection-crud.md, docs/chelung-#29-collection-management.md
컬렉션 CRUD, 목록 조회, soft delete, 문서 제거 API의 계약과 처리 흐름이 정의되었다.
권한 및 캐시 설계 문서
docs/chelung-#18-permission-grant-revoke.md, docs/chelung-#21-permission-query-service.md, docs/chelung-#24-document-permission-check.md
권한 판정 순서와 USER 캐시 갱신·무효화, 문서 권한 요약 API가 문서화되었다.
컬렉션 조회·삭제 API 연결
src/main/java/com/opensource/docgrid/domain/collection/controller/CollectionController.java, src/main/java/com/opensource/docgrid/domain/collection/service/{query,command}/*, src/main/java/com/opensource/docgrid/domain/collection/repository/*, src/main/java/com/opensource/docgrid/global/exception/ErrorCode.java
GET /collections와 두 DELETE 엔드포인트가 추가되고 ACTIVE 필터, 문서 연결 조회, 컬렉션 문서 없음 오류가 연결되었다.
삭제 처리와 캐시 무효화
src/main/java/com/opensource/docgrid/domain/permission/repository/*, src/main/java/com/opensource/docgrid/domain/permission/service/command/*, src/main/java/com/opensource/docgrid/domain/collection/service/command/CollectionCommandService.java
컬렉션 삭제 시 전체 USER 파생 캐시를, 문서 제거 시 해당 문서의 캐시만 일괄 무효화하도록 구현되었다.
삭제 시나리오 테스트
src/test/java/com/opensource/docgrid/domain/collection/service/command/CollectionCommandServiceTest.java
삭제·문서 제거 성공 및 COLLECTION_NOT_FOUND, COLLECTION_DOCUMENT_NOT_FOUND, PERMISSION_DENIED 예외를 검증한다.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant CollectionController
  participant CollectionCommandService
  participant UserDocumentAccessCacheService
  participant CollectionDocumentRepository
  Client->>CollectionController: DELETE /collections/{collectionId}/documents/{documentId}
  CollectionController->>CollectionCommandService: removeDocument(collectionId, documentId, userId)
  CollectionCommandService->>UserDocumentAccessCacheService: bulkRevokeBySourcesForDocument(...)
  UserDocumentAccessCacheService-->>CollectionCommandService: cache invalidated
  CollectionCommandService->>CollectionDocumentRepository: delete(collectionDocument)
  CollectionController-->>Client: 204 No Content
Loading

Possibly related PRs

  • DocGrid/backend#2: ErrorCode enum과 COLLECTION_DOCUMENT_NOT_FOUND 오류 코드 변경이 연결된다.
  • DocGrid/backend#17: 기존 컬렉션 컴포넌트에 목록 조회·삭제·문서 제거 기능을 확장한다.
  • DocGrid/backend#19: USER 문서 접근 캐시의 grant/revoke 동작을 문서 제거 흐름으로 확장한다.

Suggested labels: ✨ Feature

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning [#29] 내 목록 조회·소유자 전용 삭제·문서 제거·테스트는 맞지만, 소프트 삭제 시 collection_permissions 전체 삭제가 확인되지 않습니다. 소프트 삭제 시 해당 컬렉션의 collection_permissions를 실제로 삭제하고, 그 동작을 단위 테스트로 추가해 주세요.
Docstring Coverage ⚠️ Warning Docstring coverage is 60.71% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed 제목이 컬렉션 관리 API 구현이라는 핵심 변경을 간결하게 잘 요약합니다.
Description check ✅ Passed 필수 섹션과 작업 요약이 대부분 채워져 있어 템플릿 요구를 충족합니다.
Out of Scope Changes check ✅ Passed 변경된 코드와 문서는 모두 컬렉션 관리와 관련 권한 처리에 연관되어 있어 명백한 범위 이탈은 보이지 않습니다.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/29

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (4)
src/test/java/com/opensource/docgrid/domain/collection/service/command/CollectionCommandServiceTest.java (2)

301-303: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

구체적인 Mock 인자 검증을 통해 테스트 정확도를 높이세요.

bulkRevokeBySourcesForDocument 호출 시 any()를 사용하고 있어 의도하지 않은 파라미터가 전달되어도 테스트가 통과할 위험이 있습니다.

Mock 사용법 지침에 따라 실제 사용되는 값을 명시하여 검증을 강화하는 것을 제안합니다.

💡 제안하는 수정안
-        then(cacheService).should().bulkRevokeBySourcesForDocument(any(), any(), any());
-        then(collectionDocumentRepository).should().delete(cd);
+        then(cacheService).should().bulkRevokeBySourcesForDocument(eq(AccessSourceType.DIRECT_COLLECTION_PERMISSION), eq(List.of(userPermission.getId())), eq(document.getId()));
+        then(collectionDocumentRepository).should().delete(cd);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/test/java/com/opensource/docgrid/domain/collection/service/command/CollectionCommandServiceTest.java`
around lines 301 - 303, Update the verification for
cacheService.bulkRevokeBySourcesForDocument in CollectionCommandServiceTest to
assert the exact arguments used by the test scenario instead of any() matchers.
Reuse the concrete source, document, and remaining parameter values established
in the test setup, while leaving the collectionDocumentRepository.delete(cd)
verification unchanged.

Source: Path instructions


236-251: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Soft delete 상태 검증 및 구체적인 Mock 인자 검증을 추가하세요.

@DisplayName에는 "soft delete 처리되고"라고 명시되어 있으나, 실제 테스트 코드에는 collection 객체가 soft delete 상태(deletedAt, status 등)로 변경되었는지 검증하는 Assertion이 누락되어 있습니다. 또한, Mock 객체의 행위 검증 시 any() 대신 구체적인 값을 사용하면 테스트의 신뢰도를 높일 수 있습니다.

테스트 커버리지와 mock 사용법 규칙을 준수하기 위해 아래와 같이 개선할 것을 제안합니다.

💡 제안하는 수정안
     `@Test`
     `@DisplayName`("소유자가 컬렉션을 삭제하면 soft delete 처리되고 권한 및 캐시가 무효화된다")
     void deleteCollection_succeeds_when_owner() {
         User owner = CollectionFixture.createOwner();
         DocumentCollection collection = CollectionFixture.createCollection(owner);
         CollectionPermission userPermission = PermissionFixture.createCollectionPermission(collection, owner);
 
         given(collectionRepository.findById(CollectionFixture.COLLECTION_ID)).willReturn(Optional.of(collection));
         given(collectionPermissionRepository.findAllByCollectionId(CollectionFixture.COLLECTION_ID))
                 .willReturn(List.of(userPermission));
 
         collectionCommandService.deleteCollection(CollectionFixture.COLLECTION_ID, CollectionFixture.USER_ID);
 
-        then(cacheService).should().bulkRevokeBySource(any(), any());
-        then(collectionPermissionRepository).should().deleteAll(any());
+        then(cacheService).should().bulkRevokeBySource(eq(AccessSourceType.DIRECT_COLLECTION_PERMISSION), eq(userPermission.getId()));
+        then(collectionPermissionRepository).should().deleteAll(List.of(userPermission));
+        
+        // Soft delete 상태 검증 (실제 도메인 객체의 메서드명에 맞게 조정)
+        assertThat(collection.getDeletedAt()).isNotNull();
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/test/java/com/opensource/docgrid/domain/collection/service/command/CollectionCommandServiceTest.java`
around lines 236 - 251, Update deleteCollection_succeeds_when_owner to assert
that collection is soft-deleted by verifying its deletedAt/status state after
the service call. Replace broad any() arguments in
cacheService.bulkRevokeBySource and collectionPermissionRepository.deleteAll
verifications with the specific collection and permission values expected from
this scenario.

Source: Path instructions

src/main/java/com/opensource/docgrid/domain/collection/service/command/CollectionCommandService.java (2)

113-117: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

N+1 쿼리 방지를 위한 성능 최적화 제안

deleteAll(permissions)는 내부적으로 각 엔티티마다 단건 DELETE 쿼리를 수행하여 N+1 문제를 유발할 수 있습니다. 성능 최적화를 위해 deleteAllInBatch(permissions) 사용을 권장합니다.

또한, cacheService.bulkRevokeBySource를 반복문 내에서 호출하면 권한 수만큼 업데이트 쿼리가 발생하므로, 향후 성능 확장을 위해 다건의 sourceId를 한 번에 무효화하는 벌크 처리 메서드 도입을 고려해 보는 것도 좋습니다.

💡 제안하는 수정안
         List<CollectionPermission> permissions = collectionPermissionRepository.findAllByCollectionId(collectionId);
         permissions.stream()
                 .filter(p -> p.getTargetType() == PermissionTargetType.USER)
                 .forEach(p -> cacheService.bulkRevokeBySource(AccessSourceType.DIRECT_COLLECTION_PERMISSION, p.getId()));
-        collectionPermissionRepository.deleteAll(permissions);
+        collectionPermissionRepository.deleteAllInBatch(permissions);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/com/opensource/docgrid/domain/collection/service/command/CollectionCommandService.java`
around lines 113 - 117, Update the permission deletion flow in
CollectionCommandService to use
collectionPermissionRepository.deleteAllInBatch(permissions) instead of
deleteAll(permissions). Preserve the existing filtering and cache invalidation
behavior; the bulk cache API is only a future consideration and does not need to
be introduced here.

135-139: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

메모리 필터링 대신 DB 레벨 필터링 제안

현재 컬렉션에 속한 모든 권한 엔티티를 영속성 컨텍스트로 불러온 뒤 메모리에서 USER 타입만 필터링하여 ID를 추출하고 있습니다. 컬렉션에 연결된 부서나 역할 권한 데이터가 많아질 경우 불필요한 메모리 및 I/O 오버헤드가 발생할 수 있습니다.

성능 향상을 위해 Repository 단에서 특정 타입의 권한 ID만 직접 조회하는 전용 쿼리를 추가하는 것을 권장합니다.

💡 제안하는 접근 방식

CollectionPermissionRepository에 기존 방식처럼 정규화된 Enum 패키지명을 활용한 쿼리를 추가할 수 있습니다:

`@Query`("""
        SELECT cp.id FROM CollectionPermission cp 
        WHERE cp.collection.id = :collectionId 
          AND cp.targetType = com.opensource.docgrid.domain.permission.enums.PermissionTargetType.USER
        """)
List<Long> findUserPermissionIdsByCollectionId(`@Param`("collectionId") Long collectionId);

이후 CollectionCommandService의 호출부를 다음과 같이 간결하게 수정할 수 있습니다:

-        List<Long> userPermissionIds = collectionPermissionRepository.findAllByCollectionId(collectionId)
-                .stream()
-                .filter(p -> p.getTargetType() == PermissionTargetType.USER)
-                .map(CollectionPermission::getId)
-                .toList();
+        List<Long> userPermissionIds = collectionPermissionRepository.findUserPermissionIdsByCollectionId(collectionId);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/com/opensource/docgrid/domain/collection/service/command/CollectionCommandService.java`
around lines 135 - 139, Replace the in-memory filtering in
CollectionCommandService with a repository-level query that directly returns
USER-target permission IDs. Add a dedicated method such as
findUserPermissionIdsByCollectionId to CollectionPermissionRepository, filtering
by collectionId and PermissionTargetType.USER, then call it from the existing
command-service flow.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@CLAUDE.md`:
- Around line 74-78: Update the filename examples in the docs/ section of
CLAUDE.md to use the {github아이디}-#{이슈번호}-{설명}.md convention for both PR design
documents and test-results documents, matching .claude/rules/deploy.md and the
existing documentation naming pattern.

In `@docs/chelung-`#24-document-permission-check.md:
- Around line 204-212: 완료 기준의 PUBLIC 문서 권한 항목에서 잘못 표기된 `PUBLILC`를 API 응답과 일치하는
`PUBLIC`으로 수정하세요.

---

Nitpick comments:
In
`@src/main/java/com/opensource/docgrid/domain/collection/service/command/CollectionCommandService.java`:
- Around line 113-117: Update the permission deletion flow in
CollectionCommandService to use
collectionPermissionRepository.deleteAllInBatch(permissions) instead of
deleteAll(permissions). Preserve the existing filtering and cache invalidation
behavior; the bulk cache API is only a future consideration and does not need to
be introduced here.
- Around line 135-139: Replace the in-memory filtering in
CollectionCommandService with a repository-level query that directly returns
USER-target permission IDs. Add a dedicated method such as
findUserPermissionIdsByCollectionId to CollectionPermissionRepository, filtering
by collectionId and PermissionTargetType.USER, then call it from the existing
command-service flow.

In
`@src/test/java/com/opensource/docgrid/domain/collection/service/command/CollectionCommandServiceTest.java`:
- Around line 301-303: Update the verification for
cacheService.bulkRevokeBySourcesForDocument in CollectionCommandServiceTest to
assert the exact arguments used by the test scenario instead of any() matchers.
Reuse the concrete source, document, and remaining parameter values established
in the test setup, while leaving the collectionDocumentRepository.delete(cd)
verification unchanged.
- Around line 236-251: Update deleteCollection_succeeds_when_owner to assert
that collection is soft-deleted by verifying its deletedAt/status state after
the service call. Replace broad any() arguments in
cacheService.bulkRevokeBySource and collectionPermissionRepository.deleteAll
verifications with the specific collection and permission values expected from
this scenario.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 4f22cbd7-87a6-4a77-9fc3-820c154e25b9

📥 Commits

Reviewing files that changed from the base of the PR and between 69203eb and 188b428.

📒 Files selected for processing (17)
  • .claude/rules/deploy.md
  • CLAUDE.md
  • docs/chelung-#16-collection-crud.md
  • docs/chelung-#18-permission-grant-revoke.md
  • docs/chelung-#21-permission-query-service.md
  • docs/chelung-#24-document-permission-check.md
  • docs/chelung-#29-collection-management.md
  • src/main/java/com/opensource/docgrid/domain/collection/controller/CollectionController.java
  • src/main/java/com/opensource/docgrid/domain/collection/repository/CollectionDocumentRepository.java
  • src/main/java/com/opensource/docgrid/domain/collection/repository/CollectionRepository.java
  • src/main/java/com/opensource/docgrid/domain/collection/service/command/CollectionCommandService.java
  • src/main/java/com/opensource/docgrid/domain/collection/service/query/CollectionQueryService.java
  • src/main/java/com/opensource/docgrid/domain/permission/repository/CollectionPermissionRepository.java
  • src/main/java/com/opensource/docgrid/domain/permission/repository/UserDocumentAccessCacheRepository.java
  • src/main/java/com/opensource/docgrid/domain/permission/service/command/UserDocumentAccessCacheService.java
  • src/main/java/com/opensource/docgrid/global/exception/ErrorCode.java
  • src/test/java/com/opensource/docgrid/domain/collection/service/command/CollectionCommandServiceTest.java

Comment thread CLAUDE.md Outdated
Comment thread docs/chelung-#24-document-permission-check.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@kangcheolung
kangcheolung merged commit b4f1231 into develop Jul 16, 2026
1 check passed
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.

[Feat] 컬렉션 관리 API — 목록 조회 / 삭제 / 문서 제거

1 participant