쌓고 쌓다
[ABAP] XML로 그래프 커스터마이징하여 그리기 본문
목표

막대그래프, 꺾은선 그래프를 만들어보자.
그런데 더 세밀한 커스터마이징이 가능한.
마지막에 보여주려 했는데 선공개하자면

이렇게도 막대 그래프를 겹쳐서 그릴 수 있다.
다양하게 설정할 수 있는 값들이 많다.
잡담
CL_GUI_CUSTOM_CONTAINER와 CL_GUI_CONTAINER
타입에 대해 많이 헷갈려서
Split Container를 이용해 CHART ENGINE으로 그래프를 그리는 부분에서
CL_GUI_CUSTOM_CONTAINER과 CL_GUI_CONTAINER
를 사용하는 타입이 각각 다를 때
어떻게 막 변환하고 업캐스트하고 해야 하나 싶었는데
다음과 같이 정리하니깐 쉽게 CHART ENGINE을 사용할 수 있었다.
CL_GUI_CUSTOM_CONTAINER는
Screen Painter에서 직접 그린 Custom Control 영역에 연결하는 클래스이고
CL_GUI_CONTAINER는 이미 Split Container를 생성할 때
CL_GUI_CONTAINER 타입으로 생성되어 있는 Container이다.
GET_CONTAINER를 통해 받아와서 CHART ENGINE의 부모로 사용하면 되었다.

위 사진처럼 말이다.
주저리주저리 했는데
즉, CL_GUI_CONTAINER로 차트를 그려 넣을 수 있다.
1. 기본 환경 세팅

우선 위의 화면처럼 기본 그래프가 출력되는 환경을 맞춰보자.
그리고 누적 막대 그래프를 그려볼 것이다.
TOP Include

이전의 ALV 사진에
Split Container로 찢어진 2개의 영역 중
하단의 그래프 영역에 사용되는 변수는 빨간색으로 표시한 3개가 사용된다.
ALV Init

두 번째 영역에 CHART_CONTAINER과 CHART_ENGINE를 세팅한다.
이까지하면 다음과 같이 기본으로 표시되는 차트가 잘 나온다.

2. GRAPHICS_GUI_CE_DEMO

'GRAPHICS_GUI_CE_DEMO' 스탠다드 프로그램을 사용해서
원하는 차트를 만들어보자.
여기서 세밀한 커스터마이징이 가능하다는 것이다.
원하는 형태로 수정하여 차트 디자인을 구성하고 파일을 생성하면 된다.

위의 형태로 XML 파일을 생성했다.

위처럼 내용이 구성된 파일이다.
우리가 필요한 부분은 <SAPChart~> 머시기 부분이다.
3. STRANS
T-Code : STRANS
에 들어간다.

Create 클릭

엔터

<tt:template> 여는 태그와 </tt:template> 닫는 태그 사이에
GRAPHICS_GUI_CE_DEMO 프로그램으로 생성된 xml 내용들을 넣는다.

<SAPChart~> 머시기 부분을 저 부분에 넣어주면 된다.
내가 만든 커스터마이징 디자인으로 틀을 사용하겠다한 것이다.
4. ABAP 코드
저 틀에 사용되는 데이터는 ChartData 태그로 구성된다.
ChartData 태그에 포함되는
ChartData, Categories, Category, Series, Point, Value의 형태를 먼저 이해하고 코드를 보자.

위의 형태로 ChartData가 구성된다면

이렇게 차트가 구성된다.
차트에 들어가는 카테고리, 시리즈, 제목 등
동적으로 구성하거나 반복문을 통해 값을 넣는 방법도 존재하니 참고 링크를 보자.
이제 ChartData 태그의 형식을 이해했으면 이것을
ABAP 코드로 구성해서 데이터를 세팅해 주면 된다.



