컴포넌트

Combobox (콤보박스)

VanillaFront 2026. 9. 10. 15:12

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 계열에서 가장 큼

Combobox
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. 알아두면 좋을 주의사항

  1. getValue() 반환 타입 — single이면 문자열/숫자, multiSelect면 배열. 서버 전송 시 타입 검사.
  2. setValue()는 데이터에 있어야 반영 — data에 없는 key를 세팅하면 무시되거나 필드가 비어 보임.
  3. select 이벤트 시그니처 매우 김 — (component, element, listItem, data, key, display, evt) 7개 인자. 필요한 것만 받으면 됨.
  4. autoSetFieldValue: false 강제 — PureField의 자동 값 세팅 로직 우회. 커스터마이즈 시 주의.
  5. template 없이도 기본 렌더링 — display 필드만 있는 심플 리스트로 자동 생성.
  6. addCheckAll은 multiSelect일 때만 유효 — single에서는 무시.
  7. 팝업이 hiddenArea로 이동 — 부모 overflow/z-index 영향 없음.
  8. 가상 스크롤 자동 — 데이터 대량일 때 별도 옵션 없이 자동 적용.
  9. ARIA 자동 세팅 — combobox / listbox / expanded / haspopup. 접근성 준수.
  10. data는 structuredClone으로 복사됨 — 원본 배열 수정해도 combobox에 자동 반영 안 됨. setData() 재호출 필요.
  11. vaDataSelected, vaDataId 등 내부 필드 — DataManager가 항목마다 자동으로 부여하는 메타. 사용자 필드명과 충돌 조심.
  12. 팝업 상태머신 참여 — 다른 팝업과 자동 상호 배타.
  13. editable: true시 자유 입력 가능 — 데이터에 없는 값도 필드에 남지만 선택 상태는 아님.
  14. focus() 호출 시 팝업이 자동으로 열리지는 않음 — 팝업이 필요하면 showPop() 별도 호출.

'컴포넌트' 카테고리의 다른 글

DatePicker (날짜선택)  (0) 2026.09.10