-
[RAP] #6 RAPSAP/RAP 2026. 3. 1. 14:33
RAP(RESTful Application Programming Model)
ABAP 기반으로 RESTful OData 서비스를 표준 방식으로 개발하는 최신 프로그래밍 모델
- Fiori UI와 자연스럽게 통합
- REST 기반 OData 서비스 자동 생성
- Cloud 환경에 최적화된 구조 제공
- 표준화된 아키텍처 제공
REST(Representational State Transfer)
자원 중심 설계 방식이다.
여기서 자원은 Web 상의 데이터나 객체로, 각 자원은 고유한 URI로 식별된다.
HTTP Methods
Restful 웹서비스는 HTTP method를 사용해서 자원에 대한 다양한 작업을 수행한다.
- 주요 HTTP Method : GET, POST, PUT, DELETE …
그리고 RESTful API는 HTTP 프로토콜을 기반으로 자원에 접근하는 방식이다.
RESTful API의 특징
- 단순성 : HTTP 프로토콜을 기반으로 하므로 구현이 간단하고 직관적
- 확장성 : 서버와 클라이언트 간의 분리가 명확해 각각 독립적으로 개발 가능
- 유지보수성 : RESTful API는 URI와 HTTP 메소드만 사용하여, 일관된 방식으로 자원에 접근하므로, 시스템 유지보수에 용이함
RAP의 계층 구조

1. SAP Fiori UI / Other Clients
- 최상단에는 사용자가 사용하는 화면(Fiori Elements 등)이나 외부 시스템
- 여기서 사용자가 데이터를 조회하거나 저장하는 작업을 한다.
- Fiori Elements : CDS 메타데이터 기반으로 UI 자동 생성
- Freestyle UI5 : 개발자가 XML, JS 직접 코딩
2. OData
- 화면과 서버 사이를 잇는 통신 규격
- RAP은 기본적으로 OData 서비스를 통해 데이터를 주고받는다.
3. Enterprise Application Infrastructure
- 비즈니스 로직이 실행되는 핵심 구간
- 데이터의 유효성 검사, 저장 로직 등이 여기서 처리된다.
- Behavior Definition (.behavior)
- Behavior Implementation (.abap class)
=> 여기서 실제 비즈니스 로직이 동작한다.
4. Core Data Services (CDS)
- 테이블을 기반으로 데이터 모델을 정의한다.
ABAP 프로그래밍의 진화

1. Classic ABAP Programming (과거)
- S/4HANA 이전 방식
- ABAP으로만 프로그램을 개발했었다.
- UI + 비즈니스 로직 + DB 접근이 한 프로그램 안에 섞여 있다.
2. ABAP Programming Model for SAP Fiori (과도기)
- S/4HANA 초기에 사용되던 방식
- CDS View (데이터 모델), SAP Gateway (SEGW), BOPF (Business Object Processing Framework)로 구성
- 구조
CDS ↓ BOPF (비즈니스 로직) ↓ SEGW (OData 수동 생성) ↓ Fiori3. ABAP RESTful Application Programming Model (현재 & 미래)
- SAP BTP나 최신 S/4HANA(1909 버전 이상)에서 권장하는 표준 모델
- 과거의 BOPF 기능을 Behavior Definition + Implementation 으로 통합
- Cloud 최적화
ABAP 개발 환경

SAP GUI든 ADT든, 동일한 ABAP 시스템에 접속하는 경우 모든 개발 객체는 동일한 ABAP Repository에 저장된다.
즉, 개발 도구만 다를 뿐 저장소는 동일하다.
RAP 모델의 구성 요소
1. CDS(Core Data Service) View
- RAP 모델의 핵심 요소
- DB 테이블과 매핑
- Association 정의
- Composition 정의
- 데이터 모델링
- 일반적으로 두 단계로 나뉜다.
- Interface View (Root View Entity)
→ 테이블과 직접 매핑, 비즈니스 객체의 기반 - Projection View (Consumption View)
→ 외부 노출용 View
→ UI Annotation 가능
→ 필드 가공 / 일부만 노출
2. Metadata Extension
- UI Annotation 분리용 파일
- Fiori Elements가 읽는 UI 정보 정의
- 데이터 모델과 UI 설정 분리 목적
3. Behavior Definition
- 자원의 “행동” 정의
- CREATE, UPDATE, DELETE
- Action
- Validation
- Determination
- Lock 설정
4. Service Definition
- 어떤 Projection View를 외부에 노출할지 정의
5. Service Binding
- OData V2/V4 선택
- 실제 OData 서비스 생성
- URL 생성
- 서비스 활성화
6. 그 외 비즈니스 로직 구현
RAP 개발 (CDS로 데이터모델 만드는 과정)
1. Interface View 생성
- 테이블과 직접 매핑
- RAP Business Object의 기반
- define root view entity 사용
- 실제 데이터 구조를 정의하는 핵심 뷰
- 이 단계에서 DB 테이블과 연동되는 데이터 모델 설계
※ 네이밍 룰 : _R_ 또는 _IR_을 사용한다.


@AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'Interface View(root)' @Metadata.ignorePropagatedAnnotations: true define root view entity ZSFLIGHT_IR_B20 as select from sflight //composition of target_data_source_name as _association_name { key carrid as Carrid, key connid as Connid, key fldate as Fldate, @Semantics.amount.currencyCode: 'Currency' price as Price, currency as Currency, planetype as Planetype, seatsmax as Seatsmax, seatsocc as Seatsocc, @Semantics.amount.currencyCode: 'Currency' paymentsum as Paymentsum, seatsmax_b as SeatsmaxB, seatsocc_b as SeatsoccB, seatsmax_f as SeatsmaxF, seatsocc_f as SeatsoccF }
2. Projection View (= Consumption View)
- 특정 서비스 요구사항에 맞게 데이터 가공
- 일부 필드만 노출
- UI Annotation 가능
- Search / Filter / Sort 설정 가능
- 외부에 노출될 최종 View
- 즉, 클라이언트(Fiori)에서 사용할 View 정의
※ 네이밍 룰 : _C_ 또는 _CR_을 사용한다.
Interface View 파일 선택 후 우클릭하여 파일을 생성해야 한다.



@AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'Consumption View (root, Projection view)' @Metadata.ignorePropagatedAnnotations: false define root view entity ZSFLIGHT_CR_B20 as projection on zsflight_ir_b20 { key Carrid, key Connid, key Fldate, Price, Currency, Planetype, Seatsmax, Seatsocc, Paymentsum, SeatsmaxB, SeatsoccB, SeatsmaxF, SeatsoccF }
3. Metadata Extension
Projection View 파일 선택 후 우클릭 후 파일을 생성해야 한다.
템플릿은 annotateView를 선택한다.
해당 파일에 정의된 필드는 1개 이상의 어노테이션을 작성해줘야 한다.
※ 네이밍 룰 : Projection View와 이름을 동일하게 생성해준다.



@Metadata.layer: #CORE annotate view ZSFLIGHT_CR_B20 with { @UI:{ lineItem: [{ position: 10, label: '항공사' }], selectionField: [{ position: 10 }] } Carrid; @UI:{ lineItem: [{ position: 30, label: '항공편' }], selectionField: [{ position: 20 }] } Connid; @UI:{ lineItem: [{ position: 40, label: '비행일' }], selectionField: [{ position: 30 }] } Fldate; @UI.lineItem: [{ position: 20, label: '항공사명' }] Carrname; @UI.lineItem: [{ position: 50, label: '가격' }] Price; @UI.lineItem: [{ position: 60, label: '통화키' }] Currency; @UI.lineItem: [{ position: 70, label: '항공기타입' }] Planetype; @UI.lineItem: [{ position: 80, label: '최대좌석수' }] Seatsmax; @UI.lineItem: [{ position: 90, label: '예약좌석수' }] Seatsocc; }그리고 Metadata Extension을 사용하기 위해서는 Projection View 파일에 아래 Annotation을 작성해줘야 한다.
@Metadata.allowExtensions: true
4. Service Definition
Projection View 파일 선택 후 우클릭 후 파일을 생성해야 한다.


