TL;DR
- 드롭다운 자리표시를 넣었는데 ComboBox 칸이 빈 채로 열린다.
- Select는
forceSelection기본값이true라 첫 항목을 자동으로 고른다. ComboBox엔 그 속성이 없다. - 자리표시 키를
'ALL'같은 실제 값으로 주고, 서버로 보낼 때만 빈 값으로 되돌린다.
환경
| 항목 | 값 |
|---|---|
| SAPUI5 | 1.108.7 (운영) / 1.136.0 (빌드) |
| 테마 | sap_belize |
| 컨트롤 | sap.m.ComboBox, sap.ui.core.ListItem |
| 모델 | JSONModel 양방향 바인딩 |
🚨 문제 상황
드롭다운 맨 앞에 「- 선택 -」 같은 자리표시를 넣는 건 흔한 요구사항이다.
아무것도 안 고른 상태를 화면에 보여 줘야 하고, 그 상태는 서버로 빈 값으로 보내야 한다. 그래서 키를 빈 문자열로 뒀다.
<ComboBox
selectedKey="{/kind}"
items="{/kindSet}"
change=".onKindChange">
<core:ListItem key="{code}" text="{text}"/>
</ComboBox>
oModel.setProperty('/kindSet', [
{ code: '', text: '- 선택 -' }, // 자리표시
{ code: '01', text: '본인' },
{ code: '02', text: '배우자' },
]);
oModel.setProperty('/kind', '');
그런데 화면을 열면 칸이 비어 있다.
드롭다운을 펼치면 세 줄이 다 있는데, 칸에는 아무것도 안 떠 있다. 같은 코드표를 Select에 넣으면 「- 선택 -」이 보인다.

콘솔은 조용하다. 경고 하나 없다.
console.log(oModel.getProperty('/kindSet').length); // 3
console.log(oModel.getProperty('/kindSet')[0]); // { code: '', text: '- 선택 -' }
원인 추적
처음 의심한 것
데이터가 세 줄인데 칸이 비어 있으니 바인딩부터 뒤졌다.
| 의심 | 확인 | 결과 |
|---|---|---|
| 모델에 안 들어갔나 | getProperty로 길이 확인 |
3줄 들어 있음 |
| 목록에 안 그려졌나 | 드롭다운 펼쳐서 세어 봄 | 세 줄 다 있음 |
forceSelection을 안 줬나 |
속성 추가 | ComboBox엔 없는 속성 |
| 컨트롤 문제인가 | 같은 데이터를 Select로 교체 |
정상 동작 |
두 번째 줄이 의외였다. 목록에는 멀쩡히 있다.
안 보이는 건 칸에 고른 값이 안 뜨는 것뿐이었다.
<!-- 이건 「- 선택 -」 이 멀쩡히 보인다 -->
<Select selectedKey="{/kind}" items="{/kindSet}">
<core:Item key="{code}" text="{text}"/>
</Select>
콘솔에서 두 컨트롤을 나란히 재 봤다.
// 코드표도 selectedKey 도 똑같이 넣은 상태
oCombo.getSelectedItem(); // null ← 아무것도 안 골랐다
oSelect.getSelectedItem(); // '- 선택 -' ← 첫 항목을 골라 뒀다
진짜 원인
범인은 forceSelection이었다.
sap.m.Select는 이 속성의 기본값이 true다. selectedKey가 어떤 항목과도 안 맞으면 첫 항목을 자동으로 고른다.
그래서 빈 문자열을 줘도 목록 첫 줄인 「- 선택 -」이 칸에 떴다. 내가 의도한 게 아니라 Select가 알아서 메워 준 것이다.
sap.m.ComboBox에는 그 속성이 없다. 못 맞추면 아무것도 안 고른 상태로 둔다. 그래서 칸이 빈다.
더 위험한 쪽
ComboBox에서 사용자가 「- 선택 -」을 직접 클릭하면 칸에 글자는 뜨지만 selectedKey는 여전히 빈 문자열이다. 모델만 보면 「자리표시를 골랐다」와 「아무것도 안 골랐다」가 구분되지 않는다.
그리고 화면은 끝까지 조용하다. 콘솔 경고도 없고 바인딩 오류도 없다.
해결
자리표시 키를 빈 문자열이 아닌 값으로 준다.
oModel.setProperty('/kindSet', [
{ code: 'ALL', text: '- 선택 -' }, // 빈 문자열 아님
{ code: '01', text: '본인' },
{ code: '02', text: '배우자' },
]);
oModel.setProperty('/kind', 'ALL'); // 초기값도 'ALL'

