본문 바로가기
ALV · 화면 · 리포트

[SAP ABAP] ALV 소계 행에 커스텀 텍스트 넣기 — SUBTOTAL_TEXT 이벤트 (no_out + sort subtot 조건)

by Song.sh 2026. 5. 29.

ALV 에서 소계(Subtotal) 를 잡으면 기본으로 "* 소계" 같은 밋밋한 표시만 나옵니다. 이걸 "Plane Type 747-400" 처럼 소계 그룹이 무엇인지 알려주는 의미 있는 텍스트로 바꾸고 싶을 때 쓰는 것이 SUBTOTAL_TEXT 이벤트입니다.

 

핵심은 이 이벤트가 그냥은 발생하지 않는다 는 점입니다. 두 가지 조건 — (1) 소계 기준 컬럼을 Field Catalog 에서 숨김(no_out) 처리 하고, (2) Sort 에서 그 컬럼에 소계(subtot) 를 켜야 — 가 맞아야 SAP 가 소계 행을 그릴 때 SUBTOTAL_TEXT 이벤트를 던집니다.

 

이 글에서는 이벤트 발생 조건 → 이벤트 핸들러 등록 → Field Catalog/Sort 설정 → 소계 텍스트 할당까지 정리합니다. 데이터는 SAP 표준 학습 환경 테이블 SFLIGHT 를 기종(PLANETYPE) 별로 소계 잡는 예시.

핵심 — SUBTOTAL_TEXT 이벤트가 걸리는 조건

조건 설정 위치 설정
1. 소계 기준 숨김 Field Catalog 소계 기준 컬럼 no_out = abap_true
2. 소계 켜기 Sort 같은 컬럼 subtot = abap_true
3. 합계 컬럼 Field Catalog 합산할 금액 컬럼 do_sum = abap_true
4. 이벤트 핸들러 로컬 클래스 SUBTOTAL_TEXT 이벤트로 텍스트 할당

특히 1번이 가장 헷갈리는 함정 입니다. 소계 기준 컬럼이 화면에 보이면(no_out 아님) SAP 는 굳이 텍스트 이벤트를 던지지 않습니다 — 컬럼 자체에 값이 보이니까요. 컬럼을 숨겨야 "그럼 소계 행에 무슨 글자를 쓸까?" 를 개발자에게 묻는 이벤트가 발생합니다.

PLANETYPE 컬럼 숨김(no_out) + Sort subtot
        ↓
SAP 가 기종별 소계 행을 그릴 때
        ↓
SUBTOTAL_TEXT 이벤트 발생 (criteria='PLANETYPE', key='747-400')
        ↓
핸들러에서 e_event_data->m_data 에 'Plane Type 747-400' 할당
        ↓
소계 행에 의미 있는 텍스트 표시

1단계 — DATA + 이벤트 핸들러 클래스

소계 텍스트 이벤트를 받을 핸들러 클래스를 정의합니다. 이벤트 시그니처가 핵심입니다.

CLASS lcl_receiver DEFINITION.
  PUBLIC SECTION.
    METHODS handle_subtotal
      FOR EVENT subtotal_text OF cl_gui_alv_grid
      IMPORTING es_subtottxt_info     " 소계 정보 (criteria · key)
                ep_subtot_line        " 소계 라인 데이터
                e_event_data.         " 표시 텍스트를 담을 객체
ENDCLASS.

CLASS lcl_receiver IMPLEMENTATION.
  METHOD handle_subtotal.
    PERFORM handle_subtotal USING es_subtottxt_info
                                  ep_subtot_line
                                  e_event_data.
  ENDMETHOD.
ENDCLASS.

SUBTOTAL_TEXT 이벤트의 세 인자가 핵심입니다.

인자 의미
es_subtottxt_info 소계 정보. criteria(소계 기준 컬럼명) · key(소계 그룹 값)
ep_subtot_line 소계 라인 데이터 참조 (집계 값)
e_event_data 표시할 텍스트를 담는 객체 (m_data 에 ASSIGN)

