RAP

구조체 vs Abstract Entity — RAP 액션 파라미터 차이 #shorts #SAP #RAP

▶ YouTube에서 보기

Action 파라미터 설계의 두 갈래

RAP(RESTful ABAP Programming Model)에서 Action을 구현할 때 개발자가 처음 마주치는 질문이 있다. 파라미터를 어디에 어떻게 정의할 것인가. 일반적인 ABAP 개발 경험을 가진 사람이라면 자연스럽게 딕셔너리 구조체(Data Dictionary Structure, DDIC Structure)를 떠올릴 것이다. 이미 존재하는 구조체를 재활용하거나, 새 구조체를 만들어 BDEF(Behavior Definition)에서 parameter 키워드 뒤에 참조하는 방식이다.

하지만 RAP는 이 방식과 전혀 다른 선택지를 제공한다. Abstract Entity다. 이름부터 낯설다. CDS 뷰인데 데이터베이스 테이블도 없고, 실제로 SQL로 조회할 수도 없다. 그렇다면 왜 SAP는 이 구조를 만들었을까. 그리고 언제 사용해야 할까.

이 글은 두 접근법의 기술적 차이를 명확히 하고, Abstract Entity가 RAP 아키텍처에서 갖는 고유한 의미를 설명한다.


DDIC 구조체 방식의 한계

먼저 기존 방식부터 살펴보자. BDEF에서 Action 파라미터를 DDIC 구조체로 정의하는 것은 다음과 같이 생긴다.

// BDEF: ZI_TravelApproval 의 Action 선언 (DDIC 구조체 방식)
action approve parameter ztrave_approve_s result [1] $self;

여기서 ztrave_approve_s는 SE11에서 만들어진 ABAP Dictionary Structure다. 이 방식은 작동한다. 하지만 몇 가지 구조적 문제를 안고 있다.

1. DDIC 의존성이 RAP 레이어를 오염시킨다

RAP는 CDS 기반의 선언적 프레임워크다. 비즈니스 오브젝트의 구조, 행동, 노출 방식 모두 CDS와 BDEF로 정의된다. 그런데 Action 파라미터만 SE11의 DDIC 구조체에 의존한다면, 이 비즈니스 오브젝트는 완전히 CDS 계층에서 독립적으로 관리될 수 없다. 누군가 구조체를 수정할 때 RAP의 영향이 고려되지 않을 수 있고, 반대로 RAP에서 파라미터를 변경하려면 SE11이라는 별도의 도구를 열어야 한다.

2. 재사용성이 제한된다

DDIC 구조체는 테이블 필드, 프로그램 변수, 함수 모듈 파라미터 등 다양한 곳에서 사용된다. 이 구조체가 RAP Action 파라미터 전용으로 만들어졌다 해도, 그 의미가 코드에서 명확하게 드러나지 않는다. 반면 Abstract Entity는 그 자체로 "이것은 CDS 기반의 순수한 구조 정의"임을 선언한다.

3. OData 메타데이터에서 타입이 불투명해진다

SAP Fiori Elements나 OData 서비스를 통해 외부에 노출될 때, DDIC 구조체 기반 파라미터는 Edm.String, Edm.Boolean 같은 기본 타입으로 평탄화(flatten)되지만 그 의미 정보가 약해진다. Abstract Entity를 사용하면 CDS 어노테이션을 통해 레이블, 검색 도움말, 의미 정보를 풍부하게 부여할 수 있고, 이것이 OData 메타데이터에도 반영된다.


Abstract Entity란 무엇인가

Abstract Entity는 CDS 뷰의 특별한 형태다. 일반 CDS 뷰와 달리 데이터베이스 테이블이나 다른 뷰를 select from으로 참조하지 않는다. 순수하게 필드 구조만 정의하는 타입 선언체다.

@EndUserText.label: 'Action Input for Travel Approval'
define abstract entity ZA_TravelApproveInput {
  key TravelId  : /dmo/travel_id;
  Reason        : abap_bool;
  Comment       : abap_clnt;
}

문법 구조를 보면 일반 CDS 뷰와 거의 동일하다. 차이는 define abstract entity라는 키워드와 as select from이 없다는 것이다. 이 뷰는 SQL로 조회할 수 없다. 데이터베이스에 대응하는 테이블이 없기 때문이다. 오직 타입 정의의 역할만 한다.

