ABAP RAP 입문 — Managed Scenario로 CRUD 앱 만들기

AI News

slide

slide

slide

1. 개요 및 학습 목표

SAP BTP ABAP 환경에서 RESTful Application Programming Model(RAP)은 Fiori 기반 트랜잭셔널 앱을 표준화된 방식으로 구축하는 프레임워크입니다. 이 튜토리얼에서는 RAP의 Managed Scenario를 활용하여 직원 관리 CRUD 앱을 처음부터 끝까지 만들어 봅니다. Managed Scenario는 프레임워크가 표준 CRUD 처리를 자동으로 수행하므로, 개발자는 비즈니스 로직에만 집중할 수 있습니다.

학습 목표 체크리스트:

2. 선수 지식

이 튜토리얼을 원활하게 따라가려면 다음 배경지식이 권장됩니다.

3. 환경 / 버전 / 준비물

이 튜토리얼은 다음 환경을 기준으로 작성되었습니다.

항목상세
SAP BTP EditionSAP BTP ABAP Environment 또는 SAP S/4HANA Cloud, ABAP Environment
ABAP 언어 버전ABAP Cloud (ABAP for Cloud Development)
RAP 구현 타입Managed (with Draft 옵션 포함)
OData 프로토콜OData V2 또는 V4 (Service Binding에서 선택)
개발 도구Eclipse ADT (최신 버전 권장)
서비스 소비SAP Fiori Elements — List Report Object Page 템플릿

준비물: SAP BTP Trial 또는 유료 계정, Eclipse에 ADT 플러그인 설치, ABAP Cloud Project 연결 완료 상태가 필요합니다. SAP ABAP Trial 인스턴스는 SAP BTP Cockpit에서 생성할 수 있습니다.

4. 핵심 개념 — RAP BO 아키텍처와 Managed vs Unmanaged

RAP Business Object 아키텍처 4계층

RAP 앱은 마치 4층 건물에 비유할 수 있습니다. 지하(Data Modeling)에서 옥상(Business Service)까지 각 층이 명확한 역할을 담당합니다.

계층구성 요소역할비유
1층 — Data ModelingDB Table + CDS View Entity데이터 영속성(저장)과 추상화건물의 기초와 골조
2층 — BO BehaviorBehavior Definition(BDEF) + Behavior Pool(ABP 클래스)CRUD 동작 정의 및 비즈니스 로직 구현건물의 배관/전기 시스템
3층 — ProjectionsProjection View + Projection BDEF서비스별 맞춤 노출 (필드 선택, UI 어노테이션)인테리어와 내부 설계
4층 — Business ServiceService Definition + Service Binding외부 노출, OData 프로토콜 바인딩건물의 출입문과 간판

Managed vs Unmanaged

Managed Scenario는 프레임워크가 표준 CRUD(Create, Read, Update, Delete) 작업을 자동으로 처리합니다. 그린필드(새 프로젝트) 시나리오에 적합하며, 개발자는 Validation(검증), Determination(자동 계산), Action(사용자 정의 작업) 같은 비표준 로직만 직접 구현하면 됩니다. DB 테이블에 대한 INSERT/UPDATE/DELETE 문을 직접 작성할 필요가 없습니다.

Unmanaged Scenario는 기존 레거시 코드나 BAPI가 이미 있는 브라운필드 프로젝트에 적합합니다. 모든 CRUD 로직을 개발자가 ABP 클래스 안에서 직접 구현해야 합니다.

선택 기준 요약: 새 테이블에서 시작하는 신규 개발이라면 Managed, 기존 로직을 재사용해야 한다면 Unmanaged를 선택하는 것이 일반적입니다.

5. 실전 코드 3단계

1단계: 기본 — DB Table + CDS Root View + Behavior Definition + Service

먼저 직원 정보를 저장할 데이터베이스 테이블을 생성합니다.

@EndUserText.label : 'Employee Table for RAP Demo'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table ztb_rap_emp {
  key client    : abap.clnt not null;
  key emp_uuid  : sysuuid_x16 not null;
  emp_id        : abap.numc(8);
  emp_name      : abap.char(50);
  department    : abap.char(30);
  role_title    : abap.char(40);
  salary        : abap.dec(11,2);
  created_at    : timestampl;
  changed_at    : timestampl;
}

