여러분이 사용하고 계신 브라우저는 HTML5를 지원하지 않기 때문에 몇몇 요소가 제대로 보이도록 JScript를 사용하고 있습니다. 하지만 여러분의 브라우저 설정에서 스크립트 기능이 꺼져있으므로, 현재 페이지를 제대로 확인하시려면 스크립트 기능을 켜주셔야 합니다. 데이터 필터링 시스템의 모든 것 | HTML, CSS, JS 구현 가이드

데이터 필터링 시스템의 모든 것 | HTML, CSS, JS 구현 가이드

1주전 작성

데이터 필터링

데이터 필터링은 웹 페이지에서 사용자가 원하는 정보만 선택적으로 볼 수 있게 해주는 강력한 기능이다.
예를들어, 대량의 국가 정보를 다루는 웹 페이지에서 지역별 필터링 기능이 없다면, 사용자는 필요한 정보를 찾는 데 많은 시간을 낭비하게 된다.

예시를 먼저 확인해보자.

예시

 

이제 예시도 봤으니, 지역 필터를 클릭하여 해당 지역 국가 정보를 보이거나 숨기는 HTML 필터링 시스템을 구현하는 방법을 알아보자.

목차

HTML

데이터 필터링을 구현하기 위한 첫 단계는 적절한 마크업 구조를 설계하는 것이다.

국가 정보를 지역별로 필터링하기 위해서는 필터 버튼 영역과 국가 정보 영역으로 구성된 HTML 구조가 필요하다.

아래는 지역별 국가 정보 필터링을 위한 기본 HTML 구조 예시다.

<div class="container">
<div class="filter-container">
<div class="filter-buttons">
<button class="filter-btn active" data-region="all">모든 지역</button>
<button class="filter-btn active" data-region="JAPAC">아시아 태평양</button>
<button class="filter-btn active" data-region="AMR">아메리카</button>
<button class="filter-btn active" data-region="EMEA">유럽/중동/아프리카</button>
</div>
</div>
<div class="countries-container">
<div class="country-card" data-region="JAPAC">
<h4>한국</h4>
<p>인구: 5,170만 명</p>
<p>영토 크기: 100,210 km²</p>
<p>주요 언어: 한국어</p>
</div>
<div class="country-card" data-region="JAPAC">
<h4>일본</h4>
<p>인구: 1억 2,580만 명</p>
<p>영토 크기: 377,975 km²</p>
<p>주요 언어: 일본어</p>
</div>
<div class="country-card" data-region="AMR">
<h4>미국</h4>
<p>인구: 3억 3,100만 명</p>
<p>영토 크기: 9,833,517 km²</p>
<p>주요 언어: 영어</p>
</div>
<div class="country-card" data-region="EMEA">
<h4>영국</h4>
<p>인구: 6,800만 명</p>
<p>영토 크기: 242,495 km²</p>
<p>주요 언어: 영어</p>
</div>
</div>
</div>
사용된 코드 설명
button class=”filter-btn active” data-region=”all”
:
필터 버튼 요소로, ‘active’ 클래스는 필터가 활성화된 상태를 나타내며, data-region 속성은 어떤 지역을 필터링할지 지정한다.
div class=”country-card” data-region=”JAPAC”
:
국가 정보 카드 요소로, data-region 속성에 해당 국가가 속한 지역을 명시한다.
class=”active”
:
필터 버튼이 활성화 상태임을 나타내는 클래스로, 초기에는 모든 버튼이 활성화되어 모든 국가가 표시된다.
data-region 속성
:
필터링의 핵심 요소로, 버튼과 카드 사이의 관계를 연결하는 데이터 속성이다.

위 HTML 구조에서 가장 중요한 요소는 데이터 속성(data-region)이다.

모든 국가 카드에는 해당 국가가 속한 지역을 data-region 속성으로 표시했다.
예를 들어, 한국과 일본은 ‘JAPAC’으로, 미국은 ‘AMR’으로, 영국은 ‘EMEA’로 표시된다.

또한 각 필터 버튼에도 어떤 지역을 필터링할지 data-region 속성으로 표시했다.
이 속성값을 기준으로 JavaScript에서 필터링 로직을 구현할 수 있다.

