RadioButton의 한계와 SegmentedButton이 등장한 이유
SAPUI5/OpenUI5에서 "여러 선택지 중 하나"를 제시할 때 가장 먼저 떠오르는 컨트롤은 sap.m.RadioButton이지만, 모바일 우선(Mobile First) Fiori 환경에서는 sap.m.SegmentedButton이 훨씬 더 자주 사용됩니다.
RadioButton은 데스크톱 폼 시대의 유산으로 모바일 환경에서 두 가지 약점을 드러냅니다. 첫째, 터치 타깃이 작습니다. 둘째, 공간 효율이 떨어집니다. SegmentedButton은 이러한 한계를 정면 돌파합니다. 비유하자면 RadioButton이 "투표용지의 동그라미 칠하기"라면 SegmentedButton은 "라디오 채널 프리셋 버튼"입니다. 둘 다 단일 선택이라는 시맨틱은 같지만, 후자는 즉각적인 모드 전환이 본질입니다.
SegmentedButton 기본 구조와 XML View 선언
SegmentedButton의 핵심 속성은 selectedKey이고, 자식 aggregation은 items(SegmentedButtonItem 배열)입니다. selectedKey는 모델 경로와 양방향으로 바인딩되어 사용자가 버튼을 누르면 모델 값이 자동으로 갱신됩니다.
RadioButtonGroup의 selectedIndex가 정수 인덱스인 것과 달리, SegmentedButton은 의미 있는 문자열 키를 사용해 유지보수성이 훨씬 좋습니다.
실전 예제 1단계: selectedKey 바인딩과 초기화
sap.ui.define([
"sap/ui/core/mvc/Controller",
"sap/ui/model/json/JSONModel"
], function (Controller, JSONModel) {
"use strict";
return Controller.extend("com.example.order.controller.Filter", {
onInit: function () {
const oViewModel = new JSONModel({
statusFilter: "PENDING" // 초기 선택값
});
this.getView().setModel(oViewModel, "view");
},
onStatusChange: function (oEvent) {
const sKey = oEvent.getParameter("key");
console.log("선택된 상태:", sKey);
}
});
});
onInit에서 초기값을 모델에 셋팅하면 화면 진입 시 첫 번째 버튼이 자동으로 활성화된 상태로 렌더링됩니다. 명시적 초기화가 항상 안전합니다.
실전 예제 2단계: 판매주문 필터링과 에러 처리
onStatusChange: function (oEvent) {
const sKey = oEvent.getParameter("key");
const oBinding = this.byId("orderTable").getBinding("items");
if (!oBinding) {
Log.warning("orderTable binding not ready");
return;
}
try {
const aFilters = sKey === "ALL"
? []
: [new Filter("status", FilterOperator.EQ, sKey)];
oBinding.filter(aFilters);
} catch (oErr) {
MessageToast.show("필터 적용 중 오류가 발생했습니다.");
Log.error(oErr.message, oErr.stack);
}
}
"ALL" 키로 필터 해제 의도를 명시적으로 표현하고, oBinding undefined 가능성을 방어하는 패턴이 핵심입니다.
실전 예제 3단계: 동적 항목과 권한 기반 enabled 제어
_loadStatusOptions: async function () {
const bCanShip = await this._checkShippingRole();
const aOptions = [
{ key: "ALL", text: "전체", enabled: true },
{ key: "PENDING", text: "대기", enabled: true },
{ key: "CONFIRMED", text: "확정", enabled: true },
{ key: "SHIPPED", text: "발송", enabled: bCanShip }
];
this.getView().getModel("view")
.setProperty("/statusOptions", aOptions);
}
RadioButton을 완전히 대체하지 못하는 케이스
SegmentedButton은 2~5개의 짧은 라벨 옵션, 즉각적 모드 전환이 본질인 시나리오에 최적화되어 있습니다. 옵션 개수가 5개를 넘거나, 각 옵션에 긴 설명이 필요한 경우, 또는 폼 안에서 다른 입력 필드들과 세로 흐름을 유지해야 하는 경우에는 RadioButtonGroup이 더 적합합니다.
접근성과 반응형 고려사항
SegmentedButton은 ARIA role="radiogroup"과 내부 aria-checked를 자동으로 부여하므로 스크린 리더에서 RadioButton 그룹과 동일하게 해석됩니다. 좁은 화면에서 텍스트가 잘릴 경우 icon만 표시하거나, sap.m.Select로 대체하는 반응형 패턴을 적용합니다. Device.system.phone 분기로 화면 폭에 따라 다른 컨트롤을 보여주는 방식도 자주 쓰입니다.
흔한 실수와 트러블슈팅
- 첫 진입 시 아무 버튼도 선택되지 않음: selectedKey에 바인딩된 모델 속성이 undefined인 경우. onInit에서 명시적 초기값 설정 필수.
- select 이벤트가 두 번 발생: 양방향 바인딩과 이벤트 핸들러에서 동시에 모델을 갱신하면 추가 변경 알림 트리거. 핸들러 안에서 setProperty 재호출 금지.
- 좁은 화면에서 텍스트 잘림: icon만 표시하거나 sap.m.Select로 반응형 대체.
- selectedKey 값이 OData 필터에 그대로 사용될 경우: 백엔드에서 화이트리스트 검증 병행 필요.
댓글 0
아직 댓글이 없습니다.
💬 댓글 작성, 좋아요, 북마크는 UI5 모드에서 사용할 수 있습니다.
UI5 모드에서 사용하기