리액트를 사용하며 체크박스 전체선택, 선택 해제 등을 구현했다. 약관동의 체크박스와 표에서의 체크박스 전체 선택. 생각보다 오래 걸렸던 기능이기도 한데 실제로 여러가지를 찾아보면 크게 어렵지 않은 것이었던 것 같다.
약관동의 체크박스
가장 처음으로 여러 블로그를 뒤적이면서 약관동의 체크박스 구현에 대해 알아보게 되었다. 약관동의 화면의 경우 약관동의 자세히 보기를 클릭했을 때 팝업으로 보여줄 것인지, 아니면 아래로 열리는 창을 만들어줄 것인지 등에 대해서 고민을 할 수밖에 없었다. 그 후 ui 구현이 끝난 후 약관 동의 체크박스에서 전체선택, 선택동의 등의 기능을 구현했다.
약관동의 박스 필수 조건은 아래와 같다.
1. 각각의 약관 체크박스를 클릭했을 때 체크 선택
2. 모두 동의합니다 체크시 전체 체크박스 선택
3. 각각의 체크박스 모두 선택 시, 모두 동의합니다 체크박스까지 선택
4. 오른쪽 약관보기 아이콘 클릭시 각각의 약관에 대한 스크립트 오픈
5. 모두 동의합니다의 아이콘 클릭시 전체 약관 상세정보 열리기
스타일은 스타일드 컴포넌트를 사용했고, 아이콘은 react-icons을 사용했다. 체크박스 선택에 더하여 아래로 열리는 모달까지 같이 구현했는데 체크박스 관련 코드는 해당 블로그를 참고했다.
// Checkbox.jsx
import React, { useState } from 'react';
import styled from 'styled-components';
import { MdOutlineKeyboardArrowDown } from "react-icons/md";
import { MdOutlineKeyboardArrowUp } from "react-icons/md";
const Checkbox = () => {
const [open1, setOpen1] = useState(false)
const [open2, setOpen2] = useState(false)
const [open3, setOpen3] = useState(false)
const data = [
{
id: 0,
title: '멤버십 이용약관 동의',
contents: '멤버십 필수 약관에 동의합니다. 멤버십 필수 약관에 동의합니다. 멤버십 필수 약관에 동의합니다. 멤버십 필수 약관에 동의합니다. 멤버십 필수 약관에 동의합니다.',
status: '(필수)',
},
{
id: 1,
title: '개인정보 수집 및 이용 동의',
contents: '반갑습니다',
status: '(필수)',
},
{
id: 2,
title: 'SMS 및 광고성 정보 수신 동의',
contents: '선택입니다',
status: '(선택)',
}
]
const [checkItems, setCheckItems] = useState([]);
// 체크박스 개별 선택하기
const selectChecked = (checked, id) => {
if (checked) {
setCheckItems(item => [...item, id]);
} else {
setCheckItems(checkItems.filter((el) => el !== id));
}
};
// 체크박스 전체 선택하기
const allChecked = (checked) => {
if (checked) {
const itemList = [];
data.forEach((el) => itemList.push(el.id));
setCheckItems(itemList);
} else {
setCheckItems([]);
}
}
return (
<Wrap>
{/* 전체선택 부분 */}
<div>
<label>
<input type='checkbox'
name='all-checked'
onChange={(e) => allChecked(e.target.checked)}
checked={checkItems.length === data.length ? true : false}
/>
모두 동의합니다.
</label>
{open1 && open2 && open3 ?
<MdOutlineKeyboardArrowUp
size={30}
color='gray'
onClick={() => {
setOpen1(!open1)
setOpen2(!open2)
setOpen3(!open3)
}} />
:
<MdOutlineKeyboardArrowDown
size={30}
color='gray'
onClick={() => {
setOpen1(!open1)
setOpen2(!open2)
setOpen3(!open3)
}} />
}
</div>
<hr />
{/* 개별 선택 부분 */}
<div>
<label>
<input
type='checkbox'
name='select-checked'
onChange={(e) => selectChecked(e.target.checked, data[0].id)}
checked={checkItems.includes(data[0].id) ? true : false}
/>
<span style={{ marginRight: '5px', color: data[0].status === '(필수)' ? 'red' : 'gray' }}>{data[0].status}</span> {data[0].title}
</label>
{open1 ?
<MdOutlineKeyboardArrowUp size={30} color='gray' onClick={() => {
setOpen1(!open1)
}} />
:
<MdOutlineKeyboardArrowDown size={30} color='gray' onClick={() => {
setOpen1(!open1)
}} />
}
</div>
{open1 &&
<ContentsWrap>
<p>{data[0].contents}</p>
</ContentsWrap>
}
{/* 데이터 2,3 생략 */}
</Wrap>
)
}
// 스타일 코드 생략
export default Checkbox
원래 처음에는 data 값을 map을 돌려 화면을 구현했었는데 이렇게 하니까 각각의 모달을 열기가 어려웠다. 그래서 데이터가 적으니 그냥 하드코딩. 각각의 모달을 열어주기 위해 각각의 div에 데이터를 넣어줬고, 모달을 열었을 때의 아이콘 변경까지 이렇게 마무리했다.