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 클래스 인터페이스를 확인하시기 바랍니다.