컴포넌트/필드 컴포넌트

CheckboxGroupField (체크박스그룹필드)

VanillaFront 2026. 9. 12. 19:36

Va.CheckboxGroupField — 라벨 + 다중 선택 체크박스 그룹

Va.CheckboxGroup이 순수 다중 선택 그룹이라면, Va.CheckboxGroupField는 그 위에 폼 라벨·필수 표시·검증 메시지를 얹은 완성 폼 필드입니다. Field 계열 아키텍처 그대로, 내부에 Va.CheckboxGroup을 소유하는 Composition 구조.

  • 클래스: Va.CheckboxGroupField  va_component.js:11011
  • short name: checkboxGroupField
  • 상속: Va.Field (다른 Field 형제들과 같음)
  • 내부 컴포넌트: Va.CheckboxGroup 인스턴스 (fieldComponent)
  • isContainer: true
  • 베이스 CSS: va-checkbox-group-field


1. 기본 사용

{
    tagName: 'checkboxGroupField',
    label: '관심 분야',
    key: 'code',
    display: 'name',
    data: [
        { code: 'JS', name: 'JavaScript', checked: true },
        { code: 'TS', name: 'TypeScript' },
        { code: 'PY', name: 'Python' }
    ],
    required: true,
    onChange: 'onInterestsChange'
}

라벨 + 검증 + 여러 체크박스가 한 번에 세팅. 값은 선택된 key들의 배열로 다룹니다.


2. Field 계열에서의 위치

Va.Field
   ├─ Va.InputField           ← Va.Input
   ├─ Va.CheckboxField        ← Va.Checkbox            (단일)
   ├─ Va.CheckboxGroupField   ← Va.CheckboxGroup       (다중)      ← 이 문서
   ├─ Va.RadioField           ← Va.Radio               (단일)
   ├─ Va.RadioGroupField      ← Va.RadioGroup          (배타)
   └─ ...

CheckboxGroupField의 정체: Field 베이스 + 내부에 Va.CheckboxGroup 인스턴스.


3. CheckboxGroup / 다른 Field와의 차이

항목Va.CheckboxGroupVa.CheckboxGroupFieldVa.CheckboxField

폼 라벨
검증 메시지
info 툴팁
필수 표시(별표)
선택 개수 N개 (다중) N개 (다중) 1개 (단일)
값 형태 key 배열 key 배열 boolean/YN/10
데이터 배열 렌더
상속 Component Field Field

한 줄 요약: "폼 안 라벨 붙은 다중 선택 체크박스 그룹."


4. 주요 속성

데이터 관련 (CheckboxGroup 계승)

속성설명

data 옵션 배열. 각 항목 { [key], [display], checked }
key 값 필드명 (예: 'code')
display 표시 필드명 (예: 'name')
direction 'horizontal'(기본) / 'vertical'
checkboxLabelClick 라벨 클릭 토글 (기본 true)
readonly / disabled 상태 (자식 전체에 전파)

라벨 관련 (Field 상속)

속성설명

label 폼 라벨
labelPosition top / bottom / left / right
labelWidth 라벨 폭
noLabel 폼 라벨 숨김
infoButton info 아이콘
required 필수 표시

검증

속성설명

validation {state, size, message}
validationState success / warning / error
validationMessage 메시지

세부 커스터마이즈 (checkboxGroup 옵션 키)

{
    tagName: 'checkboxGroupField',
    label: '카테고리',
    checkboxGroup: {                // ← 내부 CheckboxGroup에 직접 전달
        stopPropagation: false
    }
}

각 Field 계열 옵션 키:

  • InputField → input
  • CheckboxField → checkbox
  • CheckboxGroupField → checkboxGroup
  • RadioGroupField → radioGroup

5. 이벤트

이벤트시그니처발생 시점

change (component, element, key, evt) 자식 체크박스 상태 변경 시. key는 변경된 항목의 key (전체 배열이 아님)

⚠️ change 인자 주의 — CheckboxGroup과 동일하게, 세 번째 인자는 변경된 항목의 key 하나. 전체 선택 상태는 comp.getValue() 재호출로.

onInterestsChange(comp, el, key, evt) {
    console.log('바뀐 항목:', key);              // 'JS'
    console.log('전체 선택:', comp.getValue());  // ['JS', 'TS']
}

이벤트 목록이 매우 짧음 — 소스에 this.events = ['change']만 있습니다. focus/blur 등 표준 Field 이벤트가 재발화 코드에 포함되지 않아, 필요하면 component.fieldComponent에 직접 리스너를 붙여야 합니다.


6. 메서드

