SAP 리포트에서 사용자가 PC 파일을 업로드 / 다운로드하는 화면을 만들 때, 첫 호출 시에는 사용자가 매번 C 드라이브 최상위부터 폴더를 헤매고, 같은 화면을 다시 열어도 또 처음부터 경로를 찾아 들어가야 합니다. 사용자 경험이 나쁘고 업무 시간도 낭비됩니다.
표준 SAP 에서 이 문제를 해결하는 방법은 이전에 사용한 경로를 어딘가에 저장해 두었다가 다음 호출 시 initial_directory 파라미터로 넘겨주는 것 입니다. 저장 위치는 두 가지 — SPA/GPA 메모리 ID(세션 단위 임시) 와 Z 테이블(사용자별 영구 저장). 시나리오에 따라 선택해서 사용합니다.
이 글에서는 두 방법의 구현 패턴 · 장단점 · 메모리 ID 작명 규칙 · Z 테이블 구조 · 파일 다이얼로그 표준 API(file_open_dialog · file_save_dialog) 와의 연동 · 함정까지 한 번에 정리합니다.
핵심 — 두 가지 저장 방식 비교
| 항목 | 방법1 — SPA/GPA 메모리 ID | 방법2 — Z 테이블 영구 저장 |
|---|---|---|
| 저장 위치 | SAP 사용자 세션 메모리 | DB 테이블 (Z 자체) |
| 지속 시간 | 세션 동안 (로그아웃 시 초기화) | 영구 (다음 날도 유지) |
| 구현 비용 | 매우 낮음 (2줄) | 중간 (테이블 + SELECT/MODIFY) |
| 사용자 PC 변경 | 즉시 반영 (세션 단위) | 즉시 반영 (DB 단위) |
| 운영 부담 | 없음 (메모리 자동) | CTS 이송 · 사용자 데이터 백업 |
선택 기준 — 하루 동안만 유지되면 충분 하면 방법1, 다음 날 / 다음 주에도 유지되어야 하면 방법2. 두 방식을 결합한 하이브리드 (Z 테이블에서 가져온 후 메모리에도 함께 저장) 도 가능합니다.
1단계 — 방법1: SPA/GPA 메모리 ID 활용
가장 가벼운 방법. SET PARAMETER ID / GET PARAMETER ID 두 줄로 끝.
DATA: lv_path TYPE string,
lt_file TYPE filetable,
lv_rc TYPE i,
lv_action TYPE i.
" ★ 1) 메모리 ID 에서 이전 사용 경로 가져오기
GET PARAMETER ID 'ZUPLOAD_PATH' FIELD lv_path.
" 2) 파일 다이얼로그 호출 (이전 경로를 기본값으로)
cl_gui_frontend_services=>file_open_dialog(
EXPORTING
window_title = '파일 선택'
initial_directory = lv_path " ★ 핵심 — 이전 경로 전달
multiselection = abap_false
CHANGING
file_table = lt_file
rc = lv_rc
user_action = lv_action
EXCEPTIONS
OTHERS = 1 ).
CHECK lv_action = cl_gui_frontend_services=>action_ok.
" 3) 선택 결과 처리
READ TABLE lt_file INTO DATA(ls_file) INDEX 1.
DATA(lv_full_path) = CONV string( ls_file-filename ).
" ★ 4) 디렉토리 부분만 추출해서 메모리에 저장
DATA lv_dir TYPE string.
FIND REGEX '^(.*)[/\\][^/\\]+$' IN lv_full_path SUBMATCHES lv_dir.
IF sy-subrc = 0.
SET PARAMETER ID 'ZUPLOAD_PATH' FIELD lv_dir.
ENDIF.
메모리 ID 작명 — Z 접두사 + 의미 있는 이름. SU01 사용자 마스터의 Parameters 탭에 해당 메모리 ID 가 미리 등록되어 있을 필요는 없습니다 (자동 생성).
SET PARAMETER 의 길이 제한 — SPA/GPA 는 기본 132자. 일반적인 PC 경로는 충분하지만 매우 깊은 디렉토리 구조에서는 잘릴 수 있어 사전 길이 체크 권장.
2단계 — 방법2: Z 테이블 영구 저장
여러 날 / 여러 세션에 걸쳐 경로를 유지하려면 자체 테이블 활용.
Z 테이블 정의 — ZTXX_FILE_PATH (예시)
+----------+-----------+--------+---------+
| Field | DataType | Length | Key |
+----------+-----------+--------+---------+
| MANDT | CLNT | 3 | Yes |
| UNAME | SYUNAME | 12 | Yes |
| PROGRAM | SYREPID | 40 | Yes |
| TYPE | CHAR1 | 1 | Yes | ← U(Upload) / D(Download)
| PATH | STRING | - | No |
| AEDAT | DATUM | 8 | No |
+----------+-----------+--------+---------+
키 — 사용자(UNAME) + 프로그램(PROGRAM) + 유형(TYPE) 조합. 같은 사용자라도 업로드 / 다운로드 / 다른 프로그램별로 경로를 따로 유지할 수 있습니다.
조회 + 다이얼로그 + 저장 흐름:
DATA: lv_path TYPE string,
lt_file TYPE filetable,
lv_rc TYPE i.
" ★ 1) Z 테이블에서 이전 경로 조회
SELECT SINGLE path
INTO @lv_path
FROM ztxx_file_path
WHERE uname = @sy-uname
AND program = @sy-repid
AND type = 'U'. " Upload
" 2) 파일 다이얼로그 호출
cl_gui_frontend_services=>file_open_dialog(
EXPORTING
window_title = '파일 선택'
initial_directory = lv_path
CHANGING
file_table = lt_file
rc = lv_rc
user_action = DATA(lv_action) ).
CHECK lv_action = cl_gui_frontend_services=>action_ok.
" 3) 선택 결과
READ TABLE lt_file INTO DATA(ls_file) INDEX 1.
DATA(lv_full_path) = CONV string( ls_file-filename ).
DATA lv_dir TYPE string.
FIND REGEX '^(.*)[/\\][^/\\]+$' IN lv_full_path SUBMATCHES lv_dir.
" ★ 4) Z 테이블에 새 경로 영구 저장
IF sy-subrc = 0.
MODIFY ztxx_file_path FROM @( VALUE #(
mandt = sy-mandt
uname = sy-uname
program = sy-repid
type = 'U'
path = lv_dir
aedat = sy-datum ) ).
ENDIF.
3단계 — 다운로드 시 file_save_dialog
다운로드 화면도 동일한 패턴. file_save_dialog 도 initial_directory 파라미터를 받습니다.
DATA: lv_path TYPE string,
lv_filename TYPE string,
lv_action TYPE i.
GET PARAMETER ID 'ZDOWNLOAD_PATH' FIELD lv_path.
cl_gui_frontend_services=>file_save_dialog(
EXPORTING
window_title = '저장 위치 선택'
default_extension = 'xlsx'
default_file_name = 'export'
initial_directory = lv_path " ★
file_filter = 'Excel Files (*.xlsx)|*.xlsx|'
CHANGING
filename = lv_filename
path = lv_path
fullpath = DATA(lv_fullpath)
user_action = lv_action
EXCEPTIONS
OTHERS = 1 ).
CHECK lv_action = cl_gui_frontend_services=>action_ok.
" 실제 다운로드 (예: GUI_DOWNLOAD)
cl_gui_frontend_services=>gui_download(
EXPORTING
filename = lv_fullpath
filetype = 'BIN'
CHANGING
data_tab = lt_binary_data
EXCEPTIONS
OTHERS = 1 ).
" 새 경로 저장
SET PARAMETER ID 'ZDOWNLOAD_PATH' FIELD lv_path.
업로드용 메모리 ID 와 다운로드용 메모리 ID 를 분리 하는 것이 중요. 사용자가 일반적으로 업로드는 Downloads 폴더에서, 다운로드는 Documents 폴더에 저장하는 식으로 다르기 때문에 한 ID 로 두면 매번 잘못된 폴더에서 시작합니다.
4단계 — 하이브리드 패턴 (Z 테이블 + 메모리 ID)
가장 사용자 경험이 좋은 방식. 세션 안에서는 메모리 ID 로 빠르게 가져오고, 세션 시작 시점에는 Z 테이블 에서 영구 저장된 값을 로드.
DATA lv_path TYPE string.
" ★ 1) 메모리 먼저 확인
GET PARAMETER ID 'ZUPLOAD_PATH' FIELD lv_path.
" 메모리에 없으면 Z 테이블에서 가져옴
IF lv_path IS INITIAL.
SELECT SINGLE path
INTO @lv_path
FROM ztxx_file_path
WHERE uname = @sy-uname
AND program = @sy-repid
AND type = 'U'.
IF lv_path IS NOT INITIAL.
" 메모리에 캐시
SET PARAMETER ID 'ZUPLOAD_PATH' FIELD lv_path.
ENDIF.
ENDIF.
" 2) file_open_dialog 호출
" ... ( 기존 패턴 ) ...
" ★ 3) 선택 후 메모리 + Z 테이블 둘 다 저장
SET PARAMETER ID 'ZUPLOAD_PATH' FIELD lv_dir.
MODIFY ztxx_file_path FROM @( VALUE #(
mandt = sy-mandt
uname = sy-uname
program = sy-repid
type = 'U'
path = lv_dir
aedat = sy-datum ) ).
장점 — 세션 안에서는 DB 호출 없이 빠르게 동작하고, 세션 시작 / 다음 날 로그인 시점에는 영구 저장된 경로가 자동 복원됩니다.
5단계 — 디렉토리만 선택할 때 (directory_browse)
파일이 아니라 폴더만 선택할 때는 directory_browse. 다중 파일 일괄 처리 / ZIP 다운로드 등에 사용.
DATA: lv_folder TYPE string.
GET PARAMETER ID 'ZDOWNLOAD_PATH' FIELD lv_folder.
cl_gui_frontend_services=>directory_browse(
EXPORTING
window_title = '폴더 선택'
initial_folder = lv_folder " ★
CHANGING
selected_folder = lv_folder
EXCEPTIONS
OTHERS = 1 ).
CHECK sy-subrc = 0.
" 메모리 갱신
SET PARAMETER ID 'ZDOWNLOAD_PATH' FIELD lv_folder.
initial_folder 와 selected_folder 가 같은 변수를 가리키므로 사용자가 변경한 경로가 그대로 갱신됩니다.
흔히 빠뜨리는 함정
디렉토리 / 파일명 분리 실패
file_open_dialog 가 돌려주는 filename 은 풀 경로 + 파일명 형태(C:\Users\Alice\report.xlsx). 디렉토리만 저장하려면 마지막 슬래시 이전까지만 잘라야 합니다. 풀 경로를 그대로 저장하면 다음 호출 시 파일명이 폴더로 인식되어 다이얼로그가 이상하게 뜹니다.
메모리 ID 길이 132자 제한
SPA/GPA 의 길이가 132자로 잘립니다. 매우 깊은 폴더 구조에서는 잘려서 잘못된 경로가 됩니다. 운영 전 사용자 디렉토리 깊이를 확인하고, 가능성이 있다면 Z 테이블(STRING 타입) 사용.
슬래시 / 백슬래시 혼합
Windows 는 \, 일부 라이브러리는 / 를 사용합니다. 경로 매칭 / 합치기 시 둘 다 처리하는 정규식이 안전 — [/\\] 패턴 활용.
메모리 ID 충돌
같은 시스템에 다른 프로그램에서 같은 ID 를 쓰면 경로가 섞입니다. 메모리 ID 는 프로그램 단위 / 시나리오 단위로 고유 하게 — 예: ZUPLOAD_PO·ZUPLOAD_INV 같이 의미를 붙임.
Z 테이블 권한
자체 테이블에 사용자별 데이터가 쌓이므로 SE16N 의 일반 권한이 있는 사용자가 다른 사용자의 경로를 볼 수 있습니다. 권한 그룹(DELIVERY · DICBERCLS) 을 적용해서 노출 범위 제한.
사용자 PC 마이그레이션
사용자가 PC 를 교체했는데 Z 테이블에 옛 PC 의 경로가 남아 있으면, 새 PC 에서 그 경로가 없어 다이얼로그가 빈 상태로 뜨거나 에러가 발생할 수 있습니다. cl_gui_frontend_services=>directory_exist 로 존재 여부 사전 체크 후 없으면 기본값으로 fallback.
Linux / Mac SAP GUI 사용자
드물지만 Mac / Linux 의 SAP GUI 사용자는 경로 형식이 다릅니다(/Users/Alice/). 멀티 플랫폼 회사라면 OS 분기 필요 — cl_gui_frontend_services=>get_platform( ).
첫 호출 시 빈 경로
첫 호출에서는 메모리 / Z 테이블 모두 빈 값. initial_directory = '' 를 그대로 넘기면 다이얼로그가 기본 폴더로 뜨는데, 회사 정책상 특정 기본 폴더(C:\Users\<사용자>\Downloads) 로 유도하려면 첫 호출 시 fallback 처리.
전체 코드 — 복사용 통합본 (하이브리드 패턴)
*&---------------------------------------------------------------------*
*& Report Z_XX_FILE_PATH_MEMORY
*&---------------------------------------------------------------------*
*& 업/다운로드 경로 자동 저장 + 재사용 (하이브리드)
*&---------------------------------------------------------------------*
REPORT z_xx_file_path_memory.
PARAMETERS: p_type TYPE c LENGTH 1 DEFAULT 'U'. " U=Upload · D=Download
START-OF-SELECTION.
IF p_type = 'U'.
PERFORM upload_file.
ELSE.
PERFORM download_file.
ENDIF.
*---------------------------------------------------------------------*
* Form upload_file
*---------------------------------------------------------------------*
FORM upload_file.
DATA: lv_path TYPE string,
lv_dir TYPE string,
lt_file TYPE filetable,
lv_rc TYPE i,
lv_action TYPE i.
" ★ 1) 메모리 → Z 테이블 순으로 이전 경로 조회
GET PARAMETER ID 'ZUPLOAD_PATH' FIELD lv_path.
IF lv_path IS INITIAL.
SELECT SINGLE path
INTO @lv_path
FROM ztxx_file_path
WHERE uname = @sy-uname
AND program = @sy-repid
AND type = 'U'.
IF lv_path IS NOT INITIAL.
SET PARAMETER ID 'ZUPLOAD_PATH' FIELD lv_path.
ENDIF.
ENDIF.
" 2) 다이얼로그 호출
cl_gui_frontend_services=>file_open_dialog(
EXPORTING
window_title = '업로드 파일 선택'
initial_directory = lv_path
multiselection = abap_false
CHANGING
file_table = lt_file
rc = lv_rc
user_action = lv_action
EXCEPTIONS
OTHERS = 1 ).
CHECK lv_action = cl_gui_frontend_services=>action_ok.
" 3) 디렉토리 부분만 추출
READ TABLE lt_file INTO DATA(ls_file) INDEX 1.
FIND REGEX '^(.*)[/\\][^/\\]+$' IN ls_file-filename SUBMATCHES lv_dir.
CHECK sy-subrc = 0.
" ★ 4) 메모리 + Z 테이블 둘 다 저장
SET PARAMETER ID 'ZUPLOAD_PATH' FIELD lv_dir.
MODIFY ztxx_file_path FROM @( VALUE #(
mandt = sy-mandt
uname = sy-uname
program = sy-repid
type = 'U'
path = lv_dir
aedat = sy-datum ) ).
" 5) 실제 업로드 처리
WRITE: / '선택된 파일:', ls_file-filename.
WRITE: / '저장된 경로:', lv_dir.
ENDFORM.
*---------------------------------------------------------------------*
* Form download_file
*---------------------------------------------------------------------*
FORM download_file.
DATA: lv_path TYPE string,
lv_filename TYPE string,
lv_fullpath TYPE string,
lv_action TYPE i.
" ★ 1) 다운로드용 메모리 / Z 테이블 조회
GET PARAMETER ID 'ZDOWNLOAD_PATH' FIELD lv_path.
IF lv_path IS INITIAL.
SELECT SINGLE path
INTO @lv_path
FROM ztxx_file_path
WHERE uname = @sy-uname
AND program = @sy-repid
AND type = 'D'.
IF lv_path IS NOT INITIAL.
SET PARAMETER ID 'ZDOWNLOAD_PATH' FIELD lv_path.
ENDIF.
ENDIF.
" 2) 저장 다이얼로그
cl_gui_frontend_services=>file_save_dialog(
EXPORTING
window_title = '저장 위치 선택'
default_extension = 'xlsx'
default_file_name = 'report'
initial_directory = lv_path
file_filter = 'Excel Files (*.xlsx)|*.xlsx|'
CHANGING
filename = lv_filename
path = lv_path
fullpath = lv_fullpath
user_action = lv_action
EXCEPTIONS
OTHERS = 1 ).
CHECK lv_action = cl_gui_frontend_services=>action_ok.
" ★ 3) 새 경로 저장
SET PARAMETER ID 'ZDOWNLOAD_PATH' FIELD lv_path.
MODIFY ztxx_file_path FROM @( VALUE #(
mandt = sy-mandt
uname = sy-uname
program = sy-repid
type = 'D'
path = lv_path
aedat = sy-datum ) ).
" 4) 실제 다운로드 처리 (예: GUI_DOWNLOAD)
WRITE: / '저장 경로:', lv_fullpath.
ENDFORM.
같이 보면 좋은 글:
- "CL_GOS_MANAGER 첨부파일 활용 — 자체 화면에 표준 GOS 서비스 끼우기" — 표준 첨부 메뉴와 함께 사용
- "SWU_OBJECT_PUBLISH ALV GOS 아이콘 만드는 방법" — ALV 리포트의 첨부 흐름과 연동
- "BAPI · BDC 에러 메시지 확인 방법 — MESSAGE INTO · FORMAT_MESSAGE 빌드" — 업로드 실패 / 권한 부족 시 메시지 처리
요약
| 단계 | 하는 일 | 핵심 포인트 |
|---|---|---|
| 1 | 이전 경로 조회 | GET PARAMETER ID / SELECT ZT* |
| 2 | 다이얼로그 호출 | initial_directory 파라미터에 전달 |
| 3 | 디렉토리 추출 | 정규식 ^(.*)[/\\\\][^/\\\\]+$ |
| 4 | 경로 저장 | SET PARAMETER ID + MODIFY ZT* |
| 5 | 업/다운 분리 | 메모리 ID + Z 테이블 키 별도 운영 |
업/다운로드 경로 자동 저장은 SPA/GPA 메모리 ID 와 Z 테이블 두 도구의 조합 으로 깔끔하게 처리됩니다. 세션 안에서는 메모리로 빠르게, 다음 로그인 / 다음 날에는 Z 테이블에서 영구 복원. 업로드용 / 다운로드용 메모리 ID 와 테이블 키를 분리하고, 첫 호출 시 빈 경로 fallback, 정규식으로 디렉토리만 깔끔하게 추출하는 패턴을 표준으로 잡아두면 사용자 경험과 운영 안정성을 동시에 잡을 수 있습니다.
Disclaimer — 이 포스트는 실무 정리 노트를 바탕으로 AI 보조로 정리되었습니다.
cl_gui_frontend_services 메소드 시그니처 · SET / GET PARAMETER ID 동작은 SAP NetWeaver 표준(ECC 6.0 / S/4HANA on-premise) 기준이며, 사내 보안 / 클라이언트 OS · 권한 그룹 정책에 따라 일부 동작이 다를 수 있으니 운영 시스템 적용 전 개발·QA 환경에서 검증하시기 바랍니다. Z 테이블 설계 시 사용자별 권한 / GDPR · 개인정보 정책에 따른 보관 기간도 함께 검토하시기 바랍니다.