News

ABAP IMPORT/EXPORT Memory, 언제 쓰는 건가요? #shorts #SAP #ABAP

▶ YouTube에서 보기

1. 개요 및 이 글의 목표

ABAP에서 서로 다른 프로그램, 서브루틴, 함수 모듈, 다이얼로그 사이에 임시 데이터를 주고받아야 하는 상황은 빈번하게 발생합니다. 파라미터로 직접 전달할 수 없는 컨텍스트(예: SUBMIT 호출, CALL TRANSACTION, 모달 팝업 종료 후 결과 수신)에서는 EXPORT TO MEMORY ID / IMPORT FROM MEMORY ID 구문이 사실상 표준 패턴으로 자리 잡고 있습니다.

본 글에서 다루는 범위는 다음과 같습니다.

  • ABAP 메모리(ABAP Memory)와 SAP 메모리(SAP Memory)의 차이
  • EXPORT/IMPORT 구문의 기본 문법과 동작 원리
  • FREE MEMORY ID를 통한 명시적 메모리 해제
  • 세션 범위(internal session) 안에서의 가시성
  • 실무에서 자주 마주치는 함정과 ID 네이밍 규칙
  • 모달 팝업·서브루틴·SUBMIT 시나리오에서의 응용 패턴

대상은 ABAP 7.40 이상(7.50 / 7.54 / NW 7.55 검증) 환경에서 ECC, S/4HANA on-prem, BTP ABAP Environment(일부 제약)를 사용하는 개발자입니다.

2. 핵심 개념 — EXPORT TO MEMORY vs 글로벌 변수 vs 파라미터 전달 비교

ABAP에서 데이터를 공유하는 방식은 크게 네 가지로 정리할 수 있습니다.

방식범위수명주 용도
FORM/메서드 파라미터호출 체인 내호출 종료 시 소멸일반적인 함수 호출
글로벌 변수동일 프로그램 내프로그램 종료 시 소멸같은 컴파일 단위 공유
SAP Memory (SET/GET PARAMETER)전체 사용자 세션(여러 트랜잭션)로그아웃 시 소멸SU3 파라미터, 화면 초기값
ABAP Memory (EXPORT/IMPORT)동일 internal session(LUW)internal session 종료 또는 FREESUBMIT·CALL TRANSACTION·서브루틴 간 데이터 전달

비유하자면 ABAP 메모리는 같은 "내부 세션"이라는 작업장 안에 있는 사물함이고, ID는 그 사물함을 식별하는 열쇠표입니다. 같은 사물함을 쓰는 직원(서브루틴, CALL TRANSACTION, SUBMIT으로 호출된 프로그램)은 모두 같은 열쇠표만 알면 내용을 꺼낼 수 있지만, 다른 사용자나 다른 세션(다른 모드)에서는 그 사물함 자체에 접근할 수 없습니다.

중요한 점은 ABAP Memory는 직렬화된 바이너리 형태로 저장된다는 점입니다. 따라서 구조체, 내부 테이블, 객체 참조(같은 internal session 내에서만 유효)까지 통째로 저장이 가능합니다. 단, 클러스터 형식으로 직렬화되므로 export한 변수의 데이터 타입은 import 시점에서도 호환되어야 합니다.

3. 1단계: EXPORT ... TO MEMORY ID로 구조체/테이블 저장

가장 기본적인 형태는 다음과 같습니다. 단일 변수, 구조체, 내부 테이블을 자유롭게 묶어 하나의 메모리 ID에 저장할 수 있습니다.

REPORT zdemo_export_basic.

TYPES: BEGIN OF ty_order,
         order_id   TYPE vbeln_va,
         customer   TYPE kunnr,
         net_value  TYPE netwr_ak,
       END OF ty_order.

DATA: ls_order  TYPE ty_order,
      lt_orders TYPE STANDARD TABLE OF ty_order,
      lv_count  TYPE i.

ls_order = VALUE #( order_id  = '0000004711'
                    customer  = '0000100001'
                    net_value = '1500.00' ).
APPEND ls_order TO lt_orders.

ls_order = VALUE #( order_id  = '0000004712'
                    customer  = '0000100002'
                    net_value = '2300.00' ).
APPEND lt_orders TO lt_orders.
lv_count = lines( lt_orders ).

EXPORT ls_order  FROM ls_order
       lt_orders FROM lt_orders
       lv_count  FROM lv_count
  TO MEMORY ID 'ZORDER_BUFFER'.

IF sy-subrc = 0.
  WRITE: / '메모리에 저장 완료, 행 수:', lv_count.
ENDIF.

