RFC 표 파라미터를 Python(pyrfc)으로 받기 ② 행 하나가 dict 하나
①편에서는 함수 하나를 SE37과 Python으로 불러 돌아온 값을 비교했습니다. 실제 업무에서는 거래처 목록이나 구매오더 품목처럼 여러 행을 받는 일이 더 많습니다.
이번 편에서는 표(테이블) 파라미터를 돌려주는 RFC 함수를 같은 함수 그룹에 더하고, Python에서 그 표를 어떤 모양으로 받는지 봅니다. 코드 · 출력 · 화면은 모두 실제 SAP 서버(A4H)에서 2026-11-04에 돌려 본 그대로입니다.
이번 편 내용은 녹화 영상의 2분 15초부터 나옵니다. 아래 영상은 그 장면부터 재생됩니다.
이 편의 함수가 읽는 테이블
강의용 연습 테이블
ZEDU_MAT(자재)와ZEDU_VEND(거래처)를 읽습니다. 거래처 이름 · 주소 · 전화번호는 강의용 예시 데이터입니다. 여러분 시스템에서는 조회해도 되는 다른 테이블로 바꿔 쓰세요. 이때 테이블 유형의 행 유형과 SELECT 필드를 함께 바꿉니다.
이번 글에서 배울 내용과 진행 순서
1단계 — DDIC 준비: 구조
ZLEC01_S_TYPES와 테이블 유형ZLEC01_T_VEND를 만듭니다.2단계 — 함수 만들기: FORM 네 개와 함수 모듈
Z_LEC01_TYPES_DEMO를 만듭니다.3단계 — SE37에서 표 보기:
ET_VEND에 거래처 3행이 돌아오는 것을 화면으로 봅니다.4단계 — Python으로 받기:
rfc_table.py로 같은 표를 받습니다. 표는list[dict]입니다.5단계 — 타입이 어떻게 바뀌나: 날짜 · 금액 · 숫자 · 바이트가 Python에서 무엇이 되는지 봅니다.
1단계: DDIC 준비 — 구조와 테이블 유형
구조
ZLEC01_S_TYPES: 타입별 예시 값을 하나씩 담는 구조입니다. ADT에서 New > Dictionary > Structure로 만들고 아래 정의를 붙여 넣어 활성화합니다.
- SE11로 만들 때는 같은 필드와 데이터 엘리먼트를 넣고, F_CURR의 참조 필드를 ZLEC01_S_TYPES-F_WAERS로, F_QUAN의 참조 필드를 ZLEC01_S_TYPES-F_MEINS로 지정합니다.
테이블 유형
ZLEC01_T_VEND: SE11에서 데이터 유형 → 테이블 유형으로 만듭니다. 행 유형ZEDU_VEND, 접근Standard Table, 키Standard key로 두고 활성화합니다. 이것이 "거래처 여러 행"의 모양입니다.
@EndUserText.label : 'Lecture 01: RFC type mapping demo'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
define structure zlec01_s_types {
f_char : maktx;
f_numc : ebelp;
f_dats : datum;
f_tims : uzeit;
@Semantics.amount.currencyCode : 'zlec01_s_types.f_waers'
f_curr : stprs;
f_waers : waers;
@Semantics.quantity.unitOfMeasure : 'zlec01_s_types.f_meins'
f_quan : menge_d;
f_meins : meins;
f_int4 : int4;
f_fltp : float;
f_string : stringval;
f_xstring : xstringval;
f_tstmp : timestamp;
}확인: 구조와 테이블 유형이 모두 활성화되면 다음으로 넘어갑니다.
2단계: 함수 만들기
LZLEC01F01에 이어 붙일 FORM 네 개
①편에서 만든 LZLEC01F01의 맨 아래에 그대로 이어 붙입니다.
check_authority: 테이블 이름 단위로 조회 권한(S_TABU_NAM)을 검사합니다.get_material: 자재 한 건을 읽습니다.get_vendors: 거래처 번호 순으로 앞에서 N건을 읽습니다.set_type_samples: 타입별 예시 값을 채웁니다.
*&---------------------------------------------------------------------*
*& Form check_authority
*&---------------------------------------------------------------------*
FORM check_authority USING pv_table TYPE tabname
CHANGING pv_error TYPE abap_bool.
CLEAR pv_error.
" 테이블 이름 단위 조회 권한
AUTHORITY-CHECK OBJECT 'S_TABU_NAM'
ID 'ACTVT' FIELD gc_actvt_display
ID 'TABLE' FIELD pv_table.
IF sy-subrc <> 0.
pv_error = abap_true.
ENDIF.
ENDFORM.
*&---------------------------------------------------------------------*
*& Form get_material
*&---------------------------------------------------------------------*
FORM get_material USING pv_matnr TYPE zedu_matnr
CHANGING ps_mat TYPE zedu_mat
pv_found TYPE abap_bool.
CLEAR: ps_mat, pv_found.
SELECT SINGLE mandt, matnr, maktx, matkl, meins,
stprs, waers, erdat, ernam
FROM zedu_mat
WHERE matnr = @pv_matnr
INTO CORRESPONDING FIELDS OF @ps_mat.
IF sy-subrc = 0.
pv_found = abap_true.
ENDIF.
ENDFORM.
*&---------------------------------------------------------------------*
*& Form get_vendors
*&---------------------------------------------------------------------*
FORM get_vendors USING pv_max TYPE int4
CHANGING pt_vend TYPE zlec01_t_vend.
CLEAR pt_vend.
" 거래처 번호 순으로 앞에서 pv_max 건
SELECT mandt, lifnr, name1, land1, ort01,
stras, telf1, erdat, ernam
FROM zedu_vend
ORDER BY lifnr
INTO CORRESPONDING FIELDS OF TABLE @pt_vend
UP TO @pv_max ROWS.
ENDFORM.
*&---------------------------------------------------------------------*
*& Form set_type_samples
*&---------------------------------------------------------------------*
FORM set_type_samples USING ps_mat TYPE zedu_mat
pt_vend TYPE zlec01_t_vend
CHANGING ps_types TYPE zlec01_s_types.
CLEAR ps_types.
" 자재 마스터에서 가져온 값
ps_types-f_char = ps_mat-maktx.
ps_types-f_dats = ps_mat-erdat.
ps_types-f_curr = ps_mat-stprs.
ps_types-f_waers = ps_mat-waers.
ps_types-f_meins = ps_mat-meins.
" 타입 확인용 고정 예시값
ps_types-f_numc = '00010'.
ps_types-f_tims = '093000'.
ps_types-f_quan = '12.500'.
ps_types-f_tstmp = '20261015093000'.
ps_types-f_int4 = lines( pt_vend ).
ps_types-f_fltp = CONV f( 1 ) / 3.
ps_types-f_string = |{ ps_mat-matnr } / { ps_mat-maktx }|.
" 자재번호를 UTF-8 바이트로
TRY.
ps_types-f_xstring = cl_abap_codepage=>convert_to(
source = CONV string( ps_mat-matnr )
codepage = `UTF-8` ).
CATCH cx_parameter_invalid_range
cx_sy_codepage_converter_init
cx_sy_conversion_codepage
cx_parameter_invalid_type.
" 변환 실패는 빈 바이트로 두고 나머지 값은 돌려준다
CLEAR ps_types-f_xstring.
ENDTRY.
ENDFORM.Z_LEC01_TYPES_DEMO — 자재 한 행과 거래처 여러 행을 돌려주는 함수
①편과 같이 Remote-Enabled Module, 모든 파라미터 Pass Value로 만듭니다.
IV_MATNR(자재 번호)와IV_MAX_VEND(거래처 건수, 0 이하면 3 · 100을 넘으면 100)를 받아ES_TYPES(타입 예시) ·ES_MAT(자재 한 행) ·ET_VEND(거래처 여러 행)를 돌려줍니다.조회 권한이 없으면
NO_AUTHORITY(메시지 002), 자재가 없으면MATERIAL_NOT_FOUND(메시지 003) 예외를 냅니다.
*&---------------------------------------------------------------------*
*& 객체 종류 : 함수 모듈 Z_LEC01_TYPES_DEMO
*& 함수 그룹 : ZLEC01 (패키지 $ZLEC)
*& 만드는 곳 : SE37 (또는 ADT New > ABAP Function Module)
*& 속성 : Processing Type = Remote-Enabled Module, 모든 파라미터 Pass Value
*& 내용 : DDIC 타입별 값(ES_TYPES), 자재 한 행(ES_MAT), 거래처 몇 행(ET_VEND)을
*& 돌려준다. Python 에서 RFC 타입이 무엇으로 바뀌는지 눈으로 보는 함수.
*& 먼저 만들 것: 구조 ZLEC01_S_TYPES (zlec01_s_types.ddl)
*& 테이블 유형 ZLEC01_T_VEND (SE11, 행 유형 ZEDU_VEND, Standard table,
*& 기본 키 Standard key) - ADT DDL 로는 만들 수 없어 SE11 에서 만든다
*& 권한 : S_TABU_NAM ACTVT 03, TABLE ZEDU_MAT / ZEDU_VEND
*& 예외 : NO_AUTHORITY - 조회 권한 없음 (메시지 ZLEC01 002)
*& MATERIAL_NOT_FOUND - IV_MATNR 자재 없음 (메시지 ZLEC01 003)
*& 붙여 넣기 : ADT 에서는 아래 FUNCTION 줄부터 그대로. SE37 에서는 파라미터를
*& 탭에 등록한 뒤 본문(CLEAR 부터)만 붙여 넣는다.
*&---------------------------------------------------------------------*
*& Change History
*& 2026.11.03 Aiden Kim (local $ZLEC) 최초 작성 - 1강 실습용
*&---------------------------------------------------------------------*
FUNCTION z_lec01_types_demo
IMPORTING
VALUE(iv_matnr) TYPE zedu_matnr DEFAULT 'RM-1001'
VALUE(iv_max_vend) TYPE int4 DEFAULT 3
EXPORTING
VALUE(es_types) TYPE zlec01_s_types
VALUE(es_mat) TYPE zedu_mat
VALUE(et_vend) TYPE zlec01_t_vend
EXCEPTIONS
no_authority
material_not_found.
DATA: lv_error TYPE abap_bool,
lv_found TYPE abap_bool,
lv_max TYPE int4.
CLEAR: es_types, es_mat, et_vend.
" 자재·거래처 테이블 조회 권한
PERFORM check_authority USING gc_tab_mat
CHANGING lv_error.
IF lv_error = abap_true.
MESSAGE e002(zlec01) WITH gc_tab_mat RAISING no_authority.
ENDIF.
PERFORM check_authority USING gc_tab_vend
CHANGING lv_error.
IF lv_error = abap_true.
MESSAGE e002(zlec01) WITH gc_tab_vend RAISING no_authority.
ENDIF.
" RFC 는 대문자 변환을 하지 않으므로 받은 값 그대로 찾는다
PERFORM get_material USING iv_matnr
CHANGING es_mat
lv_found.
IF lv_found = abap_false.
MESSAGE e003(zlec01) WITH iv_matnr RAISING material_not_found.
ENDIF.
" 반환 건수 1 ~ 100
lv_max = iv_max_vend.
IF lv_max <= 0.
lv_max = gc_vend_default.
ELSEIF lv_max > gc_vend_max.
lv_max = gc_vend_max.
ENDIF.
PERFORM get_vendors USING lv_max
CHANGING et_vend.
PERFORM set_type_samples USING es_mat
et_vend
CHANGING es_types.
ENDFUNCTION.확인: 함수 그룹을 활성화했을 때 오류가 없으면 다음 단계로 넘어갑니다.
3단계: SE37에서 표 보기
SE37에서 Z_LEC01_TYPES_DEMO를 열고 F8을 누르면 기본값(RM-1001, 3)이 들어 있습니다. 그대로 다시 F8을 누릅니다.
ET_VEND 줄의 값 칸(표 아이콘)을 두 번 누르면 표가 열립니다.
확인: '3 Entries'와 거래처 3행이 보이면 SAP 쪽 준비는 끝입니다.
4단계: Python으로 받기 — rfc_table.py
①편과 접속 부분은 같고, 호출과 결과만 다릅니다.
import os
from pyrfc import Connection
conn = Connection(ashost="vhcala4hci", sysnr="00", client="001",
user="DEVELOPER", lang="EN", passwd=os.environ["SAP_PASSWORD"])
# ⑤ 표 파라미터는 list — 행 하나가 dict 하나
result = conn.call("Z_LEC01_TYPES_DEMO", IV_MATNR="RM-1001", IV_MAX_VEND=3)
vendors = result["ET_VEND"]
print(len(vendors), "rows")
for row in vendors:
print(row["LIFNR"], row["NAME1"], row["ORT01"])
conn.close()⑤ 표 = list:
result["ET_VEND"]는 list(줄 세운 묶음)이고, 행 하나가 dict 하나입니다. dict의 키는 필드 이름(대문자)입니다.len(vendors)가 SE37의 '3 Entries'이고,for row in vendors:가 표를 한 줄씩 읽는 것입니다.한 번의 호출로
ES_TYPES·ES_MAT·ET_VEND가 함께 옵니다.result.keys()는['ES_MAT', 'ES_TYPES', 'ET_VEND']였습니다.
실제로 나온 결과입니다.
3 rows
0000100001 한빛금속(주) 인천
0000100002 대성포장산업 화성
0000100003 세진정밀 창원SE37(그림 2)과 비교하면 이름 · 도시는 같고 거래처 번호만 다릅니다. SE37은 100001, pyrfc는 0000100001입니다.
SAP 안에는 10자리
0000100001로 저장되어 있고, SE37(SAP GUI)은 변환 루틴(ALPHA)으로 앞자리 0을 떼서 보여 줍니다. RFC는 이런 화면용 변환을 하지 않고 저장된 값을 그대로 줍니다.거꾸로 보낼 때도 내부 값으로 보내야 합니다. 거래처
100001을 조건으로 넘기려면"0000100001"로 보냅니다.대문자 변환도 해 주지 않습니다. 소문자로
IV_MATNR="rm-1001"을 보냈더니MATERIAL_NOT_FOUND(메시지ZLEC01 003, 변수rm-1001)가 났습니다. SE37 테스트 화면은 'Case-sensitive'를 체크하지 않으면 입력을 대문자로 바꿔서 보냅니다.
확인: 3 rows와 거래처 세 줄이 보이면 표를 받은 것입니다.
5단계: 타입이 어떻게 바뀌나 — ES_TYPES
ES_TYPES는 DDIC 타입마다 예시 값을 하나씩 담은 구조입니다. 4단계 코드의 conn.call 아래에 두 줄을 더하면 Python에서 받은 타입을 볼 수 있습니다.
for name, value in result["ES_TYPES"].items():
print(f"{name:<10} {type(value).__name__:<8} {value!r}")실제로 나온 결과입니다.
F_CHAR str '알루미늄 판재 2T'
F_NUMC str '00010'
F_DATS str '20261030'
F_TIMS str '093000'
F_CURR Decimal Decimal('48.00')
F_WAERS str 'KRW'
F_QUAN Decimal Decimal('12.500')
F_MEINS str 'KG'
F_INT4 int 3
F_FLTP float 0.3333333333333333
F_STRING str 'RM-1001 / 알루미늄 판재 2T'
F_XSTRING bytes b'RM-1001'
F_TSTMP Decimal Decimal('20261015093000')CHAR · CUKY · UNIT →
str:F_CHAR,F_WAERS,F_MEINSNUMC →
str:'00010'. 앞자리 0이 남습니다.int()로 바꾸면 사라지니 번호는 글자로 다룹니다.DATS · TIMS →
str:'20261030','093000'. ①편의EV_DATE와 같은 내부 형식입니다.CURR · QUAN →
Decimal:Decimal('48.00'),Decimal('12.500'). 금액을float로 바꾸지 않습니다.INT4 →
int, FLTP →float:3,0.3333333333333333STRING →
str, XSTRING →bytes:b'RM-1001'TIMESTAMP(DEC 15) →
Decimal:Decimal('20261015093000')구조 →
dict, 테이블 →list[dict]
보낼 때도 같은 타입으로 넘깁니다. INT4인 IV_MAX_VEND에 문자열 "3"을 넘기면 SAP에 가기 전에 TypeError(an integer required, received '3')가 납니다.
핵심 정리
표 파라미터 =
list, 표의 한 행 =dict, 열 이름 = dict의 키SE37의 'N Entries' =
len(...)번호의 앞자리 0, 소문자 · 대문자는 RFC가 바꿔 주지 않습니다. 받을 때도 보낼 때도 내부 값입니다.
금액 · 수량은
Decimal, 날짜 · 시각 · NUMC는str로 옵니다.
주의할 점
RFC 함수는 화면을 거치지 않고 데이터를 바로 돌려줍니다. 그래서 함수 안에서
AUTHORITY-CHECK로 권한을 직접 검사합니다(이 함수는S_TABU_NAM).S_RFC는 "함수를 부를 수 있는가"만 봅니다.돌려줄 건수에는 한도를 둡니다(이 함수는
IV_MAX_VEND최대 100). 큰 표를 한 번에 보내면 SAP 메모리와 네트워크 부담이 커집니다.ES_TYPES의F_CURR같은 금액 필드는 통화(F_WAERS)를 참조 필드로 지정해야 활성화됩니다. 구조를 SE11로 만들 때 빠뜨리기 쉽습니다.pyrfc 유지보수 종료와 NW RFC SDK 라이선스는 ①편 '주의할 점'과 같습니다.
출처와 이용 안내
「SAP + WEB 연동 강의」 1강 실습을 위해 직접 작성한 코드입니다.
2026-11-04 ABAP Cloud Developer Trial 2025(A4H,
SAP_BASIS816, 클라이언트001)에서 SE37 테스트와 Python 실행(Windows 11, Python 3.12.4, pyrfc 3.3.1, NW RFC SDK 7.50)으로 확인했습니다. 본문의 화면과 출력은 그때 받은 그대로입니다.자재 · 거래처 값은 강의용 예시 데이터입니다.
회사마다 SAP 버전 · 이름 규칙 · 권한 정책이 다르므로, 실무에 쓸 때는 소속 프로젝트의 개발 표준을 먼저 확인해 주세요.
SAP와 SAP 제품명은 SAP SE의 상표이며, 바로투입은 SAP와 관련이 없습니다.
댓글 0
아직 댓글이 없어요.
로그인하고 댓글 쓰기