컴포넌트/필드 컴포넌트

RadioField (라디오 필드)

VanillaFront 2026. 9. 12. 19:45

Va.RadioField — 라벨 + 단일 라디오 버튼 필드

Va.Radio가 순수 라디오 버튼이라면, Va.RadioField는 그 위에 폼 라벨·필수 표시·검증 메시지를 얹은 완성 폼 필드입니다. Field 계열 아키텍처 그대로, 내부에 Va.Radio를 소유하는 Composition 구조. CheckboxField와 완전히 같은 패턴.

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


1. 기본 사용

{
    tagName: 'radioField',
    label: '결제 방식',
    radioLabel: '신용카드',
    checked: false,
    onChange: 'onPaymentChange'
}

두 종류의 라벨:

  • label — Field가 담당하는 폼 라벨 (상단 또는 좌측)
  • radioLabel — Radio가 담당하는 라디오 옆 텍스트

2. Radio / 다른 Field와의 차이

항목Va.RadioVa.RadioFieldVa.CheckboxField

폼 라벨(label)
라디오 옆 텍스트(radioLabel) (checkboxLabel)
검증 메시지
info 툴팁
필수 표시(별표)
선택 개념 배타 (단독은 로직 없음) 배타 (단독은 로직 없음) 다중 (단일)
valueType ✓ (반환값만) ✓ (반환값만)

한 줄 요약: "폼 안 라벨 붙은 단일 라디오. 하지만 대개 실무엔 RadioGroupField가 더 어울림."

⚠️ RadioField 단독 사용의 한계: Radio와 마찬가지로 이 컴포넌트도 배타 로직이 없습니다. 여러 개를 나열해도 서로 자동 해제되지 않아요. 실무에서 배타 선택이 필요하면 Va.RadioGroupField를 사용하세요.


3. 주요 속성

체크 상태 (Radio 계승)

속성기본값설명

checked false 상태값. true/'true'만 정확히 체크 인식
valueType undefined 'YN' / '10' / 미지정 (getChecked() 반환값에만 영향)
selected 별개 개념

두 라벨

속성설명

label 폼 라벨 (Field 상속)
radioLabel 라디오 옆 텍스트 (Radio 옵션)
radioLabelClick 라디오 옆 텍스트 클릭으로 선택 가능 (기본 true)

데이터 매핑

속성설명

key / display 그리드·리스트 활용 시 데이터 필드명
direction 배치 방향 (RadioGroup에서 실질적 활용)

라벨 관련 (Field 상속)

속성설명

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

검증

속성설명

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

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

{
    tagName: 'radioField',
    label: '선택',
    radio: {                       // ← 내부 Radio에 직접 전달
        radioLabelClick: false
    }
}

각 Field 계열 옵션 키:

  • InputField → input
  • CheckboxField → checkbox
  • RadioField → radio
  • RadioGroupField → radioGroup

4. 이벤트

Radio의 이벤트를 재발화 + Field 표준:

이벤트시그니처발생 시점

change (component, element, checked, evt) 체크 상태 변경 시. checked가 새 상태값 (항상 true)
click (component, element, evt) 클릭 시
keydown (component, element, keyCode, evt) 키다운. 검증 자동 리셋 (Tab 제외)
keyup (component, element, keyCode, evt) 키업
focus / blur (component, element, evt) 포커스 진입/이탈
select (component, element, evt) 선택 이벤트 (검증 자동 리셋)

⚠️ select 이벤트에 검증 자동 리셋 로직이 있음 — Radio 자체에는 select 이벤트가 정의되어 있지 않지만, RadioField는 select 리스너를 등록하고 있음 (va_component.js:10400). 실질적으론 keydown 리셋만 동작하고 select 리스너는 RadioGroup과 연동될 때를 위한 것으로 보임.

change 콜백 예시

