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

[SAP ABAP] ALV 셀 편집 후 검증·자동 입력 — DATA_CHANGED 이벤트 (CL_ALV_CHANGED_DATA_PROTOCOL)

by Song.sh 2026. 6. 2.

편집 가능한 ALV 에서 사용자가 셀 값을 바꿨을 때 그 변화를 가로채서 검증하거나 다른 셀을 자동으로 채울 때 쓰는 이벤트가 DATA_CHANGED 입니다. "가격을 입력하면 통화가 자동으로 USD 로 채워지게" 같은 연동 입력이나 "음수 값은 안 됨" 같은 입력 검증을 ALV 안에서 처리할 수 있습니다.

 

핵심은 두 가지입니다. (1) register_edit_event 로 변경 인식 시점을 등록 해야 이벤트가 발생합니다(Enter / 셀 수정). (2) 이벤트 핸들러에서 CL_ALV_CHANGED_DATA_PROTOCOL 객체로 변경 셀 분석·다른 셀 수정·오류 메시지 등록 을 합니다.

 

이 글에서는 편집 ALV 구성 → 이벤트 시점 등록 → 변경 셀 분석 → 셀 자동 수정 → 오류 메시지 표시까지 정리합니다. 데이터는 SAP 스탠다드 학습 환경 테이블 SFLIGHT 의 CARRID·PRICE·CONNID 예시.

핵심 — DATA_CHANGED 이벤트의 짝

단계 설정 의미
1. 편집 가능 set_ready_for_input(1) + fcat edit = abap_true 사용자가 셀을 수정할 수 있게
2. 변경 시점 등록 register_edit_event(mc_evt_enter / mc_evt_modified) 언제 DATA_CHANGED 가 발생할지
3. 핸들러 등록 SET HANDLER ... data_changed FOR go_grid 변경 발생 시 받을 핸들러
4. 처리 er_data_changed(CL_ALV_CHANGED_DATA_PROTOCOL) 변경 셀 분석·수정·오류 등록

흐름을 그림으로 정리하면:

편집 ALV (set_ready_for_input + edit) + register_edit_event
        ↓
사용자가 셀 수정 (Enter / 포커스 이동)
        ↓
DATA_CHANGED 이벤트 → er_data_changed
        ↓
mt_mod_cells (변경된 셀 목록) 분석
        ↓
get_cell_value     : 같은 행 다른 컬럼 값 조회
modify_cell        : 다른 셀 값 자동 수정 (연동 입력)
add_protocol_entry : 오류 메시지 + 셀 빨간 표시 (검증 실패)

핵심 객체는 CL_ALV_CHANGED_DATA_PROTOCOL. 이 객체의 메소드 세 개(get_cell_value·modify_cell·add_protocol_entry) 가 검증·연동·오류 표시의 도구입니다.


1단계 — 이벤트 발생 시점: mc_evt_enter vs mc_evt_modified

DATA_CHANGED 는 그냥 발생하지 않습니다. register_edit_event 로 "언제" 발생시킬지 등록해야 합니다.

상수 발생 시점
cl_gui_alv_grid=>mc_evt_enter 사용자가 셀에 값을 넣고 Enter 를 누른 순간
cl_gui_alv_grid=>mc_evt_modified 셀이 수정되고 다른 셀로 포커스가 이동할 때 (Enter 안 쳐도)

두 개 다 등록해 두면 Enter 와 포커스 이동 양쪽에서 모두 이벤트가 발생합니다. 보통은 둘 다 등록합니다.

FORM set_event.
  event_receiver = NEW #( ).
  SET HANDLER event_receiver->handle_data_changed FOR go_grid.

* 편집 가능 모드
  go_grid->set_ready_for_input( i_ready_for_input = 1 ).

* 변경 인식 시점 등록 (둘 다)
  go_grid->register_edit_event( i_event_id = cl_gui_alv_grid=>mc_evt_enter ).
  go_grid->register_edit_event( i_event_id = cl_gui_alv_grid=>mc_evt_modified ).
ENDFORM.

register_edit_event 는 반드시 set_table_for_first_display 호출 전 에 해야 적용됩니다. ALV 가 화면에 그려진 뒤 등록하면 무시됩니다.


2단계 — 이벤트 핸들러 클래스

핸들러 시그니처는 단순합니다. er_data_changed 하나로 모든 변경 정보를 받습니다.

CLASS lcl_event DEFINITION.
  PUBLIC SECTION.
    METHODS handle_data_changed
      FOR EVENT data_changed OF cl_gui_alv_grid
      IMPORTING er_data_changed.
ENDCLASS.

