Abstract Entity와 RAP Action 파라미터 설계의 출발점
SAP S/4HANA Cloud Private Edition 2023 및 ABAP Platform 2023 이상에서 RESTful ABAP Programming Model(RAP)을 다루다 보면, Action을 정의할 때 단일 스칼라 파라미터로는 표현하기 어려운 복합 입력이 자주 등장합니다. 예를 들어 수주(SalesOrder)를 확정 처리할 때 확정 일자, 출고 창고, 배송 우선순위, 승인자 코멘트를 동시에 받아야 한다면, 단순한 import 파라미터로는 한계가 명확합니다. 이때 Abstract Entity가 해결책이 됩니다. 이 예제에서 다음을 학습합니다.
- Abstract Entity의 정의와 일반 CDS 뷰와의 차이
- RAP Action 파라미터에 Abstract Entity를 바인딩하는 문법
- Behavior Implementation에서 복합 파라미터를 읽고 검증하는 패턴
- 수주 확정 시나리오로 보는 프로덕션 수준 설계
- 흔한 함정과 트러블슈팅 체크리스트
읽기 전에 갖춰야 할 배경
이 예제는 RAP의 Managed/Unmanaged Scenario를 한 번이라도 구현해 본 ABAP 개발자를 대상으로 합니다. CDS View Entity 문법(define view entity), Behavior Definition(BDEF) 키워드(action, determination, validation), Behavior Implementation 클래스(FOR BEHAVIOR OF, FOR MODIFY) 작성 경험이 필요합니다. 추가로 Service Definition과 Service Binding을 통해 OData V4 엔드포인트를 노출해 본 경험이 있으면 이해가 빠릅니다.
실습 환경과 준비물 정리
예제 코드는 ABAP Development Tools(ADT) for Eclipse 2024-03 패치 이상, ABAP Platform 2023 SP00 이상, S/4HANA Cloud Private Edition 2023 또는 BTP ABAP Environment 2024 릴리스에서 검증했습니다. 일부 문법(예: $self 참조, deep action result)은 7.55 이하에서는 동작이 제한되므로 반드시 릴리스를 확인하세요. 실습용 패키지를 하나 만들고 다음 리포지토리 객체를 미리 준비합니다.
- 루트 엔티티 테이블:
ZTB_SO_HEAD(수주 헤더) - 자식 엔티티 테이블:
ZTB_SO_ITEM(수주 품목) - CDS Root View:
ZR_SalesOrder - BDEF:
ZR_SalesOrder - Behavior Implementation Class:
ZBP_R_SalesOrder
권한 측면에서는 개발 시스템의 S_DEVELOP 권한과 패키지 쓰기 권한이 필요하며, 운영 이관 시에는 별도 게이트웨이 권한 검토가 일반적으로 권장됩니다.
Abstract Entity의 본질과 일반 뷰와의 차이
Abstract Entity는 키워드 define abstract entity로 선언되며, 데이터 소스를 가지지 않는 순수한 타입 정의 구조체입니다. 일반 CDS View Entity가 데이터베이스 테이블이나 다른 뷰를 from 절로 참조해 SELECT 가능한 결과셋을 만들어내는 반면, Abstract Entity는 SELECT 대상이 아니며 런타임 데이터가 존재하지 않습니다. 비유하자면 일반 CDS 뷰가 "이미 만들어진 요리"라면, Abstract Entity는 "주방에 전달되는 주문서 양식"에 가깝습니다.
이 차이가 중요한 이유는 RAP Action 파라미터의 성격과 정확히 맞아떨어지기 때문입니다. Action에 전달되는 입력은 호출 시점에만 존재하며 영구 저장되지 않습니다. 즉 영속 데이터가 없는 일시적 입력 구조체를 표현하는 데 Abstract Entity가 의미적으로 적합합니다. 또한 Abstract Entity는 다음 특성을 가집니다.
- 키 필드 정의가 선택 사항이지만, 액션 파라미터로 쓸 때는 키 필드를 두지 않는 것이 일반적
- 연관(Association) 정의 가능하나, 액션 파라미터 용도에서는 잘 쓰지 않음
@Semantics,@EndUserText.label같은 어노테이션을 통해 Fiori Elements 자동 UI 생성에 영향- 중첩(Deep) 구조 표현이 가능하여, 헤더+품목 형태의 입력도 표현 가능
특히 Fiori Elements의 Action 파라미터 다이얼로그가 Abstract Entity 필드를 기반으로 입력 폼을 자동 렌더링한다는 점은 실무에서 큰 장점으로 꼽힙니다. 라벨, 값 도움말(Value Help), 필수 여부를 어노테이션으로 한 번 정의하면 UI에 그대로 반영됩니다.
실전 코드 1단계: 기본 Abstract Entity와 Action 바인딩
먼저 가장 단순한 형태의 Abstract Entity를 정의하고, BDEF에서 Action 입력으로 연결합니다. 수주 확정(confirmOrder) 액션에 필요한 확정일자와 코멘트만 받는 형태입니다.
@EndUserText.label: 'SalesOrder Confirm Input'
define abstract entity ZA_SoConfirmInput
{
@EndUserText.label: 'Confirm Date'
confirmDate : abap.dats;
@EndUserText.label: 'Approver Comment'
approverNote : abap.string(255);
}
이제 BDEF에서 액션을 정의하면서 위 Abstract Entity를 입력으로 지정합니다.
managed implementation in class zbp_r_salesorder unique;
strict ( 2 );
define behavior for ZR_SalesOrder alias SalesOrder
persistent table ztb_so_head
lock master
authorization master ( instance )
{
field ( readonly ) SalesOrderId;
field ( mandatory ) CustomerId, OrderDate;
action ( features : instance ) confirmOrder
parameter ZA_SoConfirmInput
result [1] $self;
}
핵심은 parameter ZA_SoConfirmInput 구문입니다. 이 한 줄로 액션 호출 시 Abstract Entity 구조 전체가 입력으로 전달됩니다. result [1] $self는 액션 실행 후 동일 인스턴스를 반환하라는 의미로, Fiori Elements가 UI를 자동 갱신할 때 활용됩니다.
실전 코드 2단계: 깊은 구조와 검증 로직 포함
실무에서는 단일 평면 구조로 끝나지 않는 경우가 많습니다. 수주 확정 시 품목별로 출고 창고와 배치 번호를 다르게 지정해야 한다면, Abstract Entity 내부에 테이블 타입 필드를 둘 수 있습니다. ABAP RAP에서는 Abstract Entity 자체를 중첩하기보다는 BDEF에서 deep parameter를 별도로 묶거나, 별도 Abstract Entity를 참조하는 패턴을 씁니다.
@EndUserText.label: 'Item Shipping Override'
define abstract entity ZA_SoItemShipping
{
key itemPosition : abap.numc(6);
warehouseId : abap.char(4);
batchNumber : abap.char(10);
shippingPriority : abap.char(2);
}
@EndUserText.label: 'SalesOrder Confirm Input v2'
define abstract entity ZA_SoConfirmInputV2
{
confirmDate : abap.dats;
approverNote : abap.string(255);
defaultWarehouse: abap.char(4);
itemOverrides : array of ZA_SoItemShipping;
}
Behavior Implementation에서는 다음과 같이 파라미터를 수신하고 검증합니다.
METHOD confirmorder.
DATA: lt_messages TYPE TABLE FOR REPORTED EARLY salesorder,
lt_failed TYPE TABLE FOR FAILED EARLY salesorder.
LOOP AT keys ASSIGNING FIELD-SYMBOL(<ls_key>).
DATA(ls_input) = <ls_key>-%param.
" 1) 필수 검증
IF ls_input-confirmdate IS INITIAL
OR ls_input-confirmdate < cl_abap_context_info=>get_system_date( ).
APPEND VALUE #(
%tky = <ls_key>-%tky
%msg = NEW zcm_salesorder(
severity = if_abap_behv_message=>severity-error
textid = zcm_salesorder=>invalid_confirm_date )
%element-confirmdate = if_abap_behv=>mk-on
) TO reported-salesorder.
APPEND VALUE #( %tky = <ls_key>-%tky ) TO failed-salesorder.
CONTINUE.
ENDIF.
" 2) 상태 전이 검증 (이미 확정된 주문은 재확정 불가)
READ ENTITY IN LOCAL MODE zr_salesorder
FIELDS ( orderstatus customerid )
WITH VALUE #( ( %tky = <ls_key>-%tky ) )
RESULT DATA(lt_so).
IF lt_so[ 1 ]-orderstatus = 'CONFIRMED'.
APPEND VALUE #(
%tky = <ls_key>-%tky
%msg = NEW zcm_salesorder(
severity = if_abap_behv_message=>severity-error
textid = zcm_salesorder=>already_confirmed )
) TO reported-salesorder.
APPEND VALUE #( %tky = <ls_key>-%tky ) TO failed-salesorder.
CONTINUE.
ENDIF.
" 3) 헤더 업데이트
MODIFY ENTITIES OF zr_salesorder IN LOCAL MODE
ENTITY salesorder
UPDATE FIELDS ( orderstatus confirmdate approvernote )
WITH VALUE #( (
%tky = <ls_key>-%tky
orderstatus = 'CONFIRMED'
confirmdate = ls_input-confirmdate
approvernote = ls_input-approvernote
) )
REPORTED DATA(ls_rep).
" 4) 품목별 출고 정보 갱신
LOOP AT ls_input-itemoverrides INTO DATA(ls_item).
MODIFY ENTITIES OF zr_salesorder IN LOCAL MODE
ENTITY salesorderitem
UPDATE FIELDS ( warehouseid batchnumber shippingpriority )
WITH VALUE #( (
salesorderid = <ls_key>-salesorderid
itemposition = ls_item-itemposition
warehouseid = ls_item-warehouseid
batchnumber = ls_item-batchnumber
shippingpriority = ls_item-shippingpriority
) ).
ENDLOOP.
APPEND VALUE #( %tky = <ls_key>-%tky ) TO result.
ENDLOOP.
ENDMETHOD.
이 단계에서는 %param으로 Abstract Entity 입력에 접근하고, 검증 실패 시 reported와 failed에 표준 방식으로 기록합니다. %element-confirmdate는 어느 필드에서 오류가 발생했는지를 UI에 알려 하이라이트를 가능하게 합니다.
실전 코드 3단계: 프로덕션 수준 보강
운영 환경에서는 권한 체크, 트랜잭션 로깅, 그리고 단위 테스트가 추가됩니다. 권한 객체 검증과 Application Log 기록을 포함한 패턴은 다음과 같습니다.
METHOD confirmorder.
" 권한 사전 체크
AUTHORITY-CHECK OBJECT 'Z_SO_ACT'
ID 'ACTVT' FIELD '43'.
IF sy-subrc <> 0.
LOOP AT keys ASSIGNING FIELD-SYMBOL(<ls_k>).
APPEND VALUE #(
%tky = <ls_k>-%tky
%msg = NEW zcm_salesorder(
severity = if_abap_behv_message=>severity-error
textid = zcm_salesorder=>no_confirm_authority )
) TO reported-salesorder.
APPEND VALUE #( %tky = <ls_k>-%tky ) TO failed-salesorder.
ENDLOOP.
RETURN.
ENDIF.
DATA(lo_log) = zcl_app_log_factory=>get_logger( 'SO_CONFIRM' ).
LOOP AT keys ASSIGNING FIELD-SYMBOL(<ls_key>).
TRY.
DATA(ls_input) = <ls_key>-%param.
lo_log->info( |Confirm requested for { <ls_key>-salesorderid }| ).
" 검증 + 업데이트 로직 (2단계 코드 재사용)
" ...
CATCH cx_root INTO DATA(lx).
lo_log->error( lx->get_text( ) ).
APPEND VALUE #(
%tky = <ls_key>-%tky
%msg = new_msg( lx ) )
TO reported-salesorder.
APPEND VALUE #( %tky = <ls_key>-%tky ) TO failed-salesorder.
ENDTRY.
ENDLOOP.
ENDMETHOD.
ABAP Unit 테스트에서는 cl_abap_behv_test_helper나 EML 직접 호출 방식으로 액션을 검증합니다. Abstract Entity 입력은 일반 구조체처럼 채워 넣으면 됩니다.
METHOD test_confirm_order_success.
DATA(lt_param) = VALUE za_soconfirminputv2(
confirmdate = '20260620'
approvernote = 'Approved by QA test'
defaultwarehouse = '1010'
).
MODIFY ENTITIES OF zr_salesorder
ENTITY salesorder
EXECUTE confirmorder FROM VALUE #( (
salesorderid = '0000010001'
%param = lt_param ) )
FAILED DATA(lt_failed)
REPORTED DATA(lt_rep).
cl_abap_unit_assert=>assert_initial( lt_failed-salesorder ).
ENDMETHOD.
자주 마주치는 함정과 해결 체크리스트
현장에서 반복되는 이슈를 정리하면 다음과 같습니다.
Q1. Abstract Entity 필드를 추가했는데 Fiori UI에 보이지 않습니다.
Service Binding을 재게시(Republish)하지 않은 경우가 대부분입니다. BDEF 변경 후 Service Definition과 Service Binding을 순서대로 활성화하고 메타데이터 캐시를 비우세요. SAP Gateway 클라이언트의 메타데이터 새로 고침도 일반적으로 필요합니다.
Q2. %param이 항상 비어 있습니다.
BDEF의 parameter 절에 Abstract Entity 이름을 잘못 적었거나, 클라이언트가 POST 본문에 잘못된 JSON 키를 보낸 경우입니다. OData V4에서는 {"confirmDate": "...", "approverNote": "..."} 형태로 키 이름이 camelCase가 되며, CDS 필드명과 매핑이 정확히 일치해야 합니다.
Q3. deep parameter(array of)가 전달되지 않습니다.
ABAP Platform 릴리스에 따라 deep action input 지원 범위가 다릅니다. 7.56 이하에서는 array 타입 액션 파라미터 지원이 제한될 수 있으니, 가능하면 2023 SP 이상 또는 BTP ABAP Environment 최신 릴리스를 권장합니다. 임시 회피책으로 JSON 문자열을 받아 /ui2/cl_json으로 파싱하는 패턴도 쓰입니다.
그 외에도 Abstract Entity에 키를 정의하면 OData 메타데이터에서 별도 Entity Type으로 노출되어 혼란을 줄 수 있으므로, 액션 파라미터 용도의 Abstract Entity에는 키를 두지 않는 것이 일반적입니다. 또한 BDEF에서 strict ( 2 ) 모드를 켜면 누락된 매핑이나 잘못된 result 정의가 컴파일 시점에 잡히므로 활성화를 권장합니다.
이후 확장해 볼 만한 주제
Abstract Entity를 액션 파라미터로 익혔다면, Function의 입력 파라미터, Determination에서 사용되는 trigger condition 구조, 그리고 Custom Entity와의 차이를 비교 학습하길 권장합니다. 또한 BTP ABAP Environment의 Cloud Development 가이드라인을 따라 Released API 어노테이션과 함께 Abstract Entity를 공개하면, 외부 Extension에서도 동일한 입력 스키마를 재사용할 수 있습니다. Fiori Elements의 @UI.lineItem, @Consumption.valueHelpDefinition을 Abstract Entity 필드에 부착해 자동 입력 다이얼로그를 풍부하게 만드는 실험도 추천합니다.
댓글 0
아직 댓글이 없습니다.