RAP

RAP 개발자 90%가 모르는 Service Definition #shorts #SAP #RAP

▶ YouTube에서 보기

1. Service Definition이 필요한 이유

SAP S/4HANA Cloud 또는 ABAP Platform 환경에서 RAP(ABAP RESTful Application Programming Model)를 다루다 보면, 비즈니스 로직을 캡슐화한 CDS 뷰를 외부 시스템이 사용할 수 있게 만들어야 하는 순간이 옵니다. 데이터베이스 테이블 위에 CDS 뷰만 잘 만들어 두었다고 해서 Fiori 앱이나 외부 클라이언트가 곧바로 호출할 수 있는 것은 아닙니다. 그 사이를 잇는 "노출 계약서"가 필요한데, 그 역할을 담당하는 ABAP 개발 객체가 바로 Service Definition입니다.

이 글에서 익히게 될 내용은 다음과 같습니다.

  • RAP 스택에서 Service Definition이 차지하는 위치와 책임
  • CDS 뷰 엔티티(View Entity)를 OData V2/V4로 노출하기 위한 최소 코드
  • Service Binding과의 연결 흐름, Preview 실행으로 검증하기
  • 운영 단계에서 자주 마주치는 권한·캐싱·버전 문제 회피 방법

난이도는 입문(beginner)이지만, 실제 SalesOrder 시나리오를 기준으로 진행하므로 RAP 첫 실습으로 적합합니다.

2. 시작 전 알아두면 좋은 배경

이 글은 ABAP CDS의 기본 문법(define view entity, association)을 한 번이라도 작성해 본 독자를 가정합니다. 또한 ADT(ABAP Development Tools for Eclipse) 환경에서 패키지를 만들고 객체를 활성화한 경험, OData가 무엇인지에 대한 개략적 이해(EntitySet, $filter, $expand)가 있다면 흐름을 따라오기 수월합니다. 권한 객체(S_SERVICE)나 IAM App 개념은 후반부 권한 섹션에서 가볍게 짚으므로 사전 학습이 필수는 아닙니다.

3. 실습 환경과 준비물

이 글의 코드는 다음 환경에서 검증되었으며, 일반적으로 ABAP Platform 2022 이상에서 유사하게 동작합니다.

  • SAP BTP, ABAP Environment (Steampunk) 또는 S/4HANA 2022 On-Premise
  • ABAP Development Tools for Eclipse 3.36 이상
  • Eclipse 2023-12 (4.30) 권장
  • 예제 패키지: ZSD_SALES_DEMO (Local Package, 전송요청 없음)
  • 실습용 더미 데이터 테이블 ZSDT_SO_HDR 사전 생성 가정

Steampunk가 아닌 On-Premise라면 SICF 서비스 활성화 및 /IWFND/MAINT_SERVICE(OData V2의 경우) 등록이 추가로 필요할 수 있지만, 본 글은 RAP 표준 흐름인 Service Binding 기반 자동 노출을 다룹니다.

4. RAP 아키텍처 안에서 Service Definition의 위치

RAP는 4개의 핵심 레이어로 구성됩니다. 데이터 모델(CDS Behavior Definition 포함), 비즈니스 서비스 프로비저닝(Service Definition + Service Binding), 그리고 소비 레이어(UI/외부 시스템). 이 글의 주인공인 Service Definition은 두 번째 레이어 중에서도 "어떤 CDS 엔티티를 외부에 노출할지를 선언하는 텍스트 파일"이라고 보면 됩니다.

비유하자면, CDS 뷰는 식당 주방에서 만든 요리 자체이고, Service Definition은 손님에게 내놓을 메뉴판입니다. 메뉴판에 올리지 않은 요리는 손님이 주문할 수 없죠. 그리고 Service Binding은 그 메뉴판을 어느 테이블(OData V2 / V4 / InA)에 비치할지를 결정합니다.

중요한 동작 원리는 두 가지입니다. 첫째, Service Definition은 프로토콜에 독립적입니다. 동일한 정의를 OData V2와 V4 양쪽 Service Binding에 동시에 묶을 수 있습니다. 둘째, expose 키워드로 명시한 엔티티만 외부에 보입니다. CDS Composition으로 연결된 자식 엔티티라도 expose하지 않으면 $expand 대상이 되지 않습니다. 이 두 가지 규칙이 후반 트러블슈팅의 단골 소재입니다.

레이어 흐름을 단순화하면 다음과 같습니다.

[DB Table ZSDT_SO_HDR]
      v
[CDS View Entity Z_I_SalesOrder]      <- 데이터 모델
      v
[Behavior Definition (선택)]          <- Managed/Unmanaged
      v
