SAP 운영에서 사용자가 보내는 Excel 파일을 받아 CBO 테이블에 한 번에 올리거나, 표준 트랜잭션에 일괄 등록하는 작업은 정말 흔합니다. 가장 검증된 표준 패턴이 SAP 가 함수로 제공하는 ALSM_EXCEL_TO_INTERNAL_TABLE — Excel 파일을 행/열 좌표가 들어간 ALSMEX_TABLINE 구조의 내부 테이블로 변환해줍니다.
핵심 흐름: 파일 선택 다이얼로그 → Excel 읽기 → row/col 좌표로 들어온 데이터를 구조체에 동적 매핑 → 검증 → DB 저장. 추가로 SMW0(웹 오브젝트) 에 양식 파일을 등록해두고 DOWNLOAD_WEB_OBJECT 로 다운로드 버튼까지 붙이면, 사용자가 "양식 받기 → 채우기 → 업로드" 한 사이클을 화면 안에서 끝낼 수 있습니다.
이 글은 ALSM_EXCEL_TO_INTERNAL_TABLE 의 파라미터 + F4_FILENAME / cl_gui_frontend_services=>file_open_dialog 파일 선택 + SMW0 양식 다운로드 + ASSIGN COMPONENT 동적 매핑 + AT END OF row 검증 까지 한 번에 정리한 메모입니다.
핵심 원리
ABAP 에서 Excel 파일을 읽는 방법은 여러 가지지만, 각자 특성이 다릅니다.
| 방법 | 특징 | 언제 쓰나 |
|---|---|---|
ALSM_EXCEL_TO_INTERNAL_TABLE |
row/col 좌표 기반 — ALSMEX_TABLINE 구조로 반환 | 가장 안정적 — 검증된 SAP 표준 |
cl_fdt_xl_spreadsheet |
xlsx 직접 파싱 — 시트별 ITAB 반환 | 최신 xlsx 파일 + 시트별 처리 |
cl_gui_frontend_services=>gui_upload |
텍스트/CSV 단순 업로드 | 탭 구분자 텍스트로 변환된 파일 |
OLE 자동화 (Excel.Application) |
실제 Excel 프로세스 띄워 셀 단위 접근 | 서식·매크로까지 필요한 특수 케이스 |
핵심 트레이드오프: 단순 업로드 는 ALSM_EXCEL_TO_INTERNAL_TABLE 이 표준. 단일 시트·정해진 양식·xls/xlsx 둘 다 지원이라 회사 업무 80% 를 커버. 여러 시트 처리 가 필요하면 cl_fdt_xl_spreadsheet 로 넘어가는 게 안전합니다.
1단계 — 필요한 DATA 선언
ALSM 함수의 반환 테이블은 고정 구조(ALSMEX_TABLINE) 라, 별도로 사용자 데이터를 담을 work area 를 같이 선언합니다.
*EXCEL UPLOAD
DATA: gt_intern LIKE TABLE OF alsmex_tabline, " 함수가 채워주는 테이블
gs_intern TYPE alsmex_tabline, " row/col/value 구조
gv_type TYPE c LENGTH 1,
gv_nrow TYPE i.
FIELD-SYMBOLS <fs> TYPE any.
" SMW0 에 저장해놓은 EXCEL 다운로드 양식 순서에 맞춰 필드를 지정해야 한다
DATA: BEGIN OF gs_excel,
matnr TYPE string, " 자재코드
menge TYPE string, " PO수량
meins TYPE string, " 구매오더단위
netpr TYPE string, " 단가
waers TYPE string, " 통화
eindt TYPE string, " 납품일
f_len TYPE string, " 길이
f_qty TYPE string, " 매수
spec_cd TYPE string, " 규격약호
dim_cd TYPE string, " 치수코드
prd_len TYPE string, " 제품길이
maker TYPE string, " 상품메이커
END OF gs_excel,
gt_excel LIKE TABLE OF gs_excel.
핵심 포인트:
alsmex_tabline의 컬럼:row·col·value— 셀 좌표와 값gs_excel의 필드 순서가 Excel 컬럼 순서와 1:1 매칭 되어야 함- 모두
string으로 받고, 매핑 후 적절한 SAP 타입으로 변환
2단계 — 파일 선택 다이얼로그
방법 A — 레거시 함수 F4_FILENAME (PARAMETERS 의 F4 기본):
PARAMETERS p_file TYPE rlgrap-filename MODIF ID m1.
AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_file.
CALL FUNCTION 'F4_FILENAME'
EXPORTING
program_name = syst-cprog
dynpro_number = syst-dynnr
field_name = 'FILENAME'
IMPORTING
file_name = p_file.
방법 B — 클래스 cl_gui_frontend_services=>file_open_dialog (현대적):
DATA: l_file_table TYPE filetable,
l_rc TYPE i,
file_name TYPE string.
cl_gui_frontend_services=>file_open_dialog(
EXPORTING
window_title = 'File Open'
default_filename = space
file_filter = '*.xls;*.xlsx'
initial_directory = 'C:\'
multiselection = space
CHANGING
file_table = l_file_table
rc = l_rc ).
READ TABLE l_file_table INTO file_name INDEX 1.
p_file = file_name.
선택 기준은 단순합니다 — F4 다이얼로그 가 필요하면 함수, 버튼 클릭으로 직접 호출 하면 클래스.
3단계 — SMW0 양식 다운로드 (DOWNLOAD_WEB_OBJECT)
사용자가 채워야 할 표준 양식 Excel 을 미리 SMW0(웹 오브젝트) 에 등록해두고, 프로그램 안에서 다운로드 버튼을 제공하는 패턴.
DATA: lv_fname TYPE rlgrap-filename,
ls_wwwdatatab LIKE wwwdatatab.
CLEAR: lv_fname, ls_wwwdatatab.
" 저장 경로 받기
CALL FUNCTION 'F4_FILENAME'
EXPORTING
program_name = syst-cprog
dynpro_number = syst-dynnr
field_name = 'FILENAME'
IMPORTING
file_name = lv_fname.
IF lv_fname IS INITIAL.
MESSAGE '저장 경로를 선택해주세요' TYPE 'E'.
ENDIF.
lv_fname = lv_fname && '.xlsx'.
" SMW0 에 등록된 양식 메타정보 조회
SELECT SINGLE * INTO CORRESPONDING FIELDS OF ls_wwwdatatab
FROM wwwdata
WHERE objid = 'ZTPL_EXCEL_UPLOAD'. " ★ SMW0 에서 등록한 객체 ID
IF sy-subrc <> 0.
EXIT.
ENDIF.
" 다운로드
CALL FUNCTION 'DOWNLOAD_WEB_OBJECT'
EXPORTING
key = ls_wwwdatatab
destination = lv_fname.
IF sy-subrc EQ 0.
MESSAGE 'excel 다운로드 완료!!' TYPE 'S'.
ELSE.
MESSAGE 'excel 다운로드 실패!!' TYPE 'E'.
ENDIF.
핵심 포인트:
- SMW0 에서 미리 양식 Excel 파일을 MIME 객체로 등록 + 객체 ID(예:
ZTPL_EXCEL_UPLOAD) 부여 wwwdata테이블에서 메타 조회,DOWNLOAD_WEB_OBJECT가 실제 다운로드 수행- 사용자 PC 의 임의 경로에 저장 — 다국어 환경에서 한글 경로도 OK
4단계 — ALSM_EXCEL_TO_INTERNAL_TABLE 호출
핵심 함수 호출. 셀 좌표 범위만 넘기면 됩니다.
DESCRIBE FIELD gs_excel TYPE gv_type COMPONENTS gv_nrow.
" gv_nrow = gs_excel 구조의 컬럼 개수 → ALSM 의 end_col 로 사용
CALL FUNCTION 'ALSM_EXCEL_TO_INTERNAL_TABLE'
EXPORTING
filename = p_file
i_begin_col = 1 " 시작 컬럼
i_begin_row = 2 " 헤더 제외, 2행부터
i_end_col = gv_nrow " 마지막 컬럼 (구조체 컬럼 수)
i_end_row = 6500 " 최대 행 (실제 데이터보다 크게)
TABLES
intern = gt_intern
EXCEPTIONS
inconsistent_parameters = 1
upload_ole = 2
OTHERS = 3.
IF sy-subrc <> 0.
MESSAGE '데이터 불러오는 중 오류 발생' TYPE 'E'.
ENDIF.
파라미터 정리.
| 파라미터 | 의미 |
|---|---|
filename |
Excel 파일 풀 경로 (xls / xlsx 둘 다 가능) |
i_begin_col / i_begin_row |
읽기 시작 좌표 (보통 1행은 헤더라 row=2 부터) |
i_end_col / i_end_row |
읽기 종료 좌표 (실제보다 넉넉히 줘도 됨 — 빈 셀은 안 들어옴) |
intern (TABLES) |
결과 — row/col/value 의 ALSMEX_TABLINE 형태 |
5단계 — 동적 컬럼 매핑 (ASSIGN COMPONENT)
gt_intern 은 셀 단위(row/col/value) 라 행 단위 데이터로 만들어야 합니다. ASSIGN COMPONENT col OF STRUCTURE gs_excel 로 col 번호에 해당하는 구조체 필드에 값을 꽂아줍니다.
DATA gs_data TYPE zsexample_upload. " 최종 DB 저장용 구조
LOOP AT gt_intern INTO gs_intern.
* col 번호에 해당하는 gs_excel 필드로 동적 할당
ASSIGN COMPONENT gs_intern-col OF STRUCTURE gs_excel TO <fs>.
<fs> = gs_intern-value.
* 한 행 끝(다음 row 로 넘어가기 직전) 처리
AT END OF row.
* 천단위 콤마 → 마침표
SEARCH gs_excel-netpr FOR ','.
REPLACE ',' WITH '.' INTO gs_excel-netpr.
* selection-screen 의 공통 입력값으로 필드 채우기
MOVE-CORRESPONDING gs_excel TO gs_data.
gs_data-werks = p_werks.
gs_data-werks_nm = gv_werks.
gs_data-bsart = p_bsart.
gs_data-batxt = gv_bsart.
gs_data-lifnr = p_lifnr.
gs_data-lifnr_nm = gv_lifnr.
gs_data-zterm = p_zterm.
gs_data-zterm_nm = gv_zterm.
gs_data-inco1 = p_inco1.
gs_data-inco1_nm = gv_inco1.
APPEND gs_data TO gt_data.
CLEAR: gs_excel, gs_data.
ENDAT.
ENDLOOP.
핵심 포인트:
ASSIGN COMPONENT col OF STRUCTURE— col 번호를 인덱스로 구조체의 N번째 필드 참조 (필드명 안 써도 됨 → 양식이 바뀌어도 유연)AT END OF row— Excel 한 행의 마지막 셀을 읽은 직후 호출 → 한 row 단위로 가공/APPEND- 모든 필드를
string으로 받은 뒤MOVE-CORRESPONDING으로 변환
6단계 — 유효성 체크 + DB 저장
LOOP 끝나면 gt_data 에 행 단위 결과가 쌓여있습니다. 그 다음은 표준 흐름.
" 1) 검증 — 필수값 누락·범위 초과 체크
LOOP AT gt_data ASSIGNING FIELD-SYMBOL(<ls>).
IF <ls>-matnr IS INITIAL.
<ls>-msg = |[Row { sy-tabix + 1 }] 자재코드 누락|.
CONTINUE.
ENDIF.
" 자재 마스터 존재 체크
SELECT SINGLE matnr FROM mara INTO @DATA(lv_chk)
WHERE matnr = @<ls>-matnr.
IF sy-subrc <> 0.
<ls>-msg = |[Row { sy-tabix + 1 }] 자재 마스터 없음|.
ENDIF.
ENDLOOP.
" 2) 에러 없을 때만 INSERT
IF NOT line_exists( gt_data[ msg <> '' ] ).
TRY.
INSERT ztxx0123 FROM TABLE gt_data.
COMMIT WORK.
MESSAGE |{ lines( gt_data ) } 건 등록 완료| TYPE 'S'.
CATCH cx_root INTO DATA(oref).
ROLLBACK WORK.
MESSAGE oref->get_text( ) TYPE 'E'.
ENDTRY.
ELSE.
cl_demo_output=>display( gt_data ).
ENDIF.
흔히 빠뜨리는 함정
i_begin_row 헤더 포함 실수
i_begin_row = 1 로 두면 1행 헤더("자재코드", "PO수량" 등) 도 데이터로 들어와 매핑 오류. 항상 row=2 부터.
gs_excel 컬럼 순서 ≠ Excel 컬럼 순서
ASSIGN COMPONENT col 은 col 번호 그대로 N번째 필드에 꽂으므로, 구조체 필드 순서가 Excel 컬럼 순서와 1:1 매칭이어야 함. 양식 변경 시 둘 다 같이 손봐야 함.
빈 셀은 안 들어옴
ALSM 은 값이 있는 셀만 반환. 빈 셀이 있는 행에서 CLEAR gs_excel 안 하면 이전 행의 값이 남음. AT END OF row 안에서 APPEND 후 즉시 CLEAR gs_excel.
셀이 50자 초과면 잘림
ALSMEX_TABLINE 의 value 필드는 char50. 긴 텍스트(주소·비고 등) 가 들어가면 잘려서 들어옴. 50자 넘는 컬럼은 cl_fdt_xl_spreadsheet 로 우회.
숫자 천단위 콤마
Excel 셀이 1,234,567 식이면 그대로 string 으로 들어와 numc/p 타입으로 캐스팅 시 오류. REPLACE ALL OCCURRENCES OF ',' IN ... WITH '' 로 사전 제거.
날짜 시리얼 숫자로 들어옴
Excel 의 날짜는 내부적으로 시리얼 숫자(1900-01-01 기준) 라 45000 같이 들어올 수 있음. 양식에서 셀 서식을 텍스트로 강제하거나 ABAP 에서 변환 처리.
i_end_row 너무 작게 주기
실제 데이터가 7000행인데 i_end_row = 6500 이면 잘림. 여유있게 크게 주거나 가변값으로 처리 (예: 99999).
xlsx 압축 파일 그대로 첨부
xlsx 는 내부적으로 zip — 파일이 손상되거나 매크로 보안 차단되면 ALSM 도 실패. 사용자에게 "xls 또는 xlsx 평문" 양식 사용을 안내.
DESCRIBE FIELD 누락 → end_col 하드코딩
DESCRIBE FIELD gs_excel TYPE gv_type COMPONENTS gv_nrow 로 컬럼 수 동적 추출하면 양식 컬럼 늘어도 코드 안 고쳐도 됨. 하드코딩 i_end_col = 12 는 유지보수 부담.
전체 코드 — 복사용 통합본
위 단계를 하나의 ABAP 프로그램으로 합친 통합본입니다. SE38 에 그대로 복사 후 활성화 + SMW0 에 ZTPL_EXCEL_UPLOAD 양식 객체를 등록하면 동작합니다.
*&---------------------------------------------------------------------*
*& ALSM_EXCEL_TO_INTERNAL_TABLE 패턴 — Excel 업로드 + 양식 다운로드
*&---------------------------------------------------------------------*
REPORT zexample_excel_upload NO STANDARD PAGE HEADING.
* ========== DATA 선언 ==========
DATA: gt_intern LIKE TABLE OF alsmex_tabline,
gs_intern TYPE alsmex_tabline,
gv_type TYPE c LENGTH 1,
gv_nrow TYPE i.
FIELD-SYMBOLS <fs> TYPE any.
DATA: BEGIN OF gs_excel,
matnr TYPE string, " 자재코드
menge TYPE string, " PO수량
meins TYPE string, " 구매오더단위
netpr TYPE string, " 단가
waers TYPE string, " 통화
eindt TYPE string, " 납품일
END OF gs_excel,
gt_excel LIKE TABLE OF gs_excel.
DATA: BEGIN OF gs_data,
matnr TYPE matnr,
menge TYPE menge_d,
meins TYPE meins,
netpr TYPE p LENGTH 11 DECIMALS 2,
waers TYPE waers,
eindt TYPE sy-datum,
msg TYPE string,
END OF gs_data,
gt_data LIKE TABLE OF gs_data.
* ========== Selection Screen ==========
SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME TITLE TEXT-001.
PARAMETERS:
p_file TYPE rlgrap-filename MODIF ID m1,
p_dl AS CHECKBOX.
SELECTION-SCREEN END OF BLOCK b1.
* ========== F4 파일 선택 ==========
AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_file.
DATA: l_file_table TYPE filetable,
l_rc TYPE i,
file_name TYPE string.
cl_gui_frontend_services=>file_open_dialog(
EXPORTING
window_title = 'Excel 파일 선택'
file_filter = '*.xls;*.xlsx'
initial_directory = 'C:\'
CHANGING
file_table = l_file_table
rc = l_rc ).
READ TABLE l_file_table INTO file_name INDEX 1.
p_file = file_name.
* ========== START-OF-SELECTION ==========
START-OF-SELECTION.
* ---------- 양식 다운로드 옵션 ----------
IF p_dl = abap_true.
PERFORM download_template.
RETURN.
ENDIF.
IF p_file IS INITIAL.
MESSAGE 'Excel 파일을 선택하세요' TYPE 'E'.
ENDIF.
* ---------- Excel → gt_intern ----------
DESCRIBE FIELD gs_excel TYPE gv_type COMPONENTS gv_nrow.
CALL FUNCTION 'ALSM_EXCEL_TO_INTERNAL_TABLE'
EXPORTING
filename = p_file
i_begin_col = 1
i_begin_row = 2
i_end_col = gv_nrow
i_end_row = 99999
TABLES
intern = gt_intern
EXCEPTIONS
inconsistent_parameters = 1
upload_ole = 2
OTHERS = 3.
IF sy-subrc <> 0.
MESSAGE '데이터 불러오는 중 오류 발생' TYPE 'E'.
ENDIF.
* ---------- gt_intern → gt_data (동적 매핑) ----------
LOOP AT gt_intern INTO gs_intern.
ASSIGN COMPONENT gs_intern-col OF STRUCTURE gs_excel TO <fs>.
IF <fs> IS ASSIGNED.
<fs> = gs_intern-value.
ENDIF.
AT END OF row.
* 천단위 콤마 → 마침표
REPLACE ALL OCCURRENCES OF ',' IN gs_excel-netpr WITH '.'.
CONDENSE gs_excel-netpr NO-GAPS.
MOVE-CORRESPONDING gs_excel TO gs_data.
APPEND gs_data TO gt_data.
CLEAR: gs_excel, gs_data.
ENDAT.
ENDLOOP.
* ---------- 유효성 체크 ----------
LOOP AT gt_data ASSIGNING FIELD-SYMBOL(<ls>).
IF <ls>-matnr IS INITIAL.
<ls>-msg = |[Row { sy-tabix + 1 }] 자재코드 누락|.
ENDIF.
ENDLOOP.
* ---------- 결과 표시 ----------
cl_demo_output=>display( gt_data ).
* ======================================================================
* FORM download_template — SMW0 양식 다운로드
* ======================================================================
FORM download_template.
DATA: lv_fname TYPE rlgrap-filename,
ls_wwwdatatab LIKE wwwdatatab.
CALL FUNCTION 'F4_FILENAME'
EXPORTING
program_name = syst-cprog
dynpro_number = syst-dynnr
field_name = 'FILENAME'
IMPORTING
file_name = lv_fname.
IF lv_fname IS INITIAL.
MESSAGE '저장 경로를 선택해주세요' TYPE 'E'.
ENDIF.
lv_fname = lv_fname && '.xlsx'.
SELECT SINGLE * INTO CORRESPONDING FIELDS OF ls_wwwdatatab
FROM wwwdata
WHERE objid = 'ZTPL_EXCEL_UPLOAD'.
IF sy-subrc <> 0.
MESSAGE 'SMW0 에 양식이 등록되지 않았습니다' TYPE 'E'.
ENDIF.
CALL FUNCTION 'DOWNLOAD_WEB_OBJECT'
EXPORTING
key = ls_wwwdatatab
destination = lv_fname.
IF sy-subrc EQ 0.
MESSAGE 'Excel 양식 다운로드 완료' TYPE 'S'.
ELSE.
MESSAGE 'Excel 양식 다운로드 실패' TYPE 'E'.
ENDIF.
ENDFORM.
* ※ 참고:
* - 양식 컬럼 순서와 gs_excel 필드 순서가 1:1 매칭 필요
* - i_begin_row = 2 (헤더 제외), i_end_row 는 여유있게 크게
* - 셀 50자 제한 — 긴 텍스트는 cl_fdt_xl_spreadsheet 권장
* - 천단위 콤마·날짜 시리얼 등 사전 정제 필수
* - SMW0 객체 ID 는 환경에 맞게 교체
요약
| 단계 | 처리 | 핵심 |
|---|---|---|
| 1 | DATA 선언 | gt_intern LIKE TABLE OF alsmex_tabline + 양식 순서대로 gs_excel |
| 2 | 파일 선택 | F4_FILENAME 또는 cl_gui_frontend_services=>file_open_dialog |
| 3 | 양식 다운로드 | SMW0 등록 후 DOWNLOAD_WEB_OBJECT |
| 4 | ALSM 호출 | begin_row = 2, end_col = DESCRIBE FIELD ... COMPONENTS |
| 5 | 동적 매핑 | ASSIGN COMPONENT col OF STRUCTURE + AT END OF row |
| 6 | 검증·저장 | 필수값 체크 → INSERT FROM TABLE + COMMIT WORK |
Excel 업로드의 표준 흐름은 파일 선택 → ALSM 으로 셀 좌표 변환 → ASSIGN COMPONENT 로 동적 매핑 → AT END OF row 에서 행 단위 가공·검증 → DB 저장. ALSM_EXCEL_TO_INTERNAL_TABLE 은 셀 단위로 row/col/value 를 돌려주므로 ASSIGN COMPONENT 로 양식 순서에 맞게 풀어주는 게 핵심이고, 50자 제한·천단위 콤마·날짜 시리얼 같은 디테일만 챙기면 SAP 업무 자동화의 80% 가 커버됩니다.
SMW0 에 양식까지 등록하면 "양식 받기 → 채우기 → 업로드" 가 한 화면에서 끝나는 깔끔한 UX 가 완성됩니다.
Disclaimer — 이 포스트는 실무 정리 노트를 바탕으로 AI 보조로 정리되었습니다. ALSM_EXCEL_TO_INTERNAL_TABLE·F4_FILENAME·DOWNLOAD_WEB_OBJECT 는 SAP NetWeaver 표준 RFC/모듈로 시스템 버전 의존 없이 동작합니다. cl_gui_frontend_services=>file_open_dialog 는 SAP GUI for Windows 에서만 정상 동작하며 웹 GUI/Fiori 환경에서는 별도 처리가 필요합니다.
ALSMEX_TABLINE 의 value 필드는 char50 제한이 있어 긴 텍스트가 필요한 경우 cl_fdt_xl_spreadsheet·abap2xlsx 같은 대안을 검토하시기 바랍니다. 운영 적용 전 양식 컬럼 순서·검증 로직·트랜잭션 처리(COMMIT WORK/ROLLBACK WORK) 를 충분히 테스트하시기 바랍니다.