전체 코드
FORM SHOW_DAY_GRAPH .
* 1. XML로 Chart Engine 세팅
DATA : LV_XML TYPE XSTRING.
CALL TRANSFORMATION ZSIMPLE_CHART_CUST_086
SOURCE ROOT = SPACE " ST의 <tt:root name="ROOT"/>와 매핑
RESULT XML LV_XML.
GO_CHART_ENGINE->SET_CUSTOMIZING( XDATA = LV_XML ).
* 2. 데이터 세팅 - Dummy 데이터 넣기
DATA: LO_IXML TYPE REF TO IF_IXML,
LO_DOC TYPE REF TO IF_IXML_DOCUMENT,
LO_ROOT TYPE REF TO IF_IXML_ELEMENT,
LO_CATEGORIES TYPE REF TO IF_IXML_ELEMENT,
LO_CATEGORY TYPE REF TO IF_IXML_ELEMENT,
LO_LOCAL_SERIES TYPE REF TO IF_IXML_ELEMENT,
LO_FOREIGN_SERIES TYPE REF TO IF_IXML_ELEMENT,
LO_POINT TYPE REF TO IF_IXML_ELEMENT,
LO_VALUE TYPE REF TO IF_IXML_ELEMENT,
LO_STREAM_FACTORY TYPE REF TO IF_IXML_STREAM_FACTORY,
LO_OSTREAM TYPE REF TO IF_IXML_OSTREAM,
LO_RENDERER TYPE REF TO IF_IXML_RENDERER,
LV_XDATA TYPE XSTRING.
" 1. iXML 기본 객체 생성
LO_IXML = CL_IXML=>CREATE( ).
LO_DOC = LO_IXML->CREATE_DOCUMENT( ).
" 2. 루트 엘리먼트 <ChartData> 생성 및 추가
LO_ROOT = LO_DOC->CREATE_SIMPLE_ELEMENT( NAME = 'ChartData' PARENT = LO_DOC ).
" 3. <Categories> 영역 생성
LO_CATEGORIES = LO_DOC->CREATE_SIMPLE_ELEMENT( NAME = 'Categories' PARENT = LO_ROOT ).
" 4. <Series> 영역 생성 (예: 국내매출)
LO_LOCAL_SERIES = LO_DOC->CREATE_SIMPLE_ELEMENT( NAME = 'Series' PARENT = LO_ROOT ).
LO_LOCAL_SERIES->SET_ATTRIBUTE( NAME = 'label' VALUE = '국내매출' ).
" 4. <Series> 영역 생성 (예: 해외매출)
LO_FOREIGN_SERIES = LO_DOC->CREATE_SIMPLE_ELEMENT( NAME = 'Series' PARENT = LO_ROOT ).
LO_FOREIGN_SERIES->SET_ATTRIBUTE( NAME = 'label' VALUE = '해외매출' ).
DO 12 TIMES.
" 카테고리(X축) 값 추가
LO_CATEGORY = LO_DOC->CREATE_SIMPLE_ELEMENT( NAME = 'Category' PARENT = LO_CATEGORIES ).
LO_CATEGORY->IF_IXML_NODE~SET_VALUE( SY-INDEX && '월' ).
" Series 내부의 데이터 포인트 추가 (X월 국내매출)
LO_POINT = LO_DOC->CREATE_SIMPLE_ELEMENT( NAME = 'Point' PARENT = LO_LOCAL_SERIES ).
LO_VALUE = LO_DOC->CREATE_SIMPLE_ELEMENT( NAME = 'Value' PARENT = LO_POINT ).
LO_VALUE->IF_IXML_NODE~SET_VALUE( |{ SY-INDEX }| ).
" Series 내부의 데이터 포인트 추가 (X월 해외매출)
LO_POINT = LO_DOC->CREATE_SIMPLE_ELEMENT( NAME = 'Point' PARENT = LO_FOREIGN_SERIES ).
LO_VALUE = LO_DOC->CREATE_SIMPLE_ELEMENT( NAME = 'Value' PARENT = LO_POINT ).
LO_VALUE->IF_IXML_NODE~SET_VALUE( |{ 12 - SY-INDEX }| ).
ENDDO.
" 6. 완성된 DOM을 XSTRING으로 렌더링 (변환)
LO_STREAM_FACTORY = LO_IXML->CREATE_STREAM_FACTORY( ).
LO_OSTREAM = LO_STREAM_FACTORY->CREATE_OSTREAM_XSTRING( LV_XDATA ).
LO_RENDERER = LO_IXML->CREATE_RENDERER( OSTREAM = LO_OSTREAM DOCUMENT = LO_DOC ).
LO_RENDERER->RENDER( ).
" 7. Chart Engine에 데이터 세팅 및 출력
GO_CHART_ENGINE->SET_DATA( XDATA = LV_XDATA ).
GO_CHART_ENGINE->RENDER( ).
ENDFORM.
위의 서브루틴은 클릭 시 그래프를 그리는 서브루틴이다.
위의 예제 코드로 아래의 차트가 그려진다.