Abstract Entity가 가질 수 있는 요소

  • 필드 정의: 일반 CDS 뷰처럼 필드와 타입을 선언한다. ABAP 빌트인 타입, DDIC 도메인, 다른 데이터 요소를 참조할 수 있다.
  • CDS 어노테이션: @EndUserText.label, @Search.searchable, @Consumption.valueHelpDefinition 등을 사용할 수 있다.
  • Key 필드 선언: key 키워드를 사용할 수 있다. Action 파라미터에서는 주로 비즈니스 오브젝트의 키를 포함시킨다.
  • 어소시에이션(Association): 다른 CDS 뷰와의 어소시에이션도 정의 가능하다.

Abstract Entity를 Action 파라미터로 연결하기

BDEF에서 Abstract Entity를 파라미터로 참조하는 방법은 DDIC 구조체와 동일한 문법이다.

// BDEF: ZI_TravelApproval
managed implementation in class ZBP_TravelApproval unique;

define behavior for ZI_TravelApproval alias Travel
  persistent table /DMO/TRAVEL
  lock master
  authorization master (instance)
{
  field (readonly) TravelId;

  // Abstract Entity를 파라미터로 사용한 Action
  action approve parameter ZA_TravelApproveInput result [1] $self;
  action reject  parameter ZA_TravelRejectInput  result [1] $self;

  // 파라미터 없는 Action과의 비교
  action recalcTotalPrice;

  // ...
}

parameter 키워드 뒤에 Abstract Entity 이름을 그대로 쓰면 된다. ABAP Development Tools(ADT)는 이것을 Abstract Entity로 인식하고 적절한 타입 체크를 수행한다.

구현 클래스에서의 파라미터 접근

Action의 구현 메서드에서 파라미터는 importing 파라미터로 전달된다.

CLASS ZBP_TravelApproval DEFINITION PUBLIC ABSTRACT FINAL
  INHERITING FROM CL_ABAP_BEHAVIOR_HANDLER.

  PRIVATE SECTION.
    METHODS approve FOR MODIFY
      IMPORTING keys FOR ACTION Travel~approve RESULT result.

    METHODS reject  FOR MODIFY
      IMPORTING keys FOR ACTION Travel~reject  RESULT result.

ENDCLASS.

CLASS ZBP_TravelApproval IMPLEMENTATION.

  METHOD approve.
    " keys 테이블에 Action 파라미터가 포함되어 있다
    " 각 row는 %key (비즈니스 오브젝트 키)와 %param (Abstract Entity 필드) 구조를 가진다
    LOOP AT keys INTO DATA(key).
      DATA(travel_id) = key-%key-TravelId.      " BO 키
      DATA(reason)    = key-%param-Reason.       " Abstract Entity 필드
      DATA(comment)   = key-%param-Comment.

      " 승인 로직 처리
      UPDATE /dmo/travel
        SET status = 'A',
            lastchangedat = cl_abap_context_info=>get_system_date( )
        WHERE travel_id = @travel_id.

      IF sy-subrc = 0.
        APPEND VALUE #(
          %key = key-%key
          %msg = new_message_with_text(
            severity = if_abap_behv_message=>severity-success
            text = |Travel { travel_id } approved|
          )
        ) TO reported-travel.

        APPEND VALUE #( %key = key-%key ) TO result.
      ENDIF.
    ENDLOOP.
  ENDMETHOD.

  METHOD reject.
    LOOP AT keys INTO DATA(key).
      DATA(travel_id)   = key-%key-TravelId.
      DATA(rej_reason)  = key-%param-RejectReason.

      UPDATE /dmo/travel
        SET status = 'X'
        WHERE travel_id = @travel_id.

      APPEND VALUE #( %key = key-%key ) TO result.
    ENDLOOP.
  ENDMETHOD.

ENDCLASS.

핵심은 key-%param이다. Action 파라미터로 전달된 Abstract Entity의 필드들은 %param 컴포넌트 아래에 구조화되어 접근된다. 이는 RAP 프레임워크가 런타임에 파라미터를 비즈니스 오브젝트 키(%key)와 함께 일관된 구조로 제공하기 때문이다.


Abstract Entity 설계 패턴

실제 프로젝트에서 Abstract Entity를 어떻게 설계하는지 몇 가지 패턴을 살펴보자.

패턴 1: 비즈니스 키 + 의사결정 파라미터

승인/거절처럼 특정 인스턴스에 대한 의사결정 Action에 가장 흔한 패턴이다.

// 승인용 Abstract Entity
@EndUserText.label: 'Approval Parameters'
define abstract entity ZA_ApprovalInput {
  key TravelId    : /dmo/travel_id;        " BO 키 (참조용)
  AcceptOrReject  : abap_bool;             " true=승인, false=거절
  Comment         : abap_clnt;             " 의사결정 코멘트 (선택)
}

