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

[SAP ABAP] ALV 스탠다드 버튼에 로직 끼워넣기 — BEFORE/AFTER USER_COMMAND 이벤트 (get_filtered_entries)

by Song.sh 2026. 5. 29.

ALV 의 USER_COMMAND 이벤트는 내가 직접 추가한 커스텀 버튼(예: 행추가, 행삭제) 의 클릭을 처리합니다. 그런데 정렬·필터·합계 같은 SAP 스탠다드 툴바 버튼의 동작 전후에 로직을 끼워넣고 싶을 때는 USER_COMMAND 로는 안 됩니다. 스탠다드 기능은 SAP 가 내부에서 처리하기 때문입니다.

 

이때 쓰는 것이 BEFORE_USER_COMMANDAFTER_USER_COMMAND 이벤트입니다. 이름 그대로 스탠다드 기능이 실행되기 직전(before)실행된 직후(after) 에 끼어들어, 사전 준비나 후처리를 할 수 있게 해줍니다. 대표적인 활용이 "사용자가 필터를 건 뒤, 화면에서 숨겨진 행의 데이터를 정리" 하는 후처리입니다.

 

이 글에서는 세 이벤트(USER_COMMAND / BEFORE / AFTER) 의 역할 차이 → 이벤트 등록 → 스탠다드 기능 전후 처리 → 필터 후 숨겨진 행 처리 실전 예시까지 정리합니다. 데이터는 SAP 스탠다드 학습 환경 테이블 SFLIGHT 예시.

핵심 — 세 이벤트의 역할 차이

이벤트 대상 버튼 발생 시점
USER_COMMAND 커스텀 버튼 (내가 추가) 커스텀 버튼 클릭 시
BEFORE_USER_COMMAND 스탠다드 버튼 (정렬·필터·합계 등) 스탠다드 기능 실행 직전
AFTER_USER_COMMAND 스탠다드 버튼 (정렬·필터·합계 등) 스탠다드 기능 실행 직후

같은 버튼 클릭에 대해 SAP 가 이벤트를 던지는 순서는 다음과 같습니다.

사용자가 스탠다드 툴바 버튼(예: 필터) 클릭
        ↓
1) BEFORE_USER_COMMAND  ← 스탠다드 처리 전 (사전 준비)
        ↓
2) SAP 스탠다드 기능 실행 (필터 적용)
        ↓
3) AFTER_USER_COMMAND   ← 스탠다드 처리 후 (후처리)

핵심 포인트는 스탠다드 버튼에 로직을 추가하고 싶을 때 이 두 이벤트를 쓴다 는 것입니다. 스탠다드 function code 는 & 로 시작합니다 (예: &FILTER, &SORT_ASC, &SUM).


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

세 이벤트의 시그니처를 비교하면 인자가 다릅니다.

CLASS lcl_receiver DEFINITION.
  PUBLIC SECTION.
* 스탠다드 기능 실행 직전
    METHODS handle_before_uc
      FOR EVENT before_user_command OF cl_gui_alv_grid
      IMPORTING e_ucomm.

* 스탠다드 기능 실행 직후
    METHODS handle_after_uc
      FOR EVENT after_user_command OF cl_gui_alv_grid
      IMPORTING e_ucomm e_saved e_not_processed.
ENDCLASS.
이벤트 인자
before_user_command e_ucomm (실행될 스탠다드 기능 코드)
after_user_command e_ucomm · e_saved(레이아웃 저장 여부) · e_not_processed(스탠다드 미처리 여부)

등록은 SET HANDLER 로 합니다.

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

2단계 — BEFORE_USER_COMMAND: 스탠다드 기능 직전 처리

스탠다드 기능이 실행되기 전에 끼어듭니다. e_ucomm 으로 어떤 스탠다드 기능인지 판단해, 사전 검증·상태 저장·로그 같은 준비 작업을 합니다.

FORM handle_before_uc USING p_ucomm TYPE sy-ucomm.
  CASE p_ucomm.
    WHEN '&INFO'.        " 정보 버튼 — 예: 클릭 로그 남기기
*     사전 처리 로직
    WHEN '&FILTER'.      " 필터 버튼 — 예: 현재 화면 상태 백업
*     필터 적용 전 현재 데이터 스냅샷 보관 등
  ENDCASE.
ENDFORM.

스탠다드 기능 실행 자체는 SAP 가 진행하고, BEFORE_USER_COMMAND 는 그 직전 "준비 시점" 을 제공합니다. 스탠다드 처리를 막거나 바꾸는 게 아니라, 그 앞에 한 단계 끼어드는 것입니다.


3단계 — AFTER_USER_COMMAND: 스탠다드 기능 직후 처리

스탠다드 기능이 끝난 뒤 끼어듭니다. 정렬·필터가 적용된 결과 상태를 기준으로 후처리할 수 있습니다.

FORM handle_after_uc USING p_ucomm         TYPE sy-ucomm
                           p_saved         TYPE char01
                           p_not_processed TYPE char01.
  CASE p_ucomm.
    WHEN '&FILTER'.
