diff --git a/dev/guides/file-storage-guide.md b/dev/guides/file-storage-guide.md index 0b034fa..7f20e44 100644 --- a/dev/guides/file-storage-guide.md +++ b/dev/guides/file-storage-guide.md @@ -1045,17 +1045,47 @@ export default function StorageQuotaBar({ used, limit }) { - 대시보드 추가 - 차트 라이브러리 (Chart.js, Recharts) -### Phase 3 (1년 후) -1. **Object Storage 전환** - - AWS S3 / Naver Cloud Object Storage - - Laravel Flysystem 드라이버 변경 - - 기존 파일 마이그레이션 +### Phase 3 — ✅ 완료 (2026-03-20) +1. **Object Storage → Cloudflare R2** 전환 완료 +2. **이미지 서빙 → R2 Presigned URL** 방식 적용 +3. **CDN** — 미적용 (Cloudflare DNS 등록 필요) -2. **CDN 연동** - - CloudFront / CloudFlare - - 이미지 썸네일 자동 생성 +--- -3. **고급 기능** - - 파일 버전 관리 - - 협업 편집 - - 파일 잠금 +## ☁️ R2 파일 서빙 정책 (2026-03-20~) + +### 핵심 원칙 + +- **파일 저장**: Cloudflare R2 (S3 호환) +- **이미지 서빙**: API Resource에서 `image_url` (presigned URL) 반환 → 브라우저가 R2 직접 로드 +- **프록시 금지**: MNG/React에서 API를 경유한 바이너리 스트리밍 방식 사용하지 않음 +- **공개 라우트 금지**: 인증 없는 파일 접근 라우트 생성 금지 + +### 파일 접근 방식 + +``` +Browser → R2 presigned URL 직접 로드 (1홉) +``` + +### API Resource 규칙 + +`image_file_id`를 반환하는 모든 API Resource는 `image_url`도 함께 반환한다. + +- `File::presignedUrl()` 메서드 사용 (30분 유효) +- `temporaryUrl()`은 로컬 서명 생성만 수행 (R2 네트워크 호출 없음) + +### 소비자(MNG, React) 규칙 + +- API 응답의 `image_url` 필드를 ``에 직접 사용 +- `image_url`이 없으면 `route('files.view', $id)` fallback (MNG FileViewController redirect) + +### 보안 + +- 서명 기반 접근: 30분 만료, 만료 후 403 +- 테넌트 격리: BelongsToTenant 스코프 적용 후 URL 발급 +- 인증 없는 파일 공개 라우트 금지 + +### 향후 개선 + +- **R2 Custom Domain**: 도메인을 Cloudflare DNS에 등록하면 CDN 캐시 적용 가능 (10~30ms) +- **확대 적용**: 품목, 문서 등 `image_file_id`가 있는 모든 API Resource에 `image_url` 추가