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

[SAP ABAP] ALV 체크박스 전체 선택 — TOOLBAR 이벤트로 select 버튼 추가 + USER_COMMAND 로 LOOP·REFRESH 5단계

by Song.sh 2026. 5. 27.

ALV 그리드에 체크박스 컬럼을 만들고 사용자가 라인별로 선택하게 하는 화면은 흔합니다. 그런데 데이터가 많을 때 한 행씩 체크하는 건 비효율적이라, 사용자들이 가장 먼저 요청하는 기능이 “전체 선택 / 전체 해제 버튼”. ALV 도구 모음에 작은 버튼 하나만 추가하면 한 번 클릭으로 모든 행의 체크박스를 켜고 끌 수 있습니다.

 

구현의 핵심은 두 가지 — (1) ALV 도구 모음에 사용자 정의 버튼 “select” 를 추가하는 TOOLBAR 이벤트 핸들러, (2) 버튼 클릭을 받아 gt_data 의 box 컬럼을 일괄 ON 한 뒤 ALV 새로고침을 거는 USER_COMMAND 이벤트 핸들러. SAP 표준 ALV 의 두 이벤트만 연결하면 끝입니다.

 

이 글에서는 데이터 구조 선언부터 Field Catalog 의 체크박스 설정, 이벤트 핸들러 클래스, 도구 모음 버튼 추가, 클릭 시 전체 선택 로직, 그리고 변경 후 화면 새로고침까지 5단계로 정리합니다.

핵심 — 전체 선택 기능 5단계

단계 작업 핵심 키워드
1 데이터 구조에 box 컬럼 추가 box TYPE char01
2 Field Catalog 에서 box 컬럼 체크박스 처리 gs_fcat-checkbox = abap_true + edit = abap_true
3 이벤트 핸들러 클래스 정의 + SET HANDLER FOR EVENT toolbar / user_command OF cl_gui_alv_grid
4 TOOLBAR 이벤트에서 'select' 버튼 추가 APPEND stb_button TO p_object->mt_toolbar
5 USER_COMMAND 에서 LOOP + REFRESH gs_data-box = abap_true + refresh_table_display

핵심 그림은 단순 — 도구 모음에 select 버튼 추가 → 클릭하면 gt_data 전체 LOOP 돌면서 box 컬럼 ON → ALV 새로고침. 이 흐름이 ALV 이벤트 두 개(TOOLBAR + USER_COMMAND) 안에 깔끔하게 들어갑니다.


1단계 — 데이터 구조에 box 컬럼 추가

체크박스로 쓸 컬럼을 ALV 데이터 구조의 맨 앞 에 char01 타입으로 둡니다. 비즈니스 컬럼들 사이에 박혀 있으면 사용자 입장에서 찾기 힘드므로 위치는 항상 가장 왼쪽.

DATA: BEGIN OF gs_data,
        box   TYPE char01,
        ebeln TYPE ekko-ebeln,
        bstyp TYPE ekko-bstyp,
        bsart TYPE ekko-bsart,
      END OF gs_data,
      gt_data LIKE TABLE OF gs_data.

이 글에서는 구매오더 헤더(EKKO) 의 PO 번호·문서 유형·문서 종류를 예시로 사용. 실무에서는 자기 비즈니스 컬럼으로 바꿔 쓰면 됩니다.


2단계 — Field Catalog 에서 box 컬럼 체크박스 처리

Field Catalog 자동 생성 후, 그중 BOX 컬럼만 “체크박스 + 편집 가능” 으로 표시.

IF gt_fcat IS NOT INITIAL.

  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(gs_fcat).
    CASE gs_fcat-fieldname.
      WHEN 'BOX'.
        gs_fcat-checkbox = abap_true.    " 체크박스 모양
        gs_fcat-edit     = abap_true.    " 사용자가 직접 클릭 가능
        gs_fcat-col_opt  = abap_true.    " 컬럼 폭 자동 최적화
        gs_fcat-coltext  = '체크박스'.
    ENDCASE.
    MODIFY gt_fcat FROM gs_fcat.
  ENDLOOP.

