한 화면에 ALV 두 개를 위/아래 또는 좌/우로 나란히 보여주고 싶을 때 가장 빠르게 만들 수 있는 컨테이너가 CL_GUI_EASY_SPLITTER_CONTAINER 입니다. 이름 그대로 "쉬운(Easy) Splitter" — 일반 CL_GUI_SPLITTER_CONTAINER 가 행/열 N개로 자유 분할이라면, EASY 는 항상 2개 영역으로만 나뉘는 경량 버전.
장점은 코드가 짧다는 것. orientation(영역 배치 방향) 과 sash_position(분할 비율) 두 파라미터만 정해주면 두 영역이 만들어지고, 각 영역에 ALV Grid 를 하나씩 붙이면 끝. PO Header(EKKO) + PO Item(EKPO) 같은 "마스터 + 디테일" 구조의 화면을 가장 빠르게 짤 때 쓰는 표준 패턴입니다.
이 글에서는 Easy Splitter 의 컨테이너 계층 구조 → 5단계 생성 흐름 → orientation/sash_position 파라미터 → top_left vs bottom_right 컨테이너 접근 → 마스터/디테일 동기화 → 전체 코드 통합본까지 정리합니다. 데이터는 SAP 표준 EKKO(구매오더 헤더) + EKPO(구매오더 라인) 예시.
핵심 — Easy Splitter 의 컨테이너 계층
| 계층 | 클래스 | 역할 |
|---|---|---|
| 1. 화면 영역 | SE51 Custom Control 'AREA' | Screen 100 에 미리 정의된 화면 위치 |
| 2. 부모 컨테이너 | cl_gui_custom_container |
'AREA' 와 ABAP 객체 연결 |
| 3. Splitter | cl_gui_easy_splitter_container |
부모 컨테이너를 2개 영역으로 분할 |
| 4. ALV Grid x 2 | cl_gui_alv_grid 두 개 |
각각 splitter 의 top_left / bottom_right 자식 컨테이너에 배치 |
그림으로 정리하면:
Screen 100
└ Custom Control 'AREA'
└ g_container (cl_gui_custom_container)
└ g_splitter (cl_gui_easy_splitter_container)
├ top_left_container ← g_grid (위쪽/왼쪽 ALV)
└ bottom_right_container ← g_grid2 (아래쪽/오른쪽 ALV)
부모 컨테이너 1개를 받아 2개로 나누고, 각 영역에 ALV 를 붙이는 단순 구조. 일반 SPLITTER 처럼 ROWS·COLUMNS 파라미터로 N분할하는 게 아니라 무조건 2분할만 가능 — 그 점이 "Easy" 의 의미.
1단계 — SE51 Screen 100 에 Custom Control 'AREA' 배치
[1] T-Code SE51 (또는 SE38 의 화면 페인터 진입)
[2] Screen 100 의 Layout 편집
[3] 도구 모음의 "Custom Control" 아이콘 클릭 후 화면에 드래그
└ 사이즈는 분할할 영역 크기 만큼
[4] Custom Control 의 Name 을 'AREA' 로 지정 (또는 회사 표준 이름)
[5] 활성화 + 메인 프로그램 PBO 모듈 연결
여기서 정한 'AREA' 라는 이름이 ABAP 코드에서 container_name = 'AREA' 로 연결됩니다.
2단계 — 데이터 + 객체 참조 변수 선언
* 컨테이너 + Splitter + Grid 두 개
DATA: g_container TYPE REF TO cl_gui_custom_container,
g_splitter TYPE REF TO cl_gui_easy_splitter_container,
g_grid TYPE REF TO cl_gui_alv_grid,
g_grid2 TYPE REF TO cl_gui_alv_grid.
* Screen ok_code
DATA: ok_code TYPE sy-ucomm.
* Field Catalog 두 개 (ALV 두 개니까)
DATA: gt_fcat TYPE lvc_t_fcat,
gt_fcat2 TYPE lvc_t_fcat.
* PO 헤더 데이터 (위쪽 ALV)
DATA: BEGIN OF gs_data,
ebeln TYPE ekko-ebeln,
bstyp TYPE ekko-bstyp,
bsart TYPE ekko-bsart,
END OF gs_data,
gt_data LIKE TABLE OF gs_data.
* PO 라인 데이터 (아래쪽 ALV)
DATA: gt_ekpo TYPE TABLE OF ekpo,
gs_ekpo LIKE LINE OF gt_ekpo.
Grid 두 개, Field Catalog 두 개, 데이터 테이블 두 개. 분할이 2개라 모든 게 "쌍" 으로 묶입니다.
3단계 — START-OF-SELECTION → 데이터 조회 → CALL SCREEN
START-OF-SELECTION.
* 위쪽 ALV 데이터 — EKKO
SELECT * INTO CORRESPONDING FIELDS OF TABLE gt_data
FROM ekko.
* 아래쪽 ALV 데이터 — EKPO
SELECT * INTO CORRESPONDING FIELDS OF TABLE gt_ekpo
FROM ekpo.
CALL SCREEN 100.
데이터를 먼저 채운 뒤 화면 100 호출. 화면 PBO 가 돌면서 컨테이너와 그리드가 그려집니다.
4단계 — PBO 모듈에서 컨테이너 + Splitter + 두 그리드 생성
PBO 의 핵심 모듈은 "컨테이너 한 번만 만들기" 패턴.
MODULE status_0100 OUTPUT.
SET PF-STATUS 'S100'.
ENDMODULE.
MODULE create_container OUTPUT.
IF g_container IS INITIAL. " 첫 PBO 에서만 1회 생성
PERFORM create_container. " 컨테이너 + Splitter + Grid 생성
PERFORM create_fcat. " 두 Grid 의 Field Catalog 작성
PERFORM register_event. " 더블클릭 이벤트 등록
PERFORM display. " set_table_for_first_display
ENDIF.
ENDMODULE.
PBO 가 여러 번 도는 화면에서 매번 컨테이너를 새로 만들면 메모리 누수 + 화면 깜빡임이 발생하므로 "g_container IS INITIAL 일 때만" 가드.
컨테이너 생성 FORM — Easy Splitter 의 핵심
FORM create_container.
* 1) 부모 Custom Container 생성 — SE51 의 'AREA' 와 연결
g_container = NEW #( container_name = 'AREA' ).
* 2) Easy Splitter 생성 — 'AREA' 를 2분할
g_splitter = NEW #(
parent = g_container " 부모 컨테이너
orientation = 0 " 0=VERTICAL(위/아래), 1=HORIZONTAL(좌/우)
sash_position = 30 ). " 위쪽 30%, 아래쪽 70%
* 3) Grid 두 개를 splitter 의 자식 컨테이너에 배치
* orientation=0 (VERTICAL) 기준:
* top_left_container = 위쪽 영역 (PO Header)
* bottom_right_container = 아래쪽 영역 (PO Item)
g_grid = NEW #( i_parent = g_splitter->top_left_container ). " 위쪽
g_grid2 = NEW #( i_parent = g_splitter->bottom_right_container ). " 아래쪽
ENDFORM.
핵심 3가지:
parent— Splitter 가 분할할 부모 컨테이너orientation— 0=ORIENTATION_VERTICAL(위/아래), 1=ORIENTATION_HORIZONTAL(좌/우)sash_position— 분할 비율(%). 예: 30 = 첫 영역이 30%, 나머지가 70%
orientation 과 자식 컨테이너 매핑
Splitter 의 자식 컨테이너 속성 이름이 top_left_container · bottom_right_container 인데, 한 속성 이름이 orientation 값에 따라 두 가지 의미를 가집니다.
| orientation | SAP 상수 | 영역 배치 | top_left_container | bottom_right_container |
|---|---|---|---|---|
| 0 | ORIENTATION_VERTICAL |
위/아래로 쌓임 (분할선 가로) | 위쪽 영역 | 아래쪽 영역 |
| 1 | ORIENTATION_HORIZONTAL |
좌/우로 배치 (분할선 세로) | 왼쪽 영역 | 오른쪽 영역 |
SAP 상수 이름이 "두 영역이 배치되는 방향" 기준이라 직관적으로 헷갈리기 쉽습니다. ORIENTATION_VERTICAL 은 두 영역이 세로(위→아래) 로 쌓인다는 의미고, 분할선은 가로 방향. PO 헤더(EKKO) + PO 라인(EKPO) 마스터-디테일에서는 위쪽 헤더 + 아래쪽 라인 배치가 자연스러우니 orientation=0 을 씁니다. 컬럼이 많아 가로 스크롤이 부담되면 orientation=1 로 좌/우 배치.
5단계 — Field Catalog 생성 + 이벤트 등록 + ALV 표시
두 Grid 의 Field Catalog 를 각각 자동 생성하고, 위쪽 ALV 의 더블클릭 이벤트를 등록한 뒤 화면 표시.
FORM create_fcat.
* 위쪽 ALV (gt_data) 의 Field Catalog
IF gt_data 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.
ENDIF.
* 아래쪽 ALV (gt_ekpo) 의 Field Catalog
IF gt_ekpo IS NOT INITIAL.
DATA lr_str2 TYPE REF TO cl_abap_structdescr.
lr_str2 ?= cl_abap_structdescr=>describe_by_data( gs_ekpo ).
DATA(lt_fcat2) = cl_salv_data_descr=>read_structdescr( lr_str2 ).
gt_fcat2 = CORRESPONDING #( lt_fcat2 MAPPING ref_table = reftable
ref_field = reffield ).
CALL FUNCTION 'LVC_FIELDCAT_COMPLETE'
CHANGING
ct_fieldcat = gt_fcat2.
ENDIF.
ENDFORM.
FORM register_event.
CREATE OBJECT g_event_receiver.
SET HANDLER g_event_receiver->handle_double_click FOR g_grid.
ENDFORM.
FORM display.
g_grid->set_table_for_first_display(
CHANGING
it_fieldcatalog = gt_fcat
it_outtab = gt_data ). " 위쪽 ALV — PO 헤더
g_grid2->set_table_for_first_display(
CHANGING
it_fieldcatalog = gt_fcat2
it_outtab = gt_ekpo ). " 아래쪽 ALV — PO 라인
ENDFORM.
두 그리드 모두 표시되면 사용자가 한 화면에서 PO 헤더 + 라인을 동시에 보는 마스터-디테일 뷰가 완성됩니다.
마스터-디테일 동기화 패턴
Easy Splitter 로 두 ALV 를 만들고 끝이 아니라, 보통 "위쪽 ALV 의 라인을 더블클릭하면 아래쪽 ALV 가 그 PO 의 라인만 보여주도록" 동기화하는 패턴이 같이 따라옵니다.
* 이벤트 핸들러 클래스
CLASS lcl_event_receiver DEFINITION.
PUBLIC SECTION.
METHODS handle_double_click
FOR EVENT double_click OF cl_gui_alv_grid
IMPORTING e_row sender.
ENDCLASS.
CLASS lcl_event_receiver IMPLEMENTATION.
METHOD handle_double_click.
DATA: lt_filtered LIKE gt_ekpo.
IF sender = g_grid. " 위쪽 헤더 ALV 더블클릭
DATA(ls_data) = gt_data[ e_row-index ].
* 해당 PO 의 라인만 필터링
LOOP AT gt_ekpo INTO DATA(ls_ekpo) WHERE ebeln = ls_data-ebeln.
APPEND ls_ekpo TO lt_filtered.
ENDLOOP.
* 아래쪽 라인 ALV 갱신
g_grid2->set_table_for_first_display(
CHANGING
it_fieldcatalog = gt_fcat2
it_outtab = lt_filtered ).
ENDIF.
ENDMETHOD.
ENDCLASS.
DATA g_event_receiver TYPE REF TO lcl_event_receiver.
핸들러 안에서는 위쪽 그리드(g_grid) 의 더블클릭만 처리하고, 선택된 PO 의 EBELN 으로 EKPO 라인을 필터링한 결과를 아래쪽 그리드(g_grid2) 에 다시 표시. 이벤트 핸들러 등록 방식은 "[SAP ABAP] ALV 체크박스 전체 선택" 글에서 다룬 lcl_event_receiver 패턴과 동일합니다.
비교 — Easy Splitter vs 일반 Splitter
| 구분 | CL_GUI_EASY_SPLITTER_CONTAINER | CL_GUI_SPLITTER_CONTAINER |
|---|---|---|
| 분할 수 | 항상 2개 | ROWS x COLUMNS 자유 (예: 2x2, 3x1) |
| 자식 접근 | 속성 top_left_container / bottom_right_container |
메서드 get_container( row, column ) |
| 코드 길이 | 짧음 (한 줄 NEW + 자식 속성 2개) | 중간 (rows · columns 설정 + 각 셀 get_container) |
| 분할 비율 변경 | set_sash_position 한 값 |
set_row_height / set_column_width 메서드 |
| 적합한 시나리오 | 마스터-디테일 (헤더+라인) · 좌/우 비교 | 2x2 격자 대시보드 · 다중 ALV + 트리 |
대부분 시나리오는 EASY 로 충분. 3분할 이상이 필요하거나 좌/우 + 위/아래 동시 배치(2x2) 가 필요할 때만 일반 SPLITTER 로 갑니다.
자주 빠뜨리는 함정
g_container IS INITIAL 가드 누락
PBO 가 여러 번 도는데 매번 NEW 하면 메모리 누수 + 화면 깜빡임 발생. 첫 PBO 한 번만 생성하도록 가드 필수.
orientation 의미 혼동
SAP 상수 이름은 "영역이 배치되는 방향" 기준. ORIENTATION_VERTICAL(0) = 두 영역이 세로로 쌓임(위/아래), ORIENTATION_HORIZONTAL(1) = 가로로 배치(좌/우). 분할선 방향 기준으로 외우면 정반대로 헷갈리므로, 코드 옆 주석 권장.
sash_position 단위 오해
픽셀이 아닌 퍼센트(%). 30 이면 첫 영역(top_left) 이 30%, 두 번째(bottom_right) 가 70%. 0~100 범위 밖 값은 무시됨.
자식 컨테이너 속성 이름 헷갈림
한 속성 이름(top_left_container / bottom_right_container) 이 orientation 값에 따라 두 가지 의미를 가짐. 위 매핑 표를 코드 옆에 두고 작성.
두 Grid 의 Field Catalog 공용
각 Grid 마다 별도 Field Catalog 가 필요. gt_fcat + gt_fcat2 처럼 따로 둬야 컬럼 구성이 섞이지 않음.
EKPO 전체 표시
마스터-디테일 패턴에서 위쪽 PO 선택 시 아래쪽 EKPO 를 필터링 안 하면 전체 EKPO 가 보임. handle_double_click 에서 필터링 갱신 패턴 필수.
Background 실행 시 덤프
이 글의 패턴은 Dialog 전용. 배치 잡으로 돌리려면 "[SAP ABAP] ALV 백그라운드 DUMP 방지" 글의 OFFLINE( ) 분기 패턴을 같이 적용.
전체 코드 — 복사용 통합본
아래 통합본은 SE38 에 그대로 붙여 활성화 가능. Screen 100 의 PBO 모듈에 status_0100·create_container, PAI 모듈에 user_command_0100 연결하고, SE51 에서 Screen 100 에 Custom Control 'AREA' 만 추가하면 됩니다.
*&---------------------------------------------------------------------*
*& Report ZRXX_ALV_EASY_SPLITTER (예시)
*&---------------------------------------------------------------------*
REPORT zrxx_alv_easy_splitter.
* ★ 1) DATA 선언 — 컨테이너 + Splitter + Grid 두 개 + 데이터 + Field Catalog
DATA: g_container TYPE REF TO cl_gui_custom_container,
g_splitter TYPE REF TO cl_gui_easy_splitter_container,
g_grid TYPE REF TO cl_gui_alv_grid,
g_grid2 TYPE REF TO cl_gui_alv_grid.
DATA: ok_code TYPE sy-ucomm.
DATA: gt_fcat TYPE lvc_t_fcat,
gt_fcat2 TYPE lvc_t_fcat.
DATA: BEGIN OF gs_data,
ebeln TYPE ekko-ebeln,
bstyp TYPE ekko-bstyp,
bsart TYPE ekko-bsart,
END OF gs_data,
gt_data LIKE TABLE OF gs_data.
DATA: gt_ekpo TYPE TABLE OF ekpo,
gs_ekpo LIKE LINE OF gt_ekpo.
* ★ 2) 이벤트 핸들러 클래스 — 마스터 ALV 더블클릭 → 디테일 ALV 필터링
CLASS lcl_event_receiver DEFINITION.
PUBLIC SECTION.
METHODS handle_double_click
FOR EVENT double_click OF cl_gui_alv_grid
IMPORTING e_row sender.
ENDCLASS.
CLASS lcl_event_receiver IMPLEMENTATION.
METHOD handle_double_click.
DATA: lt_filtered LIKE gt_ekpo.
IF sender = g_grid. " 위쪽 헤더 ALV 더블클릭
DATA(ls_data) = gt_data[ e_row-index ].
* 해당 PO 의 라인만 필터링
LOOP AT gt_ekpo INTO DATA(ls_ekpo) WHERE ebeln = ls_data-ebeln.
APPEND ls_ekpo TO lt_filtered.
ENDLOOP.
* 아래쪽 라인 ALV 갱신
g_grid2->set_table_for_first_display(
CHANGING
it_fieldcatalog = gt_fcat2
it_outtab = lt_filtered ).
ENDIF.
ENDMETHOD.
ENDCLASS.
DATA g_event_receiver TYPE REF TO lcl_event_receiver.
* ★ 3) 데이터 조회 → CALL SCREEN
START-OF-SELECTION.
SELECT * INTO CORRESPONDING FIELDS OF TABLE gt_data
FROM ekko.
SELECT * INTO CORRESPONDING FIELDS OF TABLE gt_ekpo
FROM ekpo.
CALL SCREEN 100.
* ★ 4) PBO 모듈 — 첫 호출 시에만 컨테이너 + Splitter + Grid 생성
MODULE status_0100 OUTPUT.
SET PF-STATUS 'S100'.
ENDMODULE.
MODULE create_container OUTPUT.
IF g_container IS INITIAL.
PERFORM create_container.
PERFORM create_fcat.
PERFORM register_event.
PERFORM display.
ENDIF.
ENDMODULE.
* ★ 5) PAI 모듈 — ok_code 처리
MODULE user_command_0100 INPUT.
CASE ok_code.
WHEN 'BACK' OR 'EXIT' OR 'CANC'.
LEAVE PROGRAM.
ENDCASE.
ENDMODULE.
* ★ 6) ★ 핵심: Easy Splitter 생성 — orientation + sash_position
FORM create_container.
g_container = NEW #( container_name = 'AREA' ).
g_splitter = NEW #(
parent = g_container
orientation = 0 " 0=VERTICAL(위/아래), 1=HORIZONTAL(좌/우)
sash_position = 30 ). " 위쪽 30%, 아래쪽 70%
* orientation=0 기준:
* top_left_container = 위쪽 영역 (PO Header)
* bottom_right_container = 아래쪽 영역 (PO Item)
g_grid = NEW #( i_parent = g_splitter->top_left_container ).
g_grid2 = NEW #( i_parent = g_splitter->bottom_right_container ).
ENDFORM.
* ★ 7) Field Catalog 두 개 — 각 그리드 별도 작성
FORM create_fcat.
IF gt_data 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.
ENDIF.
IF gt_ekpo IS NOT INITIAL.
DATA lr_str2 TYPE REF TO cl_abap_structdescr.
lr_str2 ?= cl_abap_structdescr=>describe_by_data( gs_ekpo ).
DATA(lt_fcat2) = cl_salv_data_descr=>read_structdescr( lr_str2 ).
gt_fcat2 = CORRESPONDING #( lt_fcat2 MAPPING ref_table = reftable
ref_field = reffield ).
CALL FUNCTION 'LVC_FIELDCAT_COMPLETE'
CHANGING ct_fieldcat = gt_fcat2.
ENDIF.
ENDFORM.
* ★ 8) 이벤트 핸들러 등록 — 위쪽 ALV 더블클릭
FORM register_event.
CREATE OBJECT g_event_receiver.
SET HANDLER g_event_receiver->handle_double_click FOR g_grid.
ENDFORM.
* ★ 9) 두 그리드 표시
FORM display.
g_grid->set_table_for_first_display(
CHANGING
it_fieldcatalog = gt_fcat
it_outtab = gt_data ).
g_grid2->set_table_for_first_display(
CHANGING
it_fieldcatalog = gt_fcat2
it_outtab = gt_ekpo ).
ENDFORM.
요약
| 단계 | 위치 | 핵심 행동 |
|---|---|---|
| 1 | SE51 Screen 100 | Custom Control 'AREA' 배치 |
| 2 | 데이터 / 객체 선언 | g_container + g_splitter + g_grid x 2 + 데이터 테이블 x 2 |
| 3 | START-OF-SELECTION |
SELECT * + CALL SCREEN 100 |
| 4 | PBO create_container 모듈 (IS INITIAL 가드) |
custom_container → easy_splitter(orientation/sash_position) → grid x 2 |
| 5 | Field Catalog + 이벤트 + Display | 두 Grid 각각 gt_fcat 채워 set_table_for_first_display + 더블클릭 핸들러 |
Easy Splitter 의 본질은 "한 Custom Container 를 2개로 나누고, 각 영역에 ALV 한 개씩 붙이기" 한 줄. orientation 으로 영역 배치 방향(ORIENTATION_VERTICAL=0 위/아래, ORIENTATION_HORIZONTAL=1 좌/우), sash_position 으로 비율, top_left_container / bottom_right_container 속성으로 자식 접근. 마스터-디테일 화면이나 좌/우 비교 ALV 가 필요한 시나리오에 가장 빠르게 만들 수 있는 표준 패턴이고, 3분할 이상이 정말 필요한 경우만 일반 SPLITTER 로 옮기면 됩니다.
Disclaimer — 이 포스트는 실무 정리 노트를 바탕으로 AI 보조로 정리되었습니다.
CL_GUI_EASY_SPLITTER_CONTAINER 의 생성자 파라미터(orientation·sash_position) 와 자식 컨테이너 속성(top_left_container·bottom_right_container) 은 NetWeaver 표준 정의(ECC 6.0 / S/4HANA on-premise 기준) 입니다. 적용 환경 버전에 따라 일부 차이가 있을 수 있으니 실제 적용 시 SE24 의 클래스 정의를 확인하시기 바랍니다.