[Feat] 문서 목록 조회 API - #154
Conversation
검색 pre-filter UNION 쿼리에 하드코딩된 status = 'INDEXED'를 파라미터로 바꿔 목록 조회에서도 재사용할 수 있게 한다. 쿼리를 복제하면 권한 정책이 두 벌이 되어 접근 경로가 추가될 때 한쪽만 고치는 사고가 나기 쉽다. 검색 호출부는 INDEXED만 넘겨 기존 동작을 그대로 유지한다. 목록 화면용 페이지 조회 쿼리도 함께 추가한다.
권한 pre-filter로 읽을 수 있는 문서 ID를 먼저 좁히고, 그 집합에 대해서만 정렬·페이징을 수행한다. 읽을 수 있는 문서가 없으면 페이지 쿼리를 실행하지 않는다. 정렬은 createdAt DESC, id DESC로 고정한다. 정렬 키를 외부에 열어두면 인덱스 없는 컬럼 정렬 요청이 그대로 DB로 흘러가고 응답 계약도 불안정해진다.
GET /api/documents로 로그인 사용자가 읽을 수 있는 문서를 페이지 조회한다. @validated로 page는 0 이상, size는 1~100 범위를 검증한다.
목록 조회의 권한 필터, status 기본값과 명시 지정 동작을 단위 테스트로 검증하고 상태 파라미터가 실제 UNION 쿼리에 반영되는지 @DataJpaTest로 확인한다. Repository 테스트는 seed 데이터에 PUBLIC·INDEXED 문서가 있어 모든 사용자 조회 결과에 포함되므로, 테스트가 생성한 문서만 포함·제외로 검증한다. 상태 파라미터화로 시그니처가 바뀐 기존 검색 테스트 호출부도 함께 맞춘다.
📝 WalkthroughWalkthrough문서 목록 조회 API를 추가했습니다. 사용자의 읽기 권한을 먼저 확인한 뒤 상태 필터, 고정 정렬, 페이지네이션을 적용합니다. 검색은 기존처럼 Changes문서 목록 조회 API
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant Client
participant DocumentQueryController
participant DocumentQueryService
participant DocumentRepository
participant DocumentSummaryConverter
Client->>DocumentQueryController: GET /api/documents?status&page&size
DocumentQueryController->>DocumentQueryService: getMyDocuments(userId, status, page, size)
DocumentQueryService->>DocumentRepository: readable document IDs with statuses
DocumentRepository-->>DocumentQueryService: accessible IDs
DocumentQueryService->>DocumentRepository: paged documents with currentVersion
DocumentRepository-->>DocumentQueryService: Page<Document>
DocumentQueryService->>DocumentSummaryConverter: convert each document
DocumentSummaryConverter-->>DocumentQueryService: DocumentSummaryResponse
DocumentQueryService-->>DocumentQueryController: PageResponse
DocumentQueryController-->>Client: document summary page
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/main/java/com/opensource/docgrid/domain/document/dto/response/DocumentSummaryResponse.java (1)
12-25: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win신규 Java 타입에 클래스 수준 주석을 추가하십시오.
src/main/java/com/opensource/docgrid/domain/document/dto/response/DocumentSummaryResponse.java#L12-L25: 목록 API 응답 DTO의 역할, 포함 범위, 불변 응답 계약을 설명하는 주석을 추가하십시오.src/main/java/com/opensource/docgrid/domain/document/converter/DocumentSummaryConverter.java#L9-L10:Document를 목록 응답 DTO로 변환하는 책임과 경계를 설명하는 주석을 추가하십시오.As per coding guidelines: “Every newly created class, interface, or record must have a class-level comment explaining its role, responsibility, and boundary.”
🤖 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/document/dto/response/DocumentSummaryResponse.java` around lines 12 - 25, Add class-level comments to DocumentSummaryResponse and DocumentSummaryConverter. In src/main/java/com/opensource/docgrid/domain/document/dto/response/DocumentSummaryResponse.java lines 12-25, document the list API response DTO’s role, included scope, and immutable response contract; in src/main/java/com/opensource/docgrid/domain/document/converter/DocumentSummaryConverter.java lines 9-10, document its responsibility and boundary for converting Document instances into list response DTOs.Source: Coding guidelines
🤖 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.
Nitpick comments:
In
`@src/main/java/com/opensource/docgrid/domain/document/dto/response/DocumentSummaryResponse.java`:
- Around line 12-25: Add class-level comments to DocumentSummaryResponse and
DocumentSummaryConverter. In
src/main/java/com/opensource/docgrid/domain/document/dto/response/DocumentSummaryResponse.java
lines 12-25, document the list API response DTO’s role, included scope, and
immutable response contract; in
src/main/java/com/opensource/docgrid/domain/document/converter/DocumentSummaryConverter.java
lines 9-10, document its responsibility and boundary for converting Document
instances into list response DTOs.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: ddd1ac1e-2ad7-495c-8cc1-b2b8034c19e2
📒 Files selected for processing (11)
docs/design/gimin-#153-document-list-api.mdsrc/main/java/com/opensource/docgrid/domain/document/controller/DocumentQueryController.javasrc/main/java/com/opensource/docgrid/domain/document/converter/DocumentSummaryConverter.javasrc/main/java/com/opensource/docgrid/domain/document/dto/response/DocumentSummaryResponse.javasrc/main/java/com/opensource/docgrid/domain/document/repository/DocumentRepository.javasrc/main/java/com/opensource/docgrid/domain/document/service/query/DocumentQueryService.javasrc/main/java/com/opensource/docgrid/domain/search/service/query/AccessibleDocumentQueryService.javasrc/test/java/com/opensource/docgrid/domain/document/repository/DocumentReadableIdsRepositoryTest.javasrc/test/java/com/opensource/docgrid/domain/document/service/query/DocumentQueryServiceTest.javasrc/test/java/com/opensource/docgrid/domain/embedding/integration/DocumentIndexingCompletionIntegrationTest.javasrc/test/java/com/opensource/docgrid/domain/search/service/query/AccessibleDocumentQueryServiceTest.java
closes #153
배경
문서 도메인에는 업로드와 단건 인덱싱 상태 조회만 있고 사용자가 읽을 수 있는 문서 목록을 반환하는 API가 없었다. 업로드 후
documentId를 따로 기억하지 않으면 문서를 다시 찾아갈 방법이 없어 문서 화면을 구성할 수 없었다.변경 내용
권한 쿼리를 복제하지 않고 상태 조건만 파라미터화
권한 판정은 검색 pre-filter가 쓰는
DocumentRepository의 UNION 쿼리에 이미 있다(OWNER / PUBLIC / USER 캐시 / ROLE live / DEPARTMENT live). 다만d.status = 'INDEXED'가 하드코딩되어 있어 인덱싱 중이거나 실패한 문서를 목록에 노출할 수 없었다.UNION 7개 브랜치짜리 쿼리를 목록용으로 복사하면 권한 정책이 두 벌이 되어 접근 경로가 추가될 때 한쪽만 고치는 사고가 나기 쉽다.
d.status IN (:statuses)로 바꾸고 호출부에서 상태를 넘기는 방식으로 한 벌을 유지했다.AccessibleDocumentQueryService(검색)INDEXED— 기존 동작 그대로DocumentQueryService(목록)DELETED제외 전체, 또는 요청한 단일 상태권한 판정과 페이징 분리
읽을 수 있는 문서 ID를 먼저 구하고, 그 집합에 대해 JPQL 페이지 쿼리로 정렬·페이징만 수행한다.
Document.currentVersion이LAZY라 버전 번호·상태를 응답에 담기 위해LEFT JOIN FETCH하고countQuery를 분리했다. 읽을 수 있는 문서가 없으면 페이지 쿼리를 실행하지 않고 빈 응답을 반환한다.정렬은
createdAt DESC, id DESC고정이며 외부sort입력을 받지 않는다.API
statusDELETED제외 전체page0size20응답은
PageResponse<DocumentSummaryResponse>. 인덱싱이 끝난 버전이 없으면currentVersionNo·currentVersionStatus는null이다.에러 케이스는 설계 문서에 정리했다. 읽을 수 있는 문서가 없는 것은 정상 상태이므로 404가 아니라 빈 페이지를 반환하고, 권한 없는 문서는 존재 여부를 노출하지 않고 조용히 제외한다.
테스트
DocumentQueryServiceTest4개(정상 페이지 / 권한 없을 때 문서 조회 스킵 / status 미지정 시 DELETED 제외 / status 지정),DocumentReadableIdsRepositoryTest4개(@DataJpaTest로 실제 UNION 쿼리 검증) 추가.상태 파라미터화가 검색 경로를 바꾸지 않았는지
AccessibleDocumentQueryServiceTest,SearchFacadeTest,DocumentIndexingCompletionIntegrationTest로 확인했다../gradlew build— 760개 테스트 전부 통과.