이때 key TravelId를 Abstract Entity에 포함하는 것은 선택 사항이다. BDEF에서 Action이 호출될 때 이미 %key에 비즈니스 오브젝트 키가 들어오기 때문에 %param에 다시 키를 넣을 필요가 없는 경우도 있다. 하지만 OData 표현이나 Fiori Elements에서의 호출 시 파라미터 의미를 명확히 하기 위해 포함하기도 한다.

패턴 2: 복잡한 입력 구조

단순 플래그가 아니라 여러 비즈니스 속성을 입력받는 경우다.

@EndUserText.label: 'Price Recalculation Input'
define abstract entity ZA_PriceCalcInput {
  key BookingId       : /dmo/booking_id;
  CurrencyCode        : /dmo/currency_code;
  DiscountRate        : abap_dec_8;
  ApplyLoyaltyPoints  : abap_bool;
  EffectiveDate       : abap_dats;
}

패턴 3: 값 도움말이 있는 파라미터

Abstract Entity의 강력한 장점 중 하나는 CDS 어노테이션을 통해 값 도움말(Value Help)을 직접 연결할 수 있다는 것이다. DDIC 구조체에서는 이것이 불가능하다.

@EndUserText.label: 'Status Change Input'
define abstract entity ZA_StatusChangeInput {
  key TravelId    : /dmo/travel_id;

  @Consumption.valueHelpDefinition: [{
    entity: { name: '/DMO/I_Overall_Status_VH', element: 'OverallStatus' }
  }]
  @EndUserText.label: 'New Status'
  NewStatus       : /dmo/overall_status;

  @EndUserText.label: 'Changed By'
  ChangedBy       : syuname;
}

이 어노테이션은 Fiori Elements UI에서 NewStatus 필드에 자동으로 값 도움말 팝업이 뜨도록 만든다. Action 파라미터 입력 폼이 완전한 메타데이터를 가진 인터페이스로 동작하는 것이다.


OData 서비스에서의 동작 방식

RAP 기반 OData V4 서비스에서 Abstract Entity로 정의된 Action 파라미터가 어떻게 표현되는지 이해하면, 왜 이 방식이 외부 연동에 더 유리한지 알 수 있다.

OData 메타데이터(EDMX)에서 Abstract Entity는 ComplexType 또는 별도의 EntityType으로 노출된다. Action은 이 타입을 파라미터로 참조한다.

<!-- OData EDMX 메타데이터 예시 (간략화) -->
<ComplexType Name="ZA_TravelApproveInput">
  <Property Name="TravelId" Type="Edm.String" MaxLength="8"/>
  <Property Name="Reason" Type="Edm.Boolean"/>
  <Property Name="Comment" Type="Edm.String" MaxLength="1333"/>
</ComplexType>

<Action Name="approve" IsBound="true">
  <Parameter Name="in" Type="Collection(TravelType)"/>
  <Parameter Name="ApprovParams" Type="ZA_TravelApproveInput"/>
  <ReturnType Type="Collection(TravelType)"/>
</Action>

Fiori Elements나 외부 시스템이 이 메타데이터를 읽으면 Action의 파라미터 구조를 정확히 파악할 수 있다. 레이블, 타입, 값 도움말 정보까지 모두 CDS 어노테이션에서 파생된다. DDIC 구조체 방식에서는 이 수준의 메타데이터 풍부함을 얻기 어렵다.


Abstract Entity와 DDIC 구조체 비교 정리

비교 항목 DDIC 구조체 Abstract Entity
정의 도구 SE11 (Data Dictionary) ADT CDS Editor
CDS 어노테이션 지원 불가 전면 지원
값 도움말 연결 간접적 (도메인 고정 값) @Consumption.valueHelpDefinition 직접 연결
RAP 아키텍처 일관성 CDS 레이어 외부 의존성 존재 완전히 CDS 레이어 내 관리
OData 메타데이터 품질 기본 타입 정보만 레이블·의미·값 도움말 포함
재사용 명확성 범용 구조체와 혼재 가능 RAP 파라미터 전용임이 명확
트랜스포트 관리 DDIC 오브젝트로 별도 관리 CDS 오브젝트로 통합 관리
Fiori Elements 통합 수동 어노테이션 별도 필요 CDS 어노테이션 자동 반영

실전 구현 시 주의사항

네이밍 컨벤션

