Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .claude/rules/deploy.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,20 @@ globs: "docker-compose*.yml, deploy.sh, .github/workflows/**, Dockerfile"
- 마이그레이션 파일은 한 번 적용 후 수정 금지 — 새 파일 추가
- `spring.jpa.hibernate.ddl-auto=validate` 유지 (Flyway가 스키마 관리)

## 문서 관리

### PR 설계 문서
- 위치: `docs/`
- 파일명: `{github아이디}-#{이슈번호}-{설명}.md` (예: `chelung-#29-collection-management.md`)
- 기능 구현 PR과 함께 작성 — 설계 배경, API 명세, 구현 구조, 주요 설계 결정 포함

### 테스트 결과 문서
- 위치: `docs/test-results/`
- 파일명: 설계 문서와 동일한 이름 사용
- 기능 머지 후 **별도 테스트 이슈**로 분리해서 작성
- Swagger 수동 테스트(시나리오별 결과, DB 검증)와 `./gradlew test` 자동 테스트 결과를 **하나의 문서**에 통합 작성
- 섹션 구성 참고: 테스트 목적 → 환경/제약 → 테스트 데이터 → 시나리오별 결과 → 자동 테스트 결과 → 최종 결론

## 빌드
```bash
./gradlew build -x test # CI용 (테스트 제외)
Expand Down
5 changes: 5 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ src/main/java/com/opensource/docgrid/
│ └── response/
├── converter/ # Entity ↔ DTO 변환
└── enums/

docs/
├── pr-{번호}-{설명}.md # PR 상세 설계 문서
└── test-results/
└── pr-{번호}-{설명}.md # 테스트 결과 문서 (Swagger 수동 + 자동 테스트 통합)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
```

## 주요 명령어
Expand Down
245 changes: 245 additions & 0 deletions docs/chelung-#16-collection-crud.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
# Issue #16 컬렉션 기본 CRUD 설계

## 1. 목적

문서를 그룹으로 묶어 관리하는 컬렉션(폴더/워크스페이스) 단위를 도입한다.

```text
컬렉션 생성
컬렉션 단건 조회
컬렉션에 문서 추가
```

기본 권한 단위는 컬렉션 단위(`collection_permissions`)로 부여한다. 문서 단위 권한(`document_permissions`)은 예외 케이스에만 최소한으로 사용한다.

```text
브랜치명: feature/16
```

---

## 2. 핵심 용어

### DocumentCollection

문서를 그룹화하는 논리 단위다. 폴더, 워크스페이스, 프로젝트 등 다양한 맥락에서 사용할 수 있다.

```text
java.util.Collection과의 이름 충돌을 피하기 위해 클래스명은 DocumentCollection으로 명명
테이블명은 collections
```

### CollectionDocument

컬렉션과 문서의 N:M 관계를 해소하는 중간 엔티티다.

```text
같은 문서가 여러 컬렉션에 속할 수 있다.
하나의 컬렉션에 여러 문서가 속할 수 있다.
```

---

## 3. 컬렉션 구조

### 계층 구조 (선택)

컬렉션은 다른 컬렉션을 상위로 가질 수 있다. 최상위 컬렉션은 `parent_collection_id`가 null이다.

```text
워크스페이스 (최상위, parent = null)
├─ 설계 문서 컬렉션
│ ├─ 시스템 아키텍처.md
│ └─ DB 설계.md
└─ 회의록 컬렉션
└─ 주간 회의록.md
```

### Visibility

```text
PUBLIC — 인증된 모든 사용자가 읽기 가능
PRIVATE — 권한이 있는 사용자만 접근 가능 (기본값)
```

`visibility` 미입력 시 `PRIVATE`으로 생성된다.

### Status

```text
ACTIVE — 정상 사용 중
DELETED — soft delete 상태 (deleted_at 설정)
```

---

## 4. API 계약

### 컬렉션 생성

```http
POST /collections
Authorization: Bearer {token}
Content-Type: application/json
```

요청 필드:

| 필드 | 필수 | 설명 |
|---|---|---|
| `name` | 필수 | 컬렉션 이름 |
| `description` | 선택 | 컬렉션 설명 |
| `visibility` | 선택 | `PUBLIC` / `PRIVATE` (기본값: `PRIVATE`) |
| `parentCollectionId` | 선택 | 상위 컬렉션 ID (없으면 최상위) |

성공 응답 `201 Created`:

```json
{
"id": 3,
"name": "설계 문서",
"description": "설계 관련 문서 모음",
"visibility": "PRIVATE",
"status": "ACTIVE",
"ownerId": 1
}
```

### 컬렉션 단건 조회

```http
GET /collections/{collectionId}
Authorization: Bearer {token}
```

성공 응답 `200 OK`:

```json
{
"id": 3,
"name": "설계 문서",
"description": "설계 관련 문서 모음",
"visibility": "PRIVATE",
"status": "ACTIVE",
"ownerId": 1
}
```

### 컬렉션에 문서 추가

```http
POST /collections/{collectionId}/documents
Authorization: Bearer {token}
Content-Type: application/json
```

요청 필드:

| 필드 | 필수 | 설명 |
|---|---|---|
| `documentId` | 필수 | 추가할 문서 ID |

성공 응답 `201 Created`:

```json
{
"id": 10,
"collectionId": 3,
"documentId": 5,
"addedAt": "2025-07-01T10:00:00"
}
```

같은 컬렉션에 같은 문서를 이미 추가한 경우 `409 Conflict`를 반환한다.

---

## 5. 구현 구조

```text
Controller
- CollectionController
- POST /collections
- GET /collections/{collectionId}
- POST /collections/{collectionId}/documents

Service
- CollectionCommandService
- createCollection(userId, request)
- addDocument(collectionId, userId, request)
- CollectionQueryService
- getCollection(collectionId)

Repository
- CollectionRepository
- CollectionDocumentRepository
- existsByCollectionIdAndDocumentId(collectionId, documentId)

Entity
- DocumentCollection
- CollectionDocument

DTO
- CreateCollectionRequest
- AddDocumentRequest
- CollectionResponse
- CollectionDocumentResponse

Converter
- CollectionConverter
```

---

## 6. 처리 흐름

### 컬렉션 생성

```text
요청 사용자 인증
parentCollectionId가 있으면 상위 컬렉션 존재 확인
visibility 미입력이면 PRIVATE 설정
DocumentCollection 생성 (status = ACTIVE)
201 Created 반환
```

### 문서 추가

```text
컬렉션 존재 확인
컬렉션 쓰기 권한 확인 (canWriteCollection)
문서 존재 확인
이미 추가된 문서인지 확인 → 409
CollectionDocument 생성
201 Created 반환
```

---

## 7. 오류 응답

| 상황 | HTTP | 오류 코드 |
|---|---:|---|
| 컬렉션 없음 | 404 | `COLLECTION_NOT_FOUND` |
| 문서 없음 | 404 | `DOCUMENT_NOT_FOUND` |
| 쓰기 권한 없음 | 403 | `PERMISSION_DENIED` |
| 이미 추가된 문서 | 409 | `COLLECTION_DOCUMENT_ALREADY_EXISTS` |

---

## 8. 완료 기준

- 컬렉션을 생성하면 요청 사용자가 소유자(owner)로 설정된다.
- `visibility` 미입력 시 `PRIVATE`으로 생성된다.
- `parentCollectionId` 입력 시 존재하지 않는 컬렉션이면 404를 반환한다.
- 컬렉션에 같은 문서를 중복 추가하면 409를 반환한다.
- 컬렉션 쓰기 권한이 없는 사용자가 문서를 추가하면 403을 반환한다.
Loading