한 화면에서 여러 ALV 를 탭으로 전환 해서 보여줘야 하는 케이스가 자주 있습니다. 항공편 마스터 / 예약 / 자재 같이 성격이 다른 데이터를 한 화면에 묶고, 사용자가 탭을 눌러 화면을 갈아끼우는 구조입니다. ABAP 에서는 TABSTRIP 컨트롤 + SUBSCREEN 두 가지를 조합해서 구현합니다.
핵심은 메인 화면에 TABSTRIP 컨트롤 + 그 아래 SUBSCREEN 영역 을 두고, 별도 서브 스크린(110·120·130) 을 각각 만들어 CALL SUBSCREEN ... INCLUDING sy-repid gv_screen 으로 동적으로 끼워 넣는 것입니다. 탭을 누르면 g_tabstrip-activetab 값이 바뀌고, PBO 에서 그 값에 맞춰 gv_screen 변수에 호출할 서브 화면 번호를 세팅해 줍니다.
이 글에서는 메인 화면 디자인 → 서브 화면 / 컨테이너 배치 → TABSTRIP 컨트롤 선언 → PBO 분기 → PAI 처리 순서로, 항공편 데이터(SFLIGHT / SBOOK / MARA) 3개 탭을 보여주는 풀 모듈풀을 만들어 봅니다.
핵심 — TABSTRIP + SUBSCREEN 의 구성 요소
탭 ALV 한 화면을 만들려면 화면 디자인 쪽과 코드 쪽에서 각각 갖춰야 할 것들이 다릅니다.
| 구성 요소 | 위치 | 역할 |
|---|---|---|
TABSTRIP 컨트롤 G_TABSTRIP |
메인 Screen 100 Layout | 탭 헤더를 화면에 그리는 컨트롤 |
PUSH BUTTON TAB1 / TAB2 / TAB3 |
TABSTRIP 안 | 탭 한 칸씩 — Function code 가 곧 탭 이름 |
SUBSCREEN 영역 SUB_SCR |
TABSTRIP 아래 | 서브 화면이 끼워 들어가는 자리 |
| 서브 화면 110 / 120 / 130 | 화면 유형 = "서브화면" | 탭별 ALV 가 들어갈 화면 |
Custom Container CON1 / CON2 / CON3 |
각 서브 화면 안 | ALV 가 붙는 Custom Control |
CONTROLS g_tabstrip TYPE TABSTRIP |
TOP INCLUDE | 활성 탭(activetab) 을 담는 구조 |
전체 흐름은 다음과 같습니다.
사용자 TAB2 클릭
↓
PAI: g_tabstrip-activetab = ok_code ('TAB2')
↓
PBO: case activetab → gv_screen = '0120'
↓
PROCESS BEFORE OUTPUT:
call subscreen sub_scr INCLUDING sy-repid gv_screen
↓
서브 화면 120 PBO 실행 → SBOOK ALV 표시
핵심 트릭은 CALL SUBSCREEN 의 마지막 인자에 변수(gv_screen) 를 넣을 수 있다는 점. 이 변수는 sy-dynnr 타입이고, PBO 시점에 활성 탭에 맞춰 다른 서브 화면 번호로 갈아끼우면 같은 SUBSCREEN 영역에 다른 화면이 들어옵니다.
1단계 — 메인 화면(100) 디자인
메인 화면은 Screen Painter(SE51) 에서 만듭니다. 두 가지 영역만 배치하면 됩니다.
┌──────────────────────────────────────────────┐
│ Screen 100 (Main) │
│ │
│ ┌──────────────────────────────────────┐ │
│ │ G_TABSTRIP (TABSTRIP 컨트롤) │ │
│ │ ┌────┐ ┌─────┐ ┌─────┐ │ │
│ │ │TAB1│ │TAB2 │ │TAB3 │ │ │
│ │ └────┘ └─────┘ └─────┘ │ │
│ └──────────────────────────────────────┘ │
│ ┌──────────────────────────────────────┐ │
│ │ SUB_SCR (Subscreen area) │ │
│ │ → 110/120/130 이 여기로 들어옴 │ │
│ └──────────────────────────────────────┘ │
└──────────────────────────────────────────────┘
TABSTRIP 컨트롤 만들기
요소 목록(Element List) 에서 새 요소를 Tabstrip Control 로 추가하고 이름을 G_TABSTRIP 로 줍니다. 그 안에 PUSH BUTTON 세 개를 배치하고 Function code 를 각각 TAB1·TAB2·TAB3 로 지정합니다.
SUBSCREEN 영역 만들기
같은 화면에 Subscreen area 요소를 추가하고 이름을 SUB_SCR 로 줍니다. 이 영역의 크기가 곧 탭 내용물의 크기를 결정하므로 충분히 넓게.
2단계 — 서브 화면(110·120·130) 만들기
탭 별로 하나씩 서브 화면을 만듭니다. SE51 → 화면 번호 입력 → 속성 → 화면 유형에서 "서브화면" 라디오 체크가 핵심입니다. 일반 화면으로 만들면 CALL SUBSCREEN 시점에 덤프가 납니다.
| 화면 | 담당 탭 | Custom Container | 표시 데이터 |
|---|---|---|---|
| 110 | TAB1 | CON1 |
SFLIGHT (항공편) |
| 120 | TAB2 | CON2 |
SBOOK (예약) |
| 130 | TAB3 | CON3 |
MARA (자재) |
각 서브 화면 안에 Custom Control 요소(사용자제어) 를 하나씩 배치하고 이름을 CON1·CON2·CON3 으로 지정. 이 컨테이너에 코드에서 ALV 를 붙입니다.
서브 화면 Flow Logic
서브 화면도 PBO / PAI 가 있어야 합니다. 각 화면에 다음을 추가합니다.
PROCESS BEFORE OUTPUT.
MODULE create_alv110. " 화면 110 의 경우
PROCESS AFTER INPUT.
MODULE user_command_0110.
3단계 — TABSTRIP 컨트롤 선언 (TOP INCLUDE)
ABAP 코드에서 탭 상태를 받으려면 CONTROLS 구문으로 TABSTRIP 컨트롤을 선언해 줍니다. 화면 디자인의 컨트롤 이름과 정확히 동일한 이름 이어야 합니다.
CONTROLS : g_tabstrip TYPE TABSTRIP.
DATA : gv_screen TYPE sy-dynnr,
ok_code TYPE sy-ucomm.
CONTROLS 로 선언된 g_tabstrip 은 activetab 컴포넌트를 가집니다. 현재 활성 탭의 Function code (= 'TAB1' / 'TAB2' / 'TAB3') 가 여기에 들어 있습니다.
gv_screen 은 PBO 에서 활성 탭에 따라 호출할 서브 화면 번호('0110'·'0120'·'0130') 를 담아두는 변수.
4단계 — PBO 분기: set_tabscreen + CALL SUBSCREEN
PBO 에서 두 가지 모듈을 실행합니다. 하나는 활성 탭에 따라 gv_screen 을 세팅하는 모듈, 다른 하나는 그 변수를 가지고 SUBSCREEN 을 호출하는 Flow Logic 명령입니다.
메인 Screen 100 의 Flow Logic
PROCESS BEFORE OUTPUT.
MODULE status_0100.
MODULE set_tabscreen.
CALL SUBSCREEN sub_scr INCLUDING sy-repid gv_screen.
PROCESS AFTER INPUT.
CALL SUBSCREEN sub_scr.
MODULE user_command_0100.
set_tabscreen 모듈 (PBO INCLUDE)
MODULE set_tabscreen OUTPUT.
IF g_tabstrip-activetab IS INITIAL.
g_tabstrip-activetab = 'TAB1'.
ENDIF.
CASE g_tabstrip-activetab.
WHEN 'TAB1'. gv_screen = '0110'.
WHEN 'TAB2'. gv_screen = '0120'.
WHEN 'TAB3'. gv_screen = '0130'.
WHEN OTHERS. gv_screen = '0110'.
ENDCASE.
ENDMODULE.
처음 화면이 떴을 때 activetab 이 비어 있는 케이스를 처리하기 위해 초기값 'TAB1' 을 박아 줍니다. 그렇지 않으면 첫 진입 시 gv_screen 이 공백이라 SUBSCREEN 호출이 실패합니다.
create_alv110 같은 ALV 생성 모듈 (PBO INCLUDE)
각 서브 화면 PBO 에서 자기 ALV 를 만들어 컨테이너에 붙입니다. 한 번만 만들면 되므로 인스턴스가 비어 있을 때만 생성합니다.
MODULE create_alv110 OUTPUT.
SELECT * FROM sflight INTO TABLE @DATA(lt_sflight) UP TO 50 ROWS.
IF go_con IS INITIAL.
go_con = NEW #( container_name = 'CON1' ).
go_grid = NEW #( i_parent = go_con ).
go_grid->set_table_for_first_display(
EXPORTING
i_structure_name = 'SFLIGHT'
CHANGING
it_outtab = lt_sflight ).
ENDIF.
ENDMODULE.
120 / 130 도 동일한 구조로 각각 SBOOK / MARA 데이터를 붙이면 됩니다.
5단계 — PAI 처리: activetab 갱신
PAI 에서는 단 한 줄이 핵심입니다. 사용자가 클릭한 탭의 Function code 를 g_tabstrip-activetab 에 다시 넣어 줘야 다음 PBO 때 set_tabscreen 이 올바른 화면 번호를 세팅합니다.
MODULE user_command_0100 INPUT.
g_tabstrip-activetab = ok_code.
CASE ok_code.
WHEN 'BACK' OR 'EXIT' OR 'CANC'.
SET SCREEN 0.
ENDCASE.
CLEAR ok_code.
ENDMODULE.
이 한 줄 — g_tabstrip-activetab = ok_code — 을 빠뜨리면 탭을 눌러도 화면이 안 바뀌고 첫 탭 그대로 머무릅니다. 가장 흔한 실수.
자주 빠뜨리는 함정
푸시버튼 "참조필드" 미설정
TAB 푸시버튼의 속성(참조필드) 에 G_TABSTRIP 컨트롤 이름을 안 적으면 일반 버튼처럼 동작합니다. 더블클릭 후 Dict 탭에서 참조필드를 컨트롤 이름과 동일하게 지정해야 탭으로 인식됩니다.

