블로그 목록으로
개발

Cloudflare R2 도입기: 이미지를 코드에서 꺼내다

2025년 12월 13일·7 분 소요

한동안 이미지 파일을 그냥 프로젝트 안에 넣어두고 있었습니다. `public/images` 폴더에 심리테스트 결과 이미지, 게임 썸네일, 각종 아이콘이 조금씩 쌓이다 보니 어느 순간부터 저장소 용량이 부담스러워졌습니다. 클론 받는 데만 시간이 걸리고, CI/CD 파이프라인도 괜히 무거운 느낌이었죠. 언젠가는 분리해야겠다고 생각하면서도 계속 미루다가, 결국 직접 손을 댔습니다.

R2를 고른 이유: 비용 비교 + 같은 생태계

저장소 후보로는 AWS S3, Cloudinary, Cloudflare R2 정도가 떠올랐습니다. 각각 어느 정도 알고는 있었지만, 직접 사용해본 적이 없으니 비용이나 한계치가 정확히 감이 안 왔습니다. 그래서 이번에도 Gemini에게 물어봤습니다. 각 서비스의 장단점과 대략적인 비용 구조를 정리해달라고 했더니, R2가 소규모 사이드 프로젝트 기준으로는 가장 저렴한 선택지라는 결론이 나왔습니다.

거기다 이미 Cloudflare Pages로 배포하고 있었습니다. 같은 Cloudflare 대시보드에서 관리하면 설정도 자연스럽게 이어질 것 같았고, 콘텐츠를 동일 네트워크 안에서 제공하니 지연도 줄 것 같았습니다. 결정은 금방 났습니다.

설정과 이전 작업: 스크린샷 + AI + 스크립트

R2 버킷 생성부터 공개 도메인 설정까지는 3편 Cloudflare Pages, 5편 Firebase 때와 같은 방식으로 진행했습니다. 대시보드 화면을 스크린샷으로 찍어서 Gemini에게 "여기서 다음 뭐 하면 돼?"라고 물어가면서 하나씩 진행하는 방식입니다. 이렇게 하면 처음 써보는 서비스라도 막히는 일 없이 설정이 됩니다. AI가 있으면 왠만한 건 다 가능한 시대라는 걸 이번에도 다시 느꼈습니다.

문제는 이미지를 옮기는 작업이었습니다. `public/images`에 파일이 꽤 많이 쌓여 있었는데, 하나씩 업로드하면 시간이 오래 걸릴 것 같았습니다. Gemini에게 일괄 업로드 스크립트를 만들어달라고 했더니, R2 API를 활용해서 폴더 구조 그대로 업로드해 주는 스크립트를 만들어줬습니다. 스크립트를 실행하고 나서 코드 안에 하드코딩된 이미지 경로를 R2 URL로 일괄 치환하는 작업도 마찬가지로 부탁해서 처리했습니다.

예상치 못한 문제들

이전 작업 자체는 생각보다 빠르게 끝났습니다. 하지만 그 뒤에 잡일들이 좀 있었습니다. 첫 번째는 로컬 개발 환경에서 이미지가 안 보이는 문제였습니다. R2 CORS 정책에 `localhost`가 빠져 있었던 게 원인이었습니다. CORS 정책에 `localhost`를 추가하고 나서 해결됐습니다.

두 번째 문제는 심리테스트 결과 이미지 저장 기능에서 발생한 CORS 캐시 이슈였습니다. 결과 카드를 이미지로 저장하는 기능이 R2 이전 후 갑자기 오류를 내기 시작했습니다. 까다로운 건 브라우저가 예전의 CORS 응답을 캐시해두고 있어서, 설정을 바꿔도 캐시가 살아 있는 동안은 오류가 계속 났다는 것입니다. 이 부분은 AI가 원인을 짚어주고 해결까지 도와줬습니다.

깃 히스토리에 이미지 파일들이 아직 남아 있습니다. 히스토리를 정리하는 방법이 없는 건 아닌데, 자칫하면 다른 커밋이 망가질 수 있어서 건드리지 않고 그냥 두고 있습니다. 신경은 쓰이지만, 어쩔 수 없다고 생각합니다.

프로젝트가 가벼워졌다

R2로 이전하고 나서 가장 먼저 체감한 건 클론 속도였습니다. 이미지 파일들이 저장소에서 빠지고 나니 전체 용량이 눈에 띄게 줄었습니다. CI/CD도 조금 가벼워진 느낌이었습니다. 작은 차이일 수 있지만, 개발 중에 이미지 때문에 불필요하게 기다리는 일이 없어지니 확실히 편했습니다.

처음부터 R2를 썼으면 좋았겠지만

처음부터 이미지를 외부 저장소에 올렸다면 깃 히스토리가 훨씬 깔끔했을 겁니다. 하지만 Cloudflare를 처음 써봤고, 비용 면에서 어디를 써야 할지 처음엔 판단이 안 됐습니다. 사이드 프로젝트를 한동안 굴려보면서 비용 구조를 실제로 겪고 나서야 비교가 가능했던 거라, 처음부터 R2를 선택하지 못한 건 어쩔 수 없었다고 생각합니다.

지금이라도 R2로 넘어와서 프로젝트를 경량화시킨 게 잘한 일이라고는 생각합니다. 사이드 프로젝트란 원래 이렇게 만들면서 고쳐가는 거라고 생각하기로 했습니다. 히스토리에 남은 이미지는 그냥 당시의 흔적이려니 합니다.

다음에 새 프로젝트를 시작한다면, 이미지 저장소는 처음부터 분리해 둘 것 같습니다. 이번 경험이 그 정도 교훈은 남겨줬습니다.

다른 글 더 보기