VanillaFront 2026. 9. 13. 23:48

Va.Switch — ON/OFF 토글 스위치

체크박스와 기능은 같지만 시각적으로 슬라이드형 스위치 UI를 제공하는 컴포넌트. iOS·Android의 설정 화면에서 흔히 보는 그 스위치입니다. 알림 켜기/끄기, 활성/비활성 같은 명확한 ON/OFF 상태에 어울려요.

  • 클래스: Va.Switch  va_component.js:9716
  • short name: switch
  • 상속: Va.PureField (Checkbox·Radio와 형제)
  • isContainer: false
  • 베이스 CSS: va-switch


1. 기본 사용

{
    tagName: 'switch',
    checked: true,
    onChange: 'onNotifChange'
}
  • 슬라이드형 스위치 UI (thumb이 좌↔우 이동)
  • checked: true — thumb 오른쪽, 채워진 색
  • checked: false — thumb 왼쪽, 회색

2. Switch vs Checkbox의 차이

기능적으로는 거의 동일합니다. 시각적·의미적으로만 다름:

항목Va.SwitchVa.Checkbox

UI 형태 슬라이드 스위치 사각 체크박스
의미 ON/OFF 상태 (즉시 반영) 선택/미선택 (제출 시 반영)
3상 상태 (mixed)
checkboxLabel/radioLabel ✕ (자체 라벨 없음)
HTML type checkbox (내부적으로) checkbox
role ARIA switch checkbox
valueType
isContainer false true

UX 관행:

  • Switch 설정 화면·즉시 반영 상황에서 (알림 켜기/끄기 등)
  • Checkbox 폼 안에서 저장 버튼으로 반영 상황에서 (약관 동의 등)

한 줄 요약: "체크박스와 기능은 같지만 슬라이드형 UI로 ON/OFF를 표현."


3. 주요 속성

체크 상태

속성기본값설명

checked false 상태값. true/'true'/'Y'/'1' 등 다양한 형태 인식
valueType undefined 'YN' / '10' / 미지정 (표준 boolean) — getChecked() 반환에만 영향

PureField 상속

readonly, disabled, size, appearance, stopPropagation 등 표준.

주목: Checkbox와 달리 checkboxLabel·radioLabel 같은 자체 라벨 옵션이 없음. 라벨은 부모에서 별도로 붙이거나 Va.SwitchField를 써야 합니다.


4. valueType — DB 스키마 대응

Checkbox와 동일 로직. 서버 저장 형식에 맞춰 선택:

valueType켜짐꺼짐언제

(기본) true false 표준 JS boolean
'YN' 'Y' 'N' DB가 Y/N 컬럼
'10' 1 0 DB가 int 0/1 컬럼
{
    tagName: 'switch',
    valueType: 'YN',
    checked: 'Y'
}
// getChecked() → 'Y' / 'N'

5. 이벤트

이벤트시그니처발생 시점

change (component, element, checked, evt) 스위치 토글 시. checked가 새 상태값
click (component, element, evt) 클릭 시
focus / blur (component, element, evt) 포커스 진입/이탈
keydown (component, element, keyCode, evt) 스페이스로 토글 시
contextmenu (component, element, evt) 우클릭

change 콜백 예시

onNotifChange(comp, el, checked, evt) {
    console.log('알림 상태:', checked);   // true / false
    NotifService.setEnabled(this, checked);   // 즉시 서버 반영
}

Switch UX 관행: change 즉시 서버·백엔드에 반영. Checkbox처럼 "저장 버튼"을 기다리지 않음.


6. 메서드

상태 조회·변경

메서드설명

getChecked() 현재 값 반환. valueType 규약 반영
setChecked(value) 상태 세팅. 다양한 형태 수용
check() ON으로 세팅 (편의)
uncheck() OFF로 세팅 (편의)

상태 (PureField 상속)

메서드설명

setDisabled(bool) / setReadOnly(bool) 상태
focus() / blur() 포커스