등록은 SET HANDLER 로 합니다.

FORM set_event.
  g_event = NEW #( ).
  SET HANDLER g_event->handle_subtotal FOR go_grid.
ENDFORM.

2단계 — Field Catalog: 소계 기준 숨김 + 합계 지정

Field Catalog 를 자동 생성한 뒤, 소계 기준 컬럼(PLANETYPE) 은 숨기고, 합산할 컬럼(PAYMENTSUM) 은 합계를 켭니다.

FORM set_fcat.
  DATA lr_str TYPE REF TO cl_abap_structdescr.
  lr_str ?= cl_abap_structdescr=>describe_by_data( gs_data ).
  DATA(lt_fcat) = cl_salv_data_descr=>read_structdescr( lr_str ).
  gt_fcat = CORRESPONDING #( lt_fcat MAPPING ref_table = reftable
                                              ref_field = reffield ).
  CALL FUNCTION 'LVC_FIELDCAT_COMPLETE'
    CHANGING ct_fieldcat = gt_fcat.

  LOOP AT gt_fcat INTO DATA(ls_fcat).
    CASE ls_fcat-fieldname.
      WHEN 'PAYMENTSUM'.
        ls_fcat-do_sum = abap_true.        " 금액 합계
      WHEN 'MANDT'.
        ls_fcat-no_out = abap_true.        " 클라이언트 숨김
      WHEN 'PLANETYPE'.
        ls_fcat-no_out = abap_true.        " ★ 소계 기준 — 숨겨야 이벤트 발생
    ENDCASE.
    MODIFY gt_fcat FROM ls_fcat.
    CLEAR ls_fcat.
  ENDLOOP.
ENDFORM.

PLANETYPEno_out = abap_true 가 핵심입니다. 이게 빠지면 컬럼이 화면에 그대로 보이고 SUBTOTAL_TEXT 이벤트가 발생하지 않습니다.


3단계 — Sort: 소계 켜기

소계를 켜려면 Sort 에 해당 컬럼을 넣고 subtot = abap_true 를 지정합니다.

FORM set_sort.
  DATA gs_sort TYPE lvc_s_sort.

* 1차 정렬 — 항공사 (소계 없음)
  gs_sort-spos      = '1'.
  gs_sort-fieldname = 'CARRID'.
  gs_sort-up        = abap_true.
  APPEND gs_sort TO gt_sort.
  CLEAR gs_sort.

* 2차 정렬 — 기종, 소계 ON
  gs_sort-spos      = '2'.
  gs_sort-fieldname = 'PLANETYPE'.
  gs_sort-up        = abap_true.
  gs_sort-subtot    = abap_true.        " ★ 소계 켜기
  APPEND gs_sort TO gt_sort.
ENDFORM.

subtot = abap_true 가 켜진 컬럼(PLANETYPE) 의 값이 바뀔 때마다 소계 행이 생기고, 그 행을 그릴 때 SUBTOTAL_TEXT 이벤트가 발생합니다. set_table_for_first_displayit_sort 에 이 테이블을 넘깁니다.


4단계 — handle_subtotal: 소계 텍스트 할당

이벤트 핸들러에서 criteria 로 어느 소계인지 판단하고, e_event_data->m_data 에 표시할 텍스트를 할당합니다.

FORM handle_subtotal USING p_subtottxt_info TYPE lvc_s_stxt
                           p_subtot_line    TYPE REF TO data
                           p_event_data     TYPE REF TO cl_alv_event_data.

  FIELD-SYMBOLS <fs_text> TYPE any.

* 소계 기준이 PLANETYPE 일 때만 처리
  IF p_subtottxt_info-criteria = 'PLANETYPE'.

*   표시 텍스트를 담을 영역 연결
    ASSIGN p_event_data->m_data->* TO <fs_text>.

