ABAP 을 처음 시작하면 FIELD-SYMBOLS 와 DATA REFERENCE(TYPE REF TO) 가 비슷해 보여서 어떤 상황에 무엇을 써야 할지 헷갈리는 순간이 옵니다. 둘 다 "주소" 와 관련된 개념이라 더 그렇습니다.
차이를 한 줄로 정리하면, FIELD-SYMBOLS 는 기존 변수에 붙이는 "별명", DATA REFERENCE 는 주소값 자체를 담는 변수 입니다. 별명은 그대로 값으로 쓰면 되고, 주소값 변수는 ->*(dereference 연산자) 로 한 번 더 꺼내야 값에 접근할 수 있습니다.
이 글은 두 개념의 차이와 사용 절차, 같이 쓰는 표준 패턴(CREATE DATA + ASSIGN ...->* TO <fs>), 그리고 실무에서 자주 만나는 함정을 정리한 메모입니다.
핵심 — 별명(Field Symbol) 과 주소 변수(Data Reference) 의 차이
두 개념은 결국 "메모리 어딘가에 있는 데이터를 간접적으로 가리킨다" 는 점에서 같지만, 선언 · 접근 방식 · 사용 시점 이 다릅니다.
| 항목 | FIELD-SYMBOL | DATA REFERENCE |
|---|---|---|
| 본질 | 변수에 붙이는 별명 · 라벨 | 주소값을 담는 일반 변수 |
| 선언 | FIELD-SYMBOLS <fs> TYPE ... |
DATA dref TYPE REF TO ... |
| 연결 방법 | ASSIGN var TO <fs> |
GET REFERENCE OF var INTO dref |
| 값 접근 | <fs> (그대로 변수처럼) |
dref->* (dereference 필수) |
| 주요 용도 | LOOP·READ TABLE 시 복사 없이 직접 접근, 성능 개선 | 동적 데이터 생성, 객체 참조 보관, 매개변수 전달 |
| 변수에 담을 수 있나 | 불가 (별명이라 다른 변수에 대입 X) | 가능 (일반 변수처럼 인터널 테이블·구조체 안에 담아둘 수 있음) |
쉽게 비유하자면 FIELD-SYMBOL 은 "이 변수의 다른 이름" 같은 별명 시스템이고, DATA REFERENCE 는 "이 변수가 있는 메모리 주소를 적어둔 종이" 같은 별도 변수입니다. 종이를 가지고 다니다가 필요할 때 ->* 로 펼쳐서 값을 확인합니다.
1단계 — FIELD-SYMBOL: 변수에 별명 붙이기
가장 자주 쓰이는 패턴은 LOOP AT ... ASSIGNING <fs> 입니다. 인터널 테이블의 각 행을 워크 에어리어에 복사하지 않고 원본 행 자체에 별명 을 붙여서 직접 읽고 수정합니다.
DATA: BEGIN OF gs_data,
name TYPE string,
age TYPE i,
END OF gs_data,
gt_data LIKE TABLE OF gs_data.
gt_data = VALUE #(
( name = 'Tom' age = 20 )
( name = 'Eva' age = 30 )
).
" 방법 1 — 미리 선언
FIELD-SYMBOLS <fs_line> LIKE gs_data.
LOOP AT gt_data ASSIGNING <fs_line>.
WRITE: / <fs_line>-name, <fs_line>-age.
ENDLOOP.
" 방법 2 — 인라인 선언 (ABAP 7.40+)
LOOP AT gt_data ASSIGNING FIELD-SYMBOL(<fs_inline>).
<fs_inline>-age = <fs_inline>-age + 1. " 원본 테이블이 직접 수정됨
ENDLOOP.
핵심 포인트:
LOOP ... INTO는 행을 워크 에어리어로 복사 합니다. 복사 후 워크 에어리어를 수정해도 원본 테이블은 그대로입니다 (별도로MODIFY가 필요).LOOP ... ASSIGNING은 행에 별명만 붙입니다. 별명을 통한 수정은 곧 원본 수정이며, 복사가 없어 대량 데이터에서 빠릅니다.- 별명은 LOOP 종료 후에도 마지막으로 가리켰던 행을 계속 가리킵니다. 다른 곳에서 재사용하기 전에
UNASSIGN <fs>로 명시적 해제를 권장합니다.
2단계 — DATA REFERENCE: 주소값을 담는 변수
DATA REFERENCE 는 다른 변수의 메모리 주소 를 값처럼 담아두는 변수입니다. TYPE REF TO 로 선언하고, GET REFERENCE OF 로 채웁니다.
DATA: int TYPE i VALUE 15,
ref_to_i TYPE REF TO i. " 정수형 데이터를 가리킬 reference
GET REFERENCE OF int INTO ref_to_i. " ref_to_i 에 int 의 주소를 저장
WRITE ref_to_i->*. " 결과: 15 (dereference 로 값 꺼냄)
값을 꺼낼 때는 반드시 ->* 가 필요합니다. WRITE ref_to_i 만 쓰면 주소값 자체(또는 컴파일 에러) 가 출력됩니다.
DATA REFERENCE 가 FIELD-SYMBOL 보다 강력한 점은 변수처럼 자유롭게 담아둘 수 있다는 것 입니다.
" 인터널 테이블에 reference 보관 가능
TYPES tt_ref TYPE STANDARD TABLE OF REF TO i WITH EMPTY KEY.
DATA lt_ref TYPE tt_ref.
APPEND ref_to_i TO lt_ref.
" 구조체 필드로 보관 가능
DATA: BEGIN OF ls_meta,
desc TYPE string,
ref TYPE REF TO i,
END OF ls_meta.
ls_meta-ref = ref_to_i.
FIELD-SYMBOL 은 별명이라 위 두 케이스 모두 불가능합니다.
3단계 — Dereference 연산자 (->*)
->* 는 "이 reference 가 가리키는 주소에 가서 그 값을 꺼내라" 는 연산자입니다. 데이터 reference 한정으로 쓰입니다.
DATA: int TYPE i VALUE 15,
ref_to_i TYPE REF TO i.
GET REFERENCE OF int INTO ref_to_i.
WRITE ref_to_i->*. " 결과: 15 (읽기)
ref_to_i->* = 17. " 쓰기 — int 자체가 17 로 변경됨
WRITE: / int. " 결과: 17 (원본도 같이 바뀜)
dereference 한 결과는 원본 변수와 같은 메모리 영역 을 가리키므로, dereference 결과에 값을 대입하면 원본도 같이 바뀝니다. 이 점에서 FIELD-SYMBOL 과 동일한 효과를 냅니다.
| 사용 | 의미 |
|---|---|
ref->* |
전체 값 (스칼라 / 구조체 / 인터널 테이블 전체) |
ref->component_name |
구조체 reference 의 특정 필드 (예: ls_ref->name) |
ASSIGN ref->* TO <fs> |
dereference 한 데이터에 FIELD-SYMBOL 까지 붙임 (4단계 패턴) |
4단계 — CREATE DATA + ASSIGN 패턴 (둘을 같이 쓰는 표준)
DATA REFERENCE 와 FIELD-SYMBOL 이 같이 쓰이는 가장 흔한 상황 은 동적 데이터 생성입니다. 컴파일 시점에는 타입을 모르고 런타임에 결정될 때 사용합니다.
표준 3단계 절차:
1. CREATE DATA ← 동적으로 메모리 할당, reference 만 얻음
2. GET / ASSIGN ← reference 의 dereference 결과를 field symbol 에 붙임
3. <fs> 로 데이터 사용
" 런타임에 결정되는 타입 (예: 함수 파라미터로 받아온 테이블명)
DATA: dref TYPE REF TO data.
FIELD-SYMBOLS: <fs_struct> TYPE any,
<fs_table> TYPE STANDARD TABLE.
" 1) 동적으로 구조체 메모리 할당
CREATE DATA dref TYPE ('MARA'). " 런타임에 자재 마스터 구조 생성
" 2) dereference 결과에 field symbol 붙이기
ASSIGN dref->* TO <fs_struct>.
" 3) <fs_struct> 로 사용 (READ TABLE·SELECT ... INTO @<fs_struct> 등)
SELECT SINGLE * FROM mara INTO @<fs_struct> WHERE matnr = '...'.
WRITE: / <fs_struct>.
CREATE DATA 가 만든 메모리는 reference 변수 (dref) 가 가리키고 있을 동안 살아있습니다. 마지막 reference 가 사라지면 가비지 컬렉터가 회수합니다.
ABAP 7.40+ 인라인 신택스로 줄여 쓰면:
" dref 선언과 메모리 할당 동시
DATA(dref) = NEW mara( ).
" ASSIGN dref->* TO <fs_struct> 패턴
ASSIGN dref->* TO FIELD-SYMBOL(<fs_struct>).
5단계 — 둘을 같이 쓰는 실무 패턴
패턴 A — 인터널 테이블에 다양한 타입 보관
같은 인터널 테이블에 자재·고객·문서 같은 서로 다른 타입의 객체 를 섞어 보관해야 한다면 reference 가 답입니다.
TYPES: BEGIN OF ty_box,
kind TYPE string,
data TYPE REF TO data,
END OF ty_box.
DATA: lt_box TYPE STANDARD TABLE OF ty_box,
ls_box LIKE LINE OF lt_box.
" 자재 구조 담기
DATA(ref_mara) = NEW mara( matnr = 'MAT-001' ).
ls_box-kind = 'MATERIAL'.
ls_box-data = ref_mara.
APPEND ls_box TO lt_box.
" 고객 구조 담기
DATA(ref_kna1) = NEW kna1( kunnr = 'CUST-001' ).
ls_box-kind = 'CUSTOMER'.
ls_box-data = ref_kna1.
APPEND ls_box TO lt_box.
패턴 B — 동적 SELECT 결과 처리
테이블명을 런타임에 받아 SELECT 하고 결과를 일반 코드로 처리하는 경우.
DATA: dref TYPE REF TO data.
FIELD-SYMBOLS: <ft_data> TYPE STANDARD TABLE.
CREATE DATA dref TYPE TABLE OF ('MARA').
ASSIGN dref->* TO <ft_data>.
SELECT * FROM mara INTO TABLE @<ft_data> UP TO 10 ROWS.
LOOP AT <ft_data> ASSIGNING FIELD-SYMBOL(<fs_line>).
" <fs_line> 으로 한 행씩 접근
ENDLOOP.
패턴 C — 매개변수로 reference 전달 (FM·메소드 인자)
함수에 "값" 이 아니라 "그 변수를 직접 수정해도 되는 권한" 을 넘기는 효과.
METHODS update_record
IMPORTING
ir_data TYPE REF TO mara. " reference 로 받음
" 호출 측
DATA(ref) = NEW mara( ).
ref->matnr = 'A'.
me->update_record( ir_data = ref ).
흔히 빠뜨리는 함정
LOOP ASSIGNING 후 가 계속 살아있음
LOOP 가 끝나면 <fs> 는 마지막 행을 그대로 가리킵니다. 그 뒤에 다른 곳에서 <fs> 를 의도 없이 쓰면 마지막 행이 수정됩니다. 안전하게 UNASSIGN <fs> 또는 IF <fs> IS ASSIGNED. 체크를 사용합니다.
dereference 안 하고 ref 자체를 사용
WRITE ref_to_i. 처럼 ->* 없이 reference 를 그대로 출력하면 컴파일 에러 또는 의미 없는 주소값이 나옵니다. 값을 다룰 때는 반드시 ref->* 로 dereference 합니다.
Reference 가 초기값일 때 dereference
비어있는 reference (IS INITIAL) 를 ->* 하면 런타임 에러 (CX_SY_REF_IS_INITIAL) 가 발생합니다. dereference 전에 IF ref IS BOUND. 체크가 안전합니다.
FIELD-SYMBOL 을 일반 변수처럼 다른 변수에 담으려 시도
DATA: lt = <fs>. 같이 별명을 변수에 담으려 하면 별명이 "복사" 됩니다 (값만 옮김, 별명 자체는 안 옮겨짐). 별명을 보관해야 한다면 DATA REFERENCE 를 사용합니다.
CREATE DATA 후 ASSIGN 누락
CREATE DATA dref TYPE ... 로 메모리만 만들고 ASSIGN dref->* TO <fs> 단계를 건너뛰면 정적 코드(예: SELECT INTO @<fs>) 에서 <fs> 가 비어있다는 에러가 납니다. 3단계(생성 → 할당 → 사용) 를 항상 한 묶음으로 다룹니다.
타입 불일치 ASSIGN
ASSIGN var TO <fs> 시 타입이 일치하지 않으면 런타임 에러 또는 의도 못 한 메모리 해석이 발생합니다. 타입이 가변적이면 FIELD-SYMBOLS <fs> TYPE any 또는 CASTING TYPE ... 을 사용합니다.
LOOP ... INTO 와 ASSIGNING 혼용
대량 데이터 LOOP 에서 INTO 와 ASSIGNING 을 한 코드에서 섞으면 성능 이점이 사라집니다. 수정이 필요하면 무조건 ASSIGNING 으로 통일합니다.
전체 코드 — 복사용 통합본
세 가지 사용 패턴(LOOP ASSIGNING · DATA REFERENCE · CREATE DATA 동적 생성) 을 한 프로그램에 담았습니다. SE38 에 그대로 붙여 실행 가능합니다.
REPORT z_fs_dref_demo.
* ★ 데모 1) FIELD-SYMBOL + LOOP ASSIGNING
DATA: BEGIN OF gs_data,
name TYPE string,
age TYPE i,
END OF gs_data,
gt_data LIKE TABLE OF gs_data.
gt_data = VALUE #(
( name = 'Tom' age = 20 )
( name = 'Eva' age = 30 )
).
WRITE: / '=== 1) LOOP ASSIGNING (원본 직접 수정) ==='.
LOOP AT gt_data ASSIGNING FIELD-SYMBOL(<fs_line>).
<fs_line>-age = <fs_line>-age + 1. " 원본 테이블이 직접 수정됨
WRITE: / <fs_line>-name, <fs_line>-age.
ENDLOOP.
* ★ 데모 2) DATA REFERENCE + GET REFERENCE + dereference
DATA: int TYPE i VALUE 15,
ref_to_i TYPE REF TO i.
GET REFERENCE OF int INTO ref_to_i.
WRITE: / '=== 2) DATA REFERENCE 와 ->* ==='.
WRITE: / 'ref->* 읽기:', ref_to_i->*.
ref_to_i->* = 17. " 쓰기 — int 도 17 로 바뀜
WRITE: / 'int 원본도 변경:', int.
* ★ 데모 3) CREATE DATA + ASSIGN ->* TO <fs> — 동적 데이터
DATA: dref TYPE REF TO data.
FIELD-SYMBOLS: <fs_struct> TYPE any.
CREATE DATA dref TYPE ('T001'). " 회사코드 마스터 구조 동적 생성
ASSIGN dref->* TO <fs_struct>.
SELECT SINGLE *
FROM t001
INTO @<fs_struct>
UP TO 1 ROWS.
WRITE: / '=== 3) 동적 생성 + ASSIGN ==='.
WRITE: / 'T001 1건 조회:', <fs_struct>.
* ★ 마무리 — Field symbol 명시적 해제
UNASSIGN: <fs_line>, <fs_struct>.
요약
| 단계 | 개념 | 핵심 신택스 |
|---|---|---|
| 1 | FIELD-SYMBOL — 변수 별명 | ASSIGN var TO <fs>, LOOP ... ASSIGNING <fs> |
| 2 | DATA REFERENCE — 주소값 변수 | DATA dref TYPE REF TO ..., GET REFERENCE OF var INTO dref |
| 3 | Dereference — 주소가 가리키는 값 | ref->*, ref->component |
| 4 | CREATE DATA + ASSIGN — 동적 생성 | CREATE DATA dref TYPE ('NAME') → ASSIGN dref->* TO <fs> |
| 5 | 실무 패턴 | 다양한 타입 보관(인터널 테이블 안 reference), 동적 SELECT, 메소드 인자 전달 |
요약하면 FIELD-SYMBOL 은 변수 별명, DATA REFERENCE 는 주소값을 담는 변수입니다. 별명은 곧바로 변수처럼 쓰면 되고, 주소값 변수는 ->* 로 dereference 해서 값을 꺼냅니다. 동적 데이터 처리에서는 두 개념이 함께 등장하며, "생성 → 할당 → 사용" 의 3단계 패턴이 표준입니다. 성능이 중요한 LOOP 는 무조건 ASSIGNING 으로, 변수처럼 보관·전달이 필요하면 DATA REFERENCE 로 가는 것이 기본 가이드입니다.
Disclaimer — 이 포스트는 실무 정리 노트를 바탕으로 AI 보조로 정리되었습니다.
FIELD-SYMBOLS · DATA REFERENCE · ASSIGN · GET REFERENCE OF · CREATE DATA · ->* 는 ABAP NetWeaver 표준 언어 기능으로 시스템 버전 의존 없이 동작합니다. 인라인 신택스(DATA(...), FIELD-SYMBOL(...), NEW #( ), REF #( )) 는 ABAP 7.40 이상에서만 사용 가능하며, 그 이전 버전에서는 사전 DATA · FIELD-SYMBOLS 선언이 필요합니다. CREATE DATA 로 생성한 메모리는 마지막 reference 가 사라지면 자동 회수되며, 명시적 해제는 필요하지 않습니다. 다만 long-running 프로그램에서 대량의 동적 데이터를 만들 때는 CLEAR 또는 FREE 로 reference 를 끊어 메모리 해제 타이밍을 명확히 하는 편이 안전합니다.