[Service Definition Z_SD_SALESORDER]  <- 노출할 엔티티 선언
      v
[Service Binding (OData V4 UI)]       <- 프로토콜 결정
      v
[Fiori Elements / 외부 클라이언트]

5. 1단계 — CDS 뷰 엔티티 준비

먼저 노출 대상이 될 CDS 뷰를 작성합니다. 실무에서 자주 쓰는 판매 주문 헤더 시나리오로 구성했습니다. 패키지 ZSD_SALES_DEMO 하위에 Data Definition 객체 Z_I_SALESORDER를 생성합니다.

@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Sales Order Header - Interface View'
define view entity Z_I_SalesOrder
  as select from zsdt_so_hdr as SalesOrder
{
  key SalesOrder.so_id          as SalesOrderId,
      SalesOrder.customer_id    as CustomerId,
      SalesOrder.order_date     as OrderDate,
      SalesOrder.currency_code  as CurrencyCode,
      @Semantics.amount.currencyCode: 'CurrencyCode'
      SalesOrder.total_amount   as TotalAmount,
      SalesOrder.overall_status as OverallStatus,
      SalesOrder.created_by     as CreatedBy,
      SalesOrder.created_at     as CreatedAt
}

여기서 주의할 포인트는 두 가지입니다. @AccessControl.authorizationCheck#NOT_REQUIRED로 두면 DCL(Data Control Language) 검사 없이 모든 행이 노출됩니다. 실습용으로는 무방하나, 운영 환경이라면 #CHECK로 두고 별도 Access Control 객체를 함께 만들어야 합니다. 두 번째는 통화 필드에 @Semantics.amount.currencyCode 어노테이션을 붙여 OData 메타데이터에 sap:semantics로 자동 변환되도록 한 것입니다.

6. 2단계 — Service Definition 작성: 최소 설정과 실무 확장

ADT에서 패키지를 우클릭 → New → Other ABAP Repository Object → Business Services → Service Definition을 선택합니다. 이름은 Z_SD_SALESORDER로 지정합니다. 가장 단순한 형태는 다음과 같습니다.

@EndUserText.label: 'Sales Order Service Definition'
define service Z_SD_SALESORDER {
  expose Z_I_SalesOrder as SalesOrder;
}

단 세 줄이지만 의미는 명확합니다. Z_I_SalesOrder 뷰를 SalesOrder라는 외부 이름(EntitySet 이름의 기반)으로 공개하겠다는 선언입니다. as 절을 생략하면 CDS 뷰 이름이 그대로 노출되어 Z_I_SalesOrder처럼 보기 흉한 EntitySet 이름이 만들어지므로 alias 부여는 사실상 필수 규칙으로 두는 것이 좋습니다.

실무 시나리오에서는 헤더와 아이템을 함께 노출하고, 로그성으로 별도 뷰까지 묶는 경우가 흔합니다. 그 경우 다음처럼 확장합니다.

@EndUserText.label: 'Sales Order Composite Service'
define service Z_SD_SALESORDER_FULL {
  expose Z_I_SalesOrder      as SalesOrder;
  expose Z_I_SalesOrderItem  as SalesOrderItem;
  expose Z_I_Customer        as Customer;
  expose Z_I_Material        as Material;
}

CDS Association으로 SalesOrder → Customer, SalesOrder → SalesOrderItem이 정의되어 있다면 위처럼 expose만 추가해도 OData $expand=Customer,SalesOrderItem 요청이 정상 처리됩니다. 단, 한쪽이라도 빠지면 $expand 시 "Resource not found for the segment" 류 오류가 떨어집니다. 이 글에서 다루는 흔한 실수 1번이 바로 이 케이스입니다.

7. 3단계 — Service Binding 연결, OData 버전 선택, Preview 검증

Service Definition 자체는 메뉴판일 뿐, 실제 URL이 열리려면 Service Binding을 함께 생성해야 합니다. 패키지 우클릭 → New → Other → Business Services → Service Binding을 선택하고 다음과 같이 입력합니다.

  • 이름: Z_UI_SALESORDER_O4
  • Service Definition: Z_SD_SALESORDER
  • Binding Type: OData V4 - UI (Fiori Elements 연동 시)

버전 선택은 용도에 따라 달라집니다. 외부 시스템 통합·기존 GW(Gateway) 자산 호환이 필요하면 OData V2 - UI, 신규 Fiori Elements V4 앱·draft 기반 RAP이라면 OData V4 - UI가 일반적으로 권장됩니다. 분석 시나리오라면 InA도 선택 가능합니다.