대신 'ALL'이 서버로 샐 수 있으니, 나가는 길목에서만 빈 값으로 되돌린다.
// 'ALL' 은 선택 안 한 상태다. 밖으로 나갈 때만 빈 값으로.
const pick = (s) => (s && s !== 'ALL' ? s : '');
const isEmpty = (s) => !s || s === 'ALL';
// 저장
const mPayload = { Kind: pick(oModel.getProperty('/kind')) };
// 필수값 검사
if (isEmpty(oModel.getProperty('/kind'))) {
throw new Error('구분을 선택하십시오.');
}
이 두 함수는 한 군데 모아 둔다. 자리표시를 쓰는 칸이 늘어나면 어디선가 한 번 빠뜨리고, 그러면 'ALL'이 그대로 DB에 들어간다.
같은 함정이 또 나오는 곳
1. 하위 칸 되돌리기
「구분을 바꾸면 하위 선택을 비운다」 같은 로직에서 또 걸린다.
// ❌ 칸이 '빈 칸' 으로 남는다
onKindChange() {
this.getModel().setProperty('/subKind', '');
}
// ✅ 자리표시로 되돌아온다
onKindChange() {
this.getModel().setProperty('/subKind', 'ALL');
}
''를 넣으면 ComboBox가 아무것도 안 고른 상태가 돼서 칸이 빈다. 사용자 입장에선 뭔가 깨진 것으로 보인다.
2. 코드표를 비울 때
조건에 따라 코드표를 통째로 비우면 자리표시까지 날아간다.
// ❌ 자리표시까지 사라진다
oModel.setProperty('/kindSet', []);
// ✅ 자리표시 한 줄은 남긴다
const withPlaceholder = (aEntry) => [{ code: 'ALL', text: '- 선택 -' }, ...(aEntry || [])];
oModel.setProperty('/kindSet', withPlaceholder(aFromServer));
확인
- 칸에 뜨나 — 화면을 열자마자 「- 선택 -」이 보이는지
getSelectedItem()—null이 아닌지- 돌아오나 — 다른 값 골랐다가 다시 자리표시로 되돌릴 수 있는지
- 안 새나 — 저장 payload에
'ALL'이 아니라 빈 문자열이 실리는지
2번이 핵심이다. 눈으로는 「골라진 것」과 「글자만 떠 있는 것」을 구분할 수 없다.
💡 배운 것
데이터를 의심하느라 컨트롤은 한참 뒤에야 봤다. 모델을 찍고 바인딩 경로를 바꾸고 서버 응답을 다시 받는 동안, 범인은 계속 같은 자리에 있었다.
「같은 데이터를 다른 컨트롤에 꽂아 보기」를 먼저 했으면 데이터 쪽 의심이 한 번에 날아갔을 것이다.
그동안 「잘 동작한다」고 본 건 내 코드가 맞아서가 아니라 Select가 조용히 보정해 주고 있었기 때문이다. 보정이 없는 컨트롤로 옮기는 순간 드러났다.
프레임워크의 기본값은 내가 쓰지 않은 코드다. 그런데 결과에는 내 코드만큼 영향을 준다. 컨트롤을 바꿀 때 속성 이름이 같다고 안심하면 안 되는 이유다.
이어지는 글
- belize 테마에서 Select와 ComboBox 드롭다운 폭이 다른 이유
- renderType="Bare"로 바꾸자 라벨이 가운데로 몰렸다
'Front > SAPUI5' 카테고리의 다른 글
| belize 테마에서 Select 와 ComboBox 드롭다운 폭이 다른 이유 (0) | 2026.10.30 |
|---|---|
| OData 로 1900년 날짜를 보냈더니 500 — 한국 LMT +08:27:52 (0) | 2026.10.29 |
| SAPUI5 JSONModel (0) | 2026.06.12 |
| SAPUI5 Event Handling 가이드 (0) | 2026.06.12 |
| SAPUI5 XML View 가이드 (0) | 2026.06.04 |