diff --git a/설계/db/개념적 설계.md b/설계/db/설계.md
similarity index 94%
rename from 설계/db/개념적 설계.md
rename to 설계/db/설계.md
index a7accaa..3a10722 100644
--- a/설계/db/개념적 설계.md
+++ b/설계/db/설계.md
@@ -1,24 +1,4 @@
-# Cloud# — 데이터베이스 개념적 설계서
-
-> 본 문서는 Cloud# 서비스의 핵심 엔티티에 대한 개념적 DB 설계를 정의한다.
-> 물리적 DDL이 아닌 **엔티티의 역할, 컬럼 의미, 설계 의도, 제약 조건**을 중심으로 기술한다.
-
----
-
-## 목차
-
-1. [엔티티 목록](#1. 엔티티 목록)
-2. [ER 다이어그램](#2-er-다이어그램)
-3. [엔티티 상세 설계](#3-엔티티-상세-설계)
- - [3.1 User](#31-user)
- - [3.2 Folder](#32-folder)
- - [3.3 FileItem](#33-fileitem)
- - [3.4 UploadSession](#34-uploadsession)
-4. [관계 정의](#4-관계-정의)
-5. [제약 조건 요약](#5-제약-조건-요약)
-6. [설계 결정 사항 (Decision Log)](#6-설계-결정-사항-decision-log)
-
----
+# Cloud# — 데이터베이스 설계서
## 1. 엔티티 목록
diff --git a/설계/flows/다운로드 흐름.md b/설계/flows/다운로드 흐름.md
deleted file mode 100644
index 311e7a5..0000000
--- a/설계/flows/다운로드 흐름.md
+++ /dev/null
@@ -1,136 +0,0 @@
-
-![[file_download_flow.svg]]
-## 단계별 상세
-
-### 1. 사용자 인증 / 공유 링크 접근
-
-요청의 진입점으로, 두 가지 접근 경로를 처리한다.
-
-- **인증 사용자**: `Authorization` 헤더의 JWT Bearer 토큰을 파싱 및 검증
-- **공유 링크**: URL 쿼리 파라미터의 `share_token`을 파싱
-
-두 경로 모두 이후 단계에서 동일한 인터페이스로 처리될 수 있도록 내부적으로 정규화한다.
-
----
-
-### 2. 파일 권한 / 공유 상태 검증
-
-DB를 조회하여 요청자가 해당 파일에 접근할 수 있는지 확인한다.
-
-- 인증 사용자: 파일의 소유자 또는 공유 대상 여부 확인
-- 공유 링크: 링크의 활성화 여부, 만료 여부, 비밀번호 보호 여부 확인
-- 권한이 없으면 즉시 **`403 Forbidden`** 반환 (이후 단계로 진행하지 않음)
-
-##### Risk
-권한이 없는 경우 `403`, 파일이 없는 경우 `404`를 분리 반환한다. 이 차이를 이용하면 공격자가 파일 ID를 열거하여 존재하는 파일 목록을 추론할 수 있다.
-
----
-
-### 3. 다운로드 토큰 발급
-
-실제 스트리밍 요청에 사용할 단명(short-lived) 토큰을 생성한다.
-
-- TTL은 **약 60초** 권장 (재사용·탈취 위험 최소화)
-- 토큰에는 파일 ID, 요청자 식별자, 만료 시각을 포함
-- HMAC 서명 또는 JWT로 위변조 방지
-
-```
-# Redis 기반 원자적 무효화
-result = REDIS.SET(f"dl_token:{token}", "used", NX=True, EX=60)
-# NX: key가 없을 때만 SET → 원자적 보장
-# result가 None이면 이미 사용된 토큰 → 401
-
-```
-
-> API 서버와 스트리밍 서버를 분리 운영할 때 특히 유용하다.
-> 클라이언트는 이 토큰을 받아 스트리밍 엔드포인트에 직접 요청한다.
-
----
-
-### 4. 다운로드 토큰으로 파일 스트림 요청
-
-클라이언트가 발급받은 토큰으로 스트리밍 엔드포인트에 요청한다.
-
-```
-GET /files/stream?token={download_token}
-Range: bytes=0-1048575 (선택, Range 요청 시)
-```
-
-- 토큰이 만료되었거나 유효하지 않으면 **`401 Unauthorized`** 반환
-- 토큰은 1회 사용 후 무효화하는 것을 권장 (재생 공격 방어)
-
-#### Risk
-- "토큰은 1회 사용 후 무효화하는 것을 권장"이라고만 기술되어 있으나, 동시에 같은 토큰으로 2개 이상의 요청이 들어오는 경우의 원자적 무효화 전략이 없다.
-- 토큰을 1회 사용 후 무효화하면, 대용량 파일의 Range 기반 이어받기(resume)가 불가능해진다. 네트워크 끊김 후 클라이언트가 `Range: bytes=10485760-`으로 재요청하면, 이미 무효화된 토큰이므로 `401`이 반환된다.
--
----
-
-### 5. storage_key → 로컬 FS 경로 resolve
-
-`storage_key`를 실제 파일 시스템 경로로 변환한다.
-
-- 키-경로 맵(DB 또는 설정 파일)을 조회하여 절대 경로 획득
-- **Path traversal 방어 필수**: `realpath()` 등으로 경로를 정규화하고, 허용된 루트 디렉터리(`/data/uploads/` 등) 내에 있는지 반드시 검증
-- 파일이 존재하지 않으면 **`404 Not Found`** 반환
-
----
-
-### 6. 파일 스트림 응답 (Range 지원 권장)
-
-파일을 청크 단위로 스트리밍하여 응답한다.
-
-**응답 헤더:**
-
-|헤더|값|
-|---|---|
-|`Content-Type`|파일 MIME 타입|
-|`Content-Disposition`|`attachment; filename="파일명"`|
-|`Accept-Ranges`|`bytes`|
-|`Content-Length`|파일 크기 (전체 또는 청크)|
-
-**Range 지원 시 동작:**
-
-- `Range` 헤더가 있으면 **`206 Partial Content`** 로 응답
-- `Range` 헤더가 없으면 **`200 OK`** 로 전체 파일 응답
-- Range를 지원하면 대용량 파일 resume, 미디어 스트리밍, 멀티파트 다운로드 모두 대응 가능
-
-
-#### Risk
-- 응답 헤더에 `Content-Type: 파일 MIME 타입`이라고만 되어 있고, 이 값을 **어디서 가져오는지** 명시되어 있지 않다. 업로드 시 클라이언트가 보낸 값을 DB에 저장하고 그대로 사용하는 경우, 공격자가 `text/html`이나 `image/svg+xml`로 위장한 악성 파일을 업로드할 수 있다.
-- 로컬 FS에서 직접 스트리밍하므로 API/스트리밍 서버의 egress 대역폭이 병목이 된다. 동시 다운로드 수 제한, 사용자별 대역폭 제한, 전체 서버 대역폭 보호 정책이 없다.
-
-
----
-
-### 7. 로그 기록 / 카운트 증가
-
-다운로드 완료 후 감사(Audit) 로그를 기록하고, 공유 링크의 경우 카운트를 증가시킨다.
-
-- **로그 항목**: 파일 ID, 요청자, IP, User-Agent, 다운로드 시각, 파일 크기
-- **카운트 증가**: 공유 링크에만 적용 (`download_count++`)
-- **타이밍 주의**: 스트림이 **완전히 전송된 이후**에 카운트를 올린다. 요청 시점에 올리면 중단된 다운로드도 집계된다.
-
----
-
-## 에러 응답 정리
-
-|상황|HTTP 상태 코드|
-|---|---|
-|인증 실패 / 토큰 만료|`401 Unauthorized`|
-|권한 없음|`403 Forbidden`|
-|파일 없음|`404 Not Found`|
-|Range 범위 초과|`416 Range Not Satisfiable`|
-|정상 전체 응답|`200 OK`|
-|정상 부분 응답 (Range)|`206 Partial Content`|
-
----
-
-## 보안 체크리스트
-
-- [ ] JWT / share_token 서명 검증
-- [ ] 파일 접근 권한 DB 조회 (캐시 사용 시 TTL 주의)
-- [ ] 다운로드 토큰 단명 발급 (TTL ≤ 60s)
-- [ ] 토큰 1회 사용 후 무효화
-- [ ] Path traversal 방어 (`realpath` + 루트 경로 검증)
-- [ ] `Content-Disposition: attachment` 헤더 명시
-- [ ] 다운로드 완료 후 감사 로그 기록
\ No newline at end of file
diff --git a/설계/img/download_flowchart_part1.svg b/설계/img/download_flowchart_part1.svg
new file mode 100644
index 0000000..9a84967
--- /dev/null
+++ b/설계/img/download_flowchart_part1.svg
@@ -0,0 +1,127 @@
+
\ No newline at end of file
diff --git a/설계/img/download_flowchart_part2.svg b/설계/img/download_flowchart_part2.svg
new file mode 100644
index 0000000..b67aa05
--- /dev/null
+++ b/설계/img/download_flowchart_part2.svg
@@ -0,0 +1,125 @@
+
\ No newline at end of file
diff --git a/설계/img/file_download_flow.svg b/설계/img/file_download_flow.svg
deleted file mode 100644
index a564223..0000000
--- a/설계/img/file_download_flow.svg
+++ /dev/null
@@ -1,121 +0,0 @@
-
\ No newline at end of file
diff --git a/설계/img/file_download_sequence_chart.svg b/설계/img/file_download_sequence_chart.svg
new file mode 100644
index 0000000..c56b7ef
--- /dev/null
+++ b/설계/img/file_download_sequence_chart.svg
@@ -0,0 +1,175 @@
+
\ No newline at end of file
diff --git a/설계/pipeline/다운로드 파이프라인.md b/설계/pipeline/다운로드 파이프라인.md
new file mode 100644
index 0000000..5209b9b
--- /dev/null
+++ b/설계/pipeline/다운로드 파이프라인.md
@@ -0,0 +1,284 @@
+## 개요
+
+이 문서는 **인증 사용자**와 **공유 링크 사용자** 모두를 지원하는 파일 다운로드 시스템의 전체 흐름을 정의한다. 핵심 구조는 다음과 같다:
+
+> **권한 검증 → 다운로드 가능 상태 사전 검증 → 다운로드 세션 발급 → 최종 경로 검증 및 스트리밍**
+
+---
+
+## 전체 흐름 (8단계)
+![[download_flowchart_part1.svg]]
+
+![[download_flowchart_part2.svg]]
+---
+
+## 단계별 상세
+
+### 1단계: 사용자 인증 / 공유 링크 접근
+
+**목적:** 요청자의 신원을 확인하고 내부 컨텍스트로 정규화한다.
+
+| 접근 경로 | 인증 방식 | 정규화 결과 |
+|-----------|-----------|-------------|
+| 인증 사용자 | `Authorization` 헤더의 JWT Bearer 토큰 | `subjectType=user`, `subjectId={userId}` |
+| 공유 링크 사용자 | 쿼리 파라미터 또는 별도 엔드포인트의 `share_token` | `subjectType=share_link`, `subjectId={shareLinkId}` |
+
+**에러 처리:**
+
+| 상황 | 응답 |
+|------|------|
+| 인증 토큰 유효하지 않음 / 만료 | `401 Unauthorized` |
+| 공유 토큰 유효하지 않음 | `404 Not Found` |
+
+---
+
+### 2단계: 파일 권한 / 공유 상태 검증
+
+**목적:** DB 조회를 통해 요청자가 해당 파일에 접근할 수 있는지 확인한다.
+
+**검증 항목:**
+
+| 접근 유형 | 검증 내용 |
+| --------- | ---------------------------------------- |
+| 인증 사용자 | 파일 소유자 여부, 공유 대상 여부, 조직/프로젝트/폴더 단위 접근 권한 |
+| 공유 링크 사용자 | 링크 활성화 여부, 만료 여부, 비밀번호 검증, 다운로드 허용 여부 |
+
+**핵심 원칙:** 파일 존재 여부 노출을 최소화하기 위해 아래 경우는 **모두 `404 Not Found`** 로 처리한다.
+
+- 파일이 실제로 존재하지 않음
+- 요청자에게 접근 권한이 없음
+- 공유 링크가 파일에 매핑되지 않음
+- 비활성화된 공유 링크로 접근 시도
+
+> 외부 클라이언트 관점에서 **"접근 불가"와 "존재하지 않음"을 구분할 수 없다.**
+
+---
+
+### 3단계: 파일 메타데이터 및 다운로드 가능 상태 검증
+
+**목적:** 다운로드가 불가능한 파일에 대해 세션을 발급하지 않기 위한 **사전 검증**이다.
+
+**검증 항목:**
+
+- 파일 메타데이터 존재 여부
+- `storage_key` 존재 여부
+- 삭제 / 손상 / 비활성 / 격리 상태 여부
+- 다운로드 정책상 차단 대상 여부
+- (가능 시) 실제 스토리지 객체 존재 여부
+
+**실패 시:** `404 Not Found`
+
+> ⚠️ 이 단계는 사전 검증이다. 세션 발급 후 실제 스트리밍 시점까지 파일 상태가 변할 수 있으므로, **최종 검증은 6단계에서 다시 수행**한다.
+
+---
+
+### 4단계: 다운로드 세션 토큰 발급
+
+**목적:** 실제 파일 스트리밍에 사용할 세션 토큰을 발급한다.
+
+**세션 토큰 속성:**
+
+| 항목 | 값 |
+|------|-----|
+| TTL | 약 5분 |
+| 포함 정보 | 파일 ID, 요청자 식별자(`subjectType`, `subjectId`), 세션 ID, 만료 시각 |
+| 보호 방식 | HMAC 또는 JWT 서명, 필요 시 Redis/DB에 메타데이터 저장 |
+
+**세션이 지원하는 요청 유형:**
+
+- 브라우저의 동일 다운로드 재요청
+- `Range` 기반 이어받기(resume)
+- 네트워크 끊김 후 재시도
+- 일부 브라우저/다운로드 매니저의 병렬 Range 요청
+
+**응답 예시:**
+
+```json
+{
+ "downloadUrl": "/files/stream?sessionToken={download_session_token}",
+ "expiresAt": "2026-03-17T12:00:00Z"
+}
+```
+
+**리스크 및 대응:**
+
+| 리스크 | 대응 |
+|--------|------|
+| TTL이 너무 길면 URL 탈취 악용 가능 | TTL 5분 내외로 제한 |
+| TTL이 너무 짧으면 대용량 파일 재요청 실패 | 세션 TTL 내 반복 요청 허용 |
+| 토큰 유출 | 파일 ID + 요청자 식별자에 바인딩, 선택적 IP/UA 바인딩 |
+
+**추가 보안:**
+
+- 다운로드 세션은 **발급 시점 권한**을 기준으로 TTL 동안 유효
+- Access log 마스킹
+- `Cache-Control: private, no-store`
+
+---
+
+### 5단계: 세션 토큰으로 파일 스트림 요청
+
+**클라이언트 요청 예시:**
+
+```http
+GET /files/stream?sessionToken={download_session_token}
+Range: bytes=0-1048575
+```
+
+**처리 규칙:**
+
+| 상황 | 처리 |
+|------|------|
+| 세션 토큰 만료 / 위변조 | `401 Unauthorized` |
+| 세션이 가리키는 파일과 요청 대상 불일치 | `404 Not Found` |
+| 세션 TTL 내 반복 GET / Range / resume | ✅ 허용 |
+| Multi-range 요청 | ❌ 미지원 (정책에 따라 `416` 또는 전체 응답) |
+
+**설계 원칙:**
+
+> 세션 자체는 짧게 유지하되, 세션이 살아있는 동안은 브라우저 다운로드 모델을 수용한다.
+
+**"1회용 토큰 + 즉시 무효화" 방식을 사용하지 않는 이유:**
+
+브라우저는 다운로드 중 동일 URL로 재요청, Range resume, 병렬 요청, 프록시 중복 요청 등을 발생시킬 수 있어 충돌한다.
+
+---
+
+### 6단계: `storage_key` → 실제 파일 경로 resolve 및 최종 검증
+
+**목적:** 스트림을 열기 직전에 실제 파일 경로를 확정하고 최종 검증을 수행한다.
+
+**처리 절차:**
+
+1. 키-경로 맵(DB 또는 설정) 조회 → 절대 경로 획득
+2. `realpath()` 등으로 경로 정규화
+3. 정규화된 경로가 허용된 루트 디렉터리 내부인지 검증 (예: `/data/uploads/`)
+4. 실제 파일 존재 여부 확인
+5. 파일 open 가능 여부 확인
+
+**보안 요구사항:**
+
+- Path traversal 방어 필수 (`../`, 심볼릭 링크 우회, 상대 경로 등 차단)
+- 허용 루트 외부로 벗어나면 즉시 실패 처리
+
+**에러 처리:**
+
+| 상황 | 응답 | 내부 처리 |
+|------|------|-----------|
+| 실제 파일 미존재 | `404 Not Found` | - |
+| 메타데이터 존재하나 스토리지 파일 누락 | `404 Not Found` | 경고 로그 / 장애 알림 |
+
+---
+
+### 7단계: 파일 스트림 응답
+
+**기본 응답 헤더:**
+
+| 헤더 | 값 |
+|------|-----|
+| `Content-Type` | 서버가 결정한 안전한 MIME 타입 |
+| `Content-Disposition` | `attachment; filename="..."; filename*=UTF-8''...` |
+| `Accept-Ranges` | `bytes` |
+| `Content-Length` | 전체 또는 부분 응답 크기 |
+| `ETag` | 파일 버전 식별자 |
+| `Last-Modified` | 파일 최종 수정 시각 (선택) |
+
+**Range 처리:**
+
+| 조건 | 응답 |
+|------|------|
+| `Range` 헤더 없음 | `200 OK` (전체 파일) |
+| `Range` 헤더 있음 | `206 Partial Content` (부분 응답) |
+| 범위 초과 | `416 Range Not Satisfiable` |
+| `If-Range` 일치 | 부분 응답 |
+| `If-Range` 불일치 | 전체 파일 재전송 또는 정책에 따라 처리 |
+
+**MIME 타입 처리 원칙:**
+
+1. 서버 측 MIME 판별 사용 (클라이언트 업로드 값 불신)
+2. 저장된 메타데이터와 확장자는 보조적으로 사용
+3. 판별 불확실 시 `application/octet-stream`
+4. XSS 가능 타입이라도 `Content-Disposition: attachment`로 강제
+
+**파일명 처리 원칙:**
+
+- 제거/이스케이프 대상: `"`, `\r`, `\n`, `\0`, `/`, `\`
+- 최대 길이 제한 적용
+- 비 ASCII 문자는 `filename*`에 RFC 5987 방식으로 인코딩
+
+```http
+Content-Disposition: attachment; filename="safe_name.ext"; filename*=UTF-8''original%20name.ext
+```
+
+**성능 / 운영 보호 정책:**
+
+- 사용자별 동시 다운로드 수 제한
+- 공유 링크별 rate limit
+- 전체 서버 동시 스트림 수 제한
+- 대용량 파일 전송 속도 제한 (필요 시)
+- `sendfile` / `X-Accel-Redirect` / `X-Sendfile` 활용 검토
+
+### 8단계: 로그 기록 / 카운트 증가
+
+**카운트 증가 정책:**
+
+- **공유 링크 다운로드에만 적용**
+- `download_count`는 **세션 단위 전체 파일 다운로드 완료 시점**에만 증가
+- 운영 분석을 위해 시도 수와 완료 수를 분리 집계 가능
+
+| 카운트 항목 | 증가 시점 | 용도 |
+|------------|-----------|------|
+| `download_attempt_count` | 스트림 요청 시작 시점 | 시도 횟수 추적 |
+| `download_completed_count` | 전체 파일 전송 완료 시점 | 실제 완료 횟수 추적 |
+
+> 분리 집계를 통해 네트워크 중단, 사용자 취소, 실패율 등을 더 정확히 분석할 수 있다.
+
+---
+
+## 에러 응답 정리
+
+| 상황 | HTTP 상태 코드 |
+|------|----------------|
+| 인증 실패 / 인증 토큰 만료 | `401 Unauthorized` |
+| 다운로드 세션 토큰 만료 / 위변조 | `401 Unauthorized` |
+| 파일 없음 또는 권한 없음 | `404 Not Found` |
+| Range 범위 초과 | `416 Range Not Satisfiable` |
+| 정상 전체 응답 | `200 OK` |
+| 정상 부분 응답 (Range) | `206 Partial Content` |
+
+---
+
+## 보안 체크리스트
+
+| # | 항목 | 관련 단계 |
+|---|------|-----------|
+| 1 | JWT / share token 서명 검증 | 1단계 |
+| 2 | 파일 접근 권한 및 공유 상태 검증 | 2단계 |
+| 3 | 권한 없음과 파일 없음은 외부에 동일하게 `404` 처리 | 2단계 |
+| 4 | 파일 메타데이터 및 다운로드 가능 상태 사전 검증 | 3단계 |
+| 5 | 다운로드 세션 토큰 단명 발급 (권장 TTL: 약 5분) | 4단계 |
+| 6 | 세션 토큰은 파일 ID 및 요청자 식별자에 바인딩 | 4단계 |
+| 7 | 세션 TTL 내 GET / Range / resume / 병렬 요청 허용 | 5단계 |
+| 8 | Path traversal 방어 (`realpath` + 루트 경로 검증) | 6단계 |
+| 9 | 실제 스트리밍 직전 파일 존재 및 open 가능 여부 재검증 | 6단계 |
+| 10 | `Content-Disposition: attachment` 강제 | 7단계 |
+| 11 | 파일명 sanitize 및 `filename*` 지원 | 7단계 |
+| 12 | MIME 타입은 서버 기준으로 안전하게 결정 | 7단계 |
+| 13 | `ETag` / `If-Range` 지원 | 7단계 |
+| 14 | 다운로드 로그 및 감사 로그 기록 | 8단계 |
+| 15 | 동시 다운로드 수 / rate limit / 대역폭 보호 정책 적용 | 7~8단계 |
+
+---
+
+## 설계 요약
+
+이 설계는 다음 **6가지 목표**를 동시에 만족한다.
+
+| 목표 | 달성 방법 |
+|------|-----------|
+| **파일 존재 여부 노출 최소화** | 권한 없음 / 파일 없음 모두 `404` 통일 처리 |
+| **권한 검증 강화** | 인증 사용자·공유 링크 모두 다단계 검증 |
+| **불필요한 세션 발급 방지** | 세션 발급 전 다운로드 가능 상태 사전 검증 |
+| **브라우저 호환성 확보** | 세션 TTL 내 GET / Range / resume / 병렬 요청 허용 |
+| **재생 공격 위험 완화** | 단명 세션 토큰 + 파일·요청자 바인딩 |
+| **운영 환경 자원 보호** | 동시 다운로드 제한, rate limit, 대역폭 제어 |
\ No newline at end of file
diff --git a/설계/pipeline/업로드 파이프라인.md b/설계/pipeline/업로드 파이프라인.md
new file mode 100644
index 0000000..cf188b6
--- /dev/null
+++ b/설계/pipeline/업로드 파이프라인.md
@@ -0,0 +1,194 @@
+
+## 1. 업로드 준비 단계
+
+### 1-1. 클라이언트가 업로드 대상 폴더를 선택한다
+
+클라이언트는 업로드 요청 시 아래 정보를 서버에 전달한다.
+
+|항목|설명|
+|---|---|
+|`targetFolderId`|업로드 대상 폴더 식별자|
+|원본 파일명|사용자가 선택한 파일의 이름|
+|파일 크기|바이트 단위 파일 크기|
+|클라이언트 MIME 타입|브라우저가 판단한 MIME 타입 (신뢰 금지)|
+|체크섬 정보|(선택) 무결성 검증용 해시값|
+
+### 1-2. 서버가 사용자 인증 및 권한을 검증한다
+
+- 로그인 여부 확인
+- 대상 폴더에 대한 쓰기 권한 확인
+- 타 사용자 폴더 접근 차단
+
+### 1-3. 서버가 업로드 사전 검증을 수행한다
+
+- 파일명 규칙 검증
+- 허용 최대 크기 검증
+- 확장자 / 업로드 정책 검증
+- 사용자 quota 사전 검사
+- 동일 폴더 내 중복 이름 정책 검사
+
+### 1-4. 서버가 업로드 대상 폴더를 검증한다
+
+- 폴더 존재 여부 확인
+- 삭제 상태 여부 확인
+- 업로드 가능 상태 여부 확인
+
+---
+
+## 2. 업로드 세션 생성 단계
+
+### 2-1. 서버가 `UploadSession`을 생성한다
+
+|항목|내용|
+|---|---|
+|상태|`CREATED`|
+|업로드 대상 폴더 ID|`targetFolderId` 저장|
+|파일 정보|원본 파일명 / 예상 크기 저장|
+|임시 저장 경로|임시 저장 경로 또는 임시 키 발급|
+|tus 매핑|tus 업로드 식별자 매핑 준비|
+
+### 2-2. 서버가 클라이언트에 tus 업로드 정보를 반환한다
+
+- 업로드 URL
+- 업로드 세션 ID
+- 재개에 필요한 메타데이터
+
+---
+
+## 3. 파일 전송 단계
+
+### 3-1. 클라이언트가 tus 프로토콜로 청크 업로드를 시작한다
+
+- 청크 단위 전송
+- 오프셋 검증
+- 중단 시 재개 가능
+- 서버는 진행 상태를 `UploadSession.received_size` 등에 반영
+
+---
+
+## 4. 업로드 완료 확정 단계
+
+### 4-1. 서버가 업로드 완료 이벤트 또는 콜백을 수신한다
+
+- tus 훅 또는 완료 엔드포인트 기반 처리
+- 동일 세션에 대한 중복 완료 요청 방지 필요 (idempotency)
+
+### 4-2. 서버가 임시 업로드 파일을 최종 검증한다
+
+- 실제 파일 크기 검증
+- MIME 타입 서버 측 추정 (클라이언트 값 신뢰 금지)
+- 손상 여부 또는 체크섬 검증 (선택)
+- quota 최종 재검사
+- 위험 파일 정책 검사 (필요 시)
+
+### 4-3. 서버가 최종 저장 경로를 결정하고 파일을 이동한다
+
+- `storage_key` 생성
+- 임시 경로 → 최종 object 경로로 **원자적 이동**
+- 사용자 표시 경로와 실제 디스크 경로는 분리
+
+### 4-4. 서버가 `FileItem` 메타데이터를 생성한다
+
+|필드|설명|
+|---|---|
+|`display_name`|사용자에게 표시되는 파일명|
+|`folder_id`|소속 폴더 ID|
+|`mime_type`|서버 측 추정 MIME 타입|
+|`size_bytes`|실제 파일 크기|
+|`storage_key`|내부 저장 경로 식별자|
+|`preview_status`|초기값 `PENDING`|
+|사용자 사용량|`storage_used_bytes` 갱신 (필요 시)|
+
+### 4-5. 서버가 `UploadSession` 상태를 완료로 변경한다
+
+|항목|내용|
+|---|---|
+|상태|`COMPLETED`|
+|완료 시각|기록|
+|`file_id`|최종 생성된 `FileItem`과 연결|
+
+---
+
+## 5. 후처리 단계
+
+### 5-1. 서버가 후처리 작업을 비동기로 등록한다
+
+|작업|유형|
+|---|---|
+|이미지 썸네일 생성|동기 또는 큐 기반|
+|텍스트 / PDF 미리보기 캐시 생성|큐 기반|
+|바이러스 스캔|후속 비동기|
+|검색 인덱싱|후속 비동기|
+|감사 로그 기록|동기 또는 큐 기반|
+
+### 5-2. 서버가 클라이언트에 업로드 완료 응답을 반환한다
+
+- 생성된 `FileItem` 정보
+- 목록 갱신에 필요한 최소 메타데이터
+- 프론트엔드는 현재 폴더 목록을 갱신
+
+---
+
+## 6. 보강 포인트
+
+### A. 상태 전이
+
+`UploadSession`의 상태는 아래와 같이 전이한다.
+
+```
+CREATED → UPLOADING → COMPLETED
+ ↘ FAILED
+ ↘ ABORTED
+ ↘ EXPIRED (선택)
+```
+
+|상태|설명|
+|---|---|
+|`CREATED`|세션 생성 완료, 아직 전송 시작 전|
+|`UPLOADING`|청크 전송 진행 중|
+|`COMPLETED`|모든 검증 및 저장 완료|
+|`FAILED`|완료 후 검증 실패 또는 저장 오류|
+|`ABORTED`|클라이언트 또는 서버 측 명시적 중단|
+|`EXPIRED`|세션 TTL 만료 (선택 구현)|
+
+---
+
+### B. 실패 처리
+
+|시나리오|처리 방식|
+|---|---|
+|업로드 중 네트워크 끊김|세션 유지, tus 재개 허용|
+|완료 후 검증 실패|`UploadSession = FAILED`, 임시 파일 삭제|
+|quota 초과|최종 확정 거부, 파일 반영 금지|
+|파일 이동 성공 후 DB 저장 실패|고아 파일 정리 배치 또는 보상 로직 필요|
+|완료 콜백 중복 호출|idempotency 보장 필요|
+
+---
+
+### C. 트랜잭션 경계
+
+아래 순서는 원자성 보장이 중요하며, 메타데이터 반영은 하나의 트랜잭션으로 묶는 것을 권장한다. 파일 이동은 트랜잭션 외부에서 수행되므로 전후 보상 로직이 필요하다.
+
+```
+1. 임시 파일 검증
+2. 최종 파일 이동 ← 트랜잭션 외부 (파일시스템 작업)
+──────────────────────────── 트랜잭션 시작
+3. FileItem 생성
+4. 사용자 사용량 갱신
+5. UploadSession 완료 처리
+──────────────────────────── 트랜잭션 커밋
+```
+
+> **주의:** 파일 이동 성공 후 트랜잭션 실패 시 고아 파일이 발생할 수 있으므로, 정기 정리 배치 또는 보상 트랜잭션을 설계해야 한다.
+
+---
+
+### D. 보안 포인트
+
+|항목|내용|
+|---|---|
+|클라이언트 MIME 신뢰 금지|서버에서 직접 MIME 타입을 추정하여 사용|
+|사용자 입력 파일명 검증|특수문자, 예약어, 인코딩 우회 등 검사|
+|Path Traversal 방지|`../` 등 경로 탈출 시도 차단|
+|내부 실제 경로 외부 노출 금지|`storage_key`와 응답 경로 분리|
+|최종 저장 경로 격리|사용자 표시 폴더 구조와 실제 디스크 경로 분리|