Original article: How to Build a Search Filter using React and React Hooks
API에 GET 요청을 보내면 서버로부터 응답 데이터를 받습니다. 하지만 가끔은 이 데이터를 관리하는 게 문제가 되기도 합니다.
이 글에서는 React에서 검색 필터를 생성하는 법을 알려 드리려고 합니다. 함수형 컴포넌트와 React 훅을 이용하여 데이터에 있는 특정 단어를 검색할 것입니다.
API에 GET 요청을 보내는 법
우선 서버에서 데이터를 불러오는 API에 GET 요청을 만들어 보겠습니다. 이 데이터를 불러오기 위해 아무 서버를 사용해도 되지만 이 글에서 필자는 사용자 목록을 불러오기 위해 {JSON} placeholder를 사용할 것입니다.
이 예시에서 사용자들의 이름과 이메일을 보여주는 카드들이 있습니다. 또한 특정 사용자들 검색하기 위해 사용할 검색 입력 창이 있습니다.
import React, { useState, useEffect } from 'react';
import axios from 'axios';
import { Card, Input } from 'semantic-ui-react'
export default function Posts() {
const [APIData, setAPIData] = useState([])
useEffect(() => {
axios.get(`https://jsonplaceholder.typicode.com/users`)
.then((response) => {
setAPIData(response.data);
})
}, [])
return (
<div style={{ padding: 20 }}>
<Input icon='search'
placeholder='Search...'
/>
<Card.Group itemsPerRow={3} style={{ marginTop: 20 }}>
{APIData.map((item) => {
return (
<Card>
<Card.Content>
<Card.Header>{item.name}</Card.Header>
<Card.Description>
{item.email}
</Card.Description>
</Card.Content>
</Card>
)
})}
</Card.Group>
</div>
)
}
그리고 만약 React에서 GET API 호출을 다루는 법을 모르신다면 API 호출에 대해 설명하는 React CRUD 작업에 관한 제 블로그 혹은 비디오를 보시길 바랍니다.
검색 입력 박스로부터 검색 입력을 얻는 법
이제 검색 입력 박스로 부터 검색 쿼리(query)를 가져와 보겠습니다.
검색 입력을 위한 state를 생성합니다.
const [searchInput, setSearchInput] = useState('');
여기 searchInput
은 문자열이고 검색 입력 값 설정을 위해 setSearchInput
을 사용할 것입니다.
이제 검색 기능을 처리할 함수를 만들어 보겠습니다.
const searchItems = () => {
}
그리고 이 함수를 onChange
이벤트로 검색 입력에 바인드(bind)합니다.
<Input icon='search'
placeholder='Search...'
onChange={() => searchItems()}
/>
이리하여 입력 필드에 어떤 값이든 입력할 때마다 searchItems
가 실행이 될 것입니다.
이제 searchItems
에 입력 값을 전달해야 합니다.
<Input icon='search'
placeholder='Search...'
onChange={(e) => searchItems(e.target.value)}
/>
다음 검색 쿼리를 searchItems
함수에서 전달 받고 이전에 생성한 setSearchInput
을 사용하여 searchInput
state를 이 전달 받은 값으로 설정하겠습니다.
const searchItems = (searchValue) => {
setSearchInput(searchValue)
}
콘솔로 searchValue
값을 확인함으로써 위 로직이 잘 실행되는지 확인할 수 있습니다.
보시다시피 필자의 이름을 입력했고 콘솔에서 이름이 나타납니다.
검색 결과에 따른 아이템 필터링
이제 filter 메소드를 사용하여 APIData를 필터링(filtering)할 것입니다.
const searchItems = (searchValue) => {
setSearchInput(searchValue)
APIData.filter((item) => {
return Object.values(item).join('').toLowerCase().includes(searchInput.toLowerCase())
})
}
searchItems
함수에서 서버로부터 얻는 데이터를 포함한 APIData state를 필터링하는 filter
메소드를 사용하고 있다는 것을 알 수 있습니다.
또한 객체 아이템으로부터 값들을 얻기 위해 Object.values
를 사용하고 있습니다.
그런 다음 join(' ')
메소드를 사용하여 이 값들을 문자열로 바꾸어 줍니다.
다음 toLowerCase
메소드를 사용하여 이 문자열들을 소문자로 바꾸어 줍니다.
마지막으로 이 문자열들이 검색란에 입력한 값을 포함하고 있는지 확인합니다. 검색 입력 또한 소문자로 바꾸어 줍니다. 이는 단어를 대문자로 입력했을 시 검색을 더 효율적으로 만들어 주기 위해 그 문자열을 소문자로 바꾸게 하는 것입니다.
그런 다음 전체 쿼리를 반환합니다.
이제 이 필터링한 배열을 변수에 저장해야 합니다.
const filteredData = APIData.filter((item) => {
return Object.values(item).join('').toLowerCase().includes(searchInput.toLowerCase())
})
콘솔을 통해 위에서 만든 기능을 확인해 보겠습니다. 사용자 이름을 검색해보면 해당 사용자 이름에 맞는 데이터를 보게 될 것입니다.
이제 필터 데이터를 저장해야 하므로 state를 생성하겠습니다.
const [filteredResults, setFilteredResults] = useState([]);
필터링한 데이터를 담은 state를 생성합니다.
그런 다음 setFilteredResults
를 사용하여 searchItems
함수에서 이 state를 filteredData
로 설정합니다.
const searchItems = (searchValue) => {
setSearchInput(searchValue)
const filteredData = APIData.filter((item) => {
return Object.values(item).join('').toLowerCase().includes(searchInput.toLowerCase())
})
setFilteredResults(filteredData)
}
필터링 결과를 UI에 보여주는 법
이제 이 필터링 결과들을 주 UI에 보여주도록 하겠습니다.
우선 검색 입력란이 비어 있는지 확인하고 비어 있다면 모든 데이터를 보여주는 코드를 작성해야 합니다. 비어 있지 않다면 입력 값에 따라 결과를 필터링할 것입니다.
const searchItems = (searchValue) => {
setSearchInput(searchValue)
if (searchInput !== '') {
const filteredData = APIData.filter((item) => {
return Object.values(item).join('').toLowerCase().includes(searchInput.toLowerCase())
})
setFilteredResults(filteredData)
}
else{
setFilteredResults(APIData)
}
}
또한 애플리케이션의 반환 부분에서도 똑같은 확인이 필요합니다.
그래서 검색 단어의 길이가 1보다 크면 필터가 된 데이터를 보여줄 것입니다. 1보다 작다면 APIData state에 담긴 모든 데이터를 보여줄 것입니다.
<Card.Group itemsPerRow={3} style={{ marginTop: 20 }}>
{searchInput.length > 1 ? (
filteredResults.map((item) => {
return (
<Card>
<Card.Content>
<Card.Header>{item.name}</Card.Header>
<Card.Description>
{item.email}
</Card.Description>
</Card.Content>
</Card>
)
})
) : (
APIData.map((item) => {
return (
<Card>
<Card.Content>
<Card.Header>{item.name}</Card.Header>
<Card.Description>
{item.email}
</Card.Description>
</Card.Content>
</Card>
)
})
)}
</Card.Group>
이제 검색 필드에서 사용자 이름을 검색한다면 해당 사용자의 데이터를 얻게 됩니다.
검색 창에서 이름을 지우면 모든 데이터를 얻게 됩니다.
여기 참고할 분들을 위해 전체 코드를 적어 두겠습니다.
import React, { useState, useEffect } from 'react';
import axios from 'axios';
import { Card, Input } from 'semantic-ui-react'
export default function Posts() {
const [APIData, setAPIData] = useState([])
const [filteredResults, setFilteredResults] = useState([]);
const [searchInput, setSearchInput] = useState('');
useEffect(() => {
axios.get(`https://jsonplaceholder.typicode.com/users`)
.then((response) => {
setAPIData(response.data);
})
}, [])
const searchItems = (searchValue) => {
setSearchInput(searchValue)
if (searchInput !== '') {
const filteredData = APIData.filter((item) => {
return Object.values(item).join('').toLowerCase().includes(searchInput.toLowerCase())
})
setFilteredResults(filteredData)
}
else{
setFilteredResults(APIData)
}
}
return (
<div style={{ padding: 20 }}>
<Input icon='search'
placeholder='Search...'
onChange={(e) => searchItems(e.target.value)}
/>
<Card.Group itemsPerRow={3} style={{ marginTop: 20 }}>
{searchInput.length > 1 ? (
filteredResults.map((item) => {
return (
<Card>
<Card.Content>
<Card.Header>{item.name}</Card.Header>
<Card.Description>
{item.email}
</Card.Description>
</Card.Content>
</Card>
)
})
) : (
APIData.map((item) => {
return (
<Card>
<Card.Content>
<Card.Header>{item.name}</Card.Header>
<Card.Description>
{item.email}
</Card.Description>
</Card.Content>
</Card>
)
})
)}
</Card.Group>
</div>
)
}
이제 React 훅을 이용하여 React에서 완전 함수형의 검색 필터를 가지게 되었습니다.
보통 이 기능은 API 엔드포인트에서 검색 쿼리 인자를 전달하여 백엔드에서 처리하지만 프론트엔드에서 그 기능을 처리하는 것을 아는 것도 중요합니다.
이 글에 대한 보충 설명이 필요하다면 React와 React 훅을 이용하여 검색 필터 만들기에 관한 제 유투브 비디오를 확인하시면 되겠습니다.
이상 마무리 짓겠습니다. 감사합니다.