SALV(CL_SALV_TABLE) 두 개를 한 화면에 위/아래로 나란히 띄우고 싶을 때 가장 짧게 짤 수 있는 패턴이 CL_GUI_SPLITTER_CONTAINER + cl_gui_container=>default_screen 조합입니다. SE51 에서 화면을 만들 필요도 없고 모듈풀로 짜지 않아도 됩니다 — 단순 REPORT 하나면 끝.
핵심 트릭은 parent = cl_gui_container=>default_screen 입니다. SAP GUI 의 리스트 출력 영역(WRITE 명령이 그려지는 화면) 을 곧바로 Splitter 의 부모로 잡아 행/열로 쪼개면, 각 영역이 CL_SALV_TABLE 의 컨테이너로 그대로 쓸 수 있게 됩니다.
이 글에서는 SALV 두 개를 위/아래로 띄우는 가장 짧은 패턴을 단계별로 정리합니다. 데이터는 SAP 스탠다드 학습 환경 테이블 SCARR(항공사 마스터) + SPFLI(항공편 스케줄) 예시.
핵심 — SALV + Splitter 의 컨테이너 계층
| 계층 | 클래스 | 역할 |
|---|---|---|
| 1. 화면 영역 | cl_gui_container=>default_screen |
SAP GUI 리스트 출력 영역 (WRITE 가 그려지는 곳) |
| 2. Splitter | cl_gui_splitter_container |
화면을 N×M 분할 (rows·columns 지정) |
| 3. 영역 | cl_gui_container (top / bottom) |
Splitter get_container( row column ) 로 잡은 각 칸 |
| 4. SALV | cl_salv_table |
FACTORY 호출 시 r_container 인자에 영역 전달 |
| 5. 출력 | REPORT 의 list-processing | cl_abap_list_layout=>suppress_toolbar( ) + WRITE : space |
전체 흐름을 그림으로 정리하면:
SAP GUI 리스트 화면 (default_screen)
↓ parent
cl_gui_splitter_container (rows=2 columns=1)
↓ get_container( row=1 column=1 ) get_container( row=2 column=1 )
o_container_top o_container_bottom
↓ r_container ↓ r_container
cl_salv_table FACTORY (it_scarr) cl_salv_table FACTORY (it_spfli)
↓ display( ) ↓ display( )
상단 Airline ALV 하단 Flight schedule ALV
핵심은 두 가지. (1) parent = cl_gui_container=>default_screen — Custom Container 만들 필요 없이 list-processing 화면을 그대로 잡아 씁니다. (2) CL_SALV_TABLE=>FACTORY 의 r_container — SALV 가 어느 영역에 그려질지 지정. 두 개 인스턴스를 만들고 display 만 호출하면 끝.
1단계 — 데이터 준비
상단 / 하단에 띄울 두 인터널 테이블을 미리 채워 둡니다.
* 상단 — 항공사 마스터
SELECT * FROM scarr INTO TABLE @DATA(it_scarr).
* 하단 — 항공편 스케줄
SELECT * FROM spfli INTO TABLE @DATA(it_spfli).
테이블 타입 선언 없이 @DATA(it_scarr) 인라인 선언으로 받으면 코드가 짧아집니다. CL_SALV_TABLE=>FACTORY 의 t_table 파라미터는 STANDARD TABLE 이면 어떤 구조든 받습니다.
2단계 — Splitter 컨테이너 생성
parent = cl_gui_container=>default_screen 으로 Splitter 를 만들고 2행 1열로 분할.
DATA : o_splitter_main TYPE REF TO cl_gui_splitter_container,
o_container_top TYPE REF TO cl_gui_container,
o_container_bottom TYPE REF TO cl_gui_container.
o_splitter_main = NEW #(
parent = cl_gui_container=>default_screen
no_autodef_progid_dynnr = abap_true
rows = 2
columns = 1 ).
* 첫 행 높이를 30 으로 (전체 100 기준)
o_splitter_main->set_row_height( id = 1 height = 30 ).
* 두 영역 참조 받기
o_container_top = o_splitter_main->get_container( row = 1 column = 1 ).
o_container_bottom = o_splitter_main->get_container( row = 2 column = 1 ).
| 파라미터 | 의미 |
|---|---|
parent |
cl_gui_container=>default_screen = SAP GUI 의 리스트 출력 영역 |
no_autodef_progid_dynnr |
abap_true — Splitter 가 자기 프로그램/화면을 자동으로 잡지 않도록 (REPORT 에서는 화면이 없으니 필수) |
rows / columns |
행/열 개수 (2×1 이면 위/아래 두 칸) |
set_row_height |
전체 100 기준 비율 — 30 이면 상단이 30%, 하단이 70% |
no_autodef_progid_dynnr = abap_true 가 가장 중요한 옵션입니다. 빠뜨리면 Splitter 가 자기 부모 화면을 등록하려다 실패해서 컨테이너가 그려지지 않습니다. REPORT 출력 패턴에서는 무조건 박아두는 게 안전.
3단계 — CL_SALV_TABLE FACTORY 두 번 호출
각 영역에 SALV 인스턴스를 하나씩 생성. r_container 인자에 위에서 잡은 영역 참조를 넘기면 됩니다.
DATA : o_salv_top TYPE REF TO cl_salv_table,
o_salv_bottom TYPE REF TO cl_salv_table.
* 상단 — SCARR
cl_salv_table=>factory(
EXPORTING r_container = o_container_top
IMPORTING r_salv_table = o_salv_top
CHANGING t_table = it_scarr ).
* 하단 — SPFLI
cl_salv_table=>factory(
EXPORTING r_container = o_container_bottom
IMPORTING r_salv_table = o_salv_bottom
CHANGING t_table = it_spfli ).
CL_SALV_TABLE=>FACTORY 는 인터널 테이블 구조를 보고 컬럼 카탈로그를 자동으로 만들어 줍니다. SCARR 면 CARRID·CARRNAME·CURRCODE·URL 4컬럼 자동, SPFLI 면 CARRID·CONNID·CITYFROM·AIRPFROM·CITYTO·AIRPTO·FLTIME 등 자동.
4단계 — SALV 표준 설정
생성된 SALV 인스턴스에 자주 쓰는 4가지 설정을 박아 줍니다. get_functions·get_columns·get_display_settings·get_selections 메소드로 각 설정 객체를 얻은 뒤 짧은 setter 호출.
* 상단 SALV
o_salv_top->get_functions( )->set_all( abap_true ). " 표준 툴바 다 켜기
o_salv_top->get_columns( )->set_optimize( abap_true ). " 컬럼 폭 자동
o_salv_top->get_display_settings( )->set_list_header( 'Airline' ). " 헤더 텍스트
o_salv_top->get_display_settings( )->set_striped_pattern( abap_true ). " 줄무늬
o_salv_top->get_selections( )->set_selection_mode(
if_salv_c_selection_mode=>row_column ).
o_salv_top->display( ).
* 하단 SALV — 같은 패턴
o_salv_bottom->get_functions( )->set_all( abap_true ).
o_salv_bottom->get_columns( )->set_optimize( abap_true ).
o_salv_bottom->get_display_settings( )->set_list_header( 'Flight schedule' ).
o_salv_bottom->get_display_settings( )->set_striped_pattern( abap_true ).
o_salv_bottom->get_selections( )->set_selection_mode(
if_salv_c_selection_mode=>row_column ).
o_salv_bottom->display( ).
| 설정 객체 | 자주 쓰는 setter |
|---|---|
get_functions( ) |
set_all(스탠다드 툴바 표시) / set_default(기본만) |
get_columns( ) |
set_optimize(폭 자동) / get_column(개별 컬럼) |
get_display_settings( ) |
set_list_header(제목) / set_striped_pattern(줄무늬) |
get_selections( ) |
set_selection_mode(row_column = 셀 클릭, multiple = 다중 선택) |
5단계 — 화면 출력: suppress_toolbar + WRITE : space
SALV 두 개를 display( ) 만 하면 끝일 것 같지만, REPORT 의 list-processing 화면이 실제로 떠야 그 위에 그려진 SALV 가 보입니다. WRITE : space 한 줄 이 그 역할.
* 표준 list 툴바 숨기기 (SALV 가 자체 툴바를 띄우므로 중복 방지)
cl_abap_list_layout=>suppress_toolbar( ).
* list-processing 화면 진입 — 빈 출력이지만 화면은 떠야 함
WRITE : space.
cl_abap_list_layout=>suppress_toolbar( ) 는 SAP 스탠다드 list-processing 의 회색 툴바를 안 보이게. SALV 두 개가 각자 툴바를 띄우므로 위에 또 다른 표준 툴바가 깔리면 화면이 답답해집니다.
WRITE : space 는 실제로 보이는 출력은 없지만 ABAP 런타임이 "출력이 있다" 고 판단해서 list-processing 화면을 띄우게 만듭니다. SALV 의 display 만으로는 화면이 안 뜹니다.
자주 빠뜨리는 함정
no_autodef_progid_dynnr 누락
REPORT 에서 Splitter 를 만들 때 no_autodef_progid_dynnr = abap_true 를 빠뜨리면 Splitter 가 자기 프로그램/화면 ID 를 자동으로 등록하려다 실패합니다. 화면이 없으니 당연. 이 옵션은 default_screen 패턴에서 거의 필수.
WRITE : space 누락
display 호출만 하면 SALV 인스턴스는 생성되지만 화면이 안 뜹니다. list-processing 진입을 위해 WRITE : space 같은 출력 한 줄이 반드시 필요. 실수로 빠뜨리면 화면 자체가 안 떠서 SALV 가 보이지 않습니다.
suppress_toolbar 위치
cl_abap_list_layout=>suppress_toolbar( ) 는 WRITE 명령 전에 호출해야 적용됩니다. 순서를 바꾸면 무시되고 표준 툴바가 떠 버립니다.
CHANGING 인자 — STANDARD TABLE 필수
CL_SALV_TABLE=>FACTORY 의 t_table 은 STANDARD TABLE 만 받습니다. SORTED 나 HASHED 테이블을 넘기면 컴파일 시점에 못 잡고 런타임에 덤프. SELECT ... INTO TABLE @DATA(it_xx) 는 기본이 STANDARD 라 안전합니다.
set_row_height 비율
set_row_height( id height ) 의 height 는 픽셀이 아니라 전체 100 기준 비율. 30 이면 30%. 너무 작으면(예: 10) 헤더만 보이고 데이터가 잘립니다.
데이터 갱신 시 추가 호출 필요
data 가 바뀐 후 다시 보여주려면 o_salv_top->refresh( ) 호출. 단순히 인터널 테이블 내용만 바꿔서는 화면이 갱신되지 않습니다.
Splitter 인스턴스 보관
o_splitter_main 참조를 지역 변수에 두고 함수가 끝나면 GC 가 회수해버립니다. 글로벌 DATA 에 보관해야 화면이 떠 있는 동안 안전합니다.
전체 코드 — 복사용 통합본
SE38 에 그대로 붙여 활성화하면 됩니다. CALL SCREEN 없이 단일 REPORT 한 블록.
*&---------------------------------------------------------------------*
*& Report ZRXX_SALV_SPLITTER
*&---------------------------------------------------------------------*
REPORT zrxx_salv_splitter.
* ---------- 컨테이너 참조 ----------
DATA : o_splitter_main TYPE REF TO cl_gui_splitter_container,
o_container_top TYPE REF TO cl_gui_container,
o_container_bottom TYPE REF TO cl_gui_container.
* ---------- SALV 참조 ----------
DATA : o_salv_top TYPE REF TO cl_salv_table,
o_salv_bottom TYPE REF TO cl_salv_table.
START-OF-SELECTION.
* 1) 데이터
SELECT * FROM scarr INTO TABLE @DATA(it_scarr).
SELECT * FROM spfli INTO TABLE @DATA(it_spfli).
* 2) Splitter — default_screen 분할
o_splitter_main = NEW #(
parent = cl_gui_container=>default_screen
no_autodef_progid_dynnr = abap_true
rows = 2
columns = 1 ).
o_splitter_main->set_row_height( id = 1 height = 30 ).
o_container_top = o_splitter_main->get_container( row = 1 column = 1 ).
o_container_bottom = o_splitter_main->get_container( row = 2 column = 1 ).
* 3) 상단 SALV — SCARR
cl_salv_table=>factory(
EXPORTING r_container = o_container_top
IMPORTING r_salv_table = o_salv_top
CHANGING t_table = it_scarr ).
o_salv_top->get_functions( )->set_all( abap_true ).
o_salv_top->get_columns( )->set_optimize( abap_true ).
o_salv_top->get_display_settings( )->set_list_header( 'Airline' ).
o_salv_top->get_display_settings( )->set_striped_pattern( abap_true ).
o_salv_top->get_selections( )->set_selection_mode(
if_salv_c_selection_mode=>row_column ).
o_salv_top->display( ).
* 4) 하단 SALV — SPFLI
cl_salv_table=>factory(
EXPORTING r_container = o_container_bottom
IMPORTING r_salv_table = o_salv_bottom
CHANGING t_table = it_spfli ).
o_salv_bottom->get_functions( )->set_all( abap_true ).
o_salv_bottom->get_columns( )->set_optimize( abap_true ).
o_salv_bottom->get_display_settings( )->set_list_header( 'Flight schedule' ).
o_salv_bottom->get_display_settings( )->set_striped_pattern( abap_true ).
o_salv_bottom->get_selections( )->set_selection_mode(
if_salv_c_selection_mode=>row_column ).
o_salv_bottom->display( ).
* 5) list-processing 화면 진입 + 표준 툴바 숨기기
cl_abap_list_layout=>suppress_toolbar( ).
WRITE : space.

