본문 바로가기
ABAP 문법 & 기법/Excel in SAP

[SAP ABAP] Excel 업로드 한 데이터를 ALV로 바로 보기 — cl_fdt_xl_spreadsheet + cl_salv_table

by Song.sh 2026. 5. 14.

사용자가 보내준 Excel 데이터를 SAP에 저장하지 않고 ALV로 바로 보기만 하고 싶을 때 가 있습니다. 데이터 검토·검증·임시 분석 같은 작업이죠. CBO 테이블에 저장하는 별도의 업로드 프로그램을 만들기는 과해 보이고, 그렇다고 SE16N에 직접 붙여넣기도 번거롭습니다.

 

SAP NetWeaver 7.40 이상에서는 표준 클래스 cl_fdt_xl_spreadsheet 를 이용해서 Excel 파일을 그대로 ALV로 띄울 수 있습니다. 별도 테이블 정의 없이도 Excel의 첫 행을 헤더로 인식해서 동적 internal table을 만들어주고, SALV factory로 그대로 출력하면 끝.

 

이 글은 Excel 파일 → 동적 itab → SALV ALV 흐름을 한 화면 ABAP 프로그램으로 구현하는 패턴을 정리한 메모입니다.


핵심 원리

흐름은 5단계로 깔끔합니다.

단계 처리 핵심 API
1 파일 선택 다이얼로그 cl_gui_frontend_services=>file_open_dialog
2 바이너리 업로드 + XSTRING 변환 gui_upload + cl_bcs_convert=>solix_to_xstring
3 Excel 파싱 (XML 내부 변환) cl_fdt_xl_spreadsheet 인스턴스 생성
4 Worksheet → 동적 itab get_itab_for_alv_update( ) + FIELD-SYMBOLS
5 ALV 표시 cl_salv_table=>factory + 컬럼 헤더 설정

핵심 트릭은 cl_fdt_xl_spreadsheet 가 Excel의 첫 행을 헤더로 인식해서 동적 internal table 의 컬럼 구조를 자동으로 만들어준다 는 점입니다. 사용자가 어떤 Excel을 가져오든 별도 DDIC 정의 없이 그대로 표시됩니다.

⚠️ 중요한 제약: Excel 첫 행(헤더) 에 한글이 있으면 안 됩니다. 첫 행이 동적 itab의 컬럼명이 되는데, ABAP은 컬럼명에 한글을 허용하지 않으므로 첫 행은 영문/숫자/언더스코어로만 작성해야 합니다.


1단계 — 파일 선택 다이얼로그

사용자가 Excel 파일을 직접 선택하도록 다이얼로그를 띄웁니다. 필터를 *.xlsx 로 잡아두면 깔끔합니다.

DATA: lv_rc       TYPE i,
      it_files    TYPE filetable,
      lv_action   TYPE i.

cl_gui_frontend_services=>file_open_dialog(
  EXPORTING
    file_filter = |xlsx (*.xlsx)\|*.xlsx\||
               && |{ cl_gui_frontend_services=>filetype_all }|
  CHANGING
    file_table  = it_files
    rc          = lv_rc
    user_action = lv_action ).

IF lv_action <> cl_gui_frontend_services=>action_ok OR lines( it_files ) = 0.
  RETURN.   " 사용자 취소 또는 파일 미선택
ENDIF.

선택된 파일 경로는 it_files[ 1 ]-filename 에 담깁니다.


2단계 — 바이너리 업로드 + XSTRING 변환

Excel은 텍스트 파일이 아니라 ZIP 기반 바이너리(XLSX) 입니다. 반드시 filetype = 'BIN' 으로 업로드해야 합니다.

DATA: lv_filesize TYPE w3param-cont_len,
      it_bin_data TYPE w3mimetabtype.

cl_gui_frontend_services=>gui_upload(
  EXPORTING
    filename   = |{ it_files[ 1 ]-filename }|
    filetype   = 'BIN'                       " 바이너리 모드 필수
  IMPORTING
    filelength = lv_filesize
  CHANGING
    data_tab   = it_bin_data ).

