웹 애플리케이션에서 데이터 테이블을 다룰 때 사용자 경험을 향상시키는 중요한 기능 중 하나는 검색과 필터링입니다. 오늘은 서버에 추가 요청을 보내지 않고 클라이언트 측에서 테이블 데이터를 효율적으로 필터링하는 접근 방식에 대해 알아보겠습니다.
클라이언트 사이드 필터링의 개념
클라이언트 사이드 필터링은 페이지 로드 시 모든 데이터를 한 번에 가져온 후, JavaScript를 사용하여 DOM 요소의 가시성을 조절하는 기법입니다. 이 방식은 다음과 같은 상황에서 특히 유용합니다:
- 데이터 세트가 비교적 작을 때 (수백~수천 개 정도)
- 실시간 검색 결과가 필요할 때
- 서버 부하를 줄이고 싶을 때
- 오프라인 기능이 필요한 애플리케이션에서
구현 방법
1. 데이터 로드하기
먼저 JSON이나 다른 형식으로 데이터를 가져옵니다:
fetch('data.json')
.then(response => response.json())
.then(data => {
renderTable(data);
setupFilters();
});
2. 테이블 렌더링
가져온 데이터로 테이블을 생성합니다:
function renderTable(items) {
const tableBody = document.querySelector('table tbody');
tableBody.innerHTML = '';
items.forEach(item => {
const row = document.createElement('tr');
// 데이터 속성 추가 - 필터링에 사용됨
row.dataset.name = item.name.toLowerCase();
row.dataset.category = item.category;
row.innerHTML = `
<td>${item.name}</td>
<td>${item.category}</td>
<td>${item.price}</td>
`;
tableBody.appendChild(row);
});
}
3. 필터링 함수 구현
사용자 입력에 따라 테이블 행의 가시성을 조절하는 필터링 함수를 구현합니다:
function setupFilters() {
const searchInput = document.querySelector('#search');
const categoryFilter = document.querySelector('#category');
function filterTable() {
const searchTerm = searchInput.value.toLowerCase();
const selectedCategory = categoryFilter.value;
// 모든 행을 가져옴
const rows = document.querySelectorAll('table tbody tr');
rows.forEach(row => {
const name = row.dataset.name;
const category = row.dataset.category;
// 검색어와 카테고리 필터 모두 만족하는지 확인
const matchesSearch = searchTerm === '' || name.includes(searchTerm);
const matchesCategory = selectedCategory === 'all' || category === selectedCategory;
// 조건에 따라 행 표시/숨김
if (matchesSearch && matchesCategory) {
row.classList.remove('hidden');
} else {
row.classList.add('hidden');
}
});
}
// 이벤트 리스너 설정
searchInput.addEventListener('input', filterTable);
categoryFilter.addEventListener('change', filterTable);
}
4. CSS로 숨김 스타일 정의
CSS에서 .hidden
클래스를 정의하여 요소를 숨깁니다:
.hidden {
display: none;
}
장점
- 응답성: 서버 요청 없이 즉시 결과를 표시하므로 사용자 경험이 매우 빠릅니다.
- 서버 부하 감소: 검색이나 필터링할 때마다 서버에 요청을 보내지 않습니다.
- 네트워크 트래픽 절약: 초기 데이터 로드 후 추가 요청이 발생하지 않습니다.
- 오프라인 지원: 초기 데이터만 로드되면 네트워크 연결 없이도 작동합니다.
단점
- 초기 로딩 시간: 모든 데이터를 한 번에 로드하므로 초기 페이지 로드 시간이 길어질 수 있습니다.
- 메모리 사용량: 큰 데이터셋의 경우 클라이언트 메모리 사용량이 증가합니다.
- 확장성 제한: 데이터가 수만 개 이상으로 많아지면 성능 문제가 발생할 수 있습니다.
성능 최적화 팁
- 데이터 속성 활용: HTML5
data-*
속성을 활용하여 검색과 필터링에 필요한 데이터를 저장합니다. - 디바운싱 적용: 검색 입력에 디바운스 기법을 적용하여 입력이 완료된 후에만 필터링을 수행합니다.
- 가상 스크롤링: 대량의 데이터가 있는 경우 모든 행을 렌더링하지 않고 화면에 보이는 부분만 렌더링하는 가상 스크롤링 기법을 적용합니다.
// 디바운스 함수 예시
function debounce(func, wait) {
let timeout;
return function(...args) {
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(this, args), wait);
};
}
// 디바운스 적용
searchInput.addEventListener('input', debounce(filterTable, 300));
실제 사용 사례
이 패턴은 다음과 같은 상황에서 특히 유용합니다:
- 제품 카탈로그나 목록 페이지
- 관리자 대시보드의 데이터 테이블
- 라이브러리나 콘텐츠 컬렉션 브라우저
- 검색 결과를 빠르게 필터링해야 하는 경우
결론
클라이언트 사이드 테이블 필터링은 적절한 데이터 크기와 사용 사례에서 매우 효과적인 UI 패턴입니다. 서버와의 통신을 최소화하고 사용자에게 즉각적인 피드백을 제공하여 웹 애플리케이션의 사용자 경험을 크게 향상시킬 수 있습니다. 데이터 규모와 애플리케이션 요구사항에 맞게 서버 사이드 필터링과 클라이언트 사이드 필터링을 적절히 조합하여 최적의 결과를 얻는 것이 중요합니다.