onPaymentChange(comp, el, checked, evt) {
    console.log('선택됨:', checked);   // 항상 true (Radio는 클릭 시 true로만 바뀜)
    // 실질적 값은 어떤 옵션이 선택됐는지에 있으므로
    // 여러 개 나열한 경우 comp의 ref로 판단
}

Radio 특유: change의 checked는 사실상 항상 true. 어떤 것이 선택됐는지는 콜백을 받은 컴포넌트로 판단해야 함.


5. 메서드

상태 조회·변경 (Radio 위임 + 로컬 추가)

메서드설명

getChecked() 현재 값 반환. valueType 규약에 맞춰 반환
setChecked(value) 상태 세팅
check() true로 세팅 (편의)
uncheck() false로 세팅 (편의)

상태 (Field 상속)

메서드설명

setDisabled(bool) / getDisabled() 비활성화
setReadOnly(bool) / setReadonly(bool) 읽기 전용
setLabel(label) 폼 라벨 변경
setSize(size) 크기

검증

메서드설명

setValidation(state, message) 검증 표시 + aria
clearValidation() 검증 해제

포커스

메서드설명

focus() / blur() 내부 Radio에 위임

⚠️ getChecked()의 valueType: '10' 반환값 주의 — CheckboxField와 같이 문자열 '1'/'0' 반환 (va_component.js:10490, 10492). Radio 원본은 '1'/'0' (문자열). 두 클래스가 통일되어 있어 CheckboxField가 특이한 상황이었다면, 여기선 Radio와도 일치.


6. 내부 구조

<div elname="element" class="va-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-radio [checked]">      ← 내부 Va.Radio
        <div class="radio-inner">
          <div class="field-wrapper" tabindex="0"
               role="radio" aria-checked="false" aria-label="신용카드">
            <input type="radio" style="display:none">
            <span class="icon ico_radio_button_unchecked">◯</span>
          </div>
          <label class="label">신용카드</label>              ← radioLabel
        </div>
      </div>
    </div>
  </div>
  <div elname="validationDiv" style="display:none">
    <div class="va-validation">...</div>
  </div>
</div>

두 라벨:

  • 상단(또는 좌측)의 폼 라벨 label — Field 담당
  • 라디오 우측의 radioLabel — Radio 담당

CheckboxField와 완전히 같은 구조 패턴입니다.


7. 왜 RadioField 단독 사용은 드물까

Radio 자체가 단독으로는 배타 로직이 없어서, RadioField를 여러 개 나열해도 자동 배타가 안 됩니다. 예:

// ❌ 이건 작동 안 함 - 모두 체크될 수 있음
tags: [
    { tagName: 'radioField', label: '', radioLabel: '남성' },
    { tagName: 'radioField', label: '', radioLabel: '여성' }
]

사용자가 두 개를 다 클릭하면 둘 다 체크되어 시각적 혼란이 생깁니다.

진짜 실무에선:

// ✓ RadioGroupField 사용
{
    tagName: 'radioGroupField',
    label: '성별',
    key: 'code',
    display: 'name',
    data: [
        { code: 'M', name: '남성' },
        { code: 'F', name: '여성' }
    ]
}

8. 언제 쓰나

RadioField 단독이 맞을 때 (극히 드묾)

  • 커스텀 UI를 조립하며 라벨·검증만 붙이고 싶을 때
  • 배타 로직을 개발자가 명시적으로 관리하는 경우
  • 폼 안에서 "선택됨/미선택됨" 단일 상태를 시각적으로 라디오로 표현하고 싶을 때

RadioGroupField가 나을 때 (대부분)

  • 여러 옵션 중 하나만 선택 (성별, 결제 방식, 등급)
  • 배타 로직 자동
  • 데이터 배열로 관리

다른 걸 쓸 때

  • 여러 옵션 여러 개 선택 → CheckboxGroupField
  • 단일 on/off → CheckboxField
  • 스위치 UI → SwitchField
  • 툴바 배타 → ToggleButton + ButtonGroup

9. 흔한 조합 예시 (단독 사용)

