ABAP

ABAP 메서드 체이닝으로 코드 50% 줄이기 #shorts #SAP #ABAP

▶ YouTube에서 보기

개요 및 핵심 포인트

ABAP에서 객체를 다룰 때 임시 변수가 화면을 가득 채우는 경험을 해본 적이 있을 것입니다. 메서드 체이닝(Method Chaining)은 동일 객체의 메서드를 점으로 이어 호출하는 패턴으로, 빌더(Builder)와 같은 Fluent Interface를 ABAP OO에서 자연스럽게 구현하는 방법입니다. 이 글에서는 RETURNING me 선언 패턴부터 예외 통합, 인라인 선언과의 조합까지 실무 관점에서 다룹니다.

핵심 개념: RETURNING me와 Fluent Interface의 동작 원리

메서드 체이닝의 본질은 단순합니다. 메서드가 자기 자신(me)을 RETURNING으로 돌려주면, 호출자는 그 반환값에 다시 점을 붙여 다음 메서드를 호출할 수 있습니다. ABAP에서는 RETURNING 파라미터가 하나일 때만 식(expression) 위치에서 메서드를 사용할 수 있고, 이때 결과를 다시 메서드 호출의 좌변처럼 다룰 수 있습니다.

비유하자면 컨베이어 벨트 위의 작업자를 떠올리면 됩니다. 각 작업자는 들어온 박스에 스티커를 붙이고, 같은 박스를 다음 작업자에게 그대로 넘깁니다. 마지막 작업자가 박스를 출고(build())하면 완성품이 나옵니다. RETURNING me는 "박스를 그대로 다음 라인으로 넘겨라"라는 약속입니다.

METHODS set_customer
  IMPORTING iv_id          TYPE kunnr
  RETURNING VALUE(ro_self) TYPE REF TO zcl_order_builder.

여기서 ro_self는 항상 me를 담아 반환합니다. 호출 측은 다음과 같이 매끄럽게 쓸 수 있습니다.

DATA(lo_order) = lo_builder->set_customer( '0000123' )
                            ->add_item( 'MAT-01' )
                            ->build( ).

1단계 — 기본 예제: 주문 빌더

판매 주문 생성을 위해 고객, 품목, 수량, 통화 등을 단계적으로 설정하는 빌더 클래스입니다.

CLASS zcl_order_builder DEFINITION
  PUBLIC FINAL CREATE PUBLIC.

  PUBLIC SECTION.
    METHODS:
      set_customer
        IMPORTING iv_kunnr       TYPE kunnr
        RETURNING VALUE(ro_self) TYPE REF TO zcl_order_builder,

      add_item
        IMPORTING iv_matnr       TYPE matnr
                  iv_qty         TYPE i DEFAULT 1
        RETURNING VALUE(ro_self) TYPE REF TO zcl_order_builder,

      set_currency
        IMPORTING iv_waers       TYPE waers
        RETURNING VALUE(ro_self) TYPE REF TO zcl_order_builder,

      build
        RETURNING VALUE(rs_order) TYPE zorder_s.

  PRIVATE SECTION.
    DATA ms_order TYPE zorder_s.
ENDCLASS.

CLASS zcl_order_builder IMPLEMENTATION.
  METHOD set_customer.
    ms_order-kunnr = iv_kunnr.
    ro_self = me.
  ENDMETHOD.

  METHOD add_item.
    APPEND VALUE #( matnr = iv_matnr qty = iv_qty )
      TO ms_order-items.
    ro_self = me.
  ENDMETHOD.

  METHOD set_currency.
    ms_order-waers = iv_waers.
    ro_self = me.
  ENDMETHOD.

  METHOD build.
    rs_order = ms_order.
  ENDMETHOD.
ENDCLASS.

호출부는 다음과 같이 한 흐름으로 읽힙니다.

DATA(ls_order) = NEW zcl_order_builder( )
  ->set_customer( '0000123' )
  ->add_item( iv_matnr = 'MAT-100' iv_qty = 5 )
  ->add_item( 'MAT-200' )
  ->set_currency( 'EUR' )
  ->build( ).

NEW 연산자가 인스턴스를 만들자마자 곧바로 체인을 시작하므로, 임시 참조 변수를 선언할 필요가 없습니다.

2단계 — 예외 처리와 로깅 통합

