본문으로 건너뛰기

1.1 Branching이 해결하는 문제

운영 데이터베이스의 복사본이 필요한 경우는 생각보다 많습니다. 새 index를 추가하기 전에 실제 데이터로 실행 계획을 확인하고 싶을 때가 있습니다. 대량 UPDATE가 몇 분이나 걸릴지 미리 확인하고 싶을 때도 있습니다. 어제 실수로 지운 행을 되살리는 절차를 연습하거나 PR마다 독립된 테스트 데이터베이스를 연결하고 싶을 때도 있습니다. 이 요구는 모두 "지금 이 상태의 데이터를, 원본에 영향 없이, 빨리" 얻는 문제로 수렴합니다.

기존 방법은 이 문제의 비용을 시간과 공간으로 치렀습니다. Database branching은 이 비용 구조를 바꿉니다. 복사본을 만드는 시간은 데이터 크기와 무관해지고, 저장 공간은 원본과 달라진 부분만 추가됩니다.

기존 방식의 비용 구조

PostgreSQL에서 복사본을 만드는 방법은 크게 세 가지였습니다.

첫째는 pg_dumppg_restore입니다. 논리 백업이므로 버전과 플랫폼이 달라도 사용할 수 있습니다. 일부 테이블만 선택할 수도 있습니다. 대신 복원할 때는 데이터를 다시 적재하고 index를 다시 만들어야 합니다. 따라서 데이터 크기에 비례해 느려집니다. pg_dump의 기본 출력은 COPY 형식이고 --inserts를 지정할 때만 INSERT 문이 됩니다. 수백 GB면 시간 단위, TB급이면 하루가 넘어가기도 합니다.

둘째는 스냅샷 복원입니다. 클라우드 블록 스토리지 스냅샷이나 pg_basebackup 결과를 새 인스턴스에 적용하는 방식입니다. dump보다 빠르지만 여전히 물리 복사입니다. 클라우드 스냅샷 복원은 먼저 인스턴스를 사용 가능한 상태로 만듭니다. 블록은 백그라운드나 첫 접근 시 적재(lazy load)하는 경우가 많습니다. 따라서 사용할 수 있게 되는 시점은 빠릅니다. 하지만 전체 초기화와 워밍업이 끝나기 전에는 첫 접근 성능이 낮습니다. 복원된 볼륨은 원본과 같은 크기를 차지합니다.

셋째는 공유 스테이징 데이터베이스입니다. 복사본을 하나만 만들고 여러 사람이 함께 사용합니다. 준비 비용은 한 번이지만 격리가 없습니다. 누군가 schema를 바꾸면 다른 사람의 테스트가 깨집니다. 데이터도 시간이 갈수록 운영과 멀어집니다.

방식준비 시간저장 공간데이터 시점격리 단위
pg_dump / pg_restore크기 비례데이터 규모에 비례(원본과 같지는 않음)dump 시각복사본마다
스냅샷 복원사용 가능까지는 짧음, 전체 초기화는 크기 비례원본과 동일스냅샷 시각복사본마다
공유 스테이징최초 1회원본과 동일(1개)마지막 갱신 시각없음
Copy-on-write branch크기 무관(수 초)변경분(delta)만제품별 상이(Neon, Tiger는 보존 범위 안 과거 시점, Aurora clone은 최신 복구 가능 시점)branch마다

표의 마지막 행이 branching입니다. 준비 시간이 상수라는 점이 가장 큰 차이입니다. 복사 과정이 없으므로 데이터가 커져도 branch 생성 시간은 늘지 않습니다. Neon 문서는 branch 생성이 데이터 크기와 무관하게 약 1초 안에 끝난다고 설명합니다. Xata도 50GB든 5TB든 같은 시간이 걸린다고 말합니다.

무엇이 달라지는가

준비 시간이 상수가 되면 복사본을 사용하는 방식이 달라집니다. 하루에 한 번 만들던 복사본을 작업마다 만들고, 작업이 끝나면 버립니다. 스테이징을 공유할 필요가 없어 격리도 확보됩니다.

저장 공간이 delta만큼만 늘어난다는 점도 중요합니다. 10개의 branch를 만들어도 처음에는 추가 공간이 거의 없습니다. 이후에는 계보 전체에서 새로 생기거나 보존해야 하는 고유 블록과 page 버전만큼만 늘어납니다. 부모에 쓰기가 발생해도 자식이 참조하는 이전 버전을 보존해야 합니다. 따라서 copy-on-write는 부모 쪽에서도 발생합니다. 세부 과금은 제품과 보존 기간에 따라 다릅니다. Neon 가격 문서에 따르면 history window 밖으로 나간 자식 branch는 root branch 수준으로 과금될 수 있습니다. 그래도 "실험용 복사본 10개"가 원본 크기의 10배를 차지하는 일은 없어집니다.

세 번째 변화는 시점 선택입니다. copy-on-write branch는 대개 WAL 또는 스토리지 history를 기반으로 만듭니다. 따라서 현재 상태뿐 아니라 보존 범위 안의 과거 시점에서도 분기합니다. 예를 들어 어제 14시 상태로 branch를 만들어 지운 데이터를 확인합니다. 이 특성은 2.4 Timeline과 Branch 내부에서 Neon 구현을 통해 다시 살펴봅니다.

대표 사용 사례

개발 환경 drift 제거

개발자마다 로컬에 seed 데이터를 넣어 사용하면 운영과 다른 분포에서 테스트합니다. index가 쓰이지 않는 query가 생기고 컬럼의 NULL 비율도 달라집니다. 운영에서는 수백만 행인 테이블이 로컬에서는 백 행에 불과한 상황도 반복됩니다. 운영 branch에서 개발용 branch를 만들면 같은 분포로 테스트합니다. 다만 운영 데이터에는 개인정보가 섞여 있습니다. 따라서 이 방식은 anonymization이나 schema-only branch와 함께 설계해야 합니다. 1.44.5에서 다룹니다.

