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

[SAP ABAP] 가벼운 카테고리 트리 + ALV 마스터-디테일 — SIMPLE TREE (MTREESNODE + add_nodes)

by Song.sh 2026. 6. 4.

계층 데이터를 트리로 보여주되 데이터 컬럼 없이 순수 텍스트 노드만 필요할 때 쓰는 가벼운 클래스가 CL_GUI_SIMPLE_TREE 입니다. ALV Tree(CL_GUI_ALV_TREE) 와 달리 오른쪽에 데이터 컬럼이 붙지 않고, 노드 클릭 시 다른 ALV 그리드에 상세 데이터를 띄우는 마스터-디테일 화면 의 왼쪽 트리로 자주 씁니다.

 

핵심은 두 가지입니다. (1) MTREESNODE 구조체로 노드 한 행씩 인터널 테이블에 쌓아두고, (2) add_nodes 메소드 한 번 호출로 전체 노드를 화면에 그립니다. ALV Tree 가 노드 하나씩 ADD_NODE 호출하는 방식이라면, SIMPLE TREE 는 테이블 일괄 등록 방식.

이 글에서는 컨테이너 2개(왼쪽 트리 / 오른쪽 ALV) 구성 → MTREESNODE 노드 테이블 채우기 → add_nodes 호출 → 이벤트(node_double_click) 등록 → 클릭 시 ALV 갱신까지 정리합니다. 데이터는 SAP 스탠다드 학습 환경 SCARR(항공사 마스터) + SFLIGHT(항공편) 마스터-디테일 예시.

핵심 — SIMPLE TREE 의 노드 구조

SIMPLE TREE 의 각 노드는 MTREESNODE(SEU_TREE_CONTROL 패키지의 스탠다드 구조체) 한 행으로 표현됩니다. 키와 부모 키로 계층을 형성하고, 텍스트·아이콘·스타일 등 표시 속성을 함께 담습니다.

필드 의미
node_key 노드 고유 키 (이벤트 발생 시 어느 노드인지 식별)
relatkey 부모 노드 키. 루트는 공백
isfolder 'X' 면 폴더(자식 가질 수 있음) / 공백이면 leaf
text 화면에 표시할 텍스트
expander 'X' 면 처음에 펼친 상태로 시작
n_image 노드 아이콘 (예: '@10@') — leaf 노드의 아이콘만 적용
style 텍스트 강조 — cl_gui_simple_tree=>style_emphasized
disabled 'X' 면 선택 불가 (회색)

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

[ 화면 100 ]
┌────────────────────┬───────────────────────────────┐
│ TREE_CONTAINER     │ CON1 (ALV 그리드)             │
│ ▼ AIRPLANE         │                               │
│   ▼ CARRID         │  CARRID/CONNID/FLDATE/...     │
│     • LH           │  LH / 0400 / 19950228 / ...   │
│     • AA           │                               │
│     • AZ           │                               │
└────────────────────┴───────────────────────────────┘

         더블클릭 LH
            ↓
SELECT * FROM sflight WHERE carrid = 'LH' INTO TABLE gt_sflight.
            ↓
go_grid->refresh_table_display( )
            ↓
오른쪽 ALV 가 LH 항공편만 표시

ALV Tree 와 가장 큰 차이를 정리하면:

항목 SIMPLE TREE ALV TREE
클래스 CL_GUI_SIMPLE_TREE CL_GUI_ALV_TREE
데이터 컬럼 없음 (텍스트만) 있음 (DDIC 구조 기반)
노드 추가 MTREESNODE 테이블 + add_nodes 일괄 ADD_NODE 한 번씩
이벤트 node_double_click 등 (node_key 전달) ALV 이벤트 풀세트 + 트리 이벤트
적합 시나리오 마스터-디테일의 왼쪽 카테고리 트리 계층 + 컬럼 데이터를 한 곳에 보여줘야 할 때

1단계 — 화면 + 컨테이너 2개

SE51 의 Screen 100 에 Custom Control 두 개를 배치합니다. 왼쪽은 트리용 TREE_CONTAINER, 오른쪽은 ALV 그리드용 CON1. 코드에서는 각각 CL_GUI_CUSTOM_CONTAINER 인스턴스를 만들어 자식 컨트롤(트리 / 그리드) 의 부모로 사용합니다.

* TOP INCLUDE
TYPES : node_table_type LIKE TABLE OF mtreesnode.

DATA : tree_con_ref TYPE REF TO cl_gui_custom_container,
       g_tree       TYPE REF TO cl_gui_simple_tree.

