개요와 이 글에서 얻어갈 것
CAP(Cloud Application Programming Model) 프로젝트를 SAP BTP에서 운영할 때 가장 먼저 마주치는 벽 중 하나가 바로 HANA Cloud 연결입니다. 로컬 SQLite에서는 잘 돌아가던 서비스가 cf push 후에는 "no such table" 혹은 "invalid schema name" 오류를 뱉고 멈추는 경우가 흔합니다. 원인은 대부분 하나입니다. HDI 컨테이너에 스키마가 아직 배포되지 않았기 때문입니다. 이 글에서는 cds deploy --to hana 명령이 내부적으로 무엇을 수행하는지, 그리고 SalesOrder / OrderItem 엔티티를 예로 들어 3단계 배포 흐름을 정리합니다.
- HDI 컨테이너와 스키마 생성 원리 이해
- hdbtable/hdbview 산출물 생성 파이프라인 파악
cds deploy --to hana:<service>실전 옵션 활용- 배포 후 스키마 검증 및 흔한 실패 시나리오 대응
이 글을 편하게 읽기 위한 배경 지식
Node.js 18 이상 환경, CAP 프로젝트 구조(db/, srv/, app/), Cloud Foundry CLI 사용 경험, 그리고 SAP HANA Cloud 인스턴스가 이미 프로비저닝되어 있는 상태를 전제합니다. CDS 기본 문법과 @sap/cds-dk CLI를 한 번이라도 실행해 본 경험이 있다면 충분합니다.
준비 환경과 도구 버전
이 글의 예제는 다음 환경에서 검증되었습니다. 버전 차이에 따라 옵션 이름이 미묘하게 다를 수 있으니 로컬 CLI 버전을 먼저 확인하는 것을 권장합니다.
- SAP CAP:
@sap/cds ^7.9,@sap/cds-dk ^7.9 - Node.js: 20.x LTS
- HANA Cloud: 2025 QRC(스키마-only 모드 지원)
- BTP 서브계정: Cloud Foundry 런타임 활성화, hana-cloud-tools / hdi-shared 엔티티 사용 가능
- CLI 보조 도구:
cfCLI 8.x,hdbsql(선택),@sap/hana-client자동 설치
dependencies:
"@sap/cds": "^7.9.0"
"@sap/cds-hana": "^2"
"@cap-js/hana": "^1"
devDependencies:
"@sap/cds-dk": "^7.9.0"
핵심 개념: HDI 컨테이너와 cds deploy가 실제로 하는 일
많은 개발자가 오해하는 지점이 있습니다. HANA Cloud는 "그냥 DB"가 아닙니다. 하나의 HANA Cloud 인스턴스 위에 여러 마이크로서비스가 각자 격리된 HDI(HANA Deployment Infrastructure) 컨테이너를 갖는 구조입니다. 컨테이너는 논리적으로 하나의 스키마이며, 그 안에 테이블/뷰/시노님이 배포되고, 접근 권한도 컨테이너 단위로 관리됩니다.
비유하자면 HANA Cloud가 "아파트 단지", HDI 컨테이너가 "각 세대"입니다. 아파트에 입주(bind)만 했다고 방에 가구가 놓이는 것은 아닙니다. 가구(테이블)를 배송하고 배치하는 이사 작업이 바로 cds deploy --to hana가 수행하는 일입니다.
내부 동작을 단계별로 뜯어보면 다음과 같습니다.
- CDS → HDBTABLE 컴파일:
db/schema.cds를 파싱해gen/db/src/gen/*.hdbtable,*.hdbview,*.hdbindex산출물을 생성 - HDI 프로젝트 구성:
gen/db/package.json,.hdiconfig,.hdinamespace자동 생성 - 바인딩된 서비스 조회:
default-env.json혹은 CF 바인딩에서hana타입 인스턴스 credentials 로드 - HDI Deployer 실행:
@sap/hdi-deploy가 컨테이너에 접속해 산출물을 make/delete 방식으로 반영 - 배포 결과 캐싱:
.cdsrc-private.json에 프로파일별 연결 정보 저장
이 흐름을 이해하지 못한 채 서비스만 바인딩하면, SELECT FROM SALES_ORDER 실행 시점에 "invalid table name" 오류가 필연적으로 발생합니다. 스키마 자체가 비어 있으니 당연한 결과입니다.
실전 1단계: 기본 CDS 모델과 로컬 배포
주문 관리 시나리오를 상정합니다. SalesOrder와 OrderItem이 1:N으로 연결되는 전형적인 헤더-라인 구조입니다.
// db/schema.cds
namespace shop.orders;
using { cuid, managed } from '@sap/cds/common';
entity SalesOrder : cuid, managed {
orderNo : String(20) not null;
customer : String(80);
totalNet : Decimal(15,2);
currency : String(3) default 'KRW';
items : Composition of many OrderItem on items.parent = $self;
}
entity OrderItem : cuid {
parent : Association to SalesOrder;
sku : String(40);
quantity : Integer;
unitPrice : Decimal(13,2);
}
// srv/order-service.cds
using { shop.orders as db } from '../db/schema';
service OrderService @(path:'/order') {
entity SalesOrders as projection on db.SalesOrder;
entity OrderItems as projection on db.OrderItem;
}
로컬에서 먼저 확인합니다. 이 단계에서는 SQLite로 충분합니다.
"cds": {
"requires": {
"db": {
"[development]": { "kind": "sqlite", "credentials": { "url": ":memory:" } },
"[production]": { "kind": "hana", "impl": "@cap-js/hana" }
}
}
}
cds watch로 로컬 검증이 끝났다면, 이제 HANA로 넘어갈 차례입니다.
실전 2단계: HANA Cloud 바인딩과 cds deploy 실행
여기서부터가 본론입니다. 먼저 BTP 서브계정의 Cloud Foundry space에서 HDI 컨테이너 인스턴스를 생성합니다.
# CF CLI로 hdi-shared 인스턴스 생성
cf create-service hana hdi-shared shop-orders-db
# 프로비저닝 상태 확인 (create succeeded가 될 때까지 대기)
cf service shop-orders-db
이제 이 인스턴스를 로컬 CDS 프로세스에 바인딩해 배포합니다. cds deploy는 두 가지 방식으로 실행할 수 있는데, 로그 추적을 위해 verbose 옵션을 함께 켜는 것을 권장합니다.
# 방식 A: CF 서비스 인스턴스로 직접 배포
cds deploy --to hana:shop-orders-db --profile production
# 방식 B: 서비스 키 생성 후 로컬 바인딩
cf create-service-key shop-orders-db dev-key
cf service-key shop-orders-db dev-key
# default-env.json에 저장 후
cds deploy --to hana --profile production
배포가 정상적으로 진행되면 다음과 같은 로그가 출력됩니다. 각 라인이 앞서 설명한 파이프라인의 어느 단계인지 매핑해 두면 문제 발생 시 빠르게 원인을 찾을 수 있습니다.
> updating db schema for shop-orders-db...
[cds] - compiling to hana
[cds] - generated files in ./gen/db
[hdi-deploy] using container "SHOP_ORDERS_DB_1"
[hdi-deploy] making 6 files:
- SHOP_ORDERS_SALESORDER.hdbtable
- SHOP_ORDERS_ORDERITEM.hdbtable
- ...
[hdi-deploy] make succeeded
/> successfully deployed to shop-orders-db
실전 3단계: 프로덕션 배포와 스키마 검증
실제 운영 환경에서는 cds deploy를 로컬에서 매번 실행하지 않고, mta.yaml로 db-deployer 모듈을 정의해 자동화합니다. CI 파이프라인이 mbt build → cf deploy를 실행하면, db-deployer 모듈이 태스크로 한 번만 실행되고 종료됩니다.
# mta.yaml (핵심 모듈만 발췌)
ID: shop-orders
version: 1.0.0
modules:
- name: shop-orders-srv
type: nodejs
path: gen/srv
requires:
- name: shop-orders-db
- name: shop-orders-db-deployer
type: hdb
path: gen/db
parameters:
buildpack: nodejs_buildpack
requires:
- name: shop-orders-db
resources:
- name: shop-orders-db
type: com.sap.xs.hdi-container
parameters:
service: hana
service-plan: hdi-shared
배포 후에는 반드시 스키마가 실제로 생성되었는지 검증합니다. HANA Cloud Central의 Database Explorer 혹은 hdbsql로 확인하는 방법이 있습니다.
-- 컨테이너 스키마의 모든 테이블 조회
SELECT SCHEMA_NAME, TABLE_NAME, RECORD_COUNT
FROM M_TABLES
WHERE SCHEMA_NAME = CURRENT_SCHEMA
ORDER BY TABLE_NAME;
-- 특정 엔티티 구조 확인
SELECT COLUMN_NAME, DATA_TYPE_NAME, LENGTH, IS_NULLABLE
FROM TABLE_COLUMNS
WHERE TABLE_NAME = 'shop.orders.SalesOrder';
테이블 이름은 CDS namespace가 그대로 유지되어 shop.orders.SalesOrder 형태로 나타난다는 점에 주의합니다. 마지막으로 서비스 엔드포인트에 GET 요청을 보내 200 응답과 빈 배열({"value":[]})이 반환되면 배포가 완결된 상태입니다.
배포가 실패할 때 자주 만나는 함정들
현장에서 가장 자주 반복되는 실수를 정리합니다. 대부분 credentials 흐름 또는 profile 설정 문제로 귀결됩니다.
Q1. "no service matches hana" 오류가 납니다.
cds deploy --to hana는 CF에 바인딩된 hana 타입 서비스를 자동 탐색합니다. 로컬에서 실행 중이라면cf create-service-key로 키를 만들고VCAP_SERVICES환경변수 혹은default-env.json에 주입해야 합니다.cds bind명령을 사용하면 이 과정을 간소화할 수 있습니다.
Q2. HDI make 단계에서 "insufficient privilege" 오류가 발생합니다.
서비스 키의 사용자(#OO#접미사가 붙은 기술 사용자)에게 컨테이너 대상 권한이 부여되지 않았을 가능성이 큽니다. hdi-shared 인스턴스를 재생성하거나,hdi-deploy의auto_undeploy옵션을 켠 상태에서 이전 배포 잔재를 정리하는 것을 권장합니다.
Q3. 로컬에선 되던 쿼리가 배포 후 "sql syntax error" 를 냅니다.
SQLite와 HANA는 함수 시그니처가 다릅니다. 예를 들어strftime은 HANA에 없습니다. CDS의 표준 함수만 사용하거나cds.env.requires.db.kind로 분기 처리하는 것이 안전합니다.
이외에도 gen/ 폴더를 .gitignore에 넣지 않고 커밋해서 배포마다 이전 산출물이 남는 경우, cds build --production을 생략해서 최신 스키마가 반영되지 않는 경우가 빈번합니다. 배포 파이프라인에는 항상 cds build를 명시적으로 포함시키는 것을 권장합니다.
CI/CD 파이프라인과 배포 자동화 전략
실제 운영에서는 cds deploy를 수동으로 실행하는 대신, GitHub Actions 혹은 BTP Continuous Integration 서비스를 활용한 자동화를 권장합니다. 파이프라인은 크게 세 단계로 구성됩니다.
cds build --production:gen/폴더에 배포 산출물 생성mbt build: MTA 아카이브(.mtar) 생성cf deploy *.mtar: CF 런타임에 배포 — db-deployer 모듈이 태스크로 실행된 후 srv 모듈 기동
배포 파이프라인에 cds build를 생략하면 이전 버전의 산출물이 배포되는 문제가 발생할 수 있습니다. 반드시 cds build --production을 먼저 실행한 뒤 MTA 빌드를 진행하는 순서를 지킵니다.
한 걸음 더: CSV 초기 데이터와 스키마 진화
배포 흐름을 이해했다면 데이터 이관과 스키마 진화가 다음 관심사입니다. cds deploy는 CSV 초기 데이터 로드도 지원합니다. db/data/ 경로에 엔티티명과 일치하는 CSV 파일을 두면 최초 배포 시 자동으로 데이터가 삽입됩니다.
// db/data/shop.orders-SalesOrder.csv
orderNo,customer,totalNet,currency
ORD-2025-001,ACME Corp,1500000,KRW
ORD-2025-002,Beta Ltd,320000,KRW
스키마가 변경될 때 HDI는 diff를 계산하고 필요한 경우 DROP을 수행합니다. 운영 데이터가 있는 컬럼을 삭제하거나 NOT NULL 제약을 추가할 때는 auto_undeploy 정책과 마이그레이션 스크립트를 함께 준비해야 합니다. 장기적으로는 xsuaa와 결합된 멀티테넌시 배포(mtx sidecar) 구조로 확장하는 흐름이 자연스럽습니다.
댓글 0
아직 댓글이 없습니다.