왜 Fiori Elements인가 — 프리스타일 개발의 함정
SAPUI5로 List Report 화면을 처음부터 직접 만들어 본 적이 있다면 공감할 것이다. 필터바를 붙이고, 테이블을 바인딩하고, 변형 관리(Variant Management)를 연결하고, 내보내기 버튼을 달고, 접근성과 다크 테마까지 챙기다 보면 "목록 하나 보여주는 화면"에 수천 줄의 코드가 쌓인다. 문제는 그다음이다. UI5 버전이 올라갈 때마다 직접 만든 컨트롤 조합이 깨지지 않는지 회귀 테스트를 해야 하고, Fiori 디자인 가이드라인이 바뀌면 화면을 다시 손봐야 한다.
Fiori Elements는 이 반복 노동을 프레임워크에 위임하는 접근이다. 개발자는 OData 서비스와 어노테이션(메타데이터)만 제공하고, 화면 자체는 SAPUI5가 제공하는 표준 템플릿 엔진이 런타임에 생성한다. 코드가 줄어드는 것 이상으로 중요한 효과는 일관성과 유지보수 비용이다. 일반적으로 S/4HANA 표준 앱의 상당수가 Fiori Elements 기반으로 제공되며, 커스텀 앱도 같은 방식으로 만들면 사용자는 학습 없이 화면을 이해한다. 이 글에서는 Fiori Elements의 4가지 대표 플로어플랜을 실무 시나리오(SalesOrder, PurchaseRequisition, Supplier) 기준으로 비교하고, 각각 언제 써야 하는지 정리한다.
4가지 플로어플랜 한눈에 보기
플로어플랜(Floorplan)은 "화면의 설계 도면"이다. Fiori Elements(OData V4 기준, SAPUI5 1.120+ 권장)는 다음 네 가지를 핵심 플로어플랜으로 제공한다.
| 플로어플랜 | 목적 | 대표 시나리오 |
|---|---|---|
| List Report | 대량 데이터 검색·필터·탐색 | 판매오더 전체 조회 |
| Object Page | 단일 객체 상세 조회·편집 | 판매오더 헤더+아이템 편집 |
| Worklist | 처리해야 할 작업 목록 | 승인 대기 구매요청 처리 |
| Analytical List Page | 차트+KPI+테이블 통합 분석 | 공급업체별 지출 분석 |
비유하자면 List Report는 "도서관 검색대", Object Page는 "책 한 권의 상세 페이지", Worklist는 "오늘 반납 처리할 책 더미", Analytical List Page(ALP)는 "대출 통계 대시보드"다. 네 가지 모두 동일한 어노테이션 문법을 공유하므로, 하나를 익히면 나머지는 manifest.json 설정과 어노테이션 몇 개의 차이로 전환할 수 있다.
List Report — 검색·필터·목록의 표준
List Report는 필터바 + 테이블 + 변형 관리로 구성되며, 사용자가 조건을 좁혀 원하는 레코드를 찾아 Object Page로 이동하는 흐름을 담당한다. 핵심은 CDS(또는 CAP) 어노테이션이다. 판매오더 예제를 보자.
annotate SalesService.SalesOrders with @(
UI.SelectionFields : [ orderDate, customer_ID, overallStatus ],
UI.LineItem : [
{ $Type : 'UI.DataField', Value : orderNumber, Label : '오더번호' },
{ $Type : 'UI.DataField', Value : customer.name, Label : '고객' },
{ $Type : 'UI.DataField', Value : netAmount, Label : '순액' },
{
$Type : 'UI.DataField',
Value : overallStatus,
Criticality : statusCriticality // 1=빨강, 2=노랑, 3=초록
}
]
);
UI.SelectionFields가 필터바를, UI.LineItem이 테이블 컬럼을 만든다. Criticality를 연결하면 상태 컬럼이 자동으로 색상 표시된다. 앱 쪽 manifest.json에는 라우팅과 테이블 동작만 선언한다.
"routing": {
"targets": {
"SalesOrdersList": {
"type": "Component",
"name": "sap.fe.templates.ListReport",
"options": {
"settings": {
"contextPath": "/SalesOrders",
"initialLoad": "Enabled",
"controlConfiguration": {
"@com.sap.vocabularies.UI.v1.LineItem": {
"tableSettings": { "type": "ResponsiveTable", "selectionMode": "Multi" }
}
}
}
}
}
}
}
표준 컬럼으로 부족하면 XML Fragment로 커스텀 컬럼을 끼워 넣을 수 있다. 예를 들어 납기 임박 오더에 아이콘을 붙이는 경우다.
<core:FragmentDefinition xmlns="sap.m" xmlns:core="sap.ui.core">
<ObjectStatus
text="{= ${deliveryDate} < ${/today} ? '지연' : '정상' }"
state="{= ${deliveryDate} < ${/today} ? 'Error' : 'Success' }"
icon="sap-icon://shipping-status" />
</core:FragmentDefinition>
이 Fragment를 manifest의 columns 확장 포인트에 등록하면 표준 기능(정렬, 개인화, 엑셀 내보내기)을 유지한 채 커스텀 셀만 추가된다. 프리스타일이라면 직접 구현했어야 할 기능들이다.
Object Page — 상세 화면과 섹션 설계
List Report에서 행을 클릭하면 도착하는 곳이 Object Page다. 헤더(제목·KPI·상태) + 섹션(폼, 하위 테이블)으로 구성되며, Draft 기반 편집까지 어노테이션으로 처리된다. 설계의 중심은 UI.Facets다.
annotate SalesService.SalesOrders with @(
UI.HeaderInfo : {
TypeName : '판매오더',
TypeNamePlural : '판매오더 목록',
Title : { Value : orderNumber },
Description : { Value : customer.name }
},
UI.Facets : [
{
$Type : 'UI.ReferenceFacet',
Label : '기본 정보',
Target : '@UI.FieldGroup#General'
},
{
$Type : 'UI.ReferenceFacet',
Label : '오더 아이템',
Target : 'items/@UI.LineItem' // 하위 엔티티 테이블 섹션
}
],
UI.FieldGroup #General : {
Data : [
{ Value : orderDate },
{ Value : paymentTerms },
{ Value : netAmount }
]
}
);
주목할 부분은 items/@UI.LineItem이다. 헤더-아이템 관계(association)만 모델에 정의돼 있으면, 아이템 테이블 섹션이 자동 생성되고 편집 모드에서 행 추가·삭제까지 동작한다. manifest에는 sap.fe.templates.ObjectPage 타깃을 추가하고 editableHeaderContent, sectionLayout(Page/Tabs) 정도만 조정하면 된다. 섹션이 5개를 넘어가면 일반적으로 sectionLayout: "Tabs"가 스크롤 피로를 줄여준다.
Worklist — 필터 없는 작업 목록
Worklist는 별도 템플릿이 아니라 List Report의 변형이다. 차이는 사용 맥락에 있다. List Report가 "찾는 화면"이라면 Worklist는 "처리하는 화면"이다. 승인 대기 구매요청처럼 사용자가 조건을 고를 필요 없이, 들어오자마자 처리 대상이 보여야 하는 경우다. manifest 설정 세 가지로 전환한다.
"settings": {
"contextPath": "/PurchaseRequisitions",
"hideFilterBar": true,
"initialLoad": "Enabled",
"variantManagement": "None"
}
필터바를 숨기는 대신, 서비스 레벨에서 처리 대상만 내려주는 것이 핵심이다. CAP이라면 뷰 또는 where 조건으로 "승인 대기" 상태만 노출하는 전용 엔티티를 만드는 방식이 권장된다.
entity OpenPurchaseRequisitions as projection on PurchaseRequisitions
where approvalStatus = 'PENDING';
여기에 UI.LineItem 안에 UI.DataFieldForAction으로 "승인/반려" 액션 버튼을 선언하면, 목록에서 바로 일괄 처리가 가능한 전형적인 Worklist가 완성된다. 필터바가 필요해지는 순간이 오면 설정 한 줄만 되돌리면 List Report로 돌아간다 — 이것이 같은 템플릿을 공유하는 장점이다.
Analytical List Page — 차트와 KPI로 보는 데이터
ALP는 "분석에서 트랜잭션으로(insight to action)" 흐름을 위한 플로어플랜이다. 상단 KPI/필터, 중단 차트, 하단 테이블이 하나의 필터 컨텍스트를 공유한다. 차트 막대를 클릭하면 아래 테이블이 즉시 그 조각으로 필터링된다. 공급업체별 지출 분석 예제다.
annotate AnalyticsService.SupplierSpend with @(
Aggregation.ApplySupported : {
GroupableProperties : [ supplier_ID, category ],
AggregatableProperties : [ { Property : spendAmount } ]
},
UI.Chart : {
ChartType : #Bar,
Dimensions : [ supplier_ID ],
Measures : [ spendAmount ],
MeasureAttributes : [{
Measure : spendAmount,
Role : #Axis1
}]
},
UI.PresentationVariant : {
SortOrder : [{ Property : spendAmount, Descending : true }],
Visualizations : [ '@UI.Chart', '@UI.LineItem' ]
}
);
OData V4에서는 집계 엔진(Aggregation.ApplySupported)이 전제 조건이다. CAP은 @Aggregation.ApplySupported 어노테이션으로, ABAP RAP은 분석용 CDS 뷰(@Analytics.query 계열)로 이를 충족한다. manifest에서는 List Report 타깃에 뷰 모드를 추가하는 방식이다.
"settings": {
"contextPath": "/SupplierSpend",
"views": {
"paths": [
{ "primary": [{ "annotationPath": "com.sap.vocabularies.UI.v1.Chart" }],
"secondary": [{ "annotationPath": "com.sap.vocabularies.UI.v1.LineItem" }],
"defaultPath": "both" }
]
}
}
주의할 점은 데이터 특성이다. 집계가 의미 없는 데이터(예: 문서 번호 나열)에 ALP를 쓰면 차트가 장식에 그친다. 측정값(금액, 수량)과 차원(공급업체, 카테고리, 기간)이 명확할 때만 선택해야 한다.
어떤 플로어플랜을 골라야 하나 — 의사결정 흐름
실무에서 쓰기 좋은 판단 순서를 질문 형태로 정리하면 다음과 같다.
- 사용자가 숫자를 집계·비교해야 하는가? → 그렇다면 ALP. 측정값/차원이 없으면 다음으로.
- 사용자가 검색 조건을 스스로 조합해야 하는가? → 그렇다면 List Report + Object Page 조합. 데이터가 수천 건 이상이고 탐색 패턴이 다양할 때 기본값이다.
- 들어오자마자 처리할 항목이 정해져 있는가? → 그렇다면 Worklist. 인박스성 업무, 예외 처리 목록에 적합하다.
- 하나의 객체를 깊게 보고 편집하는가? → Object Page. 단독으로 쓰기보다 앞의 목록형 플로어플랜과 짝으로 쓰는 것이 일반적이다.
자주 나오는 질문 세 가지도 짚어두자. "List Report와 Worklist 중 애매하면?" — 사용자가 필터를 하루 한 번이라도 바꾼다면 List Report가 안전하다. Worklist에서 필터 요구가 나중에 생기면 설정 변경으로 복구 가능하니 부담은 작다. "ALP인데 편집도 필요하면?" — ALP 테이블에서 Object Page로 내비게이션을 연결하면 된다. 분석 화면 자체에서 인라인 편집을 넣는 설계는 일반적으로 권장되지 않는다. "Overview Page(OVP)는?" — 카드 기반 대시보드로 별도 플로어플랜이지만, OData V4 신규 개발에서는 ALP 또는 SAP Build Work Zone 카드로 대체되는 흐름이라 이 글에서는 제외했다.
실무 도입 시 주의점과 더 볼 자료
도입 전 확인할 것들이다. 첫째, 버전과 스택. 신규 프로젝트라면 Fiori elements for OData V4(SAPUI5 1.108 이상, 실무에서는 장기 유지보수 버전인 1.120대 이상 권장)를 선택하는 것이 일반적이다. V2 템플릿은 유지되지만 신규 기능은 V4 중심으로 추가된다. 둘째, 확장 한계 인지. 표준 확장 포인트(커스텀 컬럼·섹션·액션, controller extension)로 해결되지 않는 요구가 화면의 절반을 넘으면 프리스타일 또는 flexible programming model 기반 커스텀 페이지를 검토해야 한다. 셋째, 어노테이션 관리 위치. 백엔드(CDS)와 프런트엔드(annotation.xml)에 같은 어노테이션이 있으면 프런트가 우선 적용되므로, 팀 차원에서 "UI 어노테이션은 백엔드에 둔다" 같은 규칙을 정해두지 않으면 디버깅 지옥이 된다. 넷째, Draft와 잠금. Object Page 편집은 Draft 활성화가 전제인 경우가 많으므로 RAP/CAP 모델링 단계에서 미리 결정해야 한다.
더 깊이 파고들 때 유용한 자료들이다.
- help.sap.com — SAP Fiori Elements 제품 문서
- help.sap.com — SAPUI5 개발 가이드 (Developing Apps with Fiori Elements)
- help.sap.com — ABAP RAP과 Fiori Elements 연계
- ui5.sap.com — Fiori Elements 토픽 문서
- Flexible Programming Model Explorer — 어노테이션별 라이브 예제
- CAP 문서 — Serving Fiori UIs
- SAP Fiori Design Guidelines — 플로어플랜 UX 기준
정리하면, 목록 화면을 직접 만드는 시대는 지났다. OData 서비스와 어노테이션을 잘 설계하면 네 가지 플로어플랜이 화면을 대신 만들어 주고, 여러분의 시간은 도메인 로직에 쓰인다. 다음 주제로는 Flexible Programming Model을 이용한 커스텀 페이지 확장, 그리고 RAP Draft 처리와 Object Page 편집 흐름을 이어서 다루면 자연스럽게 연결된다.
댓글 0
아직 댓글이 없습니다.