ENDIF.

핵심 3가지 — checkbox = abap_true 가 “네모 박스 모양” 으로 표시, edit = abap_true 가 사용자 클릭 허용. 둘 다 켜야 “화면에 체크박스로 보이고, 클릭으로 ON/OFF 가능” 상태가 됩니다.


3단계 — 이벤트 핸들러 클래스 정의

ALV 의 TOOLBAR · USER_COMMAND 이벤트를 받을 로컬 클래스. 보통 한 화면에 여러 이벤트(더블클릭·데이터 변경 등) 가 같이 묶이므로 모두 한 클래스에 모아둡니다.

CLASS lcl_event_receiver DEFINITION.

  PUBLIC SECTION.
    METHODS handle_double_click FOR EVENT double_click OF cl_gui_alv_grid
      IMPORTING e_row e_column es_row_no sender.

    METHODS handle_toolbar      FOR EVENT toolbar OF cl_gui_alv_grid
      IMPORTING e_object e_interactive.

    METHODS handle_usercommand  FOR EVENT user_command OF cl_gui_alv_grid
      IMPORTING e_ucomm.

    METHODS handle_datachanged  FOR EVENT data_changed OF cl_gui_alv_grid
      IMPORTING er_data_changed e_ucomm.

ENDCLASS.

CLASS lcl_event_receiver IMPLEMENTATION.

  METHOD handle_double_click.
    CASE sender.
      WHEN g_grid.   DATA(ls_ekpo) = gt_ekpo[ e_row-index ].
      WHEN g_grid2.  DATA(ls_data) = gt_data[ e_row-index ].
    ENDCASE.
  ENDMETHOD.

  METHOD handle_toolbar.
    PERFORM handle_toolbar USING e_object e_interactive.
  ENDMETHOD.

  METHOD handle_usercommand.
    PERFORM handle_usercommand USING e_ucomm.
  ENDMETHOD.

  METHOD handle_datachanged.
    PERFORM handle_datachanged USING er_data_changed e_ucomm.
  ENDMETHOD.

ENDCLASS.

DATA lo_class TYPE REF TO lcl_event_receiver.

각 METHOD 안에서 PERFORM 으로 외부 FORM 을 호출하는 패턴 — 이벤트 메서드는 “라우터” 역할만 하고, 실제 로직은 FORM 으로 분리해야 가독성이 좋아집니다.

SET HANDLER 등록

FORM create_event.
  lo_class = NEW #( ).

* 여러 그리드에 같은 이벤트를 쓰고 싶을 때 FOR ALL INSTANCES
  SET HANDLER lo_class->handle_double_click FOR ALL INSTANCES.

* 특정 그리드 전용
  SET HANDLER lo_class->handle_toolbar     FOR g_grid2.
  SET HANDLER lo_class->handle_usercommand FOR g_grid2.
  SET HANDLER lo_class->handle_datachanged FOR g_grid2.

* 편집 가능 + Enter/수정 이벤트 등록
  g_grid2->set_ready_for_input(
    EXPORTING
      i_ready_for_input = 1 ).

  g_grid2->register_edit_event(
    EXPORTING
      i_event_id = cl_gui_alv_grid=>mc_evt_enter ).

  g_grid2->register_edit_event(
    EXPORTING
      i_event_id = cl_gui_alv_grid=>mc_evt_modified ).
ENDFORM.

set_ready_for_input(1) 이 빠지면 사용자가 체크박스 클릭해도 box 컬럼 값이 업데이트되지 않으니 빠뜨리지 말 것.


4단계 — TOOLBAR 이벤트에서 'select' 버튼 추가

TOOLBAR 이벤트는 ALV 가 도구 모음을 그릴 때마다 호출됩니다. 여기서 stb_button 구조체를 만들어 p_object->mt_toolbar 에 APPEND 하면 도구 모음 끝에 사용자 버튼이 생성됩니다.

