SAP S/4HANA 시대, ABAP 개발의 핵심인 CDS View에 대해서 알아볼까 합니다.
CDS View의 기본 개념, 코드 푸시다운, 기존 뷰와의 비교, AMDP 등
그리고 실무에 바로 적용 가능한 MM 모듈 구매 오더 조회 예제까지 작성해 보겠습니다
SAP S/4HANA 개발의 새로운 표준, CDS View 완벽 가이드
SAP가 S/4HANA 시대로 전환되면서, 이제 더 이상 CDS Views등 개발 방식에도 거대한 변화의 바람이 불고 있습니다.
"아직도 SE11에서 뷰(View)를 만드시나요?"라는 질문이 어색하지 않을 만큼, CDS View는 이제 선택이 아닌 필수 역량이 되었습니다.
1. CDS View 대체 왜 써야 할까요? (feat. 코드 푸시다운)
CDS View를 한마디로 정의하면 "데이터베이스의 능력을 최대한 활용하는 차세대 데이터 모델링 도구"입니다.
과거의 ABAP 개발 방식(Data-to-Code)은 필요한 데이터를 전부 애플리케이션 서버로 가져와서 ABAP 코드로 가공했습니다.
데이터가 많아질수록 서버에 부하가 걸리고 속도가 느려지는 건 당연한 수순이었죠.
하지만 CDS View(Code-to-Data)는 패러다임을 바꿨습니다. 계산, 집계 등 복잡한 로직을 데이터가 있는 HANA DB에서 직접 수행하고, 애플리케이션 서버는 그 최종 결과만 받습니다. 이를 '코드 푸시다운(Code Pushdown)'이라고 부르며, S/4HANA의 압도적인 성능 향상의 핵심 비결입니다.
2. 무엇이 다른가: ABAP Dictionary View vs ABAP CDS View
구분 | ABAP CDS View | ABAP Dictionary View |
핵심 철학 | 코드 푸시다운 (HANA DB 최적화) | 데이터 이동 후 처리 (서버 부하) |
개발 도구 | 이클립스 기반 ADT | SAP GUI (SE11) |
주요 기능 | SQL 내장 함수, 계산된 필드, 어노테이션(@) | 기본적인 Join, Projection, Selection |
성능 | 인메모리 기반 고속 처리 | DB 종류에 따라 성능 편차 발생 |
확장성 | Extend View로 표준 확장 용이 | 수정(Modification) 필요 |
현대 기술 연동 | Fiori, SAC 등과 직접 연동 (OData 서비스) | 별도 인터페이스 개발 필요 |
3. CDS View의 핵심 기능: 어노테이션과 VDM
CDS View의 강력함은 두 가지 핵심 요소에서 나옵니다.
① 의미를 부여하는 마법, 어노테이션(Annotation)
어노테이션(@)은 CDS View에 단순한 데이터 정의를 넘어 '비즈니스 의미'와 '기능'을 부여합니다.
- @OData.publish: true : 단 한 줄로 Fiori 앱이 사용할 수 있는 OData 서비스를 발행합니다.
- @UI.lineItem: [{ position: 10 }] : Fiori 화면에 이 필드를 1번째 컬럼으로 보여달라고 정의합니다.
- @Semantics.amount.currencyCode: '...' : 이 필드가 금액이며, 참조하는 통화키가 무엇인지 명시합니다.
코드가 곧 설계도가 되는, **'의미가 풍부한 데이터 모델(Semantically Rich Data Models)'**을 만들 수 있는 것입니다.
② 체계적인 설계, VDM(Virtual Data Model)
CDS View는 목적에 따라 3가지 계층으로 나누어 설계하는 것을 권장합니다. 이를 VDM 아키텍처라고 합니다.
- Basic (Interface) View: DB 테이블의 원자재를 정제하는 1차 가공 단계.
- Composite (Composition) View: Basic 뷰들을 조합해 비즈니스 로직을 구현하는 중간 조립 단계.
- Consumption View: 최종 사용자가 소비(Fiori 앱, 리포트 등)하는 완제품 단계.
이렇게 계층적으로 설계하면 재사용성과 유지보수성이 극대화됩니다.
4. MM 모듈 구매 오더 리포트 CDS View 만들기
백문이 불여일견이죠. MM 모듈에서 가장 많이 쓰이는 '구매 오더 현황 조회'를 VDM 아키텍처에 맞춰 직접 만들어 보겠습니다.
관련테이블: 구매 오더 헤더(EKKO), 아이템(EKPO), 공급업체명(LFA1), 자재명(MAKT)
1단계: Basic View (원자재 가공)
먼저 EKKO와 EKPO의 핵심 데이터를 뽑아내는 Basic View를 만듭니다
/*******************************************************************
* 1. Basic View: 구매 오더의 핵심 데이터(헤더/아이템)를 정제
*******************************************************************/
@AbapCatalog.sqlViewName: 'Z_I_POITEM_SQL'
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Basic View for Purchase Order Item'
define view Z_I_PurchaseOrderItem
as select from ekpo as Item
association [1] to ekko as _Header on $projection.PurchaseOrder = _Header.ebeln
association [0..1] to lfa1 as _Vendor on _Header.lifnr = _Vendor.lifnr
association [0..1] to makt as _MaterialText on $projection.Material = _MaterialText.matnr
and _MaterialText.spras = $session.system_language
{
key Item.ebeln as PurchaseOrder, // 구매 오더 번호
key Item.ebelp as PurchaseOrderItem, // 아이템 번호
_Header.lifnr as Vendor, // 공급업체 코드
Item.matnr as Material, // 자재 코드
Item.menge as OrderQuantity, // 주문 수량
Item.meins as OrderUnit, // 단위
Item.netpr as NetPrice, // 단가
_Header.waers as CurrencyCode, // 통화
/* Associations */
_Header, _Vendor, _MaterialText
}
2단계: Composite View
1단계에서 만든 Basic View에 공급업체명과 자재명을 붙여 의미를 더합니다.
/*******************************************************************
* 2. Composite View: Basic View에 마스터 데이터를 조합
*******************************************************************/
@AbapCatalog.sqlViewName: 'Z_C_POINFO_SQL'
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Composite View for PO Information'
define view Z_C_PurchaseOrderInfo
as select from Z_I_PurchaseOrderItem as PO
{
PurchaseOrder, PurchaseOrderItem, Vendor,
PO._Vendor.name1 as VendorName, // Association으로 공급업체명 추가
Material,
PO._MaterialText.maktx as MaterialText, // Association으로 자재 텍스트 추가
OrderQuantity, OrderUnit, NetPrice, CurrencyCode
}
3단계: Consumption View
Fiori 앱에서 직접 사용할 최종 뷰입니다. UI 관련 어노테이션을 집중적으로 사용합니다.
/*******************************************************************
* 3. Consumption View: Fiori 앱 등 최종 사용을 위한 뷰
*******************************************************************/
@AbapCatalog.sqlViewName: 'Z_F_POREPORT_SQL'
@EndUserText.label: 'Consumption View for PO Report'
@OData.publish: true // Fiori가 사용할 OData 서비스 자동 생성!
@UI.headerInfo: { typeName: 'Purchase Order', typeNamePlural: 'Purchase Orders' }
define view Z_F_PurchaseOrderReport
as select from Z_C_PurchaseOrderInfo
{
// @UI 어노테이션으로 Fiori 화면의 컬럼 순서, 검색 조건 등을 정의
@UI.lineItem: [{ position: 10, label: 'PO Number' }]
@UI.selectionField: [{ position: 10 }]
key PurchaseOrder,
@UI.lineItem: [{ position: 20, label: 'Item No.' }]
key PurchaseOrderItem,
@UI.lineItem: [{ position: 30, label: 'Vendor Name' }]
@UI.selectionField: [{ position: 20 }]
VendorName,
@UI.lineItem: [{ position: 40, label: 'Material' }]
Material,
@UI.lineItem: [{ position: 50, label: 'Material Description' }]
MaterialText,
@UI.lineItem: [{ position: 60, label: 'Quantity' }]
OrderQuantity,
@Semantics.amount.currencyCode: 'CurrencyCode' // 금액 필드 의미 부여
@UI.lineItem: [{ position: 80, label: 'Net Price' }]
NetPrice,
CurrencyCode
}
뷰를 만들고 활성화하면, 별도 코딩 없이도 Fiori Elements 앱으로 만들 수 있는 OData 서비스가 즉시 생성됩니다.
지금까지 S/4HANA 개발의 표준으로 자리 잡은 CDS View에 대해 알아보았습니다.
처음에는 이클립스 기반의 ADT 환경이나 생소한 어노테이션 때문에 어렵게 느껴질 수 있습니다.
여기서 끝낸다면 너무 시시하겠죠? 그래서 준비했습니다.
공급업체 위탁 재고 현황 리포트 개발
특정 플랜트(Plant)에 보관된 공급업체의 위탁 재고(Consignment Stock) 현황을 실시간으로 조회되고
리포트에는 공급업체명, 자재 번호 및 텍스트, 플랜트명, 위탁 재고 수량 등의 정보가 포함되어야 하며, Fiori 앱을 통해 쉽게 필터링되는 프로그램을 구현해 보겠습니다.
1. ADT 개발 절차 (Step-by-Step)
1단계: 프로젝트 및 패키지 확인
- ADT에서 ABAP Perspective를 엽니다.
- Project Explorer에서 대상 시스템 프로젝트를 선택하고, 개발을 진행할 패키지(Package)를 정합니다.
테스트 이므로 Local Object에 $TMP패키지에 만들어 보겠습니다.
2단계: Basic/Interface View 생성 (Z_I_VendorConsignStock)
가장 먼저 데이터의 원천이 되는 DB 테이블에서 필요한 데이터를 정제하고 기본적인 연관 관계를 맺어주는 Basic View를 생성합니다.
- 패키지($TMP) 위에서 마우스 오른쪽 클릭 > New > Other ABAP Repository Object를 선택합니다.
- Core Data Services > Data Definition을 선택하고 Next를 클릭합니다.
- 아래와 같이 이름을 입력하고 Finish를 눌러 DDL 소스 파일을 생성합니다.
- Name: Z_I_VENDORCONSIGNSTOCK
- Description: Basic View for Vendor Consignment Stock
- 이 코드는 공급업체별 특수 재고 테이블(MSSL)을 중심으로 자재, 공급업체, 플랜트 마스터 테이블과의 관계를 association으로 정의합니다.
@AbapCatalog.sqlViewName: 'ZIVENDCONSSTCK'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Basic View for Vendor Consignment Stock'
@Metadata.ignorePropagatedAnnotations: true
/*******************************************************************
* 1. Basic View: 공급업체 위탁 재고 데이터 정제 및 기본 관계 설정
* - 주 테이블: MSSL (공급업체별 특수 재고)
*******************************************************************/
define view Z_I_VendorConsignStock
-- Association: Join보다 유연하며, 실제 사용될 때만 DB에서 데이터를 가져와 성능에 유리합니다.
as select from mssl as Stock
association [1..1] to mara as _Material on $projection.Material = _Material.matnr
association [0..1] to makt as _MaterialText on $projection.Material = _MaterialText.matnr
and _MaterialText.spras = $session.system_language
association [0..1] to lfa1 as _Vendor on $projection.Vendor = _Vendor.lifnr
association [0..1] to t001w as _Plant on $projection.Plant = _Plant.werks
{
key Stock.matnr as Material, // 자재 번호
key Stock.werks as Plant, // 플랜트
key Stock.lifnr as Vendor, // 공급업체
// 위탁 재고(K)만 필터링합니다. 'slabs'는 위탁 재고 수량 필드입니다.
@Semantics.quantity.unitOfMeasure: 'BaseUnit'
Stock.sllab as ConsignmentStockQuantity,
// 수량 필드의 단위를 참조하기 위해 자재 마스터의 기본 단위를 가져옵니다.
_Material.meins as BaseUnit,
/* Associations: 상위 뷰에서 마스터 데이터를 쉽게 가져올 수 있도록 관계를 노출합니다. */
_Material,
_MaterialText,
_Vendor,
_Plant
}
// 특수 재고 유형(SOBKZ)이 'K'(공급업체 위탁)인 데이터만 조회합니다.
where Stock.sobkz = 'K'
// 재고 수량이 0보다 큰 데이터만 조회합니다.
and Stock.sllab > 0
3단계: Composite View 생성 (Z_C_VendorConsignStock)
Basic View를 기반으로, 실제 비즈니스 의미를 더해주는 Composite View를 만듭니다. 여기서는 association을 활용해 마스터 테이블의 텍스트 정보(공급업체명, 자재명 등)를 필드로 추가합니다.
- 위와 동일한 방법으로 Z_C_VENDORCONSIGNSTOCK 이름의 Data Definition을 생성합니다.
- 아래 코드를 붙여넣습니다. 이 뷰는 하위 뷰(Z_I_VendorConsignStock)를 select from하여 데이터를 가공합니다.
@AbapCatalog.sqlViewName: 'ZCVENDCONSSTCK'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Z_C_VENDORCONSIGNSTOCK'
@Metadata.ignorePropagatedAnnotations: true
/*******************************************************************
* 2. Composite View: Basic View에 마스터 텍스트를 조합하여 의미 부여
*******************************************************************/
define view Z_C_VendorConsignStock
as select from Z_I_VendorConsignStock as Consignment
{
// Key 필드는 그대로 전달합니다.
key Material,
key Plant,
key Vendor,
// Association을 사용하여 다른 테이블의 필드를 직접 가져옵니다.
// 이것이 VDM 계층 구조의 가장 큰 장점 중 하나입니다.
Consignment._Vendor.name1 as VendorName,
Consignment._MaterialText.maktx as MaterialText,
Consignment._Plant.name1 as PlantName,
ConsignmentStockQuantity,
BaseUnit,
/* Associations: 상위 뷰로 관계를 그대로 전달합니다. */
Consignment._Material,
Consignment._MaterialText,
Consignment._Vendor,
Consignment._Plant
}
4단계: Consumption View 생성 (Z_F_VendorConsignStock)
Fiori 앱을 통해 소비할 Consumption View를 만듭니다.
이 단계에서는 UI 표시에 필요한 어노테이션을 집중적으로 사용합니다.
- Z_F_VENDORCONSIGNSTOCKREPORT 이름의 Data Definition을 생성합니다.
- 아래 코드를 붙여넣습니다. @OData, @UI 어노테이션을 통해 Fiori Elements 앱의 화면 구성(검색 조건, 리스트 컬럼 등)을 정의합니다.
@AbapCatalog.sqlViewName: 'ZFVENDCONSSTCK'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Report - Vendor Consignment Stock'
@Metadata.ignorePropagatedAnnotations: true
/**************************************************************
* 1. 서비스 및 리포트 헤더 정의
**************************************************************/
@OData.publish: true
@UI.headerInfo: {
typeName: 'Vendor Consignment Stock',
typeNamePlural: 'Vendor Consignment Stocks',
title: {
type: #STANDARD,
label: 'Vendor Consignment Stock',
value: 'VendorName'
}
}
define view Z_F_VendorConsignStock
as select from Z_C_VendorConsignStock -- 2단계에서 만든 Composite View를 소스로 사용
{
/**************************************************************
* 2. 검색 조건(필터) 정의
**************************************************************/
@UI.selectionField: [{ position: 10 }]
key Vendor,
@UI.selectionField: [{ position: 20 }]
key Material,
@UI.selectionField: [{ position: 30 }]
key Plant,
/**************************************************************
* 3. 리스트 컬럼(결과) 정의
**************************************************************/
@UI.lineItem: [{ position: 10, label: 'Vendor' }]
VendorName,
@UI.lineItem: [{ position: 20, label: 'Material' }]
MaterialText,
@UI.lineItem: [{ position: 30, label: 'Plant' }]
PlantName,
@UI.lineItem: [{ position: 40, label: 'Stock Quantity', importance: #HIGH }]
@Semantics.quantity.unitOfMeasure: 'BaseUnit' // 수량 필드임을 명시하고 단위 필드를 연결
ConsignmentStockQuantity,
@UI.lineItem: [{ position: 50, label: 'Unit', importance: #MEDIUM }]
BaseUnit
}
5단계: 활성화 및 테스트
- 작성한 3개의 CDS View를 모두 저장(Ctrl+S)하고 활성화(Ctrl+F3) 합니다.(하위 뷰부터 순서대로 활성화해야 합니다.)
- 최종 뷰인 Z_F_VendorConsignStockReport 위에서 마우스 오른쪽 클릭 > Open With > Data Preview를 선택합니다.
- 데이터가 정상적으로 조회되는지 확인합니다.
결과 및 활용
위 과정을 모두 마치면, 별도의 백엔드 코딩 없이도 Fiori Elements(List Report) 앱으로 즉시 개발할 수 있는 OData 서비스(Z_F_VENDORCONSIGNSTOCKREPORT_CDS)가 생성됩니다.
CDS View의 계층적 설계(VDM)는 복잡한 비즈니스 로직을 체계적으로 분리하고, 재사용성을 극대화하며, 성능과 유지보수성을 모두 잡을 수 있는 S/4HANA 시대의 표준 개발 방식입니다
오늘은 여기까지 하겠습니다.
'ABAP' 카테고리의 다른 글
[ABAP] 애플리케이션 로그 마스터하기: BAL과 CBO 테이블 로깅, 언제 무엇을 써야 할까? (0) | 2025.07.13 |
---|---|
[ABAP] 출력 제어(Output Control) - Inbound Delivery 및 Invoice 생성하기 (0) | 2025.07.10 |
[ABAP] Part 2 - 시점별 재고 ( 전통적인ABAP + ALV 리포트) (0) | 2025.07.05 |
[ABAP] Part 1 - 시점별 재고 (CDS View + ALV 리포트) (0) | 2025.07.05 |
[ABAP] 표준 자재 검색(F4)에 나만의 탭 추가하기 (Append Search Help) (0) | 2025.07.02 |