*   소계 그룹 값(key) 으로 텍스트 구성 → "Plane Type 747-400"
    <fs_text> = |Plane Type { p_subtottxt_info-key }|.

  ENDIF.

ENDFORM.

e_event_data->m_data 는 제네릭 데이터 참조이므로 ASSIGN ...->* 로 필드 심볼에 연결한 뒤 값을 씁니다. p_subtottxt_info-key 에는 해당 소계 그룹의 값(예: 747-400) 이 들어옵니다. 소계 기준이 여러 개면 criteria 로 분기해서 각각 다른 텍스트를 줄 수 있습니다.


자주 빠뜨리는 함정

no_out 누락 — 이벤트가 안 걸림

가장 흔한 실수. 소계 기준 컬럼을 화면에 그대로 두면(no_out 미설정) SUBTOTAL_TEXT 이벤트 자체가 발생하지 않습니다. 컬럼을 숨겨야 SAP 가 텍스트를 묻습니다.

no_out 과 subtot 컬럼 불일치

숨긴 컬럼과 소계 켠 컬럼이 같은 컬럼 이어야 합니다. PLANETYPE 을 숨겼는데 Sort 의 subtot 은 CARRID 에 걸면 PLANETYPE 소계 텍스트 이벤트가 안 옵니다.

m_data 직접 대입

e_event_data->m_data 는 데이터 참조(REF) 라 직접 대입할 수 없습니다. 반드시 ASSIGN p_event_data->m_data->* TO 로 역참조한 필드 심볼에 값을 써야 합니다.

criteria 분기 누락

소계 기준이 여러 개(예: CARRID 와 PLANETYPE 둘 다 subtot) 면 이벤트가 각각 발생합니다. criteria 로 분기하지 않으면 모든 소계에 같은 텍스트가 찍힙니다.

do_sum 누락 — 소계 행이 안 생김

합산할 숫자 컬럼에 do_sum 이 없으면 소계 행 자체가 의미가 없거나 안 나타날 수 있습니다. 합계 컬럼을 최소 하나 지정해야 소계 구조가 잡힙니다.


전체 코드 — 복사용 통합본

아래는 SAP 표준 모듈풀 구조에 맞춰 메인 + 6개 INCLUDE(T / C / SCR / O / I / F) 로 나눈 통합본입니다. SE38 에서 메인 프로그램과 각 INCLUDE 를 만들고, Screen 100 의 Layout 은 빈 상태로 두고 Flow Logic 에 status_0100 · set_alv (PBO) · user_command_0100 (PAI) 를 연결합니다. INCLUDE 는 T → C → SCR → O → I → F 순서로 로드됩니다.

*&---------------------------------------------------------------------*
*& Report ZRXX_ALV_SUBTOTAL_TEXT  (메인 프로그램)
*&---------------------------------------------------------------------*
REPORT zrxx_alv_subtotal_text.

INCLUDE zrxx_alv_subtotal_text_t.     " TOP   - 전역 선언
INCLUDE zrxx_alv_subtotal_text_c.     " CLASS - 로컬 클래스
INCLUDE zrxx_alv_subtotal_text_scr.   " SCR   - 셀렉션 스크린
INCLUDE zrxx_alv_subtotal_text_o.     " PBO   - OUTPUT 모듈
INCLUDE zrxx_alv_subtotal_text_i.     " PAI   - INPUT 모듈
INCLUDE zrxx_alv_subtotal_text_f.     " FORM  - 서브루틴

START-OF-SELECTION.
  SELECT * UP TO 30 ROWS
    INTO CORRESPONDING FIELDS OF TABLE gt_data
    FROM sflight.

  CALL SCREEN 100.

*&---------------------------------------------------------------------*
*& INCLUDE ZRXX_ALV_SUBTOTAL_TEXT_T  (TOP - 전역 선언)
*&---------------------------------------------------------------------*
DATA: go_docking TYPE REF TO cl_gui_docking_container,
      go_grid    TYPE REF TO cl_gui_alv_grid.

