컴포넌트/필드 컴포넌트
CheckboxGroup (체크박스그룹)
VanillaFront
2026. 9. 12. 19:29
Va.CheckboxGroup — 여러 체크박스를 데이터로 관리하는 그룹
여러 개의 선택지 중 여러 개를 동시 선택할 수 있는 컴포넌트. 각 체크박스를 하드코딩으로 나열하지 않고, 데이터 배열로 선언해 한 번에 렌더링합니다. 값은 선택된 key들의 배열로 다룹니다.
- 클래스: Va.CheckboxGroup — va_component.js:10813
- short name: checkboxGroup
- 상속: Va.Component (PureField 아님!)
- isContainer: true
- 베이스 CSS: va-checkbox-group

1. 기본 사용
{
tagName: 'checkboxGroup',
key: 'code',
display: 'name',
data: [
{ code: 'A', name: '항목 A', checked: true },
{ code: 'B', name: '항목 B', checked: false },
{ code: 'C', name: '항목 C', checked: true }
],
onChange: 'onSelectionChange'
}
getValue() 결과: ['A', 'C'] — 체크된 항목의 key 배열.
2. Va.Checkbox를 나열하는 것과의 차이
여러 체크박스가 필요할 때 두 가지 접근이 있습니다.
✕ 하드코딩 나열
{
tagName: 'div',
tags: [
{ tagName: 'checkbox', checkboxLabel: '항목 A', ref: 'chkA' },
{ tagName: 'checkbox', checkboxLabel: '항목 B', ref: 'chkB' },
{ tagName: 'checkbox', checkboxLabel: '항목 C', ref: 'chkC' }
]
}
- 항목이 늘어나면 코드 반복
- 값 조회할 때 getRef('chkA').getChecked() 반복
- 서버에서 받은 동적 목록에 대응 못함
✓ CheckboxGroup
{
tagName: 'checkboxGroup',
key: 'code',
display: 'name',
data: [ ... ] // 배열 하나로 끝
}
- 데이터 배열 하나로 선언
- getValue() 한 번에 선택 결과 배열 획득
- 서버 응답을 그대로 setData() 로 넘기면 됨
3. CheckboxGroup vs Checkbox vs 다른 그룹
항목Va.CheckboxVa.CheckboxGroupVa.RadioGroup
| 선택 개수 | 1개 (단일) | N개 (다중) | 1개 (배타) |
| 데이터 배열로 렌더 | ✕ | ✓ | ✓ |
| 값 형태 | boolean/YN/10 | key 배열 | 단일 key |
| 상속 | PureField | Component | Component |
| getValue() 반환 | 체크 상태 | 선택된 key 배열 | 선택된 key |
| CRUD 메서드 | 없음 | addData/insertData | 유사 |
| 라벨 검증 | ✕ | ✕ | ✕ |
한 줄 요약: "옵션 배열에서 여러 개 선택 → 결과를 key 배열로 받는 컴포넌트."
4. 주요 속성
데이터 관련
속성기본값설명
| data | — | 옵션 배열. 각 항목은 { key값, display값, checked } 형태 |
| key | — | 값으로 저장할 필드명 (예: 'code') |
| display | — | 표시할 필드명 (예: 'name') |
| fakeData | — | 에디터 모드용 미리보기 데이터 |
배치·상태
속성기본값설명
| direction | 'horizontal' | 'horizontal'(가로) / 'vertical'(세로) |
| readonly | — | 읽기 전용 (모든 자식 체크박스에 전파) |
| disabled | — | 비활성화 (모든 자식에 전파) |
| size | — | 크기 |
| checkboxLabelClick | — | 라벨 클릭으로 토글 (기본 true) |
| stopPropagation | true | 이벤트 버블링 차단 |
5. 데이터 스키마
setData()가 받는 배열의 각 항목은 이런 필드를 가질 수 있습니다:
[
{
code: 'A', // key 필드 (옵션의 'key'로 지정한 이름)
name: '항목 A', // display 필드 (옵션의 'display'로 지정한 이름)
checked: true, // 초기 체크 상태 (생략 가능)
name: '...', // ⚠️ name 속성이 있으면 HTML name 그룹핑
}
]
⚠️ name 필드 오버로딩 주의 — 코드상 item.name이 없으면 componentId로 자동 세팅됩니다 (va_component.js:10875-10877). 데이터의 name 필드가 사용자 이름 같은 걸 담고 있으면 HTML name 속성과 충돌할 수 있어요. 데이터 필드명을 label, title 등으로 바꾸는 게 안전.
6. 이벤트
이벤트시그니처발생 시점
| change | (component, element, key, evt) | 자식 체크박스 상태 변경 시. key는 변경된 항목의 키 (전체 선택 배열이 아님!) |
| checkboxClick | — | 자식 체크박스 클릭 시 (실질 로직은 별도 확인 필요) |
| mousedown | (component, element, evt) | 그룹 마우스다운 |
change 콜백 예시
onSelectionChange(comp, el, key, evt) {
console.log('바뀐 항목:', key); // 예: 'B'
console.log('현재 전체 선택:', comp.getValue()); // 예: ['A', 'C']
}
⚠️ change의 세 번째 인자가 "변경된 항목의 key" — 전체 선택 상태 배열이 아닙니다. 전체를 알려면 comp.getValue() 재호출.
7. 메서드
값 조회·세팅
메서드설명
| getValue() | 체크된 항목의 key 배열 반환 (예: ['A', 'C']) |
| getDisplay() | 체크된 항목의 display 배열 반환 (예: ['항목 A', '항목 C']) |
| getDisplayAsText() | display를 콤마로 이은 한 줄 문자열 (예: '항목 A, 항목 C') |
| setValue(values) | key 배열을 넘기면 그에 맞춰 체크. 단일 값 넘기면 배열로 감쌈 |
데이터 관리
메서드설명
| setData(data) | 데이터 전체 교체. 기존 체크박스 모두 제거 후 재렌더 |
| getData() | 현재 데이터 반환 |
| addData(item) | 항목 하나 추가 (기본 체크됨) |
| insertData(item, index) | 특정 위치에 삽입 |
상태
메서드설명
| setDisabled(bool) | 자식 전체 비활성화 |
| setReadOnly(bool) / setReadonly(bool) | 자식 전체 읽기 전용 |
⚠️ removeData나 modifyData는 없음 — CRUD 완비형이 아니라 add/insert만 지원. 전체 교체가 필요하면 setData() 사용.
8. 내부 구조
<div elname="element" class="va-checkbox-group [horizontal|vertical]" field="true">
<div elname="inner" class="checkbox-group-inner">
<!-- 자식 체크박스들 -->
<div class="va-checkbox">
<div class="checkbox-inner">
<div class="field-wrapper"><input type="checkbox"><span>☑</span></div>
<label>항목 A</label>
</div>
</div>
<div class="va-checkbox">
<div class="checkbox-inner">
<div class="field-wrapper"><input type="checkbox"><span>☐</span></div>
<label>항목 B</label>
</div>
</div>
<!-- ... -->
</div>
</div>
- direction: 'horizontal' — 자식들이 가로 나열 (flex row)
- direction: 'vertical' — 자식들이 세로 나열 (flex column)
- CSS 클래스: .va-checkbox-group.horizontal / .va-checkbox-group.vertical
9. 언제 쓰나
CheckboxGroup이 맞을 때
- 여러 옵션 중 여러 개 선택 (관심 분야, 선호 태그, 필터 조건)
- 동적 목록 (서버에서 받은 카테고리 등)
- 항목 수가 5개 이상이거나 유동적
- 최종 값이 선택된 것들의 배열로 정리되면 편한 상황
다른 걸 쓸 때
- 단일 체크박스 (동의) → Va.Checkbox / Va.CheckboxField
- 여러 옵션 중 하나만 선택 → Va.RadioGroup
- 드롭다운 선택 (많은 옵션) → Va.Combobox + multiSelect
- 태그처럼 표시하며 선택 → Va.Tag
- 라벨·검증 필요 → Va.CheckboxGroupField
10. 흔한 조합 예시
// 표준 (초기 일부 체크됨)
{
tagName: 'checkboxGroup',
key: 'code',
display: 'name',
data: [
{ code: 'JS', name: 'JavaScript', checked: true },
{ code: 'TS', name: 'TypeScript' },
{ code: 'PY', name: 'Python', checked: true },
{ code: 'GO', name: 'Go' }
],
direction: 'horizontal'
}
// 세로 배치
{
tagName: 'checkboxGroup',
key: 'code',
display: 'name',
data: [ ... ],
direction: 'vertical'
}
// 서버 응답을 그대로 setData
mounted() {
CategoryService.list(this, {}, (view, ok, res) => {
if (ok) view.getRef('cats').setData(res.data.list);
});
}
// 초기값 프로그램적 세팅
mounted() {
this.getRef('cats').setValue(['A', 'C']); // A와 C만 체크
}
// 읽기 전용 (표시만)
{
tagName: 'checkboxGroup',
key: 'code',
display: 'name',
data: [ ... ],
readonly: true
}
11. 실전 예 — 관심 분야 선택
class Profile extends Va.View {
async mounted() {
// 서버에서 카테고리 목록 + 이 사용자의 기존 선택 로드
const categories = await CategoryService.list(this);
const my = await ProfileService.getInterests(this);
// 카테고리에 사용자 선택 상태 매핑
const data = categories.data.list.map(c => ({
code: c.code,
name: c.name,
checked: my.data.interests.includes(c.code)
}));
this.getRef('interests').setData(data);
}
onSave(btn, el, evt) {
const selected = this.getRef('interests').getValue();
if (selected.length === 0) {
new Va.Alert({ title: '알림', message: '최소 1개 선택하세요' }).show(this);
return;
}
ProfileService.save(this, { interests: selected }, this.onSaved);
}
onSaved(view, ok) {
if (ok) new Va.Alert({ title: '저장', message: '저장 완료' }).show(view);
}
config() {
return {
tagName: 'page',
tags: [{
tagName: 'panel',
tags: [
{ tagName: 'h2', innerHTML: '관심 분야' },
{
tagName: 'checkboxGroup',
ref: 'interests',
key: 'code',
display: 'name',
direction: 'horizontal'
},
{
tagName: 'button',
text: '저장',
appearance: 'primary',
onClick: 'onSave'
}
]
}]
};
}
}
흐름:
- 서버에서 전체 카테고리 + 사용자의 기존 선택 로드
- 매핑해서 setData() 한 번에 세팅
- 저장 시 getValue()로 선택된 코드 배열 획득 → 서버 전송
12. 알아두면 좋을 주의사항
- Va.Component 상속 — PureField 계열이 아님. focus(), getValue() API가 다른 필드와 약간 다름.
- getValue()는 배열 — 단일 값이 아님. 서버 전송 시 배열 처리 필요.
- setValue()도 배열 전달 — 단일 값도 자동으로 배열로 감싸지만, 처음부터 배열로 넘기는 게 명확.
- change 인자는 변경된 항목의 key — 전체 선택 배열이 아니라. 전체 상태는 getValue() 재호출.
- removeData / modifyData 없음 — 전체 교체는 setData().
- addData는 새 항목을 기본 체크된 상태로 추가 — 코드가 checked: true로 고정 (va_component.js:10927). 미체크로 추가하려면 데이터에서 조작 후 setData() 재호출.
- insertData(item, index)의 위치 로직 미묘 — 코드가 append 후 insertBefore를 함께 호출해 실제 동작 확인 권장.
- 데이터 필드명 name 조심 — HTML name 속성으로 자동 세팅되므로 사용자 이름 같은 실데이터를 이 필드명으로 두지 말 것.
- setData()는 자식 전체 제거 후 재렌더 — 기존 선택 상태 사라짐. 유지가 필요하면 getValue()로 백업 후 setData() → setValue() 재세팅.
- direction CSS 클래스로 반영 — 실제 flex 방향은 CSS(va.css 계열)에서 정의.
- 자식 checkbox의 valueType 지정 안 됨 — 그룹에서 관리하는 값은 항상 key 배열. 개별 체크박스의 valueType 옵션이 무시됨.
- 키보드 조작 — 각 체크박스는 스페이스로 토글 가능 (Checkbox의 표준 동작).
13. checkboxGroup vs checkboxGroupField vs combobox multi
상황추천
| 폼 안, 라벨·검증 필요 | checkboxGroupField |
| 폼 아닌 인라인, 간단 선택 | checkboxGroup |
| 옵션 5개 이하 | checkboxGroup |
| 옵션 20개 이상 | combobox + multiSelect: true (드롭다운) |
| 선택된 항목을 칩으로 표시 | Va.Tag |
| 한 화면에 세로로 나열 (설정 화면 등) | checkboxGroup + direction: 'vertical' |