값 조회·세팅 (CheckboxGroup 위임)

메서드설명

getValue() 체크된 항목의 key 배열 반환
setValue(value) key 배열 넘기면 매칭되는 항목만 체크
getDisplay() 체크된 항목의 display 배열
getDisplayAsText() display 배열을 콤마로 이은 문자열

데이터 관리

메서드설명

setData(data) 데이터 전체 교체
getData() 현재 데이터 반환
addData(item) 항목 추가 (기본 체크됨)
insertData(item, index) 특정 위치에 삽입

상태

메서드설명

setDisabled(bool) 자식 전체 비활성화
setReadOnly(bool) 자식 전체 읽기 전용

자식 추가

메서드설명

append(component) 프로그램적으로 체크박스 자식 추가

주의: getChecked(), setChecked() 같은 단일 체크박스용 메서드는 없음. CheckboxField와 달리 다중 그룹이라 값은 배열 단위로만 다룹니다.


7. 내부 구조

<div elname="element" class="va-field va-checkbox-group-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-checkbox-group [horizontal|vertical]">
        ← 내부 Va.CheckboxGroup
        <div class="checkbox-group-inner">
          <div class="va-checkbox">…</div>
          <div class="va-checkbox">…</div>
          <!-- 자식 체크박스들 -->
        </div>
      </div>
    </div>
  </div>
  <div elname="validationDiv" style="display:none">
    <div class="va-validation">...</div>
  </div>
</div>

폼 라벨 방향체크박스 배치 방향 두 축이 각각 존재:

  • labelPosition — 폼 라벨의 위치 (Field 담당)
  • direction — 그룹 안 체크박스 배열 방향 (CheckboxGroup 담당)

두 옵션을 따로 조합할 수 있습니다.


8. update()의 특별한 동작 — 200ms 지연

update()에서 상태 반영이 _setTimer('update', ..., 200)으로 감싸져 있어 200ms 후에 실행됩니다 (va_component.js:11058-11064).

this._setTimer('update', ()=>{
    this.fieldComponent.setReadOnly(this.readonly);
    this.fieldComponent.setDisabled(this.disabled);
    ...
}, 200)

이유는 자식 체크박스가 렌더되기를 기다리는 것으로 보입니다. 실무적 함의:

  • setReadOnly() / setDisabled() 호출 후 즉시 UI에 반영되지 않을 수 있음 (200ms 후 반영)
  • 연속 호출 시 마지막 것만 반영됨 (_setTimer 특성)

프로그램적으로 상태 바꾸고 바로 다른 로직 진행할 때 이 지연을 인지하고 있어야 합니다.


9. 언제 쓰나

CheckboxGroupField가 맞을 때

  • 폼 안 다중 선택 필드 (관심 분야, 선호 카테고리, 필터 조건)
  • 라벨·검증·필수 표시가 필요할 때
  • 서버에서 받은 동적 옵션 목록
  • 항목 5개 이상 유동적

다른 걸 쓸 때

  • 라벨 없이 인라인 → Va.CheckboxGroup
  • 단일 체크박스 (동의) → Va.CheckboxField
  • 하나만 선택 (배타) → Va.RadioGroupField
  • 옵션 20개 이상 (드롭다운) → Va.ComboboxField + multiSelect
  • 칩(태그) 형태로 표시 → Va.TagField

10. 흔한 조합 예시

// 표준
{
    tagName: 'checkboxGroupField',
    label: '관심 분야',
    key: 'code',
    display: 'name',
    data: [
        { code: 'JS', name: 'JavaScript' },
        { code: 'TS', name: 'TypeScript' },
        { code: 'PY', name: 'Python' }
    ],
    required: true,
    onChange: 'onChange'
}

// 세로 배치
{
    tagName: 'checkboxGroupField',
    label: '알림 설정',
    key: 'code',
    display: 'name',
    direction: 'vertical',
    data: [
        { code: 'EMAIL',  name: '이메일',    checked: true },
        { code: 'SMS',    name: 'SMS' },
        { code: 'PUSH',   name: '푸시 알림', checked: true }
    ]
}

// 좌측 라벨 (폼 정렬)
{
    tagName: 'checkboxGroupField',
    label: '요일',
    labelPosition: 'left',
    labelWidth: 100,
    key: 'code',
    display: 'name',
    data: [
        { code: 'MON', name: '월' },
        { code: 'TUE', name: '화' },
        { code: 'WED', name: '수' },
        { code: 'THU', name: '목' },
        { code: 'FRI', name: '금' }
    ]
}

