📖 개요: 확장 코드 없이 컬럼을 추가한다는 것
SAP Fiori Elements List Report에서 컬럼 하나를 추가해야 할 때, 많은 개발자가 습관적으로 Adaptation Project나 컨트롤러 확장부터 떠올립니다. 하지만 RAP(ABAP RESTful Application Programming Model) 기반 앱이라면 대부분의 경우 CDS 어노테이션만으로 컬럼 추가가 끝납니다. 프런트엔드 확장 프로젝트를 만들지 않으므로 업그레이드 리스크가 줄고, 유지보수 지점이 백엔드 한 곳으로 모입니다. 이 글에서 확인할 수 있는 내용은 다음과 같습니다.
- Fiori Elements가 어노테이션을 읽어 테이블 컬럼을 그리는 동작 원리 이해
@UI.lineItem으로 기본 컬럼 추가 및 위치·중요도 제어- CDS case 식 + criticality로 색상 있는 상태 컬럼 구현
@UI.dataPoint를 활용한 진행률(Progress) 컬럼 구성- 메타데이터 확장(Metadata Extension) 레이어링과 성능 관점의 점검 포인트
📚 미리 알아두면 좋은 배경
이 글은 중급 난이도의 실전 예제입니다. CDS View Entity의 기본 문법(select, association, case 식), RAP의 Consumption View와 Metadata Extension의 관계, 그리고 Fiori Elements List Report 템플릿의 기본 구조(FilterBar + Table)를 알고 있다면 수월하게 따라올 수 있습니다. OData 어노테이션 vocabulary(UI, Common)에 대한 사전 지식은 없어도 됩니다. CDS 어노테이션이 OData 어노테이션으로 어떻게 변환되는지는 본문에서 함께 설명합니다.
🔧 환경 및 준비물
예제는 다음 환경을 기준으로 작성했습니다.
- 백엔드: SAP S/4HANA 2022 이상(온프레미스) 또는 SAP BTP ABAP Environment. CDS View Entity와 Metadata Extension 문법을 사용하므로 구형
define view(DDIC 기반)만 지원하는 NetWeaver 7.5x에서는 일부 문법 조정이 필요합니다. - 프런트엔드: Fiori elements for OData V4 (SAPUI5 1.108 이상 권장). OData V2 템플릿에서도
@UI.lineItem계열은 동일하게 동작합니다. - 개발 도구: ABAP Development Tools(ADT) in Eclipse. 어노테이션 자동완성(Ctrl+Space)과 Annotation Propagation 뷰를 적극 활용합니다.
- 시나리오 데이터: 구매오더 모니터링 앱을 가정합니다. Consumption View
ZC_PurchOrdMonitor에 구매오더 번호, 공급업체, 금액, 납기일 필드가 있다고 전제합니다.
💡 핵심 개념: 화면을 그리는 것은 코드가 아니라 설계도
Fiori Elements를 이해하는 가장 쉬운 비유는 조립식 가구입니다. 프리스타일 UI5 앱이 목재를 직접 자르고 못을 박는 방식이라면, Fiori Elements는 완성된 프레임(템플릿)에 조립 설명서(어노테이션)만 끼워 넣는 방식입니다. 컬럼을 추가하고 싶다면 프레임을 뜯어고치는 게 아니라 설명서에 한 줄을 추가하면 됩니다.
동작 흐름은 다음과 같습니다.
- CDS에
@UI.lineItem같은 어노테이션을 작성합니다. - ABAP 런타임이 이를 OData 메타데이터의
UI.LineItem어노테이션 타깃으로 변환해$metadata(V4) 또는 annotation 문서(V2)로 노출합니다. - 브라우저에서 앱이 기동될 때 Fiori Elements 템플릿 엔진이 이 메타데이터를 읽고,
UI.LineItem컬렉션의 각DataField를 테이블 컬럼으로 렌더링합니다.
즉, 테이블 컬럼의 실체는 UI.LineItem이라는 정렬된 레코드 목록이며, CDS의 position 값이 그 정렬 순서를 결정합니다. 여기서 중요한 설계 원칙이 하나 있습니다. UI 어노테이션은 데이터 모델 자체(Interface View)가 아니라 Metadata Extension에 두는 것이 일반적으로 권장됩니다. 데이터 정의와 화면 표현을 분리하면 하나의 뷰를 여러 앱에서 다른 레이아웃으로 재사용할 수 있고, @Metadata.layer(#CORE → #CUSTOMER 순으로 우선순위 상승)를 통해 표준 레이아웃을 건드리지 않고 고객 레이어에서 덮어쓸 수 있기 때문입니다. "어노테이션만으로 컬럼 추가"가 가능한 이유가 바로 이 레이어링 구조에 있습니다.
💻 실전 예제 3단계
1단계 — 기본: 기존 필드를 컬럼으로 노출하기
Consumption View에 이미 존재하지만 화면에는 보이지 않는 SupplierName과 DeliveryDate를 컬럼으로 추가합니다. Metadata Extension만 수정하며, 뷰 본체와 프런트엔드는 전혀 건드리지 않습니다.
@Metadata.layer: #CUSTOMER
annotate view ZC_PurchOrdMonitor with
{
@UI.lineItem: [{ position: 10, importance: #HIGH, label: '구매오더' }]
PurchaseOrderId;
@UI.lineItem: [{ position: 20, importance: #HIGH }]
@UI.selectionField: [{ position: 10 }]
SupplierName;
@UI.lineItem: [{ position: 30, importance: #MEDIUM, label: '납기일' }]
DeliveryDate;
}
저장·활성화 후 앱을 새로고침하면 컬럼이 나타납니다. 포인트 세 가지입니다. 첫째, position은 10 단위로 띄워 두면 나중에 중간 삽입이 쉽습니다. 둘째, importance: #HIGH는 좁은 화면에서도 컬럼이 숨지 않게 합니다(#MEDIUM/#LOW는 반응형으로 팝인 영역으로 이동). 셋째, label을 생략하면 데이터 요소의 필드 레이블이 사용되므로, 번역이 필요한 텍스트라면 어노테이션보다 데이터 요소 레이블을 관리하는 편이 일반적으로 유리합니다.
2단계 — 실무: 색상 있는 상태 컬럼 (계산 필드 + Criticality)
실무에서 가장 많이 요청받는 것은 "납기 임박 여부를 색으로 보여 달라"는 유형입니다. ABAP 클래스나 확장 코드 없이, CDS case 식으로 상태 코드를 만들고 criticality로 연결하면 됩니다. 먼저 뷰 본체에 계산 필드를 추가합니다.
define view entity ZC_PurchOrdMonitor
as select from ZI_PurchOrdMonitor
{
key PurchaseOrderId,
SupplierName,
DeliveryDate,
@Semantics.amount.currencyCode: 'CurrencyCode'
NetAmount,
CurrencyCode,
case
when DeliveryDate < $session.system_date then 'OVERDUE'
when dats_add_days( $session.system_date, 7, 'NULL' )
>= DeliveryDate then 'DUE_SOON'
else 'ON_TRACK'
end as DeliveryStatus,
case
when DeliveryDate < $session.system_date then 1 // 빨강(Negative)
when dats_add_days( $session.system_date, 7, 'NULL' )
>= DeliveryDate then 2 // 주황(Critical)
else 3 // 초록(Positive)
end as DeliveryCriticality
}
이어서 Metadata Extension에서 두 필드를 연결합니다.
@UI.lineItem: [{ position: 40, importance: #HIGH,
label: '납기 상태',
criticality: 'DeliveryCriticality' }]
DeliveryStatus;
@UI.hidden: true
DeliveryCriticality;
criticality 값 규칙은 0=중립, 1=빨강, 2=주황, 3=초록입니다. DeliveryCriticality는 색상 계산용 보조 필드이므로 @UI.hidden: true로 숨기되, 뷰의 select 목록에서 제거하면 안 됩니다. OData 응답에 값이 포함되어야 프런트엔드가 색을 칠할 수 있기 때문입니다. 검증 시에는 브라우저에서 ...$metadata를 열어 UI.LineItem에 Criticality 경로가 붙었는지 확인하고, 온프레미스라면 게이트웨이 오류는 /IWFND/ERROR_LOG에서 추적하는 것이 일반적인 점검 순서입니다.
3단계 — 프로덕션: DataPoint 진행률 컬럼과 성능·품질 점검
단순 텍스트를 넘어 시각화 컬럼도 어노테이션만으로 가능합니다. 입고 진행률을 프로그레스 바로 보여주는 예입니다.
@UI.dataPoint: { qualifier: 'ReceiptRate',
title: '입고 진행률',
targetValue: 100,
visualization: #PROGRESS }
@UI.lineItem: [{ position: 50, importance: #MEDIUM,
type: #AS_DATAPOINT,
valueQualifier: 'ReceiptRate',
label: '입고 진행률' }]
GoodsReceiptRate;
type: #AS_DATAPOINT는 일반 DataField 대신 DataFieldForAnnotation으로 변환되어 DataPoint 정의를 참조합니다. 같은 패턴으로 visualization: #RATING(별점)도 구성할 수 있습니다.
프로덕션 반영 전 체크리스트입니다.
- 성능: 2단계의 case 식은 DB에서 계산되므로 대체로 저렴하지만, 계산 필드에 필터/정렬을 걸면 인덱스를 타지 못할 수 있습니다. 대량 데이터라면 상태 코드를 저장 필드로 물리화하는 것을 검토하세요. ABAP 클래스가 필요한 virtual element는 페이지 단위 후처리라 필터가 제한되는 점도 함께 고려합니다.
- 보안: 컬럼으로 노출하는 필드가 DCL(Access Control) 범위 안에 있는지 확인합니다. 어노테이션은 표시만 제어할 뿐 권한을 대신하지 않습니다.
- 품질: ADT의 Annotation Propagation 뷰로 레이어 병합 결과를 확인하고, ATC 점검과 EML 기반 단위 테스트로 계산 로직(상태 판정 경계값: 당일, +7일)을 검증하는 것이 권장됩니다.
⚠️ 흔한 실수와 트러블슈팅
Q1. 어노테이션을 추가했는데 컬럼이 안 보입니다.
가장 흔한 원인은 캐시입니다. 브라우저 하드 리로드 후에도 안 보이면 온프레미스는 /IWFND/CACHE_CLEANUP과 /IWBEP/CACHE_CLEANUP으로 게이트웨이/백엔드 메타데이터 캐시를 비웁니다. 그다음 의심할 것은 레이어 충돌입니다. 더 높은 @Metadata.layer의 다른 Metadata Extension이 같은 필드의 @UI.lineItem을 덮어쓰고 있을 수 있으니 Annotation Propagation 뷰로 최종 병합 결과를 확인하세요.
Q2. 사용자가 개인화(P13n)로 컬럼을 조정한 뒤에는 새 컬럼이 반영되지 않습니다.
테이블 개인화 변형(Variant)이 저장된 사용자는 저장 시점의 컬럼 구성을 유지합니다. 새 컬럼은 "설정 → 컬럼" 다이얼로그에는 나타나므로 직접 추가하거나 변형을 초기화해야 합니다. 배포 공지에 이 내용을 포함하는 것이 일반적으로 안전합니다.
Q3. criticality 색이 전혀 적용되지 않습니다.
세 가지를 순서대로 확인하세요. (1) criticality 필드가 select 목록에 실제로 존재하는지, (2) 값이 숫자 0~3 범위인지(문자 'RED' 같은 값은 무시됨), (3) criticality:에 적은 필드명이 alias와 정확히 일치하는지. 특히 뷰에서 alias를 바꾸고 어노테이션은 옛 이름을 참조하는 실수가 잦습니다.
Q4. V2 앱인데 #AS_DATAPOINT 컬럼이 이상하게 나옵니다.
DataPoint 시각화 컬럼은 OData V4 템플릿에서 지원 범위가 넓습니다. V2 List Report라면 UI5 버전에 따라 렌더링이 제한될 수 있으므로, 시각화가 핵심 요건이면 V4 전환을 검토하는 편이 권장됩니다.
🚀 여기서 더 나아가기
어노테이션만으로 컬럼을 다뤘다면 자연스러운 확장 방향은 세 가지입니다. 첫째, @Consumption.semanticObject를 붙여 컬럼을 다른 앱으로 이동하는 내비게이션 링크로 만드는 인텐트 기반 탐색. 둘째, @UI.fieldGroup과 Object Page facet 어노테이션으로 상세 화면까지 코드 없이 구성하기. 셋째, 어노테이션의 한계(예: 버튼이 들어간 완전 커스텀 셀)에 부딪혔을 때 사용하는 Guided Development의 커스텀 컬럼 프래그먼트 — 이때 비로소 확장 코드가 등장하며, 그 전까지는 어노테이션이 항상 첫 번째 선택지입니다.
📚 함께 보면 좋은 자료
- SAP Help Portal — ABAP Cloud / RAP 개발 가이드 (UI 어노테이션 레퍼런스 포함)
- SAP Help Portal — SAP S/4HANA 온프레미스 개발 문서 (CDS Annotation 섹션)
- SAP Help Portal — SAP BTP 문서 (ABAP Environment 및 Fiori 배포)
- SAPUI5 데모 키트 — Fiori elements for OData V4 개발 문서 (LineItem/DataPoint 매핑)
- SAP Community — Fiori Elements 토픽 (실무 사례와 Q&A)
- SAP-samples GitHub — RAP 실습 리포지토리 (Metadata Extension 예시)
댓글 0
아직 댓글이 없습니다.