Field Symbol과 동적 프로그래밍
Field Symbol이란?
개념
Field Symbol은 ABAP에서 기존 변수의 메모리를 직접 가리키는 포인터(별칭)입니다. 일반 변수가 자체 메모리 공간을 가지는 것과 달리, Field Symbol은 자체 메모리 없이 다른 변수의 메모리를 참조합니다.
C언어의 포인터와 비슷하지만, ABAP의 Field Symbol은 항상 유효한 메모리만 가리킬 수 있어 더 안전합니다.
FIELD-SYMBOLS 선언
Field Symbol은 < > 꺾쇠 괄호로 감싸서 선언합니다.
1
2FIELD-SYMBOLS: <lv_name> TYPE 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 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
6ASSIGN lv_name TO <lv_name>.
7
8WRITE: / '원본:', lv_name.
9WRITE: / 'FS:', <lv_name>.
10
11
12<lv_name> = 'Hello ABAP'.
13
14WRITE: / '변경 후 원본:', lv_name.
15WRITE: / '변경 후 FS:', <lv_name>.
핵심: 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>.
13ENDIF.
14
15
16lv_field_name = 'LV_AMOUNT'.
17ASSIGN (lv_field_name) TO <lv_value>.
18WRITE: / '동적 접근:', <lv_value>.
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
17ASSIGN COMPONENT 1 OF STRUCTURE ls_emp TO <lv_field>.
18WRITE: / '1번 필드:', <lv_field>.
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
16LOOP AT lt_items ASSIGNING <ls_item>.
17 <ls_item>-price = <ls_item>-price * '1.1'.
18ENDLOOP.
19
20
21LOOP AT lt_items ASSIGNING <ls_item>.
22 WRITE: / <ls_item>-name, <ls_item>-price.
23ENDLOOP.
24
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
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
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
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
5
6
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
15
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->*.
9
10
11lr_ref->* = 100.
12WRITE: / '원본 변경됨:', lv_value.
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
15ASSIGN lr_data->* TO <lv_data>.
16
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
Data Reference vs Field Symbol 비교
| 구분 | Field Symbol | Data 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
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
5
6PARAMETERS: p_table TYPE dd02l-tabname DEFAULT 'MARA'.
7PARAMETERS: p_rows TYPE i DEFAULT 20.
8
9
10
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
27
28START-OF-SELECTION.
29
30
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
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
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
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)를 활용하여 작성된 초안을 바탕으로 검수 및 보완하여 작성되었습니다. 내용 중 오류나 오타가 있다면 댓글로 알려주시면 감사하겠습니다.