" SOLIX 테이블 → XSTRING 변환
DATA(lv_bin_data) = cl_bcs_convert=>solix_to_xstring( it_solix = it_bin_data ).

gui_upload 결과는 SOLIX(바이너리 라인 테이블) 형식이라 cl_bcs_convert=>solix_to_xstring 으로 XSTRING 으로 변환해서 다음 단계로 넘김.


3단계 — Excel 파싱 (cl_fdt_xl_spreadsheet)

XSTRING으로 받은 Excel 바이너리를 cl_fdt_xl_spreadsheet 인스턴스에 넘기면 내부에서 XML 으로 풀어내고 워크시트 단위로 접근할 수 있게 해줍니다.

" Excel (itab) → XML → 객체 참조
" 주의: 메모리 사용 큼 · 느림 — 대용량 데이터는 부적합
DATA(o_excel) = NEW cl_fdt_xl_spreadsheet(
                  document_name = CONV #( it_files[ 1 ]-filename )
                  xdocument     = lv_bin_data ).

" 워크시트 이름 목록 조회
DATA: it_worksheet_names TYPE if_fdt_doc_spreadsheet=>t_worksheet_names.

o_excel->if_fdt_doc_spreadsheet~get_worksheet_names(
  IMPORTING worksheet_names = it_worksheet_names ).

핵심 포인트:

  • document_name — 파일명(확장자 포함)
  • xdocument — XSTRING 바이너리
  • if_fdt_doc_spreadsheet 인터페이스로 워크시트 목록·데이터 접근

원래 NetWeaver 의 BRFplus 모듈에서 사용하던 내부 유틸리티지만, 일반 ABAP에서도 그대로 호출 가능합니다.


4단계 — Worksheet → 동적 itab

특정 워크시트의 데이터를 ALV용 동적 internal table 로 변환합니다. get_itab_for_alv_update( ) 가 핵심 메서드.

IF lines( it_worksheet_names ) > 0.

  " 첫 워크시트의 itab 참조 가져오기
  DATA(o_worksheet_itab) = o_excel->if_fdt_doc_spreadsheet~get_itab_for_alv_update( ).

  " 동적 데이터 객체 → field-symbol 매핑
  ASSIGN o_worksheet_itab->* TO FIELD-SYMBOL(<worksheet>).

  ...
ENDIF.

이 한 줄로 끝납니다. <worksheet> 는 Excel 첫 행을 헤더로 한 동적 internal table 입니다. 별도 TYPES 선언이 전혀 필요 없습니다. 컬럼 개수·이름이 매번 다른 Excel을 그대로 처리할 수 있다는 게 이 패턴의 큰 강점.


5단계 — SALV factory로 ALV 표시 + 헤더 정리

cl_salv_table=>factory 는 동적 internal table도 그대로 받아서 ALV를 생성해줍니다.

TRY.
    DATA: o_salv TYPE REF TO cl_salv_table.

    cl_salv_table=>factory(
      IMPORTING
        r_salv_table = o_salv
      CHANGING
        t_table      = <worksheet> ).

    " 기본 설정
    o_salv->get_functions( )->set_all( abap_true ).
    o_salv->get_columns( )->set_optimize( abap_true ).
    o_salv->get_display_settings( )->set_list_header( 'Worksheet' ).
    o_salv->get_display_settings( )->set_striped_pattern( abap_true ).
    o_salv->get_selections( )->set_selection_mode(
                                 if_salv_c_selection_mode=>row_column ).

    " 컬럼 헤더 텍스트 — 기술명을 long text 로 표시
    LOOP AT o_salv->get_columns( )->get( ) ASSIGNING FIELD-SYMBOL(<c>).
      DATA(o_col) = <c>-r_column.
      o_col->set_short_text(  || ).
      o_col->set_medium_text( || ).
      o_col->set_long_text( |{ o_col->get_columnname( ) }| ).
    ENDLOOP.

    o_salv->display( ).

  CATCH cx_root INTO DATA(e_txt).
    WRITE: / e_txt->get_text( ).
