1. Criticality가 없을 때 Fiori 앱이 왜 밋밋한가
RAP(ABAP RESTful Application Programming Model)으로 List Report 앱을 처음 만들어 보면 데이터는 잘 나오는데 화면이 이상하게 밋밋합니다. 판매 오더가 "지연" 상태든 "완료" 상태든 전부 똑같은 회색 텍스트로 표시되기 때문입니다. 실무 사용자는 수백 건의 목록에서 "지금 문제가 있는 건"을 3초 안에 찾고 싶어 하는데, 색상 구분이 없으면 상태 컬럼을 한 줄씩 읽어야 합니다. 표준 SAP Fiori 앱들이 빨강(오류)·노랑(주의)·초록(정상)으로 상태를 표현하는 이유가 바로 여기에 있습니다.
이 글에서는 RAP CDS View에 @UI.Criticality 계열 어노테이션을 적용해, 코드 한 줄의 UI5 개발 없이 상태 색상을 구현하는 과정을 3단계 실전 예제로 다룹니다. 읽고 나면 다음을 할 수 있게 됩니다.
- Criticality 값 0~5가 어떤 색으로 렌더링되는지 설명할 수 있다
- CDS View에서 CASE 식으로 Criticality 필드를 산출할 수 있다
- List Report와 Object Page에 동시에 색상을 적용할 수 있다
- 색상이 안 나올 때 원인을 단계별로 진단할 수 있다
대상 환경은 SAP BTP ABAP Environment 또는 SAP S/4HANA 2022 이상(온프레미스/클라우드), 개발 도구는 ADT(ABAP Development Tools) 기준입니다. 사전에 CDS View 기본 문법과 RAP Managed 시나리오의 대략적인 구조(BO 뷰 → Projection 뷰 → Service Definition/Binding)를 알고 있으면 충분하며, UI5나 JavaScript 지식은 필요하지 않습니다.
2. @UI.Criticality 어노테이션의 동작 원리 — 값 0~4의 의미
Criticality는 신호등에 비유하면 정확합니다. CDS View가 "이 행의 신호등 값은 1(빨강)이다"라고 숫자만 내려주면, Fiori Elements 프레임워크가 그 숫자를 읽어 색상·아이콘으로 바꿔 그립니다. 즉 백엔드는 판정만, 프론트엔드는 표현만 담당하는 역할 분리 구조입니다.
| 값 | 의미 | 렌더링 색상 | 일반적 용도 |
|---|---|---|---|
| 0 | Neutral | 회색(색 없음) | 판정 불가/무관 상태 |
| 1 | Negative | 빨강 | 오류, 지연, 반려 |
| 2 | Critical | 주황/노랑 | 경고, 진행 중 주의 |
| 3 | Positive | 초록 | 완료, 승인, 정상 |
| 5 | New Item | 파랑 | 신규 항목(최신 UI5 버전에서 지원) |
값 4는 Information(파랑 계열)으로 정의되어 있으나 지원 컨트롤이 제한적이라 실무에서는 0~3만으로 설계하는 것이 일반적으로 권장됩니다. 핵심은 Criticality 값 자체를 담는 별도 숫자 필드를 CDS에 만들고, 상태를 보여주는 필드의 어노테이션에서 그 필드를 참조한다는 점입니다. 상태 필드에 직접 색을 칠하는 게 아니라, "이 필드의 색은 저 필드 값을 보고 결정해"라고 연결하는 방식입니다.
3. CDS View에 Criticality 필드 추가하기 — 1단계 기본 예제
구매 오더(PurchaseOrder) 시나리오로 시작합니다. 상태 코드 overall_status가 A(승인)/W(대기)/R(반려)로 관리된다고 가정합니다. 먼저 BO 인터페이스 뷰(또는 Projection 뷰)에 가상 판정 필드를 추가합니다.
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Purchase Order Projection'
define root view entity ZC_PurchaseOrder
provider contract transactional_query
as projection on ZR_PurchaseOrder
{
key PurchaseOrderId,
SupplierName,
OverallStatus,
// 신호등 값을 담는 판정 필드
case OverallStatus
when 'A' then 3 // 승인 → 초록
when 'W' then 2 // 대기 → 노랑
when 'R' then 1 // 반려 → 빨강
else 0 // 그 외 → 색 없음
end as StatusCriticality,
TotalAmount,
Currency
}
이 필드는 사용자에게 숫자로 보일 필요가 없으므로, 메타데이터 확장(Metadata Extension)에서 숨김 처리하고 상태 필드와 연결합니다.
@Metadata.layer: #CORE
annotate view ZC_PurchaseOrder with
{
@UI.lineItem: [{ position: 30,
criticality: 'StatusCriticality' }]
OverallStatus;
@UI.hidden: true
StatusCriticality;
}
여기까지만 하고 서비스를 활성화한 뒤 Preview를 열면 List Report의 상태 컬럼에 색상과 상태 아이콘이 함께 나타납니다. 어노테이션의 criticality 속성 값은 필드명을 문자열로 지정한다는 점에 주의하세요.
4. 계산 로직 심화 — CASE/WHEN 설계와 실무 시나리오 (2단계)
실무에서는 단일 상태 코드만으로 색을 정하기 어렵습니다. 판매 오더(SalesOrder)에서 "납기일이 지났는데 미완료면 빨강, 납기 3일 전이면 노랑"처럼 복합 조건이 필요합니다. CDS의 CASE 검색식(searched case)과 날짜 함수를 조합합니다.
define view entity ZR_SalesOrderStatus
as select from zso_header
{
key sales_order_id as SalesOrderId,
delivery_date as DeliveryDate,
completion_flag as CompletionFlag,
case
when completion_flag = 'X'
then 3 // 완료 → 초록
when delivery_date < $session.system_date
then 1 // 납기 초과 미완료 → 빨강
when dats_add_days( $session.system_date, 3,
'NULL' ) >= delivery_date
then 2 // 납기 임박 → 노랑
else 0
end as DeliveryCriticality
}
설계 시 주의할 점 두 가지입니다. 첫째, CASE는 위에서부터 첫 매칭 조건으로 확정되므로 "완료" 같은 종결 조건을 반드시 최상단에 배치해야 합니다. 완료된 오더가 납기 초과라는 이유로 빨갛게 표시되는 사고가 이 순서 실수에서 나옵니다. 둘째, else 0을 생략하면 NULL이 반환되어 렌더링이 환경에 따라 달라질 수 있으므로 기본값을 명시하는 편이 안전합니다. 상태 코드가 늘어날 가능성이 있다면 CASE를 뷰마다 복사하지 말고 판정 전용 뷰 하나에 모아 재사용하는 구조가 유지보수에 유리합니다.
5. Fiori Elements에서 색상이 렌더링되는 흐름
동작 순서를 이해하면 디버깅이 쉬워집니다. 전체 파이프라인은 다음과 같습니다.
- CDS 어노테이션이 활성화 시점에 OData 메타데이터(
$metadata)의UI.LineItem/UI.DataPoint어노테이션으로 변환됩니다. - Fiori Elements 템플릿이 앱 구동 시 메타데이터를 읽어, 상태 컬럼을 일반 Text가 아닌
ObjectStatus계열 컨트롤로 생성합니다. - 런타임에 각 행의 Criticality 필드 값(0~3)이 데이터와 함께 내려오고, 컨트롤이 값을 색상 상태(Error/Warning/Success)로 매핑해 그립니다.
즉 색상 결정은 매 행의 데이터 값으로 이뤄지므로, 같은 컬럼이라도 행마다 색이 다를 수 있습니다. 아이콘 없이 색상 텍스트만 원한다면 criticalityRepresentation: #WITHOUT_ICON을 추가합니다.
@UI.lineItem: [{ position: 30,
criticality: 'DeliveryCriticality',
criticalityRepresentation: #WITHOUT_ICON }]
DeliveryDate;
6. 실전 종합 예제 — SalesOrder 3색 구현과 프로덕션 고려사항 (3단계)
프로덕션 관점에서 한 단계 더 다듬습니다. 첫째, 성능: Criticality를 CDS 계산식으로 두면 DB 푸시다운으로 처리되어 일반적으로 부담이 적지만, 복잡한 서브쿼리·조인이 섞이면 목록 조회 전체가 느려집니다. 무거운 판정 로직은 가상 요소(Virtual Element)로 ABAP 계층에서 계산하는 대안도 있으나, 이 경우 해당 필드로 정렬·필터가 불가능해지므로 트레이드오프를 검토해야 합니다. 둘째, 테스트: 판정 로직은 CDS Test Double Framework로 단위 테스트를 작성해 상태 코드별 기대값(1/2/3/0)을 검증해 두면 상태 코드 추가 시 회귀를 막을 수 있습니다.
METHOD test_rejected_is_red.
" Given: 반려 상태의 판매 오더 테스트 데이터
environment->insert_test_data( i_data = VALUE zso_header(
sales_order_id = '9001' overall_status = 'R' ) ).
" When: 뷰 조회
SELECT SINGLE StatusCriticality
FROM zc_salesorder
WHERE SalesOrderId = '9001'
INTO @DATA(lv_crit).
" Then: 빨강(1) 기대
cl_abap_unit_assert=>assert_equals( act = lv_crit exp = 1 ).
ENDMETHOD.
셋째, 보안/권한: Criticality 필드도 결국 조회 데이터의 일부이므로 DCL(Access Control)이 적용된 뷰 위에 얹어야 권한 밖 데이터의 상태가 노출되지 않습니다.
7. List Report와 Object Page 동시 적용
List Report에서 색이 나와도 상세 화면(Object Page)의 헤더나 필드에는 자동 적용되지 않습니다. Object Page에는 @UI.dataPoint를 사용합니다. 하나의 메타데이터 확장에서 두 곳을 함께 정의하는 패턴이 일반적입니다.
@Metadata.layer: #CORE
annotate view ZC_SalesOrder with
{
@UI: { lineItem: [{ position: 40,
criticality: 'StatusCriticality' }],
dataPoint: { title: '주문 상태',
criticality: 'StatusCriticality' },
identification: [{ position: 40 }] }
OverallStatus;
@UI.hidden: true
StatusCriticality;
}
헤더 영역에 상태를 강조하고 싶다면 @UI.headerInfo 또는 Facet 구성에서 위 dataPoint를 #DATAPOINT_REFERENCE 타입 Facet으로 참조하면 됩니다. 같은 판정 필드 하나(StatusCriticality)를 목록·상세·헤더가 모두 재사용하므로, 판정 로직 변경이 한 곳에서 끝난다는 것이 이 구조의 장점입니다.
8. 흔한 실수와 디버깅 팁 — 색상이 안 나올 때
Q1. 어노테이션을 넣었는데 색이 전혀 안 나옵니다.
가장 흔한 원인은criticality: 'StatusCriticality'의 필드명 오타 또는 대소문자 불일치입니다. 어노테이션 문자열은 활성화 시 문법 오류로 잡히지 않는 경우가 있어 조용히 무시됩니다. 브라우저에서/$metadata를 열어UI.LineItem안에CriticalityPath가 실제로 생성됐는지 확인하는 것이 가장 빠른 진단법입니다.
Q2. 메타데이터에는 있는데 화면이 예전 그대로입니다.
메타데이터 캐시 문제일 가능성이 큽니다. Service Binding을 다시 게시(Publish)하고 브라우저 캐시를 지운 뒤(하드 리로드) 확인하세요. 온프레미스라면/IWFND/CACHE_CLEANUP계열 캐시 정리도 점검 대상입니다.
Q3. 색은 나오는데 전부 회색입니다.
Criticality 필드 값이 실제로 0만 내려오는 경우입니다. CASE의 비교 값(예: 'A')과 DB 저장 값(예: 'a', 공백 포함)이 다르면 항상 else로 빠집니다. ADT의 Data Preview로 판정 필드의 실제 값 분포를 먼저 확인하세요.
Q4. 판정 필드가 화면에 숫자로 그대로 노출됩니다.
@UI.hidden: true누락입니다. Criticality 필드는 참조용이므로 숨기는 것이 표준 패턴입니다.
이 글의 패턴을 익혔다면 다음 주제로 확장해 보세요. 마이크로 차트(@UI.chart)와 Criticality 결합, Rating/Progress 인디케이터, 그리고 계산이 무거운 경우의 Virtual Element 구현이 자연스러운 다음 단계입니다. 색상 판정 로직을 BAdI 없이 CDS 한 곳에 모으는 설계 습관은 RAP 전반의 유지보수성을 크게 끌어올립니다.
댓글 0
아직 댓글이 없습니다.