안녕하세요, 코딩하는곰입니다! 오늘은 React에서 가장 자주 구현되는 기능 중 하나인 실시간 검색창과 필터링 리스트를 어떻게 효율적으로 구현하는지에 대해 깊이 있게 알아보겠습니다. 단순한 구현을 넘어서 성능 최적화, 사용자 경험 개선, 그리고 유지보수가 쉬운 코드 작성법까지 모두 다룰 예정입니다. React를 20년 이상 다루어온 경험을 바탕으로 여러분에게 가장实用的인 방법을 알려드리겠습니다.
실시간 검색 기능을 구현하기 전에 먼저 이해해야 할 기본 개념들이 있습니다. React의 상태 관리, 이벤트 처리, 그리고 리스트 렌더링의 원리를 명확히 이해하는 것이 중요합니다.
가장 먼저 검색어를 저장할 상태가 필요합니다. React의 useState 훅을 사용하면 간단하게 구현할 수 있습니다.
import React, { useState } from 'react';const SearchComponent = () => {const [searchTerm, setSearchTerm] = useState('');const handleSearchChange = (event) => {setSearchTerm(event.target.value);};return (<div><inputtype="text"placeholder="검색어를 입력하세요..."value={searchTerm}onChange={handleSearchChange}/></div>);};
검색어를 기반으로 데이터를 필터링하는 로직은 배열의 filter 메서드를 사용합니다. 대소문자 구분 없이 검색할 수 있도록 toLowerCase() 메서드를 활용하는 것이 좋습니다.
const data = [{ id: 1, name: '사과', category: '과일' },{ id: 2, name: '바나나', category: '과일' },{ id: 3, name: '당근', category: '채소' },{ id: 4, name: '오이', category: '채소' },{ id: 5, name: '소고기', category: '고기' }];const filteredData = data.filter(item =>item.name.toLowerCase().includes(searchTerm.toLowerCase()));
검색어가 변경될 때마다 필터링된 결과를 실시간으로 보여주기 위해서는 상태 변화에 따른 리렌더링 메커니즘을 이해해야 합니다. React는 상태가 변경되면 컴포넌트를 자동으로 리렌더링합니다.
🎯 개발자 생산성 향상 방법을 찾고 있다면, (자바 기초) 배열(Array)과 리스트(List)의 차이점 완벽 가이드 - 선택 기준과 활용법를 참고해보세요.
기본적인 검색 기능을 구현했다면, 이제 성능 최적화와 사용자 경험을 향상시키는 고급 기법들을 적용해보겠습니다.
사용자가 빠르게 타이핑할 때마다 매번 필터링을 수행하면 성능 저하가 발생할 수 있습니다. Debounce 기법을 사용하면 일정 시간 동안 입력이 멈춘 후에만 필터링을 수행할 수 있습니다.
import React, { useState, useCallback } from 'react';import { debounce } from 'lodash';const SearchComponent = () => {const [searchTerm, setSearchTerm] = useState('');const [filteredData, setFilteredData] = useState([]);const debouncedSearch = useCallback(debounce((term) => {const results = data.filter(item =>item.name.toLowerCase().includes(term.toLowerCase()));setFilteredData(results);}, 300),[]);const handleSearchChange = (event) => {const term = event.target.value;setSearchTerm(term);debouncedSearch(term);};return (<div><inputtype="text"placeholder="검색어를 입력하세요..."value={searchTerm}onChange={handleSearchChange}/><ul>{filteredData.map(item => (<li key={item.id}>{item.name}</li>))}</ul></div>);};
불필요한 재계산을 방지하기 위해 useMemo 훅을 사용할 수 있습니다. 검색어나 데이터가 변경되지 않았다면 이전 결과를 재사용합니다.
import React, { useState, useMemo } from 'react';const SearchComponent = ({ data }) => {const [searchTerm, setSearchTerm] = useState('');const filteredData = useMemo(() => {return data.filter(item =>item.name.toLowerCase().includes(searchTerm.toLowerCase()));}, [data, searchTerm]);const handleSearchChange = (event) => {setSearchTerm(event.target.value);};return (<div><inputtype="text"placeholder="검색어를 입력하세요..."value={searchTerm}onChange={handleSearchChange}/><ul>{filteredData.map(item => (<li key={item.id}>{item.name}</li>))}</ul></div>);};
카테고리별 추가 필터링이나 여러 조건을 조합한 검색 기능을 구현할 때는 다음과 같이 접근할 수 있습니다.
const [filters, setFilters] = useState({searchTerm: '',category: 'all',priceRange: [0, 100000]});const filteredData = useMemo(() => {return data.filter(item => {const matchesSearch = item.name.toLowerCase().includes(filters.searchTerm.toLowerCase());const matchesCategory = filters.category === 'all' || item.category === filters.category;const matchesPrice = item.price >= filters.priceRange[0] && item.price <= filters.priceRange[1];return matchesSearch && matchesCategory && matchesPrice;});}, [data, filters]);
로또 번호를 과학적으로 접근하고 싶다면, AI 분석과 통계 기반 번호 추천 앱 지니로또AI가 최적의 도구입니다.
이제까지 배운 기술들을 실제 프로젝트에 효과적으로 적용하기 위한 실무 팁과 best practices를 공유드립니다.
검색 기능을 더 큰 애플리케이션의 일부로 통합할 때는 컴포넌트를 적절히 분리하는 것이 중요합니다.
// SearchInput.jsconst SearchInput = ({ value, onChange, placeholder }) => (<inputtype="text"placeholder={placeholder}value={value}onChange={onChange}/>);// SearchResults.jsconst SearchResults = ({ data, renderItem }) => (<ul>{data.map(item => (<li key={item.id}>{renderItem(item)}</li>))}</ul>);// MainSearchComponent.jsconst MainSearchComponent = () => {const [searchTerm, setSearchTerm] = useState('');const filteredData = useMemo(() => filterData(data, searchTerm), [data, searchTerm]);return (<div><SearchInputvalue={searchTerm}onChange={(e) => setSearchTerm(e.target.value)}placeholder="검색어 입력..."/><SearchResultsdata={filteredData}renderItem={(item) => (<div><span>{item.name}</span><span>{item.category}</span></div>)}/></div>);};
사용자 경험을 위해 검색 결과가 없을 때의 상태를 꼭 처리해주세요.
const SearchResults = ({ data, renderItem, searchTerm }) => {if (data.length === 0) {return (<div><p>'{searchTerm}'에 대한 검색 결과가 없습니다.</p><p>다른 검색어를 시도해보세요.</p></div>);}return (<ul>{data.map(item => (<li key={item.id}>{renderItem(item)}</li>))}</ul>);};
