Field Symbol과 동적 프로그래밍

Field Symbol이란?

개념

Field Symbol은 ABAP에서 기존 변수의 메모리를 직접 가리키는 포인터(별칭)입니다. 일반 변수가 자체 메모리 공간을 가지는 것과 달리, Field Symbol은 자체 메모리 없이 다른 변수의 메모리를 참조합니다.

C언어의 포인터와 비슷하지만, ABAP의 Field Symbol은 항상 유효한 메모리만 가리킬 수 있어 더 안전합니다.

FIELD-SYMBOLS 선언

Field Symbol은 < > 꺾쇠 괄호로 감싸서 선언합니다.

1" ── 타입 지정 선언 ──
2FIELD-SYMBOLS: <lv_name>  TYPE string,       " STRING 타입 전용
3               <lv_value> TYPE i,            " 정수 타입 전용
4               <ls_mara>  TYPE mara.         " 구조체 타입 전용
5
6" ── 제네릭(범용) 선언 ──
7FIELD-SYMBOLS: <lv_any>   TYPE any,          " 모든 단일 타입
8               <ls_any>   TYPE any,          " 모든 구조체
9               <lt_table> TYPE ANY TABLE.    " 모든 내부 테이블
10
11" ── 데이터 타입 선언 ──
12FIELD-SYMBOLS: <lv_data>  TYPE data.         " TYPE any와 동일
타이핑키워드설명
완전 타이핑TYPE mara특정 타입만 할당 가능 (컴파일 타임 체크)
제네릭 타이핑TYPE any모든 타입 할당 가능 (런타임 체크)
테이블 타이핑TYPE ANY TABLE모든 내부 테이블 할당 가능

ASSIGN 구문

정적 ASSIGN

변수를 Field Symbol에 할당하면, Field Symbol을 통해 해당 변수를 직접 읽고 쓸 수 있습니다.

1DATA: lv_name TYPE string VALUE 'ABAP'.
2
3FIELD-SYMBOLS: <lv_name> TYPE string.
4
5" Field Symbol에 변수 할당
6ASSIGN lv_name TO <lv_name>.
7
8WRITE: / '원본:', lv_name.       " ABAP
9WRITE: / 'FS:', <lv_name>.       " ABAP
10
11" Field Symbol로 값 변경 → 원본도 변경됨
12<lv_name> = 'Hello ABAP'.
13
14WRITE: / '변경 후 원본:', lv_name.  " Hello ABAP
15WRITE: / '변경 후 FS:', <lv_name>.  " Hello ABAP

핵심: Field Symbol로 값을 변경하면 원본 변수도 함께 변경됩니다. 같은 메모리를 가리키기 때문입니다.

동적 ASSIGN

변수 이름을 문자열로 지정하여 런타임에 동적으로 할당할 수 있습니다.

1DATA: lv_price  TYPE p LENGTH 8 DECIMALS 2 VALUE '1500.00',
2      lv_amount TYPE p LENGTH 8 DECIMALS 2 VALUE '3000.00'.
3
4FIELD-SYMBOLS: <lv_value> TYPE any.
5
6" 변수 이름을 문자열로 지정
7DATA: lv_field_name TYPE string VALUE 'LV_PRICE'.
8
9ASSIGN (lv_field_name) TO <lv_value>.  " 동적 할당
10
11IF <lv_value> IS ASSIGNED.
12  WRITE: / '동적 접근:', <lv_value>.  " 1,500.00
13ENDIF.
14
15" 다른 변수로 변경
16lv_field_name = 'LV_AMOUNT'.
17ASSIGN (lv_field_name) TO <lv_value>.
18WRITE: / '동적 접근:', <lv_value>.    " 3,000.00

COMPONENT 접근

구조체의 필드를 인덱스 또는 이름으로 동적 접근합니다.

