Logo

React에서 라디오 버튼 사용하기

웹에서 라디오 버튼은 사용자로부터 여러 가지 옵션 중 하나를 선택받기 위해 사용되며, 양식(form)이나 설문(survey)과 같은 UI를 구현할 때 빠질 수 없는 핵심적인 요소입니다.

이번 포스팅에서는 React를 사용하여 여러 개의 라디오 버튼으로 이루어진 라디오 그룹 UI를 어떻게 구현할 수 있는지 알아보겠습니다.

HTML과 CSS만으로 라디오 버튼을 사용하는 방법은 관련 포스팅을 참고 바랍니다.

Radio 컴포넌트 작성

먼저 개별 라디오 버튼을 그리기 위한 React 컴포넌트를 작성해보겠습니다.

라디오 버튼은 HTML에서 <input> 요소의 type 속성을 radio로 설정해주면 얻을 수 있습니다. 텍스트를 클릭햇을 때도 라디오 버튼이 선택될 수 있도록 <label> 요소로 <input> 요소와 children prop 모두 감싸줍니다.

src/Radio.jsx
function Radio({ children, value, name, defaultChecked, disabled }) {
  return (
    <label>
      <input
        type="radio"
        value={value}
        name={name}
        defaultChecked={defaultChecked}
        disabled={disabled}
      />
      {children}
    </label>
  );
}

RadioGroup 컴포넌트 작성

웹에서 라디오 버튼은 여러 개의 선택 사항 중에 사용자로 부터 하나를 선택받기 위해서 사용되죠? 따라서 라디오 버튼은 항상 2개 씩 그룹을 지어 사용되는 경우가 대부분이기 때문에 여러 개의 라디오 버튼을 담을 수 있는 React 컴포넌트를 만들겠습니다.

label prop으로 해당 라디오 그룹의 목적을 텍스트로 받아서 HTML의 <legend> 요소를 이용하여 표시해주겠습니다. 그리고 HTML의 <fieldset> 요소를 이용하여 children prop으로 넘어온 여러 개의 라디오 버튼을 묶어주겠습니다.

src/RadioGroup.jsx
function RadioGroup({ label, children }) {
  return (
    <fieldset>
      <legend>{label}</legend>
      {children}
    </fieldset>
  );
}

비제어 컴포넌트로 활용

지금까지 작성한 <Radio/><RadioGroup/> 컴포넌트는 비제어(uncontrolled) 컴포넌트로 바로 활용할 수 있습니다. 예를 들어, 사용자가 원하는 연락 방법을 선택할 수 있는 간단한 양식 UI를 만들어보겠습니다.

src/App.jsx
import RadioGroup from "./RadioGroup";
import Radio from "./Radio";

function App() {
  return (
    <form
      onSubmit={(e) => {
        e.preventDefault();
        alert(`${e.target.contact.value}를 통해 연락드리겠습니다!`);
      }}
    >
      <RadioGroup label="연락 방법">
        <Radio name="contact" value="EMAIL" defaultChecked>
          이메일
        </Radio>
        <Radio name="contact" value="PHONE">
          전화
        </Radio>
        <Radio name="contact" value="FAX">
          팩스
        </Radio>
        <Radio name="contact" value="MAIL" disabled>
          우편
        </Radio>
      </RadioGroup>
      <button type="submit">제출</button>
    </form>
  );
}

제출 버튼을 클릭해보면 라디오 버튼으로 선택된 연락 방법으로 연락을 드린다는 알람창이 뜰 것입니다.

React의 Uncontrolled Components 개발에 대해서는 관련 포스팅에서 자세히 다루고 있으니 참고 바랍니다.

RadioContext 추가

이번에는 <Radio/><RadioGroup/> 컴포넌트를 Controlled Components로 진화시켜보면 어떨까요? 다시 말해서, 좀 더 섬세한 상태 관리를 위해서 브라우저가 아닌 React에게 상태 관리를 맡기려고 합니다.

이를 위해서는 React Context를 통해 부모 컴포넌트인 <RadioGroup/>와 자식 컴포넌트인 <Radio/> 간에 일부 정보가 공유되야 하는데요. 대표적으로 현재 어떤 라디오 버튼이 선택되어 있는지, 그리고 사용자가 다른 라디오 버튼이 클릭하면 상태 변경을 위하여 무엇을 해야하는지가 공유되야 하겠습니다.