모든 필터 버튼에 초기에는 ‘active’ 클래스를 추가하여, 페이지가 로드될 때 모든 지역의 국가가 표시되도록 설정했다.
필터 기능을 활성화하면 JavaScript에서 이 클래스를 토글하여 필터 상태를 변경한다.

CSS

데이터 필터링의 시각적 효과와 사용자 경험을 향상시키기 위해 CSS 스타일을 적용해야 한다.

필터 버튼의 상태 변화와 국가 카드의 표시/숨김 효과를 스타일로 구현해보자.

body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; background-color: #f8f9fa; margin: 0; padding: 20px; }
.container { max-width: 1200px; margin: 0 auto; }
h1 { text-align: center; margin-bottom: 30px; }
.filter-container { background-color: #fff; padding: 20px; border-radius: 5px; box-shadow: 0 2px 5px rgba(0,0,0,0.1); margin-bottom: 20px; }
.filter-section { margin-bottom: 15px; }
.filter-section h4 { margin-top: 0; margin-bottom: 10px; }
.filter-buttons { display: flex; flex-wrap: wrap; gap: 10px; }
.filter-btn { padding: 8px 15px; border: none; border-radius: 4px; background-color: #e9ecef; cursor: pointer; transition: all 0.3s ease; }
.filter-btn.active { background-color: #4361ee; color: white; }
.filter-btn:hover { opacity: 0.9; }
.countries-container { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 20px; }
.country-card { background-color: #fff; border-radius: 5px; padding: 20px; box-shadow: 0 2px 5px rgba(0,0,0,0.1); transition: all 0.3s ease; }
.country-card h4 { margin-top: 0; border-bottom: 1px solid #eee; padding-bottom: 10px; }
.country-card.hidden { display: none; }
.reset-filters-btn { display: block; margin: 15px auto 0; padding: 8px 15px; border: none; border-radius: 4px; background-color: #dc3545; color: white; cursor: pointer; transition: all 0.3s ease; }
.reset-filters-btn:hover { opacity: 0.9; }
사용된 코드 설명
.filter-btn.active
:
활성화된 필터 버튼 스타일로, 배경색이 파란색(#4361ee)이고 텍스트가 흰색으로 변경된다.
.countries-container
:
그리드 레이아웃을 사용하여 국가 카드를 정렬한다. repeat(auto-fill, minmax(300px, 1fr))는 화면 크기에 따라 자동으로 카드 배치를 조정하는 반응형 그리드를 생성한다.
.country-card.hidden
:
필터링에 의해 숨겨진 국가 카드에 적용되는 스타일로, display: none을 설정하여 화면에서 완전히 제거한다.
.reset-filters-btn
:
필터 초기화 버튼 스타일로, 빨간색 배경에 흰색 텍스트를 사용해 시각적으로 두드러지게 만든다.

이 CSS 스타일의 핵심은 필터 상태에 따른 시각적 피드백과 숨김 효과다.

.filter-btn.active 클래스는 활성화된 필터 버튼을 시각적으로 구분하기 위한 스타일로, 배경색을 파란색으로 변경하고 텍스트를 흰색으로 표시한다.
사용자는 이를 통해 현재 어떤 필터가 활성화되어 있는지 직관적으로 인식할 수 있다.

.country-card.hidden 클래스는 필터링에 의해 숨겨진 국가 카드를 처리하는 스타일로, display: none을 사용하여 화면에서 완전히 제거한다.
JavaScript에서 필터 상태에 따라 이 클래스를 추가하거나 제거하여 카드의 표시 여부를 제어한다.

반응형 그리드 레이아웃(.countries-container)을 사용하여 다양한 화면 크기에 대응하며, 카드와 버튼에 transition 효과를 적용하여 상태 변화 시 부드러운 애니메이션을 제공한다.
이러한 디테일은 사용자 경험(UX)을 향상시키는 중요한 요소다.

JavaScript

데이터 필터링의 핵심 기능은 JavaScript를 통해 구현된다.

필터 버튼 클릭 시 해당 지역의 국가 정보를 보이거나 숨기는 동작을 JavaScript로 구현해보자.

document.addEventListener('DOMContentLoaded', function() {
const filterButtons = document.querySelectorAll('.filter-btn');
const countryCards = document.querySelectorAll('.country-card');
filterButtons.forEach(button => {
button.addEventListener('click', function() {
this.classList.toggle('active');
const activeFilters = Array.from(filterButtons)
.filter(btn => btn.classList.contains('active'))
.map(btn => btn.getAttribute('data-region'));
if (this.getAttribute('data-region') === 'all') {
if (this.classList.contains('active')) {
filterButtons.forEach(btn => btn.classList.add('active'));
countryCards.forEach(card => card.classList.remove('hidden'));
} else {
filterButtons.forEach(btn => btn.classList.remove('active'));
countryCards.forEach(card => card.classList.add('hidden'));
}
return;
}
countryCards.forEach(card => {
const cardRegion = card.getAttribute('data-region');
if (activeFilters.includes(cardRegion) || activeFilters.includes('all')) {
card.classList.remove('hidden');
} else {
card.classList.add('hidden');
}
});
});
});
});
사용된 코드 설명
document.addEventListener(‘DOMContentLoaded’, function() { … })
:
DOM이 완전히 로드된 후 코드가 실행되도록 보장한다.
this.classList.toggle(‘active’)
:
클릭된 필터 버튼의 활성화 상태를 전환한다(켜짐/꺼짐).
const activeFilters = Array.from(filterButtons)…
:
현재 활성화된 모든 필터 버튼의 지역 값을 배열로 수집한다.
if (this.getAttribute(‘data-region’) === ‘all’) { … }
:
‘모든 지역’ 버튼을 클릭했을 때의 특별 처리 로직이다.
if (activeFilters.includes(cardRegion) || activeFilters.includes(‘all’))
:
카드의 지역이 활성화된 필터에 포함되는지 확인하여 표시 여부를 결정한다.

이 JavaScript 코드의 핵심 동작 원리는 다음과 같다.

  1. 필터 버튼 클릭 시 해당 버튼의 활성화 상태(active 클래스)를 토글한다.
  2. 현재 활성화된 모든 필터 버튼의 지역 값(data-region)을 배열로 수집한다.
  3. ‘모든 지역’ 버튼은 특별 케이스로 처리하여 모든 필터를 한 번에 켜거나 끌 수 있게 한다.
  4. 각 국가 카드에 대해 해당 카드의 지역(data-region)이 활성화된 필터에 포함되는지 확인한다.
  5. 포함되면 hidden 클래스를 제거하여 카드를 표시하고, 그렇지 않으면 hidden 클래스를 추가하여 카드를 숨긴다.

이 방식을 사용하면 여러 필터를 동시에 활성화하거나 비활성화할 수 있어 다양한 조합의 필터링이 가능하다.
예를 들어, JAPAC과 AMR 필터는 활성화하고 EMEA 필터는 비활성화하여 아시아와 아메리카 지역의 국가만 표시할 수 있다.

특히 ‘모든 지역’ 필터 버튼을 통해 사용자가 빠르게 모든 국가를 표시하거나 숨길 수 있어 편리하다.
이는 필터링 UI에서 중요한 사용자 경험(UX) 요소다.

데이터 속성

데이터 필터링에서 데이터 속성(data-*)은 매우 효과적인 도구다.

기본 예제에서는 단일 지역(data-region) 속성만 사용했지만, 더 복잡한 필터링을 위해 데이터 속성을 다양하게 활용할 수 있다.

아래는 여러 데이터 속성을 활용한 고급 필터링 예시다.

<div class="country-card" data-region="JAPAC" data-population="medium" data-language="asian">
<h4>한국</h4>
<p>인구: 5,170만 명</p>
<p>영토 크기: 100,210 km²</p>
<p>주요 언어: 한국어</p>
</div>

위 코드에서는 국가 카드에 지역(region) 외에도 인구 규모(population), 언어 그룹(language) 등 여러 데이터 속성을 추가했다.

이처럼 다양한 데이터 속성을 활용하면 더 정교한 필터링 시스템을 구현할 수 있다.
JavaScript에서는 이러한 속성들을 활용하여 복합 필터링을 구현할 수 있다.

function applyFilters() {
const activeRegions = getActiveFilters('region');
const activePopulations = getActiveFilters('population');
const activeLanguages = getActiveFilters('language');
countryCards.forEach(card => {
const cardRegion = card.getAttribute('data-region');
const cardPopulation = card.getAttribute('data-population');
const cardLanguage = card.getAttribute('data-language');
const regionMatch = activeRegions.length === 0 || activeRegions.includes(cardRegion);
const populationMatch = activePopulations.length === 0 || activePopulations.includes(cardPopulation);
const languageMatch = activeLanguages.length === 0 || activeLanguages.includes(cardLanguage);
if (regionMatch && populationMatch && languageMatch) {
card.classList.remove('hidden');
} else {
card.classList.add('hidden');
}
});
}
function getActiveFilters(filterType) {
return Array.from(document.querySelectorAll(`.filter-btn[data-type="${filterType}"].active`))
.map(btn => btn.getAttribute(`data-${filterType}`));
}
사용된 코드 설명
function applyFilters()
:
모든 필터 타입에 대해 활성화된 필터를 적용하는 함수다.
getActiveFilters(filterType)
:
특정 타입(region, population 등)의 활성화된 필터 값들을 배열로 반환하는 도우미 함수다.
const regionMatch = …
:
카드의 지역이 활성화된 지역 필터에 포함되는지 확인한다.
if (regionMatch && populationMatch && …)
:
모든 필터 조건을 AND 연산으로 결합하여 모든 조건을 만족하는 카드만 표시한다.

이 코드에서는 여러 종류의 필터(지역, 인구, 언어)를 동시에 적용하고 있다.

각 필터 타입마다 활성화된 값들을 수집하고, 모든 조건을 AND 연산으로 결합하여 모든 조건을 만족하는 카드만 표시한다.
이를 통해 “아시아 지역의 중규모 인구를 가진 국가”와 같은 복합적인 필터링이 가능해진다.

데이터 속성의 주요 장점은 HTML 요소에 직접 메타데이터를 저장할 수 있다는 점이다.
이는 DOM에 데이터를 명시적으로 연결하여 JavaScript에서 쉽게 접근하고 조작할 수 있게 해준다.

또한 데이터 속성은 CSS 선택자로도 사용할 수 있어, 특정 데이터 속성값을 가진 요소에 스타일을 적용할 수도 있다.
예를 들어, [data-region=”JAPAC”] 선택자를 사용하여 JAPAC 지역의 모든 카드에 특별한 스타일을 적용할 수 있다.

다중 필터

데이터 필터링에서 더 강력한 사용자 경험을 제공하기 위해 다중 필터 시스템을 구현해보자.

지금까지는 지역별 필터만 다루었지만, 이제는 인구 규모와 언어 그룹 등 여러 필터 유형을 조합하는 방법을 살펴보자.

먼저 HTML 구조에 여러 필터 유형을 추가한다.

<div class="filter-container">
<div class="filter-section">
<h4>지역별</h4>
<div class="filter-buttons">
<button class="filter-btn active" data-type="region" data-region="all">모든 지역</button>
<button class="filter-btn active" data-type="region" data-region="JAPAC">아시아 태평양</button>
<button class="filter-btn active" data-type="region" data-region="AMR">아메리카</button>
<button class="filter-btn active" data-type="region" data-region="EMEA">유럽/중동/아프리카</button>
</div>
</div>
<div class="filter-section">
<h4>인구 규모별</h4>
<div class="filter-buttons">
<button class="filter-btn active" data-type="population" data-population="all">모든 인구 규모</button>
<button class="filter-btn active" data-type="population" data-population="small">소규모 (5천만 미만)</button>
<button class="filter-btn active" data-type="population" data-population="medium">중규모 (5천만-2억)</button>
<button class="filter-btn active" data-type="population" data-population="large">대규모 (2억 이상)</button>
</div>
</div>
<div class="filter-section">
<h4>언어 그룹별</h4>
<div class="filter-buttons">
<button class="filter-btn active" data-type="language" data-language="all">모든 언어</button>
<button class="filter-btn active" data-type="language" data-language="indo-european">인도-유럽어족</button>
<button class="filter-btn active" data-type="language" data-language="asian">아시아어족</button>
<button class="filter-btn active" data-type="language" data-language="other">기타</button>
</div>
</div>
</div>

이렇게 각 필터 유형별로 버튼 그룹을 만들고, data-type 속성으로 필터 유형을 구분했다.

또한 각 버튼에는 해당 필터 유형에 맞는 데이터 속성(data-region, data-population, data-language)을 추가하여 필터 값을 지정했다.

다중 필터를 처리하기 위한 JavaScript 코드는 다음과 같다.

document.addEventListener('DOMContentLoaded', function() {
const filterButtons = document.querySelectorAll('.filter-btn');
const countryCards = document.querySelectorAll('.country-card');
let filterState = {
region: ['all', 'JAPAC', 'AMR', 'EMEA'],
population: ['all', 'small', 'medium', 'large'],
language: ['all', 'indo-european', 'asian', 'other']
};
filterButtons.forEach(button => {
button.addEventListener('click', function() {
const filterType = this.getAttribute('data-type');
let filterValue;
if (filterType === 'region') {
filterValue = this.getAttribute('data-region');
} else if (filterType === 'population') {
filterValue = this.getAttribute('data-population');
} else if (filterType === 'language') {
filterValue = this.getAttribute('data-language');
}
if (filterValue === 'all') {
if (this.classList.contains('active')) {
document.querySelectorAll(`.filter-btn[data-type="${filterType}"]`)
.forEach(btn => btn.classList.add('active'));
filterState[filterType] = [];
document.querySelectorAll(`.filter-btn[data-type="${filterType}"]`).forEach(btn => {
if (filterType === 'region') {
filterState[filterType].push(btn.getAttribute('data-region'));
} else if (filterType === 'population') {
filterState[filterType].push(btn.getAttribute('data-population'));
} else if (filterType === 'language') {
filterState[filterType].push(btn.getAttribute('data-language'));
}
});
} else {
document.querySelectorAll(`.filter-btn[data-type="${filterType}"]`)
.forEach(btn => btn.classList.remove('active'));
filterState[filterType] = [];
}
} else {
this.classList.toggle('active');
const allButton = document.querySelector(`.filter-btn[data-type="${filterType}"][data-${filterType}="all"]`);
const typeButtons = document.querySelectorAll(`.filter-btn[data-type="${filterType}"]:not([data-${filterType}="all"])`);
const allActive = Array.from(typeButtons).every(btn => btn.classList.contains('active'));
if (allActive) {
allButton.classList.add('active');
} else {
allButton.classList.remove('active');
}
filterState[filterType] = [];
document.querySelectorAll(`.filter-btn[data-type="${filterType}"].active`).forEach(btn => {
if (filterType === 'region') {
filterState[filterType].push(btn.getAttribute('data-region'));
} else if (filterType === 'population') {
filterState[filterType].push(btn.getAttribute('data-population'));
} else if (filterType === 'language') {
filterState[filterType].push(btn.getAttribute('data-language'));
}
});
}
applyFilters();
});
});
function applyFilters() {
countryCards.forEach(card => {
const cardRegion = card.getAttribute('data-region');
const cardPopulation = card.getAttribute('data-population');
const cardLanguage = card.getAttribute('data-language');
const matchesRegion = filterState.region.includes('all') || filterState.region.includes(cardRegion);
const matchesPopulation = filterState.population.includes('all') || filterState.population.includes(cardPopulation);
const matchesLanguage = filterState.language.includes('all') || filterState.language.includes(cardLanguage);
if (matchesRegion && matchesPopulation && matchesLanguage) {
card.classList.remove('hidden');
} else {
card.classList.add('hidden');
}
});
}
});
사용된 코드 설명
const filterState = { … }
:
각 필터 유형별로 현재 활성화된 값들을 추적하는 객체다.
const filterType = this.getAttribute(‘data-type’)
:
클릭된 필터 버튼의 유형(region, population, language 등)을 가져온다.
if (filterType === ‘region’) { filterValue = this.getAttribute(‘data-region’); }
:
필터 유형에 따라 적절한 데이터 속성에서 값을 가져온다.
if (filterValue === ‘all’) { … }
:
‘모든 xxx’ 옵션 버튼에 대한 특별 처리 로직이다.
const allActive = Array.from(typeButtons).every(…)
:
특정 유형의 모든 일반 필터가 활성화되었는지 확인한다.

이 다중 필터 구현의 핵심은 filterState 객체로, 각 필터 유형별로 현재 활성화된 값들을 추적한다.

사용자가 필터 버튼을 클릭할 때마다 해당 필터 유형의 상태를 업데이트하고, 모든 필터 유형의 조건을 AND 연산으로 결합하여 국가 카드를 필터링한다.

특히 중요한 점은 각 필터 버튼에서 필터 타입과 필터 값을 가져오는 방식이다.
버튼의 data-type 속성으로 필터 유형을 파악한 후, 해당 유형에 맞는 데이터 속성(data-region, data-population, data-language)에서 값을 가져온다.

또한 ‘모든 xxx’ 버튼은 특별하게 처리하여 해당 유형의 모든 필터를 한 번에 켜거나 끌 수 있게 했다.
특정 유형의 모든 일반 필터가 활성화되면 자동으로 ‘모든 xxx’ 버튼도 활성화되는 논리를 구현했다.

이런 방식으로 복합적인 필터링 시스템을 구현하면 사용자는 매우 구체적인 조건으로 데이터를 필터링할 수 있다.
예를 들어, “아시아 지역의 중규모 인구를 가진 아시아어족 국가”와 같은 세부적인 필터링이 가능하다.

필터 상태 관리

데이터 필터링 시스템의 사용자 경험을 더욱 개선하기 위해 필터 상태를 저장하고 복원하는 기능을 추가해보자.

사용자가 페이지를 새로고침하거나 나중에 다시 방문했을 때 이전에 설정한 필터 상태를 유지하면 더 나은 경험을 제공할 수 있다.

localStorage를 활용하여 필터 상태를 저장하고 복원하는 코드는 다음과 같다.

document.addEventListener('DOMContentLoaded', function() {
const filterButtons = document.querySelectorAll('.filter-btn');
const countryCards = document.querySelectorAll('.country-card');
let filterState = {
region: ['all', 'JAPAC', 'AMR', 'EMEA'],
population: ['all', 'small', 'medium', 'large'],
language: ['all', 'indo-european', 'asian', 'other']
};
restoreFilterState();
filterButtons.forEach(button => {
button.addEventListener('click', function() {
const filterType = this.getAttribute('data-type');
let filterValue;
if (filterType === 'region') {
filterValue = this.getAttribute('data-region');
} else if (filterType === 'population') {
filterValue = this.getAttribute('data-population');
} else if (filterType === 'language') {
filterValue = this.getAttribute('data-language');
}
if (filterValue === 'all') {
if (this.classList.contains('active')) {
document.querySelectorAll(`.filter-btn[data-type="${filterType}"]`)
.forEach(btn => btn.classList.add('active'));
filterState[filterType] = [];
document.querySelectorAll(`.filter-btn[data-type="${filterType}"]`).forEach(btn => {
if (filterType === 'region') {
filterState[filterType].push(btn.getAttribute('data-region'));
} else if (filterType === 'population') {
filterState[filterType].push(btn.getAttribute('data-population'));
} else if (filterType === 'language') {
filterState[filterType].push(btn.getAttribute('data-language'));
}
});
} else {
document.querySelectorAll(`.filter-btn[data-type="${filterType}"]`)
.forEach(btn => btn.classList.remove('active'));
filterState[filterType] = [];
}
} else {
this.classList.toggle('active');
const allButton = document.querySelector(`.filter-btn[data-type="${filterType}"][data-${filterType}="all"]`);
const typeButtons = document.querySelectorAll(`.filter-btn[data-type="${filterType}"]:not([data-${filterType}="all"])`);
const allActive = Array.from(typeButtons).every(btn => btn.classList.contains('active'));
if (allActive) {
allButton.classList.add('active');
} else {
allButton.classList.remove('active');
}
filterState[filterType] = [];
document.querySelectorAll(`.filter-btn[data-type="${filterType}"].active`).forEach(btn => {
if (filterType === 'region') {
filterState[filterType].push(btn.getAttribute('data-region'));
} else if (filterType === 'population') {
filterState[filterType].push(btn.getAttribute('data-population'));
} else if (filterType === 'language') {
filterState[filterType].push(btn.getAttribute('data-language'));
}
});
}
saveFilterState();
applyFilters();
});
});
function saveFilterState() {
localStorage.setItem('countryFilters', JSON.stringify(filterState));
}
function restoreFilterState() {
const savedState = localStorage.getItem('countryFilters');
if (savedState) {
filterState = JSON.parse(savedState);
filterButtons.forEach(button => {
const type = button.getAttribute('data-type');
let value;
if (type === 'region') {
value = button.getAttribute('data-region');
} else if (type === 'population') {
value = button.getAttribute('data-population');
} else if (type === 'language') {
value = button.getAttribute('data-language');
}
if (filterState[type].includes(value)) {
button.classList.add('active');
} else {
button.classList.remove('active');
}
});
applyFilters();
}
}
function applyFilters() {
countryCards.forEach(card => {
const cardRegion = card.getAttribute('data-region');
const cardPopulation = card.getAttribute('data-population');
const cardLanguage = card.getAttribute('data-language');
const matchesRegion = filterState.region.includes('all') || filterState.region.includes(cardRegion);
const matchesPopulation = filterState.population.includes('all') || filterState.population.includes(cardPopulation);
const matchesLanguage = filterState.language.includes('all') || filterState.language.includes(cardLanguage);
if (matchesRegion && matchesPopulation && matchesLanguage) {
card.classList.remove('hidden');
} else {
card.classList.add('hidden');
}
});
}
const resetButton = document.createElement('button');
resetButton.textContent = '필터 초기화';
resetButton.classList.add('reset-filters-btn');
document.querySelector('.filter-container').appendChild(resetButton);
resetButton.addEventListener('click', function() {
filterButtons.forEach(button => button.classList.add('active'));
filterState = {
region: ['all', 'JAPAC', 'AMR', 'EMEA'],
population: ['all', 'small', 'medium', 'large'],
language: ['all', 'indo-european', 'asian', 'other']
};
saveFilterState();
applyFilters();
});
});
사용된 코드 설명
function saveFilterState()
:
현재 필터 상태를 브라우저의 localStorage에 저장하는 함수다.
function restoreFilterState()
:
localStorage에서 저장된 필터 상태를 불러와 적용하는 함수다.
localStorage.setItem(‘countryFilters’, JSON.stringify(filterState))
:
filterState 객체를 JSON 문자열로 변환하여 localStorage에 저장한다.
const resetButton = document.createElement(‘button’)
:
모든 필터를 초기 상태로 리셋하는 버튼을 동적으로 생성한다.
if (type === ‘region’) { value = button.getAttribute(‘data-region’); }
:
필터 유형에 따라 올바른 데이터 속성에서 값을 가져온다.

이 코드에서 구현한 필터 상태 관리 기능은 크게 세 가지다.

  1. saveFilterState() 함수는 현재 필터 상태를 localStorage에 JSON 문자열 형태로 저장한다.
    필터 버튼을 클릭할 때마다 이 함수를 호출하여 최신 상태를 저장한다.
  2. restoreFilterState() 함수는 페이지 로드 시 localStorage에서 저장된 필터 상태를 불러와 적용한다.
    저장된 상태가 있으면 filterState 객체를 업데이트하고, 버튼 UI와 카드 표시 여부를 그에 맞게 조정한다.
  3. 필터 초기화 버튼을 추가하여 사용자가 모든 필터를 초기 상태(모두 활성화)로 쉽게 되돌릴 수 있게 했다.
    이는 여러 필터를 조작하다가 빠르게 원래 상태로 돌아가고 싶을 때 유용하다.

기존 코드와 달리, 다중 필터 타입을 지원하기 위해 필터 유형(type)에 따라 올바른 데이터 속성에서 값을 가져오는 로직이 추가되었다.
이는 버튼의 data-region, data-population, data-language 속성과 카드의 동일한 속성들을 매핑하기 위한 것이다.

localStorage는 브라우저에 데이터를 영구적으로 저장하는 Web Storage API로, 사용자가 브라우저를 닫았다가 다시 열어도 데이터가 유지된다.
이를 활용하면 사용자 설정을 세션 간에도 보존할 수 있어 사용자 경험이 크게 향상된다.

필터 상태 관리는 특히 복잡한 필터링 시스템이나 사용자가 자주 방문하는 웹 애플리케이션에서 중요한 기능이다.
사용자의 선호도를 기억하고 반영함으로써 더 개인화된 경험을 제공할 수 있다.

데이터 필터링은 많은 양의 정보를 효과적으로 관리하고 사용자에게 필요한 데이터만 표시할 수 있는 강력한 기능이다.

JavaScript와 데이터 속성(data-*)을 활용하면 복잡한 필터링 시스템도 비교적 쉽게 구현할 수 있으며, 사용자 경험을 크게 향상시킬 수 있다.

이 글에서 소개한 방법들을 활용하여 자신만의 데이터 필터링 시스템을 구축해보자.

Mingg`s Diary
밍구
공부 목적 블로그