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.
PLANETYPE 에 no_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_display 의 it_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_GRID 의 SUBTOTAL_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) 테이블이라 운영 시스템에는 없을 수 있으니, 실무 적용 시 운영 테이블로 교체하시기 바랍니다.