[작성자:] speedpointer

  • 클라이언트 사이드 테이블 필터링: 웹 개발의 효율적인 UI 패턴

    웹 애플리케이션에서 데이터 테이블을 다룰 때 사용자 경험을 향상시키는 중요한 기능 중 하나는 검색과 필터링입니다. 오늘은 서버에 추가 요청을 보내지 않고 클라이언트 측에서 테이블 데이터를 효율적으로 필터링하는 접근 방식에 대해 알아보겠습니다.

    클라이언트 사이드 필터링의 개념

    클라이언트 사이드 필터링은 페이지 로드 시 모든 데이터를 한 번에 가져온 후, 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;
    }
    

    장점

    1. 응답성: 서버 요청 없이 즉시 결과를 표시하므로 사용자 경험이 매우 빠릅니다.
    2. 서버 부하 감소: 검색이나 필터링할 때마다 서버에 요청을 보내지 않습니다.
    3. 네트워크 트래픽 절약: 초기 데이터 로드 후 추가 요청이 발생하지 않습니다.
    4. 오프라인 지원: 초기 데이터만 로드되면 네트워크 연결 없이도 작동합니다.

    단점

    1. 초기 로딩 시간: 모든 데이터를 한 번에 로드하므로 초기 페이지 로드 시간이 길어질 수 있습니다.
    2. 메모리 사용량: 큰 데이터셋의 경우 클라이언트 메모리 사용량이 증가합니다.
    3. 확장성 제한: 데이터가 수만 개 이상으로 많아지면 성능 문제가 발생할 수 있습니다.

    성능 최적화 팁

    1. 데이터 속성 활용: HTML5 data-* 속성을 활용하여 검색과 필터링에 필요한 데이터를 저장합니다.
    2. 디바운싱 적용: 검색 입력에 디바운스 기법을 적용하여 입력이 완료된 후에만 필터링을 수행합니다.
    3. 가상 스크롤링: 대량의 데이터가 있는 경우 모든 행을 렌더링하지 않고 화면에 보이는 부분만 렌더링하는 가상 스크롤링 기법을 적용합니다.
    // 디바운스 함수 예시
    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 패턴입니다. 서버와의 통신을 최소화하고 사용자에게 즉각적인 피드백을 제공하여 웹 애플리케이션의 사용자 경험을 크게 향상시킬 수 있습니다. 데이터 규모와 애플리케이션 요구사항에 맞게 서버 사이드 필터링과 클라이언트 사이드 필터링을 적절히 조합하여 최적의 결과를 얻는 것이 중요합니다.