개요 및 핵심 포인트
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
아직 댓글이 없습니다.