[RAP] ABAP RESTful Application Programming Model 입문 — Business Object 4-Layer 구조 완전 가이드

Moderator · 조회 2

[RAP] ABAP RESTful Application Programming Model 입문 — Business Object 4-Layer 구조 완전 가이드

1. 개요 및 학습 목표

RAP(ABAP RESTful Application Programming Model)는 SAP S/4HANA 및 BTP ABAP Environment에서 OData 서비스를 선언적으로 구축하는 현대적 프레임워크입니다. 기존 SEGW 기반 OData 개발과 달리, CDS View부터 Behavior Definition, Service Definition까지 코드 중심으로 Business Object를 정의하고, 프레임워크가 CRUD 처리와 저장 시퀀스를 자동 관리합니다. 이 튜토리얼에서는 Travel 시나리오를 통해 RAP의 4-Layer 구조를 단계별로 구현합니다.

학습 목표 체크리스트:

2. 선수 지식

3. 환경 / 버전 / 준비물

항목 권장 사양
SAP 시스템 S/4HANA 2023 이상 또는 BTP ABAP Environment
개발 도구 ADT (Eclipse 2023-06 이상 + ABAP Development Tools 플러그인)
OData 버전 OData V4 (UI 또는 Web API)
데모 데이터 SAP Flight Reference Scenario (/DMO/ 패키지)
권한 개발자 권한 (패키지 생성, 서비스 발행 가능)

SAP Flight Reference Scenario가 설치되어 있지 않다면, SAP-samples/abap-platform-rap-workshops 리포지토리의 설치 가이드를 참고하시기 바랍니다. BTP ABAP Environment에는 일반적으로 사전 설치되어 있습니다.

4. 핵심 개념 — RAP 4-Layer 아키텍처

RAP의 Business Object는 4개 레이어가 쌓이는 구조로 동작합니다. 건물에 비유하면 다음과 같습니다.

비유: RAP BO = 건물 짓기

1층(CDS View) = 설계도: 어떤 데이터가 있는지 구조를 선언합니다.
2층(Behavior Definition) = 규칙서: 이 건물에서 무엇을 할 수 있는지(생성, 수정, 삭제, 승인 등) 규칙을 정합니다.
3층(Service Definition) = 출입구: 외부에서 접근할 수 있도록 어떤 Entity를 노출할지 결정합니다.
4층(Service Binding) = 도로 연결: 실제 URL을 생성하여 Fiori 앱이나 외부 시스템이 호출할 수 있게 합니다.

4-1. CDS View (데이터 모델 레이어)

CDS Root View Entity는 Business Object의 뼈대입니다. define root view entity 키워드로 루트 노드를 선언하며, Composition과 Association으로 자식 노드(예: Booking)와의 관계를 표현합니다. 필드에 @Semantics 어노테이션을 붙이면 Fiori Elements UI가 자동으로 적절한 컨트롤을 렌더링합니다.

4-2. Behavior Definition (동작 정의 레이어)

Behavior Definition(BDEF)은 BO가 지원하는 오퍼레이션을 선언적으로 기술합니다. 핵심 구성 요소는 다음과 같습니다.

4-3. Managed vs Unmanaged 시나리오

Managed 시나리오에서는 RAP 프레임워크가 CRUD 로직과 save sequence(Interaction Phase + Save Phase)를 자동 처리합니다. 개발자는 Validation, Determination, Action 같은 비즈니스 로직만 구현하면 됩니다. 반면 Unmanaged 시나리오는 개발자가 모든 저장 로직을 직접 구현하며, 기존 레거시 Business Object를 RAP으로 래핑할 때 주로 사용합니다. 신규 개발이라면 Managed 시나리오가 일반적으로 권장됩니다.

5. 실전 코드 3단계

1단계: CDS Root View Entity 정의 (기본 예제)

가장 먼저 데이터 모델을 정의합니다. SAP Flight Reference Scenario의 /dmo/travel 테이블을 기반으로 Travel BO의 루트 뷰를 생성합니다.

