TagField (태그필드)
Va.TagField — 라벨 + 태그(칩) 입력 + 검증까지 감싼 완성 폼 필드
Va.Tag가 순수 태그 칩 입력이라면, Va.TagField는 그 위에 라벨·필수 표시·검증 메시지·설명 텍스트 + 부모-자식 필드 바인딩을 얹은 완성 폼 필드입니다. ComboboxField와 완전히 같은 아키텍처 패턴을 따르되, 내부에 Va.Combobox 대신 Va.Tag 를 소유합니다.
- 클래스: Va.TagField — va_tag.js:1407
- short name: tagField
- 상속: Va.Field (ComboboxField·InputField 등과 형제)
- 파일: va_tag.js (import 필요)
- 내부 컴포넌트: Va.Tag 인스턴스 (fieldComponent)
- isContainer: true
- 베이스 CSS: va-tag-field

1. 기본 사용
import '../../lib/va_tag.js'; // ← 필수 (Tag + TagField 둘 다 이 파일에)
// config 안에서
{
tagName: 'tagField',
label: '기술 스택',
data: [
{ key: 'js', display: 'JavaScript' },
{ key: 'ts', display: 'TypeScript' },
{ key: 'py', display: 'Python' },
{ key: 'go', display: 'Go' }
],
value: ['js', 'ts'],
required: true,
onSelect: 'onTagChange'
}
라벨 + 검증 + 팝업 드롭다운 + 칩 UI가 한 번에 세팅됩니다.
2. Field 계열에서의 위치 — ComboboxField의 판박이
TagField는 사실상 ComboboxField의 소스를 복사해서 내부만 Combobox → Tag로 바꾼 것입니다. 옵션 구성, 이벤트 재발화, 메서드 위임 패턴이 거의 100% 동일해요.
Va.Field
├─ Va.InputField ← Va.Input
├─ Va.SearchField ← Va.Search
├─ Va.NumberField ← Va.Number
├─ Va.ComboboxField ← Va.Combobox
├─ Va.FilterboxField ← Va.Filterbox
├─ Va.TagField ← Va.Tag ← 이 문서
└─ ...
차이는 하나: 내부에 어떤 컴포넌트가 들어가느냐. 그래서 옵션·메서드·이벤트 세트가 ComboboxField와 거의 같은 걸 보게 됩니다.
3. Va.Tag / Va.ComboboxField와의 차이
항목Va.TagVa.TagFieldVa.ComboboxField
| 라벨 | ✕ | ✓ | ✓ |
| 검증 메시지 | ✕ | ✓ | ✓ |
| info 툴팁 | ✕ | ✓ | ✓ |
| 선택 표시 방식 | 칩 (chip) | 칩 (chip) — Tag 위임 | 텍스트 콤마 나열 |
| 개별 삭제 X 버튼 | ✓ | ✓ | ✕ |
| multiSelect 기본 | true | true | false |
| bindParams (부모 바인딩) | ✓ | ✓ | ✓ |
| bindCallbackKeys (자식 자동 채움) | ✓ | ✓ | ✓ |
| 파일 | va_tag.js | va_tag.js | va_component.js |
한 줄 요약: "다중 선택 + 칩 UI + 라벨 + 검증"이 필요한 폼 필드.
4. 주요 속성 — Tag 옵션 + Field 옵션 + 바인딩 옵션
ComboboxField와 거의 동일합니다.
데이터 관련
속성기본값설명
| data | — | 항목 배열 |
| fakeData | — | 에디터 미리보기 |
| key | 'key' | 값 필드명 |
| display | 'display' | 표시 필드명 |
| displayType | 'display' | key / display / both — 칩 안 텍스트 형태 |
| template | 자동 생성 | 팝업 항목 렌더 템플릿 (칩 자체 아님) |
| dataMode | 'data' | 데이터 소스 모드 |
선택 동작
속성기본값설명
| value | — | 선택된 키 배열 |
| multiSelect | true | 기본이 true |
| addCheckAll | — | "전체" 체크 |
| clickToSelect | true | 클릭 선택 |
| getValueType | — | 'string'이면 getValue()가 "'a','b','c'" 형태 |
| fromValue / toValue | — | 범위 옵션 |
팝업
속성기본값설명
| expanded | false | 팝업 초기 상태 |
| popWidth | 자동 | 팝업 폭 |
| popMaxHeight | '400px' | 팝업 최대 높이 |
라벨 (Field 상속)
속성설명
| label | 라벨 텍스트 또는 객체 |
| labelPosition | top / bottom / left / right |
| labelWidth | 라벨 폭 |
| noLabel | 라벨 숨김 |
| infoButton | info 아이콘 |
| required | 필수 표시 |
검증
속성설명
| validation | {state, size, message} |
| validationState | success / warning / error |
| validationMessage | 메시지 |
바인딩 (ComboboxField와 동일)
속성설명
| bindParams | 팝업 열기 전 부모 필드 값 자동 수집 |
| bindRequired | 바인딩 필드 비면 팝업 안 열림 |
| bindCallbackKeys | 선택 후 다른 필드에 값 자동 세팅 |
| bindCallbackClearKeys | 선택 후 다른 필드 자동 비우기 |
| commonCode | 공통 코드 마스터 |
| commonBlankKey | "빈 값" 옵션 key |
| dynamicCode | 동적 코드 (파라미터 기반) |
세부 커스터마이즈 (tag 옵션 키)
{
tagName: 'tagField',
label: '태그',
tag: { // ← 내부 Tag에 직접 전달
rowHeight: 40,
pageSize: 50
}
}
각 Field 계열 옵션 키:
- InputField → input
- SearchField → search
- NumberField → number
- ColorField → colorPicker
- ComboboxField → combobox
- TagField → tag
5. 이벤트
Tag의 이벤트를 모두 재발화:
이벤트시그니처발생 시점
| select | (component, element, listItem, data, key, display, evt) | 팝업에서 선택 시 또는 칩 X로 제거 시 |
| beforePop / afterPop | 팝업 표시 | |
| hidePop | 팝업 숨김 | |
| expand / collapse | 팝업 확장 | |
| focus / blur | 표준 | |
| change / click / keydown / keyup | Field 표준 (검증 자동 리셋) |
⚠️ X 클릭 시 select 이벤트는 인자 개수가 짧음 — Tag 자체의 동작 그대로. 팝업 선택 시 7개, X 클릭 시 3개. 콜백 방어 필요.
6. 메서드 — Tag의 모든 메서드에 대한 위임
값
메서드설명
| getValue() | 선택된 키 배열 반환. getValueType: 'string'이면 문자열 |
| setValue(value) | 배열 또는 단일 값. null/"" 넘기면 초기화 |
| getDisplay() / getDisplayAsText() | 표시 텍스트 |
데이터 (Tag 위임)
메서드설명
| setData(data) | 데이터 교체 (선택된 태그 초기화됨) |
| getData() | 팝업 리스트 데이터 (선택 제외) |
| getSelectedData() | 선택된 전체 객체 배열 |
| addData(data) / insertData(data, baseData) | 추가/삽입 |
| modifyData(data, cls) | 수정 |
| removeData(data) / removeDataByKey(key) | 제거 |
| moveData(data, beforeData) | 이동 |
| select(data) | 프로그램적 선택 |
| selectFirstData(eventOccur) | 첫 항목 자동 선택 |
| focusFirstData(eventOccur) | selectFirstData 별칭 |
| applyData() | 데이터 반영 |
| getDataById(vaDataId) | 내부 ID로 조회 |
| getDataByListItem(listItem) / getListItemByData(data) | 상호 조회 |
상태 (Field 상속)
메서드설명
| setDisabled(bool) / getDisabled() | 비활성화 |
| setReadOnly(bool) / setReadonly(bool) | 읽기 전용 |
| setLabel(label) | 라벨 변경 |
| setPlaceholder(text) | 플레이스홀더 |
| setSize(size) | 크기 |
검증
메서드설명
| setValidation(state, message) | 검증 표시 + aria 자동 |
| clearValidation() | 검증 해제 |
바인딩
메서드설명
| getBindParams(component) | 부모 필드 값 수집 (Tag 위임) |
| setCallbackKeys(component, data) | 자식 필드 자동 채움 |
포커스
메서드설명
| focus() / blur() | 내부 Tag의 fieldElement에 위임 |
7. 내부 구조
<div elname="element" class="va-field va-tag-field [vertical|horizontal]" field="true">
<div elname="inner" class="field-inner">
<div elname="labelDiv" class="label-div">
<label cpname="label">기술 스택 <span class="required">*</span></label>
</div>
<div elname="comment" class="field-comment"></div>
<div elname="fieldDiv" class="field-div">
<div cpname="field" class="va-input"> ← 내부 Va.Tag
<div class="field-wrapper">
<div elname="field" tabindex="-1"
style="display:flex; flex-wrap:wrap; gap:3px">
<span class="va-tag-item">JavaScript ✕</span>
<span class="va-tag-item">TypeScript ✕</span>
</div>
<div class="focus-line"></div>
<div class="va-combobox-dropdown">▼</div>
</div>
<!-- 팝업은 hiddenArea로 이동됨 -->
</div>
</div>
</div>
<div elname="validationDiv" style="display:none">
<div class="va-validation">...</div>
</div>
</div>
8. 언제 쓰나
TagField가 맞을 때
- 폼 안 다중 태그 선택 (게시글 카테고리 여러 개, 참여자 목록, 관심 분야)
- 개별 삭제 UX가 필요한 폼 필드
- 라벨·필수·검증 메시지가 필요한 태그 UI
- Gmail·GitHub 스타일 라벨 지정
다른 걸 쓸 때
- 라벨 없는 인라인 → Va.Tag
- 콤마 텍스트로 충분 (칩 안 필요) → Va.ComboboxField + multiSelect: true
- 텍스트 필터링 강화 → Va.FilterboxField
- 단일 선택 → Va.ComboboxField
9. 흔한 조합 예시
// 표준 사용
{
tagName: 'tagField',
label: '카테고리',
data: [
{ key: 'work', display: '업무' },
{ key: 'personal', display: '개인' },
{ key: 'urgent', display: '긴급' }
],
value: ['work'],
required: true
}
// 서버 전송용 문자열 반환
{
tagName: 'tagField',
label: '권한',
data: [...],
getValueType: 'string', // "'admin','user','guest'" 형태
onSelect: 'onPermissionChange'
}
// 좌측 라벨 + 필수
{
tagName: 'tagField',
label: '참여자',
labelPosition: 'left',
labelWidth: 100,
required: true,
data: [...]
}
// 커스텀 팝업 템플릿 (아이콘 + 설명)
{
tagName: 'tagField',
label: '기술',
template: {
tagName: 'listItem',
layout: 'ds-flex fd-row ai-center gap-s',
tags: [
{ tagName: 'i', innerHTML: '{icon}' },
{ tagName: 'span', innerHTML: '{display}' },
{ tagName: 'span', innerHTML: '{level}', style: 'color:gray' }
]
},
data: [
{ key: 'js', display: 'JavaScript', icon: '🟨', level: '숙련' }
]
}
// 부모-자식 바인딩 (지역에 따른 지점 태그)
{
tagName: 'comboboxField',
label: '지역',
ref: 'region',
data: [...]
},
{
tagName: 'tagField',
label: '지점 (복수 선택)',
bindParams: { regionCode: 'region' },
bindRequired: true,
dynamicCode: 'BRANCH_BY_REGION'
}
// 팝업 폭 크게
{
tagName: 'tagField',
label: '관심 주제',
popWidth: 400,
popMaxHeight: '500px',
data: [...]
}
10. 알아두면 좋을 주의사항
- va_tag.js import 필수 — Tag와 TagField가 같이 들어 있음.
- getValue() 반환은 배열 또는 문자열 — getValueType 옵션에 따라. 서버 전송 시 확인.
- select 이벤트 인자 개수 상황에 따라 다름 — 팝업 선택 7개, 칩 X 클릭 3개. 방어 코드 필요.
- 자유 태그 생성 불가 — 사전 정의된 data에서만 선택. 사용자 타이핑으로 새 태그 만드는 기능 없음.
- autoSetFieldValue: false 강제 — Tag 로직에 맞춤.
- multiSelect: true가 기본 — TagField를 single로 쓸 일이 별로 없지만, 굳이 하려면 명시 지정.
- 팝업이 hiddenArea로 이동 — 부모 overflow/z-index 영향 없음.
- 바인딩 이벤트 자동 등록 — bindParams/bindCallbackKeys/bindCallbackClearKeys 중 하나라도 있으면 자동. ComboboxField는 명시적 등록 코드가 있는데 TagField에는 없음 → 바인딩 옵션이 있어도 자동 트리거 이벤트 리스너 코드는 빠져 있음. Combobox의 내부 로직에 의존.
- addCheckAll은 실제로 동작할지 확인 필요 — TagField의 옵션 목록엔 있으나 Tag의 template 자동 생성 로직에서 checkbox를 만들지 여부가 미묘. 데모에서 검증 권장.
- 템플릿 자동 생성 — 지정 안 하면 display 필드만 있는 심플 리스트. TagField는 Tag와 달리 checkbox 자동 포함 없음(이 자체가 Tag의 X 클릭으로 대체됨).
- _hiddenRootElement가 popElement — 팝업 상태머신 참여.
- 옵션 키가 tag — 다른 Field 형제들과 헷갈리지 말 것.
- getBindParams / setCallbackKeys 위임 메서드 존재 — 하지만 setCallbackKeys는 자기 자신을 재귀 호출하는 구현 실수가 있음 (va_tag.js:1411-1413) — 호출 시 스택 오버플로 위험. 필요 시 fieldComponent.setCallbackKeys() 직접 호출.
- focus()는 fieldElement에 포커스 — Tag의 field는 tabindex=-1이라 실질적 효과 미미. 팝업 열기 원하면 showPop() 직접.
11. TagField가 없어도 되는가?
기술적으로는 Va.Tag만으로도 라벨을 직접 붙여 폼을 만들 수 있습니다. 하지만:
- 검증 통합: setValidation(), clearValidation()으로 표준 폼 검증 UX 자동
- 폼 통일성: 다른 Field 계열과 라벨/에러 위치·간격이 자동 통일
- ARIA 접근성: 라벨-필드 연결이 자동
- 바인딩 옵션: bindParams, bindCallbackKeys 등 폼 필드 간 연동 문법 사용
이걸 손으로 다시 짜는 것보다 TagField 쓰는 게 압도적으로 편합니다.