이 글에서 다루는 내용
ABAP에서 REF TO 참조 변수는 데이터 객체나 인스턴스를 직접 가리키는 포인터 역할을 합니다. 값 복사가 아닌 참조 전달을 통해 메모리 효율성과 다형성을 동시에 확보할 수 있어, 대용량 내부 테이블 처리·동적 디스패치·Factory 패턴 등 실무 곳곳에서 핵심적으로 사용됩니다. 이 글은 참조 변수의 동작 원리부터 역참조, 타입 변환, 안전한 해제 방법까지 단계적으로 정리합니다.
- 참조 변수가 필요한 상황과 메모리 모델 이해
REF #,GET REFERENCE OF,NEW연산자 활용- 역참조(
->*,->field)와CAST연산자 적용 - Factory 패턴·런타임 다형성을 통한 구조 설계
IS BOUND체크와 DANGLING 참조 회피 전략
이 글을 읽기 전 알아두면 좋은 것들
ABAP 기본 변수 선언(DATA), 내부 테이블과 워크 에어리어 개념, 클래스·메서드 구조에 대한 기초적인 이해가 필요합니다. ABAP Objects의 인스턴스 생성 문법(CREATE OBJECT)을 한 번이라도 사용해 봤다면 참조 변수의 개념이 훨씬 직관적으로 다가옵니다. 추가로 인터페이스와 상속 관계를 다뤄봤다면 다형성 시나리오 이해가 빠릅니다.
환경 및 버전 정보
이 글의 예제는 ABAP 7.40 SP08 이상에서 동작합니다. NEW, REF #, CAST 같은 인라인 연산자는 7.40부터 도입된 문법이며, 일부 표현식(예: 인라인 선언 DATA(...))은 7.40 SP02 이상에서 사용 가능합니다. SAP NetWeaver 7.50, S/4HANA 1909/2022, BTP ABAP Environment(Steampunk) 모두에서 동일한 의미로 동작합니다.
- 개발 도구: ABAP Development Tools(ADT) for Eclipse 권장
- 대안: SAP GUI의 SE80/SE38 (구문 표시는 다소 다를 수 있음)
- 테스트: 단위 테스트 클래스(
CL_AUNIT_ASSERT) 활용 - 참고 SAP Note: 1760934 (NEW 연산자 동작 관련)
S/4HANA 2022 기준 RAP(RESTful ABAP Programming Model) 환경에서도 비즈니스 객체 처리 시 참조 변수가 광범위하게 사용되므로, 차세대 ABAP을 다룬다면 필수적으로 익혀야 합니다.
참조 변수의 동작 원리
참조 변수는 메모리 주소를 담는 "표지판" 같은 존재입니다. 일반 변수가 값 자체를 저장하는 상자라면, 참조 변수는 "그 상자가 어디에 있는지" 알려주는 화살표입니다. ABAP에서는 크게 두 가지 형태가 있습니다.
- 데이터 참조(Data Reference): 임의의 데이터 객체(구조체·테이블·기본 타입)를 가리킴 —
REF TO data,REF TO ty_sales_order - 객체 참조(Object Reference): 클래스 인스턴스나 인터페이스 구현체를 가리킴 —
REF TO cl_invoice_handler
참조 변수가 필요한 대표적인 상황은 다음과 같습니다.
- 성능: 수십만 건의 영업 주문 라인을 메서드 간 전달할 때 값 복사를 피해야 함. 참조 전달은 포인터 크기만큼만 이동합니다.
- 유연성: 컴파일 시점에 타입이 확정되지 않는 동적 SQL 결과 처리
- 다형성: 부모 인터페이스 타입의 참조에 자식 구현체를 담아 런타임에 다른 동작을 호출
- 선택적 존재: NULL 가능한 값(있을 수도, 없을 수도 있는 객체)을 표현
역참조(dereferencing)는 화살표가 가리키는 실제 값에 접근하는 행위입니다. ABAP에서는 두 가지 표기를 씁니다.
lr_order->*— 참조가 가리키는 데이터 객체 전체에 접근lr_handler->process( )— 객체 참조의 메서드 호출 (자동 역참조)lr_order->order_id— 데이터 참조의 구조체 필드 접근(7.40+ 단축 문법)
여기서 핵심은 "참조 변수 자체가 비어있을 수 있다"는 점입니다. 가리키는 대상이 없는 상태를 initial reference 또는 null reference라 부르며, 이를 무시하고 역참조하면 즉시 CX_SY_REF_IS_INITIAL 예외가 발생합니다. 따라서 IS BOUND 체크가 모든 역참조의 전제 조건이 됩니다.
1단계 — 기본 참조 변수 선언과 역참조
가장 단순한 예제부터 시작합니다. 영업 주문 구조체를 가리키는 데이터 참조 변수를 만들고, 값에 접근해 보겠습니다.
REPORT z_ref_basic.
TYPES: BEGIN OF ty_sales_order,
order_id TYPE c LENGTH 10,
customer TYPE c LENGTH 40,
net_amount TYPE p LENGTH 9 DECIMALS 2,
currency TYPE c LENGTH 3,
END OF ty_sales_order.
DATA: ls_order TYPE ty_sales_order,
lr_order TYPE REF TO ty_sales_order.
START-OF-SELECTION.
ls_order = VALUE #(
order_id = '4500001234'
customer = 'Acme Industrial'
net_amount = '15800.50'
currency = 'EUR'
).
GET REFERENCE OF ls_order INTO lr_order.
lr_order = REF #( ls_order ).
WRITE: / 'Order:', lr_order->*-order_id.
WRITE: / 'Customer:', lr_order->customer,
/ 'Amount :', lr_order->net_amount, lr_order->currency.
2단계 — 동적 처리와 안전한 예외 핸들링
실무에서는 참조 변수가 비어있을 가능성, 캐스팅 실패 가능성을 항상 고려해야 합니다. 다음은 영수증 데이터를 동적으로 받아 처리하는 시나리오로, 로깅과 예외 처리가 포함되어 있습니다.
CLASS lcl_invoice_processor DEFINITION.
PUBLIC SECTION.
TYPES: BEGIN OF ty_invoice_item,
invoice_no TYPE c LENGTH 10,
posting_dt TYPE d,
gross_amt TYPE p LENGTH 11 DECIMALS 2,
END OF ty_invoice_item.
METHODS:
process_reference
IMPORTING ir_data TYPE REF TO data
RETURNING VALUE(rv_ok) TYPE abap_bool
RAISING cx_sy_move_cast_error.
ENDCLASS.
CLASS lcl_invoice_processor IMPLEMENTATION.
METHOD process_reference.
DATA: lr_invoice TYPE REF TO ty_invoice_item.
IF ir_data IS NOT BOUND.
MESSAGE 'Reference is initial' TYPE 'I'.
rv_ok = abap_false.
RETURN.
ENDIF.
TRY.
lr_invoice ?= ir_data.
CATCH cx_sy_move_cast_error INTO DATA(lx_cast).
cl_demo_output=>display( |Cast failed: { lx_cast->get_text( ) }| ).
RAISE EXCEPTION lx_cast.
ENDTRY.
cl_demo_output=>display(
|Processing invoice { lr_invoice->invoice_no } |
&& |amount { lr_invoice->gross_amt }| ).
rv_ok = abap_true.
ENDMETHOD.
ENDCLASS.
3단계 — Factory 패턴과 다형성 적용
참조 변수의 진가는 다형성에서 드러납니다. 결제 수단(카드·계좌이체·암호화폐)에 따라 다른 검증 로직을 실행해야 하는 구매 주문 시스템을 가정해 보겠습니다.
INTERFACE lif_payment_validator.
METHODS validate
IMPORTING iv_amount TYPE p
RETURNING VALUE(rv_valid) TYPE abap_bool.
ENDINTERFACE.
CLASS lcl_card_validator DEFINITION.
PUBLIC SECTION.
INTERFACES lif_payment_validator.
ENDCLASS.
CLASS lcl_card_validator IMPLEMENTATION.
METHOD lif_payment_validator~validate.
rv_valid = COND #( WHEN iv_amount <= 50000 THEN abap_true ELSE abap_false ).
ENDMETHOD.
ENDCLASS.
CLASS lcl_transfer_validator DEFINITION.
PUBLIC SECTION.
INTERFACES lif_payment_validator.
ENDCLASS.
CLASS lcl_transfer_validator IMPLEMENTATION.
METHOD lif_payment_validator~validate.
rv_valid = abap_true.
ENDMETHOD.
ENDCLASS.
CLASS lcl_validator_factory DEFINITION.
PUBLIC SECTION.
CLASS-METHODS create
IMPORTING iv_method TYPE c
RETURNING VALUE(ro_val) TYPE REF TO lif_payment_validator
RAISING cx_sy_create_object_error.
ENDCLASS.
CLASS lcl_validator_factory IMPLEMENTATION.
METHOD create.
CASE iv_method.
WHEN 'CARD'.
ro_val = NEW lcl_card_validator( ).
WHEN 'BANK'.
ro_val = NEW lcl_transfer_validator( ).
WHEN OTHERS.
RAISE EXCEPTION TYPE cx_sy_create_object_error.
ENDCASE.
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
DATA(lo_validator) = lcl_validator_factory=>create( 'CARD' ).
IF lo_validator IS BOUND.
DATA(lv_ok) = lo_validator->validate( 75000 ).
WRITE: / 'Valid:', lv_ok.
ENDIF.
CLEAR lo_validator.
자주 마주치는 함정과 해결
Q1. CX_SY_REF_IS_INITIAL이 발생합니다.
역참조 직전에 IS BOUND 검사가 누락된 경우입니다. IF lr_data IS BOUND. 블록 안에서만 lr_data->*를 사용하세요. 함수 모듈 반환값처럼 외부에서 받은 참조는 항상 검증이 필요합니다.
Q2. DANGLING 참조 — 가리키던 데이터가 사라졌어요.
로컬 변수의 주소를 참조에 담은 뒤 해당 변수의 스코프가 끝나면 위험합니다. 다만 ABAP은 자동 가비지 컬렉션을 수행해 참조가 살아있는 한 데이터 객체도 유지됩니다. 문제가 되는 패턴은 FIELD-SYMBOLS를 거쳐 임시 행에 참조를 만든 뒤 내부 테이블이 재구성(SORT, DELETE)되는 경우입니다. 이때는 참조를 재획득하세요.
Q3. CREATE OBJECT와 NEW 중 무엇을 써야 하나요?
기능적으로 동일하지만, 7.40 이상 환경에서는 NEW가 권장됩니다. 인라인 선언과 함께 사용 가능하고(DATA(lo_x) = NEW lcl_x( )), 표현식 안에서 즉시 사용할 수 있어 코드가 간결해집니다. 동적 클래스명으로 생성해야 하는 특수한 경우에만 CREATE OBJECT ... TYPE (lv_classname) 형태를 유지합니다.
Q4. ?=와 CAST #( )의 차이는?
의미는 같으나 CAST는 표현식으로 사용 가능해 체이닝이 자연스럽습니다. 예: CAST cl_target( io_source )->do_something( ). 둘 다 실패 시 동일한 CX_SY_MOVE_CAST_ERROR를 던집니다.
참조 변수 활용 확장 주제
참조 변수를 익혔다면 다음 주제로 확장해 보세요. RTTI/RTTC(CL_ABAP_TYPEDESCR)를 활용한 런타임 타입 분석은 동적 SQL 결과 처리에 필수입니다. FIELD-SYMBOLS는 참조와 비슷하지만 자동 역참조되는 가벼운 대안으로, 대량 루프 성능 최적화에 유리합니다. RAP(RESTful ABAP Programming)에서는 비즈니스 객체를 다룰 때 참조 기반 인스턴스 핸들링이 일상적이며, 디자인 패턴(Strategy, Observer, Singleton)도 참조 변수 위에 구축됩니다.
댓글 0
아직 댓글이 없습니다.