다음으로 이 테이블 위에 CDS Root View Entity를 정의합니다. 이 뷰가 RAP Business Object의 루트 역할을 합니다.

@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Employee RAP BO - Root View'
define root view entity ZR_RapEmployee
  as select from ztb_rap_emp
{
  key emp_uuid       as EmpUuid,
      emp_id         as EmpId,
      emp_name       as EmpName,
      department     as Department,
      role_title     as RoleTitle,
      salary         as Salary,
      @Semantics.systemDateTime.createdAt: true
      created_at     as CreatedAt,
      @Semantics.systemDateTime.lastChangedAt: true
      changed_at     as ChangedAt
}

이제 Behavior Definition을 생성하여 이 BO가 Managed 타입임을 선언하고 CRUD를 활성화합니다.

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

define behavior for ZR_RapEmployee alias Employee
  persistent table ztb_rap_emp
  lock master
  etag master ChangedAt
{
  create;
  update;
  delete;

  field ( readonly, numbering : managed ) EmpUuid;

  mapping for ztb_rap_emp
  {
    EmpUuid    = emp_uuid;
    EmpId      = emp_id;
    EmpName    = emp_name;
    Department = department;
    RoleTitle  = role_title;
    Salary     = salary;
    CreatedAt  = created_at;
    ChangedAt  = changed_at;
  }
}

managed 키워드 덕분에 프레임워크가 DB 저장 로직을 자동으로 처리합니다. strict ( 2 )는 최신 RAP 엄격 모드를 적용하여 더 안전한 개발을 유도합니다. numbering : managed는 UUID 키를 프레임워크가 자동 생성하도록 지시합니다.

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

@EndUserText.label: 'Employee Management Service'
define service ZSD_Employee {
  expose ZR_RapEmployee as Employee;
}

Service Binding은 ADT 위저드에서 OData V2 또는 V4 프로토콜을 선택하여 생성합니다. Binding을 활성화(Publish)하면 Fiori Elements Preview로 기본 CRUD 앱을 즉시 테스트할 수 있습니다.

2단계: 실무 — Projection, Metadata Extension, UI Annotation, Draft

프로덕션 수준의 앱에서는 Root View를 직접 노출하지 않고 Projection View를 통해 서비스별로 필요한 필드와 UI를 구성합니다.

@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Employee Projection View'
@Metadata.allowExtensions: true
define root view entity ZC_RapEmployee
  provider contract transactional_query
  as projection on ZR_RapEmployee
{
  key EmpUuid,
      EmpId,
      EmpName,
      Department,
      RoleTitle,
      Salary,
      CreatedAt,
      ChangedAt
}

UI 어노테이션은 Metadata Extension으로 분리하여 관리합니다. 이렇게 하면 CDS View와 화면 설정을 독립적으로 유지보수할 수 있습니다.

@Metadata.layer: #CUSTOMER
annotate entity ZC_RapEmployee with
{
  @UI.facet: [
    { id: 'EmployeeInfo',
      purpose: #STANDARD,
      type: #IDENTIFICATION_REFERENCE,
      label: 'Employee Details',
      position: 10 }
  ]

  @UI: { lineItem:       [{ position: 10 }],
         identification: [{ position: 10 }],
         selectionField: [{ position: 10 }] }
  EmpId;

  @UI: { lineItem:       [{ position: 20 }],
         identification: [{ position: 20 }],
         selectionField: [{ position: 20 }] }
  EmpName;

  @UI: { lineItem:       [{ position: 30 }],
         identification: [{ position: 30 }],
         selectionField: [{ position: 30 }] }
  Department;

  @UI: { lineItem:       [{ position: 40 }],
         identification: [{ position: 40 }] }
  RoleTitle;

  @UI: { lineItem:       [{ position: 50 }],
         identification: [{ position: 50 }] }
  Salary;
}

Draft 기능을 추가하면 사용자가 데이터를 임시 저장하고 나중에 이어서 편집할 수 있습니다. Root View의 Behavior Definition에 with draft와 Draft 테이블을 추가합니다.

managed implementation in class ZBP_R_RAPEMPLOYEE unique;
strict ( 2 );
with draft;