CLASS lcl_event IMPLEMENTATION.
  METHOD handle_data_changed.
    PERFORM data_changed USING er_data_changed.
  ENDMETHOD.
ENDCLASS.

DATA event_receiver TYPE REF TO lcl_event.

er_data_changedCL_ALV_CHANGED_DATA_PROTOCOL 타입으로, 변경 정보를 담은 두 가지 테이블 속성을 가집니다.

속성 의미
mt_mod_cells 사용자가 수정한 모든 셀 목록 (검증 전)
mt_good_cells 그 중 SAP 의 1차 검증을 통과한 셀 목록

보통은 mt_mod_cells 를 분석해 추가 검증·연동을 합니다.


3단계 — 변경 분석: get_cell_value · modify_cell

핸들러 안에서 변경된 셀을 LOOP 으로 돌면서, 같은 행의 다른 컬럼 값을 조회하거나 다른 컬럼을 자동으로 수정합니다.

FORM data_changed USING pr_data_changed TYPE REF TO cl_alv_changed_data_protocol.

  DATA lv_price TYPE p_price.

* 변경된 모든 셀 LOOP
  LOOP AT pr_data_changed->mt_mod_cells INTO DATA(ls_mod_cell).

    CASE ls_mod_cell-fieldname.
*     CARRID 가 바뀌면 → PRICE 와 CONNID 도 자동 채움
      WHEN 'CARRID'.

*       같은 행의 PRICE 값 조회
        pr_data_changed->get_cell_value(
          EXPORTING
            i_row_id    = ls_mod_cell-row_id
            i_fieldname = 'CARRID'
          IMPORTING
            e_value     = DATA(lv_carrid) ).

*       항공사가 'AA' 이면 가격·항공편 자동 입력
        IF lv_carrid = 'AA'.
          lv_price = 1234.
          pr_data_changed->modify_cell(
            EXPORTING
              i_row_id    = ls_mod_cell-row_id
              i_fieldname = 'PRICE'
              i_value     = lv_price ).

          pr_data_changed->modify_cell(
            EXPORTING
              i_row_id    = ls_mod_cell-row_id
              i_fieldname = 'CONNID'
              i_value     = '0018' ).
        ENDIF.
    ENDCASE.

  ENDLOOP.
ENDFORM.

get_cell_value 는 변경된 행의 다른 컬럼 값 도 함께 읽을 수 있어 연동 검증에 유용하고, modify_cell사용자가 직접 입력하지 않은 셀도 자동으로 채울 수 있습니다.

메소드 용도
get_cell_value 특정 (row_id, fieldname) 셀의 현재(변경 후) 값을 읽음
modify_cell 특정 (row_id, fieldname) 셀 값을 새 값으로 변경

4단계 — 오류 메시지: add_protocol_entry

값이 유효하지 않을 때 셀에 빨간 오류 표시 와 메시지를 띄우려면 add_protocol_entry 를 씁니다. ALV 가 자체적으로 오류 셀을 마크하고 사용자에게 알립니다.

* 가격이 음수면 오류
pr_data_changed->add_protocol_entry(
  EXPORTING
    i_msgid     = 'ZXX_MSG'            " 메시지 클래스 (커스텀)
    i_msgty     = 'E'                  " 메시지 타입 (E=오류)
    i_msgno     = '000'                " 메시지 번호
    i_msgv1     = '가격은 양수여야 합니다'   " 메시지 내용
    i_fieldname = ls_mod_cell-fieldname  " 오류가 발생한 컬럼
    i_row_id    = ls_mod_cell-row_id ).  " 오류가 발생한 행

i_fieldnamei_row_id 로 위치를 알려주면 그 셀이 빨간색으로 표시되고, 사용자가 메시지를 확인하기 전까지는 다음 단계로 못 넘어갑니다. 표준 검증이 충분하지 않을 때 비즈니스 규칙 검증을 ALV 안에서 해결할 수 있는 강력한 도구입니다.


5단계 — check_changed_data: SAVE 직전 변경 인식 강제

사용자가 셀을 바꾸고 Enter 도 안 누른 채로 다른 동작(SAVE 버튼 클릭 등) 으로 넘어가면 DATA_CHANGED 이벤트가 발생하지 않을 수 있습니다. 이 때 check_changed_data 를 호출하면 ALV 가 미반영 변경을 강제로 인식해 DATA_CHANGED 를 발생시킵니다.

MODULE user_command_0100 INPUT.
  CASE ok_code.
    WHEN 'SAVE'.
*     SAVE 직전 — 미반영 변경을 강제로 인식
      CALL METHOD go_grid->check_changed_data.

