Logo

React에서 체크박스 사용하기

웹에서 체크박스는 사용자로부터 어떤 동의를 받거나 하나 이상의 옵션을 선택받기 위해 사용되는데요. 양식(form)이나 설문(survey)과 같은 UI를 구현할 때 빠질 수 없는 핵심적인 요소입니다.

이번 포스팅에서는 체크박스(checkbox)를 사용하여 사용자와 다양하게 상호작용을 할 수 있는 React 컴포넌트를 작성해보도록 하겠습니다.

Checkbox 컴포넌트 구현

웹에서 체크박스가 쓰이는 가장 흔한 사례는 사용자로 부터 어떤 동의를 받기 위함일텐데요. 이렇게 서로 관련이 없는 체크박스가 하나 이상 필요할 때 유용하게 쓸 수 있는 React 컴포넌트를 먼저 구현해보겠습니다.

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

src/Checkbox.jsx
function Checkbox({ children, disabled, checked, onChange }) {
  return (
    <label>
      <input
        type="checkbox"
        disabled={disabled}
        checked={checked}
        onChange={({ target: { checked } }) => onChange(checked)}
      />
      {children}
    </label>
  );
}

Checkbox 컴포넌트 활용

이제 구현한 <Checkbox/> 컴포넌트를 활용해서 간단한 회원 가입 UI를 구현해볼까요?

회웝 가입 버튼 위에 총 두 개의 사용할 건데요. 첫 번째는 서비스 이용약관 동의를 받기 위함하고, 두 번째는 마케팅 수신 동의를 받기 위함입니다.

ReactuseState() 훅(hook) 함수로 servicemarketing 상태를 만든 후에, 서비스 이용약관과 마케팅 수신을 나타내는 두 개의 <Checkbox/> 컴포넌트에 checkedonChange prop을 통해서 연결해주겠습니다.

src/App.jsx
import React from "react";
import Checkbox from "./Checkbox";

function App() {
  const [service, setService] = React.useState(false);
  const [marketing, setMarketing] = React.useState(false);
  return (
    <article>
      <header>
        <h3>체크박스</h3>
      </header>
      <Checkbox checked={service} onChange={setService}>
        (필수) 서비스 이용약관
      </Checkbox>
      <Checkbox checked={marketing} onChange={setMarketing}>
        (선택) 마케팅 수신
      </Checkbox>
      <footer>
        <button disabled={!service}>회원 가입</button>
      </footer>
    </article>
  );
}

위 React 앱을 실행해보면 서비스 이용약관을 동의해야지만 회원 가입 버튼이 활성화되는 것을 볼 수 있을 것입니다.

React의 useState() 훅 함수에 대한 자세한 내용은 관련 포스팅을 참고 바랍니다.

CheckboxContext 생성

웹에서 체크박스는 여태까지 다룬 것처럼 독립적으로 쓰이기도 하지만, 여러 개의 체크박스가 함께 선택사항을 구성하기 위해서도 자주 사용됩니다. 그래서 이번에는 사용자로부터 여러 개의 선택사항을 받을 때 유용하게 쓸 수 있는 React 컴포넌트를 한번 구현해보려고 합니다.

이를 위해서 우선 ReactcreateContext() 함수를 이용해서 CheckboxContext라는 React 컨텍스트를 하나를 생성해야합니다.

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

const CheckboxContext = createContext();

CheckboxContext는 여러 개의 체크박스를 묶어주는 부모 컴포넌트와 각 체크박스를 나타내는 자식 컴포넌트 간에 효과적인 정보 공유를 위해서 사용할 것입니다.

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

CheckboxGroup 컴포넌트 구현

다음으로 여러 개의 체크박스를 묶어서 관리해 줄 <CheckboxGroup/> 컴포넌트를 구현해도록 하겠습니다.

우선 label prop으로 해당 체크박스 그룹의 목적을 텍스트로 받아서 HTML의 <legend> 요소를 이용하여 표시해주겠습니다. 그리고 HTML의 <fieldset> 요소를 이용하여 children prop으로 넘어온 모든 체크박스들을 묶어주겠습니다. 선택된 값들을 values prop으로는 넘어오면, 선택된 값을 갱신하는 함수는 onChange prop으로는 넘어옵니다.

src/CheckboxGroup.jsx
import CheckboxContext from "./CheckboxContext";

function CheckboxGroup({
  label,
  children,
  disabled: groupDisabled,
  values,
  onChange
}) {
  const isDisabled = (disabled) => disabled || groupDisabled;

  const isChecked = (value) => values.includes(value);

  const toggleValue = ({ checked, value }) => {
    if (checked) {
      onChange(values.concat(value));
    } else {
      onChange(values.filter((v) => v !== value));
    }
  };

  return (
    <fieldset>
      <legend>{label}</legend>
      <CheckboxContext.Provider value={{ isDisabled, isChecked, toggleValue }}>
        {children}
      </CheckboxContext.Provider>
    </fieldset>
  );
}