define behavior for ZR_RapEmployee alias Employee
  persistent table ztb_rap_emp
  draft table ztb_rap_emp_d
  lock master total etag ChangedAt
  etag master ChangedAt
{
  create;
  update;
  delete;

  field ( readonly, numbering : managed ) EmpUuid;

  draft action Edit;
  draft action Activate optimized;
  draft action Discard;
  draft action Resume;
  draft determine action Prepare;

  mapping for ztb_rap_emp
  {
    EmpUuid    = emp_uuid;
    EmpId      = emp_id;
    EmpName    = emp_name;
    Department = department;
    RoleTitle  = role_title;
    Salary     = salary;
    CreatedAt  = created_at;
    ChangedAt  = changed_at;
  }
}

Projection Behavior Definition도 별도로 생성합니다.

projection;
strict ( 2 );
use draft;

define behavior for ZC_RapEmployee alias Employee
{
  use create;
  use update;
  use delete;

  use action Edit;
  use action Activate;
  use action Discard;
  use action Resume;
  use action Prepare;
}

Service Definition도 Projection View를 노출하도록 수정합니다.

@EndUserText.label: 'Employee Management Service'
define service ZSD_Employee {
  expose ZC_RapEmployee as Employee;
}

3단계: 프로덕션 — Validation, Determination, Action, ETag, Lock

실제 운영 시스템에서는 비즈니스 규칙을 강제하는 Validation, 필드를 자동 설정하는 Determination, 사용자 정의 작업인 Action을 추가합니다. Behavior Definition에 다음을 선언합니다.

" Behavior Definition에 추가할 내용

  " 저장 시 이름 필수 검증
  validation validateName on save { create; update; field EmpName; }

  " 생성 시 기본값 자동 설정
  determination setDefaults on modify { create; }

  " 급여 인상 액션
  action raiseSalary result [1] $self;

  " 필수 입력 필드 지정
  field ( mandatory ) EmpName, Department;

Behavior Pool 클래스(ZBP_R_RAPEMPLOYEE)에 각 로직을 구현합니다.

CLASS lhc_Employee DEFINITION INHERITING FROM cl_abap_behavior_handler.
  PRIVATE SECTION.
    METHODS validateName FOR VALIDATE ON SAVE
      IMPORTING keys FOR Employee~validateName.

    METHODS setDefaults FOR DETERMINE ON MODIFY
      IMPORTING keys FOR Employee~setDefaults.

    METHODS raiseSalary FOR MODIFY
      IMPORTING keys FOR ACTION Employee~raiseSalary
      RESULT result.
ENDCLASS.

CLASS lhc_Employee IMPLEMENTATION.

  METHOD validateName.
    READ ENTITIES OF ZR_RapEmployee IN LOCAL MODE
      ENTITY Employee
        FIELDS ( EmpName )
        WITH CORRESPONDING #( keys )
      RESULT DATA(lt_employees).

    LOOP AT lt_employees INTO DATA(ls_emp).
      IF ls_emp-EmpName IS INITIAL.
        APPEND VALUE #(
          %tky = ls_emp-%tky
        ) TO failed-employee.

        APPEND VALUE #(
          %tky     = ls_emp-%tky
          %msg     = new_message_with_text(
                       severity = if_abap_behv_message=>severity-error
                       text     = 'Employee name is required.' )
          %element-EmpName = if_abap_behv=>mk-on
        ) TO reported-employee.
      ENDIF.
    ENDLOOP.
  ENDMETHOD.

  METHOD setDefaults.
    READ ENTITIES OF ZR_RapEmployee IN LOCAL MODE
      ENTITY Employee
        ALL FIELDS
        WITH CORRESPONDING #( keys )
      RESULT DATA(lt_employees).

    MODIFY ENTITIES OF ZR_RapEmployee IN LOCAL MODE
      ENTITY Employee
        UPDATE FIELDS ( Department Salary )
        WITH VALUE #( FOR emp IN lt_employees
          ( %tky       = emp-%tky
            Department = COND #( WHEN emp-Department IS INITIAL
                                 THEN 'General' ELSE emp-Department )
            Salary     = COND #( WHEN emp-Salary IS INITIAL
                                 THEN '50000.00' ELSE emp-Salary ) ) )
      REPORTED DATA(lt_reported).
  ENDMETHOD.

  METHOD raiseSalary.
    READ ENTITIES OF ZR_RapEmployee IN LOCAL MODE
      ENTITY Employee
        FIELDS ( Salary )
        WITH CORRESPONDING #( keys )
      RESULT DATA(lt_employees).

    MODIFY ENTITIES OF ZR_RapEmployee IN LOCAL MODE
      ENTITY Employee
        UPDATE FIELDS ( Salary )
        WITH VALUE #( FOR emp IN lt_employees
          ( %tky   = emp-%tky
            Salary = emp-Salary * '1.10' ) )  " 10% 인상
      REPORTED DATA(lt_reported).

    READ ENTITIES OF ZR_RapEmployee IN LOCAL MODE
      ENTITY Employee
        ALL FIELDS
        WITH CORRESPONDING #( keys )
      RESULT DATA(lt_result).

    result = VALUE #( FOR emp IN lt_result
      ( %tky   = emp-%tky
        %param = emp ) ).
  ENDMETHOD.

