[React] React에서 select box를 활용한 검색 구현
이전 포스팅에서 select box 구현에 대해 포스팅했는데 궁극적인 목적은 검색 구현이었다.
select box에서 검색 조건을 선택하고 최종적으로 조건에 맞는 결과들을 출력하는 것이다.
검색은 아래 5단계의 과정을 통해 구현했다.
1. 검색 대상 만들기
import { createContext } from 'react';
import { Routes, Route } from 'react-router-dom';
import Home from './pages/Home';
const mockData=[
{id:0, series:'3door', year:2017, price:3000, mileage:17000, fuel:'disel', spot:'spot01', nextPlus:false},
{id:1, series:'3door', year:2020, price:4000, mileage:170000, fuel:'gasoline', spot:'spot02', nextPlus:false},
{id:2, series:'5door', year:2019, price:2700, mileage:90000, fuel:'electric', spot:'spot03', nextPlus:false},
{id:3, series:'convertible', year:2024, price:7000, mileage:7000, fuel:'hev', spot:'spot04', nextPlus:false},
];
export const MockDataContext=createContext();
// App.jsx의 하위 컴포넌트에서 접근
function App() {
return (
<>
<MockDataContext.Provider value={mockData}>
<Routes>
<Route path="/" element={<Home />} />
</Routes>
</MockDataContext.Provider>
</>
)
}
export default App
검색 대상은 임의로 작성한 객체들을 담은 배열로, 실제로 운영되는 홈페이지라면 일반적으로 DB에서 불러와지는 데이터일 것이다.
지금 만드는 홈페이지는 개인 포트폴리오 용도이고 DB연동을 공부하기 위한 목적은 아니기 때문에 최상위 컴포넌트인 App.jsx에 작성해주었다. (위 코드는 예시이기 때문에 일부만 가져왔다.)
참고로 이 배열은 여러 페이지에서 동시에 활용될 예정이라 createContext API를 사용해 App.jsx의 모든 하위 컴포넌트들이 접근할 수 있도록 설정해주는것이 좋다.
2. 검색 조건을 담을 state 구현
import { useState } from 'react';
const [condition, setCondition]=useState({
year:null,
price:null,
mileage:null,
fuel:null,
spot:null,
});
const { year, price, mileage, fuel, spot }=condition;
const onChangeSelect=(e)=>{
setCondition({
...condition,
[e.target.name]:e.target.value
});
}
먼저 검색 조건을 담을 state를 생성하기 위해 useState Hook을 활용한다.
검색 조건을 담을 condition을 구조분해하여 선언하고 초기값을 모두 null로 설정한다.
그 다음 select box의 항목을 선택할 때 동작할 onChangeSelect 함수를 정의해준다.
const Search=()=>{
return(
<select name='fuel' onChange={onChangeSelect}>
<option>연료</option>
<option value="gasoline">가솔린</option>
<option value="disel">디젤</option>
<option value="electric">전기</option>
<option value="hev">하이브리드</option>
<option value="lpg">LPG</option>
</select>
);
}
export default Search;
검색 영역의 select box 중 하나인 연료 항목의 코드이다.
연료 항목의 option을 선택할 때 onChange 이벤트가 발생하며 onChangeSelect 함수가 실행되며, setCondition으로 condition의 'fuel' 속성의 값이 null에서 선택한 value 값으로 업데이트된다.
3. 검색 조건 최적화
이 부분은 생략해도 검색 자체에는 별 지장이 없고 추가로 설명할 부분이 있어 별도의 포스팅으로 대체한다.
[JavaScript] Object.entries() 매서드 사용하기
이전에 리액트 카테고리의 포스팅 내용에서 검색 구현 중 검색 조건을 최적화하기 위해 사용한 방법이다. (3번 항목) https://duski96.tistory.com/5 사실 대단한 내용은 아니고 단순히 5개의 검색 조건
duski96.tistory.com
4. 조건에 맞는 데이터 필터링
const mockData=useContext(MockDataContext); // 전체 매물 목록
let filteredData=[...mockData]; // 매물 목록에서 검색 조건에 맞는 것들을 담을 배열
let filteredCondition=Object.entries(condition);
const onClickSubmit=()=>{ // 검색 버튼을 눌렀을 때 동작
filteredCondition.forEach((v)=>{
switch(v[0]){
case 'fuel' : {
filteredData=[...filteredData].filter((item)=>item[v[0]]===v[1]);
break;
}
default :
return filteredData;
}
});
console.log(filteredData);
}
onClickSubmit 함수는 검색 버튼을 클릭할 때 실행되어 검색 조건에 맞는 데이터를 필터링한다.
필터링 함수를 실행하기 전에 Object.entries(condition)을 활용해 검색 조건을 [key, value] 배열로 반환할 것이다.
예를 들어 연료 항목을 선택한다면 다음 배열이 반환될 것이다.
// [['year', null] , ['price', null] , ['mileage', null] , ['fuel', 'gasoline'] ['spot', null]]
데이터 필터링 함수를 구현하기 위해 App.jsx에서 만들어둔 mockData 배열을 useContext Hook으로 불러온 다음 필터링된 데이터를 담을 filteredData 배열을 만들어주었다.
이제 filteredCondition 배열을 순회하면서 검색 조건을 확인하고 필터링된 데이터(filteredData)를 반환한다.
switch문의 파라미터 v[0]는 filteredCondition의 원소인 배열들의 첫 번째 값('year', 'price', 'mileage', 'fuel', 'spot')이다.
블로그 가독성을 위해 위 코드에서는 생략했되었지만 당연히 각 v[0]에 해당하는 5개의 case와 그에 맞는 조건들이 추가되어야 한다.
이제 연료에 해당하는 select box에서 가솔린을 클릭하고 검색 버튼을 누르면 아래와 같이 fuel: 'gasoline'에 해당하는 객체들이 출력될 것이다.
* 아래 이미지의 id, series, nextPlus 속성은 포스팅 작성 과정에서 생략시켰기 때문에 우선 무시하자.

5. 검색 조건 초기화
setCondition({
year:null,
price:null,
mileage:null,
fuel:null,
spot:null,
});
한 번 검색이 완료되면 검색 조건(condition)을 초기화시켜야 한다.
안그러면 이전 검색 조건에 추가로 상태가 업데이트되기 때문에 검색이 정확히 작동하지 않는다.
4번 항목에 설명한 onClickSubmit 함수 안에 작성하면 된다.
결과 화면

연료 속성값이 가솔린인 매물들을 필터링한 배열을 활용해 Array.map() 매서드로 맵핑한 후 렌더링한 모습이다.
추후 결과 리스트에 매물 이미지를 추가하고 관심 매물로 등록하는 기능까지 구현할 예정이다.
리액트로 처음 만들어보는 페이지라 시행착오가 많은데, 효율적인 코드 작성이 필요해보인다.
포스팅에는 코드를 간추려서 올리고 있지만 아직 리액트에 익숙치 않다보니 렌더링 영역에 상태관리 코드가 지나치게 길게 작성된 상황이다.
가능하다면 이 부분은 추후 useReducer Hook을 활용해 코드를 다시 정리하는것이 좋아보인다.