ENDTRY.

핵심 포인트:

  • get_functions( )->set_all( abap_true ) — 표준 ALV 버튼(정렬·필터·합계 등) 전부 활성화
  • set_optimize( abap_true ) — 컬럼 너비 자동 맞춤
  • set_striped_pattern( abap_true ) — 줄 무늬 배경
  • 컬럼 헤더 LOOP — 기본은 컬럼 기술명이 short/medium 으로만 나와서 줄어 보이므로 set_long_text 에 풀 컬럼명을 박아 가독성 확보

전체 코드 (한 화면 정리)

위 5단계를 한 프로그램에 합치면 다음과 같습니다.

TRY.
    DATA: lv_rc       TYPE i,
          it_files    TYPE filetable,
          lv_action   TYPE i.

    " 1) 파일 선택
    cl_gui_frontend_services=>file_open_dialog(
      EXPORTING
        file_filter = |xlsx (*.xlsx)\|*.xlsx\||
                   && |{ cl_gui_frontend_services=>filetype_all }|
      CHANGING
        file_table  = it_files
        rc          = lv_rc
        user_action = lv_action ).

    IF lv_action = cl_gui_frontend_services=>action_ok.
      IF lines( it_files ) > 0.

        " 2) 바이너리 업로드
        DATA: lv_filesize TYPE w3param-cont_len,
              it_bin_data TYPE w3mimetabtype.

        cl_gui_frontend_services=>gui_upload(
          EXPORTING
            filename   = |{ it_files[ 1 ]-filename }|
            filetype   = 'BIN'
          IMPORTING
            filelength = lv_filesize
          CHANGING
            data_tab   = it_bin_data ).

        DATA(lv_bin_data) = cl_bcs_convert=>solix_to_xstring( it_solix = it_bin_data ).

        " 3) Excel 파싱
        DATA(o_excel) = NEW cl_fdt_xl_spreadsheet(
                          document_name = CONV #( it_files[ 1 ]-filename )
                          xdocument     = lv_bin_data ).

        DATA: it_worksheet_names TYPE if_fdt_doc_spreadsheet=>t_worksheet_names.

        o_excel->if_fdt_doc_spreadsheet~get_worksheet_names(
          IMPORTING worksheet_names = it_worksheet_names ).

        IF lines( it_worksheet_names ) > 0.

          " 4) 첫 워크시트 → 동적 itab
          DATA(o_worksheet_itab) = o_excel->if_fdt_doc_spreadsheet~get_itab_for_alv_update( ).
          ASSIGN o_worksheet_itab->* TO FIELD-SYMBOL(<worksheet>).

          " 5) SALV 표시
          TRY.
              DATA: o_salv TYPE REF TO cl_salv_table.

              cl_salv_table=>factory(
                IMPORTING
                  r_salv_table = o_salv
                CHANGING
                  t_table      = <worksheet> ).

              o_salv->get_functions( )->set_all( abap_true ).
              o_salv->get_columns( )->set_optimize( abap_true ).
              o_salv->get_display_settings( )->set_list_header( 'Worksheet' ).
              o_salv->get_display_settings( )->set_striped_pattern( abap_true ).
              o_salv->get_selections( )->set_selection_mode(
                                           if_salv_c_selection_mode=>row_column ).

              LOOP AT o_salv->get_columns( )->get( ) ASSIGNING FIELD-SYMBOL(<c>).
                DATA(o_col) = <c>-r_column.
                o_col->set_short_text(  || ).
                o_col->set_medium_text( || ).
                o_col->set_long_text( |{ o_col->get_columnname( ) }| ).
              ENDLOOP.

              o_salv->display( ).

            CATCH cx_root INTO DATA(e_txt).
              WRITE: / e_txt->get_text( ).
          ENDTRY.
        ENDIF.
      ENDIF.
    ENDIF.

  CATCH cx_root INTO DATA(e_text).
    MESSAGE e_text->get_text( ) TYPE 'S' DISPLAY LIKE 'E'.