1TYPES: BEGIN OF ty_employee,
2         empno  TYPE n LENGTH 8,
3         name   TYPE c LENGTH 30,
4         dept   TYPE c LENGTH 20,
5         salary TYPE p LENGTH 8 DECIMALS 0,
6       END OF ty_employee.
7
8DATA: ls_emp TYPE ty_employee.
9ls_emp-empno  = '00000001'.
10ls_emp-name   = '홍길동'.
11ls_emp-dept   = '개발팀'.
12ls_emp-salary = 5000000.
13
14FIELD-SYMBOLS: <lv_field> TYPE any.
15
16" 인덱스로 접근 (1번째 필드 = empno)
17ASSIGN COMPONENT 1 OF STRUCTURE ls_emp TO <lv_field>.
18WRITE: / '1번 필드:', <lv_field>.  " 00000001
19
20" 필드명으로 접근
21ASSIGN COMPONENT 'NAME' OF STRUCTURE ls_emp TO <lv_field>.
22WRITE: / 'NAME 필드:', <lv_field>. " 홍길동
23
24" 모든 필드를 동적으로 순회
25DATA: lv_idx TYPE i VALUE 1.
26
27DO.
28  ASSIGN COMPONENT lv_idx OF STRUCTURE ls_emp TO <lv_field>.
29  IF sy-subrc <> 0.
30    EXIT.  " 더 이상 필드 없음
31  ENDIF.
32  WRITE: / '필드', lv_idx, ':', <lv_field>.
33  lv_idx = lv_idx + 1.
34ENDDO.
실행 결과:
1번 필드: 00000001 NAME 필드: 홍길동 필드 1 : 00000001 필드 2 : 홍길동 필드 3 : 개발팀 필드 4 : 5,000,000

내부 테이블과 Field Symbol

LOOP AT ~ ASSIGNING

ASSIGNING을 사용하면 내부 테이블의 행을 메모리 복사 없이 직접 참조합니다. 이를 통해 LOOP 안에서 바로 값을 수정할 수 있습니다.

1TYPES: BEGIN OF ty_item,
2         id    TYPE i,
3         name  TYPE c LENGTH 20,
4         price TYPE p LENGTH 8 DECIMALS 2,
5       END OF ty_item.
6
7DATA: lt_items TYPE TABLE OF ty_item.
8
9APPEND VALUE #( id = 1 name = '사과'   price = '1000.00' ) TO lt_items.
10APPEND VALUE #( id = 2 name = '바나나' price = '2000.00' ) TO lt_items.
11APPEND VALUE #( id = 3 name = '딸기'   price = '3000.00' ) TO lt_items.
12
13FIELD-SYMBOLS: <ls_item> TYPE ty_item.
14
15" ASSIGNING으로 직접 수정 (MODIFY 불필요)
16LOOP AT lt_items ASSIGNING <ls_item>.
17  <ls_item>-price = <ls_item>-price * '1.1'.  " 10% 인상
18ENDLOOP.
19
20" 결과 확인
21LOOP AT lt_items ASSIGNING <ls_item>.
22  WRITE: / <ls_item>-name, <ls_item>-price.
23ENDLOOP.
24" 사과: 1,100.00 / 바나나: 2,200.00 / 딸기: 3,300.00

INTO vs ASSIGNING 비교

INTO데이터 복사 방식
내부 테이블 행→ 복사 →Work Area

📋 메모리: 추가 할당 (복사본)
✏️ 수정: Work Area에서 수정
🔄 반영: MODIFY 필수!
⚡ 성능: 느림 (복사 비용)

ASSIGNING메모리 직접 참조
내부 테이블 행← 참조 ←<fs>

📋 메모리: 추가 없음 (같은 메모리)
✏️ 수정: Field Symbol로 직접 수정
🔄 반영: 즉시 반영!
⚡ 성능: 2~5배 빠름

성능 비교

