React 로 Agree Checkbox 구현기

kind J·2023년 2월 11일
0
post-thumbnail
post-custom-banner

React 로 Agree Checkbox 구현기

회원가입 Form 에 Agree CheckBox

다양한 서비스의 회원가입 페이지를 보면 위의 이미지처럼 동의 체크 버튼 그룹이 있다. 맨 위의 CheckBox 는 보통 '전체 or 모두 동의하기' 버튼이고 아래 CheckBox 는 필수 동의 사항이거나 선택 동의 사항이다.

많이 접해서 친숙해보이지만 구현이 은근 간단하지 않다.

구현내용

1) '모두 동의하기' 체크시 하단의 checkbox 들이 전부 체크가 되어야함.
2) '모두 동의하기' 체크 해지시 하단의 checkbox 가 전부 체크 해제 되어야함.
3) 모두 체크된 상태에서 하단의 checkbox가 하나라도 해지가 되면 '모두 동의하기' 는 다시 체크 해제 되어야함.
4) 모두 체크되지 않은 상태에서 하단의 checkbox가 모두 체크가 될 경우 '모두 동의하기'가 체크가 되어야 함.

jquery 로 구현해본다면...?

이것을 jquery 로 구현했을 때에는 하단의 동의 버튼들의 갯수를 세고
체크된 체크박스 수를 처음에는 0으로 놓은 다음 체크 될 때마다 하나씩 카운트 하여 하단의 동의 버튼 총 갯수와 일치하면 '모두 동의하기' 버튼 체크, 아니면 체크 해제 이렇게 구현했다.

리액트로 구현하기

리액트에서는 이부분을 어떻게 구현하면 좋을까?
나는 checkbox와 label 은 묶어서 하나의 컴포넌트(CheckBox)로 만들어 놓았다.

[CheckBox 컴포넌트 모습]

이 컴포넌트는 코드를 따로 첨부하지는 않겠다.

3개의 체크박스가 있지만 크게 둘로 나눌 수 있다. '모두 동의하기 버튼' 과 '모두 동의하기'가 아닌 버튼으로. '필수', '선택' 도 이번 글에는 다루지 않고 하나로 묶어서 보겠다.

마크업을 아래와 같이 했다. '모두 동의하기' 버튼과 '모두 동의가 아닌 버튼'으로 분리했다.

<div className="agree">
  <CheckBox
    name="agreement"
    value="all"
    checked={allIsChecked}
    className="round"
    onCheck={onAllCheck}
    >
    모두 동의하기
  </CheckBox>
  {smallCheckBoxs.map((item) => (
    <CheckBox
      key={item.value}
      name={item.name}
      value={item.value}
      checked={item.checked}
      className={item.className}
      onCheck={onSingleCheck}
      >
      {item.children}
    </CheckBox>
  ))}
</div>

'모두 동의하기'가 아닌 버튼은 배열에 담아 반복문으로 컴포넌트를 뽑아냈다. 예시에는 하단의 동의 버튼이 2개 이지만 경우에 따라 늘어날 수 있기 때문에 배열에 담아 반복문으로 돌리는 것이 좋을것 같았다.

  const [smallCheckBoxs, setSmallCheckBoxs] = useState([
    {
      name: 'agreement',
      value: 'check1',
      children: '서비스 이용약관 및 개인정보 처리방침에 동의합니다. (필수)',
      checked: false,
      className: 'round small',
    },
    {
      name: 'agreement',
      value: 'check2',
      children: '마케팅 정보 수신에 동의합니다. (선택)',
      checked: false,
      className: 'round small',
    },
  ]);

디자인만 보면 '모두 동의하기' 버튼도 포함하여 전부 배열에 넣어서 map 으로 돌리고, '모두 동의하기' 만 style 을 다르게 주면 될것 같다. 그러나 '모두 동의하기' 와 아닌 버튼이 체크 됐을때 동작이 서로 다르기 때문에 아에 분리하는것이 낫다는 판단이 들었다.

동작 구현

'모두 동의하기' 를 체크시 onAllCheck 함수가 동작하고
아닌 버튼을 체크시 onSingleCheck 이다.

  // 체크박스 단일 선택
  const onSingleCheck = (e: ChangeEvent<HTMLInputElement>) => {
    const targetValue = e.currentTarget.value;
    setSmallCheckBoxs(
      smallCheckBoxs.map((item) =>
        targetValue === item.value
          ? { ...item, checked: !item.checked }
          : { ...item },
      ),
    );
  };

  //체크박스 전체 선택
  const onAllCheck = (e: ChangeEvent<HTMLInputElement>) => {
    setAllIsChecked((prev) => !prev);
    if (e.target.checked) {
      setSmallCheckBoxs(
        smallCheckBoxs.map((item) => ({ ...item, checked: true })),
      );
    } else {
      setSmallCheckBoxs(
        smallCheckBoxs.map((item) => ({ ...item, checked: false })),
      );
    }
  };

onSingleCheck 는 checkbox를 클릭/클릭해제 하면 해당 체크박스가 체크/체크해제 되는 코드이다.

onAllCheck는 '모두 동의하기' 를 클릭/클릭해제 하면 해당 Checkbox가 체크/체크해제 되고 하단의 체크 박스를 모두 체크/체크해제 하는 코드이다.

그렇다면 구현내용 중에 1,2번은 구현되었다. 3,4번은 어떻게 구현할수 있을까?

하단의 체크 박스가 모두 체크되면 '모두 동의하기' 버튼이 체크되고, 하나라도 체크해제 되면 '모두 동의하기' 버튼은 해제된다.

  useEffect(() => {
    setAllIsChecked(smallCheckBoxs.every((item) => item.checked));
  }, [smallCheckBoxs]);

useEffect hooks 을 활용하여 하단의 체크박스들의 체크 여부를 smallCheckBoxs 값이 변경 될 때마다 체크한다.

배열 메서드인 every를 활용해서 하단 동의 버튼들의 전체 체크 여부를 Boolean 값으로 받는다. 그 값을 setAllIsChecked 에 넣어주면 된다.

allIsChecked 이 true 일때 input[type="checkbox"] 이 check 되고
false 일 때 check가 해제 된다.


const [allIsChecked, setAllIsChecked] = useState(false);

...

<div className="agree">
  <CheckBox
    name="agreement"
    value="all"
    checked={allIsChecked}
    className="round"
    onCheck={onAllCheck}
    >
    모두 동의하기
  </CheckBox>
</div>

다음에는 react-hook-form 을 이용해서 FORM 을 구현해 보겠다.

profile
프론트앤드 개발자로 일하고 있는 kind J 입니다.
post-custom-banner

0개의 댓글