ENDTRY.

복붙해서 SE38에 그대로 넣고 활성화하면 즉시 동작하는 한 화면짜리 유틸리티 프로그램이 됩니다.


흔히 빠뜨리는 함정

Excel 첫 줄에 한글 사용

가장 흔한 함정. cl_fdt_xl_spreadsheet 는 Excel 첫 행을 동적 internal table 의 컬럼명으로 사용합니다. ABAP은 컬럼명에 한글을 허용하지 않으므로, 첫 행은 반드시 영문·숫자·언더스코어만 사용. 컬럼 이름은 영문으로, 한글 라벨이 필요하면 SALV 컬럼 LOOP에서 set_long_text 에 별도로 박아주는 방식.

gui_upload 에서 filetype = 'ASC' 사용

Excel(XLSX) 은 ZIP 기반 바이너리. 'ASC'(텍스트) 모드로 읽으면 깨진 데이터가 들어오고 cl_fdt_xl_spreadsheet 가 파싱 실패. 반드시 'BIN'.

대용량 Excel — 메모리 폭발 위험

Notion 원문 코멘트에도 명시되어 있는 부분 — Speicherintensiv und rel. langsam!(메모리 사용 크고 느림). cl_fdt_xl_spreadsheet 는 Excel 전체를 메모리에 XML로 풀어내는 방식이라 수만 행 이상 대용량은 부적합. 그런 경우는 TEXT_CONVERT_XLS_TO_SAP(텍스트 변환) 또는 ABAP2XLSX 같은 라이브러리 사용.

여러 워크시트가 있는 경우 첫 시트만 처리됨

get_itab_for_alv_update( ) 는 기본적으로 첫 워크시트만 가져옵니다. 다른 시트가 필요하면 it_worksheet_names 를 조회한 뒤 get_worksheet_data 같은 다른 메서드로 시트별 처리.

Excel 셀 자료형이 모두 문자열로 들어옴

cl_fdt_xl_spreadsheet 가 만드는 동적 itab은 모든 컬럼이 문자열 타입입니다. ALV로 단순 보기만 할 때는 문제 없지만, 후속 처리(예: 합계·정렬) 가 필요하면 수동으로 NUMC/DEC 변환 필요.

서버 잡(SM37)에서 실행 불가

file_open_dialog / gui_upload 는 SAP GUI 클라이언트에서만 동작. 배치 잡에서는 사용 불가. 서버에서 처리할 경우 OPEN DATASET + AL11(서버 디렉토리) 로 파일을 먼저 읽은 뒤 처리.


요약

단계 처리 핵심
1 파일 선택 file_open_dialog + xlsx 필터
2 바이너리 업로드 gui_upload (BIN) → solix_to_xstring
3 Excel 파싱 NEW cl_fdt_xl_spreadsheet( document_name, xdocument )
4 동적 itab get_itab_for_alv_update( ) + FIELD-SYMBOL
5 ALV 표시 cl_salv_table=>factory + 컬럼 LOOP 으로 long text 채우기

이 패턴의 가장 큰 가치는 DDIC 정의·CBO 테이블 없이도 Excel을 그대로 ALV로 띄울 수 있다 는 점입니다. 사용자가 보낸 임의 형식 Excel을 검토하거나, 간단한 분석·검증·테스트 데이터 비교에 매우 유용합니다. 단, Excel 첫 행 한글 금지·대용량 부적합 두 가지 제약만 기억해두면 한 번 만든 프로그램을 사내 공용 유틸리티로 두고두고 쓸 수 있습니다.


Disclaimer — 이 포스트는 실무 정리 노트를 바탕으로 AI 보조로 정리되었습니다. cl_fdt_xl_spreadsheet 는 SAP NetWeaver 7.40 이상에서 사용 가능하며, SAP GUI 클라이언트 환경에서만 동작합니다. 대용량 Excel 처리에는 부적합하므로 운영 환경 적용 전 데이터 규모를 고려해 검증하시기 바랍니다.