여기서 알아두어야 할 핵심은 EXPORT name1 FROM var1 name2 FROM var2 ... 구문입니다. name은 메모리 클러스터 내부에서 식별자로 사용되고, IMPORT 시점에 같은 이름으로 매칭됩니다. ID는 일반적으로 최대 32자(또는 60자, 릴리스 의존) 범위 내의 문자열을 권장합니다.

주의: EXPORT는 기존에 동일 ID로 저장된 데이터를 덮어씁니다. 누적 저장이 아닌 전체 교체 방식입니다.

4. 2단계: IMPORT ... FROM MEMORY ID로 다른 프로그램에서 읽기

다른 프로그램(또는 같은 internal session에서 호출된 함수, SUBMIT으로 실행된 프로그램)에서는 IMPORT 구문으로 데이터를 읽습니다.

REPORT zdemo_import_basic.

TYPES: BEGIN OF ty_order,
         order_id   TYPE vbeln_va,
         customer   TYPE kunnr,
         net_value  TYPE netwr_ak,
       END OF ty_order.

DATA: ls_order  TYPE ty_order,
      lt_orders TYPE STANDARD TABLE OF ty_order,
      lv_count  TYPE i.

IMPORT ls_order  TO ls_order
       lt_orders TO lt_orders
       lv_count  TO lv_count
  FROM MEMORY ID 'ZORDER_BUFFER'.

IF sy-subrc <> 0.
  MESSAGE 'ABAP 메모리에서 데이터를 찾을 수 없습니다.' TYPE 'E'.
ENDIF.

WRITE: / '복원된 주문 수:', lv_count.
LOOP AT lt_orders INTO ls_order.
  WRITE: / ls_order-order_id, ls_order-customer, ls_order-net_value.
ENDLOOP.

IMPORT는 부분 import도 허용합니다. EXPORT 시 3개를 저장했더라도 IMPORT 측에서는 필요한 것만 골라 받을 수 있습니다. 단, 매칭은 EXPORT name의 식별자 기준으로 이루어지므로 이름이 일치해야 합니다.

또한 sy-subrc 체크는 필수입니다. 메모리에 ID가 존재하지 않으면 4가 반환되며, 변수는 초기 상태로 남습니다. 부분적으로만 존재하는 경우(일부 식별자만 매칭) sy-subrc = 0이지만 누락된 변수는 초기값입니다.

SUBMIT과 함께 쓰는 전형적 패턴

" 호출자 프로그램
EXPORT p_input FROM 'HELLO' TO MEMORY ID 'ZSUBMIT_IN'.

SUBMIT zsub_program
       WITH p_run = 'X'
       AND RETURN.

IMPORT p_result TO DATA(lv_result)
  FROM MEMORY ID 'ZSUBMIT_OUT'.

" 호출 대상 프로그램(zsub_program)
INITIALIZATION.
  IMPORT p_input TO DATA(lv_input) FROM MEMORY ID 'ZSUBMIT_IN'.

END-OF-SELECTION.
  EXPORT p_result FROM |처리됨: { lv_input }|
    TO MEMORY ID 'ZSUBMIT_OUT'.

SUBMIT ... AND RETURN은 같은 internal session 내에서 동작하므로 ABAP 메모리가 그대로 유지됩니다. 반면 SUBMIT만 단독 사용(AND RETURN 없음)은 새로운 internal session으로 점프할 수 있어 주의가 필요합니다.

5. 3단계: FREE MEMORY ID로 메모리 명시적 해제

EXPORT로 저장된 데이터는 internal session이 끝나면 자동으로 해제됩니다. 하지만 장시간 실행되는 배치 작업이나 반복적으로 큰 테이블을 export 하는 트랜잭션에서는 명시적으로 메모리를 해제하는 것이 권장됩니다.

" 특정 ID만 해제
FREE MEMORY ID 'ZORDER_BUFFER'.

" 현재 internal session의 모든 ABAP 메모리 해제 (주의해서 사용)
FREE MEMORY.

FREE MEMORY(ID 없이)는 해당 internal session 내에 저장된 모든 메모리 클러스터를 해제합니다. 다른 모듈이 같은 메모리를 참조하고 있을 가능성이 있다면 이 무인자 형태는 사용하지 않는 것이 일반적으로 안전합니다.

모범 패턴: try-finally 스타일

DATA(lv_id) = 'ZHUGE_TAB' && sy-uname.

TRY.
    EXPORT lt_huge FROM lt_huge TO MEMORY ID lv_id.

    PERFORM call_external_program USING lv_id.

    IMPORT lt_result TO lt_result FROM MEMORY ID lv_id.
  CLEANUP.
    FREE MEMORY ID lv_id.
