Front-end/etc

필터링 기능 잔혹사

개발중인제이 2024. 4. 4. 11:52

 

🤷🏻‍♀️ 제목이 필터링 기능 잔혹사인 이유는?

그냥 내가 혼자 삽질을 하다가 두 번 일하는 잔혹한 일이 벌어졌기때문

 

홈페이지 카테고리 필터링 기능구현을 맡은 나

이미 필터링 기능을 구현해 본 나는 자신만만하게 이전에 했던 코드를 참고해서 필터링 기능을 구현하기 시작했다.

그런데? 문제가 생겨버렸다. 

아직 백엔드의 데이터를 사용하기 전 간단하게 목데이터를 만들어 사용하고 있었는데 내가 만든 필터링 기능이 전체 데이터를 필터링 하는게 아니라 이미 받아온 데이터에 한해서 필터링을 하고 있던 것이다. 

 

그니까 무한스크롤을 구현하기로 한 우리 프로젝트에서 1페이지에서만 받아오는 데이터만 필터링 할 수 있게 만들었다는것..!! ㅠ

 

자신만만해 했지만 꽤나 걸린 코드를 다 고칠 수 밖에 없었다는 슬픈이야기.. 다음에는..... 깊게 생각하자...! ㅠ

 


 

일단 레이아웃으로 구현해 놓은 카테고리! 

카테고리 이동은 Swiper 라이브러리를 사용해서 양쪽 화살표를 클릭하면 이동할 수 있게 했다.

 

const filters = [
	"전체",
	"관광호텔",
	"콘도미니엄",
	"유스호스텔",
	"펜션",
	"모텔",
	"민박",
	"게스트하우스",
	"홈스테이",
	"서비스드레지던스",
	"한옥",
];

const categoryMap: { [key: string]: string } = {
	관광호텔: "B02010100",
	콘도미니엄: "B02010500",
	유스호스텔: "B02010600",
	펜션: "B02010700",
	모텔: "B02010900",
	민박: "B02011000",
	게스트하우스: "B02011100",
	홈스테이: "B02011200",
	서비스드레지던스: "B02011300",
	한옥: "B02011600",
};

일단 카테고리는 배열로 만들어놨다. 백엔드에서 사용하는 공공 API는 위에 있는 문자로 되어있어서 사용하기위해 따로 객체로 만들어 줬다. 

 

...

export default function HomePage() {
	const [filter, setFilter] = useState(filters[0]);

	const {
		data: accommodation,
		...
	} = useInfiniteQuery({
		queryKey: ["accommodation"],
		queryFn: getAccommodations,
		...
	});

	...

	const handleFilterChange = (selectedFilter: string) => {
		setFilter(selectedFilter);
	};

	return (
		<div className="p-4">
			<FilterCategory filters={filters} />
			{accommodation.pages && (
			<FilterCategory filters={filters} onFilterChange={handleFilterChange} />
			{accommodation.pages && (
				<ul className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 gap-5 gap-y-4">
					{accommodation?.pages.map(page =>
					{accommodation.map(page =>
						page.map((item: AccommodationInfo, index: number) => {
							if (item.length == index + 1) {
								return <AccommodationCard innerRef={ref} key={index} accommodation={item} />;
							}
							return <AccommodationCard key={index} accommodation={item} />;
						}),
					)}
				</ul>
			)}
			...
		</div>
	);
}

필터 카테고리는 따로 컴포넌트로 만들어서 사용했고 filter 상태를 이용해서 해당 카테고리를 클릭하면 해당 상태값이 업데이트 될 수 있게 했다.

 

...

const filteredAccommodations = getFilteredItems(accommodation.pages, filter);

...

function getFilteredItems(accommodation: AccommodationInfo[][], filter: string) {
	if (filter === "전체") {
		return accommodation;
	} else {
		const categoryCode = categoryMap[filter];
		return accommodation.map(item => item.filter(it => it.category === categoryCode));
	}
}

그 후에 필터링을 위해 필터 함수를 만들어 사용했다. 

 

완성된 코드! 

import { useInfiniteQuery } from "@tanstack/react-query";
import { getAccommodations } from "../util/http";
import AccommodationCard from "../components/homepage/AccommodationCard";
import FilterCategory from "../components/homepage/FilterCategory";
import { useEffect, useState } from "react";
import { AccommodationInfo } from "../types/AccommodationInfo";

const filters = [
	"전체",
	"관광호텔",
	"콘도미니엄",
	"유스호스텔",
	"펜션",
	"모텔",
	"민박",
	"게스트하우스",
	"홈스테이",
	"서비스드레지던스",
	"한옥",
];

const categoryMap: { [key: string]: string } = {
	관광호텔: "B02010100",
	콘도미니엄: "B02010500",
	유스호스텔: "B02010600",
	펜션: "B02010700",
	모텔: "B02010900",
	민박: "B02011000",
	게스트하우스: "B02011100",
	홈스테이: "B02011200",
	서비스드레지던스: "B02011300",
	한옥: "B02011600",
};

export default function HomePage() {
	const [filter, setFilter] = useState(filters[0]);

	const {
		data: accommodation,
		...
	} = useInfiniteQuery({
		queryKey: ["accommodation"],
		queryFn: getAccommodations,
		...
	});

	...

	const handleFilterChange = (selectedFilter: string) => {
		setFilter(selectedFilter);
	};

	const filteredAccommodations = getFilteredItems(accommodation.pages, filter);

	return (
		<div className="p-4">
			<FilterCategory filters={filters} />
			{accommodation.pages && (
			<FilterCategory filters={filters} onFilterChange={handleFilterChange} />
			{filteredAccommodations && (
				<ul className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 gap-5 gap-y-4">
					{accommodation?.pages.map(page =>
					{filteredAccommodations.map(page =>
						page.map((item: AccommodationInfo, index: number) => {
							if (item.length == index + 1) {
								return <AccommodationCard innerRef={ref} key={index} accommodation={item} />;
							}
							return <AccommodationCard key={index} accommodation={item} />;
						}),
					)}
				</ul>
			)}
			...
		</div>
	);
}

function getFilteredItems(accommodation: AccommodationInfo[][], filter: string) {
	if (filter === "전체") {
		return accommodation;
	} else {
		const categoryCode = categoryMap[filter];
		return accommodation.map(item => item.filter(it => it.category === categoryCode));
	}
}

 

뿌듯해 하며 커밋했던 과거의 나.

 

지워.

 

필터링 기능을 수행하기 위해 만들어 줬던 filteredAccommodations, getFilteredItems 함수들을 다 지워줬다.

 

대신 필터링을 하는 부분을 네트워크 통신하는 부분에 가져와서 해당 필터에 따라서 데이터를 불러올 수 있게 만들었다.

export async function getAccommodations(pageParam: number, category: string | null) {
    let url;

    if (!category) {
        url = "/api/accommodations";
    } else {
        const categoryCode = categoryMap[category];
        url = `/api/accommodations/category/${categoryCode}`;
    }

    try {
        const res = await fetch(url + `?page=${pageParam}`);
        const data = await res.json();

        return data;
    } catch (err) {
        console.error("Error:", err);
    }
}

 

그렇게 완성된 필터링 기능! 

 

 

이제 교훈을 얻었으니 다음부터는 꼭 내가 구현하는 기능에 대해 충분히 생각하고 계획도 짜면서 시작해야겠다. 

 

~ the end ~