활성화(Ctrl+F3)하면 화면 우측에 EntitySet 목록과 함께 "Service URL"과 "Preview" 버튼이 나타납니다. Preview를 누르면 자동으로 Fiori Elements List Report가 띄워지며, 다음과 비슷한 URL 패턴으로 OData 엔드포인트에 접근할 수 있습니다.

https://<host>/sap/opu/odata4/sap/z_ui_salesorder_o4/srvd/sap/z_sd_salesorder/0001/SalesOrder?$top=5&$format=json

운영 단계에서는 $metadata를 먼저 열어 EntitySet/Property 이름이 의도한 대로 노출되는지, 통화·단위 어노테이션이 잘 들어갔는지 확인하는 습관을 들이는 것이 좋습니다. 메타데이터 캐시 때문에 변경이 즉시 반영되지 않을 때는 Service Binding 화면의 "Publish Service Locally" 후 브라우저 새로고침이 일반적인 해결책입니다.

보안과 관련해서는 Steampunk 환경의 경우 Service Binding 활성화만으로 끝나지 않습니다. IAM AppBusiness Catalog를 만들어 비즈니스 역할(Business Role)에 할당해야 외부 사용자가 호출할 수 있습니다. On-Premise에서는 SU21 권한 객체와 PFCG 역할에 서비스 URL을 등록합니다. 이 점은 다음 섹션에서 자세히 다룹니다.

8. 흔한 실수와 트러블슈팅 FAQ

Q1. $expand로 자식 엔티티를 호출했더니 404가 납니다.
가장 흔한 원인은 자식 CDS 뷰를 Service Definition에 expose하지 않은 경우입니다. Association이 정의되어 있어도 expose 선언이 없으면 OData 메타데이터에 NavigationProperty가 생성되지 않습니다. expose Z_I_SalesOrderItem as SalesOrderItem; 한 줄을 추가하고 재활성화하세요.

Q2. Preview를 눌렀더니 "User has no RFC authorization" 또는 403 Forbidden이 뜹니다.
Steampunk라면 IAM App을 만들고 Business Catalog → Business Role 순으로 할당했는지 확인합니다. On-Premise라면 S_SERVICE 권한 객체에 해당 Service Binding의 SRV_NAME(예: Z_UI_SALESORDER_O4)이 포함되어야 합니다. 개발자 본인이라면 SAP_BR_DEVELOPER 역할만으로 Preview는 일반적으로 동작합니다.

Q3. CDS 뷰를 수정했는데 OData $metadata에 반영되지 않습니다.
CDS 뷰 → Service Definition → Service Binding 순으로 활성화 의존성이 자동 전파되지만, 간혹 캐시 때문에 클라이언트에서만 옛 메타데이터가 보일 수 있습니다. Service Binding 편집기에서 "Publish Service Locally"를 다시 누르고, 브라우저 캐시를 비운 뒤 $metadata를 호출하세요. Fiori Launchpad 환경에서는 메타데이터 캐시 무효화에 수 분이 걸릴 수 있습니다.

Q4. Service Definition 활성화 시 "Entity is not exposable" 에러가 발생합니다.
ABAP Dictionary 테이블이나 Classic CDS DDL Source(define view) 중 일부 타입은 직접 expose가 불가합니다. define view entity 형태(즉 View Entity)로 재작성하거나, 중간 Projection View를 한 단계 끼워 넣어야 합니다. 신규 개발은 View Entity 사용이 일반적으로 권장됩니다.

Q5. 통화 금액 필드가 OData 응답에서 문자열로 옵니다.
OData는 통화 금액을 정밀도 손실 방지를 위해 Edm.Decimal로 내보내며, JSON에서는 문자열로 직렬화되는 것이 표준 동작입니다. 클라이언트에서 parseFloat 또는 UI5의 sap.ui.model.type.Currency로 파싱하면 됩니다.

9. 한 발 더 나아가기

여기까지 완료했다면 읽기 전용(Read-Only) OData 서비스를 노출하는 최소 구성을 마친 셈입니다. 다음 단계로 권장하는 학습 경로는 다음과 같습니다.

  • Behavior Definition + Behavior Implementation으로 CRUD 가능한 Managed RAP 시나리오 구현
  • Draft-Enabled 시나리오로 Fiori Elements Object Page 연동
  • Service Consumption Model을 활용해 외부 OData를 ABAP에서 거꾸로 소비하기
  • CDS Access Control(DCL)로 행 단위 권한 적용
  • Steampunk의 Communication Scenario를 통해 외부 시스템(S2S) 인증 구성

특히 운영 환경 전개 시 Communication Scenario·Communication Arrangement 설정은 빼놓을 수 없는 단계이므로, 권한 섹션과 묶어 별도 시간을 할애해 익히는 것을 권장합니다.

댓글 0

아직 댓글이 없습니다.