*     이제 안전하게 저장 처리
      PERFORM save_data.
    ...
  ENDCASE.
ENDMODULE.

저장 직전 이 한 줄을 빠뜨리면, 사용자가 마지막 셀의 Enter 를 안 친 경우 변경이 누락되어 저장됩니다. 편집 ALV 의 SAVE 액션에는 거의 필수.


자주 빠뜨리는 함정

register_edit_event 위치 — display 전에

register_edit_event 는 반드시 set_table_for_first_display 호출 전에 해야 합니다. 화면이 그려진 후에 호출하면 무시되어 이벤트가 발생하지 않습니다.

편집 가능 모드 누락

set_ready_for_input(1) 과 fcat 의 edit = abap_true 가 없으면 셀이 편집 불가라 DATA_CHANGED 이벤트 자체가 발생할 일이 없습니다.

check_changed_data 누락

SAVE 직전 호출 안 하면 마지막 셀의 변경이 누락됩니다. 편집 ALV 의 모든 액션(저장·전송·다음 단계 등) 직전에 호출하는 게 표준 패턴입니다.

mt_mod_cells vs mt_good_cells

검증 통과한 셀만 보려면 mt_good_cells, 사용자가 시도한 모든 변경을 보려면 mt_mod_cells. 커스텀 검증은 mt_mod_cells 를 LOOP 하는 게 일반적입니다.

add_protocol_entry 의 fieldname·row_id 불일치

i_fieldname·i_row_id 를 빠뜨리면 메시지는 떠도 어느 셀이 문제인지 표시가 안 됩니다. 사용자가 빨간 셀을 못 찾으니 둘 다 채우는 게 사용성에 직결.

메시지 클래스 미존재

i_msgid 에 지정한 메시지 클래스(SE91 에 등록된 것) 가 없으면 런타임 오류. 메시지 번호도 같은 클래스 안에 존재해야 합니다. 간단히는 i_msgv1 에 메시지 내용을 직접 박아도 됩니다.


전체 코드 — 복사용 통합본

아래는 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_DATA_CHANGED  (메인 프로그램)
*&---------------------------------------------------------------------*
REPORT zrxx_alv_data_changed.

INCLUDE zrxx_alv_data_changed_t.     " TOP   - 전역 선언
INCLUDE zrxx_alv_data_changed_c.     " CLASS - 로컬 클래스
INCLUDE zrxx_alv_data_changed_scr.   " SCR   - 셀렉션 스크린
INCLUDE zrxx_alv_data_changed_o.     " PBO   - OUTPUT 모듈
INCLUDE zrxx_alv_data_changed_i.     " PAI   - INPUT 모듈
INCLUDE zrxx_alv_data_changed_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_DATA_CHANGED_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_DATA_CHANGED_C  (CLASS - 로컬 클래스)
*&---------------------------------------------------------------------*
CLASS lcl_event DEFINITION.
  PUBLIC SECTION.
    METHODS handle_data_changed
      FOR EVENT data_changed OF cl_gui_alv_grid
      IMPORTING er_data_changed.
ENDCLASS.

CLASS lcl_event IMPLEMENTATION.
  METHOD handle_data_changed.
    PERFORM data_changed USING er_data_changed.
  ENDMETHOD.
ENDCLASS.

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

*&---------------------------------------------------------------------*
*& INCLUDE ZRXX_ALV_DATA_CHANGED_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.                      " ★ display 전에 등록

    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 ).
  ENDIF.
ENDMODULE.

*&---------------------------------------------------------------------*
*& INCLUDE ZRXX_ALV_DATA_CHANGED_I  (PAI - INPUT 모듈)
*&---------------------------------------------------------------------*
MODULE user_command_0100 INPUT.
  CASE ok_code.
    WHEN 'SAVE'.
*     ★ 마지막 셀 변경 인식 강제
      CALL METHOD go_grid->check_changed_data.
*     이후 저장 로직 ...

    WHEN 'BACK' OR 'EXIT' OR 'CANC'.
      LEAVE TO SCREEN 0.
  ENDCASE.
ENDMODULE.

*&---------------------------------------------------------------------*
*& INCLUDE ZRXX_ALV_DATA_CHANGED_F  (FORM - 서브루틴)
*&---------------------------------------------------------------------*
* Field Catalog — 편집 가능 컬럼 지정
FORM set_fcat.
  CALL FUNCTION 'LVC_FIELDCATALOG_MERGE'
    EXPORTING
      i_structure_name = 'SFLIGHT'
    CHANGING
      ct_fieldcat      = gt_fcat.

  LOOP AT gt_fcat INTO DATA(ls_fcat).
    CASE ls_fcat-fieldname.
      WHEN 'CARRID' OR 'PRICE' OR 'CONNID'.
        ls_fcat-edit = abap_true.            " ★ 편집 가능
    ENDCASE.
    MODIFY gt_fcat FROM ls_fcat.
  ENDLOOP.
