Cloudflare R2로 이미지 서버 만들기
핵심 판단
Cloudflare R2는 AWS S3와 비슷한 객체 스토리지이면서 인터넷 egress 비용이 없는 것이 강점입니다.
- 아래 목차에서 필요한 절차만 골라 읽으면 됩니다.
Cloudflare R2를 헤르메스 홈페이지의 이미지 서버, 첨부 파일 서버, AI 산출물 저장소로 쓰는 방법 — 가격, 무료 한도, egress 무료의 의미, 커스텀 도메인, 캐시, presigned URL, 보안, Hermes 자동화 운영까지 상세 정리
이 글의 결론부터 — R2는 헤르메스 홈페이지 파일 서버로 좋은가
결론부터 말하면, Cloudflare R2는 헤르메스 홈페이지의 이미지 서버, 첨부 파일 서버, 다운로드 파일 저장소로 매우 좋은 선택지입니다. 특히 블로그형 사이트, AI 에이전트 운영 문서, 튜토리얼 이미지, 스크린샷, 생성 이미지, PDF, 음성 파일, 작은 동영상 파일처럼 읽기 트래픽이 많고 파일 자체는 정적인 경우에 잘 맞습니다.
다만 표현을 정확히 해야 합니다. R2는 흔히 "무제한 트래픽"이라고 불리지만, 이것은 인터넷 egress 요금이 무료라는 뜻에 가깝습니다. 저장 용량, 요청 수, Infrequent Access 검색 처리비 같은 비용은 여전히 존재합니다. 그러므로 "트래픽 비용 때문에 갑자기 수십만 원이 나오는 구조를 피하고 싶다"에는 강하지만, "아무 사용량이나 전부 무료"는 아닙니다.
헤르메스 홈페이지 기준으로 추천하는 구조는 다음과 같습니다.
| 용도 | 추천 방식 | 이유 |
|---|---|---|
| 공개 이미지 | R2 + Custom Domain + Cloudflare Cache | 이미지 트래픽 egress 부담이 작고 CDN 캐시를 활용하기 좋음 |
| 공개 다운로드 파일 | R2 + Custom Domain | PDF, zip, 자료 파일 배포에 적합 |
| 비공개 첨부 파일 | R2 private bucket + Worker 또는 presigned URL | 원본 버킷을 숨기고 권한을 통제 가능 |
| 사용자가 올리는 이미지 | 서버에서 presigned URL 발급 후 브라우저가 직접 업로드 | Next.js 서버 부하와 업로드 병목 감소 |
| AI가 생성하는 결과물 | Hermes가 파일 생성 후 R2 업로드, URL만 사이트에 기록 | 24시간 자동 콘텐츠 운영에 적합 |
즉 헤르메스 홈페이지를 장기 운영한다면, Vercel이나 앱 서버에는 코드와 페이지 렌더링만 맡기고, 이미지와 파일은 R2로 분리하는 것이 안전합니다. 이렇게 하면 사이트가 커져도 서버 디스크, 배포 용량, 이미지 트래픽, 백업 정책을 훨씬 단순하게 관리할 수 있습니다.
Cloudflare R2를 한 문장으로 이해하기
Cloudflare R2는 AWS S3와 비슷한 객체 스토리지인데, Cloudflare 네트워크와 붙어 있고 인터넷 egress 비용이 없는 파일 저장소입니다.
객체 스토리지는 폴더처럼 보이지만 실제로는 key와 object의 조합입니다. 예를 들어 다음과 같은 주소를 생각하면 됩니다.
| 개념 | 예시 |
|---|---|
| 버킷 | hermes-assets |
| 객체 key | images/posts/r2-guide/hero.webp |
| 공개 URL | https://assets.example.com/images/posts/r2-guide/hero.webp |
| 원본 API endpoint | https://ACCOUNT_ID.r2.cloudflarestorage.com |
일반 웹 서버의 파일 시스템과 다른 점은, 디렉터리에 파일을 저장하는 느낌이 아니라 이름표가 붙은 파일 덩어리를 저장하고 HTTP 또는 S3 API로 꺼내 쓰는 구조라는 점입니다. 그래서 이미지, 문서, 백업 파일, 동영상 조각, 데이터셋, AI 결과물처럼 "한 번 저장한 뒤 여러 번 읽는 파일"에 적합합니다.
R2의 핵심 포인트는 네 가지입니다.
| 핵심 | 설명 |
|---|---|
| S3 호환 | boto3, AWS SDK, rclone, s3cmd 같은 기존 도구를 상당 부분 그대로 사용 가능 |
| egress 무료 | 인터넷으로 나가는 데이터 전송 자체에 별도 egress 비용을 붙이지 않음 |
| Cloudflare CDN 결합 | 커스텀 도메인을 붙이면 캐시, WAF, Access, Bot Management 같은 Cloudflare 기능과 조합 가능 |
| Workers 통합 | Cloudflare Workers에서 R2 버킷을 binding으로 연결해 인증, 리사이징, 프록시, API 서버를 만들 수 있음 |
헤르메스 홈페이지처럼 공개 글과 이미지가 계속 늘어나는 사이트에서는 이 네 가지가 모두 중요합니다. 글은 Next.js가 렌더링하고, 이미지는 R2가 들고 있고, Cloudflare가 전 세계 엣지에서 캐시해 주면 구조가 깔끔해집니다.
무제한 트래픽이라는 말의 정확한 의미
R2를 설명할 때 가장 많이 나오는 말이 "egress fee 없음"입니다. 이것을 한국어로 쉽게 말하면 파일을 밖으로 내보내는 데이터 전송량에 대해 별도 요금을 받지 않는다는 뜻입니다.
예를 들어 2MB짜리 이미지를 100만 번 다운로드하면 단순 계산으로 약 2TB의 트래픽이 생깁니다. AWS S3나 다른 클라우드에서는 이 egress가 비용의 핵심이 될 수 있습니다. R2는 이 부분을 무료로 내세웁니다. 그래서 이미지 서버, 파일 배포 서버, 정적 자산 서버에서 매력적입니다.
하지만 다음 비용은 남아 있습니다.
| 비용 항목 | 남아 있는가 | 설명 |
|---|---|---|
| 저장 용량 | 있음 | GB-month 기준으로 과금 |
| 요청 수 | 있음 | PUT, LIST 같은 Class A와 GET, HEAD 같은 Class B 요청 과금 |
| Infrequent Access 검색 처리비 | 있음 | 낮은 저장 단가 대신 읽을 때 검색 처리비 발생 |
| Workers, Images, Stream 등 다른 Cloudflare 제품 | 별도 | R2와 같이 쓰면 해당 제품의 과금은 별도로 계산 |
| 도메인, WAF, Rate Limiting 등 유료 기능 | 플랜별 | Free/Pro/Business/Enterprise 플랜에 따라 기능 차이 존재 |
그러므로 실전 표현은 이렇게 잡는 것이 정확합니다.
| 흔한 표현 | 더 정확한 표현 |
|---|---|
| R2는 무제한 트래픽 무료다 | R2는 인터넷 egress 요금이 무료이며, 저장 용량과 요청 수는 과금된다 |
| 이미지 서버 비용이 0원이다 | 무료 한도 안에서는 0원일 수 있지만, 요청 수와 저장량이 늘면 비용이 생긴다 |
| r2.dev로 바로 서비스하면 된다 | r2.dev는 개발용이고, 프로덕션은 커스텀 도메인이 권장된다 |
| S3와 완전히 같다 | S3 API 호환이지만 ACL, Object Lock, 일부 SSE/KMS, tagging 등 미지원 기능이 있다 |
헤르메스 홈페이지 운영 관점에서 중요한 것은 "트래픽이 터져도 egress 폭탄을 피할 수 있다"는 점입니다. AI 글이 갑자기 공유되어 이미지 조회가 늘어도 저장소 egress 때문에 큰 비용이 튀는 구조를 피할 수 있습니다.
가격 구조와 무료 한도
2026년 4월 기준 공식 문서의 R2 가격 구조는 다음과 같이 이해하면 됩니다. 실제 과금은 Cloudflare의 최신 가격표와 계정 플랜에 따라 달라질 수 있으므로 운영 전 반드시 대시보드에서 다시 확인해야 합니다.
| 항목 | Standard | Infrequent Access |
|---|---|---|
| 저장 용량 | GB-month당 0.015달러 | GB-month당 0.01달러 |
| Class A 요청 | 100만 건당 4.50달러 | 100만 건당 9.00달러 |
| Class B 요청 | 100만 건당 0.36달러 | 100만 건당 0.90달러 |
| 데이터 검색 처리비 | 없음 | GB당 0.01달러 |
| 인터넷 egress | 무료 | 무료, 단 읽을 때 검색 처리비는 별도 |
| 무료 한도 | 10GB-month, Class A 100만, Class B 1000만 | 무료 한도 적용 안 됨 |
Class A와 Class B는 요청 종류입니다. 아주 단순하게 보면 다음처럼 이해하면 됩니다.
| 요청 분류 | 대표 작업 | 느낌 |
|---|---|---|
| Class A | 객체 업로드, 목록 조회, 멀티파트 생성, 복사, lifecycle transition | 쓰기와 관리 작업 |
| Class B | 객체 다운로드, head 조회 | 읽기 작업 |
이미지 서버에서는 보통 GET 요청이 많으므로 Class B가 중요합니다. 다만 커스텀 도메인과 Cloudflare Cache를 잘 쓰면 같은 이미지를 매번 R2 원본에서 읽지 않고 엣지 캐시에서 처리할 수 있습니다. 그러면 R2 요청 수와 원본 부하도 줄어듭니다.
무료 한도는 작은 개인 사이트에는 꽤 넉넉합니다. 예를 들어 헤르메스 홈페이지에 글 이미지와 작은 첨부 파일을 올리는 정도라면 처음에는 무료 한도 안에서 시작할 가능성이 큽니다. 하지만 다음 상황에서는 비용을 다시 계산해야 합니다.
| 상황 | 주의점 |
|---|---|
| 이미지가 수만 장 이상으로 늘어남 | 저장 용량 비용 증가 |
| 캐시가 잘 안 먹는 동적 URL 사용 | 원본 GET 요청 수 증가 |
| 사용자 업로드가 많음 | PUT 요청, 저장 용량, 검수 비용 증가 |
| 대용량 동영상 배포 | egress는 무료라도 저장 용량, 요청 수, 동영상 서비스 방식 검토 필요 |
| Infrequent Access를 잘못 사용 | 읽을 때 검색 처리비와 30일 최소 저장 기간 때문에 오히려 비쌀 수 있음 |
운영 팁은 단순합니다. 처음에는 Standard로 시작하고, 정말 오래 보관하지만 거의 읽지 않는 백업성 파일만 Infrequent Access를 검토하는 것이 좋습니다. 홈페이지 이미지처럼 언제든 읽힐 수 있는 자산은 Standard가 더 안전합니다.
헤르메스 홈페이지에 R2가 잘 맞는 이유
헤르메스 홈페이지는 일반 블로그보다 파일 서버 분리가 더 중요합니다. 이유는 글만 올라가는 사이트가 아니라, AI 에이전트가 계속 이미지, 스크린샷, 로그, 리포트, 음성, PDF 같은 결과물을 만들 수 있기 때문입니다.
상황별로 보면 더 명확합니다.
| 상황 | R2가 필요한 이유 |
|---|---|
| Hermes가 자동으로 글 초안을 만들고 이미지까지 생성 | 생성 이미지를 앱 repo에 넣으면 배포 용량과 Git 이력이 무거워짐 |
| OpenClaw나 Hermes가 브라우저 QA 스크린샷을 남김 | 스크린샷을 R2에 올리고 URL만 보고서에 남기는 편이 깔끔함 |
| 사용자가 Q&A에 이미지를 첨부 | 앱 서버가 파일을 오래 들고 있지 않고 R2에 보관하는 구조가 안전함 |
| 포스트에 대형 비교표 이미지나 인포그래픽 사용 | CDN 캐시와 egress 무료 조합이 유리함 |
| 나중에 뉴스레터, 자료실, 앱 다운로드 제공 | 파일 배포 서버를 미리 분리해 두면 확장 쉬움 |
특히 Next.js 프로젝트를 Vercel에 배포할 때는 이미지와 파일을 Git 저장소에 계속 넣는 방식이 장기적으로 좋지 않습니다. 파일이 늘수록 repository가 무거워지고, 빌드 시간이 늘고, 롤백과 캐시 관리도 복잡해집니다. R2를 쓰면 사이트 코드는 Git/Vercel에, 파일은 R2/Cloudflare에 분리됩니다.
권장 사고방식은 다음과 같습니다.
| 자산 종류 | 보관 위치 |
|---|---|
| 소스 코드, 글 데이터, 컴포넌트 | Git repository |
| 공개 이미지, 첨부 PDF, 다운로드 파일 | R2 public custom domain |
| 비공개 원본 파일, 임시 업로드 | R2 private bucket |
| 생성 로그, AI 작업 산출물 | R2 또는 DB, 목적에 따라 분리 |
| 작은 아이콘과 핵심 로고 | 프로젝트 public 폴더 또는 R2, 변경 빈도에 따라 선택 |
헤르메스 AI 에이전트와도 궁합이 좋습니다. Hermes가 글을 만들고, 이미지 파일을 압축하고, R2에 올리고, URL을 포스트 markdown에 삽입하는 운영 루프를 만들 수 있습니다. 사람은 최종 검수와 승인만 맡으면 됩니다.
처음 설정 순서 — 계정, 버킷, 토큰, 도메인
초기 설정은 크게 네 단계입니다.
R2 활성화
Cloudflare 대시보드에서 Storage & databases 메뉴의 R2로 들어가 활성화합니다. 무료로 시작할 수 있지만, 계정과 지역에 따라 결제수단 등록이 필요할 수 있습니다. 무료 한도 안에서는 보통 실제 청구가 발생하지 않지만, 결제수단 등록은 Cloudflare가 사용량 초과 과금을 처리하기 위해 요구할 수 있습니다.
운영 전 확인할 것:
| 확인 항목 | 이유 |
|---|---|
| 결제수단 등록 여부 | R2 활성화와 사용량 초과 대비 |
| 계정 ID | S3 endpoint와 API 설정에 필요 |
| 현재 플랜 | WAF, Rate Limiting, Access 같은 부가 기능 제한 확인 |
| 팀 접근 권한 | 혼자 운영이 아니라면 토큰과 권한 분리 필요 |
버킷 생성
버킷 이름은 나중에 바꾸기 어렵다고 생각하고 정하는 것이 좋습니다. 헤르메스 홈페이지라면 다음처럼 분리할 수 있습니다.
| 버킷 | 용도 | 공개 여부 |
|---|---|---|
| hermes-public-assets | 공개 이미지, 공개 다운로드 파일 | 공개 |
| hermes-private-uploads | 사용자 원본 업로드, 검수 전 파일 | 비공개 |
| hermes-agent-artifacts | AI 작업 산출물, QA 스크린샷, 리포트 | 보통 비공개 또는 제한 공개 |
| hermes-backups | 백업성 파일 | 비공개 |
처음에는 하나의 public assets 버킷으로 시작해도 됩니다. 하지만 사용자 업로드나 비공개 자료가 생기는 순간 공개 버킷과 비공개 버킷은 분리해야 합니다.
API 토큰 발급
R2 Overview에서 Manage R2 API Tokens로 들어가 토큰을 발급합니다. 중요한 원칙은 권한을 최소화하는 것입니다.
| 토큰 종류 | 권한 | 사용 위치 |
|---|---|---|
| 배포/운영 토큰 | 특정 public assets 버킷 Object Read & Write | CI, Hermes 운영 서버 |
| 업로드 전용 토큰 | 특정 버킷 쓰기 중심 | 업로드 서버 또는 Worker |
| 읽기 전용 토큰 | Object Read only | 백오피스, 검수 도구 |
| 전체 계정 토큰 | 가능하면 피함 | 긴급 운영 외에는 비추천 |
Access Key ID와 Secret Access Key는 생성 직후 한 번만 제대로 볼 수 있다고 생각하고 안전하게 보관해야 합니다. Git, 클라이언트 코드, 공개 문서, 이미지에 절대 넣으면 안 됩니다.
S3 endpoint 확인
R2의 S3 호환 endpoint는 다음 형태입니다.
| 항목 | 값 |
|---|---|
| Endpoint | https://ACCOUNT_ID.r2.cloudflarestorage.com |
| Region | auto |
| Bucket | 생성한 버킷 이름 |
| Access Key ID | R2 토큰 발급 시 표시된 값 |
| Secret Access Key | R2 토큰 발급 시 표시된 값 |
AWS SDK를 사용할 때 region을 auto로 넣는 것이 기본입니다. 일부 호환 도구에서는 us-east-1 또는 빈 값도 동작할 수 있지만, 새 설정은 auto로 통일하는 것이 이해하기 쉽습니다.
이미지 서버로 공개하는 방법 — r2.dev와 커스텀 도메인
R2 버킷은 기본적으로 비공개입니다. 웹사이트 이미지로 쓰려면 공개 접근 방식을 정해야 합니다.
r2.dev 개발 URL
r2.dev는 Cloudflare가 제공하는 개발용 공개 URL입니다. 대시보드에서 Public Development URL을 켜면 https://pub-xxxx.r2.dev/... 형태로 접근할 수 있습니다.
장점은 간단합니다.
| 장점 | 설명 |
|---|---|
| 설정이 빠름 | 도메인 없이 바로 테스트 가능 |
| 개발 확인에 충분 | 업로드된 이미지가 잘 보이는지 빠르게 확인 가능 |
| 별도 DNS 작업 없음 | Cloudflare zone 연결 전에도 테스트 가능 |
하지만 프로덕션에는 권장하지 않습니다.
| 한계 | 설명 |
|---|---|
| rate limit | 초당 수백 요청 수준에서 429 Too Many Requests가 발생할 수 있음 |
| 캐시·WAF 활용 제한 | Cloudflare Cache Rules, WAF, Access, Bot Management 같은 기능을 제대로 쓰기 어려움 |
| 도메인 브랜딩 부적합 | pub-xxxx.r2.dev 주소는 사이트 자산 주소로 보기 좋지 않음 |
| CNAME 연결 불가 | r2.dev 서브도메인을 CNAME으로 붙이는 방식은 지원되지 않음 |
따라서 r2.dev는 "업로드 테스트용"으로만 쓰고, 실제 헤르메스 홈페이지에는 커스텀 도메인을 붙이는 것이 좋습니다.
커스텀 도메인
프로덕션 추천 방식은 assets.vibecoding365days.com 같은 서브도메인을 R2 버킷에 연결하는 것입니다. 조건은 해당 도메인의 DNS가 Cloudflare에서 관리되고 있어야 한다는 점입니다.
설정 순서:
| 단계 | 설명 |
|---|---|
| 1 | R2 버킷 Settings로 이동 |
| 2 | Custom Domains에서 Add custom domain 선택 |
| 3 | assets.example.com 같은 서브도메인 입력 |
| 4 | Cloudflare가 DNS 레코드를 자동 생성하거나 안내 |
| 5 | 상태가 Active가 될 때까지 대기 |
| 6 | https://assets.example.com/path/to/file.jpg 형태로 접근 확인 |
커스텀 도메인을 쓰면 Cloudflare의 장점이 살아납니다.
| 기능 | 효과 |
|---|---|
| CDN Cache | 자주 보는 이미지를 엣지에서 제공해 R2 원본 요청 감소 |
| Cache Rules | 파일 확장자별 TTL, Cache Everything 같은 정책 가능 |
| WAF | 비정상 요청, 봇, 공격성 트래픽 차단 가능 |
| Access | 특정 파일 영역을 로그인 뒤에만 보이게 구성 가능 |
| Bot Management | 봇 트래픽 제어 가능, 플랜별 차이 있음 |
| 깔끔한 URL | 브랜드 도메인으로 이미지 주소 관리 |
헤르메스 홈페이지 추천 도메인은 다음 중 하나입니다.
| 후보 | 추천도 | 설명 |
|---|---|---|
| assets.vibecoding365days.com | 매우 추천 | 이미지, CSS성 자산, 다운로드 파일을 포괄 |
| files.vibecoding365days.com | 추천 | 다운로드와 첨부 파일 느낌이 강함 |
| images.vibecoding365days.com | 조건부 | 이미지 전용이면 좋지만 나중에 PDF, audio까지 담기 애매할 수 있음 |
| cdn.vibecoding365days.com | 가능 | CDN 느낌은 좋지만 실제 원본이 R2라는 의미는 덜 명확 |
개인적으로는 assets.vibecoding365days.com을 추천합니다. 이미지뿐 아니라 PDF, 썸네일, 음성, 작은 다운로드 파일까지 자연스럽게 포괄할 수 있기 때문입니다.
업로드 방법 5가지 — 대시보드, Wrangler, Python, JavaScript, rclone
R2 업로드 방법은 운영 단계에 따라 달라집니다.
| 방법 | 적합한 상황 |
|---|---|
| 대시보드 UI | 한두 개 파일을 사람이 직접 올릴 때 |
| Wrangler CLI | 개발자 로컬, Cloudflare Workers와 함께 운영할 때 |
| Python boto3 | Hermes Agent, 배치 스크립트, 이미지 자동 업로드 |
| JavaScript AWS SDK | Next.js API route, Node.js 서버, 업로드 서비스 |
| rclone | 기존 파일 대량 이전, 백업, 동기화 |
대시보드 UI
버킷에 들어가 Upload를 누르고 파일을 드래그하면 됩니다. 가장 쉽지만 반복 운영에는 적합하지 않습니다. 글 하나 작성하면서 대표 이미지 하나를 올리는 정도는 괜찮습니다.
실전 팁:
| 상황 | 팁 |
|---|---|
| 파일명이 한글 | 가능하면 영문 소문자와 하이픈으로 바꾸기 |
| 이미지가 큼 | 업로드 전 webp 또는 avif로 압축 |
| 나중에 교체 예정 | 파일명을 그대로 덮어쓰기보다 버전 파일명 사용 권장 |
Wrangler CLI
Cloudflare 개발 환경을 이미 쓰고 있다면 Wrangler가 편합니다.
명령 예시:
wrangler r2 bucket create hermes-public-assets
wrangler r2 object put hermes-public-assets/images/posts/r2-guide/hero.webp --file=hero.webp --content-type=image/webp
wrangler r2 object get hermes-public-assets/images/posts/r2-guide/hero.webp --file=downloaded-hero.webp
Wrangler는 사람이 직접 관리하는 개발자 작업에 좋습니다. 다만 대량 파일 동기화는 rclone이 더 편할 때가 많습니다.
Python boto3
Hermes Agent와 가장 잘 맞는 방식은 Python입니다. Hermes가 파일을 만들고, 압축하고, R2에 올리고, URL을 포스트에 삽입하는 자동화를 만들기 쉽습니다.
개념 예시:
| 설정 | 값 |
|---|---|
| service_name | s3 |
| endpoint_url | https://ACCOUNT_ID.r2.cloudflarestorage.com |
| region_name | auto |
| bucket | hermes-public-assets |
| key | images/posts/r2-guide/hero.webp |
| content type | image/webp |
| cache control | public, max-age=31536000, immutable |
Python에서 boto3 client를 만들고 upload_file 또는 put_object를 호출하면 됩니다. 대용량 파일은 upload_file이 내부적으로 멀티파트 업로드를 처리할 수 있어 편합니다.
Hermes 자동화에 넣을 때는 다음 규칙을 권장합니다.
| 규칙 | 이유 |
|---|---|
| 환경변수로 키 주입 | Secret을 파일과 Git에 남기지 않기 위해 |
| 업로드 후 HEAD 요청으로 검증 | Content-Type, Content-Length 확인 |
| 업로드 결과 URL을 JSON으로 출력 | 다음 에이전트 단계에서 사용하기 쉬움 |
| 실패 시 재시도 | 네트워크 오류 대비 |
| 업로드 로그에 Secret 미출력 | 운영 보안 |
JavaScript AWS SDK
Next.js API route나 Node.js 서버에서 R2에 파일을 올릴 때는 @aws-sdk/client-s3를 사용하면 됩니다.
개념 예시:
| 항목 | 값 |
|---|---|
| 패키지 | @aws-sdk/client-s3 |
| client | S3Client |
| upload command | PutObjectCommand |
| endpoint | R2 S3 endpoint |
| region | auto |
| credentials | 서버 환경변수에서만 읽기 |
브라우저에서 직접 R2 Secret을 사용하면 절대 안 됩니다. 프론트엔드가 업로드해야 한다면 서버가 presigned URL을 만들어 주고, 브라우저는 그 URL로 PUT만 해야 합니다.
rclone
이미지 폴더가 수천 개 있거나 기존 서버에서 R2로 옮길 때는 rclone이 강합니다.
명령 예시:
rclone copy ./public/images r2:hermes-public-assets/images --transfers=16 --checkers=32
rclone sync ./public/downloads r2:hermes-public-assets/downloads --transfers=8 --checkers=16
주의할 점은 sync입니다. sync는 대상에만 있는 파일을 지울 수 있습니다. 처음에는 copy로 이전하고, 운영이 안정된 뒤에 sync를 쓰는 것이 안전합니다.
대용량 파일과 멀티파트 업로드
R2는 큰 파일도 다룰 수 있습니다. 공식 제한 기준으로 객체 최대 크기는 약 5TiB이고, 단일 업로드 파트는 5GiB 제한이 있습니다. 멀티파트 업로드를 사용하면 큰 파일을 여러 조각으로 나누어 병렬 업로드하고 실패한 부분만 다시 시도할 수 있습니다.
| 항목 | 단일 PUT | 멀티파트 업로드 |
|---|---|---|
| 적합한 파일 | 작은 이미지, 작은 PDF | 큰 zip, 영상, 백업 파일, 데이터셋 |
| 최대 크기 감각 | 단일 요청은 5GiB 수준 제한 | 전체 객체는 5TiB 수준까지 가능 |
| 실패 시 복구 | 처음부터 다시 올릴 수 있음 | 실패한 파트만 재시도 가능 |
| 병렬 처리 | 어려움 | 가능 |
| SDK 자동 처리 | 도구에 따라 다름 | boto3 upload_file 등은 자동 전환 가능 |
헤르메스 홈페이지에서는 대용량 파일이 아주 많지는 않겠지만, 다음 경우 멀티파트가 유용합니다.
| 상황 | 예시 |
|---|---|
| AI 강의 자료 배포 | 1GB 이상의 zip 자료 |
| 녹화 영상 백업 | 원본 mp4 보관 |
| 데이터셋 공유 | 실습용 샘플 데이터 압축 파일 |
| 브라우저 QA 결과 보관 | 긴 테스트 실행의 스크린샷/영상 묶음 |
주의점도 있습니다. 멀티파트 업로드가 중간에 실패하고 완료되지 않으면 미완료 업로드 상태가 남을 수 있습니다. R2에는 기본적으로 생성 후 7일 뒤 미완료 멀티파트 업로드를 정리하는 lifecycle 규칙이 있지만, 대량 업로드를 자주 한다면 직접 모니터링하는 것이 좋습니다.
Presigned URL — 서버를 거치지 않는 직접 업로드
사용자 업로드를 구현할 때 가장 피해야 할 구조는 브라우저가 큰 파일을 Next.js 서버로 올리고, Next.js 서버가 다시 R2로 올리는 방식입니다. 이렇게 하면 서버가 중간에서 파일을 두 번 처리해야 하고, timeout과 메모리 문제가 생기기 쉽습니다.
더 좋은 구조는 presigned URL입니다.
흐름:
| 단계 | 설명 |
|---|---|
| 1 | 브라우저가 업로드하고 싶은 파일 이름, 크기, MIME 타입을 서버에 전달 |
| 2 | 서버가 로그인, 권한, 파일 크기, 확장자를 검증 |
| 3 | 서버가 R2용 presigned PUT URL을 생성 |
| 4 | 브라우저가 그 URL로 R2에 직접 PUT 업로드 |
| 5 | 업로드 완료 후 서버에 key를 알려 DB에 기록 |
| 6 | 서버 또는 Hermes가 이미지 검수, 리사이징, 공개 전환 처리 |
presigned URL의 장점:
| 장점 | 설명 |
|---|---|
| 서버 부하 감소 | 파일 본문이 앱 서버를 통과하지 않음 |
| Secret 보호 | 클라이언트는 R2 Secret을 모름 |
| 시간 제한 | URL 만료 시간을 1초부터 7일 범위에서 설정 가능 |
| 권한 최소화 | 특정 객체 key와 특정 method만 허용 가능 |
주의점:
| 주의 | 설명 |
|---|---|
| URL은 bearer token처럼 취급 | URL을 가진 사람은 만료 전 해당 작업 가능 |
| custom domain에서 직접 사용 불가 | presigned URL은 S3 API 도메인에서 작동 |
| 만료 전 재사용 가능 | 한 번만 쓰이는 토큰이라고 착각하면 안 됨 |
| POST form 방식은 미지원 | S3 POST multipart form 기반 업로드는 현재 지원되지 않음 |
| 업로드 후 검수 필요 | 악성 파일, MIME 속임, 과도한 크기 검사를 해야 함 |
헤르메스 홈페이지에 Q&A 이미지 첨부나 사용자 자료 업로드를 넣는다면, presigned URL + private bucket + 검수 후 public bucket 복사 구조를 추천합니다.
캐시 전략 — 이미지 서버 성능을 제대로 뽑는 법
R2를 이미지 서버로 쓴다고 해서 자동으로 모든 것이 최적화되는 것은 아닙니다. 커스텀 도메인을 붙이고 Cloudflare Cache 정책을 잘 잡아야 합니다.
기본 원칙
이미지 서버의 캐시 전략은 두 가지 중 하나입니다.
| 전략 | 설명 | 적합한 파일명 |
|---|---|---|
| 긴 캐시 + 파일명 버전 관리 | 한 번 올린 파일은 바꾸지 않고 새 파일명으로 교체 | hero-v1.webp, hero-20260426.webp, hash.webp |
| 짧은 캐시 + 같은 파일명 교체 | 같은 URL의 파일을 자주 바꿈 | 운영은 쉽지만 캐시 무효화 필요 |
추천은 첫 번째입니다. 파일명에 날짜, 버전, 해시를 넣고 Cache-Control을 길게 잡습니다.
권장 예시:
| 파일 | Cache-Control |
|---|---|
| 포스트 대표 이미지 | public, max-age=31536000, immutable |
| 본문 이미지 | public, max-age=31536000, immutable |
| 자주 바뀌는 JSON | public, max-age=60 또는 별도 API 사용 |
| 다운로드 PDF | public, max-age=86400 또는 버전 파일명 사용 시 1년 |
Cloudflare Cache Rules
커스텀 도메인을 쓰면 Cloudflare Cache Rules를 사용할 수 있습니다. 기본적으로 Cloudflare는 특정 확장자 중심으로 캐시합니다. 모든 파일을 캐시하고 싶다면 Cache Everything 규칙이나 확장자별 규칙을 검토해야 합니다.
헤르메스 assets 도메인 추천 규칙:
| 경로 | 정책 |
|---|---|
| /images/* | Edge TTL 1개월~1년, Browser TTL 1개월~1년 |
| /downloads/* | 파일 성격에 따라 1일~1년 |
| /private/* | 공개 도메인에 두지 않음 |
| /tmp/* | 짧은 TTL, 또는 비공개 버킷 사용 |
중요한 것은 바뀌는 파일과 안 바뀌는 파일을 URL 레벨에서 분리하는 것입니다. 글 이미지처럼 한 번 발행 후 거의 안 바뀌는 파일은 긴 캐시가 좋고, 매일 바뀌는 통계 JSON은 같은 방식으로 캐시하면 안 됩니다.
이미지 최적화와 R2의 역할 구분
R2는 객체 스토리지이지 이미지 변환 서비스가 아닙니다. 이미지 리사이징, 포맷 변환, 썸네일 생성은 별도 단계가 필요합니다.
선택지는 다음과 같습니다.
| 방식 | 설명 |
|---|---|
| 빌드 전 변환 | 업로드 전에 sharp 등으로 webp/avif 썸네일 생성 |
| Hermes 자동 변환 | Hermes가 원본 이미지를 받아 여러 크기로 변환 후 R2 업로드 |
| Cloudflare Images 사용 | 별도 Cloudflare Images 제품 사용, 과금 구조 별도 |
| Worker 변환 | Worker와 이미지 도구를 조합, 복잡도 증가 |
| Next.js Image remotePatterns | R2 커스텀 도메인을 원격 이미지 소스로 허용 |
헤르메스 홈페이지는 글 중심 사이트이므로 처음에는 업로드 전 변환 + R2 정적 서빙이 가장 단순합니다. 즉 원본 이미지를 1600px webp, 800px webp, 400px webp로 만들어 R2에 올리고, 페이지에서는 필요한 크기를 직접 참조합니다.
권장 폴더 구조와 파일 이름 규칙
R2에는 실제 폴더가 있는 것은 아니지만 key에 슬래시를 넣으면 폴더처럼 관리할 수 있습니다. 처음부터 규칙을 잡아야 나중에 수천 개 파일이 생겨도 관리가 됩니다.
헤르메스 홈페이지 추천 구조:
| prefix | 용도 |
|---|---|
| images/posts/YYYY/MM/slug/ | 포스트 본문 이미지 |
| images/hero/ | 대표 이미지와 랜딩 이미지 |
| images/thumbnails/ | 목록 카드 썸네일 |
| downloads/guides/ | 공개 PDF, zip 자료 |
| audio/posts/ | 음성 요약, TTS 파일 |
| video/clips/ | 짧은 영상 클립 |
| artifacts/hermes/YYYY/MM/DD/ | AI 에이전트 작업 결과물 |
| tmp/uploads/ | 업로드 직후 임시 파일, 가능하면 private bucket 권장 |
파일 이름 규칙:
| 규칙 | 예시 | 이유 |
|---|---|---|
| 소문자 영문 | cloudflare-r2-guide.webp | URL 안정성 |
| 공백 대신 하이픈 | hero-image.webp | 복사와 공유가 쉬움 |
| 날짜 또는 버전 포함 | hero-20260426-v1.webp | 캐시 무효화 없이 교체 가능 |
| 원본과 파생본 분리 | hero-original.png, hero-1600.webp | 최적화 파일 추적 가능 |
| 한글 파일명 지양 | r2-이미지.webp 대신 r2-image.webp | 인코딩 문제 감소 |
추천 예시:
| 용도 | key |
|---|---|
| 이 글 대표 이미지 | images/posts/2026/04/cloudflare-r2-hermes-file-server/hero-1600.webp |
| 본문 다이어그램 | images/posts/2026/04/cloudflare-r2-hermes-file-server/architecture-v1.webp |
| 다운로드 PDF | downloads/guides/cloudflare-r2-hermes-checklist-20260426.pdf |
| Hermes QA 스크린샷 | artifacts/hermes/2026/04/26/qna-preview-001.webp |
이렇게 하면 나중에 Hermes에게 "2026년 4월 R2 글에 들어간 이미지 목록 정리해줘"라고 지시했을 때도 규칙적으로 찾을 수 있습니다.
Next.js 홈페이지에서 쓰는 실전 구조
헤르메스 홈페이지가 Next.js 기반이라면 R2는 다음 세 지점과 연결됩니다.
| 위치 | 역할 |
|---|---|
| 포스트 markdown | 이미지 URL을 직접 삽입 |
| next.config | R2 커스텀 도메인을 remote image host로 허용 |
| API route 또는 Server Action | presigned URL 생성, 업로드 완료 기록 |
공개 이미지 참조
가장 단순한 방식은 markdown 또는 TS 포스트 데이터에 R2 URL을 직접 넣는 것입니다.
예시 URL:
이 방식은 빠르고 단순합니다. 단, 파일이 삭제되면 글 이미지도 깨집니다. 그래서 발행된 글에 쓰인 이미지는 lifecycle로 자동 삭제하지 않는 것이 좋습니다.
Next Image와 remotePatterns
Next.js의 Image 컴포넌트를 쓴다면 R2 커스텀 도메인을 허용해야 합니다. 프로젝트 버전에 따라 next.config 작성 방식이 달라질 수 있으므로 현재 프로젝트의 Next.js 문서를 확인하고 설정해야 합니다.
개념은 다음과 같습니다.
| 항목 | 값 |
|---|---|
| protocol | https |
| hostname | assets.vibecoding365days.com |
| pathname | /images/** |
다만 R2 + Cloudflare Cache에서 이미 최적화된 webp/avif를 제공한다면, 모든 이미지를 Next Image 최적화 파이프라인에 다시 태울 필요는 없습니다. 정적 글 이미지는 일반 img 태그나 커스텀 렌더러로 충분할 수 있습니다.
업로드 API route
사용자 업로드가 필요하다면 API route는 파일 본문을 받기보다 presigned URL을 발급하는 역할만 맡기는 것이 좋습니다.
권장 흐름:
| API | 역할 |
|---|---|
| POST /api/uploads/presign | 파일명, 크기, 타입 검증 후 presigned URL 반환 |
| PUT presigned URL | 브라우저가 R2에 직접 업로드 |
| POST /api/uploads/complete | 업로드 완료 신고, DB 기록, 검수 상태 저장 |
| background worker | 이미지 리사이징, 바이러스 검사, 공개 버킷 복사 |
이 구조를 쓰면 서버리스 환경에서도 큰 파일 업로드를 안정적으로 처리할 수 있습니다.
Hermes Agent로 R2 운영 자동화하기
R2의 진짜 장점은 Hermes Agent와 결합할 때 커집니다. 파일 서버를 사람이 수동으로 관리하는 대신, Hermes가 규칙에 따라 업로드, 검증, URL 삽입, 캐시 정책 점검, 오래된 임시 파일 정리를 처리할 수 있습니다.
가능한 자동화 예시는 다음과 같습니다.
| 자동화 | Hermes가 하는 일 | 사람이 확인할 것 |
|---|---|---|
| 포스트 이미지 업로드 | 로컬 이미지 압축, 파일명 생성, R2 업로드, URL 삽입 | 이미지 품질, 저작권, 최종 배치 |
| QA 스크린샷 보관 | 브라우저 테스트 후 스크린샷을 artifacts 경로에 업로드 | 공개 여부, 민감정보 포함 여부 |
| 자료실 파일 발행 | PDF/zip을 checksums와 함께 업로드 | 파일 내용, 배포 승인 |
| 링크 깨짐 점검 | R2 URL 목록을 HEAD 요청으로 검사 | 삭제 여부 결정 |
| 비용 리포트 | 객체 수, 큰 파일, 오래된 tmp prefix 요약 | 삭제/보관 정책 승인 |
| 캐시 무효화 요청 | 교체된 파일의 URL purge 목록 생성 | purge 범위 승인 |
Hermes에게 줄 수 있는 운영 지시 예시:
| 상황 | 지시 예시 |
|---|---|
| 새 글 이미지 올리기 | 이 글에 들어갈 이미지 3장을 webp로 압축하고, images/posts/2026/04/r2-guide/ 아래에 올린 뒤 URL 목록을 만들어줘 |
| 깨진 이미지 찾기 | Hermes 포스트 전체에서 assets 도메인 이미지를 찾아 HEAD 요청으로 404 여부를 검사해줘 |
| 파일명 정리 | public/images 폴더의 한글 파일명을 영문 하이픈 규칙으로 바꾸고 R2 업로드 계획을 만들어줘 |
| 비용 점검 | R2 버킷의 큰 객체 상위 50개와 tmp prefix의 30일 이상 파일 목록을 요약해줘 |
| 배포 전 확인 | 새 R2 이미지 URL이 라이브 페이지에서 정상 로드되는지 브라우저로 확인해줘 |
중요한 운영 원칙은 Hermes에게 Secret을 출력하게 하지 않는 것입니다. R2 Access Key와 Secret은 환경변수로만 주입하고, 로그에는 endpoint, bucket, key, public URL 정도만 남깁니다. 실패 로그에도 Secret이 찍히면 안 됩니다.
보안 설정 — 절대 공개하면 안 되는 것
R2는 파일 서버로 쓰기 쉽지만, 공개 범위를 잘못 잡으면 사고가 납니다. 특히 사용자 업로드와 내부 산출물을 다룰 때 조심해야 합니다.
공개 버킷과 비공개 버킷 분리
가장 중요한 원칙은 공개와 비공개를 버킷 단위로 분리하는 것입니다.
| 버킷 | 공개 여부 | 설명 |
|---|---|---|
| hermes-public-assets | 공개 | 이미 검수된 이미지, 공개 자료 |
| hermes-private-uploads | 비공개 | 사용자가 올린 원본, 검수 전 파일 |
| hermes-agent-artifacts | 기본 비공개 | AI 작업 결과, 로그, 스크린샷 |
| hermes-backups | 비공개 | DB 백업, 설정 백업, export 파일 |
"나중에 경로로 막으면 되겠지"보다 버킷 자체를 분리하는 편이 안전합니다. 공개 버킷에는 공개되어도 되는 파일만 들어가야 합니다.
Secret 관리
절대 하면 안 되는 것:
| 금지 | 이유 |
|---|---|
| Access Key를 Git에 커밋 | 유출되면 파일 삭제/변조 위험 |
| Secret을 브라우저 JS에 넣기 | 사용자에게 그대로 노출 |
| 토큰을 스크린샷에 포함 | 블로그나 문서에서 유출 가능 |
| 전체 계정 권한 토큰을 장기 사용 | 한 토큰 유출 시 피해 범위가 큼 |
| 로그에 Authorization 헤더 출력 | 운영 로그를 통해 유출 가능 |
권장 방식:
| 위치 | 저장 방식 |
|---|---|
| 로컬 개발 | 로컬 전용 환경 설정 파일, Git ignore 확인 |
| Vercel | Project Environment Variables |
| Cloudflare Workers | wrangler secret 또는 대시보드 Secret |
| Hermes 서버 | OS 환경변수 또는 안전한 secret store |
| 문서 | 환경변수 이름만 기록, 값은 절대 기록하지 않음 |
업로드 검수
사용자 업로드를 받는다면 R2에 올렸다고 끝이 아닙니다. 최소한 다음 검사가 필요합니다.
| 검사 | 이유 |
|---|---|
| 파일 크기 제한 | 비용 폭주와 DoS 방지 |
| MIME 타입 확인 | 이미지인 척하는 스크립트 차단 |
| 확장자 제한 | 실행 파일 업로드 차단 |
| 이미지 재인코딩 | 숨겨진 payload와 메타데이터 제거에 도움 |
| 공개 전 검수 상태 | 부적절한 이미지 공개 방지 |
| 악성 파일 검사 | 다운로드 파일 제공 시 필수에 가까움 |
홈페이지에 단순 운영자 업로드만 있다면 복잡한 검수는 줄일 수 있습니다. 하지만 공개 사용자 업로드가 들어오는 순간 private bucket, presigned URL, 검수 queue, public 복사 구조를 갖추는 것이 좋습니다.
운영 모니터링과 비용 폭주 방지
R2는 egress가 무료라서 안심할 수 있지만, 요청 수와 저장 용량은 계속 쌓입니다. 운영자는 월 1회 이상 다음 항목을 확인해야 합니다.
| 점검 항목 | 확인 이유 |
|---|---|
| 총 저장 용량 | 이미지와 백업 파일 증가 추세 확인 |
| 객체 수 | 너무 작은 파일이 과도하게 많으면 목록 관리 복잡 |
| Class A 요청 | 업로드, LIST, lifecycle, copy가 과도한지 확인 |
| Class B 요청 | 캐시 미스가 많아 원본 GET이 과도한지 확인 |
| r2.dev 사용 여부 | 개발 URL이 실서비스에 남아 있지 않은지 확인 |
| tmp prefix | 임시 파일이 정리되고 있는지 확인 |
| 큰 객체 상위 목록 | 실수로 원본 영상, 대형 zip을 공개 버킷에 넣지 않았는지 확인 |
비용 폭주를 줄이는 규칙:
| 규칙 | 효과 |
|---|---|
| 공개 이미지 파일명에 버전/해시 사용 | 긴 캐시 가능, 원본 요청 감소 |
| tmp prefix lifecycle 삭제 | 임시 파일 누적 방지 |
| 업로드 크기 제한 | 큰 파일 남용 방지 |
| 사용자별 업로드 quota | 악성 사용자의 저장 공간 남용 방지 |
| Cloudflare Cache Rules 적용 | 반복 조회를 엣지 캐시로 처리 |
| r2.dev 프로덕션 사용 금지 | rate limit과 예측 불가 문제 방지 |
Hermes 자동 리포트 예시:
| 주기 | 내용 |
|---|---|
| 매일 | 새로 업로드된 파일 수, 실패 업로드, 404 이미지 요약 |
| 매주 | 큰 파일 상위 30개, tmp 7일 초과 파일, 깨진 링크 |
| 매월 | 저장 용량 추세, 요청 수 추세, 비용 추정, lifecycle 후보 |
이런 리포트는 Hermes의 cron 기능과 잘 맞습니다. 월요일 오전에 R2 점검 요약을 텔레그램으로 받게 하면, 파일 서버가 조용히 망가지는 일을 줄일 수 있습니다.
S3, Supabase Storage, Vercel Blob, Backblaze B2와 비교
R2가 항상 정답은 아닙니다. 선택은 사이트의 구조와 운영 방식에 따라 달라집니다.
| 서비스 | 장점 | 단점 | 헤르메스 홈페이지 판단 |
|---|---|---|---|
| Cloudflare R2 | egress 무료, S3 호환, Cloudflare CDN/Workers 결합 | S3 100% 호환 아님, r2.dev 프로덕션 부적합, 요청 수 과금 | 공개 이미지/파일 서버로 강력 추천 |
| AWS S3 | 표준에 가까운 생태계, 기능 풍부, IAM 강력 | egress 비용과 설정 복잡도 | 이미 AWS 중심이면 좋지만 개인 사이트에는 과할 수 있음 |
| Supabase Storage | DB/Auth와 통합 쉬움, 앱 개발 편함 | 대규모 공개 트래픽 파일 서버로는 비용 구조 검토 필요 | Supabase 앱이면 편함, 순수 assets CDN이면 R2 선호 |
| Vercel Blob | Vercel/Next.js와 개발 경험 좋음 | Vercel 종속, 트래픽/저장 비용 구조 확인 필요 | 빠른 구현은 좋지만 장기 파일 서버는 R2 검토 |
| Backblaze B2 | 저렴한 저장 비용, S3 호환 | CDN 결합은 별도 설계 필요 | 백업/저장소로 좋고, Cloudflare 결합 운영도 가능 |
| Git LFS | Git 워크플로우와 결합 | 공개 이미지 서버나 대량 트래픽에는 부적합 | 사이트 assets 서버로는 비추천 |
헤르메스 홈페이지의 추천 결론:
| 목적 | 추천 |
|---|---|
| 공개 이미지, 글 첨부 파일 | Cloudflare R2 |
| 앱 내부 사용자 파일과 DB 연동 | Supabase Storage도 검토 가능 |
| AWS 기반 기업 시스템 | S3 |
| Vercel에서 아주 빠르게 MVP | Vercel Blob |
| 장기 백업과 archive | R2 Infrequent Access 또는 Backblaze B2 |
R2가 특히 빛나는 지점은 트래픽 예측이 어렵고, 공개 파일 조회가 많고, Cloudflare 도메인을 이미 쓰는 사이트입니다. 이 조건이면 R2는 운영 난이도와 비용 사이의 균형이 좋습니다.
장점, 단점, 실제 평판을 균형 있게 보기
공식 문서와 커뮤니티 반응을 종합하면 R2의 평판은 대체로 "egress 비용을 없애고 S3 호환으로 쉽게 붙일 수 있는 매우 매력적인 객체 스토리지"에 가깝습니다. 특히 개인 개발자, 스타트업, 정적 사이트 운영자, 파일 다운로드가 많은 서비스에서 좋은 평가를 받습니다.
장점:
| 장점 | 설명 |
|---|---|
| egress 비용 부담이 작음 | 이미지나 다운로드 트래픽이 갑자기 늘어도 egress 과금 공포가 줄어듦 |
| S3 호환 | 기존 SDK, rclone, boto3, AWS SDK 사용 가능 |
| Cloudflare와 결합 | 커스텀 도메인, CDN, WAF, Workers와 자연스럽게 연결 |
| 무료 한도 | 작은 사이트는 무료 또는 매우 낮은 비용으로 시작 가능 |
| 확장성 | 버킷당 저장량과 객체 수가 문서상 unlimited로 안내됨 |
| 에이전트 자동화 친화적 | CLI/API 기반 운영이 쉬워 Hermes 자동화와 잘 맞음 |
단점과 주의점:
| 단점 | 설명 |
|---|---|
| 모든 비용이 무료는 아님 | 저장 용량과 요청 수 과금은 존재 |
| S3 완전 복제는 아님 | ACL, Object Lock, 일부 KMS/SSE, tagging 등 필요한 기능이 없을 수 있음 |
| r2.dev는 개발용 | 프로덕션 트래픽에는 커스텀 도메인 필수에 가까움 |
| Presigned URL 제약 | custom domain이 아니라 S3 API domain에서 동작 |
| Infrequent Access 주의 | 무료 한도 제외, 검색 처리비, 30일 최소 보관 과금 |
| 이미지 변환은 별도 | R2 자체가 이미지 리사이징 서비스는 아님 |
운영자가 특히 조심해야 할 오해:
| 오해 | 실제 |
|---|---|
| R2는 CDN이다 | R2는 객체 스토리지이고, CDN 캐시는 커스텀 도메인과 Cloudflare Cache로 구성 |
| R2에 올리면 자동 이미지 최적화된다 | 이미지 최적화는 별도 도구나 Cloudflare Images 등이 필요 |
| r2.dev URL로 서비스해도 된다 | 개발 테스트용이며 프로덕션에는 커스텀 도메인 권장 |
| Secret을 프론트에 넣어도 presigned니까 안전하다 | Secret은 절대 프론트에 넣으면 안 되고 서버가 presigned URL을 만들어야 함 |
| Infrequent Access가 항상 싸다 | 자주 읽히면 검색 처리비와 최소 보관 기간 때문에 손해일 수 있음 |
헤르메스 홈페이지 운영 관점에서 R2는 "강력 추천하지만, 공개/비공개 분리와 캐시 정책을 같이 설계해야 하는 도구"입니다. 그냥 버킷 하나 만들고 모든 파일을 넣는 방식보다, 운영 규칙을 먼저 정하고 시작해야 오래 갑니다.
헤르메스 홈페이지 추천 아키텍처
최종 추천 구조는 다음과 같습니다.
| 구성요소 | 추천 |
|---|---|
| 공개 assets 도메인 | assets.vibecoding365days.com |
| 공개 버킷 | hermes-public-assets |
| 비공개 업로드 버킷 | hermes-private-uploads |
| AI 산출물 버킷 | hermes-agent-artifacts |
| 공개 이미지 prefix | images/posts/YYYY/MM/slug/ |
| 다운로드 prefix | downloads/guides/ |
| 임시 prefix | tmp/ with lifecycle |
| 업로드 방식 | 운영자 파일은 Hermes/rclone, 사용자 파일은 presigned URL |
| 캐시 | 이미지 파일은 긴 TTL + 버전 파일명 |
| 보안 | Secret은 환경변수, 버킷 권한 최소화, 공개/비공개 분리 |
가장 단순한 1단계 구조
처음에는 이렇게 시작합니다.
| 항목 | 값 |
|---|---|
| 버킷 | hermes-public-assets |
| 도메인 | assets.vibecoding365days.com |
| 파일 | 글 이미지와 공개 다운로드만 저장 |
| 업로드 | 대시보드 또는 Wrangler |
| 캐시 | 이미지 prefix에 긴 TTL |
이 단계는 설정이 쉽고 사고 가능성이 낮습니다. 공개되어도 되는 파일만 넣는 것이 핵심입니다.
운영 자동화 2단계 구조
글과 이미지가 늘어나면 Hermes 자동화를 붙입니다.
| 항목 | 값 |
|---|---|
| 업로드 스크립트 | 이미지 압축 후 R2 업로드 |
| URL 생성 | key 규칙에 따라 public URL 자동 생성 |
| 검증 | HEAD 요청으로 Content-Type, 크기, 200 응답 확인 |
| 포스트 반영 | markdown에 이미지 URL 삽입 |
| 보고 | 업로드 목록과 최종 URL을 텔레그램으로 전송 |
이 단계부터는 사람이 파일을 직접 올리는 일이 줄어듭니다.
사용자 업로드 3단계 구조
나중에 Q&A 이미지 첨부, 자료 제출, 커뮤니티 기능이 들어가면 다음처럼 확장합니다.
| 항목 | 값 |
|---|---|
| 원본 업로드 | hermes-private-uploads |
| 업로드 방식 | presigned URL |
| 검수 | 서버/worker/Hermes가 크기, 타입, 내용 확인 |
| 공개 전환 | 검수 통과 파일만 hermes-public-assets로 복사 |
| DB 기록 | 원본 key, 공개 key, 상태, 사용자 ID 저장 |
| 삭제 정책 | rejected/tmp 파일 lifecycle 삭제 |
이 구조는 조금 복잡하지만 공개 사이트에서는 안전합니다. 특히 AI 에이전트가 자동으로 파일을 다루는 경우, 비공개 원본과 공개 결과물을 분리하는 습관이 중요합니다.
Cache-Control, CORS, Smart Tiered Cache 실전 설정
R2를 파일 저장소로만 쓰면 절반만 쓰는 것입니다. 이미지 서버로 제대로 쓰려면 업로드 시 HTTP 메타데이터를 넣고, Cloudflare Cache Rules를 만들고, 브라우저 직접 업로드가 필요하면 CORS까지 설정해야 합니다.
업로드 시 Cache-Control 헤더 설정
이미지처럼 한 번 발행하면 거의 바뀌지 않는 파일은 업로드 순간부터 긴 캐시를 붙이는 것이 좋습니다. 핵심은 파일명에 날짜, 버전, 해시를 넣고 Cache-Control을 길게 주는 것입니다.
Python boto3 개념 예시:
| 항목 | 값 |
|---|---|
| Bucket | my-bucket |
| Key | images/photo.jpg |
| ContentType | image/jpeg |
| CacheControl | public, max-age=31536000, immutable |
실제 코드 형태로 쓰면 다음 의미입니다.
s3.put_object에 Bucket, Key, Body, ContentType, CacheControl을 함께 전달합니다. CacheControl 값은 public, max-age=31536000, immutable처럼 둡니다. 31536000초는 약 1년이고, immutable은 이 URL의 파일이 바뀌지 않는다는 신호입니다.
주의할 점은 같은 URL로 파일을 자주 덮어쓰면 1년 캐시가 오히려 문제가 된다는 것입니다. 그래서 대표 이미지를 바꿀 가능성이 있으면 hero.webp를 덮어쓰기보다 hero-20260426-v1.webp, hero-20260426-v2.webp처럼 새 key를 쓰는 방식이 좋습니다.
| 파일 유형 | 권장 Cache-Control | 파일명 전략 |
|---|---|---|
| 발행된 글 이미지 | public, max-age=31536000, immutable | 날짜/버전/해시 포함 |
| 로고와 공통 아이콘 | public, max-age=604800 또는 버전 파일명 사용 시 1년 | 교체 가능성에 따라 결정 |
| PDF 다운로드 | public, max-age=86400 또는 버전 파일명 사용 시 1년 | 문서 버전 포함 권장 |
| 자주 바뀌는 JSON | public, max-age=60 또는 no-cache | 같은 URL 가능 |
| 검수 전 임시 파일 | public 캐시 금지, 가능하면 비공개 | private bucket 권장 |
Cloudflare Cache Rules 설정
커스텀 도메인을 붙인 뒤에는 Cloudflare 대시보드에서 Cache Rules를 설정합니다. 대표적인 흐름은 다음과 같습니다.
| 단계 | 설정 |
|---|---|
| 1 | Cloudflare 대시보드에서 Caching 메뉴로 이동 |
| 2 | Cache Rules 선택 후 Create Rule |
| 3 | 조건은 Hostname equals assets.example.com 형태로 지정 |
| 4 | Eligible for cache 활성화 |
| 5 | Edge Cache TTL을 1일, 1개월, 1년 등 파일 성격에 맞게 설정 |
| 6 | 필요하면 Browser TTL도 함께 지정 |
헤르메스 홈페이지라면 다음 규칙이 현실적입니다.
| 조건 | 권장 동작 |
|---|---|
| Hostname equals assets.vibecoding365days.com and URI Path starts with /images/ | Eligible for cache, Edge TTL 1개월~1년 |
| URI Path starts with /downloads/ | 파일 성격에 따라 1일~1개월 |
| URI Path starts with /tmp/ | 공개 도메인에 두지 않거나 짧은 TTL |
| 확장자 webp, avif, jpg, png, svg | 캐시 허용 |
| 확장자 html, json | 명확한 목적 없으면 긴 캐시 금지 |
캐시가 잘 작동하면 R2까지 도달하는 GET 요청, 즉 Class B operation을 크게 줄일 수 있습니다. 커뮤니티에서는 매우 높은 캐시 히트율을 구성해 수억 요청 중 R2 원본까지 도달하는 요청을 극히 낮게 만든 사례가 공유됩니다. 다만 이런 수치는 사이트 구조, 파일명 전략, 캐시 규칙, 사용자 지역, Cloudflare 플랜에 따라 달라지므로 그대로 보장되는 값은 아닙니다.
Smart Tiered Cache 활성화
Smart Tiered Cache는 Cloudflare가 R2 오리진에 가까운 상위 데이터센터를 경유하게 해 원본 히트를 줄이는 방식입니다. 대시보드에서는 Caching 메뉴의 Tiered Cache에서 Smart Tiered Cache를 활성화할 수 있습니다.
효과는 다음과 같습니다.
| 효과 | 설명 |
|---|---|
| 원본 접근 감소 | 여러 엣지가 같은 R2 원본을 직접 때리는 상황을 줄임 |
| 캐시 효율 개선 | 상위 tier가 중간 캐시 역할을 함 |
| R2 Class B 감소 가능 | 캐시 hit가 늘면 R2 GET/HEAD 원본 요청이 줄어듦 |
| 글로벌 지연 안정화 | 사용자 근처 엣지와 상위 캐시 조합으로 편차를 줄일 수 있음 |
이미지 서버가 커질수록 Smart Tiered Cache는 켜두는 편이 좋습니다. 특히 assets 도메인이 전 세계에서 조회되는 구조라면 R2 원본을 보호하는 완충층 역할을 합니다.
CORS 설정 — 브라우저 접근 허용
브라우저가 R2 파일을 직접 읽거나 presigned URL로 업로드하려면 CORS 정책이 필요합니다. 단순히 img 태그로 공개 이미지를 보여 주는 것만으로는 CORS가 항상 필요한 것은 아니지만, JavaScript fetch, canvas 처리, presigned PUT 업로드에는 필요해지는 경우가 많습니다.
권장 CORS 정책 개념:
| 항목 | 예시 |
|---|---|
| AllowedOrigins | https://vibecoding365days.com, http://localhost:3000 |
| AllowedMethods | GET, PUT, HEAD |
| AllowedHeaders | Content-Type, Cache-Control |
| ExposeHeaders | ETag |
| MaxAgeSeconds | 3600 |
중요한 실수는 AllowedOrigins에 경로를 넣는 것입니다. Origin은 스킴, 호스트, 포트까지만 포함합니다.
| 잘못된 예 | 올바른 예 |
|---|---|
| https://example.com/uploads | https://example.com |
| https://vibecoding365days.com/qna | https://vibecoding365days.com |
| localhost:3000 | http://localhost:3000 |
운영 팁은 개발 origin과 운영 origin을 분리해 명시하는 것입니다. 모든 origin을 허용하는 별표는 공개 다운로드에는 편할 수 있지만, 사용자 업로드나 인증 흐름에는 신중해야 합니다.
Lifecycle Rules와 Infrequent Access 운영법
R2에는 객체를 일정 기간 뒤 자동 삭제하거나 Standard에서 Infrequent Access로 전환하는 lifecycle rule이 있습니다. 파일 서버를 오래 운영하면 임시 업로드, 로그, AI 산출물, 오래된 백업이 계속 쌓이므로 lifecycle이 중요합니다.
자동 삭제 규칙
예를 들어 logs/ 아래 파일은 90일 뒤 삭제하도록 만들 수 있습니다. 대시보드에서는 버킷 Settings의 Object Lifecycle Rules에서 Add rule을 선택하고 prefix와 만료 기간을 지정합니다.
권장 삭제 규칙:
| prefix | 권장 보관 기간 | 이유 |
|---|---|---|
| tmp/uploads/ | 1~7일 | 업로드 실패와 검수 전 임시 파일 누적 방지 |
| artifacts/hermes/browser-snapshots/ | 30~90일 | QA 스크린샷은 오래 보관할 필요가 적음 |
| logs/ | 30~180일 | 운영 로그 보존 정책에 따라 결정 |
| rejected/ | 7~30일 | 거절된 업로드는 짧게 보관 |
| published images | 자동 삭제 비추천 | 글 이미지가 깨질 수 있음 |
가장 중요한 원칙은 공개 글에 이미 연결된 이미지는 lifecycle로 삭제하지 않는 것입니다. 발행된 포스트 이미지가 사라지면 SEO, 사용자 경험, 아카이브 품질이 모두 나빠집니다.
Infrequent Access 전환
Infrequent Access는 저장 단가가 낮지만 읽을 때 retrieval 비용이 있고 30일 최소 보관 기간이 있습니다. 그래서 무조건 싸지 않습니다.
| 적합한 파일 | 부적합한 파일 |
|---|---|
| 오래된 백업 | 자주 읽히는 대표 이미지 |
| 거의 열지 않는 원본 이미지 | 홈페이지 썸네일 |
| 장기 보관용 zip | 인기 글 본문 이미지 |
| 30일 이상 확실히 보관할 파일 | 며칠 뒤 삭제할 임시 파일 |
30일 전에 삭제해도 30일분 비용이 청구될 수 있으므로, 임시 파일을 Infrequent Access로 보내는 것은 보통 좋지 않습니다. 임시 파일은 짧게 삭제하고, 장기 보관 파일만 IA 전환을 검토합니다.
버킷당 rule 수와 운영 원칙
공식 제한 기준으로 lifecycle rule은 버킷당 최대 1,000개까지 가능합니다. 하지만 rule이 많다고 좋은 것은 아닙니다. prefix 설계를 잘해서 적은 rule로 넓게 관리하는 편이 낫습니다.
좋은 설계:
| prefix | rule |
|---|---|
| tmp/ | 7일 삭제 |
| logs/ | 90일 삭제 |
| artifacts/ | 180일 삭제 또는 IA 전환 |
| backups/monthly/ | 30일 후 IA 전환 |
나쁜 설계는 글 slug마다 lifecycle rule을 따로 만드는 것입니다. 나중에 rule 관리가 어려워집니다. 처음부터 prefix 정책을 크게 잡는 것이 운영에 유리합니다.
Event Notifications와 Workers Binding 자동화
R2는 단순 파일 저장소를 넘어 이벤트 기반 자동화를 만들 수 있습니다. 오브젝트가 생성되거나 삭제될 때 Cloudflare Queues로 이벤트를 보내고, Consumer Worker가 후속 작업을 처리하는 구조입니다.
Event Notifications
업로드 이벤트를 Queue로 보내면 다음 작업을 자동화할 수 있습니다.
| 이벤트 | 자동화 예시 |
|---|---|
| object-create | 이미지 썸네일 생성, DB metadata 기록, 감사 로그 저장 |
| object-delete | DB에서 공개 URL 제거, 검색 인덱스 정리 |
| 특정 suffix .jpg | jpg 업로드만 리사이징 queue로 전달 |
| 특정 prefix uploads/ | 사용자 업로드만 검수 pipeline으로 전달 |
이 구조는 AWS의 S3 Event Notification과 SNS/SQS/Lambda 조합에 가깝게 이해하면 됩니다. Cloudflare에서는 R2 이벤트가 Queue로 들어가고, Worker가 메시지를 소비합니다.
이벤트 메시지에는 보통 계정, action, bucket, object key, object size, eTag, eventTime 같은 정보가 들어갑니다. 이 정보로 어떤 파일이 올라왔는지 파악하고 후속 처리를 시작할 수 있습니다.
헤르메스 홈페이지에서 유용한 활용:
| 상황 | 처리 |
|---|---|
| 사용자가 이미지를 업로드 | Queue 이벤트로 검수 작업 시작 |
| 운영자가 글 이미지를 올림 | 자동으로 썸네일 생성 후보 등록 |
| AI가 QA 스크린샷 업로드 | artifacts DB에 key와 URL 기록 |
| 다운로드 파일 교체 | checksum 생성 및 문서 갱신 알림 |
Workers Binding
Cloudflare Workers에서는 R2 버킷을 binding으로 연결해 S3 API 없이 직접 접근할 수 있습니다. wrangler 설정의 r2_buckets에 binding 이름과 bucket_name을 지정한 뒤, Worker 코드에서 env.MY_BUCKET.get, env.MY_BUCKET.put 같은 API를 쓰는 방식입니다.
개념 구조:
| 구성 | 역할 |
|---|---|
| Worker route | 브라우저 요청을 받는 서버리스 endpoint |
| R2 binding | Worker에서 버킷에 직접 접근하는 연결 |
| get | 객체 읽기 |
| put | 객체 쓰기 |
| head | metadata 확인 |
| delete | 객체 삭제 |
| list | prefix 목록 조회 |
Workers Binding의 장점은 Cloudflare 네트워크 내부에서 R2에 접근한다는 점입니다. 인증 프록시, private 파일 다운로드, 이미지 접근 제어, 간단한 리다이렉트, signed cookie 검증 같은 일을 Worker에서 처리하고 R2를 원본 저장소로 둘 수 있습니다.
예를 들어 비공개 파일 다운로드는 이렇게 설계할 수 있습니다.
| 단계 | 설명 |
|---|---|
| 1 | 사용자가 /download/file-id로 접근 |
| 2 | Worker가 쿠키나 토큰을 확인 |
| 3 | 권한이 있으면 R2 private bucket에서 객체 get |
| 4 | Content-Type과 Cache-Control을 붙여 응답 |
| 5 | 권한이 없으면 403 반환 |
이 구조를 쓰면 private bucket을 공개하지 않고도 필요한 사람에게만 파일을 제공할 수 있습니다.
R2 이벤트와 Hermes Agent 조합
Event Notifications와 Hermes를 조합하면 파일 서버가 자동 운영 체계가 됩니다.
| 흐름 | 설명 |
|---|---|
| 업로드 발생 | R2 object-create 이벤트가 Queue로 들어감 |
| Worker 처리 | 파일 크기, 타입, prefix를 검사하고 작업 요청 생성 |
| Hermes 실행 | 썸네일 생성, 메타데이터 정리, 문서 업데이트 초안 작성 |
| 사람 승인 | 공개 페이지 반영 전 최종 확인 |
| 배포 | Git commit/push 또는 DB 업데이트 |
이렇게 하면 파일 업로드가 단순 저장으로 끝나지 않고, 사이트 운영 작업으로 이어집니다. 특히 헤르메스 홈페이지처럼 AI 에이전트가 콘텐츠와 이미지 자산을 계속 생성하는 사이트에서는 이 이벤트 기반 구조가 장기적으로 유리합니다.
커뮤니티 후기와 레딧 평판 종합
공식 문서만 보면 R2는 거의 완벽해 보입니다. 하지만 실제 운영에서는 커뮤니티 후기가 중요합니다. 공개 커뮤니티와 레딧 후기를 종합하면 평가는 대체로 긍정적이지만, 결제와 고객지원에 대한 불만은 반복적으로 보입니다. 아래 내용은 특정 개인 사례를 그대로 보장된 사실로 받아들이기보다, 운영자가 참고할 리스크 신호로 보는 것이 좋습니다.
긍정적 평가
긍정적 후기는 주로 비용 절감과 안정성에 집중됩니다.
| 긍정 포인트 | 커뮤니티에서 자주 나오는 이야기 |
|---|---|
| S3 대비 비용 절감 | 비용의 대부분이 egress였던 서비스는 R2 이전 후 비용이 크게 줄었다는 사례가 많음 |
| 이미지 많은 사이트에 적합 | 뉴스 사이트, 블로그, 다운로드 서비스에서 사진/정적 파일 서빙 비용 절감 사례가 공유됨 |
| 캐시와 결합 시 원본 요청 감소 | 인기 파일은 Cloudflare 캐시가 처리해 R2 Class B 요청도 낮게 유지 가능하다는 후기 |
| 로그와 모니터링 저장 | Loki, Prometheus, 백업성 데이터 저장소로 쓰는 사례가 있음 |
| 확장성 체감 | 대규모 동시 요청에서 병목은 R2보다 앱 레이어나 캐시 설정인 경우가 많다는 의견 |
이미지 서버 관점에서는 이 후기가 중요합니다. 헤르메스 홈페이지의 글 이미지, 스크린샷, 다운로드 자료처럼 "저장량보다 읽기 트래픽이 더 걱정되는" 워크로드라면 R2의 장점이 크게 살아납니다.
부정적 평가와 가장 큰 리스크
가장 반복적으로 언급되는 불만은 고객지원과 결제 문제입니다. 일부 사용자는 결제 실패, 카드 문제, 청구 상태 오류가 생겼을 때 R2나 다른 Cloudflare 서비스 접근이 빠르게 제한됐고 지원 응답이 늦었다고 보고합니다.
운영자가 받아들여야 할 교훈은 다음입니다.
| 리스크 | 대응 |
|---|---|
| 카드 만료 또는 결제 실패 | 주 결제수단과 예비 결제수단 관리, 만료일 알림 설정 |
| 무료 플랜 지원 한계 | 중요한 서비스라면 유료 플랜과 지원 수준 검토 |
| 갑작스러운 접근 제한 | 백업과 exit 전략 유지 |
| Cloudflare 계정 단일 장애점 | 도메인, DNS, R2, Workers가 모두 한 계정에 묶이는 리스크 인식 |
| 청구 분쟁 장기화 | 핵심 파일은 다른 스토리지에도 복제 가능하게 유지 |
AWS와 비교할 때 일부 커뮤니티 사용자는 "AWS는 결제 문제가 있어도 경고와 유예가 긴 편인데, Cloudflare는 더 빠르게 제한될 수 있다"고 느낍니다. 이 평가는 개인 경험에 따라 다르지만, 운영자는 최악의 상황을 가정해야 합니다.
R2 평판 종합 점수
| 항목 | 평가 | 이유 |
|---|---|---|
| 안정성/신뢰성 | 높음 | 일반적인 객체 저장과 공개 파일 서빙 후기는 대체로 좋음 |
| 이그레스 많은 워크로드 비용 효율 | 매우 높음 | S3 대비 비용 절감 사례 다수 |
| 순수 저장 비용 | 보통 | Backblaze B2, Wasabi보다 저장 단가는 높을 수 있음 |
| S3 호환성 | 높음 | 주요 SDK와 도구는 잘 맞지만 일부 S3 기능은 없음 |
| 성능 | 높음 | 커스텀 도메인과 Cloudflare Cache 결합 시 강함 |
| 고객 지원 | 낮음~보통 | 무료/저가 플랜에서는 기대치를 낮춰야 함 |
| 결제 장애 대응 | 주의 필요 | 커뮤니티에서 반복적으로 리스크가 언급됨 |
| 확장성 | 높음 | 이미지/정적 파일 대규모 서빙에 적합 |
결론은 단순합니다. R2는 기술적으로 훌륭하지만, 결제수단 관리와 백업 전략 없이 핵심 파일을 단독 보관하는 것은 좋지 않습니다. egress가 무료이므로 다른 스토리지로 백업을 빼는 비용 부담이 작다는 점을 적극 활용해야 합니다.
경쟁사 비교 상세 — 비용, egress, 기능
R2의 경쟁력은 저장 단가 하나가 아니라 저장 비용, 요청 비용, egress, CDN 결합, 운영 난이도를 합친 결과입니다.
저장 비용 비교
| 서비스 | Standard 저장 단가 감각 | 저빈도 접근 | 아카이브 |
|---|---|---|---|
| Cloudflare R2 | GB-month당 0.015달러 | IA 0.01달러 | 딥 아카이브 없음 |
| AWS S3 | 대략 0.023달러 수준 | S3-IA 제공 | Glacier/Deep Archive 제공 |
| Backblaze B2 | 대략 0.006달러 수준 | 단순 구조 | 별도 딥 아카이브보다는 저가 저장 중심 |
| Wasabi | 대략 0.005~0.006달러 수준 | 단순 구조 | 최소 보관/egress 정책 확인 필요 |
| Azure Blob | Hot/Cool/Archive 구분 | Cool 제공 | Archive 제공 |
| Google Cloud Storage | Standard/Nearline/Coldline/Archive | 다양 | Archive 제공 |
순수 백업 저장만 보면 R2가 항상 가장 싸지는 않습니다. Backblaze B2나 Wasabi가 더 저렴할 수 있습니다. 하지만 공개 파일을 많이 읽는 순간 egress 비용이 결정적인 차이를 만듭니다.
10TB 전송 시 egress 비용 감각
| 서비스 | 10TB 인터넷 전송 비용 감각 |
|---|---|
| Cloudflare R2 | 0달러 |
| Backblaze B2 | 무료 제공 범위 이후 GB당 과금 가능 |
| Wasabi | 정책상 저장량 대비 egress 제한과 fair use 확인 필요 |
| AWS S3 | 수백 달러~약 900달러 수준 가능 |
| Azure Blob | 수백 달러 이상 가능 |
| Google Cloud Storage | 수백~천 달러 이상 가능 |
정확한 금액은 리전, 플랜, 할인, CDN 조합에 따라 달라집니다. 중요한 것은 이미지 서버처럼 트래픽 예측이 어려운 서비스에서 R2가 egress 비용 리스크를 크게 줄인다는 점입니다.
기능 비교
| 기능 | R2 | S3 | Backblaze B2 |
|---|---|---|---|
| S3 API 호환 | 높음 | 네이티브 | 높음 |
| Object Lock/WORM | 미지원 또는 제한적 | 강력 | 지원 여부 플랜/설정 확인 |
| 스토리지 클래스 | Standard, Infrequent Access | 매우 다양 | 단순 |
| 자동 티어링 | 제한적 | Intelligent-Tiering | 제한적 |
| 딥 아카이브 | 없음 | Glacier 계열 | 별도 구조 |
| CDN 결합 | Cloudflare와 자연스럽게 결합 | CloudFront 별도 설계 | 별도 CDN 설계 |
| 커스텀 도메인 | R2 public bucket에서 지원 | S3/CloudFront 조합 필요 | 별도 프록시/CDN 필요 |
| 이벤트 알림 | Queues 연동 | SNS/SQS/Lambda/EventBridge | webhook 등 방식 확인 |
| 서버리스 연동 | Workers Binding | Lambda | 직접 결합은 약함 |
규정 준수, Object Lock, 딥 아카이브가 중요하면 S3가 더 맞을 수 있습니다. 반대로 공개 이미지와 다운로드 파일이 많고 Cloudflare를 이미 쓰고 있다면 R2가 단순하고 경제적입니다.
현재 프로젝트 적용 비용 시나리오
헤르메스 홈페이지가 이미지 자동 생성, 글 발행, 스크린샷 저장, 다운로드 자료 배포를 한다고 가정하면 R2는 매우 적합합니다.
대략적인 저장량별 감각은 다음과 같습니다. 무료 10GB를 제외한 단순 저장 비용 감각이며, 실제 비용은 요청 수, 캐시 hit, IA 사용 여부에 따라 달라집니다.
| 규모 | 예상 저장량 | R2 저장 비용 감각 | S3 계열에서 주의할 점 |
|---|---|---|---|
| 현재 소규모 | 20GB 안팎 | 무료 한도 초과분 기준 매우 낮음 | 트래픽이 붙으면 egress가 비용 핵심 |
| 이미지 2,000장 | 30~60GB | 월 1달러 미만~소액 가능 | 이미지 조회가 많으면 전송 비용 증가 |
| 이미지 10,000장 | 100~300GB | 월 몇 달러 수준 가능 | 인기 글이 터지면 egress 비용 급증 가능 |
| 자료실 운영 | PDF/zip 수십~수백GB | 저장 비용은 예측 쉬움 | 다운로드 트래픽이 핵심 변수 |
| 영상 원본 저장 | 수백GB~TB | 저장 비용 증가 | R2가 영상 플랫폼은 아니므로 Stream 등 검토 |
R2의 가치는 "한 번 저장하고 많이 읽는" 워크로드에서 큽니다. 헤르메스 홈페이지의 글 이미지와 AI 산출물은 이 패턴에 가깝습니다.
추천 운영 결론
| 항목 | 추천 |
|---|---|
| 공개 이미지 | R2 Standard + custom domain + 긴 캐시 |
| 오래된 백업 | R2 IA 또는 Backblaze B2 비교 후 결정 |
| 사용자 업로드 원본 | R2 private bucket + presigned URL |
| 공개 전환 | 검수 후 public bucket으로 copy |
| AI 산출물 | artifacts prefix에 저장, 민감정보 확인 후 공개 |
| 결제 리스크 | 카드 만료 알림, 예비 결제수단, 월 1회 청구 확인 |
| Exit 전략 | rclone copy로 다른 스토리지에 정기 백업 |
가장 중요한 운영 원칙은 세 가지입니다.
- 공개 파일과 비공개 파일을 버킷 단위로 분리합니다.
- 이미지 URL은 버전 파일명과 긴 캐시를 전제로 설계합니다.
- Cloudflare 계정/결제 문제가 생겨도 복구할 수 있도록 정기 백업과 대체 경로를 둡니다.
최종 체크리스트
R2를 헤르메스 홈페이지 파일 및 이미지 서버로 도입하기 전 체크리스트입니다.
계정과 버킷
| 체크 | 내용 |
|---|---|
| □ | Cloudflare R2 활성화 완료 |
| □ | 결제수단과 무료 한도 확인 |
| □ | public assets 버킷 생성 |
| □ | private uploads 버킷 필요 여부 결정 |
| □ | agent artifacts 버킷 필요 여부 결정 |
도메인과 공개 설정
| 체크 | 내용 |
|---|---|
| □ | r2.dev는 개발 테스트용으로만 사용 |
| □ | assets.vibecoding365days.com 같은 커스텀 도메인 연결 |
| □ | 공개 버킷에는 공개 가능 파일만 저장 |
| □ | Cache Rules 적용 여부 확인 |
| □ | WAF 또는 Rate Limiting 필요 여부 확인 |
업로드와 파일 규칙
| 체크 | 내용 |
|---|---|
| □ | 파일명은 영문 소문자, 하이픈, 날짜/버전 규칙 사용 |
| □ | 이미지 업로드 전 webp/avif 압축 |
| □ | Cache-Control 헤더 설정 |
| □ | 업로드 후 HEAD 요청으로 검증 |
| □ | public URL 목록을 문서 또는 DB에 기록 |
보안
| 체크 | 내용 |
|---|---|
| □ | R2 Secret을 Git에 저장하지 않음 |
| □ | Secret은 환경변수 또는 secret store에만 저장 |
| □ | 토큰 권한은 특정 버킷으로 제한 |
| □ | 브라우저에는 Secret을 절대 전달하지 않음 |
| □ | 사용자 업로드는 private bucket + presigned URL + 검수 구조 사용 |
운영
| 체크 | 내용 |
|---|---|
| □ | 매월 저장 용량과 요청 수 확인 |
| □ | tmp prefix lifecycle 설정 |
| □ | 깨진 이미지 URL 점검 자동화 |
| □ | 큰 파일 상위 목록 정기 점검 |
| □ | Hermes cron으로 R2 운영 리포트 받기 |
최종 판단은 간단합니다.
헤르메스 홈페이지가 글, 이미지, 자료실, AI 산출물을 계속 쌓아갈 계획이라면 R2는 지금 붙여도 좋은 파일 서버입니다. 처음에는 public assets 버킷 하나와 커스텀 도메인으로 시작하고, 사용자 업로드가 생기면 private bucket과 presigned URL 구조로 확장하면 됩니다.
참고한 공식 문서
이 글은 2026년 4월 기준 Cloudflare 공식 문서와 공개 자료를 바탕으로 정리했습니다. Cloudflare의 가격, 제한, 기능은 변경될 수 있으므로 실제 도입 전에는 최신 문서를 다시 확인해야 합니다.
| 주제 | 문서 |
|---|---|
| R2 개요 | https://developers.cloudflare.com/r2/ |
| 가격과 무료 한도 | https://developers.cloudflare.com/r2/pricing/ |
| Public buckets, custom domains, r2.dev | https://developers.cloudflare.com/r2/buckets/public-buckets/ |
| S3 API 호환성 | https://developers.cloudflare.com/r2/api/s3/api/ |
| Presigned URLs | https://developers.cloudflare.com/r2/api/s3/presigned-urls/ |
| Workers API | https://developers.cloudflare.com/r2/api/workers/workers-api-reference/ |
| 플랫폼 제한 | https://developers.cloudflare.com/r2/platform/limits/ |
| Lifecycle rules | https://developers.cloudflare.com/r2/buckets/object-lifecycles/ |
| Storage classes | https://developers.cloudflare.com/r2/buckets/storage-classes/ |