DATA: ok_code TYPE sy-ucomm.

DATA: gs_layo TYPE lvc_s_layo,
      gt_fcat TYPE lvc_t_fcat,
      gt_sort TYPE lvc_t_sort.

DATA: gs_data TYPE sflight,
      gt_data TYPE TABLE OF sflight.

DATA g_event TYPE REF TO lcl_receiver.   " C INCLUDE 의 클래스 참조

*&---------------------------------------------------------------------*
*& INCLUDE ZRXX_ALV_SUBTOTAL_TEXT_C  (CLASS - 로컬 클래스)
*&---------------------------------------------------------------------*
CLASS lcl_receiver DEFINITION.
  PUBLIC SECTION.
    METHODS handle_subtotal
      FOR EVENT subtotal_text OF cl_gui_alv_grid
      IMPORTING es_subtottxt_info ep_subtot_line e_event_data.
ENDCLASS.

CLASS lcl_receiver IMPLEMENTATION.
  METHOD handle_subtotal.
    PERFORM handle_subtotal USING es_subtottxt_info
                                  ep_subtot_line
                                  e_event_data.
  ENDMETHOD.
ENDCLASS.

*&---------------------------------------------------------------------*
*& INCLUDE ZRXX_ALV_SUBTOTAL_TEXT_SCR  (SCR - 셀렉션 스크린)
*&---------------------------------------------------------------------*
* 이 글은 셀렉션 스크린(SELECT-OPTIONS/PARAMETERS) 을 쓰지 않습니다.
* Screen 100 은 SE51 에서 정의 (Custom Control 없이 Docking 사용).
* 셀렉션 조건이 필요하면 여기에 SELECT-OPTIONS 를 선언.

*&---------------------------------------------------------------------*
*& INCLUDE ZRXX_ALV_SUBTOTAL_TEXT_O  (PBO - OUTPUT 모듈)
*&---------------------------------------------------------------------*
MODULE status_0100 OUTPUT.
  SET PF-STATUS 'S100'.
ENDMODULE.

MODULE set_alv OUTPUT.
  IF go_docking IS INITIAL.
    go_docking = NEW #( dynnr = sy-dynnr extension = 3000 ).
    go_grid    = NEW #( i_parent = go_docking ).

    PERFORM set_fcat.
    PERFORM set_sort.
    PERFORM set_event.

    gs_layo-zebra    = abap_true.
    gs_layo-cwidth_opt = abap_true.

    go_grid->set_table_for_first_display(
      EXPORTING
        is_layout       = gs_layo
      CHANGING
        it_outtab       = gt_data
        it_fieldcatalog = gt_fcat
        it_sort         = gt_sort ).
  ENDIF.
ENDMODULE.

*&---------------------------------------------------------------------*
*& INCLUDE ZRXX_ALV_SUBTOTAL_TEXT_I  (PAI - INPUT 모듈)
*&---------------------------------------------------------------------*
MODULE user_command_0100 INPUT.
  CASE ok_code.
    WHEN 'BACK' OR 'EXIT' OR 'CANC'.
      LEAVE TO SCREEN 0.
  ENDCASE.
ENDMODULE.