ENDFORM.

* ★ 이벤트 등록 + 편집 모드 + 시점 등록
FORM set_event.
  event_receiver = NEW #( ).
  SET HANDLER event_receiver->handle_data_changed FOR go_grid.

  go_grid->set_ready_for_input( i_ready_for_input = 1 ).

  go_grid->register_edit_event(
    i_event_id = cl_gui_alv_grid=>mc_evt_enter ).
  go_grid->register_edit_event(
    i_event_id = cl_gui_alv_grid=>mc_evt_modified ).
ENDFORM.

* ★ DATA_CHANGED 핸들러 — 변경 분석 + 자동 채움 + 오류 메시지
FORM data_changed USING pr_data_changed
                          TYPE REF TO cl_alv_changed_data_protocol.

  DATA lv_price  TYPE p_price.

  LOOP AT pr_data_changed->mt_mod_cells INTO DATA(ls_mod_cell).

    CASE ls_mod_cell-fieldname.

*     CARRID 가 'AA' 면 PRICE / CONNID 자동 입력
      WHEN 'CARRID'.
        pr_data_changed->get_cell_value(
          EXPORTING
            i_row_id    = ls_mod_cell-row_id
            i_fieldname = 'CARRID'
          IMPORTING
            e_value     = DATA(lv_carrid) ).

        IF lv_carrid = 'AA'.
          lv_price = 1234.
          pr_data_changed->modify_cell(
            EXPORTING
              i_row_id    = ls_mod_cell-row_id
              i_fieldname = 'PRICE'
              i_value     = lv_price ).

          pr_data_changed->modify_cell(
            EXPORTING
              i_row_id    = ls_mod_cell-row_id
              i_fieldname = 'CONNID'
              i_value     = '0018' ).
        ENDIF.

*     PRICE 가 음수면 오류 메시지
      WHEN 'PRICE'.
        pr_data_changed->get_cell_value(
          EXPORTING
            i_row_id    = ls_mod_cell-row_id
            i_fieldname = 'PRICE'
          IMPORTING
            e_value     = lv_price ).

        IF lv_price < 0.
          pr_data_changed->add_protocol_entry(
            EXPORTING
              i_msgid     = 'ZXX_MSG'
              i_msgty     = 'E'
              i_msgno     = '000'
              i_msgv1     = '가격은 양수여야 합니다'
              i_fieldname = ls_mod_cell-fieldname
              i_row_id    = ls_mod_cell-row_id ).
        ENDIF.

    ENDCASE.

  ENDLOOP.
ENDFORM.

요약

단계 설정/메소드 역할
1 set_ready_for_input + fcat edit 편집 가능 ALV
2 register_edit_event(mc_evt_enter / mc_evt_modified) 변경 인식 시점 등록
3 SET HANDLER data_changed 핸들러 연결
4 get_cell_value · modify_cell · add_protocol_entry 변경 분석 · 자동 채움 · 오류 표시
5 check_changed_data SAVE 직전 미반영 변경 강제 인식

DATA_CHANGED 의 본질은 "편집 ALV 의 셀 변화를 가로채서 같은 행의 다른 셀까지 연쇄 처리". register_edit_event 로 시점을 정하고, CL_ALV_CHANGED_DATA_PROTOCOL 의 세 메소드(get_cell_value·modify_cell·add_protocol_entry) 만 잘 다루면 됩니다. 저장 직전 check_changed_data 호출만 잊지 않으면 마지막 셀 변경 누락 없이 안전하게 처리할 수 있습니다.


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

CL_GUI_ALV_GRIDDATA_CHANGED 이벤트(er_data_changed) · register_edit_event · check_changed_data 메소드와 CL_ALV_CHANGED_DATA_PROTOCOLmt_mod_cells·mt_good_cells·get_cell_value·modify_cell·add_protocol_entry 는 NetWeaver 스탠다드 정의(ECC 6.0 / S/4HANA on-premise 기준) 입니다. 적용 환경 버전에 따라 일부 차이가 있을 수 있으니 실제 적용 시 SE24 의 클래스 정의를 확인하시기 바랍니다. SFLIGHT 는 SAP 스탠다드 학습 환경(IDES) 테이블입니다. 메시지 클래스 'ZXX_MSG'SE91 에 미리 등록된 가상 예시값이니, 실무 적용 시 운영 메시지 클래스로 교체하시기 바랍니다.