컴포넌트

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() 별도 호출.