안녕하세요. 아는 형입니다.
S/4HANA 컨버전 프로젝트를 진행하다 보면 ECC에서는 멀쩡히 돌던 프로그램이 갑자기 말썽을 부리는 경우가 종종 있습니다. 이번에 다룰 이슈도 그런 케이스인데요, 펌뱅킹(Firm Banking) 급여 배치 테스트 중에 만난 SY-SUBRC 2 에러에 대한 이야기입니다.
통합테스트에 들어가기 전, 은행과의 사전 테스트 단계에서 발생한 문제라 더 신경이 쓰였습니다. 실제 이관 후가 아니라 사전 검증 단계였던 게 그나마 다행이었죠.
어떤 상황이었나
펌뱅킹 급여 테스트(배치)를 진행하던 중이었습니다. 은행으로부터 수신받은 결과 파일을 읽어와야 하는데, ECC에서는 문제없이 동작하던 펑션(Function)이 S/4HANA로 넘어오면서 해당 디렉토리의 파일을 아예 읽지 못하는 겁니다. 확인해보니 SY-SUBRC 값이 2로 떨어지고 있었습니다.
혹시 S/4HANA 전환 중에 커스텀 프로그램에서 파일 디렉토리를 못 읽어오는 증상, 보신 분 계신가요? 저도 처음엔 권한 문제인가, 경로 문제인가 하고 한참 헤맸습니다.
원인 파악: SAP Note 3273137
결국 SAP Note를 뒤져서 원인을 찾았습니다. 바로 Note 3273137 – Read directory using the function module RZL_READ_DIR_LOCAL failed였는데요, 내용을 보니 딱 저희 상황이었습니다.
커널 함수인 RZLCallFromABAP(RZL_READ_DIR_LOCAL)은 원래 커스텀 프로그램에서 애플리케이션 서버 경로의 파일 리스트를 조회하는 용도로 흔히 쓰이던 방식입니다. 그런데 S/4HANA 2021 업그레이드 이후로 이 함수가 보안상의 이유로 막혀버렸다는 게 Note의 핵심 내용이었습니다.
실제로 dev_w* 트레이스를 확인해보면 이런 에러 로그도 함께 찍힙니다.
ERROR => RZLCallFromAbap is called from *** - rejected [saprzl.c 7
해당 Note의 원인과 해결 방향을 요약하면 이렇습니다.
| 구분 | 내용 |
|---|---|
| 대상 환경 | SAP S/4HANA 2021, SAP NetWeaver 7.56 |
| 원인 | RZL_READ_DIR_LOCAL 함수 모듈이 보안상 이유로 보호 처리되어 ABAP 프로그램 개발에서 사용 불가 |
| 해결 | CL_FILE_UTIL=>READDIR 클래스 메서드로 대체 |
정리하면, 예전 방식의 커널 함수 호출 자체를 SAP이 막아버렸으니 클래스 기반 메서드로 갈아타라는 이야기입니다. 이게 진짜 웃긴 게, 로직 자체는 그대로인데 그냥 호출 방식만 막혀서 아예 동작을 안 하니 처음엔 원인 찾기가 쉽지 않았습니다.

어떻게 수정했나
기존에 CALL 'ALERTS'로 커널 함수를 직접 호출하던 부분을 주석 처리하고, CL_FILE_UTIL=>READDIR을 사용하는 방식으로 로직을 변경했습니다. 변경 이력은 남겨두고 기존 로직은 참고용으로 주석으로만 남겨뒀습니다.
FUNCTION ztst_rzl_read_dir_local.
*"----------------------------------------------------------------------
*"*"로컬 인터페이스:
*" IMPORTING
*" REFERENCE(NAME) LIKE SALFILE-LONGNAME
*" TABLES
*" FILE_TBL STRUCTURE ZFBS2039
*" EXCEPTIONS
*" ARGUMENT_ERROR
*" NOT_FOUND
*" OTHERS
*"----------------------------------------------------------------------
DATA: BEGIN OF line_tbl OCCURS 100.
INCLUDE STRUCTURE spflist.
DATA: END OF line_tbl.
DATA: loc_name(200).
DATA: loc_dir(200).
DATA: tmp_loc_dir(200).
DATA: loc_dir_len TYPE i.
DATA: full_name(400).
IF name <> space.
loc_name = name.
loc_dir = name.
SHIFT loc_dir LEFT BY 2 PLACES.
loc_dir_len = strlen( loc_dir ).
loc_dir_len = loc_dir_len - 1.
SHIFT loc_dir CIRCULAR LEFT BY loc_dir_len PLACES.
IF loc_name(2) = '$(' AND loc_dir(1) = ')'.
loc_dir(1) = space.
SHIFT loc_dir CIRCULAR RIGHT BY loc_dir_len PLACES.
CALL 'C_SAPGPARAM' ID 'NAME' FIELD loc_dir
ID 'VALUE' FIELD loc_dir.
IF sy-subrc = 0.
full_name = loc_dir.
ELSE.
full_name = name.
ENDIF.
ELSE.
full_name = name.
ENDIF.
ELSE.
full_name = name.
ENDIF.
REFRESH line_tbl.
**********************************************************************
*** 2026.07.14 MSK - Note 3273137 대응 : RZL_READ_DIR_LOCAL -> CL_FILE_UTIL=>READDIR 대체
**********************************************************************
* CALL 'ALERTS' ID 'ADMODE' FIELD ad_rzl
* ID 'OPCODE' FIELD rzl_op_rd_dir
* ID 'FILE_NAME' FIELD full_name
* ID 'DIR_TBL' FIELD line_tbl-*sys*.
*
* CASE sy-subrc.
* WHEN 0.
* LOOP AT line_tbl.
* file_tbl-size = line_tbl(11).
* file_tbl-name = line_tbl+12.
* APPEND file_tbl.
* ENDLOOP.
* WHEN OTHERS. RAISE not_found.
* ENDCASE.
**********************************************************************
DATA: loc_path TYPE string.
DATA: dir_entry_list TYPE file_dir_entry_list.
DATA: ls_data TYPE file_dir_entry.
DATA: l_all_files TYPE string VALUE '*'.
loc_path = full_name.
DATA(rc) = cl_file_util=>readdir(
EXPORTING
dirname = loc_path
filter = l_all_files
IMPORTING
dir_entry_list = dir_entry_list ).
CASE sy-subrc.
WHEN 0.
LOOP AT dir_entry_list INTO ls_data.
file_tbl-size = ls_data-f_size.
file_tbl-name = ls_data-f_name.
APPEND file_tbl.
ENDLOOP.
WHEN OTHERS. RAISE not_found.
ENDCASE.
ENDFUNCTION.
삽질 포인트: 기존
CALL 'ALERTS'방식은 파일 리스트를line_tbl이라는 문자열 구조체 오프셋으로 잘라서 받는 방식이었는데,CL_FILE_UTIL=>READDIR은file_dir_entry_list라는 구조화된 내부테이블로 결과를 돌려줍니다. 단순히 함수만 바꾸는 게 아니라 결과를 받는 구조 자체가 다르다는 걸 놓치면 컴파일은 되는데 데이터가 이상하게 들어오는 상황을 만날 수 있으니 주의가 필요합니다.

수정 후 결과
로직 변경 후 다시 배치를 돌려보니 SY-SUBRC가 정상적으로 0으로 떨어지면서 디렉토리의 배치 파일을 문제없이 읽어오는 것을 확인했습니다. 사전 테스트 단계에서 미리 잡아낸 덕분에 이후 통합테스트에서는 이 부분 때문에 발목 잡히는 일 없이 넘어갈 수 있었습니다.
S/4HANA 컨버전 프로젝트에서는 이렇게 예전부터 관행적으로 쓰던 커널 함수 호출이 조용히 막혀 있는 경우가 종종 있습니다. 에러 메시지만 봐서는 원인을 짐작하기 어려운 경우가 많아서, 이런 유형의 이슈는 일단 관련 SAP Note부터 검색해보는 습관을 들이는 게 시간을 아끼는 방법인 것 같습니다.
펌뱅킹 배치 인터페이스 쪽 이슈는 이번이 끝이 아니고, 펌뱅킹 실시간 인터페이스 설계 관련해서도 다뤄야 할 이야기가 남아있는데, 이 부분은 다음 글에서 이어가 보겠습니다.