스테이징-프로덕션 격리는 환경마다 앱, 데이터베이스, 키를 따로 운영해 테스트가 라이브 데이터에 절대 닿지 않게 하는 관행입니다. BaaS에서는 이를 말 그대로 구현합니다. 환경이 곧 앱입니다. 같은 플랫폼 위의 두 앱은 아무것도 공유하지 않습니다. 데이터베이스 행 하나도, API 키 하나도, Cloud Code 배포 하나도 공유하지 않으므로, 격리는 인프라 프로젝트가 아니라 이름 짓기 규칙이 됩니다.
핵심 요약
| 질문 | 답변 |
|---|---|
| 핵심 규칙 | 환경 하나 = BaaS 앱 하나. 자체 데이터베이스, 키, 파일, Cloud Code |
| 절대 넘어가면 안 되는 것 | 데이터와 자격 증명. 어느 방향으로도 |
| 반드시 같아야 하는 것 | 스키마, Cloud Code 리비전, 설정의 구조 |
| 환경 사이를 이동하는 것 | 승격을 통한 구조와 코드. 행은 절대 아님 |
| 강제 메커니즘 | 빌드 시점에 선택되는 환경별 키 |
격리는 클라이언트 빌드에서 시작됩니다
이 체계 전체는 하나의 결정으로 강제됩니다. 빌드가 어떤 키를 가지고 있느냐입니다. 환경 선택을 빌드 설정에 두어야 디버그 빌드가 실수로라도 프로덕션에 닿을 수 없습니다.
// JavaScript / Node.js — Back4app JS SDK
// One codebase, two isolated apps — the keys select the environment
const ENV = process.env.APP_ENV ?? 'staging';
const config = {
staging: { appId: 'STAGING_APP_ID', jsKey: 'STAGING_JS_KEY' },
production: { appId: 'PRODUCTION_APP_ID', jsKey: 'PRODUCTION_JS_KEY' },
}[ENV];
Parse.initialize(config.appId, config.jsKey);
Parse.serverURL = 'https://parseapi.back4app.com';
// A staging bug can now corrupt only staging data — never a customer's. // Flutter / Dart — Back4app Flutter SDK
// One codebase, two isolated apps — the keys select the environment
const env = String.fromEnvironment('APP_ENV', defaultValue: 'staging');
const keys = {
'staging': ('STAGING_APP_ID', 'STAGING_CLIENT_KEY'),
'production': ('PRODUCTION_APP_ID', 'PRODUCTION_CLIENT_KEY'),
};
final (appId, clientKey) = keys[env]!;
await Parse().initialize(
appId,
'https://parseapi.back4app.com',
clientKey: clientKey,
);
// A staging bug can now corrupt only staging data — never a customer's. // iOS / Swift — Back4app Swift SDK
// One codebase, two isolated apps — the build configuration selects keys
#if DEBUG
let appId = "STAGING_APP_ID" // debug builds hit the staging app
let clientKey = "STAGING_CLIENT_KEY"
#else
let appId = "PRODUCTION_APP_ID" // release builds hit production
let clientKey = "PRODUCTION_CLIENT_KEY"
#endif
ParseSwift.initialize(
applicationId: appId,
clientKey: clientKey,
serverURL: URL(string: "https://parseapi.back4app.com")!
)
// A staging bug can now corrupt only staging data — never a customer's. // Android / Kotlin — Back4app Android SDK
// One codebase, two isolated apps — the build type selects the keys
val appId = if (BuildConfig.DEBUG) "STAGING_APP_ID" else "PRODUCTION_APP_ID"
val clientKey =
if (BuildConfig.DEBUG) "STAGING_CLIENT_KEY" else "PRODUCTION_CLIENT_KEY"
Parse.initialize(
Parse.Configuration.Builder(context)
.applicationId(appId)
.clientKey(clientKey)
.server("https://parseapi.back4app.com")
.build()
)
// A staging bug can now corrupt only staging data — never a customer's. 이 설정은 팀이 흔히 사고를 겪는 경로를 정확히 거꾸로 막아 줍니다. 그런 사고는 극적인 침해가 아니라, 조용히 프로덕션을 가리키고 있던 테스트 기기가 라이브 사용자 테이블에 test-user-final-2를 써 넣는 식으로 일어납니다.
스테이징 vs. 프로덕션: 분리되는 것과 분리되면 안 되는 것
격리와 동등성(parity)은 같은 규율을 반대편에서 본 것입니다. Twelve-Factor의 dev/prod 동등성 원칙은 환경 간 차이를 최소화하라고 말하고, 격리는 남은 차이가 절대적이어야 한다고 말합니다:
| 계층 | 스테이징 | 프로덕션 | 같아야 하는가? |
|---|---|---|---|
| 데이터베이스 | 자체 인스턴스, 버려도 되는 데이터 | 자체 인스턴스, 실제 데이터 | 스키마는 예, 내용은 절대 아님 |
| API 키와 마스터 키 | 스테이징 전용 | 프로덕션 전용 | 절대 공유하지 않음 |
| Cloud Code | 후보 리비전 | 마지막으로 승격된 리비전 | 릴리스 시점에 동일 |
| 설정 | 테스트 모드 연동, 샌드박스 결제 | 라이브 연동 | 같은 구조, 다른 값 |
| 푸시와 이메일 | 샌드박스 자격 증명, 테스트 토픽 | 라이브 자격 증명 | 같은 메커니즘, 분리된 채널 |
| 접근 | 팀 전체 | 제한되고 감사됨 | 의도적으로 다름 |
BaaS는 가장 어려운 동등성 문제를 대신 없애 줍니다. 두 앱이 같은 플랫폼 버전과 인프라에서 실행되므로, 직접 관리하는 환경의 고전적인 실패인 “스테이징이 프로덕션보다 새 데이터베이스를 쓰는” 일은 일어날 수 없습니다. 동등성을 위한 노력은 사용자가 배포하는 세 가지, 즉 스키마, 코드, 설정에 집중됩니다.
승격 워크플로
변경은 한 방향으로만 흐릅니다. 구조와 코드는 위로 올라가고, 데이터는 아예 이동하지 않습니다:
파이프라인을 신뢰할 수 있게 만드는 관행은 세 가지입니다:
- 승격은 스크립트로 처리합니다. 스키마 변경 적용과 Cloud Code 배포를 손으로 하면, 한 환경에만 존재하는 핫픽스가 생겨 이후 모든 릴리스를 괴롭힙니다. 둘 다 환경별로 구성한 같은 CI/CD 파이프라인에서 실행해야 합니다. CI/CD는 승격의 엔진이고, 격리는 그 엔진이 달리는 선로입니다.
- 스테이징 시드는 의도적으로 준비합니다. 빈 스테이징 데이터베이스는 아무것도 검증하지 못하고, 복사한 프로덕션 데이터는 테스트라는 이름을 붙인 개인정보 사고일 뿐입니다. 현실적인 규모, 관계, 엣지 케이스를 갖춘, 프로덕션과 같은 형태의 데이터를 생성하는 시드 스크립트를 유지하고, 릴리스 테스트 전에 다시 실행해 스테이징을 알려진 상태로 초기화하는 것이 좋습니다.
- 스키마 변경은 하위 호환되게 만듭니다. 필드와 클래스는 코드가 의존하기 전에 추가하고, 아무것도 의존하지 않게 된 뒤에만 제거합니다. 추가를 먼저 하는 순서를 지키면 승격이 중간에 멈추더라도 프로덕션이 두 스키마 버전 사이에 갇히지 않습니다.
일반적인 사용 사례
- 릴리스 관문. 모든 후보 빌드는 키를 프로덕션용으로 바꾸기 전에 스테이징 앱에서 실행됩니다. 스테이징이 존재하는 가장 기본적인 이유입니다.
- 마이그레이션 리허설. 스키마 변경, 인덱스 생성, 데이터 백필은 먼저 스테이징 시드에서 실행합니다. 여기서는 실수의 대가가 장애가 아니라 초기화입니다.
- 샌드박스 모드의 통합 테스트. 결제, 푸시, 이메일 제공업체는 스테이징에만 연결된 테스트 모드로 실행됩니다. 테스트 스위트에서 실제 카드에 결제가 청구되는 일은 없습니다.
- 부하 테스트와 카오스 실험. 스트레스 테스트로 고객 트래픽과 경쟁하거나 프로덕션 지표를 오염시키지 않고 스테이징에 부하를 가합니다.
- 고객과 이해관계자 대상 프리뷰. 데모는 스테이징 시드 데이터로 진행되므로, 데모 중의 성급한 클릭 한 번이 실제 사용자 40,000명에게 이메일을 보내는 일은 없습니다.
스테이징과 프로덕션을 분리해야 할까? 결정 매트릭스
| 완전히 격리해야 할 때(앱 두 개)… | 앱 하나로 충분할 수 있을 때… |
|---|---|
| 실제 사용자가 제품에 의존함 | 아직 외부 사용자가 없는 프로토타입임 |
| 개인 정보나 규제 대상 데이터를 저장함 | 어차피 모든 데이터가 합성 데이터임 |
| 두 명 이상이 배포함 | 1인 개발자가 영향 범위를 감수함 |
| 스키마나 Cloud Code 변경이 정기적으로 출시됨 | 백엔드가 사실상 동결됨 |
| 프로덕션에서 결제, 푸시, 이메일이 돌아감 | 부수 효과가 있는 연동이 없음 |
오른쪽 열을 솔직하게 읽으면, 이는 전략이 아니라 단계를 묘사합니다. 첫 실제 사용자가 가입하는 날 팀은 이 단계를 졸업합니다. 그리고 환경을 분리하기에 가장 저렴한 시점은 그날 이전, 조심스럽게 비켜 가며 마이그레이션해야 할 프로덕션 데이터가 아직 없을 때입니다. 블루-그린 배포 같은 고급 릴리스 기법은 같은 격리 논리를 릴리스 자체로 확장합니다.
한계와 트레이드오프
- 동등성 유지는 끝나지 않는 쳇바퀴입니다. 모든 스키마 수정과 설정 변경은 두 앱 모두에 반영되어야 합니다. 수작업 지름길이 하나씩 쌓일 때마다 드리프트가 커지고, 결국 스테이징은 더 이상 존재하지 않는 시스템을 검증하게 됩니다. 자동화만이 지속 가능한 답입니다.
- 스테이징 데이터는 허구입니다. 시드는 프로덕션의 규모, 데이터 쏠림, 비정상적인 레코드를 결코 완전히 재현하지 못합니다. 스테이징은 구조적 결함은 확실하게, 성능 회귀는 가끔, 데이터에 의존하는 버그는 드물게 잡아냅니다. 나머지를 위해 카나리 릴리스가 존재합니다.
- 추가되는 비용이 거의 두 배입니다. 두 번째 앱, 두 번째 데이터베이스, 샌드박스 연동은 규모가 커지면 실제 비용이 듭니다. 그래도 이들이 막아 주는 장애보다는 훨씬 저렴하며, 초기에는 무료 요금제 스테이징 앱이 이 부담을 덜어 줍니다.
- 격리는 멀티테넌시가 아닙니다. 환경 분리는 팀이 직접 내보낸 릴리스로부터 팀을 보호합니다. 고객끼리 서로 분리하는 것은 테넌트 격리로, 다른 메커니즘이 필요한 다른 문제입니다.
- 환경 두 개는 세 개를, 그다음엔 다섯 개를 부릅니다. BaaS에서는 프리뷰 앱과 QA 앱을 만들기 쉽지만, 환경이 하나 늘 때마다 동등성을 맞춰야 할 대상도 늘어납니다. 안심을 위해서가 아니라 구체적인 워크플로를 위해 추가하는 것이 좋습니다.
Back4app에서의 환경 격리
Back4app은 관리형 데이터베이스, 자동 생성되는 REST 및 GraphQL API, 인증, 파일 스토리지, Cloud Code 서버리스 함수를 결합한 오픈소스 Backend as a Service(BaaS) 플랫폼입니다. 격리는 가장 기본적인 구성 요소에 그대로 대응합니다. 각 앱은 자체 데이터베이스, 키, 파일 스토리지, Cloud Code 배포를 갖춘 완전하고 독립적인 스택이므로, 스테이징 환경은 프로덕션 환경과 같은 방식으로 몇 분 만에 만들어지며 구조상 공유되는 것이 없습니다. 두 앱은 같은 오픈소스 엔진과 플랫폼 버전에서 실행되므로 dev/prod 동등성의 가장 깊은 계층은 무료로 주어집니다. 남은 것, 즉 스키마, 코드, 설정의 승격은 사용자가 스크립트로 만들 몫입니다.
자주 묻는 질문
스테이징과 프로덕션의 차이는 무엇입니까?
프로덕션은 실제 사용자와 실제 데이터를 다루는 라이브 시스템이고, 스테이징은 그 리허설용 사본입니다. 릴리스를 현실적으로 검증할 수 있도록 프로덕션의 설정을 최대한 그대로 따릅니다. 스테이징은 버려도 되는 데이터나 합성 데이터를 담고 망가져도 괜찮지만, 프로덕션은 둘 다 허용하지 않습니다. 스테이징의 가치는 데이터를 제외한 모든 것을 얼마나 충실하게 재현하는지에 비례합니다.
BaaS에서 스테이징 환경은 어떻게 만듭니까?
플랫폼에 두 번째 앱을 만들면 됩니다. BaaS 앱은 각각 자체 데이터베이스, API 키, 파일 스토리지, Cloud Code 배포를 갖추고 있으므로 격리는 조립하는 것이 아니라 구조적으로 주어집니다. 앱 이름은 myapp-staging, myapp-production처럼 명시적으로 짓고, 배포 프로세스로 스키마와 코드를 동기화하며, 스테이징 빌드는 스테이징 키만 가리키게 해야 합니다.
스테이징과 프로덕션이 데이터베이스를 공유해도 됩니까?
아니요. 데이터베이스를 공유하면 스테이징의 존재 이유가 사라집니다. 스테이징에서 하는 마이그레이션 테스트, 부하 실험, 버그 있는 트리거가 라이브 레코드를 변경하고, 자격 증명 하나가 유출되면 고객 데이터가 노출됩니다. 데이터베이스를 분리하면 스테이징 최악의 사고도 시드로 다시 만들 수 있는 데이터만 망가뜨립니다. 한 데이터베이스 안에서 접두사로 나눠 쓰는 방식은 테넌트 격리를 손으로, 그것도 서툴게 다시 만드는 일입니다.
스테이징에서 프로덕션 데이터를 사용해야 합니까?
원본 그대로는 안 됩니다. 라이브 사용자 레코드를 보안 수준이 낮은 환경에 복사하면 노출 범위가 늘어나고, 대개 개인정보 보호법에도 어긋납니다. 프로덕션과 같은 형태의 합성 시드 데이터(같은 스키마, 같은 관계, 같은 엣지 케이스)나, 식별자와 개인 정보 필드를 뒤섞은 익명화된 부분 집합을 사용하는 것이 좋습니다. 주기적으로 갱신하면 스테이징이 부담거리가 되지 않으면서도 대표성을 유지합니다.
BaaS에서 dev/prod 동등성은 무엇을 뜻합니까?
환경끼리 데이터와 키만 다르고 나머지는 모두 같다는 뜻입니다. BaaS에서는 플랫폼이 런타임을 자동으로 맞춰 줍니다. 두 앱이 같은 서버 버전, API 동작, 인프라에서 실행됩니다. 남은 동등성 책임은 스키마, Cloud Code, 설정, 서드파티 연동 모드입니다. 이 가운데 하나라도 어긋나면 스테이징 테스트는 조용히 무의미해집니다.
스테이징의 변경 사항은 프로덕션으로 어떻게 승격합니까?
순서가 정해진 스크립트 기반 릴리스로 승격합니다. 프로덕션에 스키마 추가분을 적용하고, 스테이징에서 검증한 것과 같은 Cloud Code 리비전을 배포하고, 설정을 갱신한 뒤, 클라이언트를 출시합니다. 이를 환경별로 구성한 CI/CD 파이프라인으로 자동화하면 고전적인 실패, 즉 한 환경에만 손으로 적용되어 다른 환경에서 모두를 놀라게 하는 핫픽스가 사라집니다.
스테이징과 프로덕션 외에 더 많은 환경이 필요합니까?
대개 그렇습니다. 개발자별 또는 공용 개발 앱이 매일의 실험을 흡수해 주면 스테이징은 안정적인 릴리스 관문으로 남을 수 있고, BaaS에서 환경이 그저 또 하나의 앱이라면 QA나 프리뷰 앱도 저렴하게 추가할 수 있습니다. 안전한 릴리스를 위한 최소치는 두 개입니다. 병렬 QA나 고객 데모처럼 구체적인 워크플로가 요구할 때만 더 늘리는 것이 좋습니다.
API 키를 여러 환경에서 공유해도 됩니까?
절대 안 됩니다. 앱마다 키를 분리하는 것이 격리를 강제하는 메커니즘입니다. 프로덕션 키를 가진 스테이징 빌드는 언젠가 라이브 레코드에 테스트 데이터를 쓰게 되고, 보안 수준이 낮은 스테이징 키가 유출되더라도 고객 데이터가 열려서는 안 됩니다. 키는 하드코딩하지 말고 환경별 빌드 설정에 저장해야 하며, 환경 경계를 넘은 키는 모두 회전해야 합니다.