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



1. 개요 및 학습 목표
SAP BTP ABAP 환경에서 RESTful Application Programming Model(RAP)은 Fiori 기반 트랜잭셔널 앱을 표준화된 방식으로 구축하는 프레임워크입니다. 이 튜토리얼에서는 RAP의 Managed Scenario를 활용하여 직원 관리 CRUD 앱을 처음부터 끝까지 만들어 봅니다. Managed Scenario는 프레임워크가 표준 CRUD 처리를 자동으로 수행하므로, 개발자는 비즈니스 로직에만 집중할 수 있습니다.
학습 목표 체크리스트:
- RAP Business Object(BO) 아키텍처 4개 계층의 역할을 설명할 수 있다
- Managed Scenario에서 DB Table, CDS View, Behavior Definition, Service를 직접 생성할 수 있다
- Projection View와 Metadata Extension으로 UI를 구성할 수 있다
- Validation, Determination, Action을 구현하여 비즈니스 로직을 추가할 수 있다
- ETag과 Lock을 설정하여 동시성 제어를 적용할 수 있다
2. 선수 지식
이 튜토리얼을 원활하게 따라가려면 다음 배경지식이 권장됩니다.
- ABAP 기초 문법 (DATA, SELECT, METHOD 등)
- CDS View 기본 개념 (DEFINE VIEW ENTITY 구문)
- SAP Fiori Elements 기본 이해 (List Report, Object Page 패턴)
- Eclipse ADT(ABAP Development Tools) 사용 경험
3. 환경 / 버전 / 준비물
이 튜토리얼은 다음 환경을 기준으로 작성되었습니다.
| 항목 | 상세 |
|---|---|
| SAP BTP Edition | SAP 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 Modeling | DB Table + CDS View Entity | 데이터 영속성(저장)과 추상화 | 건물의 기초와 골조 |
| 2층 — BO Behavior | Behavior Definition(BDEF) + Behavior Pool(ABP 클래스) | CRUD 동작 정의 및 비즈니스 로직 구현 | 건물의 배관/전기 시스템 |
| 3층 — Projections | Projection View + Projection BDEF | 서비스별 맞춤 노출 (필드 선택, UI 어노테이션) | 인테리어와 내부 설계 |
| 4층 — Business Service | Service 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.
동시성 제어에 대해 정리하면 다음과 같습니다.
- ETag (etag master) — 낙관적 잠금 방식입니다. 사용자가 데이터를 읽은 시점의 타임스탬프와 저장 시점의 값을 비교하여, 다른 사용자가 중간에 변경했다면 충돌을 감지합니다.
ChangedAt필드를 일반적으로 사용합니다. - Lock (lock master) — 편집 시작 시 잠금을 설정하여 동시 편집을 방지합니다. Draft 사용 시
total etag을 함께 설정하는 것이 권장됩니다.
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 역량을 한 단계 높일 수 있습니다.
- Composition (부모-자식 관계) — Header/Item 구조의 복합 BO 구성,
composition [0..*]활용 - Unmanaged Scenario — 기존 BAPI/Function Module을 래핑하는 브라운필드 접근법
- Abstract Entity & Custom Entity — 외부 API 호출이나 비표준 데이터 소스 통합
- Authorization Control —
authorization master를 통한 인스턴스/글로벌 수준 권한 제어 - Side Effects & Feature Control — 필드 변경 시 동적 UI 갱신 및 조건부 액션 활성화
- RAP Unit Test —
CL_ABAP_BEHV_TEST_ENVIRONMENT를 활용한 BO 테스트 자동화
8. 참고 자료
- SAP Help — ABAP RESTful Application Programming Model 개요
- SAP Help — Behavior Definition Language (BDL) 레퍼런스
- SAP Help — Managed Implementation Type 상세 설명
- SAP-samples GitHub — RAP Behavior Definition Language Cheat Sheet
- SAP Zero2Hero — RAP Managed Scenario Simple Example
- SAP RAP Series #1 — Building a Managed Scenario (Substack)
📌 본 게시물은 AI(Claude)가 공개된 자료를 기반으로 자동 생성한 콘텐츠입니다. 기술 내용의 정확성은 SAP 공식 문서 와 교차 확인하시기 바랍니다.
™ SAP, S/4HANA, ABAP, Fiori, SAP BTP 등은 SAP SE 또는 그 계열사의 등록 상표입니다. 본 사이트는 SAP SE 와 공식적인 관련이 없는 비공식 학습 자료 입니다.
📧 저작권 침해 / 오류 / 콘텐츠 신고: btpstacks.com 의 "문의" 메뉴를 이용해주세요.