ALV 툴바에 버튼을 누르면 하위 항목이 드롭다운으로 펼쳐지는 메뉴 버튼을 만들고 싶을 때 쓰는 이벤트가 MENU_BUTTON 입니다. 버튼 하나에 여러 선택지(Choice 1·2·3) 를 묶어 화면을 깔끔하게 정리할 때 유용합니다.
핵심은 세 이벤트의 협력입니다. TOOLBAR 로 메뉴 버튼을 툴바에 그리고, MENU_BUTTON 으로 그 버튼을 눌렀을 때 펼쳐질 하위 메뉴를 채우며, 하위 항목을 선택하면 USER_COMMAND 가 그 선택을 처리합니다. TOOLBAR 이벤트와 세트로 움직인다고 생각하면 편합니다.
이 글에서는 세 이벤트의 협력 흐름 → 메뉴 버튼 추가 → 하위 메뉴 채우기(cl_ctmenu) → 선택 처리까지 정리합니다. 데이터는 SAP 스탠다드 학습 환경 테이블 SFLIGHT 예시.
핵심 — 세 이벤트의 협력
| 이벤트 | 역할 | 핵심 인자 |
|---|---|---|
TOOLBAR |
메뉴 버튼을 툴바에 그림 | e_object(cl_alv_event_toolbar_set) |
MENU_BUTTON |
버튼 클릭 시 하위 메뉴를 채움 | e_object(cl_ctmenu) · e_ucomm |
USER_COMMAND |
하위 항목 선택 처리 | e_ucomm (선택된 항목 fcode) |
흐름을 그림으로 정리하면:
1) TOOLBAR → 메뉴 버튼 추가 (butn_type=1, function='BTN_LIST')
↓ (사용자가 메뉴 버튼 클릭)
2) MENU_BUTTON → e_ucomm='BTN_LIST' 확인 → cl_ctmenu 에 하위 항목 add_function
('A'=Choice 1, 'B'=Choice 2, 'C'=Choice 3)
↓ (사용자가 하위 항목 선택)
3) USER_COMMAND → e_ucomm='A'/'B'/'C' → 선택 항목 처리
핵심은 버튼을 그리는 것(TOOLBAR), 펼치는 것(MENU_BUTTON), 처리하는 것(USER_COMMAND) 이 각각 다른 이벤트 라는 점입니다. 일반 버튼은 TOOLBAR + USER_COMMAND 둘이면 되지만, 드롭다운은 그 사이에 MENU_BUTTON 이 하나 더 끼어듭니다.
1단계 — 이벤트 핸들러 클래스 정의
세 이벤트를 받을 로컬 클래스를 정의합니다. MENU_BUTTON 의 시그니처가 핵심입니다.
CLASS lcl_event DEFINITION.
PUBLIC SECTION.
METHODS handle_toolbar
FOR EVENT toolbar OF cl_gui_alv_grid
IMPORTING e_object e_interactive.
METHODS handle_menu_button
FOR EVENT menu_button OF cl_gui_alv_grid
IMPORTING e_object e_ucomm.
METHODS handle_user_command
FOR EVENT user_command OF cl_gui_alv_grid
IMPORTING e_ucomm.
ENDCLASS.
CLASS lcl_event IMPLEMENTATION.
METHOD handle_toolbar.
PERFORM handle_toolbar USING e_object e_interactive.
ENDMETHOD.
METHOD handle_menu_button.
PERFORM handle_menu_button USING e_object e_ucomm.
ENDMETHOD.
METHOD handle_user_command.
PERFORM handle_user_command USING e_ucomm.
ENDMETHOD.
ENDCLASS.
DATA event_receiver TYPE REF TO lcl_event.
MENU_BUTTON 의 e_object 는 cl_ctmenu(컨텍스트 메뉴 객체) 타입이고, e_ucomm 은 클릭된 메뉴 버튼의 function 코드입니다. TOOLBAR 의 e_object(cl_alv_event_toolbar_set) 와는 타입이 다릅니다.
2단계 — TOOLBAR 이벤트: 메뉴 버튼 추가
툴바에 버튼을 추가하되, butn_type = 1(메뉴 버튼) 로 지정합니다. 일반 버튼은 butn_type = 0, 드롭다운 메뉴 버튼은 1 입니다.
FORM handle_toolbar USING p_object TYPE REF TO cl_alv_event_toolbar_set
p_interactive TYPE char01.
* 구분선
APPEND VALUE stb_button( butn_type = 3 ) TO p_object->mt_toolbar.
* 메뉴 버튼 (드롭다운) — butn_type = 1
APPEND VALUE stb_button(
function = 'BTN_LIST'
butn_type = 1
icon = icon_dropdown
text = '선택 메뉴' ) TO p_object->mt_toolbar.
ENDFORM.
여기서 정한 function = 'BTN_LIST' 가 MENU_BUTTON 이벤트의 e_ucomm 으로 넘어옵니다.
| butn_type | 버튼 종류 |
|---|---|
| 0 | 일반 버튼 (단순 클릭) |
| 1 | 메뉴 버튼 (드롭다운) — MENU_BUTTON 이벤트 발생 |
| 3 | 구분선 (separator) |
3단계 — MENU_BUTTON 이벤트: 하위 메뉴 채우기
메뉴 버튼을 클릭하면 MENU_BUTTON 이벤트가 발생합니다. e_object(cl_ctmenu) 의 add_function 으로 펼쳐질 항목을 추가합니다.
FORM handle_menu_button USING p_object TYPE REF TO cl_ctmenu
p_ucomm TYPE sy-ucomm.
* 드롭다운에 넣을 항목 목록
DATA(lt_drop) = VALUE lvc_t_dral(
( value = 'Choice 1' int_value = 'A' )
( value = 'Choice 2' int_value = 'B' )
( value = 'Choice 3' int_value = 'C' ) ).
CASE p_ucomm.
WHEN 'BTN_LIST'. " 2단계에서 정한 메뉴 버튼
LOOP AT lt_drop INTO DATA(ls_drop).
p_object->add_function(
fcode = CONV #( ls_drop-int_value ) " 선택 시 e_ucomm 이 될 값
text = CONV #( ls_drop-value ) ). " 화면에 보이는 텍스트
ENDLOOP.
ENDCASE.
ENDFORM.
add_function 의 fcode 가 항목 선택 시 USER_COMMAND 의 e_ucomm 으로 전달되고, text 는 메뉴에 표시되는 글자입니다. lvc_t_dral 은 드롭다운 항목을 담기 좋은 스탠다드 테이블 타입입니다.
4단계 — USER_COMMAND 이벤트: 선택 항목 처리
하위 항목을 선택하면 그 fcode 가 USER_COMMAND 의 e_ucomm 으로 넘어옵니다. 일반 버튼 처리와 똑같이 CASE 로 분기합니다.
FORM handle_user_command USING p_ucomm TYPE sy-ucomm.
CASE p_ucomm.
WHEN 'A'.
MESSAGE 'Choice 1 선택' TYPE 'I'.
WHEN 'B'.
MESSAGE 'Choice 2 선택' TYPE 'I'.
WHEN 'C'.
MESSAGE 'Choice 3 선택' TYPE 'I'.
ENDCASE.
ENDFORM.
즉 드롭다운 항목 선택은 결국 USER_COMMAND 로 귀결됩니다. MENU_BUTTON 은 "어떤 항목을 보여줄지" 를 정하는 단계이고, 실제 동작은 USER_COMMAND 가 맡습니다.
등록은 세 이벤트를 모두 SET HANDLER 합니다.
FORM set_event.
event_receiver = NEW #( ).
SET HANDLER event_receiver->handle_toolbar FOR go_grid.
SET HANDLER event_receiver->handle_menu_button FOR go_grid.
SET HANDLER event_receiver->handle_user_command FOR go_grid.
ENDFORM.
자주 빠뜨리는 함정
butn_type 미지정 — 드롭다운이 안 됨
버튼을 butn_type = 0(일반) 으로 만들면 MENU_BUTTON 이벤트가 발생하지 않습니다. 드롭다운이 필요하면 반드시 butn_type = 1 로 지정해야 합니다.
menu_button 핸들러 미등록
handle_menu_button 을 SET HANDLER 안 하면 메뉴 버튼을 눌러도 하위 항목이 비어 있습니다. TOOLBAR·MENU_BUTTON·USER_COMMAND 세 개를 모두 등록해야 완성됩니다.
e_object 타입 혼동
TOOLBAR 의 e_object 는 cl_alv_event_toolbar_set, MENU_BUTTON 의 e_object 는 cl_ctmenu 입니다. FORM 파라미터 타입을 헷갈리면 메소드(add_function) 호출에서 오류가 납니다.
add_function 의 fcode 와 USER_COMMAND 분기 불일치
add_function( fcode = 'A' ) 로 만들고 USER_COMMAND 에서 WHEN 'CHOICE_A' 로 받으면 안 잡힙니다. fcode 와 분기 값이 정확히 같아야 합니다.
매번 add_function 누적
MENU_BUTTON 이벤트는 메뉴 버튼을 누를 때마다 발생합니다. e_object(cl_ctmenu) 는 클릭 시점마다 새로 전달되므로 누적 걱정은 없지만, 조건에 따라 항목을 다르게 보여주려면 e_ucomm 으로 분기해서 그때그때 채웁니다.
전체 코드 — 복사용 통합본
아래는 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_MENU_BUTTON (메인 프로그램)
*&---------------------------------------------------------------------*
REPORT zrxx_alv_menu_button.
INCLUDE zrxx_alv_menu_button_t. " TOP - 전역 선언
INCLUDE zrxx_alv_menu_button_c. " CLASS - 로컬 클래스
INCLUDE zrxx_alv_menu_button_scr. " SCR - 셀렉션 스크린
INCLUDE zrxx_alv_menu_button_o. " PBO - OUTPUT 모듈
INCLUDE zrxx_alv_menu_button_i. " PAI - INPUT 모듈
INCLUDE zrxx_alv_menu_button_f. " FORM - 서브루틴
START-OF-SELECTION.
SELECT * UP TO 20 ROWS
INTO CORRESPONDING FIELDS OF TABLE gt_data
FROM sflight.
CALL SCREEN 100.
*&---------------------------------------------------------------------*
*& INCLUDE ZRXX_ALV_MENU_BUTTON_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.
DATA: gs_data TYPE sflight,
gt_data TYPE TABLE OF sflight.
DATA event_receiver TYPE REF TO lcl_event. " C INCLUDE 의 클래스 참조
*&---------------------------------------------------------------------*
*& INCLUDE ZRXX_ALV_MENU_BUTTON_C (CLASS - 로컬 클래스)
*&---------------------------------------------------------------------*
CLASS lcl_event DEFINITION.
PUBLIC SECTION.
METHODS handle_toolbar
FOR EVENT toolbar OF cl_gui_alv_grid
IMPORTING e_object e_interactive.
METHODS handle_menu_button
FOR EVENT menu_button OF cl_gui_alv_grid
IMPORTING e_object e_ucomm.
METHODS handle_user_command
FOR EVENT user_command OF cl_gui_alv_grid
IMPORTING e_ucomm.
ENDCLASS.
CLASS lcl_event IMPLEMENTATION.
METHOD handle_toolbar.
PERFORM handle_toolbar USING e_object e_interactive.
ENDMETHOD.
METHOD handle_menu_button.
PERFORM handle_menu_button USING e_object e_ucomm.
ENDMETHOD.
METHOD handle_user_command.
PERFORM handle_user_command USING e_ucomm.
ENDMETHOD.
ENDCLASS.
*&---------------------------------------------------------------------*
*& INCLUDE ZRXX_ALV_MENU_BUTTON_SCR (SCR - 셀렉션 스크린)
*&---------------------------------------------------------------------*
* 이 글은 셀렉션 스크린(SELECT-OPTIONS/PARAMETERS) 을 쓰지 않습니다.
* Screen 100 은 SE51 에서 정의 (Docking 사용, Custom Control 없음).
* 셀렉션 조건이 필요하면 여기에 SELECT-OPTIONS 를 선언.
*&---------------------------------------------------------------------*
*& INCLUDE ZRXX_ALV_MENU_BUTTON_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_event.
gs_layo-zebra = abap_true.
gs_layo-cwidth_opt = abap_true.
go_grid->set_table_for_first_display(
EXPORTING
i_structure_name = 'SFLIGHT'
is_layout = gs_layo
CHANGING
it_outtab = gt_data
it_fieldcatalog = gt_fcat ).
ENDIF.
ENDMODULE.
*&---------------------------------------------------------------------*
*& INCLUDE ZRXX_ALV_MENU_BUTTON_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_MENU_BUTTON_F (FORM - 서브루틴)
*&---------------------------------------------------------------------*
* 이벤트 등록
FORM set_event.
event_receiver = NEW #( ).
SET HANDLER event_receiver->handle_toolbar FOR go_grid.
SET HANDLER event_receiver->handle_menu_button FOR go_grid.
SET HANDLER event_receiver->handle_user_command FOR go_grid.
ENDFORM.
* ★ TOOLBAR 이벤트: 메뉴 버튼 추가 (butn_type=1)
FORM handle_toolbar USING p_object TYPE REF TO cl_alv_event_toolbar_set
p_interactive TYPE char01.
APPEND VALUE stb_button( butn_type = 3 ) TO p_object->mt_toolbar.
APPEND VALUE stb_button( function = 'BTN_LIST'
butn_type = 1
icon = icon_dropdown
text = '선택 메뉴' ) TO p_object->mt_toolbar.
ENDFORM.
* ★ MENU_BUTTON 이벤트: 하위 메뉴 채우기
FORM handle_menu_button USING p_object TYPE REF TO cl_ctmenu
p_ucomm TYPE sy-ucomm.
DATA(lt_drop) = VALUE lvc_t_dral(
( value = 'Choice 1' int_value = 'A' )
( value = 'Choice 2' int_value = 'B' )
( value = 'Choice 3' int_value = 'C' ) ).
CASE p_ucomm.
WHEN 'BTN_LIST'.
LOOP AT lt_drop INTO DATA(ls_drop).
p_object->add_function(
fcode = CONV #( ls_drop-int_value )
text = CONV #( ls_drop-value ) ).
ENDLOOP.
ENDCASE.
ENDFORM.
* ★ USER_COMMAND 이벤트: 선택 항목 처리
FORM handle_user_command USING p_ucomm TYPE sy-ucomm.
CASE p_ucomm.
WHEN 'A'.
MESSAGE 'Choice 1 선택' TYPE 'I'.
WHEN 'B'.
MESSAGE 'Choice 2 선택' TYPE 'I'.
WHEN 'C'.
MESSAGE 'Choice 3 선택' TYPE 'I'.
ENDCASE.
ENDFORM.

요약
| 단계 | 이벤트 | 핵심 |
|---|---|---|
| 1 | TOOLBAR |
stb_button( butn_type = 1 ) 메뉴 버튼 추가 |
| 2 | MENU_BUTTON |
cl_ctmenu->add_function 하위 항목 채움 |
| 3 | USER_COMMAND |
CASE e_ucomm 선택 항목 처리 |
MENU_BUTTON 의 본질은 "드롭다운 메뉴 버튼의 하위 항목을 cl_ctmenu->add_function 으로 채우는 것". 버튼 그리기는 TOOLBAR(butn_type=1), 항목 채우기는 MENU_BUTTON, 선택 처리는 USER_COMMAND 가 나눠 맡는 3단 구조만 이해하면 됩니다. 선택지가 많은 기능을 버튼 하나로 묶어 툴바를 깔끔하게 정리할 때 유용합니다.
Disclaimer — 이 포스트는 실무 정리 노트를 바탕으로 AI 보조로 정리되었습니다.
CL_GUI_ALV_GRID 의 MENU_BUTTON(e_object·e_ucomm) · TOOLBAR · USER_COMMAND 이벤트와 cl_ctmenu 의 add_function 메소드, stb_button 구조는 NetWeaver 스탠다드 정의(ECC 6.0 / S/4HANA on-premise 기준) 입니다. 적용 환경 버전에 따라 일부 차이가 있을 수 있으니 실제 적용 시 SE24 의 클래스 정의를 확인하시기 바랍니다. SFLIGHT 는 SAP 스탠다드 학습 환경(IDES) 테이블이라 운영 시스템에는 없을 수 있으니, 실무 적용 시 운영 테이블로 교체하시기 바랍니다.