백업의 목적은 파일을 하나 더 만드는 것이 아니라 정해진 시간 안에 필요한 상태로 복구하는 것이다. 3-2-1은 복사본 배치의 출발점이고, 실제 복구 시험이 없으면 “복원 가능한 백업”인지 알 수 없다.
Microsoft의 랜섬웨어 대비 백업 안내는 3개의 복사본, 2가지 저장 유형, 1개의 오프사이트 또는 콜드 복사본이라는 3-2-1 원칙을 설명한다.
이 저장소에서 확인한 현재 상태
2026년 7월 23일 읽기 전용 검사 결과는 다음과 같았다.
| 검사 | 결과 | 해석 |
|---|---|---|
| git rev-parse —is-shallow-repository | false | 전체 이력 저장소다. |
| git remote -v | origin 1개 | GitHub 원격 한 곳이 연결되어 있다. |
| git fsck —connectivity-only | 종료 코드 0 | 연결된 Git 객체에서 누락·손상 오류가 검출되지 않았다. |
| git count-objects -vH | loose 125.41MiB, pack 13.84MiB | 현재 Git 객체 저장 규모의 관측값이다. |
| Notion 동기화 workflow | 있음 | 콘텐츠를 별도 서비스로 복사하지만 Git 전체 복구본은 아니다. |
이 결과만으로 3-2-1을 충족한다고 말할 수 없다. 로컬 저장소와 GitHub 원격은 버전 이력을 제공하지만, 계정 탈취·실수·서비스 장애에 함께 영향을 받을 수 있다. Notion 동기화는 Markdown 일부를 보조 보관할 뿐 브랜치, 태그, 워크플로, 컴포넌트와 Git 객체를 모두 복원하지 않는다.
무엇을 백업할지 먼저 정한다
이 저장소를 예로 들면 대상은 네 종류다.
| 대상 | 예 | 복구 방법 |
|---|---|---|
| Git 추적 파일 | content, quartz, scripts, workflow | clone 또는 bundle 복원 |
| Git 이력 | 브랜치, 태그, 커밋 | mirror 또는 bundle |
| 추적하지 않는 설정 | 로컬 .env, 개인 설정 | 별도 암호화 백업 |
| 외부 상태 | Cloudflare 설정, GitHub Secrets, Notion DB | 서비스별 내보내기·재설정 절차 |
Git 백업은 untracked 파일과 GitHub Secrets를 포함하지 않는다. 반대로 비밀 파일을 bundle에 넣기 위해 Git에 커밋해서도 안 된다.
Git 저장소의 휴대 가능한 사본 만들기
현재 작업 변경을 먼저 확인한다.
git status --short
git branch --show-current
git tag --list
커밋된 모든 ref를 하나의 bundle로 만들 수 있다.
git bundle create confusion-blog-2026-07-23.bundle --all
git bundle verify confusion-blog-2026-07-23.bundle
bundle은 생성 시점에 커밋된 Git 객체와 ref를 담는다. 현재 작업 폴더의 미커밋 변경은 포함하지 않는다.
파일 자체의 전송 중 손상을 확인하려면 별도 checksum을 기록한다.
PowerShell:
Get-FileHash .\confusion-blog-2026-07-23.bundle -Algorithm SHA256
Linux와 macOS:
shasum -a 256 confusion-blog-2026-07-23.bundle
checksum은 파일이 동일한지 확인할 뿐, 애플리케이션이 복구되는지 확인하지 않는다.
복구 시험 절차
운영 폴더 위에 덮어쓰지 말고 빈 임시 위치에 복원한다.
git clone confusion-blog-2026-07-23.bundle restore-test
cd restore-test
git log -1 --oneline
git branch --all
git tag --list
그다음 프로젝트 수준 검사를 실행한다.
npm ci
npm run check:types
npm test
node ./quartz/bootstrap-cli.mjs build
복구 성공 조건은 “clone 명령 종료 코드 0”보다 구체적이어야 한다.
- 필요한 브랜치와 태그가 모두 보인다.
- 최신 기준 커밋이 예상값과 같다.
- 콘텐츠 파일 수가 목록과 맞는다.
- 타입 검사와 테스트가 통과한다.
- Quartz 빌드가 공개 산출물을 만든다.
- 외부 배포에 필요한 비밀을 별도 절차로 재설정할 수 있다.
AWS도 정기 복구 테스트 권장사항에서 복구가 RTO와 RPO를 만족하는지 실제로 시험하라고 안내한다.
RPO와 RTO를 기록한다
- RPO는 사고 시 허용 가능한 데이터 손실 시점이다.
- RTO는 서비스를 다시 사용할 수 있게 만들 목표 시간이다.
예를 들어 매일 밤 bundle을 만든다면 최악의 데이터 손실이 하루에 가까울 수 있다. 그러나 GitHub로 매 커밋을 푸시하고 별도 오프사이트 bundle을 매주 만든다면 사고 종류에 따라 RPO가 달라진다.
| 사고 | 사용 복사본 | 예상 RPO | 목표 RTO | 실제 시험 결과 |
|---|---|---|---|---|
| 로컬 디스크 고장 | GitHub 원격 | 마지막 push | ||
| GitHub 계정·저장소 문제 | 오프사이트 bundle | 마지막 bundle | ||
| 잘못된 콘텐츠 공개 | Git 이력 | 문제 전 커밋 | ||
| 비밀 유실 | 별도 비밀 복구 절차 | 마지막 설정 기록 |
빈칸은 실제 복구 시험으로 채운다. 숫자를 먼저 약속하고 시험을 맞추면 안 된다.
3-2-1을 저장소에 적용한 예
아래는 목표 구조 예시이며 현재 구축됐다는 의미가 아니다.
- 운영 복사본: 개발 PC의 Git 작업 저장소
- 두 번째 복사본: GitHub 원격
- 세 번째 복사본: 암호화한 bundle을 다른 공급자의 객체 저장소 또는 분리된 오프라인 매체에 보관
두 번째와 세 번째 복사본은 같은 계정의 같은 삭제 권한에 묶지 않는다. 중요한 환경에서는 하나의 불변 또는 논리적으로 분리된 복사본을 추가하는 3-2-1-1도 검토한다. 최신 Azure 백업 지침은 3-2-1-1 데이터 보호 권장사항을 설명한다.
운영 체크리스트
| 주기 | 작업 | 남길 증거 |
|---|---|---|
| 매 실행 | 백업 생성 성공 여부 | 실행 ID, 파일 크기, SHA-256 |
| 매주 | 최근 복사본 목록 확인 | 보관 위치와 생성 시각 |
| 매월 또는 위험도에 맞는 주기 | 격리된 위치에 복원 | 복원 로그, 커밋, 빌드 결과 |
| 권한 변경 시 | 삭제·복구 권한 점검 | 계정과 역할 목록 |
| 사고 후 | RPO·RTO 실제값 기록 | 타임라인과 개선 항목 |
한계
- 이번 작업에서는 bundle 파일을 실제로 만들거나 외부 저장소에 업로드하지 않았다.
- git fsck 종료 코드 0은 연결된 객체 검사 결과이며, untracked 파일과 외부 서비스 상태를 검사하지 않는다.
- loose·pack 크기는 2026년 7월 23일 관측값이고 이후 커밋과 정리 작업으로 바뀐다.
- GitHub 원격과 Notion 동기화만으로 3-2-1 충족을 입증할 수 없다.
- 암호화 키를 백업 파일과 같은 위치에 두면 분리 효과가 약해진다.
- 백업 보관에는 개인정보, 라이선스, 삭제 요청과 지역 규정도 반영해야 한다.
- 3-2-1은 랜섬웨어 방어를 보장하지 않는다. 최소 권한, MFA, 키 분리, 패치와 모니터링이 별도로 필요하다.
복구 시험을 통과한 기록이 쌓여야 백업 전략이 된다. 마지막 질문은 “복사본이 몇 개인가”가 아니라 “어떤 사고에서 어느 복사본으로 몇 분 안에 무엇까지 복원했는가”다.