ENDCLASS.

동시성 제어에 대해 정리하면 다음과 같습니다.

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

FAQ 1: "CDS View 활성화 시 mapping 오류가 발생합니다"

Behavior Definition의 mapping for 구문에서 CDS View 필드명(CamelCase)과 DB 테이블 필드명(언더스코어)이 정확히 매핑되어야 합니다. 대소문자와 필드 수가 일치하는지 확인하세요. 특히 client 필드는 mapping에 포함하지 않습니다.

FAQ 2: "Fiori Preview에서 Create 버튼이 보이지 않습니다"

Behavior Definition에 create;가 선언되어 있는지, Projection Behavior Definition에 use create;가 있는지 확인하세요. 또한 Service Binding이 올바르게 Publish 되었는지, Projection View를 노출하고 있는지 점검하세요. Draft 모드 사용 시 draft action Edit; 등 필수 draft action이 빠져 있으면 에디팅 자체가 작동하지 않습니다.

FAQ 3: "strict ( 2 ) 적용 후 기존 코드에서 오류가 발생합니다"

strict ( 2 )는 최신 RAP 엄격 모드로, 더 많은 구문 검증을 수행합니다. 기존에 strict 없이 작성된 코드를 마이그레이션할 때는 단계적으로 strict ( 1 )을 먼저 적용한 뒤 strict ( 2 )로 올리는 것이 권장됩니다. 일반적으로 신규 프로젝트에서는 처음부터 strict ( 2 )를 사용합니다.

FAQ 4: "Draft 테이블 생성을 잊어서 런타임 에러가 발생합니다"

with draft를 선언했다면 반드시 draft table에 지정할 Draft 전용 DB 테이블을 별도로 생성해야 합니다. Draft 테이블은 원본 테이블과 동일한 필드 구조에 추가적인 Draft 관리 필드가 포함됩니다. ADT의 Quick Fix 기능을 활용하면 Draft 테이블을 자동 생성할 수 있습니다.

FAQ 5: "Validation 메시지가 화면에 표시되지 않습니다"

reported 구조체에 메시지를 추가할 때 %element를 함께 지정해야 해당 필드 옆에 오류 표시가 나타납니다. %element 없이 메시지만 추가하면 일반 메시지로만 표시되어 사용자가 어떤 필드에 문제가 있는지 파악하기 어렵습니다.

7. 다음 단계 / 관련 주제

이 튜토리얼에서 다룬 단일 엔티티 Managed Scenario를 기반으로 다음 주제를 학습하면 RAP 역량을 한 단계 높일 수 있습니다.

8. 참고 자료


📌 본 게시물은 AI(Claude)가 공개된 자료를 기반으로 자동 생성한 콘텐츠입니다. 기술 내용의 정확성은 SAP 공식 문서 와 교차 확인하시기 바랍니다.

™ SAP, S/4HANA, ABAP, Fiori, SAP BTP 등은 SAP SE 또는 그 계열사의 등록 상표입니다. 본 사이트는 SAP SE 와 공식적인 관련이 없는 비공식 학습 자료 입니다.

📧 저작권 침해 / 오류 / 콘텐츠 신고: btpstacks.com 의 "문의" 메뉴를 이용해주세요.