[RAP] Side Effects와 Business Events로 워크플로우 자동화 구현 — 필드 연동부터 이벤트 드리븐 처리까지
개요 및 학습 목표
RAP(ABAP RESTful Application Programming Model)에서 단순 CRUD를 넘어 자동화 워크플로우를 구현하려면 Side Effects와 Business Events 두 가지 기둥을 반드시 이해해야 합니다. Side Effects는 필드/엔티티 변경 시 UI 갱신과 재계산을 자동으로 트리거하고, Business Events는 BO(Business Object) 외부 시스템(BTP Event Mesh, Integration Suite, 다른 ABAP 서비스)으로 비동기 통합을 가능하게 합니다. 본 튜토리얼은 Determination과 Side Effects의 연계, Event 선언 및 발행, Local/External Consumer 구현까지 한 번에 정리합니다.
학습 체크리스트
- Side Effects의 4가지 종류(field, entity, action, determine action)를 구분할 수 있다
- Determination ON MODIFY와 Side Effects를 연계해 자동 재계산 화면을 구성할 수 있다
- BDEF에서 event를 선언하고 RAISE ENTITY EVENT로 발행할 수 있다
- Local Event Consumer와 SAP Event Mesh 연동의 차이를 설명할 수 있다
- Draft → Active → Event 발행 → 후처리 워크플로우를 설계할 수 있다
선수 지식
본 글은 RAP의 4-Layer 구조(Data Model, Behavior, Projection, Service Binding)와 Managed 시나리오 기본기를 전제로 합니다. CDS View Entity, Behavior Definition(BDEF), Behavior Implementation(BIL) 클래스의 역할을 알고 있어야 하며, Validation/Determination/Action 기초 문법을 한 번이라도 작성해 본 경험을 권장합니다. 또한 ADT(ABAP Development Tools in Eclipse) 사용법과 Draft 처리 개념(%is_draft, %tky)에 대한 이해가 필요합니다.
환경 / 버전 / 준비물
- 플랫폼: SAP BTP, ABAP Environment (Steampunk) 또는 S/4HANA Cloud Private Edition 2023+, 온프레미스 S/4HANA 2022 FPS01 이상
- 개발 도구: ADT (Eclipse 2024-03 이상 + ABAP Development Tools 플러그인)
- RAP 시나리오: Managed with Unmanaged Save 또는 Managed (Draft Enabled)
- 이벤트 인프라: SAP Event Mesh (BTP 서비스) 또는 Local Event Consumption (ABAP 내부 처리)
- 샘플 모델:
/DMO/I_TRAVEL_M등 Travel/Booking 데모 데이터 모델 (ABAP Flight Reference Scenario) - 권한: Business Catalog
SAP_A4C_BC_DEVELOPER또는 Developer Extensibility 역할
참고: Business Events는 ABAP Cloud(릴리스 2208 이후) 환경에서 가장 일관된 동작을 보입니다. 온프레미스 환경에서는 RFC 기반 Outbound 처리로 대체되는 경우가 일반적입니다.
핵심 개념
RAP의 자동화 메커니즘을 이해하기 위해서는 트리거가 발생하는 위치와 처리 시점을 구분해 보아야 합니다.
Side Effects — UI 갱신의 자동 신호
Side Effects는 사용자가 필드를 변경했을 때 관련된 다른 필드/엔티티가 다시 계산되어야 함을 Fiori Elements UI에 알리는 메타데이터 선언입니다. 마치 엑셀에서 셀 A1 값을 바꾸면 SUM 수식이 들어간 셀 B1이 자동으로 갱신되는 것과 같은 원리입니다. 단, RAP의 Side Effects는 실제 계산 로직이 아니라 "이 필드가 바뀌면 저 필드를 다시 읽어와라"는 의존성 선언일 뿐입니다. 실제 계산은 Determination이 담당합니다.
Determination — 자동 계산의 실행자
Determination은 BO 인스턴스를 변경하는 행위(create, update, save)를 트리거로 해당 인스턴스의 다른 필드 값을 채우거나 변환합니다. ON MODIFY는 변경 즉시, ON SAVE는 트랜잭션 저장 시점에 동작합니다. SAP 도움말에 따르면 Determination은 "트리거 조건에 따라 BO 인스턴스를 수정하는 BO 동작의 옵션 부분"으로 정의됩니다.
Business Events — BO 외부로의 신호
Business Events는 BO 내부의 의미 있는 상태 변화(예: 여행 승인, 주문 확정)를 BO 외부 세계에 알리는 비동기 메시지입니다. CloudEvents v1.0 표준 형식으로 직렬화되어 SAP Event Mesh 또는 Local Consumer로 전달됩니다. "이벤트 드리븐 아키텍처"의 ABAP 측 진입점이라 할 수 있으며, 마이크로서비스/통합 시나리오에서 핵심 역할을 합니다.
전체 흐름 도식
사용자 입력(필드 변경)
↓
[Side Effects] ── UI에 "재로드 필요" 신호
↓
[Determination ON MODIFY] ── 필드 자동 계산
↓
SAVE 트랜잭션
↓
[Determination ON SAVE] ── 최종 검증/보정
↓
[Action 실행] ── 상태 전환
↓
[Business Event RAISE] ── 외부 시스템 비동기 통보
↓
Event Mesh / Local Consumer 후처리
실전 코드 3단계
1단계 — 기본: Side Effects + Determination 연계
Travel 엔티티에서 BookingFee 또는 CurrencyCode가 변경되면 TotalPrice가 자동 재계산되도록 구성합니다. BDEF에 Side Effect와 Determination을 함께 선언합니다.
managed implementation in class zbp_rap_traveltp unique;
strict ( 2 );
define behavior for ZRAP_R_TravelTP alias Travel
persistent table ztravel_t
lock master
authorization master ( instance )
etag master LastChangedAt
{
create;
update;
delete;
field ( numbering : managed, readonly ) TravelUUID;
field ( readonly ) TotalPrice, OverallStatus, LastChangedAt;
determination calculateTotalPrice on modify
{ field BookingFee, CurrencyCode; }
determination setStatusToOpen on modify { create; }
side effects
{
field BookingFee affects field TotalPrice;
field CurrencyCode affects field TotalPrice;
field CustomerID affects field FullName;
}
}
Behavior Implementation의 Determination 메서드는 다음 패턴을 따릅니다.
METHOD calculateTotalPrice.
READ ENTITIES OF zrap_r_traveltp IN LOCAL MODE
ENTITY Travel
FIELDS ( BookingFee CurrencyCode )
WITH CORRESPONDING #( keys )
RESULT DATA(travels).
MODIFY ENTITIES OF zrap_r_traveltp IN LOCAL MODE
ENTITY Travel
UPDATE FIELDS ( TotalPrice )
WITH VALUE #( FOR t IN travels (
%tky = t-%tky
TotalPrice = t-BookingFee ) )
REPORTED DATA(modify_reported).
ENDMETHOD.
2단계 — 실무: Action + Business Event 발행
여행 승인 Action을 만들고, 승인 성공 시 Business Event를 발행합니다. 외부 회계 시스템이 이 이벤트를 받아 자동으로 청구서를 생성하는 시나리오입니다.
define behavior for ZRAP_R_TravelTP alias Travel
{
...
action acceptTravel result [1] $self;
action rejectTravel result [1] $self;
event TravelAccepted parameter /dmo/d_travel_accepted_params;
event TravelRejected parameter /dmo/d_travel_rejected_params;
}
METHOD acceptTravel.
" 1) 상태를 ACCEPTED로 변경
MODIFY ENTITIES OF zrap_r_traveltp IN LOCAL MODE
ENTITY Travel
UPDATE FIELDS ( OverallStatus )
WITH VALUE #( FOR k IN keys (
%tky = k-%tky
OverallStatus = travel_status-accepted ) )
REPORTED DATA(update_reported).
" 2) 변경된 데이터 다시 읽기
READ ENTITIES OF zrap_r_traveltp IN LOCAL MODE
ENTITY Travel
ALL FIELDS WITH CORRESPONDING #( keys )
RESULT DATA(accepted_travels).
" 3) 결과 반환
result = VALUE #( FOR t IN accepted_travels (
%tky = t-%tky
%param = t ) ).
" 4) Business Event 발행
TRY.
RAISE ENTITY EVENT zrap_r_traveltp~TravelAccepted
FROM VALUE #( FOR t IN accepted_travels (
%key = VALUE #( TravelUUID = t-TravelUUID )
%param = VALUE #(
TravelId = t-TravelID
AgencyId = t-AgencyID
CustomerId = t-CustomerID
TotalPrice = t-TotalPrice ) ) ).
CATCH cx_abap_behv INTO DATA(lx_event).
" 로깅 - 이벤트 발행 실패는 트랜잭션을 막지 않음
" (cl_bali_*, application log 권장)
ENDTRY.
ENDMETHOD.
UI에 Action 버튼을 노출하려면 메타데이터 확장에 다음을 추가합니다.
@UI.identification: [
{ type: #FOR_ACTION, dataAction: 'acceptTravel', label: 'Accept Travel' },
{ type: #FOR_ACTION, dataAction: 'rejectTravel', label: 'Reject Travel' }
]
3단계 — 프로덕션: Local Event Consumer + 에러 격리
Local Event Consumer는 ABAP 시스템 내부에서 RAP 이벤트를 구독하는 클래스입니다. 외부 Event Mesh를 거치지 않으므로 지연이 짧고 운영 비용이 낮습니다. 인터페이스 IF_RAP_EVENT_HANDLER의 자동 생성된 하위 인터페이스를 구현합니다.
CLASS zcl_travel_accepted_handler DEFINITION
PUBLIC FINAL
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_zrap_r_traveltp_handler.
ENDCLASS.
CLASS zcl_travel_accepted_handler IMPLEMENTATION.
METHOD if_zrap_r_traveltp_handler~travelaccepted.
" parameters에 RAISE ENTITY EVENT의 %param이 전달됨
LOOP AT parameters INTO DATA(ls_param).
TRY.
" 후처리: 회계 마이크로서비스 호출, 알림 발송 등
zcl_billing_facade=>create_invoice(
iv_travel_id = ls_param-travelid
iv_customer = ls_param-customerid
iv_amount = ls_param-totalprice ).
CATCH cx_root INTO DATA(lx_post).
" Application Log에 적재 후 재시도 큐로
zcl_event_dlq=>enqueue(
iv_event_id = 'TravelAccepted'
iv_payload = ls_param
iv_error = lx_post->get_text( ) ).
ENDTRY.
ENDLOOP.
ENDMETHOD.
ENDCLASS.
외부 시스템(BTP Integration Suite, S3, Slack 등)으로 보내려면 SAP Event Mesh 통신 시나리오를 활성화하고 Communication Arrangement에 채널을 바인딩합니다. 이때 이벤트 페이로드는 CloudEvents JSON으로 자동 변환됩니다.
{
"specversion": "1.0",
"type": "sap.s4.beh.travel.v1.TravelAccepted",
"source": "/default/sap.s4.beh/12345",
"id": "9f3d2e1a-...",
"time": "2026-04-29T08:30:00Z",
"datacontenttype": "application/json",
"data": {
"TravelId": "0000000123",
"AgencyId": "70012",
"CustomerId": "10401",
"TotalPrice": "1850.00"
}
}
흔한 실수 / 트러블슈팅
FAQ 1. Side Effects를 선언했는데 UI가 갱신되지 않습니다.
가장 흔한 원인은 Projection BDEF에 use side effects 선언을 누락한 경우입니다. Side Effects는 BO Behavior에 선언했더라도 Projection Layer에서 use side effects로 노출해야 OData 메타데이터에 포함됩니다. 또한 Fiori Elements는 readonly가 아닌 결과 필드를 일반적으로 갱신하지 않으므로, TotalPrice 같은 계산 필드는 field ( readonly )로 표시해야 합니다.
FAQ 2. RAISE ENTITY EVENT가 호출되었는데 Consumer가 동작하지 않습니다.
이벤트는 SAVE 단계가 정상 완료된 후 실제로 발행됩니다. Action 메서드 안에서 RAISE를 호출했더라도, 이후 ABAP 런타임이 트랜잭션을 롤백하면 이벤트도 발행되지 않습니다. 또한 Local Consumer 클래스는 BDEF의 local handler 등록 또는 별도 생성된 핸들러 인터페이스 구현이 필요합니다. ADT의 Display Event Bindings 기능으로 바인딩 상태를 확인하세요.
FAQ 3. Determination이 무한 루프에 빠집니다.
Determination 안에서 자기 자신이 트리거하는 필드를 다시 MODIFY하면 재귀가 발생할 수 있습니다. RAP 런타임은 일반적으로 같은 트랜잭션 사이클 내 재진입을 방어하지만, 서로 다른 두 Determination이 상호 의존하는 경우에는 명시적으로 트리거 필드를 분리해야 합니다. 또한 ON MODIFY와 ON SAVE를 혼용할 때 ON SAVE에서 ON MODIFY 트리거 필드를 또 변경하지 않도록 주의합니다.
기타 트러블슈팅 팁
- 성능: 대량 처리 Determination은 keys 전체를 한 번에 읽고/수정해야 하며, LOOP 내 단건 MODIFY는 피합니다.
- 로깅:
cl_bali_log또는 Application Log API로 이벤트 발행 결과를 적재하면 운영 이슈 추적이 쉬워집니다. - 테스트:
CL_CDS_TEST_ENVIRONMENT+CL_BEHV_TEST_시리즈로 Determination 단위 테스트가 가능합니다. Event는 일반적으로 통합 테스트 단계에서 검증합니다.
다음 단계 / 관련 주제
본 튜토리얼은 자동화 워크플로우의 골격을 다뤘습니다. 다음 주제로 확장하여 학습을 이어가시기를 권장합니다.
- Draft Determine Action: Draft 저장 시점에 검증을 자동 실행하는 패턴 (Prepare 액션)
- Factory Action: copyTravel처럼 새 인스턴스를 자동 생성하는 패턴,
%is_draft활용 - SAP Event Mesh + Integration Suite: 발행된 이벤트를 외부 시스템 자동 라우팅
- Background Processing Framework (bgPF): 이벤트 후처리를 비동기 잡으로 분리해 트랜잭션 부담 분산
- RAP Generator: 위의 모든 요소를 템플릿으로 자동 생성