ABAP

FIELD-SYMBOLS vs REF TO 언제 써야 하나 #shorts #SAP #ABAP

이 글의 방향과 확인 포인트

ABAP 개발자라면 한 번쯤은 "여기서 FIELD-SYMBOLS를 써야 하나, 아니면 REF TO로 참조 변수를 만들어야 하나?" 하는 갈림길에 서게 됩니다. 둘 다 "값을 복사하지 않고 원본을 가리킨다"는 공통점이 있지만, 내부 동작 방식과 적합한 시나리오는 꽤 다릅니다. 이 글은 성능과 유지보수라는 두 가지 축에서 선택 기준을 정리하고, 실무 예제로 오용을 예방하는 것을 목표로 합니다.

  • FIELD-SYMBOLS와 REF TO의 메모리 모델 차이 이해
  • LOOP AT ... ASSIGNING vs REFERENCE INTO의 성능 특성 비교
  • 동적 컴포넌트 접근(ASSIGN COMPONENT) 패턴 활용
  • 오브젝트 참조와 데이터 참조의 구분 감각 확보
  • 7.4+ 현대 ABAP에서 권장되는 인라인 선언 패턴 적용

사전에 알아두면 좋은 것들

이 글은 중급 개발자를 대상으로 합니다. 내부 테이블(Internal Table)의 종류(STANDARD/SORTED/HASHED)와 LOOP AT의 기본 사용법, 클래스 인스턴스와 참조의 개념, 그리고 TYPES/DATA 선언 문법에 대한 기본 이해가 있으면 매끄럽게 따라올 수 있습니다. RTTS(Run Time Type Services)의 CL_ABAP_TYPEDESCR 계열 클래스에 대해 들어본 적이 있다면 후반부 동적 처리 예제가 더 잘 이해될 것입니다.

환경과 사전 준비

이 글의 코드는 ABAP 7.54 이상(SAP S/4HANA 2021, ABAP Platform 2022)을 기준으로 작성했습니다. 인라인 선언(DATA(...), FIELD-SYMBOL(<...>))과 REDUCE, VALUE, FOR 같은 표현식 문법을 자유롭게 씁니다. 7.40 SP08 이후 릴리스라면 대부분 그대로 동작하며, 필요할 경우 SE38/ADT(ABAP Development Tools) 어느 쪽에서든 실행 가능합니다.

  • 시스템: SAP NetWeaver AS ABAP 7.54+ 또는 ABAP Platform 2022/2023, 또는 BTP ABAP Environment(Steampunk)
  • 도구: Eclipse ADT 3.30+ 또는 SE80/SE38
  • 테스트 데이터: SFLIGHT, SPFLI 등 표준 예제 테이블(로컬 학습용) 또는 사용자 정의 테이블
  • 권한: 개발 키(Developer Key)와 로컬 클래스/프로그램 생성 권한

내부 동작 원리와 개념 비교

FIELD-SYMBOLS와 REF TO는 모두 "간접 참조"라는 큰 우산 아래에 있지만, 언어 수준에서 다루는 방식이 다릅니다. 비유하자면 FIELD-SYMBOLS는 "포스트잇처럼 원본 위에 그대로 붙는 별명(alias)"이고, REF TO는 "원본의 주소가 적힌 명함"입니다. 별명은 원본과 완전히 같은 취급을 받아 그대로 읽고 쓸 수 있지만, 명함은 필요할 때 ->*로 역참조를 해야 안쪽 값에 접근할 수 있습니다.

메모리 관점에서 보면 FIELD-SYMBOLS는 심볼 테이블에 등록된 이름표에 가깝습니다. ASSIGN 문이 수행되는 순간 컴파일러/런타임이 대상 메모리 위치를 심볼과 연결합니다. 반면 REF TO는 실제로 4바이트 혹은 8바이트짜리 데이터 참조 값(포인터 유사 구조)이 스택 또는 필드 안에 저장됩니다. 이 차이가 성능에 미치는 영향은 대부분의 실무 시나리오에서 미미하지만, 수백만 건 규모 루프에서는 유의미하게 벌어지기도 합니다.