1DATA: lt_big_table TYPE TABLE OF ty_item,
2      ls_item      TYPE ty_item.
3
4FIELD-SYMBOLS: <ls_item> TYPE ty_item.
5
6" 10만 건 테스트 데이터 생성
7DO 100000 TIMES.
8  APPEND VALUE #( id = sy-index name = 'TEST' price = sy-index ) TO lt_big_table.
9ENDDO.
10
11DATA: lv_start TYPE i, lv_end TYPE i, lv_diff TYPE i.
12
13" ── INTO 방식 ──
14GET RUN TIME FIELD lv_start.
15LOOP AT lt_big_table INTO ls_item.
16  ls_item-price = ls_item-price * '1.1'.
17  MODIFY lt_big_table FROM ls_item.
18ENDLOOP.
19GET RUN TIME FIELD lv_end.
20lv_diff = lv_end - lv_start.
21WRITE: / 'INTO + MODIFY:', lv_diff, '마이크로초'.
22
23" ── ASSIGNING 방식 ──
24GET RUN TIME FIELD lv_start.
25LOOP AT lt_big_table ASSIGNING <ls_item>.
26  <ls_item>-price = <ls_item>-price * '1.1'.
27ENDLOOP.
28GET RUN TIME FIELD lv_end.
29lv_diff = lv_end - lv_start.
30WRITE: / 'ASSIGNING:', lv_diff, '마이크로초'.

일반적인 결과: ASSIGNING이 INTO + MODIFY 대비 2~5배 빠릅니다. 대량 데이터 처리 시 성능 차이가 더욱 커집니다.

IS ASSIGNED 체크

Field Symbol이 유효한 메모리에 할당되었는지 반드시 확인해야 합니다.

1FIELD-SYMBOLS: <ls_item> TYPE ty_item.
2
3" 할당 전 사용 → 런타임 에러!
4" WRITE: <ls_item>-name.  " ← GETWA_NOT_ASSIGNED 덤프 발생
5
6" 안전한 사용법: READ TABLE 후 sy-subrc 체크가 표준 관행
7READ TABLE lt_items ASSIGNING <ls_item> WITH KEY id = 999.
8IF sy-subrc = 0.
9  WRITE: / '찾음:', <ls_item>-name.
10ELSE.
11  WRITE: / '데이터 없음'.
12ENDIF.
13
14" IS ASSIGNED는 할당 여부만 확인 (이전에 다른 행이 할당되었다면 TRUE)
15" 따라서 READ TABLE 직후에는 sy-subrc 체크가 더 정확합니다.
16
17" 할당 해제
18UNASSIGN <ls_item>.
19IF <ls_item> IS NOT ASSIGNED.
20  WRITE: / 'Field Symbol이 해제되었습니다.'.
21ENDIF.

Data Reference

Data Reference는 데이터 오브젝트를 가리키는 참조 변수입니다. Field Symbol과 달리 동적으로 메모리를 생성할 수 있습니다.

GET REFERENCE OF

기존 변수의 참조를 얻습니다.

1DATA: lv_value TYPE i VALUE 42.
2DATA: lr_ref   TYPE REF TO i.
3
4" 참조 생성
5GET REFERENCE OF lv_value INTO lr_ref.
6
7" 역참조로 값 읽기
8WRITE: / '참조를 통한 값:', lr_ref->*.  " 42
9
10" 참조를 통해 값 변경
11lr_ref->* = 100.
12WRITE: / '원본 변경됨:', lv_value.  " 100

CREATE DATA

런타임에 동적으로 데이터 오브젝트를 생성합니다.

1" ── 타입 지정 생성 ──
2DATA: lr_int TYPE REF TO i.
3CREATE DATA lr_int.
4lr_int->* = 999.
5WRITE: / '동적 생성:', lr_int->*.
6
7" ── 동적 타입으로 생성 ──
8DATA: lr_data TYPE REF TO data.
9FIELD-SYMBOLS: <lv_data> TYPE any.
10
11DATA: lv_type_name TYPE string VALUE 'MARA'.
12CREATE DATA lr_data TYPE (lv_type_name).
13
14" 역참조하여 Field Symbol에 할당
15ASSIGN lr_data->* TO <lv_data>.
16" 이제 <lv_data>는 MARA 구조체처럼 사용 가능
17
18" ── 동적 내부 테이블 생성 ──
19DATA: lr_table TYPE REF TO data.
20FIELD-SYMBOLS: <lt_table> TYPE ANY TABLE.
21
22CREATE DATA lr_table TYPE STANDARD TABLE OF (lv_type_name).
23ASSIGN lr_table->* TO <lt_table>.
24" <lt_table>은 MARA의 내부 테이블

