바로투입이력카드
SAP 게시판

정보·팁

RFC 표 파라미터를 Python(pyrfc)으로 받기 ② 행 하나가 dict 하나

에이든ABAP 개발자운영자모듈 MMSD기타조회 6

①편에서는 함수 하나를 SE37과 Python으로 불러 돌아온 값을 비교했습니다. 실제 업무에서는 거래처 목록이나 구매오더 품목처럼 여러 행을 받는 일이 더 많습니다.

이번 편에서는 표(테이블) 파라미터를 돌려주는 RFC 함수를 같은 함수 그룹에 더하고, Python에서 그 표를 어떤 모양으로 받는지 봅니다. 코드 · 출력 · 화면은 모두 실제 SAP 서버(A4H)에서 2026-11-04에 돌려 본 그대로입니다.

이번 편 내용은 녹화 영상의 2분 15초부터 나옵니다. 아래 영상은 그 장면부터 재생됩니다.

Python 으로 SAP RFC 호출 예시(pyRFC) — 표 파라미터 장면부터YouTube에서 보기

이 편의 함수가 읽는 테이블

강의용 연습 테이블 ZEDU_MAT(자재)와 ZEDU_VEND(거래처)를 읽습니다. 거래처 이름 · 주소 · 전화번호는 강의용 예시 데이터입니다. 여러분 시스템에서는 조회해도 되는 다른 테이블로 바꿔 쓰세요. 이때 테이블 유형의 행 유형과 SELECT 필드를 함께 바꿉니다.

이번 글에서 배울 내용과 진행 순서

  1. 1단계 — DDIC 준비: 구조 ZLEC01_S_TYPES와 테이블 유형 ZLEC01_T_VEND를 만듭니다.

  2. 2단계 — 함수 만들기: FORM 네 개와 함수 모듈 Z_LEC01_TYPES_DEMO를 만듭니다.

  3. 3단계 — SE37에서 표 보기: ET_VEND에 거래처 3행이 돌아오는 것을 화면으로 봅니다.

  4. 4단계 — Python으로 받기: rfc_table.py로 같은 표를 받습니다. 표는 list[dict]입니다.

  5. 5단계 — 타입이 어떻게 바뀌나: 날짜 · 금액 · 숫자 · 바이트가 Python에서 무엇이 되는지 봅니다.

1단계: DDIC 준비 — 구조와 테이블 유형

  1. 구조 ZLEC01_S_TYPES: 타입별 예시 값을 하나씩 담는 구조입니다. ADT에서 New > Dictionary > Structure로 만들고 아래 정의를 붙여 넣어 활성화합니다.

- SE11로 만들 때는 같은 필드와 데이터 엘리먼트를 넣고, F_CURR의 참조 필드를 ZLEC01_S_TYPES-F_WAERS로, F_QUAN의 참조 필드를 ZLEC01_S_TYPES-F_MEINS로 지정합니다.

  1. 테이블 유형 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을 누릅니다.

그림 1. Z_LEC01_TYPES_DEMO 결과 화면. ES_MAT은 자재 한 행이 한 줄로 보이고, ET_VEND는 3 Entries처럼 행 수만 보입니다.
그림 1. Z_LEC01_TYPES_DEMO 결과 화면. ES_MAT은 자재 한 행이 한 줄로 보이고, ET_VEND는 3 Entries처럼 행 수만 보입니다.

ET_VEND 줄의 값 칸(표 아이콘)을 두 번 누르면 표가 열립니다.

그림 2. ET_VEND 표. 거래처 3행이 필드 이름과 함께 보이고, 거래처 번호는 100001처럼 앞자리 0 없이 보입니다.
그림 2. ET_VEND 표. 거래처 3행이 필드 이름과 함께 보이고, 거래처 번호는 100001처럼 앞자리 0 없이 보입니다.

확인: '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_MEINS

  • NUMC → 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.3333333333333333

  • STRING → 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_BASIS 816, 클라이언트 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

아직 댓글이 없어요.

정보·팁 목록전체 목록