// 폼 안 단일 라디오 (특이 사례)
{
    tagName: 'radioField',
    label: '적용 여부',
    radioLabel: '적용함',
    onChange: 'onApply'
}

// 좌측 라벨 (폼 정렬)
{
    tagName: 'radioField',
    label: '상태',
    labelPosition: 'left',
    labelWidth: 100,
    radioLabel: '활성',
    checked: true
}

// 폼 라벨 없이
{
    tagName: 'radioField',
    noLabel: true,
    radioLabel: '한 줄로 충분',
    checked: false
}

// info 툴팁
{
    tagName: 'radioField',
    label: '기본 옵션',
    radioLabel: '기본값 사용',
    infoButton: {
        tooltip: '체크 시 기본값으로 처리됩니다'
    }
}

// 두 개를 수동으로 배타 관리하는 패턴 (권장 안 함)
class ManualRadio extends Va.View {
    onSelectA(c, e, checked) { this._select('A'); }
    onSelectB(c, e, checked) { this._select('B'); }

    _select(key) {
        this.getRef('rA').setChecked(key === 'A');
        this.getRef('rB').setChecked(key === 'B');
        this.selected = key;
    }

    config() {
        return {
            tagName: 'div',
            tags: [
                { tagName: 'radioField', label: '', radioLabel: '옵션 A', ref: 'rA', onChange: 'onSelectA' },
                { tagName: 'radioField', label: '', radioLabel: '옵션 B', ref: 'rB', onChange: 'onSelectB' }
            ]
        };
    }
}
// ...위 코드는 RadioGroupField로 리팩터링 권장

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

  1. RadioField 단독으로 배타 안 됨 — 여러 개 나열해도 자동 해제 없음. RadioGroupField 사용 권장.
  2. 두 라벨 개념 혼동 주의  label은 폼 라벨, radioLabel은 라디오 옆 텍스트.
  3. change의 checked 인자는 항상 true — Radio는 클릭 시 무조건 true. 실질적 값은 어떤 컴포넌트가 콜백을 발생시켰는지로 판단.
  4. getChecked() valueType 반환  '10'은 문자열, 'YN'은 문자열, 기본은 boolean.
  5. 옵션 키 radio — 세부 커스터마이즈용.
  6. 검증 자동 리셋  keydown(Tab 제외)·select 시 이전 에러 자동 사라짐.
  7. noLabel: true로 폼 라벨만 숨기기 — radioLabel은 유지.
  8. required: true로 별표만 표시 — 미선택 시 검증은 개발자가 setValidation() 호출.
  9. radioLabelClick: false로 라벨 클릭 무시 — 실수 방지.
  10. focus()는 내부 Radio의 fieldWrapper에 포커스 — 실제 <input> 아님.
  11. 스페이스 키로 선택 — 표준 라디오 UX.
  12. 화살표 키 그룹 네비 없음 — 이것도 RadioGroup의 몫.
  13. 이벤트 리스너 등록 코드에 focus/blur 재발화 있음 — 다른 Field 형제와 일관.

11. radioField vs radioGroupField vs checkboxField 선택

상황추천

여러 옵션 중 하나만 radioGroupField ✓ (Radio 단독 아님)
단일 on/off 표현을 라디오로 radioField (극히 드묾)
여러 옵션 중 여러 개 checkboxGroupField
단일 동의 (약관 등) checkboxField
ON/OFF 스위치 UI switchField
툴바 배타 토글 toggleButton + buttonGroup
세그먼트 배타 segmentedControl

12. 실전 원칙

"라디오는 그룹으로 쓴다" — 이 원칙만 지키면 실수를 안 합니다.

  • 단일 상태 → checkboxField
  • 배타 선택 → radioGroupField
  • 세그먼트형 배타 → segmentedControl

RadioField 단독을 검토하고 있다면, 대부분 위 세 가지 중 하나로 대체할 수 있는지 먼저 확인하세요.