Data Reference vs Field Symbol 비교

구분Field SymbolData Reference
선언FIELD-SYMBOLS: <fs>DATA: lr TYPE REF TO ...
자체 메모리없음있음 (참조 변수)
동적 생성불가CREATE DATA로 가능
역참조직접 사용->* 연산자
주요 용도LOOP, 구조체 필드 접근동적 데이터 생성, 제네릭 프로그래밍

동적 프로그래밍 활용

동적 WHERE 조건

문자열로 WHERE 조건을 구성하여 런타임에 적용합니다.

1DATA: lt_items TYPE TABLE OF ty_item.
2
3" ... 데이터 추가 ...
4
5DATA: lv_where TYPE string.
6
7" 동적 조건 구성
8lv_where = |PRICE > 1500|.
9
10FIELD-SYMBOLS: <ls_item> TYPE ty_item.
11
12LOOP AT lt_items ASSIGNING <ls_item> WHERE (lv_where).
13  WRITE: / <ls_item>-name, <ls_item>-price.
14ENDLOOP.

동적 테이블 생성과 조회

테이블명을 입력 받아 동적으로 데이터를 조회하는 패턴입니다.

1DATA: lv_tabname TYPE string VALUE 'MARA'.
2
3" 동적 내부 테이블 생성
4DATA: lr_table TYPE REF TO data,
5      lr_line  TYPE REF TO data.
6
7FIELD-SYMBOLS: <lt_table> TYPE ANY TABLE,
8               <ls_line>  TYPE any,
9               <lv_field> TYPE any.
10
11CREATE DATA lr_table TYPE STANDARD TABLE OF (lv_tabname).
12ASSIGN lr_table->* TO <lt_table>.
13
14CREATE DATA lr_line TYPE (lv_tabname).
15ASSIGN lr_line->* TO <ls_line>.
16
17" 동적 SELECT
18SELECT *
19  FROM (lv_tabname)
20  INTO TABLE <lt_table>
21  UP TO 10 ROWS.
22
23IF sy-subrc = 0.
24  WRITE: / |{ lv_tabname } 테이블에서 { lines( <lt_table> ) }건 조회|.
25
26  " 동적으로 모든 필드 출력
27  LOOP AT <lt_table> ASSIGNING <ls_line>.
28    DATA: lv_idx TYPE i.
29    lv_idx = 1.
30    DO.
31      ASSIGN COMPONENT lv_idx OF STRUCTURE <ls_line> TO <lv_field>.
32      IF sy-subrc <> 0.
33        EXIT.
34      ENDIF.
35      IF lv_idx > 1.
36        WRITE: '|'.
37      ENDIF.
38      WRITE: <lv_field>.
39      lv_idx = lv_idx + 1.
40    ENDDO.
41    NEW-LINE.
42  ENDLOOP.
43ENDIF.

실무 예제: 범용 데이터 변환 유틸리티

테이블명을 입력받아 동적으로 데이터를 조회하고, 모든 컬럼을 파이프(|) 구분자로 출력하는 범용 유틸리티입니다.

