📖 온프레미스 S/4HANA와 BTP, 왜 함께 쓰는가
많은 기업이 S/4HANA를 온프레미스(자사 데이터센터 또는 프라이빗 클라우드)에 운영하면서도, 확장 기능·모바일 앱·파트너 연동은 SAP BTP(Business Technology Platform) 위에 구축합니다. 핵심 배경은 "클린 코어(Clean Core)" 전략입니다. ERP 코어를 수정 없이 깨끗하게 유지하고, 커스텀 로직은 BTP라는 별도의 클라우드 플랫폼에서 개발하는 방식이 일반적으로 권장됩니다. 이 글에서는 방화벽 안쪽의 S/4HANA와 인터넷 위의 BTP가 어떤 컴포넌트를 통해, 어떤 순서로 연결되는지를 아키텍처 관점에서 입문자 눈높이로 정리합니다.
- 온프레미스와 클라우드가 연동되어야 하는 비즈니스적 이유를 설명할 수 있다
- Cloud Connector, Destination Service, Connectivity Service의 역할을 구분할 수 있다
- OData / RFC / 이벤트 기반 연동의 차이를 시나리오별로 판단할 수 있다
- SalesOrder 조회 예제를 통해 실제 호출 흐름을 코드로 확인할 수 있다
📚 읽기 전에 알아두면 좋은 배경
이 글은 입문자를 대상으로 하지만, 다음 개념을 대략적으로 알고 있으면 이해가 훨씬 빠릅니다. HTTP와 REST API의 기본 구조(GET/POST, 상태 코드), 방화벽과 인바운드/아웃바운드 트래픽의 차이, 그리고 SAP ERP가 "회사의 핵심 업무 데이터(주문, 재고, 회계)를 담는 시스템"이라는 정도의 감각입니다. ABAP이나 Cloud Foundry 경험은 없어도 따라올 수 있습니다.
🔧 다루는 환경과 버전, 준비물
이 글의 설명과 예제는 다음 환경을 기준으로 합니다.
- SAP S/4HANA 2023 (온프레미스 에디션) — SAP GUI 접근 권한과 OData 서비스 활성화 권한(트랜잭션 /IWFND/MAINT_SERVICE)이 있다고 가정
- SAP BTP, Cloud Foundry 환경 — Trial 또는 Free Tier 계정으로도 실습 가능. Connectivity, Destination 서비스 인스턴스 필요
- SAP Cloud Connector 2.16 이상 — 온프레미스 네트워크 내부 서버(또는 로컬 PC)에 설치
- Node.js 20 LTS + SAP Cloud SDK for JavaScript — 예제 코드 실행용
참고로 S/4HANA Cloud Public Edition을 쓰는 경우에는 Cloud Connector가 필요 없고 Communication Arrangement로 직접 연결합니다. 이 글은 앵글에 맞춰 온프레미스 S/4HANA를 전제로 합니다.
💡 연동 아키텍처의 핵심 개념 — 호텔 프런트 비유로 이해하기
온프레미스와 클라우드 연동의 근본적인 어려움은 방향성에 있습니다. 회사 방화벽은 내부에서 밖으로 나가는(아웃바운드) 트래픽은 비교적 자유롭게 허용하지만, 밖에서 안으로 들어오는(인바운드) 트래픽은 엄격히 차단합니다. BTP 앱이 S/4HANA를 호출하려면 인바운드 연결이 필요한데, 방화벽 포트를 여는 것은 보안팀이 절대 반기지 않는 방식입니다.
이 문제를 푸는 것이 SAP Cloud Connector입니다. Cloud Connector는 온프레미스 네트워크 내부에 설치되어, 내부에서 BTP 방향으로 아웃바운드 TLS 터널을 먼저 열어둡니다. 이후 BTP에서 오는 요청은 이미 열려 있는 이 터널을 역방향으로 타고 들어옵니다(리버스 인보크 방식). 즉 방화벽에 인바운드 포트를 하나도 열지 않고도 클라우드에서 온프레미스를 호출할 수 있게 됩니다.
호텔에 비유하면 이렇습니다.
- S/4HANA = 호텔 객실(외부인이 직접 들어갈 수 없는 공간)
- Cloud Connector = 호텔이 직접 고용한 벨보이. 객실 쪽에서 프런트로 먼저 나와 대기하며, 허용된 객실(가상 호스트로 매핑된 시스템)로만 손님을 안내
- Connectivity Service = 프런트 데스크. BTP 앱의 요청을 받아 벨보이에게 전달하는 프록시
- Destination Service = 객실 안내 장부. "S4HANA_ONPREM이라는 이름으로 요청이 오면 어느 주소로, 어떤 인증 방식으로 보내라"는 접속 정보를 중앙에서 관리
- Integration Suite = 컨시어지 서비스. 단순 안내를 넘어 여러 시스템 간 메시지 변환·라우팅·오케스트레이션이 필요할 때 사용
전체 호출 흐름을 텍스트 도식으로 그리면 다음과 같습니다.
# BTP 앱 → 온프레미스 S/4HANA 호출 흐름
BTP CAP/Node.js 앱
→ Destination Service # "S4HANA_ONPREM" 접속 정보 조회
→ Connectivity Service # 온프레미스행 프록시 (proxyType: OnPremise)
→ (TLS 터널, 아웃바운드로 미리 수립됨)
→ Cloud Connector # 가상 호스트 → 실제 내부 호스트 매핑
→ S/4HANA Gateway # OData 서비스 (예: API_SALES_ORDER_SRV)
여기서 온프레미스와 클라우드의 데이터·프로세스 차이도 짚고 넘어가야 합니다. 온프레미스 S/4HANA는 트랜잭션의 원천(Single Source of Truth)으로, 데이터 정합성과 잠금(lock) 관리가 데이터베이스 수준에서 이루어집니다. 반면 BTP 앱은 상태를 가능한 한 갖지 않는(stateless) 구조로 설계되며, 필요한 데이터를 API로 조회하거나 이벤트로 전달받습니다. 따라서 "데이터를 클라우드로 복제할 것인가, 매번 실시간 호출할 것인가"가 아키텍처 설계의 첫 번째 질문이 됩니다. 일반적으로 조회 빈도가 낮고 정합성이 중요하면 실시간 OData 호출, 대량·비동기 처리라면 Event Mesh 기반 이벤트 연동이 권장됩니다.
연동 방식별 특징을 표로 비교하면 다음과 같습니다.
| 방식 | 프로토콜 | 적합한 경우 | 주의점 |
|---|---|---|---|
| OData (REST) | HTTP(S) | UI 앱, 실시간 조회/생성, Fiori 확장 | 대량 데이터에 비효율적 |
| RFC / BAPI | RFC 프로토콜 | 레거시 함수 재사용, 성능 중요 구간 | JCo 필요, 클린 코어 관점에서 신규 개발엔 비권장 |
| Event Mesh | AMQP/MQTT | 비동기 알림(주문 생성 이벤트 등), 느슨한 결합 | 이벤트 유실 대비 재처리 설계 필요 |
| Integration Suite (Cloud Integration) | 다중 | 다단계 매핑, 서드파티 포함 복합 시나리오 | 단순 1:1 호출에는 과한 선택 |
💻 실전 예제 3단계 — SalesOrder 조회로 배우는 연동 흐름
영업 주문(Sales Order)을 BTP 앱에서 조회하는 시나리오로 단계를 밟아봅니다. S/4HANA 측에서는 표준 OData 서비스 API_SALES_ORDER_SRV가 활성화되어 있고, Cloud Connector에는 가상 호스트 s4h-virtual:44300이 내부 게이트웨이 서버로 매핑되어 있다고 가정합니다.
1단계: Destination 정의와 기본 조회
BTP 콕핏에서 Destination을 아래 속성으로 생성합니다. 핵심은 ProxyType=OnPremise로, 이 값이 있어야 Connectivity Service를 경유해 Cloud Connector 터널로 라우팅됩니다.
{
"Name": "S4HANA_ONPREM",
"Type": "HTTP",
"URL": "http://s4h-virtual:44300",
"ProxyType": "OnPremise",
"Authentication": "BasicAuthentication",
"User": "BTP_TECH_USER",
"Password": "********",
"sap-client": "100"
}
SAP Cloud SDK를 쓰면 Destination 조회, 토큰 처리, 프록시 설정이 모두 자동화됩니다.
// step1-basic.js — 최근 영업 주문 5건 조회
const { executeHttpRequest } = require('@sap-cloud-sdk/http-client');
async function fetchRecentSalesOrders() {
const response = await executeHttpRequest(
{ destinationName: 'S4HANA_ONPREM' },
{
method: 'GET',
url: "/sap/opu/odata/sap/API_SALES_ORDER_SRV/A_SalesOrder" +
"?$top=5&$select=SalesOrder,SoldToParty,TotalNetAmount" +
"&$orderby=CreationDate desc"
}
);
return response.data.d.results;
}
fetchRecentSalesOrders().then(orders =>
orders.forEach(o =>
console.log(`${o.SalesOrder} | ${o.SoldToParty} | ${o.TotalNetAmount}`))
);
2단계: 실무 시나리오 — 에러 처리와 로깅
실무에서는 온프레미스 시스템 점검, 터널 순단, 게이트웨이 오류가 일상적으로 발생합니다. 상태 코드별로 원인을 구분해 로그를 남기고, 일시적 오류에는 재시도를 적용합니다.
// step2-resilient.js — 에러 분류 + 재시도 + 구조화 로깅
const { executeHttpRequest } = require('@sap-cloud-sdk/http-client');
const cds = require('@sap/cds');
const log = cds.log('salesorder-sync');
const RETRYABLE = [502, 503, 504];
async function fetchSalesOrder(salesOrderId, attempt = 1) {
try {
const res = await executeHttpRequest(
{ destinationName: 'S4HANA_ONPREM' },
{ method: 'GET',
url: `/sap/opu/odata/sap/API_SALES_ORDER_SRV/A_SalesOrder('${salesOrderId}')`,
timeout: 10000 }
);
log.info('조회 성공', { salesOrderId, attempt });
return res.data.d;
} catch (err) {
const status = err.response?.status;
if (status === 401 || status === 403) {
log.error('인증/권한 오류 — Destination 자격증명 확인 필요', { salesOrderId, status });
throw err; // 재시도 무의미
}
if (RETRYABLE.includes(status) && attempt < 3) {
log.warn('일시 오류, 재시도', { salesOrderId, status, attempt });
await new Promise(r => setTimeout(r, 1000 * 2 ** attempt)); // 지수 백오프
return fetchSalesOrder(salesOrderId, attempt + 1);
}
log.error('조회 실패', { salesOrderId, status, message: err.message });
throw err;
}
}
503이 반복된다면 코드 문제가 아니라 Cloud Connector 터널 상태를 먼저 의심해야 합니다(트러블슈팅 섹션 참조).
3단계: 프로덕션 수준 — Principal Propagation과 테스트
1~2단계는 기술 사용자(BTP_TECH_USER) 하나로 모든 요청을 처리했습니다. 그러나 프로덕션에서는 "누가 조회했는지"가 S/4HANA 권한(PFCG 롤)으로 통제되어야 하는 경우가 많습니다. 이때 Principal Propagation을 사용하면 BTP에 로그인한 사용자의 신원이 단기 X.509 인증서로 변환되어 S/4HANA까지 전달됩니다. Destination의 인증 방식만 바꾸면 코드 변경은 최소화됩니다.
{
"Name": "S4HANA_ONPREM_PP",
"Type": "HTTP",
"URL": "http://s4h-virtual:44300",
"ProxyType": "OnPremise",
"Authentication": "PrincipalPropagation",
"sap-client": "100"
}
호출 시에는 사용자 JWT를 함께 전달합니다. 또한 외부 시스템에 의존하는 코드는 반드시 목(mock) 기반 단위 테스트를 갖춰야 CI 파이프라인이 온프레미스 가용성에 흔들리지 않습니다.
// step3-production.js — 사용자 신원 전파 호출
async function fetchMyOrders(req) {
return executeHttpRequest(
{ destinationName: 'S4HANA_ONPREM_PP', jwt: req.headers.authorization?.split(' ')[1] },
{ method: 'GET',
url: '/sap/opu/odata/sap/API_SALES_ORDER_SRV/A_SalesOrder?$top=20',
timeout: 10000 }
);
}
// step3.test.js — Jest 목 테스트 (온프레미스 없이 검증)
jest.mock('@sap-cloud-sdk/http-client');
const { executeHttpRequest } = require('@sap-cloud-sdk/http-client');
test('사용자 JWT가 Destination 조회에 전달된다', async () => {
executeHttpRequest.mockResolvedValue({ data: { d: { results: [] } } });
await fetchMyOrders({ headers: { authorization: 'Bearer dummy.jwt.token' } });
expect(executeHttpRequest).toHaveBeenCalledWith(
expect.objectContaining({ jwt: 'dummy.jwt.token' }),
expect.anything()
);
});
성능 관점에서는 Destination 조회 결과가 SDK 수준에서 캐싱된다는 점, 그리고 $select·$top으로 페이로드를 줄이는 것이 온프레미스 게이트웨이 부하를 줄이는 가장 손쉬운 방법이라는 점을 기억해 두면 좋습니다.
🔐 보안과 인증에서 반드시 검토할 것
연동 아키텍처에서 보안은 세 개 층으로 나누어 점검하는 것이 일반적입니다.
- 전송 구간 — Cloud Connector 터널 자체는 TLS로 암호화되지만, Cloud Connector에서 S/4HANA까지의 내부 구간도 HTTPS로 구성하는 것이 권장됩니다. 가상 호스트 매핑 시 HTTP를 쓰면 내부 구간이 평문이 됩니다.
- 인증 방식 — 시스템 간 배치성 호출은 기술 사용자 + BasicAuthentication(또는 OAuth2SAMLBearerAssertion), 사용자 컨텍스트가 필요한 화면성 호출은 Principal Propagation이 일반적입니다. 기술 사용자 비밀번호는 Destination에 저장되므로 콕핏 접근 권한(Role Collection)을 최소화해야 합니다.
- 접근 범위 제한 — Cloud Connector의 Access Control에서 허용할 URL 경로를
/sap/opu/odata/sap/API_SALES_ORDER_SRV처럼 서비스 단위로 좁게 등록하세요. 루트 경로(/) 전체를 열어두는 것은 온프레미스 시스템 전체를 노출하는 것과 같습니다.
Principal Propagation을 쓰는 경우 S/4HANA 측에 BTP 서브어카운트의 시스템 인증서를 신뢰(trust)로 등록하고, 인증서 주체(subject)와 SAP 사용자 매핑 규칙(CERTRULE)을 구성해야 하는 추가 작업이 있다는 점도 미리 알아두면 좋습니다.
⚠️ 자주 겪는 문제와 놓치기 쉬운 설정 (FAQ)
Q1. 호출하면 503 Service Unavailable이 계속 발생합니다.
가장 흔한 원인은 Cloud Connector가 해당 서브어카운트에 연결되어 있지 않은 경우입니다. Cloud Connector 관리 화면(기본 8443 포트)에서 서브어카운트 연결 상태가 Connected인지, BTP 콕핏의 Connectivity > Cloud Connectors 메뉴에 터널이 보이는지 확인하세요. 서브어카운트 리전(예: ap12)과 Cloud Connector에 등록한 리전이 다르면 터널 자체가 성립하지 않습니다.
Q2. Destination 연결 테스트는 성공인데 실제 호출은 404가 납니다.
Destination의 "Check Connection"은 호스트 도달성만 확인할 뿐 경로까지 검증하지 않습니다. Cloud Connector Access Control에 등록한 경로와 실제 호출 URL의 접두 경로가 일치하는지, 그리고 S/4HANA에서 해당 OData 서비스가 /IWFND/MAINT_SERVICE로 활성화되어 있는지 확인하세요. 가상 호스트 이름의 오타(예: s4h-virtual vs s4hvirtual)도 단골 원인입니다.
Q3. Basic 인증에서 Principal Propagation으로 바꾸자 401이 발생합니다.
Principal Propagation은 Destination 설정만으로 끝나지 않습니다. (1) Cloud Connector에서 시스템 인증서와 CA 구성, (2) S/4HANA 프로파일 파라미터에서 인증서 로그인 허용, (3) CERTRULE 트랜잭션의 사용자 매핑 규칙 — 이 세 가지가 모두 준비되어야 합니다. 어느 하나라도 빠지면 S/4HANA가 전달받은 단기 인증서를 사용자로 매핑하지 못해 401을 반환합니다.
Q4. sap-client를 지정하지 않았더니 엉뚱한 클라이언트로 접속됩니다.
Destination의 Additional Properties에 sap-client를 반드시 넣으세요. 생략하면 게이트웨이 기본 클라이언트로 라우팅되어, 개발(100)이 아닌 다른 클라이언트의 데이터를 보게 되는 사고가 생길 수 있습니다.
🚀 여기서 더 나아가려면
이 글의 구조를 이해했다면 다음 주제로 확장하는 흐름이 자연스럽습니다. 첫째, CAP(Cloud Application Programming Model)으로 S/4HANA OData를 외부 서비스로 임포트해 자체 서비스와 조합하는 패턴. 둘째, SAP Event Mesh(또는 Advanced Event Mesh)로 주문 생성 이벤트를 구독해 실시간 반응형 확장을 만드는 이벤트 기반 아키텍처. 셋째, Integration Suite의 Cloud Integration으로 서드파티(예: 물류사 API)까지 포함한 다단계 시나리오 구성. 넷째, S/4HANA Cloud Public Edition으로 전환 시 Cloud Connector 없이 Communication Management로 재구성하는 마이그레이션 관점입니다.
📚 마무리 요약과 더 읽어볼 자료
정리하면, 온프레미스 S/4HANA와 BTP 연동의 뼈대는 "인바운드 포트 개방 없이 아웃바운드 터널로 연결한다(Cloud Connector)", "접속 정보는 코드가 아니라 중앙에서 관리한다(Destination Service)", "코어는 깨끗하게, 확장은 플랫폼에서(클린 코어)"라는 세 문장으로 압축됩니다. SalesOrder 예제에서 본 것처럼 애플리케이션 코드는 Destination 이름 하나만 알면 되고, 네트워크·인증의 복잡성은 플랫폼 계층이 흡수합니다. 아래 자료로 각 컴포넌트를 더 깊이 파보시길 권합니다.
- SAP Help Portal — Cloud Connector (BTP Connectivity)
- SAP Help Portal — Destination 관리 (BTP Connectivity)
- SAP Help Portal — Principal Propagation 구성
- SAP Help Portal — SAP Integration Suite
- SAP Cloud SDK for JavaScript 문서
- SAP Business Accelerator Hub — Sales Order (A2X) OData API
- SAP CAP(Cloud Application Programming Model) 문서
댓글 0
아직 댓글이 없습니다.