이번 포스팅에서는 전자정부프레임워크에서 제공한 샘플을 활용하여 웹 애플리케이션에서 자주 사용되는 체크박스 기능을 구현하는 방법에 대해 알아보겠습니다. 체크박스 기능구현을 위한 세부코드 작성과 의의를 알아보도록 하겠습니다.
https://github.com/eGovFramework
egovframe-template-simple-react
egovframe-template-simple-backend
밑의 이미지 자료는 샘플 홈페이지 화면입니다.

import React from 'react';
// CheckBox 컴포넌트 정의
export default function CheckBox({ id, label, checked, onChange }) {
return (
// 라벨과 체크박스를 함께 표시하는 레이블 엘리먼트
<label>
{/* 라벨 텍스트를 표시 */}
{label}
{/* 체크박스 엘리먼트 */}
<input
// 체크박스의 고유한 ID
id={id}
// 체크박스의 현재 체크 상태
type="checkbox"
checked={checked}
// 체크박스의 상태가 변경될 때 실행되는 콜백 함수
onChange={onChange}
/>
</label>
);
}
이 코드에서 정의한 CheckBox 컴포넌트는 라벨과 함께 체크박스를 렌더링하는 React 함수형 컴포넌트입니다. 재사용 가능하며, 위에서 보이는 바와 같이 id, label, checked, onChange 등의 속성을 받아와 라벨과 체크박스를 함께 표시합니다.
각각의 속성은 체크박스의 기능을 구현하는 데에 필요한 정보를 나타냅니다. 이 컴포넌트를 통해 간편하게 체크박스를 구현할 수 있습니다.
'EgovAdminTrainingList' 이름의 컴포넌트는 전자정부프레임워크를 샘플을 활용하여 유저생성 관리를 위한 관리자 페이지를 구현하기위해 만든 React 컴포넌트입니다.
이 페이지에서는 사용자 목록을 표시하고, 검색 조건에 따라 유저를 조회할 수 있습니다. 또한, 각 사용자의 정보를 표시하고 전체 선택 기능을 포함한 체크박스를 통해 다양한 관리 작업을 수행할 수 있습니다.
특히 이번 포스팅의 내용의 주제인 체크박스 기능을 중점적으로 알아보겠습니다.
import React, { useState, useEffect, useCallback, useMemo } from 'react';
import { Link, useLocation } from 'react-router-dom';
import URL from 'constants/url';
import { default as EgovLeftNav } from 'components/leftmenu/EgovLeftNavAdmin';
import EgovPaging from 'components/EgovPaging';
import CheckBox from 'components/CheckBox';
function EgovAdminTrainingList(props) {
// 샘플 데이터 시뮬레이션
const sampleData = useMemo(() => [
{ bbsId: 1, useId: "김칠일123", userNm: '김칠일', group: '사이버작전센터', class: '중위', serialNb: 1234567, userCodeNm: '훈련생', frstRegisterPnttm: '2022-01-01' },
{ bbsId: 2, useId: "이천만123", userNm: '이천만', group: '사이버작전센터', class: '소위', serialNb: 1345673, userCodeNm: '훈련생', frstRegisterPnttm: '2022-01-02' },
// 필요한 만큼 더 많은 샘플 데이터 아이템 추가
], []); // 의존성 배열이 빈 배열이므로 처음 한 번만 실행됨
const [paginationInfo, setPaginationInfo] = useState({});
const [listTag, setListTag] = useState([]);
const location = useLocation();
// console.log("EgovAdminBoardList [location] : ", location);
//체크박스
const [checkedList, setCheckedLists] = useState([]);
// 전체 체크 클릭 시 발생하는 함수
const onCheckedAll = (checked) => {
if (checked) {
setCheckedLists([...sampleData]); // 전체 선택 시, 모든 데이터를 체크 리스트에 추가
} else {
setCheckedLists([]); // 전체 해제 시, 체크 리스트 초기화
}
};
// 개별 체크 클릭 시 발생하는 함수
const onCheckedElement = (checked, item) => {
if (checked) {
setCheckedLists((prevList) => [...prevList, item]); // 아이템 체크 시, 해당 아이템을 체크 리스트에 추가
} else {
setCheckedLists((prevList) => prevList.filter((el) => el !== item)); // 아이템 해제 시, 해당 아이템을 체크 리스트에서 제거
}
};
// 검색 조건 상태 초기화
const [searchCondition, setSearchCondition] = useState(location.state?.searchCondition || { pageIndex: 1, searchCnd: '0', searchWrd: '' });// 기존 조회에서 접근 했을 시 || 신규로 접근 했을 시
// 리스트 조회 함수 정의
const retrieveList = useCallback(() => {
console.groupCollapsed("EgovAdminTrainingList.retrieveList()");
const mutListTag = [];
sampleData.forEach((item, index) => {
const listIdx = index + 1; // 인덱스가 0부터 시작한다고 가정
mutListTag.push(
<div key={listIdx} className="list_item" onClick={(e) => e.stopPropagation()}>
<div>
<CheckBox
id={`checkbox_${item.bbsId}`} // 이 부분에서 bbsId를 활용하여 고유한 id 생성
label=""
checked={checkedList.includes(item)}
onChange={(e) => onCheckedElement(e.target.checked, item)}
/>
</div>
<div>{item.useId}</div>
<div>{item.userNm}</div>
<div>{item.group}</div>
<div>{item.class}</div>
<div>{item.serialNb}</div>
<div>{item.userCodeNm}</div>
<div>{item.frstRegisterPnttm}</div>
</div>
);
});
setListTag(mutListTag);
console.groupEnd("EgovAdminTrainingList.retrieveList()");
}, [checkedList, setListTag, sampleData]);
// 컴포넌트가 마운트될 때 리스트 조회 실행
useEffect(() => {
retrieveList();
}, [retrieveList]);
console.log("------------------------------EgovAdminBoardList [End]");
console.groupEnd("EgovAdminBoardList");
// 렌더링 결과 반환
return (
<div className="container">
<div className="c_wrap">
{/* <!-- Location --> */}
<div className="location">
<ul>
<li><Link to={URL.MAIN} className="home">Home</Link></li>
<li><Link to={URL.ADMIN}>사이트관리</Link></li>
<li>게시판생성 관리</li>
</ul>
</div>
{/* <!--// Location --> */}
<div className="layout">
{/* <!-- Navigation --> */}
<EgovLeftNav></EgovLeftNav>
{/* <!--// Navigation --> */}
<div className="contents BOARD_CREATE_LIST" id="contents">
{/* <!-- 본문 --> */}
<div className="top_tit">
<h1 className="tit_1">계정 관리</h1>
</div>
<h2 className="tit_2">유저생성 관리</h2>
{/* <!-- 검색조건 --> */}
<div className="condition">
<ul>
<li className="third_1 L">
<span className="lb">검색유형선택</span>
<label className="f_select" htmlFor="searchCnd">
<select id="searchCnd" name="searchCnd" title="검색유형선력"
onChange={(e) => setSearchCondition({ ...searchCondition, searchCnd: e.target.value })}
>
<option value="0">관리자</option>
<option value="1">교관</option>
<option value="2">훈련생</option>
</select>
</label>
</li>
<li className="third_2 R">
<span className="lb">검색어</span>
<span className="f_search w_400">
<input type="text" name="" defaultValue={searchCondition && searchCondition.searchWrd} placeholder=""
onChange={(e) => setSearchCondition({ ...searchCondition, searchWrd: e.target.value })}
/>
<button type="button"
onClick={() => retrieveList(searchCondition)}>조회</button>
</span>
</li>
<li >
<Link to={URL.ADMIN_TRAINING_CREATE} className="btn btn_green_h46 pd35">다운로드</Link>
<Link to={URL.ADMIN_TRAINING_CREATE} className="btn btn_blue_h46 pd35">등록</Link>
<Link to={URL.ADMIN_TRAINING_CREATE} className="btn btn_blue_h46 pd35">추가</Link>
</li>
</ul>
</div>
{/* <!--// 검색조건 --> */}
{/* <!-- 게시판목록 --> */}
<div className="board_list BRD006">
<div className="head">
<span>
<CheckBox
label="전체선택"
checked={sampleData.length === checkedList.length}
onChange={(e) => onCheckedAll(e.target.checked)}
/>
</span>
<span>ID</span>
<span>이름</span>
<span>소속</span>
<span>계급</span>
<span>군번</span>
<span>계정 유형</span>
<span>등록일</span>
</div>
<div className="result">
{listTag}
</div>
</div>
{/* <!--// 게시판목록 --> */}
<div className="board_bot">
{/* <!-- Paging --> */}
<EgovPaging pagination={paginationInfo} moveToPage={passedPage => {
retrieveList({ ...searchCondition, pageIndex: passedPage })
}} />
{/* <!--/ Paging --> */}
</div>
{/* <!--// 본문 --> */}
</div>
</div>
</div>
</div>
);
}
export default EgovAdminTrainingList;
샘플 데이터 시뮬레이션: sampleData 배열을 사용하여 유저 정보의 샘플 데이터를 시뮬레이션하고, 이를 기반으로 사용자 목록을 표시합니다.
체크박스 관리: CheckBox 컴포넌트를 활용하여 각 사용자 항목의 체크박스를 표시하고, 전체 선택 및 개별 선택 기능을 구현합니다.
검색 조건 설정: 검색 유형과 검색어를 선택하고 입력하여 원하는 사용자를 조회할 수 있는 검색 조건을 제공합니다.
사용자 목록 표시: 각 사용자의 ID, 이름, 소속, 계급, 군번, 계정 유형, 등록일 등의 정보를 표시하고, 체크박스를 통해 선택된 사용자를 확인할 수 있습니다.
페이징 기능: 사용자 목록이 많을 경우를 대비하여 페이징 기능을 구현하여 페이지 이동이 가능하도록 합니다.
//체크박스
const [checkedList, setCheckedLists] = useState([]);
checkedList: 체크된 항목을 저장하는 상태입니다. 초기값은 빈 배열로 설정됩니다.
// 전체 체크 클릭 시 발생하는 함수
const onCheckedAll = (checked) => {
if (checked) {
setCheckedLists([...sampleData]); // 전체 선택 시, 모든 데이터를 체크 리스트에 추가
} else {
setCheckedLists([]); // 전체 해제 시, 체크 리스트 초기화
}
};
onCheckedAll: 전체 선택/해제 시 호출되는 함수입니다. checked 매개변수로 현재 체크 상태를 받아와서, 전체 선택 시에는 sampleData 배열 전체를 checkedList에 추가하고, 전체 해제 시에는 빈 배열로 초기화합니다.
// 개별 체크 클릭 시 발생하는 함수
const onCheckedElement = (checked, item) => {
if (checked) {
setCheckedLists((prevList) => [...prevList, item]); // 아이템 체크 시, 해당 아이템을 체크 리스트에 추가
} else {
setCheckedLists((prevList) => prevList.filter((el) => el !== item)); // 아이템 해제 시, 해당 아이템을 체크 리스트에서 제거
}
};
onCheckedElement: 개별 항목의 체크 상태가 변경될 때 호출되는 함수입니다. checked 매개변수로 현재 체크 상태를 받아와서, 체크 시에는 해당 아이템을 checkedList에 추가하고, 해제 시에는 해당 아이템을 checkedList에서 제거합니다.
// 리스트 조회 함수 정의
const retrieveList = useCallback(() => {
console.groupCollapsed("EgovAdminTrainingList.retrieveList()");
const mutListTag = [];
sampleData.forEach((item, index) => {
const listIdx = index + 1; // 인덱스가 0부터 시작한다고 가정
mutListTag.push(
<div key={listIdx} className="list_item" onClick={(e) => e.stopPropagation()}>
<div>
<CheckBox
id={`checkbox_${item.bbsId}`} // 이 부분에서 bbsId를 활용하여 고유한 id 생성
label=""
checked={checkedList.includes(item)}
onChange={(e) => onCheckedElement(e.target.checked, item)}
/>
</div>
<div>{item.useId}</div>
<div>{item.userNm}</div>
<div>{item.group}</div>
<div>{item.class}</div>
<div>{item.serialNb}</div>
<div>{item.userCodeNm}</div>
<div>{item.frstRegisterPnttm}</div>
</div>
);
});
setListTag(mutListTag);
console.groupEnd("EgovAdminTrainingList.retrieveList()");
}, [checkedList, setListTag, sampleData]);
retrieveList: 리스트를 조회하고 화면에 렌더링하는 함수입니다. sampleData 배열의 각 아이템에 대해 체크박스와 사용자 정보를 표시하며, 체크박스 상태는 checkedList를 참조합니다.
이를 통해 다음과 같은 완성한 화면을 볼 수 있습니다.