FORM handle_toolbar USING p_object      TYPE REF TO cl_alv_event_toolbar_set
                          p_interactive TYPE char01.

  DATA(ls_toolbar) = VALUE stb_button(
    function  = 'select'         " USER_COMMAND 에서 받을 식별자
    icon      = icon_abap        " 아이콘 (회사 표준 ICON_* 상수)
    quickinfo = 'select all'     " 마우스 오버 시 툴팁
    text      = 'select'         " 버튼에 표시될 라벨
    disabled  = '' ).            " 비활성화 안 함

  APPEND ls_toolbar TO p_object->mt_toolbar.

ENDFORM.

핵심은 function 의 문자열. 이 값을 다음 단계 USER_COMMAND 이벤트에서 분기 키로 사용합니다.

추가 옵션 — 토글 vs 분리 버튼

전체 선택만 필요하면 select 1개로 충분하지만, 전체 선택 + 전체 해제 를 분리하려면 두 버튼을 APPEND.

" 전체 선택
DATA(ls_btn1) = VALUE stb_button(
  function  = 'select_all'
  icon      = icon_okay
  quickinfo = '전체 선택'
  text      = '전체 선택'
  disabled  = '' ).
APPEND ls_btn1 TO p_object->mt_toolbar.

" 전체 해제
DATA(ls_btn2) = VALUE stb_button(
  function  = 'deselect_all'
  icon      = icon_cancel
  quickinfo = '전체 해제'
  text      = '전체 해제'
  disabled  = '' ).
APPEND ls_btn2 TO p_object->mt_toolbar.

USER_COMMAND 에서 function 별로 분기하면 됩니다.


5단계 — USER_COMMAND 에서 일괄 LOOP + REFRESH

select 버튼 클릭 시 발생하는 USER_COMMAND 이벤트에서 gt_data 를 LOOP 돌며 box 컬럼을 ON 으로 일괄 변경한 뒤 ALV 새로고침.

FORM handle_usercommand USING p_ucomm TYPE sy-ucomm.

  IF p_ucomm = 'select'.

    LOOP AT gt_data INTO gs_data.
      CLEAR gs_data-box.
      gs_data-box = abap_true.
      MODIFY gt_data FROM gs_data TRANSPORTING box.
    ENDLOOP.

  ENDIF.

* ALV 새로고침 — 사용자 정렬/필터 유지하려면 stable 옵션
  DATA(ls_stbl) = VALUE lvc_s_stbl(
    row = 'X'
    col = 'X' ).

  g_grid2->refresh_table_display(
    EXPORTING
      is_stable = ls_stbl
*   EXCEPTIONS
*     finished = 1
*     others   = 2
   ).

  IF sy-subrc <> 0.
*   필요 시 에러 처리
  ENDIF.

ENDFORM.

핵심 3가지:

  • MODIFY ... TRANSPORTING box — 다른 컬럼은 안 건드리고 box 만 갱신. 성능과 데이터 보호 동시 확보
  • lvc_s_stbl 의 row='X' col='X' — 사용자가 ALV 에서 걸어둔 정렬/필터를 새로고침 후에도 유지. 이게 없으면 새로고침 후 화면이 초기 상태로 돌아가서 사용자가 짜증
  • refresh_table_display 호출 — 이게 없으면 메모리의 gt_data 만 바뀌고 화면은 그대로

전체 해제 분기

전체 해제 버튼을 추가했다면 같은 FORM 에 분기 한 줄 추가.

IF p_ucomm = 'select_all'.
  LOOP AT gt_data INTO gs_data.
    gs_data-box = abap_true.
    MODIFY gt_data FROM gs_data TRANSPORTING box.
  ENDLOOP.

ELSEIF p_ucomm = 'deselect_all'.
  LOOP AT gt_data INTO gs_data.
    CLEAR gs_data-box.
    MODIFY gt_data FROM gs_data TRANSPORTING box.
  ENDLOOP.