7. 내부 구조

<div elname="element" class="va-switch [checked] [focused] [disabled]"
     va-role="va-switch">
  <div elname="inner" class="switch-inner">
    <div elname="fieldWrapper" class="field-wrapper">
      <input elname="field" type="checkbox" role="switch"
             class="switch" tabindex="0" aria-checked="true|false">
      <div elname="switchWrapper" class="switch-div">
        <div elname="thumb" class="thumb"></div>       ← 슬라이드하는 원
      </div>
    </div>
  </div>
</div>

핵심 트릭:

  • <input type="checkbox">는 위에 겹쳐 있어 실제 클릭을 받음 — CSS로 숨기지 않고 투명하게 처리 (다른 컴포넌트와 다른 방식)
  • switchWrapper가 실제 스위치 배경 — CSS로 rail 스타일
  • thumb이 슬라이드하는 원  .checked 클래스에 따라 CSS transition으로 이동
  • ARIA 자동  role="switch", aria-checked 자동 세팅 → 스크린리더가 스위치로 인식

8. 접근성 (a11y)

Switch는 접근성 표준을 잘 따릅니다.

요소값

role "switch" (자동) — 체크박스가 아닌 스위치로 인식
aria-checked "true" / "false" (자동)
tabindex 0 (Tab으로 접근 가능)
스페이스 키 토글 (자동 처리)

스크린리더 사용자가 정확히 "스위치, 켜짐"으로 듣습니다.


9. 언제 쓰나

Switch가 맞을 때

  • 설정 화면의 ON/OFF (알림, 다크모드, 자동저장 등)
  • 즉시 반영되는 상태 (토글하자마자 서버 저장)
  • 하나의 옵션 활성/비활성 (필드 잠금 등)
  • 모바일 앱 스타일 UI

다른 걸 쓸 때

  • 폼 안 동의 (약관 등, 저장 시 반영) → Va.Checkbox / Va.CheckboxField
  • 여러 옵션 다중 선택 → Va.CheckboxGroup
  • 배타 선택 → Va.RadioGroup
  • 라벨 붙은 폼 필드 → Va.SwitchField
  • 툴바 배타 토글 → Va.ToggleButton

10. Switch 계열

컴포넌트역할

Va.Switch 단일 스위치 (이 문서)
Va.SwitchField Switch + Field 래퍼 (라벨/검증)

Checkbox 계열과 달리 Group 컴포넌트는 없음 — Switch는 본래 다중 선택 개념이 어색해서.


11. 흔한 조합 예시

// 표준
{
    tagName: 'switch',
    checked: true,
    onChange: 'onToggle'
}

// Y/N (DB 스키마)
{
    tagName: 'switch',
    valueType: 'YN',
    checked: 'Y'
}

// 0/1 (int 컬럼)
{
    tagName: 'switch',
    valueType: '10',
    checked: 1
}

// 라벨과 함께 (수동 배치)
{
    tagName: 'div',
    layout: 'ds-flex fd-row ai-center gap-s',
    tags: [
        { tagName: 'label', innerHTML: '푸시 알림' },
        { tagName: 'switch', ref: 'push', checked: true, onChange: 'onPushToggle' }
    ]
}

// 읽기 전용 (상태 표시)
{
    tagName: 'switch',
    checked: true,
    readonly: true
}

12. 실전 예 — 설정 화면

class Settings extends Va.View {
    async mounted() {
        const res = await SettingsService.get(this);
        if (res.result) {
            this.getRef('push').setChecked(res.data.pushEnabled);
            this.getRef('email').setChecked(res.data.emailEnabled);
            this.getRef('darkMode').setChecked(res.data.darkMode);
        }
    }

    // 즉시 반영 (저장 버튼 없음)
    onPushToggle(comp, el, checked, evt) {
        SettingsService.setPush(this, checked, (view, ok) => {
            if (!ok) comp.setChecked(!checked);   // 실패 시 롤백
        });
    }