DATA : con1_ref TYPE REF TO cl_gui_custom_container,
       go_grid  TYPE REF TO cl_gui_alv_grid.

DATA : gt_sflight LIKE TABLE OF sflight,
       ok_code    LIKE sy-ucomm.

node_table_typeMTREESNODE 의 STANDARD TABLE 타입. add_nodes 에 그대로 넘기는 인자이므로 한 번 선언해 두면 편합니다.


2단계 — ALV 그리드 생성 (오른쪽 패널)

먼저 오른쪽 ALV 부터 만들어 둡니다. 처음엔 빈 테이블이지만 트리 클릭 이벤트가 발생하면 SELECT 후 refresh_table_display 로 채웁니다.

MODULE create_container OUTPUT.
  CHECK con1_ref IS INITIAL.

  CREATE OBJECT con1_ref
    EXPORTING container_name = 'CON1'.

  CREATE OBJECT go_grid
    EXPORTING i_parent = con1_ref.

  CALL METHOD go_grid->set_table_for_first_display
    EXPORTING
      i_structure_name = 'SFLIGHT'
    CHANGING
      it_outtab        = gt_sflight.
ENDMODULE.

i_structure_name = 'SFLIGHT' 만 박으면 컬럼 카탈로그 자동 생성. 빈 gt_sflight 도 OK — 이후 데이터만 채우면 됩니다.


3단계 — 트리 생성 + 노드 테이블 + add_nodes

핵심 단계. 컨테이너 / 트리 객체를 만들고 build_node_table 폼에서 노드를 인터널 테이블에 쌓은 뒤 add_nodes 한 번으로 화면에 표시.

MODULE create_tree OUTPUT.
  CHECK g_tree IS INITIAL.

  DATA : node_table TYPE node_table_type.

  CREATE OBJECT tree_con_ref
    EXPORTING container_name = 'TREE_CONTAINER'.

  CREATE OBJECT g_tree
    EXPORTING
      parent              = tree_con_ref
      node_selection_mode = cl_gui_simple_tree=>node_sel_mode_single.

  PERFORM build_node_table USING node_table.

  CALL METHOD g_tree->add_nodes
    EXPORTING
      table_structure_name = 'MTREESNODE'
      node_table           = node_table.
ENDMODULE.

table_structure_name = 'MTREESNODE' 인자는 노드 테이블의 행 타입을 ALV 에게 알려주는 용도. 항상 'MTREESNODE' 고정 (대문자 문자열 리터럴).

노드 테이블 채우기 — 두 가지 방식

(a) 수동 — 노드 한 줄씩 직접 박기. 데이터가 정해진 카테고리 트리(예: PO 유형, 모듈 분류) 에 유용.

FORM build_node_table USING p_node_table TYPE node_table_type.
  DATA : node LIKE mtreesnode.

* 루트
  CLEAR node.
  node-node_key = 'Root'.
  node-isfolder = 'X'.
  node-text     = 'AIRPLANE'.
  APPEND node TO p_node_table.

* 그룹 (CARRID)
  CLEAR node.
  node-node_key = 'Child1'.
  node-relatkey = 'Root'.
  node-isfolder = 'X'.
  node-text     = 'CARRID'.
  node-expander = 'X'.                                       " 처음부터 펼친 상태
  APPEND node TO p_node_table.
ENDFORM.

(b) 자동 — SELECT 후 LOOP 으로 동적 추가. 항공사 마스터(SCARR) 처럼 DB 에서 카테고리 목록을 가져올 때.

* 위 두 노드 추가 후 이어서
DATA : gs_carr TYPE scarr,
       gt_carr LIKE TABLE OF gs_carr.

SELECT * FROM scarr INTO TABLE @gt_carr.

LOOP AT gt_carr INTO gs_carr.
  CLEAR node.
  node-node_key = gs_carr-carrid.       " CARRID 자체를 키로
  node-relatkey = 'Child1'.             " CARRID 그룹의 자식
  node-text     = gs_carr-carrid.
  APPEND node TO p_node_table.
ENDLOOP.

이 두 방식은 섞어 쓸 수 있습니다. 위처럼 루트 / 그룹은 수동으로 박고, 그 아래 데이터 노드는 LOOP 으로 자동 추가하는 게 가장 일반적.


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

SIMPLE TREE 의 이벤트는 두 단계로 등록합니다. (1) 어떤 이벤트를 받을지 set_registered_events 로 등록, (2) 그 이벤트의 핸들러 메소드를 SET HANDLER 로 연결.

핸들러 클래스 정의

CLASS lcl_application DEFINITION.
  PUBLIC SECTION.
    METHODS handle_node_double_click
      FOR EVENT node_double_click OF cl_gui_simple_tree
      IMPORTING node_key.
