웹서비스 외부 백업: S3에 올린 사본이 같이 지워지지 않게 하는 방법
매일 밤 서버에서 이런 명령이 돈다고 해 보겠습니다.
rclone sync /data/uploads s3:company-backup/uploadsrclone 문서는 sync 를 이렇게 설명합니다.
Destination is updated to match source, including deleting files if necessary.
원본 폴더가 실수로 비워지거나 랜섬웨어에 암호화된 날 밤에도 이 명령은 성실하게 돕니다. 다음 날 아침 S3에는 비워진 폴더가, 혹은 암호화된 파일이 똑같이 올라가 있습니다. 백업은 매일 성공했고, 정작 필요한 순간에 원본과 함께 망가졌습니다.
"S3에 올렸다"와 "외부 백업"이 다른 이유
흔히 외부 백업을 "서버 밖 다른 곳에 사본을 두는 것"으로 이해합니다. 그러면 위 상황이 설명되지 않습니다. 사본은 분명 다른 곳에 있었습니다. 문제는 위치가 아니라 권한이었습니다. 서버가 가진 키로 백업을 덮어쓰고 지울 수 있다면, 서버에 일어난 일은 백업에도 일어납니다.
그래서 저는 외부 백업이 되려면 조건이 셋 필요하다고 봅니다. 운영과 다른 곳에 있어야 하고, 운영의 키로는 지울 수 없어야 하고, 돌아갈 과거 시점이 여러 개 있어야 합니다.
흔히 인용하는 3-2-1 원칙은 이 중 첫째만 말합니다. US-CERT 문서의 원문은 사본 3개, 서로 다른 매체 2종, 그중 1개는 사업장 밖에 두라는 데서 끝납니다. Veeam은 여기에 변경할 수 없는 사본 1개와 복원 오류 0건을 더해 3-2-1-1-0이라고 부릅니다. 저는 이 확장이 나머지 두 조건을 채우려는 것으로 읽습니다.
업체 자동 백업이 지켜 주지 못하는 자리
관리형 DB는 대부분 자동 백업을 켜 둡니다. 기본값을 보면 생각보다 짧습니다.
| 서비스 | 기본 자동 백업 | 시점 복구(PITR) | 알아 둘 점 |
|---|---|---|---|
| AWS RDS | 콘솔로 만들면 7일, API·CLI로 만들면 1일 보관(최대 35일) | 보관 기간 안의 임의 시점. 로그는 5분마다 S3로 | 복원은 항상 새 인스턴스로 |
| Supabase | Free 없음, Pro 7일, Team 14일 | Pro 이상에서 유료 부가 기능 | Storage에 올린 파일은 DB 백업에 들어가지 않음 |
| PlanetScale Postgres | 12시간마다, 2일 보관 | 2일 전부터 5분 전까지 | 백업이 클러스터와 같은 클라우드·리전에 저장됨 |
출처는 각 사 문서입니다(RDS 보관 기간, RDS 시점 복구, Supabase, PlanetScale).
이 백업들은 쓸모가 큽니다. 실수로 테이블을 지웠을 때 가장 빨리 돌아가는 길입니다. 다만 외부 백업은 아닙니다. PlanetScale 문서는 백업이 클러스터와 같은 클라우드 업체, 같은 리전에 저장된다고 적고 있습니다. 계정이 잠기거나 결제가 끊기거나 그 리전에 문제가 생기면 DB와 백업에 한꺼번에 손이 닿지 않습니다. 보관 기간이 이틀이면, 금요일 밤에 생긴 문제를 월요일 아침에 발견했을 때 돌아갈 시점이 이미 없습니다.
Supabase 칸의 마지막 줄도 봐야 합니다. 공식 문서는 Storage API로 저장한 객체가 DB 백업에 포함되지 않는다고 적습니다. 사용자가 올린 사진과 첨부 파일은 따로 챙겨야 합니다.
잃어도 되는 시간으로 고르는 덤프와 PITR
DB를 밖으로 빼는 방법은 크게 둘입니다.
하나는 논리 덤프입니다. pg_dump 는 DB를 파일 하나로 뽑습니다. PostgreSQL 문서에 따르면 DB를 쓰는 중에도 일관된 시점으로 뽑고, 다른 사용자의 읽기·쓰기를 막지 않습니다. 파일 하나라 다루기 쉽고, 다른 업체의 Postgres에 그대로 부어 넣을 수 있습니다. 대신 덤프와 덤프 사이의 변경은 잃습니다. 하루 한 번 뜨면 최대 하루치를 잃습니다.
다른 하나는 WAL 아카이빙입니다. 기본 백업 한 벌에 WAL 파일을 계속 이어 붙여 두면, 기본 백업 이후 아무 시점으로나 되돌릴 수 있습니다. 직접 운영한다면 pgBackRest나 WAL-G가 이 일을 합니다. 둘 다 S3 호환 저장소에 올리고, 전체·증분 백업과 암호화를 지원합니다. 관리형 DB에서는 업체가 이 방식을 대신 돌리고, 그것이 위 표의 PITR입니다.
저는 이 선택을 기술 취향이 아니라 "몇 시간치 데이터가 사라져도 사업이 버티는가"로 봅니다. 하루치를 잃어도 다시 입력할 수 있는 사내 시스템이면 매일 덤프로 충분합니다. 주문과 결제가 쉬지 않고 들어오는 서비스라면 PITR을 켜고, 그 위에 덤프를 외부로 한 번 더 뺍니다. PITR은 업체 안에 있고 덤프는 업체 밖으로 나가는 사본이라, 둘은 서로를 대신하지 않습니다.
DB 백업에 들어 있지 않은 업로드 파일과 키
업로드 파일이 객체 스토리지에 있다면 가장 싼 보호 장치는 버전 관리입니다. S3에서 버전 관리를 켜면 삭제가 파일을 지우는 대신 삭제 표시를 하나 얹고, 덮어써도 이전 버전이 남습니다. 대신 버전마다 차이분이 아니라 파일 전체 크기로 과금됩니다. 1GB짜리 파일을 매일 덮어쓰면 30일 뒤 30GB가 쌓이므로, 수명 주기 규칙으로 오래된 버전을 정리해 둡니다.
키는 반대로 백업과 떨어져 있어야 합니다. restic은 백업을 기본으로 암호화하고, pgBackRest도 저장소 암호화를 켤 수 있습니다. 그 암호를 백업과 같은 버킷에 두면 암호화한 의미가 없고, 암호를 잃으면 백업이 있어도 열지 못합니다. 앱이 DB 안의 일부 값을 암호화해 저장한다면 그 키도 백업 목록에 들어가야 합니다. DB를 완벽히 되살려도 키가 없으면 그 값만 영영 읽히지 않습니다.
운영 키로 지울 수 없게 만드는 세 가지 장치
첫째는 S3의 Object Lock입니다. 정해 둔 기간 동안 객체를 덮어쓰거나 지울 수 없게 막습니다. 컴플라이언스 모드에서는 루트 사용자를 포함해 누구도 기간 전에 지우지 못합니다. 거버넌스 모드는 별도 권한과 우회 헤더를 가진 사용자만 지울 수 있습니다. Object Lock은 버전 관리가 켜진 버킷에서만 동작하고, 한 번 켜면 끌 수 없습니다.
둘째는 올리기만 되는 경로입니다. 서버에 주는 키에는 업로드 권한만 넣고 삭제 권한은 뺍니다. 버전 관리가 켜진 버킷이라면 같은 이름으로 덮어써도 이전 버전이 남습니다. restic을 쓴다면 rest-server를 --append-only 로 띄우는 방법도 있습니다. 새 백업은 받되 기존 백업의 삭제와 수정은 거부합니다.
셋째는 계정을 가르는 것입니다. 백업을 운영과 다른 계정, 가능하면 다른 업체에 둡니다. AWS 안에서라면 AWS Backup Vault Lock의 컴플라이언스 모드가 있습니다. 최소 72시간의 유예 기간이 지나면 어떤 사용자도, AWS도 잠금을 바꾸거나 지울 수 없습니다.
하나 놓치기 쉬운 곳이 있습니다. S3 문서에 따르면 버킷 정책으로도 수명 주기 규칙이 하는 삭제는 막지 못합니다. 운영 키가 수명 주기 규칙을 고칠 수 있다면 "보관 기간 1일" 규칙 하나로 백업을 비울 수 있습니다. 규칙을 바꾸는 권한도 운영 키에서 떼어 둡니다.
잠금 기능에서 갈리는 외부 저장소 다섯 가지
| 저장소 | 저장 가격(GB·월) | 꺼낼 때 비용 | 버전 관리 | 삭제 잠금 | 서울 리전 |
|---|---|---|---|---|---|
| AWS S3 Standard | 서울 $0.025 | 서울 GB당 $0.126(전 리전 합산 월 100GB 무료) | ● | Object Lock(거버넌스·컴플라이언스) | ● |
| AWS S3 Glacier Deep Archive | 서울 $0.002 | GB당 $0.005~0.022, 복원까지 12~48시간 | ● | Object Lock | ● |
| Cloudflare R2 | $0.015 | 무료 | ✕ | 버킷 잠금(기간·날짜·무기한) | ✕ |
| Backblaze B2 | TB당 $6.95 | 저장량의 3배까지 무료, 넘으면 GB당 $0.01 | ● | Object Lock(거버넌스·컴플라이언스) | ✕ |
| 네이버 클라우드 Object Storage | 확인 못 함 | 확인 못 함 | 일본 리전만 | 한국 리전은 단일 모드, 0~365일 | ● |
2026년 9월 11일에 확인한 값입니다(S3 가격, Deep Archive 복원, R2 가격, R2 버킷 잠금, B2 가격, B2 Object Lock, 네이버 클라우드 버킷 설정).
이 표에서 먼저 볼 것은 저장 가격이 아니라 오른쪽 세 칸입니다. 저장 가격 차이는 작은 서비스에서 몇 달러 수준입니다. 매일 뜬 1GB 덤프를 30일 보관하면 30GB이고, S3 서울 기준으로 월 $0.75입니다.
Deep Archive는 GB당 $0.002로 가장 싸지만 최소 180일 보관분이 과금되고, 꺼내는 데 12시간에서 48시간이 걸립니다. 오늘 복구해야 하는 백업을 여기에 두면 복구가 내일로 밀립니다. 저라면 월말 사본을 오래 보관하는 용도로만 쓰겠습니다.
R2는 꺼낼 때 비용이 없어서 복원 연습을 자주 하기에 좋습니다. 대신 버전 관리가 없어서 덮어쓴 파일을 되돌리지 못하고, 그 빈자리를 버킷 잠금 규칙으로 메워야 합니다. 네이버 클라우드는 AWS와 함께 서울 리전을 가진 곳이고 S3 API와 호환되지만, 한국 리전에서는 버전 관리가 제공되지 않는다고 문서에 적혀 있습니다.
작은 웹서비스에 제가 권하는 조합
저라면 업체 자동 백업과 PITR은 켜 둔 채 빠른 복구용으로만 기대하겠습니다. 외부 사본은 따로 둡니다. 매일 pg_dump 를 운영 DB와 다른 업체의 객체 스토리지로 보내고, 서버 키에는 올리기 권한만 줍니다. 그 버킷에는 버전 관리와 30일 잠금을 겁니다.
업로드 파일은 버전 관리를 켠 버킷에 두고, 같은 방식으로 다른 업체에 한 벌 더 복제합니다. 백업 암호와 앱의 암호화 키는 비밀번호 관리자처럼 백업과 다른 곳에 둡니다.
그리고 분기에 한 번은 빈 DB에 덤프를 부어 넣고 앱을 띄워 봅니다. 3-2-1-1-0의 마지막 0이 복원 오류 0건인 데는 이유가 있습니다. 백업 파일이 생겼다는 것과 그 파일로 서비스가 다시 선다는 것은 다른 사실이고, 둘째는 복원해 봐야만 확인됩니다.
우리 서비스에 적용할 계획이 있으신가요?
클라우드 구축·배포 운영 살펴보기 →함께 읽기
- 3-2-1 백업 규칙, 사본이 셋이면 충분할까?운영 데이터베이스가 있고, 데이터베이스 서비스가 매일 자동 백업을 해 주고, 같은 계정의 파일 저장소에 덤프를 하나 더 올려 둡니다. 사본은 셋입니다. 저장 방식도 데이터베이스와 파일 저장소로 둘입니다. 3-2-1 규칙의 숫자는 모두 맞습니다.
- 웹사이트 구축 후 운영 설계: 측정·콘텐츠·보안·백업웹사이트는 공개 버튼을 누르는 순간 완성되는 것이 아니라 운영이 시작된다. 담당자, 지표, 콘텐츠 갱신, 보안 업데이트, 백업과 복구 절차가 없으면 잘 만든 사이트도 빠르게 낡는다. 이 글에서는 소규모 팀도 지속할 수 있는 웹사이트 운영 체계를 만드는 방법을 다룬다.
- 파일 저장소 비용 비교: Amazon S3와 Cloudflare R2, 무엇을 선택할까?웹 서비스에는 첨부 파일, 프로필 이미지, 업무 문서, 내보내기 파일과 백업 데이터를 저장할 공간이 필요하다. 대표적인 선택지는 Amazon S3와 Cloudflare R2다. 두 서비스 모두 안정적인 객체 스토리지지만 비용 구조와 강점은 꽤 다르다.
- 대용량 파일은 왜 서버를 거치지 않고 R2나 S3로 올려야 할까?브라우저가 파일을 애플리케이션 서버에 보내고, 서버가 다시 객체 저장소로 전달하는 방식은 이해하기 쉽다. 하지만 파일이 커지면 서버는 같은 데이터를 두 번 전송하고, 메모리와 실행 시간을 오래 점유한다. 서버리스 환경의 본문 크기 제한에도 걸리기 쉽다.
- S3와 R2를 다시 비교해보니, 저장비보다 CDN 경로가 더 중요했습니다이 글의 가격과 제품 조건은 2026년 8월 4일 공식 문서 기준입니다.