이번 2차 프로젝트에서 쿼리스트링 관련한 기능 구현 부분을 담당했다.
그 중에서도 가장 기억에 남았던 다중필터!
구상한 로직, 코드 공유까지 해보고자 합니다--!
코드를 짜기 전에 전체적으로 어떤 흐름을 가지고 문제를 해결할지 순서대로 정리를 해보았다.
ex)
{
sort_type: 'schedules',
title: '일정',
contents: [
'월요일',
//코드 생략
'일요일',
],
},
위의 객체가 여러개로 이루어진 배열의 형태
const getCardListData = useCallback(async () => {
const res = await fetch(
`${BASE_URL}/main/search${search}`
);
const data = await res.json();
setCardList(data.result);
}, [search]);
useEffect(() => {
getCardListData();
}, [getCardListData]);
🔖 참고)getCardListData함수를 useCallback으로 감싸줌으로써, 해당 컴포넌트가 랜더링되더라도 그 함수가 의존하는 값들이 바뀌지 않는 한 기존 함수를 계속해서 반환하게끔 한다.(최적화 작업)
const [clickedCheckList, setClickedCheckList] = useState([]);
const handleCheckList = (e, content, idx, sort_type) => {
e.target.checked
? setClickedCheckList([
...clickedCheckList,
{ id: idx, content, sortType: sort_type },
])
: setClickedCheckList(
clickedCheckList.filter(list => list.content !== content)
);
};
return
// 관련 없는 코드 생략
{contents.map((content, idx) => (
<Content
key={idx}
onClick={e => handleCheckList(e, content, idx, sort_type)}
>
<input type="checkbox" />
{content}
</Content>
))}
const makeQueryString = () => {
const queryString = clickedCheckList
.map(({ id, content, sortType }) => {
return sortType === 'category' || sortType === 'types'
? `${sortType}_id=${parseInt(id) + 1}`
: `${sortType}=${content}`;
})
.map((item, idx) => {
return idx === 0 ? item : '&' + item;
})
.join('');
navigate(`?${queryString}`);
};
그러면 다중필터 완성! 💯
위의 코드 아래에 한꺼번에 정리 (관련없는 코드 생략)
const LectureList = () => {
const { search } = useLocation();
const navigate = useNavigate();
const [cardList, setCardList] = useState([]);
const [clickedCheckList, setClickedCheckList] = useState([]);
const getCardListData = useCallback(async () => {
const res = await fetch(
`${BASE_URL}/main/search${search}`
);
const data = await res.json();
setCardList(data.result);
}, [search]);
useEffect(() => {
getCardListData();
}, [getCardListData]);
const makeQueryString = () => {
const queryString = clickedCheckList
.map(({ id, content, sortType }) => {
return sortType === 'category' || sortType === 'types'
? `${sortType}_id=${parseInt(id) + 1}`
: `${sortType}=${content}`;
})
.map((item, idx) => {
return idx === 0 ? item : '&' + item;
})
.join('');
navigate(`?${queryString}`);
};
const handleCheckList = (e, content, idx, sort_type) => {
e.target.checked
? setClickedCheckList([
...clickedCheckList,
{ id: idx, content, sortType: sort_type },
])
: setClickedCheckList(
clickedCheckList.filter(list => list.content !== content)
);
};
return (
<Wrapper>
<FilterList ref={filterDom}>
{FILTER_CATEGORYS.map(({ sort_type, title, contents }, idx) => {
return (
<Filter key={idx}>
<Category>
{title}
</Category>
<Contents>
{contents.map((content, idx) => (
<Content
key={idx}
onClick={e => handleCheckList(e, content, idx, sort_type)}
>
<input type="checkbox" />
{content}
</Content>
))}
<Btns>
<Button
onClick={makeQueryString}
>
필터 적용
</Button>
</Btns>
</Contents>
</Filter>
);
})}
</FilterList>
</Wrapper>
);
};
export default LectureList;
const FILTER_CATEGORYS = [
{
sort_type: 'category',
title: '카테고리',
contents: [
'국내',
'일본',
'유럽',
'미국',
'한식',
'양식',
'일식',
'커피·디저트',
'드로잉',
'미술',
'글쓰기',
'사진',
'러닝',
'피트니스',
'등산',
'수영',
],
},
{
sort_type: 'types',
title: '클래스 진행 방식',
contents: ['오프라인', 'VOD', '전자책'],
},
{
sort_type: 'regions',
title: '지역',
contents: [
'서울',
'경기',
'인천',
'부산',
'경상',
'대전',
'걍원',
'광주',
'제주',
],
},
{
sort_type: 'schedules',
title: '일정',
contents: [
'월요일',
'화요일',
'수요일',
'목요일',
'금요일',
'토요일',
'일요일',
],
},
];
다중필터가 처음에는 괜히 어렵게 느껴져서 손대기조차 두려웠는데 내가 정확히 '어떤 문제'를 해결해야하는지, 어떤 순서로 로직을 짜야할지 처음부터 차근차근 고민하다보니 해결할 수 있었다.
사실 이게 한번 갈아엎은 코드인데, 처음에는 makeQueryString이라는 함수를 만드는 대신에 clickedCheckList를 처음부터 쿼리스트링 형태로 저장했었다. 그런데 생각해보니 checkList내에는 체크리스트 정보만 들어가야하는데, 불필요하게 최종 결과값이 들어가 오히려 코드 가독성이 떨어지고 지저분해보였다. (두가지 관심사가 한 코드 내에 들어감으로써)
어떻게 한 코드 내에 한가지 정보만 넣어줄 수 있을까 엄청 고민 끝에,, 갈아엎었다. 관심사의 분리 어렵지만 항상 고민하고 계속해서 더 좋은 코드 방식을 생각해내보자! 아마 내가 짠 코드보다 훨씬 더 좋은 코드들이 많을 거다..! 다른 방법들도 있는지 더 찾아봐야겠다.