@AbapCatalog.viewEnhancementCategory: [#NONE]
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Travel BO - Interface View'
define root view entity ZI_Travel
  as select from /dmo/travel as Travel
{
  key Travel.travel_id          as TravelId,
      Travel.agency_id          as AgencyId,
      Travel.customer_id        as CustomerId,
      Travel.begin_date         as BeginDate,
      Travel.end_date           as EndDate,
      Travel.booking_fee        as BookingFee,
      Travel.total_price        as TotalPrice,
      Travel.currency_code      as CurrencyCode,
      Travel.description        as Description,
      Travel.overall_status     as OverallStatus,
      Travel.created_by         as CreatedBy,
      Travel.created_at         as CreatedAt,
      Travel.last_changed_by    as LastChangedBy,
      Travel.last_changed_at    as LastChangedAt
}

주요 포인트:

2단계: Behavior Definition 작성 (실무 시나리오)

CDS View 위에 동작 규칙을 정의합니다. Managed 시나리오를 선택하고, CRUD뿐 아니라 Action, Validation, Determination까지 포함한 실무 수준의 BDEF를 작성합니다.

managed implementation in class zbp_i_travel unique;
strict ( 2 );

define behavior for ZI_Travel alias Travel
persistent table /dmo/travel
lock master
authorization master ( instance )
etag master LastChangedAt
{
  // --- Standard Operations ---
  create;
  update;
  delete;

  // --- Field Control ---
  field ( readonly ) TravelId;
  field ( mandatory ) AgencyId, CustomerId, BeginDate, EndDate;

  // --- Actions ---
  action acceptTravel result [1] $self;
  action rejectTravel result [1] $self;

  // --- Validations (save 시점에 실행) ---
  validation validateDates on save { create; update; }
  validation validateCustomer on save { create; update; }

  // --- Determination (modify 시점에 실행) ---
  determination setStatusToOpen on modify { create; }

  // --- DB 테이블 필드 매핑 ---
  mapping for /dmo/travel
  {
    TravelId      = travel_id;
    AgencyId      = agency_id;
    CustomerId    = customer_id;
    BeginDate     = begin_date;
    EndDate       = end_date;
    BookingFee    = booking_fee;
    TotalPrice    = total_price;
    OverallStatus = overall_status;
  }
}

주요 포인트:

3단계: Action 구현 + Service 발행 (프로덕션)

Behavior Definition에 선언한 acceptTravel Action을 실제로 구현합니다. Local Handler Class 안에서 EML(Entity Manipulation Language)을 사용합니다.

CLASS lhc_travel DEFINITION INHERITING FROM cl_abap_behavior_handler.
  PRIVATE SECTION.
    METHODS accept_travel FOR MODIFY
      IMPORTING keys FOR ACTION travel~acceptTravel RESULT result.
ENDCLASS.

CLASS lhc_travel IMPLEMENTATION.
  METHOD accept_travel.
    " 1) OverallStatus를 'A'(Accepted)로 업데이트
    MODIFY ENTITIES OF zi_travel IN LOCAL MODE
      ENTITY travel
        UPDATE FIELDS ( OverallStatus )
        WITH VALUE #( FOR key IN keys
                        ( %tky           = key-%tky
                          OverallStatus  = 'A' ) )
      REPORTED DATA(update_reported).

    " 2) 업데이트된 결과를 다시 읽어서 반환
    READ ENTITIES OF zi_travel IN LOCAL MODE
      ENTITY travel ALL FIELDS WITH
        CORRESPONDING #( keys )
      RESULT DATA(travels).

    " 3) Action 결과 매핑
    result = VALUE #( FOR travel IN travels
                        ( %tky   = travel-%tky
                          %param = travel ) ).
  ENDMETHOD.
ENDCLASS.

마지막으로 Service Definition과 Service Binding을 생성합니다.

@EndUserText.label: 'Travel Service'
define service ZUI_Travel {
  expose ZI_Travel as Travel;
}

Service Binding은 ADT에서 마우스로 생성합니다:

  1. ADT에서 Service Definition ZUI_Travel을 우클릭 > New Service Binding
  2. Binding Type으로 OData V4 - UI 선택 (Fiori Elements 연동 시) 또는 OData V4 - Web API (외부 시스템 연동 시)
  3. Publish 버튼 클릭 > Service URL 생성 확인
  4. Entity Set 옆의 Preview 버튼으로 Fiori Elements 앱을 즉시 테스트

6. 흔한 실수 / 트러블슈팅

FAQ 1: "Behavior Definition을 활성화했는데 CDS View에서 오류가 발생합니다"

CDS View가 define root view entity로 선언되어 있는지 확인하세요. 일반 define view entity는 BO의 루트가 될 수 없습니다. Composition 관계가 있다면 자식 View도 함께 활성화해야 합니다.

FAQ 2: "mapping 블록에서 필드 매핑 오류가 납니다"

mapping for 뒤에 오는 테이블 이름은 persistent table에 선언한 테이블과 동일해야 합니다. 또한 CDS 앨리어스 이름(왼쪽)과 DB 컬럼 이름(오른쪽)의 대소문자를 정확히 맞춰야 합니다.

FAQ 3: "Action 호출 시 '%tky is initial' 오류가 발생합니다"

MODIFY ENTITIES 구문에서 %tky = key-%tky로 트랜잭션 키를 정확히 전달하고 있는지 확인하세요. %key가 아니라 %tky를 사용해야 합니다. %tky는 키 필드뿐 아니라 %is_draft 등의 시스템 필드를 포함하는 확장 키입니다.

FAQ 4: "Service Binding에서 Publish 버튼이 비활성화됩니다"

Transport Request에 올바르게 할당되었는지, 그리고 현재 사용자에게 서비스 발행 권한이 있는지 확인하세요. BTP ABAP Environment에서는 일반적으로 자동 할당되지만, On-Premise 환경에서는 TR 할당이 필요합니다.

FAQ 5: "Validation이 실행되지 않습니다"

Validation은 save 시점에만 실행됩니다. Fiori Elements 앱에서 Save 버튼을 누르기 전까지는 트리거되지 않습니다. on save { create; update; } 구문에서 트리거 조건(create, update)이 올바른지 확인하세요.

7. 다음 단계 / 관련 주제

8. 참고 자료