ENDCLASS.

CLASS lcl_application IMPLEMENTATION.
  METHOD handle_node_double_click.
    PERFORM handle_node_double_click USING node_key.
  ENDMETHOD.
ENDCLASS.

DATA : lcl_application TYPE REF TO lcl_application.

FOR EVENT node_double_click OF cl_gui_simple_tree 시그니처는 고정. IMPORTING node_key 로 사용자가 더블클릭한 노드의 키가 자동으로 들어옵니다.

이벤트 등록 모듈

MODULE event_tree OUTPUT.
  DATA : event  TYPE cntl_simple_event,
         events TYPE cntl_simple_events.

* 1) 받을 이벤트 목록
  event-eventid    = cl_gui_simple_tree=>eventid_node_double_click.
  event-appl_event = 'X'.                                  " 애플리케이션 이벤트
  APPEND event TO events.

  CALL METHOD g_tree->set_registered_events
    EXPORTING
      events                    = events
    EXCEPTIONS
      cntl_error                = 1
      cntl_system_error         = 2
      illegal_event_combination = 3.

* 2) 핸들러 인스턴스 생성 + 연결
  CREATE OBJECT lcl_application.
  SET HANDLER lcl_application->handle_node_double_click FOR g_tree.
ENDMODULE.

appl_event = 'X' 는 "이 이벤트는 ABAP 백엔드에서 받겠다" 는 표시. 안 박으면 시스템 이벤트로 처리되어 핸들러가 안 불립니다.

클릭 시 ALV 갱신

FORM handle_node_double_click USING p_node_key.
  SELECT *
    INTO CORRESPONDING FIELDS OF TABLE gt_sflight
    FROM sflight
    WHERE carrid = p_node_key.

  PERFORM refresh_grid.
ENDFORM.

FORM refresh_grid.
  IF go_grid IS BOUND.
    go_grid->refresh_table_display( ).
  ENDIF.
ENDFORM.

CARRID 노드의 node_key 를 CARRID 자체로 박아뒀으니 WHERE carrid = p_node_key 로 바로 필터링. 키 설계가 이벤트 처리를 결정합니다.


자주 빠뜨리는 함정

node_key 중복

같은 키를 두 번 APPEND 하면 첫 번째만 등록되고 두 번째는 무시되거나 덤프. node_key 는 전체 트리 안에서 unique. CARRID 자체로 키를 박을 때는 SELECT 결과가 중복되지 않게 SELECT DISTINCT 또는 SORT + DELETE ADJACENT DUPLICATES 후 LOOP.

relatkey 오타

부모 키와 자식의 relatkey 가 한 글자라도 다르면 그 자식 노드는 어디에도 안 붙고 사라진 것처럼 보입니다. 대소문자 포함 정확히 일치해야.

appl_event = 'X' 누락

set_registered_events 호출 시 event-appl_event = 'X' 가 없으면 시스템 이벤트로 처리되어 ABAP 핸들러가 불리지 않습니다. SIMPLE TREE 이벤트는 거의 항상 애플리케이션 이벤트.

CHECK ... IS INITIAL 누락

PBO 모듈은 매 화면 진입 시 실행되므로 CHECK g_tree IS INITIAL 같은 가드 없이 add_nodes 를 반복하면 같은 노드가 누적됩니다. 처음 한 번만 트리를 만들고 이후엔 갱신 함수로.

Custom Container 이름 — 대문자

container_name = 'tree_container' 처럼 소문자로 박으면 SE51 의 이름과 안 맞아 컨트롤이 안 잡힙니다. 대문자 문자열로 일치시키기.

isfolder vs leaf

isfolder = 'X' 를 박은 노드는 자식을 가질 수 있는 폴더로 표시. leaf 노드(자식 없음) 인데 isfolder 를 박으면 펼치기 화살표가 보이는데 안 펼쳐지는 어색한 화면이 됩니다.

node_image 는 leaf 전용

n_image(노드 아이콘) 은 isfolder='X' 인 폴더 노드에는 적용되지 않습니다. 폴더는 표준 폴더 아이콘 고정. leaf 노드의 아이콘만 커스텀할 수 있습니다.


전체 코드 — 복사용 통합본

화면(CALL SCREEN) 이 있는 모듈풀 구조라 SAP 스탠다드 INCLUDE 6분할(T / C / SCR / O / I / F) 로 구성합니다. SE51 에서 Screen 100 의 Custom Container 2개(TREE_CONTAINER·CON1) + Flow Logic 에 status_0100 · create_container · create_tree · event_tree (PBO) · user_command_0100 (PAI).