ENDTRY.

" 정상 종료 시에도 해제
FREE MEMORY ID lv_id.

큰 내부 테이블(수십만 행 이상)을 export 후 해제하지 않으면 internal session 메모리에 그대로 누적되어 TSV_TNEW_PAGE_ALLOC_FAILED(메모리 부족) 덤프를 유발할 수 있습니다.

6. 세션 범위와 제약 — 동일 세션 전용, 다른 사용자 접근 불가

ABAP 메모리의 가시성을 잘못 이해하면 디버깅이 매우 힘들어집니다. 다음 원칙을 기억하는 것이 좋습니다.

  • 동일 internal session(LUW)에서만 유효: 같은 사용자가 새 모드(/oXXXX)로 다른 트랜잭션을 열면 그 모드의 메모리는 별도입니다.
  • 사용자 간 공유 불가: A 사용자가 EXPORT한 데이터를 B 사용자가 IMPORT 할 수 없습니다.
  • RFC 호출(다른 시스템)에도 전달되지 않음: 원격 호출은 새로운 세션이므로 ABAP 메모리가 분리됩니다.
  • 업데이트 함수(IN UPDATE TASK): 업데이트 작업은 별도 세션에서 수행되므로 호출자의 ABAP 메모리에 직접 접근할 수 없습니다. 필요하다면 함수 파라미터로 전달해야 합니다.
  • 병렬 처리(STARTING NEW TASK): 비동기 RFC 호출도 새 internal session에서 동작합니다.

다음은 세션 범위 확인 코드 예시입니다.

DATA: lv_session TYPE string.

cl_abap_session_id=>get_session_id(
  IMPORTING session_id = lv_session ).

WRITE: / '현재 internal session:', lv_session.

이 ID가 바뀌었다면 export한 메모리는 더 이상 보이지 않는다고 봐야 합니다.

7. 자주 겪는 함정 — ID 충돌, sy-subrc 미확인, 메모리 누수

FAQ 1. 다른 사용자가 같은 ID로 동시 실행하면 데이터가 섞이지 않나요?

섞이지 않습니다. ABAP 메모리는 internal session 단위로 격리됩니다. 다만 같은 사용자가 같은 모드에서 두 번 연속 export 하면 두 번째가 첫 번째를 덮어씁니다. 따라서 동일 모드 내 중첩 호출(예: 모달 팝업 → 또 다른 SUBMIT)에서는 ID 충돌을 방지하기 위해 명확한 네이밍 규칙이 필요합니다.

" 권장 네이밍: 회사 prefix + 용도 + 호출컨텍스트
DATA(lv_id) = |Z_{ sy-cprog }_{ sy-uname }_RESULT|.

FAQ 2. IMPORT 후에 sy-subrc가 0인데 변수가 비어 있어요.

EXPORT 시점에서 사용한 식별자(EXPORT name FROM var의 name)와 IMPORT 측의 식별자(IMPORT name TO var의 name)가 다르기 때문일 가능성이 가장 높습니다. sy-subrc = 0은 ID 클러스터가 존재한다는 의미일 뿐 모든 식별자가 매칭되었음을 의미하지 않습니다.

EXPORT lt_data FROM lt_orders TO MEMORY ID 'ZBUF'.    " 식별자: lt_data
IMPORT lt_data TO lt_orders   FROM MEMORY ID 'ZBUF'.   " 동일해야 함

FAQ 3. 데이터 타입을 바꿨더니 IMPORT가 깨집니다.

EXPORT는 내부 직렬화 형식으로 저장하므로, 구조체 필드 순서·타입이 바뀌면 변환 오류 또는 데이터 손상이 발생할 수 있습니다. TYPES를 공통 글로벌 타입(예: DDIC 구조체, 타입 풀)에 두고 export/import 양쪽에서 동일하게 참조하는 것이 권장됩니다. 패치/이전 작업 후에는 반드시 FREE MEMORY ID를 수행하거나 ID 자체를 변경하는 것이 안전합니다.

기타 흔한 함정

  • 객체 참조 export: 객체(REF TO)도 export 가능하지만 같은 internal session 내에서만 유효합니다. SUBMIT으로 새 세션이 열린 경우 참조는 의미를 잃습니다.
  • 대문자 ID: 메모리 ID는 대소문자를 구분합니다. 'ZBuf''ZBUF'는 다른 클러스터입니다. 일반적으로 대문자 표기를 권장합니다.
  • 업데이트 함수에서의 접근 시도: CALL FUNCTION ... IN UPDATE TASK는 별도 세션이므로 import해도 빈 값입니다. 파라미터로 전달하거나 영구 테이블에 저장 후 키만 전달하는 패턴이 권장됩니다.

