SAP 표준 트랜잭션(ME23N · FB03 · MM03 등) 의 화면 좌측 상단에 보이는 작은 서비스 아이콘 — 그게 바로 GOS(Generic Object Services) 아이콘 입니다. 클릭하면 첨부파일·노트·송신·워크플로우 같은 표준 서비스가 한 번에 펼쳐집니다. SAP 가 객체(PO·자재문서·전표 등) 와 첨부 데이터를 연결해주는 공용 인프라이며, 이걸 회사 자체 화면이나 ALV 에 끼워 넣는 표준 클래스가 CL_GOS_MANAGER 입니다.
CL_GOS_MANAGER 는 단순히 아이콘 하나가 아니라 BOR(Business Object Repository) 객체와 첨부 데이터 간의 관계(SRGBRBREL) 를 자동 관리 합니다. 첨부 / 노트 추가 / 삭제 / 권한 / 송신 흐름까지 표준이 다 잡아주므로 회사 자체 ALV 에 한 줄 추가만으로 표준 ME23N 과 동일한 첨부 UX 를 구현할 수 있습니다.
이 글에서는 CL_GOS_MANAGER 시그니처·BOR 객체 매핑·인스턴스 생성·화면 컨테이너 배치·조회 / 변경 모드 분기·첨부 유무 체크·자주 빠뜨리는 함정 까지 표준 패턴으로 정리합니다. 자체 ALV / 화면에 GOS 를 처음 붙이는 케이스를 그대로 따라할 수 있게 단계별로 구성했습니다.
핵심 — CL_GOS_MANAGER 의 정체와 BOR 객체
| 구성 요소 | 역할 |
|---|---|
CL_GOS_MANAGER |
표준 GOS 서비스를 화면에 배치하는 매니저 클래스 (Package SGOS) |
BORIDENT (IS_OBJECT) |
첨부 대상 객체 키 — OBJTYPE(BUS2012 등) + OBJKEY(PO 번호 등) |
SRGBRBREL |
BOR 객체 ↔ 첨부 관계 마스터 테이블 |
SOFFCONT1 |
실제 첨부파일 본문 저장 (SAP Office Content) |
CL_GUI_CONTAINER |
GOS 아이콘이 그려질 컨테이너 (Docking / Custom Subscreen) |
자주 사용하는 BOR 객체 유형:
| OBJTYPE | 대상 | OBJKEY 예시 |
|---|---|---|
BUS2012 |
구매오더 (PO) | 4500000001 |
BUS2017 |
자재문서 (GR) | 50000000012026 (MBLNR + MJAHR) |
BUS2081 |
송장 (MIRO) | 51000000012026 |
BUS1001 |
자재 마스터 | TEST-MAT-001 |
BKPF |
회계전표 | 회사코드 + 전표번호 + 회계연도 |
LFA1 |
거래처 마스터 | 거래처 번호 |
회사 자체 Z 객체에도 첨부를 붙이려면 SE80 에서 BOR 객체를 신규 생성하거나(SWO1) Generic OAC0 저장소를 활용하는 방법이 있지만, 표준 객체에 붙이는 게 가장 일반적입니다.
1단계 — 인스턴스 생성 (가장 기본)
PO 번호 4500000001 에 첨부파일 서비스를 붙이는 표준 패턴.
DATA: go_gos_manager TYPE REF TO cl_gos_manager,
go_container TYPE REF TO cl_gui_custom_container,
ls_object TYPE borident.
" 1) 컨테이너 생성 (예: Custom Subscreen 안의 'GOS_AREA')
go_container = NEW #( container_name = 'GOS_AREA' ).
" 2) BOR 객체 키 채우기
ls_object-objtype = 'BUS2012'. " PO
ls_object-objkey = '4500000001'.
" 3) GOS Manager 인스턴스 생성
go_gos_manager = NEW #(
io_container = go_container
is_object = ls_object
ip_mode = 'E' " E = Edit · A = Display
ip_no_commit = 'X'
).
4가지 핵심 파라미터:
io_container— GOS 아이콘이 그려질 화면 컨테이너is_object— 첨부 대상 BOR 객체 키 (없으면 아이콘 안 생김)ip_mode—'E'변경 가능 ·'A'조회 전용ip_no_commit—'X'권장(호출자가 COMMIT 책임)
인스턴스 생성 직후 컨테이너 영역에 GOS 아이콘이 표시됩니다. 사용자가 클릭하면 표준 서비스 메뉴(첨부파일 만들기 · 첨부 목록 · 노트 · 송신 등) 가 펼쳐집니다.
2단계 — IS_OBJECT 없는 경우 (아이콘 없이 첨부 list 만 출력)
PO 번호가 아직 확정 안 된 신규 생성 화면 같은 케이스에서는 is_object 를 비우고 호출하면 아이콘 없이 첨부 list 만 출력 됩니다.
go_gos_manager = NEW #(
io_container = go_container
* is_object = ls_object " 비움
ip_mode = 'E'
ip_no_commit = 'X'
).
이 방식은 신규 생성 시점에 첨부를 임시로 받아두고, 저장 후 PO 번호가 생기면 그때 SET_ID_OF_PUBLISHED_OBJECT 로 연결하는 흐름에 사용합니다.
" 신규 생성 직후 PO 번호 확정 → 첨부와 연결
ls_object-objtype = 'BUS2012'.
ls_object-objkey = lv_new_ebeln. " 방금 생성된 PO 번호
CALL METHOD go_gos_manager->set_id_of_published_object
EXPORTING
is_object = ls_object
EXCEPTIONS
no_publication = 1.
생성 흐름에서 활용도가 높은 메소드입니다. BAPI_PO_CREATE1 + GOS 첨부를 함께 묶을 때 표준 패턴.
3단계 — Dynpro 화면에 컨테이너 배치
GOS 아이콘을 보여줄 영역은 Dynpro 화면에 미리 잡아둡니다.
SE51 → Dynpro 100 → Layout
→ "Custom Control" 도구 선택
→ 영역 드래그 (가로 4 / 세로 1 정도)
→ 이름: GOS_AREA
흐름 로직(PBO) 에서 컨테이너 생성:
PROCESS BEFORE OUTPUT.
MODULE pbo_0100.
MODULE pbo_0100 OUTPUT.
" 컨테이너 + GOS Manager 가 아직 안 만들어졌을 때만 생성
IF go_container IS INITIAL.
go_container = NEW #( container_name = 'GOS_AREA' ).
ls_object-objtype = 'BUS2012'.
ls_object-objkey = lv_ebeln.
go_gos_manager = NEW #(
io_container = go_container
is_object = ls_object
ip_mode = 'E'
ip_no_commit = 'X'
).
ENDIF.
ENDMODULE.
PBO 에서 매번 인스턴스를 새로 만들지 않도록 IF 가드 — 한 번 생성된 GOS Manager 는 화면이 다시 그려져도 그대로 유지됩니다. 매번 생성하면 메모리 누수 + 성능 저하.
4단계 — 조회 vs 변경 모드 분기
표준 트랜잭션 동작과 일관되게 — 조회 화면에서는 첨부 추가 / 삭제가 안 되도록 잠금.
DATA lv_mode TYPE sgs_rwmod.
" T-Code 가 MM03 / ME23N 같은 조회면 'A', 아니면 'E'
IF sy-tcode CP 'M*03' OR sy-tcode CP '*23N'.
lv_mode = 'A'. " Display only
ELSE.
lv_mode = 'E'. " Edit
ENDIF.
go_gos_manager = NEW #(
io_container = go_container
is_object = ls_object
ip_mode = lv_mode
ip_no_commit = 'X'
).
또는 사용자 권한에 따라 분기:
AUTHORITY-CHECK OBJECT 'Z_GOS_ATTACH'
ID 'ACTVT' FIELD '02'. " 02 = Change
IF sy-subrc = 0.
lv_mode = 'E'.
ELSE.
lv_mode = 'A'.
ENDIF.
5단계 — 첨부파일 유무 체크
첨부가 있는지 미리 알고 화면 라벨에 "📎" 표시를 추가하거나, 첨부 강제 정책을 검증할 때 사용. 두 가지 방식.
방법 A — SRGBRBREL 직접 조회
SELECT COUNT(*)
INTO @DATA(lv_atta_count)
FROM srgbrbrel
WHERE instid_a = @lv_ebeln
AND typeid_a = 'BUS2012'
AND catid_a = 'BO'
AND reltype = 'ATTA'.
IF lv_atta_count > 0.
" 첨부 있음 — 아이콘 표시 등
ENDIF.
방법 B — 표준 FM BINARY_RELATION_GET_ALL_PREDEC
DATA: lt_relations TYPE TABLE OF brelations,
ls_lpor TYPE sibflporb.
ls_lpor-typeid = 'BUS2012'.
ls_lpor-instid = lv_ebeln.
ls_lpor-catid = 'BO'.
CALL FUNCTION 'BINARY_RELATION_GET_ALL_PREDEC'
EXPORTING
object_a = ls_lpor
relation = 'ATTA'
TABLES
objects = lt_relations
EXCEPTIONS
OTHERS = 1.
IF lines( lt_relations ) > 0.
" 첨부 있음
ENDIF.
대용량 ALV 에서는 방법 A 가 빠릅니다 — 표준 FM 은 라인별 호출 시 오버헤드. 천 건 단위라면 SELECT 한 번으로 끝.
6단계 — 첨부파일 종류와 SRGBRBREL 의 RELTYPE
GOS 가 다루는 관계는 RELTYPE 으로 구분됩니다.
| RELTYPE | 의미 |
|---|---|
ATTA |
일반 첨부파일 (PDF · XLS · 이미지 등) |
NOTE |
텍스트 노트 |
URL |
외부 URL 링크 |
BDS_TOID |
Business Document Service 첨부 |
회사 정책에 따라 ATTA 만 허용하고 URL / NOTE 는 막아두는 경우도 있습니다. it_service_selection 파라미터에서 노출할 서비스를 필터링 가능합니다.
흔히 빠뜨리는 함정
Custom Subscreen 컨테이너 미준비
Dynpro 화면에 GOS_AREA 같은 Custom Control 을 미리 그려두지 않으면 CL_GUI_CUSTOM_CONTAINER 생성에서 예외. Layout 단계에서 영역을 먼저 그리고 그 영역명을 코드에서 동일하게 사용.
IS_OBJECT 없는데 ip_start_direct 도 없음
is_object 가 비고 ip_start_direct 도 'X' 안 주면 아이콘도 안 보이고 list 도 안 뜹니다. 신규 생성 시나리오는 ip_start_direct = 'X' 추가.
PBO 매 사이클마다 인스턴스 생성
화면이 PAI → PBO 다시 그려질 때마다 NEW cl_gos_manager 가 호출되면 메모리 누수 + 깜빡임 + 컨테이너 중복. IF 가드로 한 번만 생성.
ip_no_commit 미설정 시 자동 COMMIT
기본값 'X'(no commit) 이지만 명시적으로 ' '로 넘기면 GOS 가 첨부 저장 시 COMMIT WORK 를 직접 호출합니다. 호출 프로그램의 트랜잭션 LUW 와 충돌 가능성이 있어 거의 항상 'X' 권장.
BOR 객체 키 길이
BORIDENT-OBJKEY 는 최대 70자. PO 번호처럼 짧은 키는 문제없지만 자재문서(MBLNR 10 + MJAHR 4 = 14자) 처럼 복합 키는 정확한 자리수로 채워야 합니다.
한 화면에 여러 GOS
같은 화면에 PO + 자재문서 두 개의 GOS 를 동시에 띄우려면 컨테이너 + GOS Manager 인스턴스를 각각 별개로 두 개 만들어야 합니다. 한 객체로 둘 다 처리 불가.
Z 객체에 첨부 붙이기
표준 BOR 객체가 없는 회사 자체 객체에 첨부를 붙이려면 SWO1 으로 BOR 객체를 신규 생성하거나 generic 객체(OAC0) 를 활용. 단순 ZAP* T-Code 에 그냥 GOS Manager 만 띄우면 SAP 가 객체를 인식 못 해 첨부 저장이 안 됩니다.
동시 사용자 락 / 권한
표준 트랜잭션이 이미 PO 를 락(SM12) 중일 때 GOS 첨부 추가가 안 되거나 권한 부족으로 실패할 수 있습니다. 사용자 권한(S_BDS_FOLD 등) 도 함께 점검.
전체 코드 — 복사용 통합본
*&---------------------------------------------------------------------*
*& 모듈 풀 Z_XX_GOS_DEMO (Custom Subscreen 0100 + GOS_AREA 영역)
*&---------------------------------------------------------------------*
REPORT z_xx_gos_demo.
DATA: go_gos_manager TYPE REF TO cl_gos_manager,
go_container TYPE REF TO cl_gui_custom_container,
ls_object TYPE borident,
lv_ebeln TYPE ekko-ebeln VALUE '4500000001',
lv_mode TYPE sgs_rwmod.
CALL SCREEN 100.
*---------------------------------------------------------------------*
* PBO Module
*---------------------------------------------------------------------*
MODULE pbo_0100 OUTPUT.
" ★ 한 번만 생성 가드
IF go_container IS INITIAL.
" 컨테이너 생성 (Dynpro Layout 에 GOS_AREA Custom Control 그려져 있어야 함)
go_container = NEW #( container_name = 'GOS_AREA' ).
" BOR 객체 키 채움
ls_object-objtype = 'BUS2012'. " PO
ls_object-objkey = lv_ebeln.
" T-Code 따라 조회/변경 모드 결정
IF sy-tcode CP '*03' OR sy-tcode CP '*23N'.
lv_mode = 'A'.
ELSE.
lv_mode = 'E'.
ENDIF.
" ★ GOS Manager 인스턴스 생성
go_gos_manager = NEW #(
io_container = go_container
is_object = ls_object
ip_mode = lv_mode
ip_no_commit = 'X'
).
ENDIF.
ENDMODULE.
*---------------------------------------------------------------------*
* PAI Module
*---------------------------------------------------------------------*
MODULE user_command_0100 INPUT.
CASE sy-ucomm.
WHEN 'BACK' OR 'EXIT' OR 'CANC'.
" GOS Manager 정리
IF go_gos_manager IS NOT INITIAL.
CALL METHOD go_gos_manager->unpublish.
ENDIF.
LEAVE PROGRAM.
ENDCASE.
ENDMODULE.
*---------------------------------------------------------------------*
* 첨부 유무 체크 (활용 예시)
*---------------------------------------------------------------------*
FORM check_attachment USING p_ebeln TYPE ekko-ebeln
CHANGING p_has_atta TYPE abap_bool.
SELECT COUNT(*)
INTO @DATA(lv_cnt)
FROM srgbrbrel
WHERE instid_a = @p_ebeln
AND typeid_a = 'BUS2012'
AND catid_a = 'BO'
AND reltype = 'ATTA'.
IF lv_cnt > 0.
p_has_atta = abap_true.
ELSE.
p_has_atta = abap_false.
ENDIF.
ENDFORM.
같이 보면 좋은 글:
- "BAPI_PO_CREATE1 — 구매오더 생성 BAPI 호출 방법" — PO 생성 직후
SET_ID_OF_PUBLISHED_OBJECT로 첨부 연결 - "Screen Exit 커스텀 필드 추가 — MAP_DYNPRO_FIELDS BAdI 활용 방법" — 표준 화면에 GOS 컨테이너 끼우는 시나리오와 짝꿍
- "BAdI 정의로 Screen Exit 구축 — Function Group · Screen · SUBSCRIBE 매핑" — 자체 BAdI 화면에 GOS 배치
요약
| 단계 | 하는 일 | 핵심 포인트 |
|---|---|---|
| 1 | BOR 객체 결정 | BUS2012·BUS2017·BUS2081 등 표준 객체 |
| 2 | 컨테이너 준비 | SE51 Custom Control + CL_GUI_CUSTOM_CONTAINER |
| 3 | 인스턴스 생성 | is_object + ip_mode + ip_no_commit |
| 4 | PBO 가드 | IF 가드로 1회만 생성 (메모리 누수 방지) |
| 5 | 모드 분기 | 조회 = 'A' · 변경 = 'E' · 권한 / T-Code 기반 |
| 6 | 첨부 유무 체크 | SRGBRBREL SELECT (대용량) / BINARY_RELATION_GET_ALL_PREDEC FM |
CL_GOS_MANAGER 는 회사 자체 ALV / 화면에 표준 ME23N 수준의 첨부 UX 를 한 번에 끼워 넣어주는 가장 가벼운 도구입니다. 컨테이너 + IS_OBJECT + 모드 3가지만 정확히 채우면 첨부 / 노트 / 송신 / 워크플로우 같은 표준 서비스가 자동으로 따라옵니다. 신규 생성 흐름에서 객체 키가 나중에 정해지는 시나리오는 SET_ID_OF_PUBLISHED_OBJECT 로 후 연결하고, 대용량 ALV 의 첨부 유무 체크는 SRGBRBREL 직접 SELECT 가 가장 빠릅니다.
Disclaimer — 이 포스트는 실무 정리 노트를 바탕으로 AI 보조로 정리되었습니다.
CL_GOS_MANAGER 시그니처 · BORIDENT 구조체 · SRGBRBREL 테이블 동작은 SAP NetWeaver 표준(패키지 SGOS · ECC 6.0 / S/4HANA on-premise) 기준이며, 사내 BOR 객체 / 권한 / 첨부 정책에 따라 일부 동작이 다를 수 있으니 운영 시스템 적용 전 개발·QA 환경에서 검증하시기 바랍니다. Z 자체 객체에 첨부를 붙이는 경우 SWO1 BOR 객체 정의 또는 Generic OAC0 저장소를 함께 검토하시기 바랍니다.