React에서 라디오 버튼 사용하기
웹에서 라디오 버튼은 사용자로부터 여러 가지 옵션 중 하나를 선택받기 위해 사용되며, 양식(form)이나 설문(survey)과 같은 UI를 구현할 때 빠질 수 없는 핵심적인 요소입니다.
이번 포스팅에서는 React를 사용하여 여러 개의 라디오 버튼으로 이루어진 라디오 그룹 UI를 어떻게 구현할 수 있는지 알아보겠습니다.
HTML과 CSS만으로 라디오 버튼을 사용하는 방법은 관련 포스팅을 참고 바랍니다.
Radio 컴포넌트 작성
먼저 개별 라디오 버튼을 그리기 위한 React 컴포넌트를 작성해보겠습니다.
라디오 버튼은 HTML에서 <input>
요소의 type
속성을 radio
로 설정해주면 얻을 수 있습니다.
텍스트를 클릭햇을 때도 라디오 버튼이 선택될 수 있도록 <label>
요소로 <input>
요소와 children
prop 모두 감싸줍니다.
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으로 넘어온 여러 개의 라디오 버튼을 묶어주겠습니다.
function RadioGroup({ label, children }) {
return (
<fieldset>
<legend>{label}</legend>
{children}
</fieldset>
);
}
비제어 컴포넌트로 활용
지금까지 작성한 <Radio/>
와 <RadioGroup/>
컴포넌트는 비제어(uncontrolled) 컴포넌트로 바로 활용할 수 있습니다.
예를 들어, 사용자가 원하는 연락 방법을 선택할 수 있는 간단한 양식 UI를 만들어보겠습니다.
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 하나를 생성하겠습니다.
import { createContext } from "react";
const RadioContext = createContext({});
RadioGroup 컴포넌트 수정
이제 <RadioGroup/>
컴포넌트에서 label
과 children
를 제외한 모든 prop
을 RadioContext
를 통해서 설정해주겠습니다.
value
나 onChange
와 같은 prop
을 <Radio>
컴포넌트가 RadioContext
를 통해서 접근할 수 있도록 해주기 위함입니다.
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
속성을 설정해줍니다.
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으로 넘김니다.
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 애플리케이션을 구현하시는데 본 포스팅이 도움이 되었으면 좋겠습니다.