Combobox (콤보박스)
Va.Combobox — 검색·다중선택·가상 스크롤을 갖춘 드롭다운
VanillaFront의 가장 복잡한 폼 필드입니다. 이름은 콤보박스지만 실질적으로는 미니 그리드 + 드롭다운 + DataManager 통합에 가까워요. 단순 선택 리스트부터 수천 건 데이터의 다중 선택 목록까지 커버합니다.
- 클래스: Va.Combobox — va_component.js:1330
- short name: combobox
- 상속: Va.PureField (Input/Search/Number와 형제)
- isContainer: true
- 베이스 CSS: va-input (+ 팝업은 va-menu-pop)
- 소스 크기: 약 1508줄 — Combobox 계열에서 가장 큼


1. 기본 사용
{
tagName: 'combobox',
value: '3',
data: [
{ key: '1', display: '하나' },
{ key: '2', display: '둘' },
{ key: '3', display: '셋' },
{ key: '4', display: '넷' }
],
onSelect: 'onSelectItem'
}
필드 클릭 → 팝업 리스트 표시 → 항목 클릭 → 값 확정 + 팝업 닫힘.
2. 다른 PureField 계열과의 차이
항목Va.InputVa.Combobox
| 역할 | 자유 텍스트 입력 | 사전 정의된 항목에서 선택 |
| 팝업 | ✕ | ✓ (리스트) |
| 다중 선택 | ✕ | ✓ (multiSelect) |
| DataManager 통합 | ✕ | ✓ |
| 템플릿 렌더링 | ✕ | ✓ (template으로 각 항목 커스텀) |
| 가상 스크롤 | ✕ | ✓ (대용량 리스트용, pageSize: 30) |
| 컬럼 표시 | ✕ | ✓ (columns — 그리드처럼) |
| 키보드 네비 | 기본 | ✓ (↑↓ Tab Enter Esc 지원) |
| 자체 파일 크기 | (공유) | (공유하지만 1500줄) |
한 줄 요약: "옵션 데이터에서 하나(또는 여러 개)를 골라 값을 확정하는 필드."
3. 핵심 데이터 개념
Combobox 데이터는 두 가지 축으로 이해합니다:
data — 항목 배열
data: [
{ key: '1', display: '하나' },
{ key: '2', display: '둘' }
]
각 항목은 최소 키(값으로 저장될 것) 와 디스플레이(사용자에게 보일 텍스트) 두 필드를 가집니다. 필드명은 옵션으로 지정:
{
tagName: 'combobox',
key: 'code', // key 필드명 (기본 'key')
display: 'name', // display 필드명 (기본 'display')
data: [
{ code: 'A', name: '서울' },
{ code: 'B', name: '부산' }
]
}
template — 각 항목의 렌더링 형태
template: {
tagName: 'listItem',
layout: 'ds-flex fd-row ai-center gap-s',
tags: [
{ tagName: 'div', innerHTML: '{key}' }, // {key}는 데이터의 key 필드
{ tagName: 'div', innerHTML: '{display}' }
]
}
{필드명} 문법으로 데이터 각 필드를 참조. 아이콘, 여러 컬럼, 커스텀 UI 등 자유롭게 조립 가능.
4. 주요 속성
데이터 관련
속성기본값설명
| data | — | 항목 배열 |
| fakeData | — | 에디터(runtime='edit') 모드용 미리보기 데이터 |
| dataMode | 'data' | 데이터 소스 모드 |
| key | 'key' | 값으로 저장할 필드명 |
| display | 'display' | 표시할 필드명 |
| displayType | 'display' | key / display / both — 필드에 뭘 표시할지 |
| template | 자동 생성 | 각 항목 렌더 템플릿 |
| columns | — | 그리드형 컬럼 정의 |
선택 동작
속성기본값설명
| value | — | 현재 선택된 값 (multiSelect면 배열) |
| multiSelect | false | 다중 선택 활성화 (체크박스 자동 렌더) |
| addCheckAll | — | multiSelect일 때 "전체" 체크박스 자동 추가 |
| clickToSelect | true | 항목 클릭으로 선택 |
| commonBlankKey | — | "빈 값" 옵션의 key |
| editable | false | 필드 텍스트 직접 편집 허용 |
팝업
속성기본값설명
| expanded | false | 팝업 초기 상태 |
| popWidth | 자동(필드 폭) | 팝업 폭 |
| popMaxHeight | '400px' | 팝업 최대 높이 (초과 시 스크롤) |
| rowHeight | 34 (size에 따라 자동) | 각 항목 높이 |
PureField 상속
value, placeholder, readonly, disabled, size, appearance, stopPropagation 등 모두 유효.
⚠️ autoSetFieldValue: false 강제 — Combobox는 값 세팅 로직을 자체 처리하므로 PureField 기본 자동 세팅을 끔.
5. multiSelect — 다중 선택 모드
{
tagName: 'combobox',
multiSelect: true,
addCheckAll: true, // "전체" 체크박스 추가
data: [
{ key: '1', display: '서울' },
{ key: '2', display: '부산' },
{ key: '3', display: '대구' }
]
}
- 각 항목 앞에 체크박스 자동 렌더 (별도 template 지정 안 해도)
- addCheckAll:true면 상단에 "전체" 체크박스 → 클릭 시 전체 선택/해제
- 선택 시 팝업이 자동으로 안 닫힘 (여러 개 선택 편의)
- getValue()는 선택된 키들의 배열 반환
6. 이벤트
이벤트시그니처발생 시점
| select | (component, element, listItem, data, key, display, evt) | 항목 클릭으로 선택 시 — 가장 자주 씀 |
| change | (component, element, evt) | 값 확정 시 |
| expand / collapse | 팝업 확장/축소 | |
| beforePop / afterPop | 팝업 표시 직전/직후 | |
| pop / hidePop | 팝업 요청 | |
| focus / blur | 표준 | |
| click | 필드 클릭 | |
| listItemClick | 항목 클릭 (select 이전) | |
| listItemContextmenu | 항목 우클릭 |
select 콜백 예시
onSelectItem(component, element, listItem, data, key, display, evt) {
console.log('선택된 키:', key); // "3"
console.log('선택된 표시:', display); // "셋"
console.log('전체 데이터:', data); // { key: '3', display: '셋' }
}
7. 핵심 메서드
값 관리
메서드설명
| getValue() | 선택된 값 반환. multiSelect면 배열. |
| setValue(value) | 값 세팅 (해당 항목이 데이터에 있으면 자동 선택 표시) |
| getSelectedData() | 선택된 항목의 전체 데이터 객체 반환 (key만이 아니라 display 등 모든 필드) |
데이터 CRUD (DataManager 위임)
메서드설명
| setData(data) | 데이터 전체 교체 (_listItemMap / value 초기화됨) |
| getData() | 현재 데이터 배열 반환 |
| addData(data) | 항목 하나 추가 |
| insertData(newData, refData) | refData 앞에 새 항목 삽입 |
| modifyData(data) | 항목 수정 (같은 vaDataId 항목 갱신) |
| removeData(data) | 항목 제거 |
| moveData(data, refData) | 항목 위치 이동 |
| selectData(data) | 프로그램적으로 특정 데이터 선택 |
팝업 제어
메서드설명
| showPop() / hidePop() | 팝업 표시/숨김 (레거시 별칭도 있음) |
| showComboboxPop() / hideComboboxPop() | 직접 이름 |
상태
메서드설명
| setDisabled(bool) / setReadonly(bool) | PureField 상속 |
| focus() / blur() | 포커스 제어 |
Demo에서 실제 사용례 → DemoCombobox.js:73-129
8. 내부 구조
<div elname="element" class="va-input [size]..." tag-name="combobox" field="true">
<div elname="fieldWrapper" class="field-wrapper"
role="combobox" aria-expanded="false" aria-haspopup="listbox">
<input elname="field" type="text" style="border:0px">
<div elname="focusLine" class="focus-line"></div>
<div elname="dropdownIconWrapper" class="va-combobox-dropdown">
<span elname="dropdownIcon" class="icon menu ico_chevron_down">▼</span>
</div>
</div>
<div elname="popDiv" class="menu-button-pop-div">
<div elname="pop" class="va-menu-pop" role="listbox"
style="position:absolute; display:none">
<div elname="popInner" class="combobox-pop-div"
style="max-height:400px; overflow-y:auto">
<!-- listItem들이 여기 렌더됨 -->
<div tag-name="listItem" class="va-list-item">…</div>
...
</div>
</div>
</div>
</div>
핵심 트릭:
- 팝업은 열릴 때 getHiddenAreaElement()로 이동해 부모 stacking context 회피
- 리스트는 popInnerElement에 이벤트 위임(delegation) — 각 항목에 리스너 개별 부착이 아니라 부모 하나에 걸어 대량 항목도 빠름 (va_component.js:2365-2400)
- ARIA 통합 — role="combobox", role="listbox", aria-expanded, aria-haspopup 자동 세팅
9. 가상 스크롤 (Virtual Scroll)
수천 건 데이터도 부드럽게 처리하기 위해 페이지 단위로만 DOM 렌더링:
- pageSize: 30 — 한 번에 30개씩 렌더
- 스크롤 이벤트로 다음 페이지 로드 (va_component.js:1458-1461)
- _scrollRafId로 requestAnimationFrame 최적화
이 덕분에 데이터가 몇 만 건이어도 UI가 느려지지 않습니다.
10. 키보드 조작
키동작
| ↓ / ↑ | 항목 이동 |
| Enter | 선택 |
| Tab | 팝업 닫고 다음 필드로 |
| Esc | 팝업 닫기 |
| 문자 키 | filter 옵션 켜져 있으면 검색 (Filterbox 참조) |
11. 팝업 상태머신 연동
CLAUDE.md 6장에서 "민감 영역"으로 지정된 프레임워크 팝업 상태머신에 참여:
- 다른 팝업(DatePicker, MenuButton 등) 열려 있으면 자동 닫고 자기 팝업 표시
- 외부 클릭 시 Va.autoHide로 자동 닫힘
- Va.hideOtherComponents() 호출
여러 팝업 동시 열림 방지 UX가 자동 보장됩니다.
12. Va.Combobox vs Va.ComboboxRaw vs Va.Filterbox vs Va.ComboboxField
같은 계열의 형제·확장 컴포넌트가 여럿:
컴포넌트차이
| Va.Combobox | 표준 드롭다운 (이 문서 대상) |
| Va.ComboboxRaw | 기본형 (필터·다중선택 등 기능 축소, 경량) |
| Va.Filterbox | Combobox + 입력한 텍스트로 필터링 강화 |
| Va.FilterboxRaw | Filterbox 경량형 |
| Va.ComboboxField | Combobox + Field 래퍼 (라벨/검증/설명) |
| Va.FilterboxField | Filterbox + Field 래퍼 |
선택 기준:
- 라벨 필요 → ComboboxField
- 텍스트 타이핑으로 필터 → Filterbox
- 최소 기능만 → ComboboxRaw
- 표준 → Combobox
13. 언제 쓰나
Combobox가 맞을 때
- 사전 정의된 항목에서 선택 (부서, 카테고리, 국가, 상태)
- 다중 선택 필터 (multiSelect + addCheckAll)
- 각 항목에 아이콘·설명·여러 컬럼 표시 필요할 때 (template 커스텀)
- 대량 데이터(수백~수만 건) 선택 UI
다른 걸 쓸 때
- 자유 텍스트 입력 → Va.Input
- 텍스트 자동완성 필터 → Va.Filterbox
- 라디오형 배타 선택 (5개 이하) → Va.RadioGroup / Va.SegmentedControl
- 팝업 없는 리스트 → Va.List
- 커스텀 액션 트리거 → Va.MenuButton
- 트리 구조 → Va.TreeGrid
14. 흔한 조합 예시
// 표준 선택
{
tagName: 'combobox',
data: [
{ key: 'A', display: '서울' },
{ key: 'B', display: '부산' }
],
value: 'A'
}
// 다중 선택 + 전체 선택
{
tagName: 'combobox',
multiSelect: true,
addCheckAll: true,
data: [...],
onSelect: 'onCategorySelect'
}
// 커스텀 필드명 + 템플릿
{
tagName: 'combobox',
key: 'code',
display: 'name',
template: {
tagName: 'listItem',
layout: 'ds-flex fd-row ai-center gap-s',
tags: [
{ tagName: 'i', innerHTML: '{icon}' },
{ tagName: 'span', innerHTML: '{name}' },
{ tagName: 'span', innerHTML: '{description}', style: 'color:gray' }
]
},
data: [
{ code: 'A', name: 'Alpha', icon: '⭐', description: '최상' },
{ code: 'B', name: 'Beta', icon: '✨', description: '중간' }
]
}
// 값 + 표시 둘 다 표시
{
tagName: 'combobox',
displayType: 'both', // "A - Alpha" 형태로 표시
data: [...]
}
// 큰 팝업 (넓게)
{
tagName: 'combobox',
popWidth: 400,
popMaxHeight: '500px',
data: [...]
}
15. 알아두면 좋을 주의사항
- getValue() 반환 타입 — single이면 문자열/숫자, multiSelect면 배열. 서버 전송 시 타입 검사.
- setValue()는 데이터에 있어야 반영 — data에 없는 key를 세팅하면 무시되거나 필드가 비어 보임.
- select 이벤트 시그니처 매우 김 — (component, element, listItem, data, key, display, evt) 7개 인자. 필요한 것만 받으면 됨.
- autoSetFieldValue: false 강제 — PureField의 자동 값 세팅 로직 우회. 커스터마이즈 시 주의.
- template 없이도 기본 렌더링 — display 필드만 있는 심플 리스트로 자동 생성.
- addCheckAll은 multiSelect일 때만 유효 — single에서는 무시.
- 팝업이 hiddenArea로 이동 — 부모 overflow/z-index 영향 없음.
- 가상 스크롤 자동 — 데이터 대량일 때 별도 옵션 없이 자동 적용.
- ARIA 자동 세팅 — combobox / listbox / expanded / haspopup. 접근성 준수.
- data는 structuredClone으로 복사됨 — 원본 배열 수정해도 combobox에 자동 반영 안 됨. setData() 재호출 필요.
- vaDataSelected, vaDataId 등 내부 필드 — DataManager가 항목마다 자동으로 부여하는 메타. 사용자 필드명과 충돌 조심.
- 팝업 상태머신 참여 — 다른 팝업과 자동 상호 배타.
- editable: true시 자유 입력 가능 — 데이터에 없는 값도 필드에 남지만 선택 상태는 아님.
- focus() 호출 시 팝업이 자동으로 열리지는 않음 — 팝업이 필요하면 showPop() 별도 호출.