Abstract Entity는 일반 CDS 인터페이스 뷰(ZI_)나 소비 뷰(ZC_)와 구분되어야 한다. 많은 프로젝트에서 ZA_ 프리픽스를 사용한다. 예를 들어 ZA_TravelApproveInput처럼 Action 이름과 "Input" 또는 "Param"을 조합한 명확한 이름을 쓰면 코드베이스에서 바로 파라미터 타입임을 알 수 있다.

키 필드 포함 여부

Abstract Entity에 key 필드를 선언할 수 있지만, Action 실행 시 %key에 이미 비즈니스 오브젝트의 키가 있으므로 %param에 같은 키를 다시 넣는 것이 중복이 될 수 있다. Fiori Elements에서 액션 파라미터 입력 폼을 제공할 때 컨텍스트가 명확하면 BO 키를 Abstract Entity에서 제외하고 순수한 입력 파라미터만 정의하는 것이 더 깔끔하다.

Import 파라미터의 구조 이해

// keys 테이블의 각 row 구조:
// - %key : 비즈니스 오브젝트의 키 (BDEF에서 선언된 키 필드들)
// - %param : Abstract Entity의 필드들
// - %cid : 클라이언트 측 요청 ID (draft 등에서 사용)

LOOP AT keys ASSIGNING FIELD-SYMBOL(<key>).
  " BO 키 접근
  DATA(lv_travel_id) = <key>-%key-TravelId.

  " Abstract Entity 파라미터 접근
  DATA(lv_reason)  = <key>-%param-Reason.
  DATA(lv_comment) = <key>-%param-Comment.

  " 파라미터 존재 여부 확인 (선택적 파라미터의 경우)
  IF <key>-%param-%is_present-Comment = if_abap_behv=>mk-on.
    " Comment가 실제로 전달된 경우만 처리
  ENDIF.
ENDLOOP.

%is_present는 클라이언트가 해당 파라미터를 실제로 전달했는지 확인하는 데 사용된다. Abstract Entity의 일부 필드를 선택적으로 처리할 때 유용하다.

Draft Action과 Non-Draft Action

Draft가 활성화된 BO에서 Draft 인스턴스에 대해 Action을 실행하는 경우도 동일한 방식으로 Abstract Entity를 파라미터로 사용할 수 있다. BDEF에서 draft action으로 선언하면 된다.

define behavior for ZI_TravelApproval alias Travel
  ...
  draft action Resume;
  action approve parameter ZA_TravelApproveInput result [1] $self;
  draft determine action Prepare;
{
  // Draft에서도 동일하게 Abstract Entity 참조
}

언제 Abstract Entity를 쓰지 않아도 되는가

모든 경우에 Abstract Entity가 최선은 아니다. 다음 상황에서는 기존 DDIC 구조체도 합리적인 선택이다.

  • 기존 DDIC 구조체가 이미 충분히 정의되어 있는 경우: 레거시 시스템과의 통합이나 기존 구조체의 재사용이 명확한 이점을 주는 경우
  • OData 노출이 없는 순수 내부 Action인 경우: Fiori Elements 통합이 필요 없고 단순히 백엔드 로직만 실행하는 경우
  • 팀 역량이 DDIC 중심인 경우: CDS 전환 과도기에 있는 팀에서 일관성을 위해 DDIC를 유지하는 경우

하지만 새로 설계하는 RAP 프로젝트이고, Fiori Elements UI와 OData 서비스가 계획되어 있다면 Abstract Entity가 기술 부채를 줄이는 더 나은 선택이다.


결론: CDS-First 설계 원칙의 구현

Abstract Entity는 단순한 기술적 대안이 아니다. 이것은 RAP가 지향하는 CDS-First 아키텍처의 구현이다. 비즈니스 오브젝트의 모든 요소—구조, 행동, 파라미터 타입—를 CDS 레이어에서 일관되게 관리하겠다는 설계 철학이 담겨 있다.

Action 파라미터를 Abstract Entity로 정의하면 세 가지를 얻는다. 첫째, RAP 아키텍처의 일관성—DDIC와 CDS가 뒤섞이지 않는다. 둘째, OData 메타데이터의 풍부함—CDS 어노테이션이 Fiori Elements까지 자동으로 연결된다. 셋째, 명확한 의도—이 타입이 Action 파라미터 전용임을 코드 구조 자체가 선언한다.

RAP에서 Action을 설계할 때 파라미터 타입을 어디에 정의할지 고민된다면, SE11보다 ADT CDS Editor를 먼저 열자. Abstract Entity로 시작하는 것이 장기적으로 더 유지보수하기 쉬운 코드베이스를 만든다.

댓글 0

아직 댓글이 없습니다.