개요와 이 글에서 얻어갈 것
CDS(Core Data Services)에서 @Annotation은 단순한 주석이 아닙니다. Fiori Elements가 자동으로 렌더링하는 리스트 컬럼, OData $metadata에 노출되는 필드의 의미론적 타입, 검색 우선순위, VDM(Virtual Data Model) 역할까지 모두 어노테이션 한 줄로 결정됩니다. 이 글에서는 실무에서 반드시 알아야 할 5가지 핵심 어노테이션 카테고리를 PurchaseOrder 시나리오로 재구성하여 살펴봅니다.
- OData 노출을 제어하는
@OData.publish의 동작 원리 이해 - Fiori Elements 리스트/오브젝트 페이지를 만드는
@UI어노테이션 5종 실전 적용 - 통화·수량·이메일 등을 표현하는
@Semantics로 값의 의미 명확화 - Enterprise Search 결과 랭킹을 조정하는
@Search활용 - VDM 계층 역할을 부여하는
@ObjectModel과 Metadata Extension 분리 패턴
알고 있어야 할 배경
이 글은 ABAP CDS View 문법(define view entity, association, select from)을 한 번이라도 작성해 본 개발자를 대상으로 합니다. OData v2/v4의 서비스 엔드포인트 개념, Fiori Elements List Report / Object Page 템플릿의 기본 구조, 그리고 SAP GUI 또는 ADT에서 CDS 오브젝트를 활성화해 본 경험이 있으면 충분합니다.
환경 및 도구 준비
이 글의 예제는 다음 환경을 기준으로 작성되었습니다.
- ABAP Platform: SAP S/4HANA 2022 On-Premise 또는 ABAP Environment (Steampunk) 최신 릴리스 권장
- IDE: ADT (Eclipse 2024-03 + ABAP Development Tools 3.40 이상)
- 미리보기: Fiori Elements Preview 또는 SAP Gateway Client
- OData 확인: 브라우저에서
/sap/opu/odata/sap/서비스명/$metadata로 XML 확인
핵심 개념: 어노테이션이 만들어내는 두 가지 산출물
CDS 어노테이션은 크게 두 가지 결과물을 생성합니다. 하나는 OData 서비스의 $metadata에 반영되는 기술적 메타데이터이고, 다른 하나는 Fiori Elements가 UI를 자동 렌더링할 때 참조하는 UI 힌트입니다.
CDS View가 "요리 재료 목록"이라면 어노테이션은 "이 재료는 소스로, 저 재료는 장식용"이라고 알려주는 라벨입니다. 같은 데이터라도 라벨에 따라 완전히 다른 화면이 만들어집니다.
어노테이션 카테고리는 접두어(namespace)로 구분됩니다.
| Namespace | 제어 대상 | 대표 어노테이션 |
|---|---|---|
@OData | 서비스 노출 | @OData.publish |
@UI | Fiori Elements 화면 레이아웃 | @UI.lineItem, @UI.facet, @UI.headerInfo |
@Semantics | 필드 의미(통화/수량/이메일 등) | @Semantics.amount.currencyCode |
@Search | Enterprise Search 활성화 | @Search.searchable, @Search.defaultSearchElement |
@ObjectModel | VDM 역할 정의 | @ObjectModel.usageType |
어노테이션은 계층적 우선순위를 가집니다. Metadata Extension에서 선언한 값이 가장 높은 우선순위를 가지며, 기본 CDS View 내부 선언이 가장 낮습니다.
실전 예제 1단계: OData 서비스 노출과 Semantics 적용
구매오더 헤더를 조회하는 기본 CDS View에 @OData.publish: true와 Semantics 어노테이션을 적용합니다.
@AbapCatalog.sqlViewName: 'ZVPORDHDR'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Purchase Order Header - Basic'
@OData.publish: true
define view Z_C_PurchaseOrderHdr
as select from ztab_pord_hdr
{
key po_number as PurchaseOrderNo,
supplier_id as SupplierID,
order_date as OrderDate,
@Semantics.amount.currencyCode: 'CurrencyCode'
total_amount as TotalAmount,
@Semantics.currencyCode: true
currency_code as CurrencyCode,
@Semantics.user.createdBy: true
created_by as CreatedBy
}@Semantics.amount.currencyCode는 금액 필드와 통화 필드를 연결합니다. OData $metadata에 sap:unit 속성이 자동으로 생성되어 Fiori Elements가 통화 심볼을 표시합니다. 금액 필드에만 붙이는 것으로는 부족하고 통화 필드에도 반드시 @Semantics.currencyCode: true를 붙여야 합니다.
활성화 후 SAP Gateway에서 서비스를 등록하면 OData 엔드포인트가 생성됩니다. 이 시점에서는 UI 힌트가 없어 Fiori Elements 미리보기 시 컬럼 순서가 무작위로 나열됩니다.
실전 예제 2단계: UI와 Search 어노테이션 실무 적용
Fiori Elements List Report 컬럼 순서, Object Page 헤더, 검색 활성화를 어노테이션으로 제어합니다.
@Search.searchable: true
@ObjectModel.usageType: {
serviceQuality: #D,
sizeCategory: #L,
dataClass: #TRANSACTIONAL
}
define view Z_C_PurchaseOrderHdr2
as select from ztab_pord_hdr
{
@UI.headerInfo: {
typeName: 'Purchase Order',
typeNamePlural: 'Purchase Orders',
title: { value: 'PurchaseOrderNo' }
}
@UI.facet: [{
id: 'General',
purpose: #STANDARD,
type: #IDENTIFICATION_REFERENCE,
label: 'General Information',
position: 10
}]
key @UI.lineItem: [{ position: 10, label: 'PO No.' }]
@UI.identification: [{ position: 10 }]
@Search.defaultSearchElement: true
po_number as PurchaseOrderNo,
@UI.lineItem: [{ position: 20, label: 'Supplier' }]
@Search.defaultSearchElement: true
@Search.fuzzinessThreshold: 0.8
supplier_id as SupplierID,
@UI.lineItem: [{ position: 30, label: 'Order Date' }]
order_date as OrderDate,
@UI.lineItem: [{ position: 40, label: 'Amount', importance: #HIGH }]
@Semantics.amount.currencyCode: 'CurrencyCode'
total_amount as TotalAmount,
@Semantics.currencyCode: true
currency_code as CurrencyCode
}각 어노테이션이 담당하는 역할입니다.
@UI.headerInfo: Object Page 상단 타이틀/서브타이틀 정의@UI.facet: Object Page 섹션(탭/블록) 구성.type: #IDENTIFICATION_REFERENCE는@UI.identification어노테이션이 붙은 필드들을 해당 섹션에 표시@UI.lineItem: List Report 테이블 컬럼과 순서.position은 10단위 간격 권장@Search.defaultSearchElement: true: Enterprise Search와 검색창의 기본 검색 대상으로 지정@Search.fuzzinessThreshold: HANA fuzzy search 유사도 임계치(0.0~1.0)@ObjectModel.usageType: VDM에서 이 View의 역할과 데이터 성격을 정의
실전 예제 3단계: Metadata Extension으로 관심사 분리
프로덕션에서는 비즈니스 정의(View)와 UI 정의(Extension)를 분리하는 것이 권장됩니다.
" 기본 View — UI 어노테이션 제거, 데이터 정의에 집중
@Metadata.allowExtensions: true
@OData.publish: true
define view Z_C_PurchaseOrderHdr3
as select from ztab_pord_hdr
{
key po_number as PurchaseOrderNo,
supplier_id as SupplierID,
order_date as OrderDate,
@Semantics.amount.currencyCode: 'CurrencyCode'
total_amount as TotalAmount,
@Semantics.currencyCode: true
currency_code as CurrencyCode
}
" Metadata Extension — UI 어노테이션만 담당
@Metadata.layer: #CUSTOMER
annotate view Z_C_PurchaseOrderHdr3 with
{
@UI.headerInfo: {
typeName: 'Purchase Order',
title: { value: 'PurchaseOrderNo' }
}
@UI.facet: [{
id: 'Main', purpose: #STANDARD,
type: #IDENTIFICATION_REFERENCE, position: 10
}]
@UI.lineItem: [{ position: 10, label: 'PO No.' }]
@UI.identification: [{ position: 10 }]
PurchaseOrderNo;
@UI.lineItem: [{ position: 20, label: 'Supplier' }]
SupplierID;
@UI.lineItem: [{ position: 30, label: 'Order Date' }]
OrderDate;
@UI.lineItem: [{ position: 40, label: 'Amount', importance: #HIGH }]
TotalAmount;
}@Metadata.allowExtensions: true가 기본 View에 있어야 Extension이 동작합니다. @Metadata.layer는 CORE → LOCALIZATION → INDUSTRY → PARTNER → CUSTOMER 순으로 우선순위가 높아집니다.
UI가 변경되어도 기본 View를 수정하지 않으므로 Transport 리스크가 감소합니다. 여러 프로젝션이 동일 기본 View를 공유하면서 각기 다른 Extension을 붙일 수 있어 재사용성도 향상됩니다.
자주 겪는 실수와 트러블슈팅
Q1. Metadata Extension을 만들었는데 어노테이션이 반영되지 않습니다.
기본 View에 @Metadata.allowExtensions: true가 선언되어 있는지 확인하세요. 이 플래그가 없으면 Extension은 활성화되어도 무시됩니다. @Metadata.layer가 낮은 레이어라면 상위 레이어에서 덮어썼을 수 있습니다.
Q2. @Semantics.amount.currencyCode를 붙였는데 통화 심볼이 안 나옵니다.
참조되는 통화 필드에도 @Semantics.currencyCode: true를 반드시 붙여야 합니다. 수량 필드도 마찬가지로 @Semantics.quantity.unitOfMeasure와 @Semantics.unitOfMeasure: true가 짝으로 존재해야 합니다.
Q3. Fiori Elements Preview에 컬럼이 뒤죽박죽입니다.@UI.lineItem의 position을 10, 20, 30처럼 간격을 두어 지정하세요. 중복되거나 누락되면 렌더링 순서가 예측 불가능해집니다. List Report는 @UI.lineItem을, Object Page는 @UI.identification과 @UI.facet을 사용합니다.
Q4. 어노테이션 변경이 프로덕션에 반영되지 않습니다.
OData 메타데이터 캐시를 /IWFND/CACHE_CLEANUP과 /IWBEP/CACHE_CLEANUP으로 삭제하세요. RAP 기반이라면 Service Binding 재활성화가 필요할 수 있습니다.
더 깊이 들어갈 주제
이 글에서 다룬 5가지 어노테이션 카테고리는 시작점입니다. 실무에서는 @UI.selectionField와 Value Help 어노테이션으로 필터 UX를 개선하고, @Consumption.valueHelpDefinition으로 CDS 기반 검색 도움말을 구현합니다. RAP Behavior와의 결합(@ObjectModel.readOnly, @ObjectModel.mandatory), Draft 시나리오 어노테이션, Analytical CDS의 KPI 어노테이션으로 범위를 넓혀가면 Fiori Elements 화면 제어를 완전히 자동화할 수 있습니다.
댓글 0
아직 댓글이 없습니다.