// info 툴팁
{
    tagName: 'checkboxGroupField',
    label: '수신 채널',
    infoButton: {
        tooltip: '2개 이상 선택 권장'
    },
    key: 'code',
    display: 'name',
    data: [ ... ]
}

// 서버에서 데이터 로드
mounted() {
    CategoryService.list(this, {}, (view, ok, res) => {
        if (ok) view.getRef('cats').setData(res.data.list);
    });
}

// 초기값 프로그램적 세팅
this.getRef('cats').setValue(['JS', 'PY']);

11. 실전 예 — 알림 설정 폼

class NotificationSettings extends Va.View {
    async mounted() {
        // 사용자의 기존 설정 로드
        const res = await NotifService.getUserSettings(this);
        if (res.result) {
            // 전체 채널 옵션 + 이 사용자의 선택
            const all = [
                { code: 'EMAIL',   name: '이메일' },
                { code: 'SMS',     name: 'SMS' },
                { code: 'PUSH',    name: '푸시 알림' },
                { code: 'KAKAO',   name: '카카오톡' }
            ];
            const selected = res.data.channels;  // ['EMAIL', 'PUSH']

            // 체크 상태 매핑
            const data = all.map(c => ({
                ...c,
                checked: selected.includes(c.code)
            }));

            this.getRef('channels').setData(data);
        }
    }

    onSave(btn, el, evt) {
        const channels = this.getRef('channels').getValue();

        if (channels.length === 0) {
            this.getRef('channels').setValidation('error', '최소 1개 채널을 선택하세요');
            return;
        }
        this.getRef('channels').clearValidation();

        NotifService.save(this, { channels }, (view, ok) => {
            if (ok) new Va.Alert({ title: '완료', message: '저장되었습니다' }).show(view);
        });
    }

    config() {
        return {
            tagName: 'page',
            tags: [{
                tagName: 'panel',
                tags: [
                    { tagName: 'h2', innerHTML: '알림 채널 설정' },
                    {
                        tagName: 'checkboxGroupField',
                        ref: 'channels',
                        label: '수신 채널',
                        key: 'code',
                        display: 'name',
                        direction: 'vertical',
                        required: true,
                        infoButton: {
                            tooltip: '알림을 받을 채널을 모두 선택하세요'
                        }
                    },
                    {
                        tagName: 'button',
                        text: '저장',
                        appearance: 'primary',
                        onClick: 'onSave'
                    }
                ]
            }]
        };
    }
}

12. 알아두면 좋을 주의사항

  1. getValue() 반환은 key 배열 — 서버 전송 시 배열로 처리.
  2. setValue()도 배열 전달 — 단일 값도 배열로 감쌈.
  3. change 인자는 변경된 항목의 key — 전체 상태 아님. 전체는 getValue().
  4. 이벤트 목록이 ['change']만 있음 — focus/blur 등 다른 Field 표준 이벤트는 재발화 안 됨. 필요하면 fieldComponent에 직접.
  5. update()가 200ms 지연  setReadOnly/setDisabled 즉시 반영 안 됨.
  6. addData는 새 항목이 자동으로 체크됨 — 미체크로 추가하려면 데이터 조작 후 setData().
  7. removeData/modifyData 없음 — 전체 교체는 setData().
  8. 검증 자동 리셋 없음 — CheckboxField는 keydown/select 시 자동 리셋, 그룹은 그런 로직 없음. change 후 명시적으로 clearValidation() 호출 필요할 수 있음.
  9. key/display 필드명 유연 — 데이터 형태에 맞춰 자유롭게 (code/name, id/label 등).
  10. direction은 CSS 클래스로 반영 — 실제 flex 방향은 프레임워크 CSS에서.
  11. 폼 라벨과 그룹 방향 독립  labelPosition: 'left' + direction: 'vertical' 조합 가능.
  12. focus() 편의 메서드 없음 — CheckboxGroup에도 없고 Field에도 위임 명시 없음. 프로그램적 포커스가 필요하면 component.fieldComponent.getChildComponents()[0].focus() 우회.

13. checkboxGroupField vs radioGroupField vs comboboxField(multi) 선택

상황추천

여러 옵션 중 여러 개 선택 checkboxGroupField
여러 옵션 중 하나만 radioGroupField
옵션 5개 이하, 가로 배치 checkboxGroupField direction: 'horizontal'
옵션 5개 이상, 세로 배치 checkboxGroupField direction: 'vertical'
옵션 20개 이상 comboboxField + multiSelect: true (드롭다운)
선택된 것을 칩으로 표시 tagField
트리 구조 그룹 커스텀 (기본 지원 안 됨)