Schema 마이그레이션 리허설

ALTER TABLE ... ADD COLUMN ... DEFAULT가 테이블을 다시 쓰는지는 실제 크기의 데이터에서만 확인됩니다. CREATE INDEX CONCURRENTLY가 얼마나 걸리는지도 마찬가지입니다. 외래 키 추가가 어떤 lock을 획득하는지도 실제 데이터로 확인해야 합니다. branch에서 마이그레이션 스크립트를 먼저 실행합니다. 소요 시간, lock 대기, 실패 여부를 기록한 뒤 운영에 적용합니다. 실패하면 branch를 지우고 다시 만들면 됩니다.

잘못된 DML 복구 리허설

WHERE 절을 빠뜨린 DELETE는 백업이 있어도 복구하기 어렵습니다. 전체를 PITR로 되돌리면 그 사이에 발생한 정상 쓰기까지 사라지기 때문입니다. branching이 있으면 사고 직전 시점에서 branch를 만듭니다. 지워진 행만 조회한 뒤 그 결과를 운영에 다시 넣습니다. 이 절차를 사고 전에 연습해 두는 것이 리허설입니다.

PR마다 독립 데이터베이스

CI에서 PR마다 branch를 만들고 테스트가 끝나면 삭제하는 흐름입니다. Neon은 CLI, API, GitHub Actions와 Vercel 연동으로 이 흐름을 지원합니다. branch에 만료 시각을 지정해 자동으로 삭제하는 기능도 제공합니다. Supabase는 PR과 연결된 preview branch를 만들고 PR이 닫히면 삭제합니다. 구현 방식은 다르지만 "PR 하나에 데이터베이스 하나"라는 형태는 같습니다.

AI agent 샌드박스

2026년 들어 주목받는 사용처입니다. agent가 schema를 바꾸거나 데이터를 수정하는 작업을 branch 안에서 수행하게 합니다. 사람이 결과를 검토한 뒤 반영합니다. Neon은 snapshot을 checkpoint로 삼습니다. 그런 다음 agent가 만든 상태로 되돌리는 흐름을 제안합니다. Dolt는 agent마다 branch를 제공하고 머지 시점에 검토하는 방식을 소개합니다. 코드 리뷰와 같은 절차를 데이터에도 적용하는 것입니다.

무거운 분석 query 격리

운영 인스턴스에서 실행하기 부담스러운 집계나 export를 branch에서 수행합니다. 읽기 전용 replica로 해결되는 경우도 많습니다. 하지만 임시 테이블을 만들거나 집계 결과를 저장해야 한다면 쓰기가 가능한 branch가 편리합니다.

용어 정리: branch, clone, fork, snapshot

제품마다 다른 단어를 쓰지만 가리키는 개념은 세 가지로 정리됩니다.

용어주로 쓰는 제품
branchNeon, Xata, Supabase, PlanetScale, Dolt, DBLab부모에서 갈라져 나온 쓰기 가능한 복사본
cloneAurora, DBLab, ZFS원본에서 만든 쓰기 가능한 복사본 (부모 관계 표현은 제품마다 다름)
forkTiger Cloud특정 시점의 서비스 복사본
snapshotZFS, Neon, DBLab특정 시점의 읽기 전용 기록. branch나 clone을 만드는 재료

branch, clone, fork는 사실상 같은 개념을 가리키는 다른 이름입니다. 차이는 부모 관계를 얼마나 명시적으로 드러내는지에 있습니다. Neon과 Dolt는 부모 branch에서 reset하거나 머지하는 동작을 제공합니다. 따라서 부모 관계가 일급 개념입니다. Aurora clone은 생성된 뒤 독립 클러스터로 취급됩니다. snapshot만은 다릅니다. 읽기 전용이며 그 자체가 접속 대상은 아닙니다. 특정 시점에서 branch를 만들기 위한 기준점입니다. Neon은 snapshot을 별도 객체로 두고 해당 시점으로 복원하거나 branch를 만드는 기능을 제공합니다.

자주 생기는 오해

branch는 replica가 아닙니다. 부모에서 분기한 뒤 발생한 변경은 자식 branch에 반영되지 않습니다. 분기 시점의 상태를 기준으로 각자 독립적으로 변경됩니다. 최신 상태가 필요하면 branch를 새로 만들거나 reset해야 합니다.

branch는 백업도 아닙니다. 같은 스토리지의 history를 공유합니다. 따라서 스토리지 자체가 사라지면 branch도 함께 사라집니다. 백업과 branching은 서로 다른 장애 시나리오를 다룹니다.

마지막으로 branch에 연결하는 compute는 별도입니다. 스토리지가 delta만큼만 늘어나더라도 branch 10개에 연결된 PostgreSQL 프로세스 10개의 비용은 별도입니다. scale-to-zero가 있는 서비스는 유휴 시간의 compute 비용을 줄입니다. 이 부분은 6.1 비용 모델에서 따로 계산합니다.

연습 문제

  1. 현재 팀의 테스트 데이터베이스 생성 절차를 적고, 위 표에서 해당하는 행으로 분류합니다. 준비 시간과 저장 공간을 실제 수치로 채웁니다.
  2. 최근 6개월 동안 "운영 데이터로 미리 확인했으면 막을 수 있었던" 장애나 지연 배포가 있었는지 찾습니다. branching이 있었다면 어떤 절차로 확인했을지도 적습니다.
  3. branch와 read replica, PITR 복원을 각각 어떤 상황에 쓰는지 표로 정리합니다. 셋이 겹치는 영역과 겹치지 않는 영역을 구분합니다.

참고