1REPORT Z_DYNAMIC_TABLE_VIEWER.
2
3" ==============================
4" 1. Selection Screen
5" ==============================
6PARAMETERS: p_table TYPE dd02l-tabname DEFAULT 'MARA'.  " 테이블명
7PARAMETERS: p_rows  TYPE i DEFAULT 20.                   " 최대 행수
8
9" ==============================
10" 2. 데이터 선언
11" ==============================
12DATA: lr_table TYPE REF TO data,
13      lr_line  TYPE REF TO data.
14
15FIELD-SYMBOLS: <lt_table> TYPE ANY TABLE,
16               <ls_line>  TYPE any,
17               <lv_field> TYPE any.
18
19DATA: lt_dfies   TYPE TABLE OF dfies,
20      ls_dfies   TYPE dfies.
21
22DATA: lv_col_count TYPE i,
23      lv_idx       TYPE i.
24
25" ==============================
26" 3. 테이블 존재 확인
27" ==============================
28START-OF-SELECTION.
29
30  " DDIC에서 테이블 필드 정보 조회
31  CALL FUNCTION 'DDIF_FIELDINFO_GET'
32    EXPORTING
33      tabname        = p_table
34    TABLES
35      dfies_tab      = lt_dfies
36    EXCEPTIONS
37      not_found      = 1
38      internal_error = 2
39      OTHERS         = 3.
40
41  IF sy-subrc <> 0.
42    WRITE: / |테이블 '{ p_table }'() 찾을 수 없습니다.|.
43    RETURN.
44  ENDIF.
45
46  lv_col_count = lines( lt_dfies ).
47  WRITE: / |테이블: { p_table } ({ lv_col_count }개 컬럼)|.
48
49" ==============================
50" 4. 컬럼 헤더 출력
51" ==============================
52  ULINE.
53  LOOP AT lt_dfies INTO ls_dfies.
54    IF sy-tabix > 1.
55      WRITE: ' | '.
56    ENDIF.
57    WRITE: ls_dfies-fieldname.
58  ENDLOOP.
59  NEW-LINE.
60  ULINE.
61
62" ==============================
63" 5. 동적 테이블 생성 및 데이터 조회
64" ==============================
65  CREATE DATA lr_table TYPE STANDARD TABLE OF (p_table).
66  ASSIGN lr_table->* TO <lt_table>.
67
68  CREATE DATA lr_line TYPE (p_table).
69  ASSIGN lr_line->* TO <ls_line>.
70
71  SELECT *
72    FROM (p_table)
73    INTO TABLE <lt_table>
74    UP TO p_rows ROWS.
75
76  IF sy-subrc <> 0.
77    WRITE: / '데이터가 없습니다.'.
78    RETURN.
79  ENDIF.
80
81  WRITE: / |{ lines( <lt_table> ) }건 조회됨.|.
82  ULINE.
83
84" ==============================
85" 6. 데이터 출력
86" ==============================
87  LOOP AT <lt_table> ASSIGNING <ls_line>.
88    lv_idx = 1.
89    DO lv_col_count TIMES.
90      ASSIGN COMPONENT lv_idx OF STRUCTURE <ls_line> TO <lv_field>.
91      IF sy-subrc <> 0.
92        EXIT.
93      ENDIF.
94      IF lv_idx > 1.
95        WRITE: ' | '.
96      ENDIF.
97      WRITE: <lv_field>.
98      lv_idx = lv_idx + 1.
99    ENDDO.
100    NEW-LINE.
101  ENDLOOP.
102
103  ULINE.
104  WRITE: / |출력 완료. (총 { lines( <lt_table> ) }건)|.
실행 결과 예시 (p_table = 'T001', p_rows = 3):
테이블: T001 (23개 컬럼) ─────────────────────────────── MANDT | BUKRS | BUTXT | ORT01 | LAND1 | WAERS | ... ─────────────────────────────── 3건 조회됨. ─────────────────────────────── 800 | 1000 | 테스트 회사 | 서울 | KR | KRW | ... 800 | 2000 | 해외 법인 | 도쿄 | JP | JPY | ... 800 | 3000 | 미국 법인 | LA | US | USD | ... ─────────────────────────────── 출력 완료. (총 3건)

이 프로그램은 CREATE DATA, ASSIGN COMPONENT, FIELD-SYMBOLS, DDIF_FIELDINFO_GET 등 동적 프로그래밍의 핵심 기법을 모두 활용한 종합 예제입니다. 테이블명만 바꾸면 어떤 테이블이든 조회할 수 있는 범용 유틸리티로 활용할 수 있습니다.

Disclaimer — 이 포스트는 AI(Claude)를 활용하여 작성된 초안을 바탕으로 검수 및 보완하여 작성되었습니다. 내용 중 오류나 오타가 있다면 댓글로 알려주시면 감사하겠습니다.