8. 응용 패턴 — 모달 팝업 결과값 전달, 서브루틴 간 대용량 데이터 공유

패턴 A: 모달 다이얼로그(CALL SCREEN STARTING AT)의 결과 회수

레거시 다이얼로그 프로그램에서 팝업으로 입력을 받은 뒤, 호출자 측에 결과를 돌려주는 전형적인 코드입니다.

" 호출자
CALL SCREEN 0100 STARTING AT 10 5 ENDING AT 80 20.

IMPORT p_selected_key TO DATA(lv_key)
  FROM MEMORY ID 'ZPOPUP_RESULT'.
FREE MEMORY ID 'ZPOPUP_RESULT'.

IF lv_key IS NOT INITIAL.
  PERFORM process_selection USING lv_key.
ENDIF.

" 팝업 PAI에서 OK 버튼 처리
MODULE user_command_0100 INPUT.
  CASE ok_code.
    WHEN 'OK'.
      EXPORT p_selected_key FROM gv_chosen_key
        TO MEMORY ID 'ZPOPUP_RESULT'.
      LEAVE TO SCREEN 0.
  ENDCASE.
ENDMODULE.

패턴 B: 함수 모듈과 호출자 간 대용량 테이블 공유

RFC가 아닌 로컬 함수 모듈을 호출할 때, 수십만 행짜리 내부 테이블을 파라미터로 전달하면 값 복사 비용이 큽니다. 대신 ID로 참조하는 방식이 사용되곤 합니다.

EXPORT lt_huge FROM lt_huge TO MEMORY ID 'ZBULK_IN'.

CALL FUNCTION 'Z_BULK_PROCESSOR'
  EXPORTING
    iv_buffer_id = 'ZBULK_IN'
  IMPORTING
    ev_status    = lv_status.

FREE MEMORY ID 'ZBULK_IN'.

" 함수 모듈 내부
FUNCTION z_bulk_processor.
  IMPORT lt_huge TO DATA(lt_local)
    FROM MEMORY ID iv_buffer_id.

  IF sy-subrc <> 0.
    ev_status = 'NO_DATA'.
    RETURN.
  ENDIF.

  " 처리 로직 ...
ENDFUNCTION.

다만 ABAP 7.40 이후에는 CHANGING 파라미터에 의한 reference passing이 충분히 빠르므로, 단순한 성능 목적이라면 파라미터 사용이 더 깔끔합니다. ABAP 메모리는 "직접 파라미터로 전달할 수 없는 경로(SUBMIT, CALL TRANSACTION, 다이얼로그 전환)"가 핵심 용도라고 보는 것이 일반적입니다.

패턴 C: 단일 사용자 캐시 활용

장시간 다이얼로그 처리 중 자주 참조되는 마스터 데이터를 사용자 세션 동안 캐시할 때도 사용됩니다. 단, 이 경우 데이터 갱신 정책과 FREE MEMORY ID 호출 시점을 명확히 설계해야 합니다. 더 정형화된 캐시가 필요하다면 CL_ABAP_MEMORY_AREA / Shared Buffer/Memory(SHMA, SHMM) 등 공유 메모리 객체 사용이 권장됩니다.

현대적 대안과의 비교

  • Shared Memory Objects(SHMA/SHMM): 여러 사용자/세션이 공유해야 할 때 사용. 트랜잭션 SHMA로 정의.
  • Database 임시 저장: 트랜잭션 경계를 넘어 보존해야 할 때 사용. INDX 테이블 또는 사용자 정의 테이블 + GUID 키.
  • RAP/CDS 컨텍스트: BTP ABAP Environment의 RAP 시나리오에서는 EXPORT/IMPORT의 사용이 권장되지 않으며, 대신 transactional buffer와 EML(Entity Manipulation Language)을 사용하는 것이 일반적입니다.

레거시 다이얼로그·SUBMIT 기반 코드와 상호 작용해야 하는 한 EXPORT/IMPORT TO MEMORY는 여전히 유효한 도구입니다. 다만 신규 개발에서는 객체 지향 파라미터 전달 또는 SHMA 기반 캐시를 우선 검토하고, ABAP 메모리는 "그 외의 어쩔 수 없는 경로 우회용"으로 한정해 사용하는 것이 코드 가독성과 유지보수성 측면에서 권장됩니다.

댓글 0

아직 댓글이 없습니다.