또 하나 중요한 구분은 "무엇을 가리킬 수 있는가"입니다. FIELD-SYMBOLS는 데이터 오브젝트(구조체, 테이블, 필드)만 가리킵니다. 인스턴스 오브젝트를 가리킬 수는 없습니다. 반대로 REF TO는 REF TO data로 데이터를 가리킬 수도, REF TO zcl_order처럼 인스턴스를 가리킬 수도 있습니다. 즉 객체지향 설계 안에서 인스턴스를 넘겨야 한다면 REF TO가 유일한 선택입니다.

정리하면, "이 값이 데이터인가 인스턴스인가", "지역 알리아스가 필요한가 참조를 저장/전달해야 하는가"가 첫 갈림길입니다. 데이터를 지역적으로 다루면 FIELD-SYMBOLS가, 참조 자체를 컬렉션에 담거나 메서드 파라미터로 넘겨야 하면 REF TO가 자연스럽습니다.

실전 예제 1단계: 기본 예제 - 인라인 수정과 참조 전달

가장 흔한 시나리오인 내부 테이블 순회부터 시작합니다. 주문 테이블을 돌면서 할인율을 재계산하는 로직을 두 방식으로 비교해 봅니다.

TYPES: BEGIN OF ty_order,
         order_id   TYPE c LENGTH 10,
         customer   TYPE c LENGTH 20,
         net_amount TYPE p LENGTH 13 DECIMALS 2,
         discount   TYPE p LENGTH 5 DECIMALS 2,
       END OF ty_order,
       tt_orders TYPE STANDARD TABLE OF ty_order WITH DEFAULT KEY.

DATA(lt_orders) = VALUE tt_orders(
  ( order_id = '1000000001' customer = 'ACME'    net_amount = '12500.00' discount = 0 )
  ( order_id = '1000000002' customer = 'CONTOSO' net_amount = '89000.00' discount = 0 )
  ( order_id = '1000000003' customer = 'INITECH' net_amount = '3100.00'  discount = 0 ) ).

" 방식 A: FIELD-SYMBOLS 사용 (지역 알리아스)
LOOP AT lt_orders ASSIGNING FIELD-SYMBOL(<fs_order>).
  IF <fs_order>-net_amount > 10000.
    <fs_order>-discount = '5.00'.
  ENDIF.
ENDLOOP.

" 방식 B: 데이터 참조 사용
LOOP AT lt_orders REFERENCE INTO DATA(lr_order).
  IF lr_order->net_amount > 10000.
    lr_order->discount = '5.00'.
  ENDIF.
ENDLOOP.

결과적으로 두 방식 모두 정상 동작합니다. 문법적으로 FIELD-SYMBOLS는 <fs_order>-필드처럼 구조체를 다루듯 자연스럽고, REF TO는 lr_order->필드처럼 화살표 접근이 필요합니다. 대부분의 벤치마크에서 FIELD-SYMBOLS가 약간 더 빠른 편이지만, 그 차이는 수십만 건 이상에서 체감됩니다. 유지보수 관점에서는 로컬 알리아스로만 쓸 거면 FIELD-SYMBOLS가 일반적으로 더 읽기 좋습니다.

실전 예제 2단계: 실무 시나리오 - 동적 컴포넌트와 예외 처리

실무에서는 필드명이 런타임에 결정되는 경우가 자주 있습니다. 예를 들어 사용자가 UI에서 선택한 컬럼에 대해 합계를 내거나, 마스터 데이터의 특정 속성을 유효성 검증하는 상황입니다. 이때 FIELD-SYMBOLS의 진가가 나옵니다.

CLASS zcl_order_analyzer DEFINITION.
  PUBLIC SECTION.
    METHODS sum_column
      IMPORTING it_orders     TYPE tt_orders
                iv_field_name TYPE csequence
      RETURNING VALUE(rv_sum) TYPE p LENGTH 15 DECIMALS 2
      RAISING   cx_sy_assign_cast_illegal_cast.