내가 만든 틀에 데이터를 넣어 차트가 그려진다.
CALL TRANSFORMATION를 처음 알았는데 ABAP 코드 <-> XML 간 변환해 주는 것이다.
정말 다양하게 커스터마이징이 가능하다.

이렇게 구성할 수도 있다.
현재 차트 제목을 고정되게 템플릿 XML 안에 구성되어 있지만
참고 링크에는 동적으로 타이틀을 구성하거나 범례, 시리즈, 카테고리를 반복문을통해 넣는 방법이 존재한다.
참고 : https://community.sap.com/t5/additional-blog-posts-by-members/easy-charts-using-cl-gui-chart-engine/ba-p/13176916
보너스
싱크 때 강사님이 알려준 막대 그래프 그리는 방식을
이제 되돌아보니 이해가 간다.
순수 IXML 인터페이스? 클래스?를 사용해서 그리는 방식이었다

위 사진처럼 그려지는 코드이다.
STRANS 티코드를 사용하지 않고 순수 아밥 코드로만 막대 그래프를 그리고 싶으면 다음 코드를 참고하세요~!
TOP
* Chart
DATA: GO_CHART_CONTAINER TYPE REF TO CL_GUI_CUSTOM_CONTAINER,
GO_CHART TYPE REF TO CL_GUI_CHART_ENGINE.
DATA: GO_IXML TYPE REF TO IF_IXML,
GO_IXML_SF TYPE REF TO IF_IXML_STREAM_FACTORY,
GO_IXML_DATA_DOC TYPE REF TO IF_IXML_DOCUMENT,
GO_OSTREAM TYPE REF TO IF_IXML_OSTREAM,
GO_ENCODING TYPE REF TO IF_IXML_ENCODING.
그리는 코드 부분
FORM GET_BALANCE_DATA USING PV_NODE_KEY.
DATA: LV_MONTH TYPE N LENGTH 2.
DATA: LS_T040 TYPE ZTFC_FIT040.
* 이월 금액, 통화
DATA: LV_CARRYFORWARD TYPE ZTFC_FIT040-CARRYFORWARD,
LV_WAERS TYPE ZTFC_FIT040-WAERS.
* Grid Title
DATA: LV_GLTXT TYPE STRING.
* 차트
DATA: ROOT TYPE REF TO IF_IXML_ELEMENT,
CATEGORIES TYPE REF TO IF_IXML_ELEMENT,
CATEGORY TYPE REF TO IF_IXML_ELEMENT,
SERIES_DEBIT TYPE REF TO IF_IXML_ELEMENT,
SERIES_CREDIT TYPE REF TO IF_IXML_ELEMENT,
POINT TYPE REF TO IF_IXML_ELEMENT,
VALUE TYPE REF TO IF_IXML_ELEMENT,
XDATA TYPE XSTRING,
DEBIT_STRING TYPE STRING,
CREDIT_STRING TYPE STRING.
* 선택된 계정과목코드로 계정과목명 조회
SELECT SINGLE GLTXT
FROM ZTFC_FIT010
INTO LV_GLTXT
WHERE SKANR = PV_NODE_KEY.
* ALV TItle 설정
GS_BALANCE_LAYOUT-GRID_TITLE = '[' && LV_GLTXT && ']'.
CALL METHOD GO_BALANCE_ALV_GRID->SET_FRONTEND_LAYOUT
EXPORTING
IS_LAYOUT = GS_BALANCE_LAYOUT.
CALL METHOD GO_CHART_CONTAINER->SET_VISIBLE( EXPORTING VISIBLE = ABAP_TRUE ).
* 차트 - procesing data
GO_IXML = CL_IXML=>CREATE( ).
GO_IXML_SF = GO_IXML->CREATE_STREAM_FACTORY( ).
* 차트 - create an empty document and set encoding
GO_IXML_DATA_DOC = GO_IXML->CREATE_DOCUMENT( ).
GO_ENCODING = GO_IXML->CREATE_ENCODING(
BYTE_ORDER = IF_IXML_ENCODING=>CO_LITTLE_ENDIAN
CHARACTER_SET = 'utf-8' ).
GO_IXML_DATA_DOC->SET_ENCODING( GO_ENCODING ).
ROOT = GO_IXML_DATA_DOC->CREATE_SIMPLE_ELEMENT( NAME = 'ChartData' PARENT = GO_IXML_DATA_DOC ).
CATEGORIES = GO_IXML_DATA_DOC->CREATE_SIMPLE_ELEMENT( NAME = 'Categories' PARENT = ROOT ).
SERIES_DEBIT = GO_IXML_DATA_DOC->CREATE_SIMPLE_ELEMENT( NAME = 'Series' PARENT = ROOT ).
SERIES_CREDIT = GO_IXML_DATA_DOC->CREATE_SIMPLE_ELEMENT( NAME = 'Series' PARENT = ROOT ).
SERIES_DEBIT->SET_ATTRIBUTE( NAME = 'label' VALUE = |{ LV_GLTXT } 차변 (단위 : 원)| ).
SERIES_CREDIT->SET_ATTRIBUTE( NAME = 'label' VALUE = |{ LV_GLTXT } 대변 (단위 : 원)| ).
CLEAR GT_DATA.
CLEAR GS_DATA.
* 이월 금액
SELECT SINGLE CARRYFORWARD WAERS
INTO (LV_CARRYFORWARD, LV_WAERS)
FROM ZTFC_FIT040
WHERE GJAHR = GV_YEAR
AND SKANR = PV_NODE_KEY
AND MON = 0.
IF SY-SUBRC <> 0.
LV_CARRYFORWARD = 0.
LV_WAERS = 'KRW'.
ENDIF.
GS_DATA-MON = 0.
GS_DATA-MON_TXT = '이월'.
GS_DATA-BALANCE = LV_CARRYFORWARD.
GS_DATA-WAERS = LV_WAERS.
APPEND GS_DATA TO GT_DATA.
DO 12 TIMES.
CLEAR GS_DATA.
GS_DATA-MON = SY-INDEX.
GS_DATA-MON_TXT = SY-INDEX && '월'.
GS_DATA-WAERS = 'KRW'.
LV_MONTH = SY-INDEX.
SELECT SINGLE *
INTO CORRESPONDING FIELDS OF LS_T040
FROM ZTFC_FIT040
WHERE GJAHR = GV_YEAR
AND MON = LV_MONTH
AND SKANR = PV_NODE_KEY.
GS_DATA-DEBIT = LS_T040-DEBIT.
GS_DATA-CREDIT = LS_T040-CREDIT.
GS_DATA-BALANCE = LS_T040-BALANCE.
APPEND GS_DATA TO GT_DATA.
* 차트
CATEGORY = GO_IXML_DATA_DOC->CREATE_SIMPLE_ELEMENT( NAME = 'Category' PARENT = CATEGORIES ).
CATEGORY->IF_IXML_NODE~SET_VALUE( GS_DATA-MON_TXT ).
* 차변
DEBIT_STRING = GS_DATA-DEBIT * 100.
POINT = GO_IXML_DATA_DOC->CREATE_SIMPLE_ELEMENT( NAME = 'Point' PARENT = SERIES_DEBIT ).
VALUE = GO_IXML_DATA_DOC->CREATE_SIMPLE_ELEMENT( NAME = 'Value' PARENT = POINT ).
VALUE->SET_ATTRIBUTE( NAME = 'type' VALUE = 'y' ).
VALUE->IF_IXML_NODE~SET_VALUE( DEBIT_STRING ).
* 대변
CREDIT_STRING = GS_DATA-CREDIT * 100.
POINT = GO_IXML_DATA_DOC->CREATE_SIMPLE_ELEMENT( NAME = 'Point' PARENT = SERIES_CREDIT ).
VALUE = GO_IXML_DATA_DOC->CREATE_SIMPLE_ELEMENT( NAME = 'Value' PARENT = POINT ).
VALUE->SET_ATTRIBUTE( NAME = 'type' VALUE = 'y' ).
VALUE->IF_IXML_NODE~SET_VALUE( CREDIT_STRING ).
ENDDO.
CLEAR GS_DATA.
GS_DATA-MON = 13.
GS_DATA-MON_TXT = '차기이월'.
GS_DATA-CARRYOVER = LS_T040-BALANCE.
GS_DATA-BALANCE = LS_T040-BALANCE.
GS_DATA-WAERS = 'KRW'.
APPEND GS_DATA TO GT_DATA.
* 차트
GO_OSTREAM = GO_IXML_SF->CREATE_OSTREAM_XSTRING( XDATA ).
GO_IXML_DATA_DOC->RENDER( GO_OSTREAM ).
GO_CHART->SET_DATA( XDATA = XDATA ).
GO_CHART->RENDER( ).
ENDFORM.
여담
누적 막대 그래프를 고객이 요구한다고 회사에서 얼핏 들어서
혼자 누적 막대 그래프를 그려보려 했는데 결국 실패했다.
나중에 그 요구사항을 받으신 선임분께 어떻게 했는지 여쭤봐야겠다.
위의 방법으로는 꺾은선, 막대 그래프만 커스터마이징하여 잘 그릴 수 있다.
그 외 그래프는 Sap Chart Designer 라는 프로그램을 다운로드하여
XML을 뽑아내서 그려야 한다고 하는데
어찌저찌 Sap Note를 뒤져 프로그램을 찾아서 다운로드했지만 sapchartd.ocx 파일이 없다며 에러를 뱉어서 실패했다.
https://userapps.support.sap.com/sap/support/knowledge/en/3566703
3566703 - Chart Designer Execution Failure Post SAP GUI 800 (64bit) Installation | SAP Knowledge Base Article
3566703 - Chart Designer Execution Failure Post SAP GUI 800 (64bit) Installation Symptom After installing SAP GUI 800 (64bit), the execution of ChartDesigner700.exe is not possible.Initial error message: "Component "comdlg32.ocx" or one of its dependencies
userapps.support.sap.com
'SAP > ABAP' 카테고리의 다른 글
| [ABAP] CREATE DATA, 동적 Internal Table에 Insert하기 (0) | 2026.01.09 |
|---|---|
| [ABAP] 동적 ALV 필드 구성 및 월별 주차 계산하기 (0) | 2026.01.03 |
| [ABAP] 'GFW_PRES_SHOW' 펑션으로 그래프 차트 쉽게 그리기 (1) | 2025.12.27 |
| [ABAP] SAP Memory, ABAP Memory 이해와 차이점 (0) | 2025.12.21 |
| [ABAP] 사용자 파라미터 (사용자 마스터 매개변수 ID) 테이블 (0) | 2025.12.21 |