*     필터가 적용된 뒤의 후처리 (아래 4단계 참고)
      PERFORM after_filter.
  ENDCASE.
ENDFORM.

e_saved 는 레이아웃이 저장됐는지, e_not_processed 는 스탠다드 처리가 안 됐는지(예: 다른 핸들러가 가로챔) 여부를 알려줍니다. 보통 e_ucomm 으로 어떤 기능 직후인지 분기하는 게 핵심입니다.


4단계 — 실전: 필터 후 숨겨진 행 처리

가장 흔한 활용입니다. 사용자가 스탠다드 필터를 걸면 조건에 안 맞는 행이 화면에서 사라지는데, 데이터 자체(내부 테이블)에는 그대로 남아 있습니다. 이때 화면에서 숨겨진 행이 무엇인지 get_filtered_entries 로 알아내, 그 행들의 체크 상태나 표시 값을 정리할 수 있습니다.

FORM after_filter.
  DATA lt_filtered TYPE lvc_t_fidx.       " 필터로 숨겨진 행 인덱스 목록

* 필터로 화면에서 사라진 행들의 인덱스를 가져옴
  go_grid->get_filtered_entries(
    IMPORTING et_filtered_entries = lt_filtered ).

* 숨겨진 행의 체크 상태 정리 (예: 체크박스 해제)
  LOOP AT lt_filtered INTO DATA(lv_index).
    READ TABLE gt_data INTO DATA(ls_data) INDEX lv_index.
    IF sy-subrc = 0.
      CLEAR ls_data-checkbox.            " 숨겨진 행은 선택 해제
      MODIFY gt_data FROM ls_data INDEX lv_index.
    ENDIF.
  ENDLOOP.

  go_grid->refresh_table_display(
    EXPORTING is_stable = VALUE #( col = abap_true row = abap_true ) ).
ENDFORM.

get_filtered_entries 가 돌려주는 lvc_t_fidx 는 필터로 숨겨진 행의 인덱스 목록입니다. 이 인덱스로 내부 테이블에 접근해, 화면에 안 보이는 행이 선택된 채로 남아 잘못 처리되는 일을 막습니다. "전체 선택 후 필터" 같은 시나리오에서 숨겨진 행까지 처리되는 버그를 예방하는 핵심 패턴입니다.


자주 빠뜨리는 함정

스탠다드 버튼인데 USER_COMMAND 로 잡으려 함

정렬·필터·합계 같은 스탠다드 버튼은 USER_COMMAND 이벤트로 안 잡힙니다. USER_COMMAND 는 커스텀 버튼 전용이고, 스탠다드 버튼은 BEFORE/AFTER_USER_COMMAND 로 잡아야 합니다.

before 에서 스탠다드 처리를 막으려 함

BEFORE_USER_COMMANDe_ucomm 만 줄 뿐 스탠다드 처리를 막는 인자가 없습니다. "끼어들기" 용도이지 "차단" 용도가 아닙니다. 특정 기능을 막으려면 애초에 it_toolbar_excluding 로 그 버튼을 제외하는 게 맞습니다.

필터 후 get_filtered_entries 시점 오해

get_filtered_entries 는 필터가 적용된 후 에 의미가 있으므로 AFTER_USER_COMMAND 에서 호출해야 합니다. BEFORE 에서 부르면 아직 필터 전이라 숨겨진 행이 없습니다.

e_ucomm 분기 누락

AFTER_USER_COMMAND 는 모든 스탠다드 기능 실행 후 발생합니다. e_ucomm 으로 분기하지 않으면 정렬·합계 등 무관한 기능에도 후처리가 돌아 성능 낭비나 오작동이 생깁니다.

스탠다드 function code 를 모름

필터는 &FILTER, 정렬은 &SORT_ASC/&SORT_DSC 등 스탠다드 코드가 정해져 있습니다. 디버거에서 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_BEFORE_AFTER_UC  (메인 프로그램)
*&---------------------------------------------------------------------*
REPORT zrxx_alv_before_after_uc.

INCLUDE zrxx_alv_before_after_uc_t.     " TOP   - 전역 선언
INCLUDE zrxx_alv_before_after_uc_c.     " CLASS - 로컬 클래스
INCLUDE zrxx_alv_before_after_uc_scr.   " SCR   - 셀렉션 스크린
INCLUDE zrxx_alv_before_after_uc_o.     " PBO   - OUTPUT 모듈
INCLUDE zrxx_alv_before_after_uc_i.     " PAI   - INPUT 모듈
INCLUDE zrxx_alv_before_after_uc_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_BEFORE_AFTER_UC_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.

* SFLIGHT + 체크박스 컬럼
DATA: BEGIN OF gs_data.
        INCLUDE STRUCTURE sflight.
DATA:   checkbox TYPE c LENGTH 1,
      END OF gs_data,
      gt_data LIKE TABLE OF gs_data.

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

*&---------------------------------------------------------------------*
*& INCLUDE ZRXX_ALV_BEFORE_AFTER_UC_C  (CLASS - 로컬 클래스)
*&---------------------------------------------------------------------*
CLASS lcl_receiver DEFINITION.
  PUBLIC SECTION.
    METHODS handle_before_uc
      FOR EVENT before_user_command OF cl_gui_alv_grid
      IMPORTING e_ucomm.
    METHODS handle_after_uc
      FOR EVENT after_user_command OF cl_gui_alv_grid
      IMPORTING e_ucomm e_saved e_not_processed.
ENDCLASS.

CLASS lcl_receiver IMPLEMENTATION.
  METHOD handle_before_uc.
    PERFORM handle_before_uc USING e_ucomm.
  ENDMETHOD.
  METHOD handle_after_uc.
    PERFORM handle_after_uc USING e_ucomm e_saved e_not_processed.
  ENDMETHOD.
ENDCLASS.

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

*&---------------------------------------------------------------------*
*& INCLUDE ZRXX_ALV_BEFORE_AFTER_UC_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_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_BEFORE_AFTER_UC_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_BEFORE_AFTER_UC_F  (FORM - 서브루틴)
*&---------------------------------------------------------------------*
* Field Catalog — 체크박스 컬럼 편집 가능
FORM set_fcat.
  DATA ls_fcat TYPE lvc_s_fcat.

  gs_data = VALUE #( ).
  CALL FUNCTION 'LVC_FIELDCATALOG_MERGE'
    EXPORTING
      i_structure_name = 'SFLIGHT'
    CHANGING
      ct_fieldcat      = gt_fcat.

* 체크박스 컬럼 추가
  ls_fcat-fieldname = 'CHECKBOX'.
  ls_fcat-checkbox  = abap_true.
  ls_fcat-edit      = abap_true.
  ls_fcat-coltext   = '선택'.
  APPEND ls_fcat TO gt_fcat.
ENDFORM.

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

* ★ BEFORE_USER_COMMAND — 스탠다드 기능 직전
FORM handle_before_uc USING p_ucomm TYPE sy-ucomm.
  CASE p_ucomm.
    WHEN '&FILTER'.
*     필터 적용 전 사전 처리 (예: 현재 상태 백업)
  ENDCASE.
ENDFORM.

* ★ AFTER_USER_COMMAND — 스탠다드 기능 직후
FORM handle_after_uc USING p_ucomm         TYPE sy-ucomm
                           p_saved         TYPE char01
                           p_not_processed TYPE char01.
  CASE p_ucomm.
    WHEN '&FILTER'.
      PERFORM after_filter.
  ENDCASE.
ENDFORM.

* 필터 후 숨겨진 행 처리
FORM after_filter.
  DATA lt_filtered TYPE lvc_t_fidx.

  go_grid->get_filtered_entries(
    IMPORTING et_filtered_entries = lt_filtered ).

  LOOP AT lt_filtered INTO DATA(lv_index).
    READ TABLE gt_data INTO DATA(ls_data) INDEX lv_index.
    IF sy-subrc = 0.
      CLEAR ls_data-checkbox.
      MODIFY gt_data FROM ls_data INDEX lv_index.
    ENDIF.
  ENDLOOP.

  go_grid->refresh_table_display(
    EXPORTING is_stable = VALUE #( col = abap_true row = abap_true ) ).
ENDFORM.

요약

이벤트 용도 핵심
USER_COMMAND 커스텀 버튼 처리 내가 추가한 버튼의 function 분기
BEFORE_USER_COMMAND 스탠다드 기능 직전 hook 사전 준비 (e_ucomm 만)
AFTER_USER_COMMAND 스탠다드 기능 직후 hook 후처리 (get_filtered_entries 등)

BEFORE/AFTER_USER_COMMAND 의 본질은 "SAP 스탠다드 툴바 버튼의 동작 전후에 내 로직을 끼워넣는 hook". 커스텀 버튼은 USER_COMMAND, 스탠다드 버튼은 BEFORE/AFTER 로 잡는다는 구분만 명확하면 됩니다. 대표 활용인 "필터 후 get_filtered_entries 로 숨겨진 행 정리" 는 전체 선택·일괄 처리 화면에서 화면에 안 보이는 행이 잘못 처리되는 버그를 막는 실전 패턴입니다.


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

CL_GUI_ALV_GRIDBEFORE_USER_COMMAND(e_ucomm) · AFTER_USER_COMMAND(e_ucomm·e_saved·e_not_processed) 이벤트와 get_filtered_entries 메소드(lvc_t_fidx) 는 NetWeaver 스탠다드 정의(ECC 6.0 / S/4HANA on-premise 기준) 입니다. 적용 환경 버전에 따라 일부 차이가 있을 수 있으니 실제 적용 시 SE24 의 클래스 정의를 확인하시기 바랍니다. 스탠다드 function code(&FILTER 등) 도 버전에 따라 다를 수 있으니 디버거로 확인 후 사용하시기 바랍니다. SFLIGHT 는 SAP 스탠다드 학습 환경(IDES) 테이블입니다.