ENDIF.

화면 결과

위 5단계를 마치면 ALV 의 도구 모음 끝에 “select” 버튼이 추가되고, 왼쪽 첫 컬럼에 “체크박스” 컬럼이 표시됩니다. 사용자가 select 버튼 한 번 누르면 모든 행의 체크박스가 ON 으로 일괄 변경 — 그 다음 비즈니스 로직(예: 선택 라인 일괄 승인/송신/삭제) 은 LOOP AT gt_data WHERE box = abap_true 로 가져가서 처리하면 됩니다.

[도구 모음]
  [표준 ALV 버튼들...] [정보] [select ✓]

[그리드]
  체크박스 | 구매 문서      | C | 유형
  ☑       | 4500000001     | F | NB
  ☑       | 4500000002     | F | NB
  ☑       | 4500000003     | F | NB
  ...

자주 빠뜨리는 함정

  • set_ready_for_input(1) 누락 — 편집 가능 모드가 아니면 사용자가 체크박스 클릭해도 값이 안 변함. ALV 표시 전에 반드시 호출
  • register_edit_event 누락 — Enter/Modified 이벤트 등록 안 하면 data_changed 가 발생 안 함. 체크 상태가 즉시 반영 안 되는 경우 의심
  • MODIFY ... TRANSPORTING 누락 — TRANSPORTING 없이 그냥 MODIFY gt_data FROM gs_data 면 모든 컬럼이 덮어쓰기됨. 다른 컬럼의 데이터 손실 위험
  • refresh_table_display 누락 — 메모리만 바뀌고 화면 그대로. 사용자는 변경 안 된 것처럼 느낌
  • is_stable 미설정 — row='X' col='X' 없이 새로고침하면 사용자가 걸어둔 정렬/필터/스크롤 위치가 초기화됨. 매번 첫 행부터 다시 봐야 해서 짜증
  • function 문자열 충돌 — 'select' 같이 너무 일반적인 이름은 SAP 표준 ALV 함수와 충돌 가능. 회사 표준은 보통 ZSEL_ALL·ZDESEL_ALL 같은 prefix 사용
  • handle_datachanged 와 충돌 — 체크박스 한 행 클릭 시 발생하는 data_changed 이벤트에서 별도 로직을 돌리고 있다면, select 버튼의 일괄 변경이 data_changed 를 행 수만큼 발생시킬 수 있음. 일괄 처리 모드 플래그를 두고 분기

요약

단계 코드 위치 핵심 키워드
1 데이터 구조 선언 box TYPE char01 컬럼 추가 (맨 앞)
2 Field Catalog LOOP checkbox + edit + coltext
3 lcl_event_receiver + create_event SET HANDLER + set_ready_for_input + register_edit_event
4 handle_toolbar FORM stb_button 채워 mt_toolbar 에 APPEND
5 handle_usercommand FORM LOOP gt_data → box ON → refresh_table_display(stable)

ALV 체크박스 전체 선택의 본질은 “TOOLBAR 이벤트로 사용자 버튼 추가 → USER_COMMAND 이벤트로 일괄 LOOP + REFRESH” 두 이벤트의 협력 한 줄. 5단계 패턴만 익혀두면 같은 구조로 전체 해제·짝수 행만 선택·특정 조건 행만 선택 같은 변형도 모두 만들 수 있고, 단가 일괄 변경·일괄 승인 같은 다른 일괄 처리 UI 패턴의 골격으로 그대로 재활용 가능합니다.


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

CL_GUI_ALV_GRID 의 이벤트 처리 메커니즘과 refresh_table_display 동작은 NetWeaver 버전(ECC 6.0 / S/4HANA on-premise · Cloud) 에 따라 일부 차이가 있을 수 있으니, 실제 적용 시에는 해당 시스템의 ALV 클래스 인터페이스를 확인하시기 바랍니다.