사용자 상호작용 향상: 체크박스를 통한 다중 선택 기능은 사용자 경험을 향상시킬 수 있었습니다. 사용자는 여러 항목을 한 번에 선택하거나 해제할 수 있습니다.
상태 관리 및 업데이트: 리액트의 상태 관리를 효과적으로 활용하여 체크박스의 상태를 관리하고 업데이트할 수 있습니다. 이는 유지보수성이 높은 코드를 작성할 수 있게 해줍니다.
모듈화: CheckBox 컴포넌트를 만들어 재사용 가능한 모듈화된 코드를 작성하면, 해당 기능을 다른 부분에서도 쉽게 재사용할 수 있습니다.
코드 가독성 향상: 컴포넌트를 사용하여 각 항목을 구조화하고, 체크박스 관련 로직을 해당 컴포넌트에 캡슐화함으로써 코드의 가독성이 향상됩니다.
상태 관리의 중요성: 체크박스를 통한 다중 선택은 상태 변화에 의존하므로, 상태 관리가 중요하기에 리액트의 useState를 통해 상태를 효과적으로 관리하면서, 변경에 따른 리렌더링을 적절히 처리해야 했습니다.
이벤트 핸들링의 유연성: 각 체크박스의 이벤트 핸들러에서 동적으로 상태를 업데이트하는 방법을 배웠습니다. 이를 통해 개별적인 체크박스 선택과 전체 선택 기능을 구현할 수 있었습니다.
컴포넌트의 재사용성: CheckBox 컴포넌트를 만들면서, 이를 다른 부분에서도 재사용할 수 있도록 설계하는 방법에 대해 고민하게 되었습니다.
사용자 입력에 대한 유연한 대응: 사용자 입력에 대한 예외처리 및 유연한 대응이 중요하다는 것을 깨달았습니다. 예를 들어, 샘플 데이터와 같이 여러 항목을 관리할 때, 사용자의 선택에 따라 어떻게 처리할 것인지 고민해야 했습니다.
이러한 의의와 깨달음은 리액트와 상태 관리, 이벤트 핸들링에 대한 이해를 높이고, 코드의 효율성 및 가독성을 향상시키는데 도움이 되었습니다.
이번 블로그에서는 전자정부프레임워크와 리액트를 결합하여 체크박스 기능을 구현하는 방법에 대해 살펴보았습니다.
위에서 언급한 바와 같이 이번 과정을 통해 배울 수 있었던 것은
상태 관리의 중요성 이해할 수 있었습니다
: 체크박스를 통한 다중 선택은 상태 관리의 핵심으로 리액트의 상태 관리 기능을 활용하여 사용자와의 상호작용할 수 있게 되었습니다.
컴포넌트 재사용성 증가: CheckBox 컴포넌트를 만들면서, 이를 다른 부분에서도 쉽게 재사용할 수 있는 코드를 작성하는 방법을 익혔습니다.
사용자 입장에서 생각해보기: 다중 선택이 필요한 상황에서 사용자가 편리하게 항목을 선택하고 해제할 수 있는 기능을 제공함으로써 사용자 의 입장에서 무엇이 필요할지 생각해보게 되었습니다.
더 복잡한 기능 추가: 이 예제를 확장하여 실제 프로젝트에 필요한 더 복잡한 기능을 추가해 볼 수 있을 것 같습니다. 예를 들어, 선택된 항목에 대한 추가 조작 또는 기능(예를 들어 전체선택 하여 관련 내용 다운로드 등의 기능)을 구현할 수 있을 것 같습니다.
Redux나 Context API 등의 활용: 리액트의 상태 관리에 대한 더 깊은 이해를 위해 Redux나 Context API와 같은 고급 상태 관리 기법을 적용할 수 있을 것 같습니다.