실제 업무 로직에서는 잘못된 고객 번호, 비어 있는 품목 등 검증이 필요합니다. 권장 패턴은 빌더 메서드는 RAISING cx_static_check로 명시하고, 호출부에서 한 번의 TRY ... CATCH로 감싸는 것입니다.

METHODS set_customer
  IMPORTING iv_kunnr       TYPE kunnr
  RETURNING VALUE(ro_self) TYPE REF TO zcl_order_builder
  RAISING   zcx_order_build.

METHOD set_customer.
  IF iv_kunnr IS INITIAL.
    RAISE EXCEPTION TYPE zcx_order_build
      EXPORTING textid = zcx_order_build=>customer_missing.
  ENDIF.
  ms_order-kunnr = iv_kunnr.
  ro_self = me.
ENDMETHOD.

호출부는 체인을 그대로 유지하면서 예외만 묶어 처리합니다.

TRY.
    DATA(ls_order) = NEW zcl_order_builder( )
      ->set_customer( iv_kunnr )
      ->add_item( iv_matnr )
      ->set_currency( 'KRW' )
      ->build( ).
  CATCH zcx_order_build INTO DATA(lo_ex).
    MESSAGE lo_ex->get_text( ) TYPE 'E'.
ENDTRY.

3단계 — ABAP Unit 테스트와 프로덕션 적용

빌더 자체가 테스트 데이터를 만드는 도구가 되기 때문에, 테스트 코드의 길이를 극적으로 줄여줍니다.

CLASS ltcl_order_builder DEFINITION FINAL FOR TESTING
  DURATION SHORT RISK LEVEL HARMLESS.
  PRIVATE SECTION.
    METHODS build_minimal FOR TESTING RAISING cx_static_check.
ENDCLASS.

CLASS ltcl_order_builder IMPLEMENTATION.
  METHOD build_minimal.
    DATA(ls_order) = NEW zcl_order_builder( )
      ->set_customer( '0000123' )
      ->add_item( 'MAT-01' )
      ->set_currency( 'EUR' )
      ->build( ).
    cl_abap_unit_assert=>assert_equals(
      act = lines( ls_order-items )
      exp = 1 ).
  ENDMETHOD.
ENDCLASS.

성능 측면에서 ro_self = me는 단순 참조 복사이므로 체이닝 자체의 비용은 거의 없습니다. 다만 대량 루프에서 매번 NEW로 빌더를 만드는 것은 GC 부담이 됩니다. 빌더를 한 번 만들고 reset( ) 메서드를 두어 재사용하는 것이 권장됩니다.

자주 만나는 실수와 FAQ

RETURNING을 EXPORTING으로 정의했습니다.
EXPORTING ro_self로 만들면 식 위치에서 메서드를 호출할 수 없어 체이닝이 불가능합니다. 반드시 RETURNING VALUE(ro_self)여야 합니다.

ro_self = me 누락
구현부에서 ro_self에 값을 세팅하지 않으면 다음 체인 호출에서 CX_SY_REF_IS_INITIAL이 발생합니다. 모든 체이닝 메서드 마지막 줄에 ro_self = me.를 습관적으로 추가하세요.

체인 중간 단계 디버깅이 어렵습니다.
디버거에서 한 줄에 묶인 체인은 단일 스텝으로 보입니다. 일시적으로 DATA(lo_step1) = lo_builder->set_customer( ... ).처럼 단계별로 분리한 뒤 검사하고, 끝나면 원복하는 방식이 실용적입니다.

응용 패턴 — 인터페이스와 NEW 연산자 조합

인터페이스 메서드에서도 체이닝이 가능합니다. RETURNING 타입을 REF TO if_xxx로 잡으면 인터페이스 메서드만 체인할 수 있습니다. 구현체 메서드까지 묶고 싶다면 인터페이스 단위에서 메서드를 충분히 노출하는 설계가 권장됩니다.

NEW 연산자와 조합하면 임시 변수 없이 즉시 빌더를 사용할 수 있어, 리포트나 BAdI 구현부에서 코드 밀도를 크게 높일 수 있습니다. EML(Entity Manipulation Language)을 사용하는 RAP 환경에서도 유사한 빌더 패턴이 널리 쓰입니다.

댓글 0

아직 댓글이 없습니다.