ENDCLASS.

CLASS zcl_order_analyzer IMPLEMENTATION.
  METHOD sum_column.
    FIELD-SYMBOLS: <fs_row>    TYPE ty_order,
                   <fs_number> TYPE numeric.

    LOOP AT it_orders ASSIGNING <fs_row>.
      ASSIGN COMPONENT iv_field_name OF STRUCTURE <fs_row> TO <fs_number>.
      IF sy-subrc <> 0.
        " 존재하지 않는 컬럼: 로그 남기고 중단
        MESSAGE |Field { iv_field_name } not found in row { sy-tabix }|
                TYPE 'E' RAISING cx_sy_assign_cast_illegal_cast.
      ENDIF.
      IF <fs_number> IS ASSIGNED.
        rv_sum = rv_sum + <fs_number>.
      ENDIF.
    ENDLOOP.
  ENDMETHOD.
ENDCLASS.

포인트는 ASSIGN COMPONENT ... OF STRUCTURE로 문자열 필드명을 실제 필드 심볼에 바인딩한다는 점입니다. 이 작업은 REF TO만으로는 그다지 우아하게 표현되지 않습니다. RTTS로 컴포넌트 타입을 확인한 뒤 ASSIGN을 결합하는 이 패턴은 리포트 생성기, 규칙 엔진, 검증 프레임워크에서 널리 씁니다. 또한 IS ASSIGNED 체크로 미할당 상태에서 접근해 발생하는 GETWA_NOT_ASSIGNED 런타임 에러를 예방하는 습관이 중요합니다.

실전 예제 3단계: 프로덕션 관점 - 데이터 참조와 오브젝트 참조 결합

대규모 서비스에서는 참조 자체를 컬렉션에 담아 전달하거나, 팩토리 패턴으로 인스턴스를 생성해 반환하는 경우가 많습니다. 이 시점부터는 REF TO의 역할이 명확해집니다. 아래는 주문을 처리 상태별로 인덱싱하고, 규칙 처리기를 다형적으로 실행하는 구조입니다.

INTERFACE zif_order_rule.
  METHODS apply IMPORTING ir_order TYPE REF TO ty_order.
ENDINTERFACE.

CLASS zcl_vip_discount_rule DEFINITION.
  PUBLIC SECTION.
    INTERFACES zif_order_rule.
ENDCLASS.

CLASS zcl_vip_discount_rule IMPLEMENTATION.
  METHOD zif_order_rule~apply.
    IF ir_order->customer CP 'VIP*'.
      ir_order->discount = '10.00'.
    ENDIF.
  ENDMETHOD.
ENDCLASS.

DATA: lt_rules TYPE STANDARD TABLE OF REF TO zif_order_rule,
      lt_index TYPE HASHED TABLE OF REF TO ty_order
               WITH UNIQUE KEY table_line.

APPEND NEW zcl_vip_discount_rule( ) TO lt_rules.

LOOP AT lt_orders REFERENCE INTO DATA(lr_row).
  INSERT lr_row INTO TABLE lt_index.
  LOOP AT lt_rules INTO DATA(lo_rule).
    lo_rule->apply( ir_order = lr_row ).
  ENDLOOP.
ENDLOOP.

TRY.
    DATA(lo_analyzer) = NEW zcl_order_analyzer( ).
    DATA(lv_total)    = lo_analyzer->sum_column(
                          it_orders     = lt_orders
                          iv_field_name = 'NET_AMOUNT' ).
  CATCH cx_sy_assign_cast_illegal_cast INTO DATA(lx_err).
    " 실패 시 대체 값 반환 또는 재발생
    lv_total = 0.
ENDTRY.

여기서 데이터 참조 lr_row는 규칙 처리기의 파라미터로 안전하게 전달됩니다. 만약 FIELD-SYMBOLS로 같은 일을 하려 하면, 심볼을 파라미터로 넘길 수는 있어도 컬렉션(테이블)에 저장할 수는 없습니다. 즉 "참조를 값처럼 다뤄야 하는 순간" REF TO가 필요합니다. 또한 예외 처리(TRY-CATCH), 다형성, 팩토리 반환값 같은 객체지향 흐름에서도 REF TO가 자연스럽게 어울립니다.