요약
| 단계 | 위치 | 핵심 |
|---|---|---|
| 1 | 데이터 | SELECT INTO TABLE @DATA(it_xx) 인라인 STANDARD TABLE |
| 2 | Splitter | parent = cl_gui_container=>default_screen + no_autodef_progid_dynnr = abap_true |
| 3 | FACTORY | cl_salv_table=>factory( r_container · r_salv_table · t_table ) |
| 4 | 설정 | get_functions·get_columns·get_display_settings·get_selections + display( ) |
| 5 | 출력 | cl_abap_list_layout=>suppress_toolbar( ) + WRITE : space |
SALV + Splitter 의 본질은 "list-processing 의 default_screen 을 Splitter 로 쪼개고, 각 영역을 CL_SALV_TABLE FACTORY 의 r_container 로 넘기기". CALL SCREEN 도 없고 SE51 화면도 없고 모듈풀도 아닙니다. no_autodef_progid_dynnr = abap_true 와 마지막 WRITE : space 두 줄만 챙기면 SAP 스탠다드 SALV 의 모든 기능(정렬·필터·합계·엑셀 다운로드) 을 두 ALV 에서 모두 쓸 수 있습니다. 마스터/디테일 조회 화면을 가장 빠르게 만드는 패턴.
Disclaimer — 이 포스트는 실무 정리 노트를 바탕으로 AI 보조로 정리되었습니다. CL_SALV_TABLE·CL_GUI_SPLITTER_CONTAINER·CL_GUI_CONTAINER · CL_ABAP_LIST_LAYOUT · IF_SALV_C_SELECTION_MODE 는 NetWeaver 스탠다드 정의(ECC 6.0 / S/4HANA on-premise 기준) 입니다. SCARR·SPFLI 는 SAP 스탠다드 학습 환경(IDES) 테이블이며, 실무 적용 시 운영 테이블로 교체하시기 바랍니다. default_screen 패턴은 단순 조회 리포트에 적합하며, 편집 ALV / 사용자 입력이 필요한 화면은 SE51 Custom Screen 또는 Docking Container 패턴을 사용하시기 바랍니다.