*&---------------------------------------------------------------------*
*& Report ZRXX_ALV_SIMPLE_TREE
*&---------------------------------------------------------------------*
REPORT zrxx_alv_simple_tree.

INCLUDE zrxx_alv_simple_tree_t.     " TOP   - 전역 선언
INCLUDE zrxx_alv_simple_tree_c.     " CLASS - 이벤트 핸들러 클래스
INCLUDE zrxx_alv_simple_tree_scr.   " SCR   - 셀렉션 스크린 (사용 안 함)
INCLUDE zrxx_alv_simple_tree_o.     " PBO   - OUTPUT 모듈
INCLUDE zrxx_alv_simple_tree_i.     " PAI   - INPUT 모듈
INCLUDE zrxx_alv_simple_tree_f.     " FORM  - 서브루틴

START-OF-SELECTION.
  CALL SCREEN 100.

*&---------------------------------------------------------------------*
*& INCLUDE ZRXX_ALV_SIMPLE_TREE_T  (TOP)
*&---------------------------------------------------------------------*
TYPES : node_table_type LIKE TABLE OF mtreesnode.

DATA : tree_con_ref TYPE REF TO cl_gui_custom_container,
       g_tree       TYPE REF TO cl_gui_simple_tree.

DATA : con1_ref TYPE REF TO cl_gui_custom_container,
       go_grid  TYPE REF TO cl_gui_alv_grid.

DATA : gt_sflight LIKE TABLE OF sflight,
       ok_code    LIKE sy-ucomm.

DATA : lcl_application TYPE REF TO lcl_application.

*&---------------------------------------------------------------------*
*& INCLUDE ZRXX_ALV_SIMPLE_TREE_C  (CLASS)
*&---------------------------------------------------------------------*
CLASS lcl_application DEFINITION.
  PUBLIC SECTION.
    METHODS handle_node_double_click
      FOR EVENT node_double_click OF cl_gui_simple_tree
      IMPORTING node_key.
ENDCLASS.

CLASS lcl_application IMPLEMENTATION.
  METHOD handle_node_double_click.
    PERFORM handle_node_double_click USING node_key.
  ENDMETHOD.
ENDCLASS.

*&---------------------------------------------------------------------*
*& INCLUDE ZRXX_ALV_SIMPLE_TREE_SCR  (SCR)
*&---------------------------------------------------------------------*
* (이 글에서는 셀렉션 스크린 사용 안 함)
* Screen 100 은 SE51 에서 정의 — Custom Container 두 개:
*   - 'TREE_CONTAINER' (왼쪽, SIMPLE TREE)
*   - 'CON1'           (오른쪽, ALV GRID)

*&---------------------------------------------------------------------*
*& INCLUDE ZRXX_ALV_SIMPLE_TREE_O  (PBO)
*&---------------------------------------------------------------------*
MODULE status_0100 OUTPUT.
  SET PF-STATUS 'S100'.
ENDMODULE.

MODULE create_container OUTPUT.
  CHECK con1_ref IS INITIAL.

  CREATE OBJECT con1_ref
    EXPORTING container_name = 'CON1'.

  CREATE OBJECT go_grid
    EXPORTING i_parent = con1_ref.

  CALL METHOD go_grid->set_table_for_first_display
    EXPORTING
      i_structure_name = 'SFLIGHT'
    CHANGING
      it_outtab        = gt_sflight.
ENDMODULE.

MODULE create_tree OUTPUT.
  CHECK g_tree IS INITIAL.

  DATA : node_table TYPE node_table_type.

  CREATE OBJECT tree_con_ref
    EXPORTING container_name = 'TREE_CONTAINER'.

  CREATE OBJECT g_tree
    EXPORTING
      parent              = tree_con_ref
      node_selection_mode = cl_gui_simple_tree=>node_sel_mode_single.

  PERFORM build_node_table USING node_table.

  CALL METHOD g_tree->add_nodes
    EXPORTING
      table_structure_name = 'MTREESNODE'
      node_table           = node_table.
ENDMODULE.

MODULE event_tree OUTPUT.
* PBO 마다 새로 등록되지 않도록 한 번만
  CHECK lcl_application IS INITIAL.

  DATA : event  TYPE cntl_simple_event,
         events TYPE cntl_simple_events.

  event-eventid    = cl_gui_simple_tree=>eventid_node_double_click.
  event-appl_event = 'X'.
  APPEND event TO events.

  CALL METHOD g_tree->set_registered_events
    EXPORTING
      events                    = events
    EXCEPTIONS
      cntl_error                = 1
      cntl_system_error         = 2
      illegal_event_combination = 3.

  CREATE OBJECT lcl_application.
  SET HANDLER lcl_application->handle_node_double_click FOR g_tree.