    onEmailToggle(comp, el, checked, evt) {
        SettingsService.setEmail(this, checked);
    }

    onDarkModeToggle(comp, el, checked, evt) {
        SettingsService.setDarkMode(this, checked);
        // 실시간 테마 변경
        Va.setTheme(checked ? 'dark' : 'light');
    }

    config() {
        return {
            tagName: 'page',
            tags: [{
                tagName: 'panel',
                tags: [
                    { tagName: 'h2', innerHTML: '알림 설정' },
                    {
                        tagName: 'div',
                        layout: 'ds-flex fd-row ai-center jc-space-between',
                        style: { padding: '10px 0' },
                        tags: [
                            { tagName: 'div', innerHTML: '푸시 알림' },
                            { tagName: 'switch', ref: 'push', onChange: 'onPushToggle' }
                        ]
                    },
                    {
                        tagName: 'div',
                        layout: 'ds-flex fd-row ai-center jc-space-between',
                        style: { padding: '10px 0' },
                        tags: [
                            { tagName: 'div', innerHTML: '이메일 알림' },
                            { tagName: 'switch', ref: 'email', onChange: 'onEmailToggle' }
                        ]
                    },
                    { tagName: 'h2', innerHTML: '테마' },
                    {
                        tagName: 'div',
                        layout: 'ds-flex fd-row ai-center jc-space-between',
                        style: { padding: '10px 0' },
                        tags: [
                            { tagName: 'div', innerHTML: '다크 모드' },
                            { tagName: 'switch', ref: 'darkMode', onChange: 'onDarkModeToggle' }
                        ]
                    }
                ]
            }]
        };
    }
}

Switch UX의 핵심:

  • 사용자가 토글하는 즉시 서버 반영
  • 실패 시 자동 롤백 (setChecked(!checked))
  • "저장" 버튼 없음 — 이것이 Checkbox와 결정적 차이

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

  1. checked 인식 형태 다양  true/'true'/'Y'/'y'/'1'/1 모두 켜짐으로 인식.
  2. getChecked()는 valueType 규약 반영 — 필드 세팅과 조회 형태 통일.
  3. 자체 라벨 없음 — Checkbox의 checkboxLabel, Radio의 radioLabel 같은 옵션 부재. 라벨은 부모에서 배치하거나 SwitchField 사용.
  4. change 이벤트 즉시 반영이 UX 관행 — 저장 버튼 대기 안 함. 실패 시 롤백 로직 필요.
  5. role="switch" 자동 — 접근성상 체크박스가 아니라 스위치로 인식됨. 스크린리더 대응 좋음.
  6. isContainer: false — 자식 태그 못 담음. Checkbox(true)와 다름.
  7. <input>이 실제로 보이는 요소 — 클릭 리시버. CSS로 투명하게 처리해서 UI엔 안 보이지만 클릭 이벤트는 여기로.
  8. 스페이스 키로 토글 — 자동 처리.
  9. 3상 상태(mixed) 없음 — Checkbox에는 있지만 Switch엔 없음. ON/OFF만.
  10. focus()는 <input>에 포커스 — 자체 focus wrapper 대신.
  11. preventParentFieldEvent: true 강제 — 생성자에서 옵션에 강제. 부모 Field 이벤트 개입 방지.

14. switch vs checkbox vs toggleButton 선택

상황추천

설정 화면 ON/OFF (즉시 반영) switch
폼 안 동의 (저장 시 반영) checkbox / checkboxField
툴바 상태 토글 (bold 등) toggleButton
여러 옵션 다중 선택 checkboxGroup
배타 선택 radioGroup
라벨·검증 필요 switchField / checkboxField
3상 상태 checkbox (mixed)

"즉시 반영이 UX의 핵심이면 switch, 폼 제출 시 반영이면 checkbox" — 명확한 원칙.


참고