이 글에서 얻어갈 것 — 30초 요약의 전체 지도
Fiori Elements는 어노테이션 기반으로 화면을 자동 생성하는 프레임워크지만, 실무에서는 반드시 "표준에 없는 버튼 하나, 컬럼 하나"를 추가해야 하는 순간이 옵니다. 이 글은 그때 필요한 확장 포인트를 종류별로 빠르게 정리한 실전 예제 모음입니다. 다 읽고 나면 아래 항목을 스스로 판단할 수 있게 됩니다.
- 어떤 요구사항에 어떤 확장 포인트(manifest 설정, Controller Extension, Fragment)를 쓸지 고르기
- extensionAPI로 표준 화면의 데이터·컨트롤에 안전하게 접근하기
- Custom Action / Custom Section을 manifest.json으로 선언하기
- 업그레이드에 강한(upgrade-safe) 커스터마이징 원칙 이해하기
시작 전에 알고 있으면 좋은 배경
JavaScript 기초 문법과 JSON 구조를 읽을 수 있으면 충분합니다. SAPUI5의 MVC 구조(View/Controller)와 OData가 무엇인지 대략적인 감만 있으면 되고, CDS 어노테이션을 깊이 몰라도 이 글의 예제는 따라올 수 있습니다. Freestyle UI5 앱을 만들어 본 경험이 있다면 "왜 Fiori Elements는 코드를 직접 안 쓰는지"가 더 빨리 이해됩니다.
실습 환경과 버전 기준
이 글의 예제는 Fiori Elements for OData V4(라이브러리 sap.fe) 기준이며, SAPUI5 1.120 LTS 이상에서 동작을 확인하는 것을 권장합니다. 준비물은 다음과 같습니다.
- SAP Business Application Studio 또는 VS Code + SAP Fiori tools 확장
- OData V4 서비스(SAP BTP의 CAP 서비스 또는 S/4HANA의 RAP 서비스 모두 가능)
- Node.js LTS 버전(로컬 미리보기
npm start실행용)
참고로 OData V2 기반(sap.suite.ui.generic.template)은 확장 문법이 다릅니다. 신규 개발은 일반적으로 V4 스택이 권장되므로 이 글도 V4를 기준으로 합니다.
핵심 개념 — 확장 포인트는 "정해진 콘센트"다
Fiori Elements를 조립식 주택에 비유하면 이해가 빠릅니다. 벽과 배선(화면 레이아웃, 데이터 바인딩)은 공장에서 완성되어 나오고, 개발자는 미리 뚫어 둔 콘센트 위치(확장 포인트)에만 가전을 꽂을 수 있습니다. 벽을 임의로 뜯으면(내부 컨트롤 직접 조작) 당장은 되지만, 다음 리모델링(SAPUI5 업그레이드) 때 깨집니다. 주요 콘센트는 4종류입니다.
- manifest.json 확장 설정 — 코드 없이 선언만으로 Custom Action(버튼), Custom Column, Custom Section을 추가하는 진입점입니다. 모든 커스터마이징은 여기서 시작됩니다.
- extensionAPI — 표준 페이지가 개발자에게 공개한 유일한 창구입니다. 선택된 행 컨텍스트 조회, 데이터 리프레시, 다이얼로그 표시, 사이드 이펙트 실행 등을 이 API로 처리합니다. 내부 컨트롤을
byId로 직접 뒤지는 대신 이 API를 쓰는 것이 upgrade-safe의 핵심입니다. - Controller Extension — List Report / Object Page의 표준 컨트롤러 라이프사이클(
onPageReady등)에 내 로직을 끼워 넣는 방식입니다.this.base.getExtensionAPI()로 extensionAPI에 접근합니다. - XML Fragment + Building Blocks — 화면 조각(섹션, 필드 그룹)을 직접 그려 넣을 때 사용합니다.
sap.fe.macros의 Table, Field 같은 빌딩 블록을 쓰면 커스텀 영역 안에서도 표준과 같은 룩앤필과 바인딩을 얻습니다.
정리하면 "선언은 manifest, 로직은 extensionAPI, 화면은 Fragment" — 이 한 줄이 30초 요약의 뼈대입니다.
실전 코드 3단계 — 버튼 하나에서 프로덕션까지
1단계: manifest.json으로 Custom Action 추가 (기본 예제)
List Report 테이블 툴바에 "긴급 승인" 버튼을 추가합니다. 코드보다 선언이 먼저입니다.
{
"sap.ui5": {
"routing": {
"targets": {
"SalesOrderList": {
"options": {
"settings": {
"controlConfiguration": {
"@com.sap.vocabularies.UI.v1.LineItem": {
"actions": {
"UrgentApprove": {
"press": "com.acme.orders.ext.OrderActions.onUrgentApprove",
"text": "긴급 승인",
"requiresSelection": true
}
}
}
}
}
}
}
}
}
}
}
핸들러 파일에서는 this가 extensionAPI로 바인딩됩니다. 선택된 컨텍스트가 파라미터로 넘어오는 점이 핵심입니다.
sap.ui.define(["sap/m/MessageToast"], function (MessageToast) {
"use strict";
return {
onUrgentApprove: function (oContext, aSelectedContexts) {
MessageToast.show(aSelectedContexts.length + "건의 판매 오더를 승인 요청했습니다.");
}
};
});
2단계: Controller Extension으로 실무 로직 넣기 (에러 처리·로깅)
Object Page가 로드될 때 오더 금액이 한도를 넘으면 경고를 띄우는 시나리오입니다. manifest에 확장 선언 후 ControllerExtension을 구현합니다.
{
"sap.ui5": {
"extends": {
"extensions": {
"sap.ui.controllerExtensions": {
"sap.fe.templates.ObjectPage.ObjectPageController": {
"controllerName": "com.acme.orders.ext.controller.OrderObjectPageExt"
}
}
}
}
}
}
sap.ui.define([
"sap/ui/core/mvc/ControllerExtension",
"sap/base/Log"
], function (ControllerExtension, Log) {
"use strict";
return ControllerExtension.extend("com.acme.orders.ext.controller.OrderObjectPageExt", {
override: {
onPageReady: function () {
try {
const oApi = this.base.getExtensionAPI();
const oCtx = this.base.getView().getBindingContext();
if (!oCtx) { return; }
const fAmount = oCtx.getProperty("TotalNetAmount");
if (fAmount > 100000) {
oApi.showMessages([{
type: "Warning",
message: "한도 초과 오더입니다. 승인 라인을 확인하세요."
}]);
}
} catch (oError) {
Log.error("OrderObjectPageExt 초기화 실패", oError.message, "com.acme.orders");
}
}
}
});
});
onInit이 아니라 onPageReady를 쓰는 이유가 중요합니다. 바인딩 컨텍스트는 페이지 데이터가 준비된 뒤에야 존재하므로, 라이프사이클 훅 선택이 곧 에러 예방입니다.
3단계: Custom Section + Building Block (프로덕션 관점)
Object Page에 배송 이력 섹션을 Fragment로 추가합니다. sap.fe.macros 빌딩 블록을 쓰면 바인딩·성능 최적화(지연 로딩)를 프레임워크가 처리합니다.
<core:FragmentDefinition
xmlns:core="sap.ui.core"
xmlns:macros="sap.fe.macros">
<macros:Table id="deliveryHistoryTable"
metaPath="_DeliveryHistory/@com.sap.vocabularies.UI.v1.LineItem" />
</core:FragmentDefinition>
manifest의 해당 Object Page 설정에 섹션을 등록합니다.
{
"content": {
"body": {
"sections": {
"DeliveryHistorySection": {
"template": "com.acme.orders.ext.fragment.DeliveryHistory",
"title": "배송 이력",
"position": { "placement": "After", "anchor": "GeneralInfoFacet" }
}
}
}
}
}
프로덕션 체크포인트 세 가지만 기억하세요. 첫째 성능: 커스텀 영역에서도 수동 read 호출 대신 빌딩 블록/바인딩을 사용해 배치 요청과 지연 로딩을 유지합니다. 둘째 테스트: 확장 로직은 OPA5/wdi5 저니에 포함시켜 업그레이드 회귀를 잡습니다. 셋째 보안: 버튼 표시 여부(enabled/visible)는 UX일 뿐이므로, 승인 같은 권한 검증은 반드시 백엔드(RAP/CAP 서비스)에서 다시 수행합니다.
자주 막히는 지점과 FAQ
Q1. extensionAPI의 byId로 컨트롤이 null만 나옵니다. 대부분 타이밍 문제입니다. 표준 컨트롤은 onPageReady 이후에 접근해야 하며, ID는 화면에 보이는 값이 아니라 안정 ID(stable ID) 규칙을 따릅니다. 애초에 컨트롤 직접 접근을 줄이고 컨텍스트/바인딩 기반으로 로직을 짜는 편이 안전합니다.
Q2. 검색해서 나온 확장 코드가 제 앱에서 안 돌아갑니다. OData V2 스택(sap.suite.ui.generic.template)과 V4 스택(sap.fe)은 manifest 구조와 extensionAPI 시그니처가 서로 다릅니다. 자료를 볼 때 어느 스택 기준인지 먼저 확인하세요. 이 글은 V4 기준입니다.
Q3. Custom Section이 화면에 안 나타납니다. position.anchor에 지정한 Facet ID가 실제 어노테이션의 ID와 일치하는지, Fragment 경로가 네임스페이스 포함 전체 경로인지 확인하세요. 로컬 미리보기 캐시 때문일 수도 있어 하드 리로드도 시도해 볼 만합니다.
Q4. 업그레이드 후 커스터마이징이 깨졌습니다. 내부(private) 컨트롤 구조에 의존한 코드가 원인인 경우가 많습니다. 공개된 확장 포인트와 extensionAPI만 사용했는지 점검하는 것이 일반적으로 가장 확실한 예방책입니다.
여기서 한 걸음 더
이 글의 확장 포인트를 익혔다면, 다음 주제로 넓혀 보세요. Flexible Programming Model(FPM)의 Custom Page로 완전 자유 레이아웃 페이지 만들기, Guided Development(Fiori tools)로 확장 코드 자동 생성하기, CDS UI 어노테이션으로 코드 없이 화면 제어하기, 그리고 키유저 관점의 UI Adaptation(RTA)과 개발자 확장의 역할 구분이 자연스러운 다음 코스입니다. RAP/CAP 백엔드의 액션 구현과 연결하면 프론트-백엔드 전체 흐름이 완성됩니다.
더 깊이 보기 좋은 문서 모음
- SAP Fiori tools — help.sap.com 문서: 확장 코드 생성(Guided Development)과 Page Map 사용법
- SAP BTP — help.sap.com 문서: Business Application Studio 환경 구성
- SAP ABAP Platform(RAP) — help.sap.com 문서: Fiori Elements와 연결되는 백엔드 액션/검증 구현
- Flexible Programming Model Explorer: Custom Action/Section/Building Block 라이브 샘플
- sap.fe.core.ExtensionAPI 레퍼런스: 사용 가능한 메서드 전체 목록
- Developing Apps with SAP Fiori Elements (SAPUI5 문서): 개념부터 확장까지 전체 가이드
댓글 0
아직 댓글이 없습니다.