ENDMODULE.

*&---------------------------------------------------------------------*
*& INCLUDE ZRXX_ALV_SIMPLE_TREE_I  (PAI)
*&---------------------------------------------------------------------*
MODULE user_command_0100 INPUT.
  CASE ok_code.
    WHEN 'BACK' OR 'EXIT' OR 'CANC'.
      LEAVE TO SCREEN 0.
  ENDCASE.
ENDMODULE.

*&---------------------------------------------------------------------*
*& INCLUDE ZRXX_ALV_SIMPLE_TREE_F  (FORM)
*&---------------------------------------------------------------------*
FORM build_node_table USING p_node_table TYPE node_table_type.
  DATA : node LIKE mtreesnode.

* 루트 — AIRPLANE
  CLEAR node.
  node-node_key = 'Root'.
  node-isfolder = 'X'.
  node-text     = 'AIRPLANE'.
  APPEND node TO p_node_table.

* 그룹 — CARRID 폴더
  CLEAR node.
  node-node_key = 'Child1'.
  node-relatkey = 'Root'.
  node-isfolder = 'X'.
  node-text     = 'CARRID'.
  node-expander = 'X'.
  APPEND node TO p_node_table.

* 자동 추가 — SCARR 의 CARRID 목록을 leaf 노드로
  DATA : gs_carr TYPE scarr,
         gt_carr LIKE TABLE OF gs_carr.

  SELECT * FROM scarr INTO TABLE @gt_carr.

  LOOP AT gt_carr INTO gs_carr.
    CLEAR node.
    node-node_key = gs_carr-carrid.
    node-relatkey = 'Child1'.
    node-text     = gs_carr-carrid.
    APPEND node TO p_node_table.
  ENDLOOP.
ENDFORM.

* 더블클릭 처리 — 클릭한 CARRID 의 SFLIGHT 만 ALV 에 표시
FORM handle_node_double_click USING p_node_key.
  SELECT *
    INTO CORRESPONDING FIELDS OF TABLE gt_sflight
    FROM sflight
    WHERE carrid = p_node_key.

  PERFORM refresh_grid.
ENDFORM.

FORM refresh_grid.
  IF go_grid IS BOUND.
    go_grid->refresh_table_display( ).
  ENDIF.
ENDFORM.

요약

단계 위치 핵심
1 TOP + Screen 컨테이너 2개(TREE_CONTAINER·CON1) + TYPES node_table_type LIKE TABLE OF mtreesnode
2 ALV 그리드 CL_GUI_ALV_GRID + SET_TABLE_FOR_FIRST_DISPLAY(빈 테이블 OK)
3 트리 + 노드 CL_GUI_SIMPLE_TREE + MTREESNODE 노드 테이블 + add_nodes( table_structure_name = 'MTREESNODE' )
4 이벤트 eventid_node_double_click + appl_event='X' + set_registered_events + SET HANDLER
5 디테일 갱신 핸들러 → SELECT WHERE carrid = node_keygo_grid->refresh_table_display( )

SIMPLE TREE 의 본질은 "MTREESNODE 한 테이블에 노드를 다 쌓아 add_nodes 로 일괄 등록 + node_double_click 이벤트로 클릭한 키를 받아 처리". node_key + relatkey 로 계층을 만들고, appl_event = 'X' 와 핸들러 클래스만 잘 짝지으면 마스터-디테일 화면이 만들어집니다. ALV Tree 와 달리 데이터 컬럼이 없어 가볍고, 카테고리 선택용 사이드 패널로 활용도가 높습니다.


Disclaimer — 이 포스트는 실무 정리 노트를 바탕으로 AI 보조로 정리되었습니다. CL_GUI_SIMPLE_TREE(SEU_TREE_CONTROL 패키지) · MTREESNODE(구조) · CNTL_SIMPLE_EVENT/CNTL_SIMPLE_EVENTS(이벤트 구조) · 메소드 ADD_NODES·SET_REGISTERED_EVENTS 와 이벤트 ID eventid_node_double_click 및 모드 상수 node_sel_mode_single·style_emphasized·relat_last_child 는 NetWeaver 스탠다드 정의(ECC 6.0 / S/4HANA on-premise 기준) 입니다. SCARR·SFLIGHT 는 SAP 스탠다드 학습 환경(IDES) 테이블이며 실무 적용 시 운영 테이블로 교체하시기 바랍니다.