1. 왜 서브어카운트 설계가 프로젝트 초반의 승부수인가
SAP BTP 프로젝트에서 가장 자주 목격되는 실수는 "일단 하나 만들고 나중에 나누자"는 접근입니다. 무료 트라이얼로 시작해 Proof of Concept(PoC)을 진행하다가, 어느 순간 그 서브어카운트에 개발/QA/운영 리소스가 뒤엉키는 상황이 벌어집니다. 결과적으로 CAP 애플리케이션 배포 시 Dev 변경사항이 Prod 데이터베이스와 같은 서비스 인스턴스를 바라보는 사고가 발생하거나, XSUAA 롤 컬렉션이 환경 간 충돌하면서 인증 오류가 급증합니다.
이 글의 목표는 다음과 같습니다.
- 서브어카운트(Subaccount)와 디렉토리(Directory)의 역할 구분 이해
- Dev/QA/Prod 분리 전략의 3가지 대표 패턴 비교
- Entitlement/Quota 배분에서 실무자가 자주 놓치는 지점
- Service Instance 격리를 통한 데이터 오염 방지 설계
- 안티패턴을 피하기 위한 운영 체크리스트
여기서 다루는 내용은 Cloud Foundry 및 Kyma 환경 기반 BTP Enterprise 계정(Feature Set B 기준)을 전제로 합니다. Trial 계정은 서브어카운트 개수 및 리전 제약이 있어 일부 패턴이 그대로 적용되지 않습니다.
2. 알고 시작하면 좋은 것들
이 글은 BTP Cockpit에서 서브어카운트를 최소 한 번은 생성해본 개발자, 또는 CAP/UI5 앱을 Cloud Foundry에 배포해본 경험이 있는 개발자를 대상으로 합니다. XSUAA 서비스 바인딩, Cloud Foundry Org/Space 개념, 그리고 btp CLI 또는 cf CLI 사용 경험이 있으면 이해가 훨씬 빠릅니다. 또한 Entitlement, Quota, Assignment 세 용어의 차이를 개념적으로 알고 있으면 좋습니다.
3. 환경 및 사전 준비
실전 예제 재현을 위해 필요한 준비물은 다음과 같습니다.
- SAP BTP Enterprise Global Account (Feature Set B) — 2024년 이후 신규 생성 계정은 대부분 이 기준
- Global Account 관리자 권한 (또는 Directory Admin)
btpCLI v2.x 이상 —btp --version으로 확인cfCLI v8 이상 (Cloud Foundry 환경 사용 시)- 대상 리전: 예제에서는
eu10(Europe - Frankfurt)를 기준으로 하되, APJ 사용자는ap11(Singapore) 또는jp10(Tokyo)으로 대체 가능 - 선택: SAP Cloud Transport Management(CTMS) 구독 — Dev→QA→Prod 전송 자동화에 권장
또한 예제에서는 가상의 프로젝트 "NovaSales"를 상정합니다. 이 프로젝트는 CAP 기반 SalesOrder 관리 서비스와 UI5 프런트엔드로 구성되어 있다고 가정합니다.
4. 서브어카운트 계층 구조를 다시 보는 시선
BTP의 계정 구조는 흔히 "폴더 트리"로 비유되지만, 실제로는 정책 상속 트리에 더 가깝습니다. Global Account가 뿌리(root)이고, 그 아래 Directory가 중간 노드, Subaccount가 실제 워크로드가 사는 잎(leaf) 노드입니다. Directory는 그저 시각적 그룹핑이 아니라 Entitlement 배분, 사용자 관리, 라벨(labels) 기반 정책 상속의 단위입니다.
중요한 점은 모든 실제 리소스(서비스 인스턴스, 앱, 사용자 롤 컬렉션)는 Subaccount 안에서만 존재한다는 것입니다. Directory에는 워크로드가 없습니다. 즉, 서브어카운트는 다음 세 가지를 격리하는 최소 단위입니다.
- 보안 경계: XSUAA 테넌트가 서브어카운트당 하나씩 생성되어 인증/인가 도메인이 분리됨
- 과금/할당 경계: Entitlement에서 배분받은 서비스 플랜과 Quota가 서브어카운트 단위로 소비됨
- 환경 경계: Cloud Foundry Org, Kyma 런타임, ABAP 시스템이 서브어카운트에 종속됨
비유하자면, Global Account는 회사 건물, Directory는 부서(팀별 정책 상속), Subaccount는 각 팀의 잠금장치가 있는 사무실입니다. 사무실을 하나만 두고 개발자·QA·운영자를 모두 몰아넣으면 서로의 서류가 섞이는 것은 시간 문제입니다.
5. 안티패턴 — "일단 하나로" 시작한 팀이 6개월 뒤 겪는 일
실무에서 가장 자주 마주치는 잘못된 구성부터 살펴봅니다. 다음은 NovaSales 팀이 초기에 만든 구조입니다.
# 안티패턴: 단일 서브어카운트에 모든 환경 뒤섞기
Global Account: NovaCorp-Global
└── Subaccount: novasales-all
├── CF Org: novasales
│ ├── Space: dev
│ ├── Space: qa
│ └── Space: prod
├── XSUAA Tenant: novasales-all.authentication.eu10.hana.ondemand.com
└── HANA Cloud Instance: novasales-hdb (공유)
이 구성의 치명적인 문제점은 다음과 같습니다.
- XSUAA 테넌트 공유: 하나의
xs-security.json롤 컬렉션 변경이 세 환경 모두에 영향을 미침. Dev에서 스코프를 삭제하면 Prod 사용자가 즉시 접근 불가 - HANA Cloud 공유: Dev 개발자가 실수로
DROP TABLE SALES_ORDERS를 실행하면 Prod 데이터가 함께 날아감 - Quota 경합: Dev 팀이 부하 테스트로 메모리를 다 써버리면 Prod 앱이 배포 실패
- 감사(Audit) 곤란: 누가 언제 Prod 리소스를 만졌는지 Space 단위로만 추적 가능해 세밀한 감사가 어려움
올바른 패턴은 서브어카운트 자체를 환경별로 분리하는 것입니다.
# 권장 패턴: 환경별 서브어카운트 분리 + Directory로 그룹핑
Global Account: NovaCorp-Global
└── Directory: NovaSales (라벨 상속: cost_center=SALES-42)
├── Subaccount: novasales-dev (리전 eu10, Stage=DEV)
├── Subaccount: novasales-qa (리전 eu10, Stage=QA)
└── Subaccount: novasales-prod (리전 eu10, Stage=PROD)
6. Dev/QA/Prod 분리 전략 설계 — 실전 예제 1단계
가장 기본이 되는 세 환경 분리를 btp CLI로 실행하는 예제입니다.
# 1) Global Account 로그인
btp login --url https://cli.btp.cloud.sap \
--subdomain novacorp-global \
--user admin@novacorp.example
# 2) Directory 생성 (환경 그룹핑)
btp create accounts/directory \
--display-name "NovaSales" \
--description "Sales Order 프로젝트 상위 디렉토리" \
--features "ENTITLEMENTS,AUTHORIZATIONS"
# 3) 환경별 서브어카운트 생성
for STAGE in dev qa prod; do
btp create accounts/subaccount \
--display-name "novasales-${STAGE}" \
--subdomain "novasales-${STAGE}" \
--region eu10 \
--directory NovaSales \
--labels "stage=${STAGE};project=novasales;owner=sales-platform"
done
--labels는 나중에 비용 리포팅과 보안 정책 자동화의 핵심이 되므로 초기부터 일관된 키/값 스킴을 정하는 것이 좋습니다. 일반적으로 stage, project, cost_center, data_classification 네 가지를 표준화하는 것을 권장합니다.
7. Entitlement 배분과 Quota — 실전 예제 2단계
서브어카운트를 나눴다면 이제 각 환경에 어떤 서비스를, 얼마나 할당할지 결정해야 합니다. 여기서 실수하기 쉬운 지점은 "Prod에만 후하게 주고 Dev는 최소로"가 항상 옳지 않다는 점입니다. Dev에는 오히려 다양한 서비스 플랜을 실험할 여지를 남겨야 하고, Prod는 정확히 필요한 것만 배분해 blast radius를 줄여야 합니다.
# Prod 서브어카운트에 HANA Cloud, XSUAA(application),
# Cloud Foundry Runtime 배분 예제
SUBACCOUNT_GUID=$(btp get accounts/subaccount novasales-prod \
--show-guid | grep guid | awk '{print $2}')
# HANA Cloud - hana 플랜, 1 unit
btp assign accounts/entitlement \
--to-subaccount "$SUBACCOUNT_GUID" \
--for-service hana-cloud \
--plan hana \
--amount 1
# XSUAA - application 플랜 (unlimited)
btp assign accounts/entitlement \
--to-subaccount "$SUBACCOUNT_GUID" \
--for-service xsuaa \
--plan application \
--enable
# Cloud Foundry Runtime - 8GB
btp assign accounts/entitlement \
--to-subaccount "$SUBACCOUNT_GUID" \
--for-service cloudfoundry-runtime \
--plan MEMORY \
--amount 8
# 로깅: 배분 결과 확인
btp list accounts/entitlement --subaccount "$SUBACCOUNT_GUID" > \
./audit/prod-entitlements-$(date +%Y%m%d).log
Dev 환경은 반대로 실험 여지를 넓게 잡되, Quota는 상한을 낮게 설정합니다.
# Dev: 다양한 플랜을 시험하되 메모리는 4GB로 제한
btp assign accounts/entitlement \
--to-subaccount novasales-dev \
--for-service hana-cloud \
--plan hana-free # 무료 플랜 우선 시도
btp assign accounts/entitlement \
--to-subaccount novasales-dev \
--for-service cloudfoundry-runtime \
--plan MEMORY \
--amount 4
8. Service Instance 격리와 CI/CD 연동 — 실전 예제 3단계
프로덕션 등급에서는 서브어카운트 분리만으로는 부족합니다. mta.yaml 또는 manifest.yml에서 환경별 서비스 인스턴스를 명확히 분리하고, CI/CD 파이프라인에서 대상 서브어카운트를 파라미터화해야 합니다.
# mta.yaml — NovaSales SalesOrder 서비스 예제
_schema-version: "3.3"
ID: novasales-orders
version: 1.0.0
parameters:
deploy_mode: html5-repo
modules:
- name: novasales-srv
type: nodejs
path: srv
parameters:
memory: 512M
requires:
- name: novasales-db
- name: novasales-uaa
- name: novasales-logs
resources:
- name: novasales-db
type: com.sap.xs.hdi-container
parameters:
service: hana
service-plan: hdi-shared
# 서비스 인스턴스명에 stage 접미사를 반드시 붙일 것
service-name: novasales-db-${stage}
- name: novasales-uaa
type: org.cloudfoundry.managed-service
parameters:
service: xsuaa
service-plan: application
service-name: novasales-uaa-${stage}
path: ./xs-security.json
- name: novasales-logs
type: org.cloudfoundry.managed-service
parameters:
service: application-logs
service-plan: lite
service-name: novasales-logs-${stage}
CI 파이프라인(예: GitHub Actions)에서는 브랜치에 따라 대상 서브어카운트를 다르게 지정합니다.
# .github/workflows/deploy.yml (발췌)
jobs:
deploy:
strategy:
matrix:
include:
- branch: develop
stage: dev
cf_api: https://api.cf.eu10.hana.ondemand.com
cf_org: novasales-dev
- branch: release
stage: qa
cf_api: https://api.cf.eu10.hana.ondemand.com
cf_org: novasales-qa
- branch: main
stage: prod
cf_api: https://api.cf.eu10.hana.ondemand.com
cf_org: novasales-prod
steps:
- uses: actions/checkout@v4
- name: Build MTA
run: mbt build -p=cf -t=./mta_archives \
--mtar novasales-${{ matrix.stage }}.mtar
- name: Deploy
run: |
cf login -a ${{ matrix.cf_api }} \
-u ${{ secrets.CF_USER }} \
-p ${{ secrets.CF_PASSWORD }} \
-o ${{ matrix.cf_org }} -s default
cf deploy ./mta_archives/novasales-${{ matrix.stage }}.mtar \
-e cfg/${{ matrix.stage }}.mtaext -f
추가로 xs-security.json에서 xsappname에 stage 접미사를 붙여 XSUAA 앱 식별자 충돌을 방지합니다. 예: "xsappname": "novasales-${stage}". 이렇게 하면 QA에서 발급받은 JWT가 Prod에서 유효하지 않게 되어 환경 간 토큰 재사용 사고를 원천 차단할 수 있습니다.
9. 흔한 실수와 트러블슈팅
Q1. Directory를 나중에 만들어서 기존 서브어카운트를 옮기려는데 실패합니다.
서브어카운트를 다른 Directory로 이동하는 것은 가능하지만, Directory에 ENTITLEMENTS feature가 활성화되어 있으면 Entitlement가 Directory 레벨에서 재계산됩니다. 이동 전 대상 Directory의 Entitlement 정책이 기존 서브어카운트 배분보다 작지 않은지 반드시 확인하세요. 일반적으로 이동 직전에 btp list accounts/entitlement로 스냅샷을 남기는 것을 권장합니다.
Q2. Dev와 Prod에 같은 XSUAA xsappname을 썼더니 배포는 되는데 인증이 이상하게 동작합니다.
XSUAA는 xsappname을 스코프 네임스페이스로 사용합니다. 서로 다른 서브어카운트라도 같은 이름을 쓰면 롤 컬렉션 매핑 시 혼동이 발생할 수 있습니다. 반드시 stage 접미사 또는 프리픽스로 구분하고, 롤 컬렉션 이름도 NovaSales_Admin_prod처럼 명시적으로 분리하는 것이 안전합니다.
Q3. Trial 계정에서 이 구조를 그대로 재현하려는데 서브어카운트가 하나만 만들어집니다.
Trial 계정은 기본적으로 서브어카운트 1개, Cloud Foundry 리전 1개로 제한됩니다. 이 구조를 학습 목적으로 실험하려면 Free Tier로 업그레이드하거나 hana-free, xsuaa broker 등 무료 플랜만 활용하는 축소 버전으로 진행해야 합니다.
Q4. Entitlement를 배분했는데 Cockpit UI에는 보이지만 cf marketplace에 안 나옵니다.
Entitlement 배분과 Service Broker 등록은 별개입니다. Cloud Foundry 환경 인스턴스가 서브어카운트에 활성화되어 있어야 하며, 활성화 후에도 서비스 브로커 카탈로그 동기화까지 수 분이 걸릴 수 있습니다. cf update-service-broker가 필요한 경우도 있습니다.
10. 이어서 살펴볼 만한 주제
서브어카운트 구조가 안정되면 자연스럽게 다음 주제로 확장됩니다. 첫째, SAP Cloud Transport Management(CTMS)를 도입해 Dev→QA→Prod 전송을 게이트 방식으로 자동화하는 방법. 둘째, Terraform Provider for SAP BTP를 활용해 위 구조 자체를 코드로 관리(IaC)하는 방식. 셋째, Custom IdP 연동을 통해 환경별로 다른 인증 정책(예: Prod만 MFA 강제)을 적용하는 설계. 넷째, SAP Alert Notification Service를 서브어카운트별로 구성해 Prod 이슈만 별도 채널로 라우팅하는 운영 패턴입니다.
11. 더 깊게 파고들 자료
댓글 0
아직 댓글이 없습니다.