<CheckboxGroup/> 컴포넌트에는 자식으로 넘어온 체크박스들을 통제하는데 필요한 3개의 함수를 구현하고 있는데요.

  • isDisabled() 함수는 disabled prop이 true로 넘어왔을 경우 내부에 있는 체크박스를 비활성시켜줍니다.
  • isChecked() 함수는 특정 체크박스의 값이 values prop에 포함되었는지를 확인해줍니다.
  • toggleValue() 함수는 체크박스가 체크 여부에 따라서 values prop에 해당 값을 추가하거나 제거해줍니다.

마지막으로 이 3개의 함수는 <CheckboxContext.Provider/> 컴포넌트를 통해서 React 컨텍스트로 설정해줘야합니다.

Checkbox 컴포넌트 수정

이제 맨 처음에 작성했던 <Checkbox/> 컴포넌트를 수정해줘야 하는데요. <CheckboxGroup/> 컴포넌트가 React 컨텍스트로 공유해준 함수들을 읽어와 적지적소에 호출을 해줘야합니다.

우선 ReactuseContext() 훅(hook) 함수를 사용하여 CheckboxContext를 읽어서 context 변수에 할당합니다. 여기서 해당 <Checkbox/> 컴포넌트가 <CheckboxGroup/> 컴포넌트 안에서 쓰이지 않았다면 contextundefined가 되겠죠? 이것을 이용하면 <Checkbox/> 컴포넌트를 단독으로 쓰기 위해서 작성했던 코드가 계속해서 문제없이 동작하도록 별도의 분기문으로 빼줄 수 있습니다.

반대로 context에 어떤 객체가 할당된 경우에는 해당 <Checkbox/> 컴포넌트가 <CheckboxGroup/> 컴포넌트 안에서 쓰였다는 의미가 됩니다. 따라서 CheckboxContext에 담겨진 isDisabled(), isChecked(), toggleValue() 함수를 각각 <input> 요소의 disabled, checked, onChange 속성에서 사용할 수 있겠습니다.

src/Checkbox.jsx
import React from "react";
import CheckboxContext from "./CheckboxContext";

function Checkbox({ children, disabled, value, checked, onChange }) {
  const context = React.useContext(CheckboxContext);
  if (!context) {    return (
      <label>
        <input
          type="checkbox"
          disabled={disabled}
          checked={checked}
          onChange={({ target: { checked } }) => onChange(checked)}
        />
        {children}
      </label>
    );
  }

  const { isDisabled, isChecked, toggleValue } = context;
  return (    <label>      <input        type="checkbox"        disabled={isDisabled(disabled)}        checked={isChecked(value)}        onChange={({ target: { checked } }) => toggleValue({ checked, value })}      />      {children}    </label>  );}

CheckboxGroup 컴포넌트 활용

<CheckboxGroup/><Checkbox/> 컴포넌트를 활용해서 사용자로 부터 좋아하는 색깔을 선택받는 UI를 구현해보겠습니다.

React의 useState() 훅(hook) 함수로 colors 상태를 만든 후에, 상태 저장 변수와 상태 변경 함수를 각각 <CheckboxGroup/> 컴포넌트의 value prop과 onChange prop으로 넘겨주면 됩니다.

한 가지 주목할 부분은 <Checkbox/> 컴포넌트를 단독으로 사용할 때는 checkedonChange prop을 넘겨줬었는데, 이렇게 여러 개의 <Checkbox/> 컴포넌트를 그룹으로 사용할 때는 value prop만을 사용하게 됩니다. 기본적인 체크박스의 상태 관리는 <CheckboxGroup/> 컴포넌트를 통해서 이뤄지고 있기 때문입니다.

src/App.jsx
import React from "react";
import CheckboxGroup from "./CheckboxGroup";
import Checkbox from "./Checkbox";

function App() {
  const [colors, setColors] = React.useState(["green"]);
  return (
    <article>
      <header>
        <h3>체크박스 그룹</h3>
      </header>
      <CheckboxGroup
        label="좋아하는 색깔은?"
        values={colors}
        onChange={setColors}
      >
        <Checkbox value="red">빨강</Checkbox>
        <Checkbox value="yellow">노랑</Checkbox>
        <Checkbox value="green">초록</Checkbox>
        <Checkbox value="blue">파랑</Checkbox>
        <Checkbox value="violet" disabled>
          보라
        </Checkbox>
      </CheckboxGroup>
      <footer>[{colors.join(",")}]을 좋아하시군요!</footer>
    </article>
  );
}

이제 체크박스를 체크하거나 체크를 해제해보면 제일 아래에 안내 문장이 실시간으로 갱신되는 것을 볼 수 있을 것입니다.

전체 코드

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

마치면서

이상으로 React로 <Checkbox/> 컴포넌트를 구현하여 단독으로 사용해보고, <CheckboxGroup/> 컴포넌트도 구현하여 여러 <Checkbox/> 컴포넌트를 묶어서도 사용해보았습니다. 체크박스가 필요한 React 애플리케이션을 구현하시는데 본 포스팅이 도움이 되었으면 좋겠습니다.