@EndUserText.label: 'Service Definition' define service ZSFLIGHT_SRV_B20 { expose ZSFLIGHT_CR_B20 as Flight; // 외부로 송출할 엔티티 셋 }
5. Service Binding
Service Definition 파일 선택 후 우클릭 후 파일을 생성한다.


※ Binding Type
UI - 데이터 요청 및 UI 제공
Web API - 데이터 요청만 가능
UI Type으로 생성하는 경우
바인딩할 서비스를 선택 후 Publish 버튼을 누르면 서비스 생성이 되고 URL도 생성된다.
그리고 UI도 제공되기 때문에 EntitySet 선택 후 Preview 버튼을 누르면 웹 페이지가 뜨게 된다.




Web API Type으로 생성하는 경우
바인딩할 서비스를 선택 후 Publish 버튼을 누르면 서비스 생성이 되고 URL도 생성된다.
UI는 제공되지 않기 때문에 Preview 버튼이 없다.
대신 Service URL 하이퍼링크를 누르면 메타데이터 및 요청에 대한 데이터를 확인할 수 있다.



6. Fiori App
프로젝트 생성 시 List Report Page 템플릿을 선택한다.

앞서 생성한 서비스를 선택한다.

Service Definition 파일에 작성한 EntitySet 이름을 선택한 후 생성한다.

※ 참고
manifest.json 파일에 해당 구문은 상세 페이지이다.

7. Fiori Launchpad Deploy
런치 패드에 배포까지 하면 완료
Header-Item 관계
- Interface root view (Header) → _IR_
- Interface view (Item) → _I_
- Projection(Consumption) root view (Header) → _CR_
- Projection(Consumption) view (Item) → _C_
1. Item Interface view 생성
association to parent 구문을 사용하여 어떤 root의 Child인지 선언한다.
즉, 단순 association이 아니라 RAP BO 계층 구조 선언이다.


@AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'Interface View(Item)' @Metadata.ignorePropagatedAnnotations: true define view entity ZSBOOK_I_B20 as select from sbook association to parent ZSFLIGHT_IR_B20 as _Header on $projection.Carrid = _Header.Carrid and $projection.Connid = _Header.Connid and $projection.Fldate = _Header.Fldate { key carrid as Carrid, key connid as Connid, key fldate as Fldate, key bookid as Bookid, customid as Customid, custtype as Custtype, smoker as Smoker, @Semantics.quantity.unitOfMeasure: 'Wunit' luggweight as Luggweight, wunit as Wunit, invoice as Invoice, class as Class, @Semantics.amount.currencyCode: 'Forcurkey' forcuram as Forcuram, forcurkey as Forcurkey, @Semantics.amount.currencyCode: 'Loccurkey' loccuram as Loccuram, loccurkey as Loccurkey, order_date as OrderDate, counter as Counter, agencynum as Agencynum, cancelled as Cancelled, reserved as Reserved, passname as Passname, passform as Passform, passbirth as Passbirth, _Header // Make association public }2. Interface root view
composition of 구문을 사용하여 자식 뷰와의 관계를 나타낸다.
RAP에서 Header-Item은 반드시 composition을 사용해야 한다.
@AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'Interface View(root)' @Metadata.ignorePropagatedAnnotations: true define root view entity ZSFLIGHT_IR_B20 as select from sflight as sf composition[0..*] of ZSBOOK_I_B20 as _Item // association 구문 위에 작성해야 함. association to scarr as _Scarr on $projection.Carrid = _Scarr.carrid { ..., _Item }3. Item Projection(Consumption) view 생성
redirected to parent는 Interface Layer의 관계를 Projection Layer 관계로 다시 연결하는 작업을 한다.
Interface ↔ Projection 은 별도 레이어이기 때문에 반드시 redirect 해줘야 한다.


@AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'Projection View(Item)' @Metadata.ignorePropagatedAnnotations: true define view entity ZSBOOK_C_B20 as projection on ZSBOOK_I_B20 { key Carrid, key Connid, key Fldate, key Bookid, Customid, Custtype, Smoker, @Semantics.quantity.unitOfMeasure: 'Wunit' Luggweight, Wunit, Invoice, Class, @Semantics.amount.currencyCode: 'Forcurkey' Forcuram, Forcurkey, @Semantics.amount.currencyCode: 'Loccurkey' Loccuram, Loccurkey, OrderDate, Counter, Agencynum, Cancelled, Reserved, Passname, Passform, Passbirth, /* Associations */ _Header: redirected to parent ZSFLIGHT_CR_B20 }4. Projection(Consumption) root view
여기서도 Interface Layer의 _Item → Projection Layer의 _Item으로 redirect 해준다.
@AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'Consumption View (root, Projection view)' @Metadata.ignorePropagatedAnnotations: false @Metadata.allowExtensions: true @Search.searchable: true @ObjectModel.semanticKey: [ 'Carrid', 'Connid' ] define root view entity ZSFLIGHT_CR_B20 as projection on ZSFLIGHT_IR_B20 { ..., /* Association Item */ _Item: redirected to composition child ZSBOOK_C_B20 }5. Service Definition 에 Entity 추가
@EndUserText.label: 'Service Definition' define service ZSFLIGHT_SRV_B20 { expose ZSFLIGHT_CR_B20 as Flight; expose ZSBOOK_C_B20 as Item; }6. Service Binding을 확인했을 때 Item이 잘 뜨면 완료

'SAP > RAP' 카테고리의 다른 글
[RAP] #8 RAP Header-Item 구조의 UI 구성 (1) 2026.03.03 [RAP] #7 Annotation 정리 (1) 2026.03.03 [RAP] #5 CDS View 추가 문법 (2) (0) 2026.02.26 [RAP] #4 CDS View 추가 문법 (1) (0) 2026.02.25 [RAP] #2 CDS View (0) 2026.02.22