프로젝트에 참여할 멤버를 추가하고 역할을 할당하는 마지막 단계입니다.
## 멤버 추가
워크스페이스 멤버 중 프로젝트에 참여할 사용자를 선택하여 추가합니다.
## 역할 할당
추가된 각 멤버에게 역할을 할당합니다.
| 역할 | 설명 |
|------|------|
| **리더(Leader)** | 프로젝트를 총괄하는 리더 역할 |
| **매니저(Manager)** | 프로젝트 운영을 관리하는 매니저 역할 |
:::note[역할 변경]
프로젝트 생성 후에도 멤버의 역할을 변경하거나 새로운 멤버를 추가할 수 있습니다.
:::
## 프로젝트 생성
모든 설정이 완료되면 **"생성"** 버튼을 클릭하여 프로젝트를 생성합니다.
```mermaid
graph LR
A["생성 클릭"] --> B{결과}
B -->|성공| C["태스크 관리 화면으로 이동"]
B -->|실패| D["오류 단계로 이동 + 오류 메시지 표시"]
style C fill:#ecfdf5,stroke:#10b981
style D fill:#fef2f2,stroke:#ef4444
```
:::caution[생성 실패 시]
서버 오류가 발생하면 오류가 발생한 단계로 자동 이동하며, 해당 필드에 오류 메시지가 표시됩니다. 오류를 수정한 후 다시 생성을 시도하세요.
:::
## API 흐름
5단계는 다른 단계와 달리 **Draft 임시 저장이 아닌 최종 프로젝트 생성**을 수행합니다. "생성" 버튼을 클릭하면 아래 순서로 API가 호출됩니다.
### 1. 단계별 데이터 검증 — `POST /projects/`
멤버-역할 데이터를 `phase: 5`와 함께 전송하여 검증합니다.
**Request**
```json
POST /projects/
{
"phase": 5,
"member_roles": [
{ "member": 12, "role": "leader" },
{ "member": 34, "role": "manager" },
{ "member": 56, "role": "manager" }
]
}
```
| 필드 | 타입 | 필수 | 설명 |
|------|------|------|------|
| `phase` | `integer` | 필수 | 현재 단계 번호 (`5`) |
| `member_roles` | `array` | 필수 | 멤버-역할 매핑 목록 |
| `member_roles[].member` | `integer` | 필수 | 워크스페이스 멤버 ID |
| `member_roles[].role` | `string` | 필수 | 할당할 역할 코드 |
:::note[빈 멤버 필터링]
멤버가 선택되지 않은 행(`member`가 falsy인 항목)은 자동으로 필터링되어 전송되지 않습니다.
:::
**Response (201 Created)**
```json
{
"member_roles": [
{ "member": 12, "role": "leader" },
{ "member": 34, "role": "manager" }
]
}
```
### 2. 기본 역할 목록 조회 — `GET /projects/default_roles/`
멤버 설정 화면 진입 시 프로젝트에서 사용 가능한 기본 역할 목록을 조회합니다.
**Request**
```
GET /projects/default_roles/
```
**Response (200 OK)**
```json
[
{ "id": 1, "display_name": "리더", "code": "leader" },
{ "id": 2, "display_name": "매니저", "code": "manager" }
]
```
### 3. 최종 프로젝트 생성 — `POST /projects/`
5단계 검증이 성공하면 Draft에서 1~4단계의 모든 누적 데이터를 조회한 뒤, 5단계 데이터와 병합하여 **`phase` 필드 없이** 최종 프로젝트 생성 API를 호출합니다.
**Request**
```json
POST /projects/
{
"title": "자동차 객체 검출 프로젝트",
"description": "COCO 데이터셋을 사용한 자동차 객체 검출 어노테이션",
"category": "image",
"access_level": "public",
"caution": "sensitive_content",
"image": "https://storage.example.com/thumbnails/abc123.png",
"data_collection": 1,
"configuration": {
"schema_type": "dm_schema",
"classification": { ... }
},
"can_discard": false,
"count_max_assignments_per_task": 3,
"project_plugins": [],
"member_roles": [
{ "member": 12, "role": "leader" },
{ "member": 34, "role": "manager" }
]
}
```
:::tip[phase 없이 전송]
최종 생성 요청에는 `phase` 필드가 포함되지 않습니다. `phase`가 없으면 서버는 단계별 검증이 아닌 실제 프로젝트 생성을 수행합니다.
:::
**Response (201 Created)**
프로젝트가 성공적으로 생성되면 생성된 프로젝트 정보가 반환됩니다.
```json
{
"id": 42,
"title": "자동차 객체 검출 프로젝트",
"description": "COCO 데이터셋을 사용한 자동차 객체 검출 어노테이션",
"category": "image",
"access_level": "public",
"data_collection": 1,
"configuration": { ... },
"can_discard": false,
"count_max_assignments_per_task": 3,
"project_plugins": [],
"member_roles": [...]
}
```
성공 시 반환된 `id`를 사용하여 **태스크 관리 화면** (`/projects/{id}/task/manage/tasks`)으로 자동 이동합니다.
**Error Response (400 Bad Request)**
최종 생성 시 여러 단계에 걸친 오류가 반환될 수 있습니다. 시스템은 첫 번째 오류가 있는 단계로 자동 이동합니다.
```json
{
"title": ["이 필드는 필수 항목입니다."],
"data_collection": ["유효하지 않은 pk \"999\" - 객체가 존재하지 않습니다."],
"configuration": ["유효하지 않은 설정입니다."]
}
```
### 전체 API 흐름 요약
```mermaid
graph TD
A["생성 클릭"] --> B["POST /projects/ (phase: 5)\n멤버-역할 검증"]
B -->|성공| C["GET /drafts/CREATE/\n1~4단계 Draft 조회"]
C --> D["POST /projects/ (phase 없음)\nDraft + 5단계 병합하여 최종 생성"]
D -->|201 Created| E["태스크 관리 화면으로 이동\n/projects/{id}/task/manage/tasks"]
D -->|400 Error| F["오류 발생 단계로 이동\n필드별 오류 메시지 표시"]
B -->|실패| G["5단계에서 오류 표시"]
style E fill:#ecfdf5,stroke:#10b981
style F fill:#fef2f2,stroke:#ef4444
style G fill:#fef2f2,stroke:#ef4444
```