성능 관점에서 대용량 처리 시 권장하는 조합은 다음과 같습니다. 첫째, 순회하며 값을 갱신하는 순수 LOOP에는 ASSIGNING FIELD-SYMBOL을 우선 고려합니다. 둘째, 참조 자체를 재사용(다른 함수에 넘기거나 인덱스 테이블에 저장)해야 하면 REFERENCE INTO로 얻은 데이터 참조를 사용합니다. 셋째, 두 방식 모두 INTO ls_row처럼 값 복사보다는 훨씬 유리하므로, 명시적 이유가 없다면 값 복사 루프는 피하는 편이 좋습니다.

자주 나오는 함정과 해결법

Q1. "GETWA_NOT_ASSIGNED" 덤프가 발생합니다.
FIELD-SYMBOLS를 선언만 하고 ASSIGN 없이 접근할 때 나옵니다. 사용 직전에 IF <fs> IS ASSIGNED.로 방어하거나, LOOP 종료 후 다시 참조하지 않도록 스코프를 좁히는 것이 좋습니다. 인라인 FIELD-SYMBOL(<fs>) 선언은 LOOP 안에서 정의되므로 이런 문제를 줄여줍니다.

Q2. REF TO 변수가 NULL이라 메서드 호출이 실패합니다.
lo_ref IS BOUND으로 바인딩 여부를 확인해야 합니다. IS INITIALIS BOUND는 의미가 미묘하게 다릅니다. 초기값(비어 있음) 여부와 실제 대상이 있는지 여부는 다르므로, 인스턴스 참조에는 IS BOUND를, 데이터 참조에도 마찬가지로 IS BOUND를 권장합니다.

Q3. LOOP 후에도 FIELD-SYMBOLS로 마지막 행을 접근하고 싶습니다.
STANDARD 테이블 LOOP가 끝난 뒤에도 심볼이 여전히 마지막 라인을 가리키는 것처럼 보일 수 있지만, 이는 구현 세부에 의존합니다. 이런 접근은 유지보수를 어렵게 만들므로, 필요하면 별도 인덱스와 READ TABLE ... ASSIGNING으로 명시적으로 다시 참조하는 편이 안전합니다.

Q4. SORTED/HASHED 테이블 인라인 수정에서 키를 바꾸면 어떻게 되나요?
FIELD-SYMBOLS든 REF TO든 KEY 컬럼을 직접 수정하면 런타임 에러가 납니다. 삭제 후 재삽입 또는 별도 워크에어리어에 복사해 수정 후 MODIFY를 사용하는 방식이 안전합니다.

Q5. FIELD-SYMBOLS를 메서드 파라미터로 넘겨도 되나요?
가능하지만 일반적으로 참조 자체를 넘기려는 목적이라면 REF TO가 의도가 명확합니다. FIELD-SYMBOLS 파라미터는 "임시 별명"으로만 쓰이는 성격이 강해 API 계약으로는 REF TO가 자연스럽습니다.

이어서 익히면 좋은 주제들

이 글에서 다룬 개념을 실무에 녹여내려면 몇 가지 심화 주제로 확장해 보는 것을 권장합니다. RTTS(CL_ABAP_TYPEDESCR, CL_ABAP_STRUCTDESCR)로 런타임 타입 정보를 얻어 동적 데이터 참조를 생성하는 기법, CREATE DATAASSIGN dref->* TO <fs>를 조합해 완전 동적 처리기를 만드는 패턴, ABAP CDS와 RAP 환경에서 엔티티 참조를 다루는 방식 등이 자연스러운 심화 주제입니다. 또한 성능이 극단적으로 중요한 배치 리포트라면 SAT(런타임 분석)로 실제 두 방식의 차이를 측정해 보는 습관을 들이면 좋습니다.

더 읽을거리와 링크

댓글 0

아직 댓글이 없습니다.