서브 화면 라디오 미체크
서브로 쓸 화면의 속성에서 화면 유형 = 서브화면 라디오를 안 체크하면 CALL SUBSCREEN 호출 시 덤프(DYNP_TOO_FEW_LINES_IN_AREA 류) 가 발생합니다.
CALL SUBSCREEN 위치 — Flow Logic 안
CALL SUBSCREEN sub_scr INCLUDING ... gv_screen 은 ABAP 소스의 MODULE 안이 아니라 메인 화면 100 의 Flow Logic 안에 직접 적습니다. MODULE 안에 적으면 컴파일 오류.
activetab 갱신 누락
PAI 에서 g_tabstrip-activetab = ok_code 빠뜨리면 탭 클릭이 무시됩니다. PBO 의 set_tabscreen 만 봐서는 이유를 못 찾으므로 가장 자주 막히는 지점.
gv_screen 초기값 미설정
첫 진입 시 activetab 이 공백이라 case 문이 다른 분기로 떨어지면 gv_screen 이 공백 그대로 CALL SUBSCREEN 에 전달되어 덤프. set_tabscreen 첫 줄에 IF activetab IS INITIAL ... 'TAB1' ... ENDIF 안전 장치 필수.
Custom Container 이름 대소문자
CL_GUI_CUSTOM_CONTAINER 컨스트럭터의 container_name 은 Screen Painter 의 컨테이너 이름과 정확히 일치(대문자) 해야 합니다. 'con1' 처럼 소문자로 박으면 컨트롤이 안 잡혀서 ALV 가 안 보이거나 다른 화면에 떠 버립니다.
같은 컨테이너에 ALV 중복 생성
PBO 가 매번 실행되므로 IF go_con IS INITIAL. 같은 가드 없이 컨테이너 / 그리드를 만들면 호출마다 새 인스턴스가 생성되어 메모리 누수 + 화면이 깜빡입니다. 항상 가드 + refresh_table_display 패턴으로.
전체 코드 — 복사용 통합본
화면 100 + 서브 화면 110·120·130 은 SE51 에서 미리 만든다는 전제로, ABAP 부분은 메인 + 6개 INCLUDE(T / C / SCR / O / I / F) 로 구성합니다. INCLUDE 는 T → C → SCR → O → I → F 순서로 메인 프로그램이 로드합니다.
*&---------------------------------------------------------------------*
*& Report ZRXX_ALV_TABSTRIP (메인)
*&---------------------------------------------------------------------*
REPORT zrxx_alv_tabstrip.
INCLUDE zrxx_alv_tabstrip_t. " TOP - 전역 선언 + CONTROLS
INCLUDE zrxx_alv_tabstrip_c. " CLASS - 로컬 클래스 (이 글에서는 사용 안 함)
INCLUDE zrxx_alv_tabstrip_scr. " SCR - 셀렉션 스크린 (이 글에서는 사용 안 함)
INCLUDE zrxx_alv_tabstrip_o. " PBO - OUTPUT 모듈
INCLUDE zrxx_alv_tabstrip_i. " PAI - INPUT 모듈
INCLUDE zrxx_alv_tabstrip_f. " FORM - 서브루틴 (이 글에서는 사용 안 함)
START-OF-SELECTION.
CALL SCREEN 100.
*&---------------------------------------------------------------------*
*& INCLUDE ZRXX_ALV_TABSTRIP_T (TOP - 전역 선언)
*&---------------------------------------------------------------------*
* ★ TABSTRIP 컨트롤 + 화면 변수
CONTROLS : g_tabstrip TYPE TABSTRIP.
DATA : gv_screen TYPE sy-dynnr,
ok_code TYPE sy-ucomm.
* 탭별 컨테이너 + ALV 인스턴스
DATA : go_con TYPE REF TO cl_gui_custom_container,
go_grid TYPE REF TO cl_gui_alv_grid.
DATA : go_con2 TYPE REF TO cl_gui_custom_container,
go_grid2 TYPE REF TO cl_gui_alv_grid.
DATA : go_con3 TYPE REF TO cl_gui_custom_container,
go_grid3 TYPE REF TO cl_gui_alv_grid.
*&---------------------------------------------------------------------*
*& INCLUDE ZRXX_ALV_TABSTRIP_C (CLASS - 로컬 클래스)
*&---------------------------------------------------------------------*
* (이 글에서는 사용 안 함 — 이벤트 핸들러가 필요해지면 여기에 lcl_event 정의)
*&---------------------------------------------------------------------*
*& INCLUDE ZRXX_ALV_TABSTRIP_SCR (SCR - 셀렉션 스크린)
*&---------------------------------------------------------------------*
* (이 글에서는 사용 안 함 — 셀렉션 조건이 필요하면 SELECT-OPTIONS 추가)
*
* Screen 100 / 110 / 120 / 130 은 SE51 에서 정의:
* 100 : 메인 (TABSTRIP G_TABSTRIP + 푸시버튼 TAB1/TAB2/TAB3 + Subscreen SUB_SCR)
* 110 : 서브화면 (Custom Container CON1) — TAB1 / SFLIGHT
* 120 : 서브화면 (Custom Container CON2) — TAB2 / SBOOK
* 130 : 서브화면 (Custom Container CON3) — TAB3 / MARA
*
* Screen 100 Flow Logic:
* PROCESS BEFORE OUTPUT.
* MODULE status_0100.
* MODULE set_tabscreen.
* CALL SUBSCREEN sub_scr INCLUDING sy-repid gv_screen.
* PROCESS AFTER INPUT.
* CALL SUBSCREEN sub_scr.
* MODULE user_command_0100.
*
* Screen 110 Flow Logic:
* PROCESS BEFORE OUTPUT.
* MODULE create_alv110.
* PROCESS AFTER INPUT.
* MODULE user_command_0110.
* (120, 130 도 동일하게 create_alv120 / 130 + user_command_0120 / 0130)
*&---------------------------------------------------------------------*
*& INCLUDE ZRXX_ALV_TABSTRIP_O (PBO - OUTPUT)
*&---------------------------------------------------------------------*
MODULE status_0100 OUTPUT.
SET PF-STATUS 'S100'.
ENDMODULE.
* ★ 활성 탭에 따라 서브 화면 번호 세팅
MODULE set_tabscreen OUTPUT.
IF g_tabstrip-activetab IS INITIAL.
g_tabstrip-activetab = 'TAB1'.
ENDIF.
CASE g_tabstrip-activetab.
WHEN 'TAB1'. gv_screen = '0110'.
WHEN 'TAB2'. gv_screen = '0120'.
WHEN 'TAB3'. gv_screen = '0130'.
WHEN OTHERS. gv_screen = '0110'.
ENDCASE.
ENDMODULE.
* TAB1 — SFLIGHT
MODULE create_alv110 OUTPUT.
SELECT * FROM sflight
INTO TABLE @DATA(lt_sflight)
UP TO 50 ROWS.
IF go_con IS INITIAL.
go_con = NEW #( container_name = 'CON1' ).
go_grid = NEW #( i_parent = go_con ).
go_grid->set_table_for_first_display(
EXPORTING
i_structure_name = 'SFLIGHT'
CHANGING
it_outtab = lt_sflight ).
ENDIF.
ENDMODULE.
* TAB2 — SBOOK
MODULE create_alv120 OUTPUT.
SELECT * FROM sbook
INTO TABLE @DATA(lt_sbook)
UP TO 50 ROWS.
IF go_con2 IS INITIAL.
go_con2 = NEW #( container_name = 'CON2' ).
go_grid2 = NEW #( i_parent = go_con2 ).
go_grid2->set_table_for_first_display(
EXPORTING
i_structure_name = 'SBOOK'
CHANGING
it_outtab = lt_sbook ).
ENDIF.
ENDMODULE.
* TAB3 — MARA
MODULE create_alv130 OUTPUT.
SELECT * FROM mara
INTO TABLE @DATA(lt_mara)
UP TO 50 ROWS.
IF go_con3 IS INITIAL.
go_con3 = NEW #( container_name = 'CON3' ).
go_grid3 = NEW #( i_parent = go_con3 ).
go_grid3->set_table_for_first_display(
EXPORTING
i_structure_name = 'MARA'
CHANGING
it_outtab = lt_mara ).
ENDIF.
ENDMODULE.
*&---------------------------------------------------------------------*
*& INCLUDE ZRXX_ALV_TABSTRIP_I (PAI - INPUT)
*&---------------------------------------------------------------------*
MODULE user_command_0100 INPUT.
* ★ 클릭한 탭의 Function code 를 activetab 으로 다시 박기 (핵심)
g_tabstrip-activetab = ok_code.
CASE ok_code.
WHEN 'BACK' OR 'EXIT' OR 'CANC'.
SET SCREEN 0.
ENDCASE.
CLEAR ok_code.
ENDMODULE.
* 서브 화면 PAI — 필요 시 각각 따로
MODULE user_command_0110 INPUT.
ENDMODULE.
MODULE user_command_0120 INPUT.
ENDMODULE.
MODULE user_command_0130 INPUT.
ENDMODULE.
*&---------------------------------------------------------------------*
*& INCLUDE ZRXX_ALV_TABSTRIP_F (FORM - 서브루틴)
*&---------------------------------------------------------------------*
* (이 글에서는 사용 안 함 — 데이터 가공 FORM 이 필요해지면 여기에 추가)
요약
| 단계 | 위치 | 핵심 |
|---|---|---|
| 1 | 메인 Screen 100 | TABSTRIP G_TABSTRIP + 푸시버튼 TAB1/2/3 + Subscreen SUB_SCR |
| 2 | 서브 화면 110/120/130 | 화면 유형 = 서브화면 + Custom Container CON1/2/3 |
| 3 | TOP INCLUDE | CONTROLS g_tabstrip TYPE TABSTRIP + gv_screen TYPE sy-dynnr |
| 4 | PBO + Flow Logic | set_tabscreen(activetab→gv_screen) + CALL SUBSCREEN sub_scr INCLUDING sy-repid gv_screen |
| 5 | PAI | g_tabstrip-activetab = ok_code (1줄, 빠뜨리면 탭 안 바뀜) |
탭 ALV 의 본질은 "같은 SUBSCREEN 영역에 다른 화면을 동적으로 끼워 넣기" 입니다.
CONTROLS 로 선언한 TABSTRIP 의 activetab 값과 CALL SUBSCREEN ... INCLUDING sy-repid gv_screen 의 변수 인자, 그리고 PAI 의 g_tabstrip-activetab = ok_code 세 가지가 맞물려 동작합니다. 푸시버튼 참조필드와 서브 화면 라디오만 정확히 설정하면 ALV 가 아니라 일반 입력 화면도 같은 패턴으로 탭에 묶을 수 있습니다.
Disclaimer — 이 포스트는 실무 정리 노트를 바탕으로 AI 보조로 정리되었습니다.
TABSTRIP 컨트롤 · CONTROLS 구문 · CALL SUBSCREEN ... INCLUDING · CL_GUI_CUSTOM_CONTAINER · CL_GUI_ALV_GRID 는 NetWeaver 스탠다드 정의(ECC 6.0 / S/4HANA on-premise 기준) 입니다. SFLIGHT · SBOOK · MARA 는 SAP 스탠다드 학습 환경(IDES) 테이블이며, 실무 적용 시 운영 테이블로 교체하시기 바랍니다. 화면 100 / 110 / 120 / 130 의 레이아웃은 SE51 Screen Painter 에서 직접 그려야 하며 코드만으로는 재현되지 않습니다.