*&---------------------------------------------------------------------*
*& INCLUDE ZRXX_ALV_SUBTOTAL_TEXT_F  (FORM - 서브루틴)
*&---------------------------------------------------------------------*
* Field Catalog — 소계 기준 숨김 + 합계 지정
FORM set_fcat.
  DATA lr_str TYPE REF TO cl_abap_structdescr.
  lr_str ?= cl_abap_structdescr=>describe_by_data( gs_data ).
  DATA(lt_fcat) = cl_salv_data_descr=>read_structdescr( lr_str ).
  gt_fcat = CORRESPONDING #( lt_fcat MAPPING ref_table = reftable
                                              ref_field = reffield ).
  CALL FUNCTION 'LVC_FIELDCAT_COMPLETE'
    CHANGING ct_fieldcat = gt_fcat.

  LOOP AT gt_fcat INTO DATA(ls_fcat).
    CASE ls_fcat-fieldname.
      WHEN 'PAYMENTSUM'.
        ls_fcat-do_sum = abap_true.
      WHEN 'MANDT'.
        ls_fcat-no_out = abap_true.
      WHEN 'PLANETYPE'.
        ls_fcat-no_out = abap_true.       " ★ 소계 기준 숨김
    ENDCASE.
    MODIFY gt_fcat FROM ls_fcat.
    CLEAR ls_fcat.
  ENDLOOP.
ENDFORM.

* Sort — 소계 켜기
FORM set_sort.
  DATA gs_sort TYPE lvc_s_sort.

  gs_sort-spos      = '1'.
  gs_sort-fieldname = 'CARRID'.
  gs_sort-up        = abap_true.
  APPEND gs_sort TO gt_sort.
  CLEAR gs_sort.

  gs_sort-spos      = '2'.
  gs_sort-fieldname = 'PLANETYPE'.
  gs_sort-up        = abap_true.
  gs_sort-subtot    = abap_true.          " ★ 소계 ON
  APPEND gs_sort TO gt_sort.
ENDFORM.

* 이벤트 등록
FORM set_event.
  g_event = NEW #( ).
  SET HANDLER g_event->handle_subtotal FOR go_grid.
ENDFORM.

* ★ SUBTOTAL_TEXT 이벤트: 소계 텍스트 할당
FORM handle_subtotal USING p_subtottxt_info TYPE lvc_s_stxt
                           p_subtot_line    TYPE REF TO data
                           p_event_data     TYPE REF TO cl_alv_event_data.
  FIELD-SYMBOLS <fs_text> TYPE any.

  IF p_subtottxt_info-criteria = 'PLANETYPE'.
    ASSIGN p_event_data->m_data->* TO <fs_text>.
    <fs_text> = |Plane Type { p_subtottxt_info-key }|.
  ENDIF.
ENDFORM.


요약

단계 FORM 핵심
1 클래스 정의 handle_subtotal FOR EVENT subtotal_text
2 set_fcat 소계 기준 no_out + 금액 do_sum
3 set_sort 같은 컬럼 subtot = abap_true
4 handle_subtotal criteria 분기 → m_data 에 텍스트 할당

SUBTOTAL_TEXT 의 본질은 "소계 기준 컬럼을 숨기고(no_out) 소계를 켜면(subtot), SAP 가 소계 행 텍스트를 개발자에게 물어보고, 그때 m_data 에 원하는 글자를 넣는 것". 이벤트가 안 걸리면 십중팔구 no_out 누락이거나 숨긴 컬럼과 소계 컬럼이 다른 경우입니다. 두 조건만 맞추면 "Plane Type 747-400" 처럼 소계 행에 의미 있는 텍스트를 자유롭게 표시할 수 있습니다.


Disclaimer — 이 포스트는 실무 정리 노트를 바탕으로 AI 보조로 정리되었습니다.

CL_GUI_ALV_GRIDSUBTOTAL_TEXT 이벤트 시그니처(es_subtottxt_info·ep_subtot_line·e_event_data) 와 lvc_s_stxt·cl_alv_event_data 구조는 NetWeaver 표준 정의(ECC 6.0 / S/4HANA on-premise 기준) 입니다. 적용 환경 버전에 따라 일부 차이가 있을 수 있으니 실제 적용 시 SE24 의 클래스 정의를 확인하시기 바랍니다. SFLIGHT 는 SAP 표준 학습 환경(IDES) 테이블이라 운영 시스템에는 없을 수 있으니, 실무 적용 시 운영 테이블로 교체하시기 바랍니다.