React Context에 대한 자세한 내용은 관련 포스팅을 참고 바랍니다.

우선 React의 createContext라는 함수를 이용해서 이러한 정보를 공유하기 위한 React Context 하나를 생성하겠습니다.

src/RadioContext.jsx
import { createContext } from "react";

const RadioContext = createContext({});

RadioGroup 컴포넌트 수정

이제 <RadioGroup/> 컴포넌트에서 labelchildren를 제외한 모든 propRadioContext를 통해서 설정해주겠습니다. valueonChange와 같은 prop<Radio> 컴포넌트가 RadioContext를 통해서 접근할 수 있도록 해주기 위함입니다.

src/RadioGroup.jsx
import RadioContext from "./RadioContext";
function RadioGroup({ label, children, ...rest }) {  return (
    <fieldset>
      <legend>{label}</legend>
      <RadioContext.Provider value={rest}>{children}</RadioContext.Provider>    </fieldset>
  );
}

Radio 컴포넌트 수정

이제 <Radio/> 컴포넌트 안에서 <RadioGroup/> 컴포넌트가 ReactContext에 저장해준 정보를 읽어와 활용할 수 있도록 코드를 수정하겠습니다.

ReactContext에 저장되어 있는 value 값과 해당 <Radio/> 컴포넌트에 prop으로 넘어온 value 값이 일치한다면 <input> 요소의 checked 속성을 true로 설정해줘야 합니다. ReactContext에 저장되어 있는 onChange 콜백 함수가 있다면, 그 함수가 <input> 요소의 value 값을 넘겨 호출될 수 있도록 있도록 <input> 요소의 onChange 속성을 설정해줍니다.

src/Radio.jsx
import { useContext } from "react";import RadioContext from "./RadioContext";
function Radio({ children, value, name, defaultChecked, disabled }) {
  const group = useContext(RadioContext);
  return (
    <label>
      <input
        type="radio"
        value={value}
        name={name}
        defaultChecked={defaultChecked}        disabled={disabled || group.disabled}        checked={group.value !== undefined ? value === group.value : undefined}        onChange={(e) => group.onChange && group.onChange(e.target.value)}
      />
      {children}
    </label>
  );
}

제어 컴포넌트로 활용

이렇게 React를 통해 상태 관리가 가능하도록 변경된 <RadioGroup/><Radio/> 컴포넌트를 사용해볼까요?

React의 useState() 후크(hook) 함수를 호출하여 얻은 상태 저장 변수와, 상태 변경 함수를 각각 <RadioGroup/> 컴포넌트의 value prop과 onChange prop으로 넘김니다.

src/App.jsx
import { useState } from "react";
import RadioGroup from "./RadioGroup";
import Radio from "./Radio";

function App() {
  const [value, setValue] = useState("EMAIL");
  return (
    <article>
      <header>
        <h3>제어(Controlled) 라디오 그룹</h3>
      </header>
      <RadioGroup label="연락 방법" value={value} onChange={setValue}>
        <Radio value="EMAIL">이메일</Radio>
        <Radio value="PHONE">전화</Radio>
        <Radio value="FAX">팩스</Radio>
        <Radio value="MAIL" disabled>
          우편
        </Radio>
      </RadioGroup>
      <footer>{value}을 통해 연락드리겠습니다!</footer>
    </article>
  );
}

다른 라디오 버튼을 클릭할 때 마다 제일 아래에 안내 문장이 실시간으로 갱신되는 것을 볼 수 있습니다.

전체 코드

본 포스팅에서 작성한 코드는 아래에서 직접 확인하고 실행해보실 수 있습니다.

마치면서

이상으로 React로 <Radio/><RadioGroup/> 컴포넌트를 작성하여, 비제어 컴포넌트로도 활용해보고 제어 컴포넌트로도 활용해보았습니다. 라디오 버튼이 필요한 React 애플리케이션을 구현하시는데 본 포스팅이 도움이 되었으면 좋겠습니다.