ํฐ์คํ ๋ฆฌ ๋ทฐ
[Outsourcing_Day 3] HotPlace In Seoul ๐ฅ - tanstack query & custom hook
์ฑ._. 2025. 3. 2. 02:162025.02.28 (๊ธ) Works
1. DB ์ฐ๊ฒฐํ๊ธฐ
- Tanstack Query
- custom hook
DB ์ฐ๊ฒฐํ๊ธฐ
๐Tanstack Query
๊ณผ์ ํ์ ๊ตฌํ ์ฌํญ ์ค ํ ์คํ ์ฟผ๋ฆฌ๋ฅผ ์ด์ฉํ์ฌ ์๋ฒ ์ํ๋ฅผ ๊ด๋ฆฌํด์ผ ํ๋ค๋ ํญ๋ชฉ์ด ์๋ค.
์ด๋ฒ ํ๋ก์ ํธ์๋ supabase, ์นด์นด์ค ์ง๋ API, ์ ํ๋ธ API ๋ฅผ ์ฌ์ฉํ๋ค.
์ฌ์ค ์ ์ฒด์ ์ผ๋ก CRUD ์ค R ์ธ์๋ ํฌ๊ฒ ๊ธฐ๋ฅ์ด ์๊ธฐ๋ ํ๊ณ
supabase์ ๊ฐ์ ์์์ ์ผ๋ก ๋ฃ์ด ์ค ๊ณ ์ ๊ฐ์ด๊ธฐ ๋๋ฌธ์ ํ ์คํ์ ์ด๋์ ์ฌ์ฉํด์ผ ํ๋ ๊ณ ๋ฏผ์ด ๋์๋ค.
์กฐ์๋ผ๋ฆฌ ๋ํ๋ฅผ ๋๋ ๋ณด๋ฉฐ '๊ตณ์ด ์บ์ฑ์ด ํ์ํ ๋ถ๋ถ์ด ์์ง ์๋' ๋ ์ด์ผ๊ธฐ๊ฐ ๋์๊ณ ํํฐ๋๊ป ์ง๋ฌธ์ ๋๋ ธ๋ค.
ํํฐ๋์ ๋ง์์ ๋ฐ๋ฅด๋ฉด
ํ ์คํ์ ํน์ key(queryKey)๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์จ๋ค.
์ด๋ ์บ์ฑ๋ง์ด ๋ชฉ์ ์ด ์๋๋ฉฐ pending๊ณผ error๋ฅผ ํธ๋ฆฌํ๊ฒ ์ฒ๋ฆฌํ ์ ์๋ค.
์ด๋ ๊ฒ ์๊ฐํ๋ฉด ์ฝ๋ค : ๋ฐ์ดํฐ ํจ์นญ์ ๊ด๋ฆฌํ๋ ํ ์ '์บ์ฑ'์ด๋ผ๋ ๊ธฐ๋ฅ์ด ๋ถ์ ๋๋
์บ์ฑ์ด ํ์์๋ค๋ฉด staleTime ์ด๋ gcTime์ 0์ผ๋ก ์ค์ ํ์ฌ ์๋ก๊ณ ์นจ์ด๋ ํ์ด์ง๊ฐ ๋ฐ๋ ๋๋ง๋ค ์๋กญ๊ฒ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์จ๋ค.
(ํ๋ฉด ๋ด๋ถ์์์ ๋ฆฌ๋ ๋๋ง์ด๋์ ๋ฌด๊ด)
๋ค๋ง ์นด์นด์ค์ง๋, ์ ํ๋ธ ํนํ ์ ํ๋ธ API๋ ํ๋ฃจ์ ์ฌ์ฉ ํ์๊ฐ ์ ํ์ ์ด๊ธฐ ๋๋ฌธ์ ์บ์ฑ ๋ฐ์ดํฐ๊ฐ ํ์ํ๋๊ณ ํ ์ ์๋ค.
๐๐ป๊ธฐ์ตํด๋๋ฉด ์ข์๋งํ ์๋ฃ
ํํฐ๋์ ๋ง์์ ๋ฃ๊ณ supabase, ์นด์นด์ค์ง๋, ์ ํ๋ธ ์ธ ๊ณณ ๋ชจ๋ ํ ์คํ์ ์ฌ์ฉํ๊ธฐ๋ก ํ๋ค.
์ต์๋จ ํ์ผ(main.jsx)์์ new QueeryClient ๋ฅผ ์์ฑํ๋ ๊ฒ์
'์ด ํ๋ก์ ํธ์์๋ ์๋ฒ ์ํ ๊ด๋ฆฌ๋ฅผ Tanstack Query ๋ก ํ๊ฒ ๋ค' ๊ณ ์ ์ธ(?) ํ๋ ๊ฒ์ด๋ ๋ง์ฐฌ๊ฐ์ง์ด๋ค ใ ใ
๊ทธ๋ ๋ค๋ฉด ์๋ก ๋ค๋ฅธ ์ธ ๊ฐ์ ์๋ฒ ์ํ๋ ์ด๋ป๊ฒ ๊ตฌ๋ถํด์ ์ฐ๋?
๋ฐ๋ก queryKey๋ฅผ ๋ค๋ฅด๊ฒ ์ฃผ์ด ๊ตฌ๋ถํ๋ ๊ฒ์ด๋ค.
์ด ์ฟผ๋ฆฌํค์ ๋ฐ๋ผ ์๋ฒ ์ํ๋ฅผ ์บ์ฑ, ๋ฆฌํจ์นญ, ๋ฌดํจํํ ์ ์๋ ๊ฒ์ด๋ค.
๋๋ ํซํ ๋ฆฌ์คํธ์ ๋ถ๋งํฌ ํ์ด์ง๋ฅผ ๋งก์์ผ๋ supabase์ 'hotplaces' ํ ์ด๋ธ์ ๊ฐ๋ง ์บ์ฑํด์ค๋ฉด ๋๋ ๊ฒ์ด๋ค.
// HotPlaceList.jsx
// supabase์์ ๊ฐ ๊ฐ์ ธ์ค๊ธฐ
const HotPlaceList = () => {
const getHotplaces = async () => {
const { data, error } = await supabase.from('hotplaces').select('*');
if (error) {
throw new Error(error.message);
}
return data;
};
// useQuery ์ฌ์ฉํ๊ธฐ
const {
data: hotplaces = [],
isPending,
isError,
} = useQuery({
queryKey: ['hotplaces'],
queryFn: getHotplaces,
});
if (isPending) {
return <div>๋ก๋ฉ ์ค...</div>;
}
if (isError) {
<div>์๋ฌ๊ฐ ๋ฐ์ํ์ต๋๋ค.</div>;
}
};
export default HotPlaceList;
๐custom hook
ํ์๋ค๊ณผ ํ์ผ ๊ตฌ์กฐ๋ฅผ ์ ํ ๋ query๋ ํ์ด์ง ์์ ๋๊ธฐ๋ณด๋ค๋ ๋ถ๋ฆฌํด์ ๊ด๋ฆฌํ์๋ ์ด์ผ๊ธฐ๊ฐ ๋์๋ค.
lib ํด๋ > quries ํด๋ ๋ด์์ ํ์ผ๋ก ๊ด๋ฆฌํ๊ฒ ๋์๋ค.

๊ทธ๋ ๋ค๋ฉด ์์ ์์ฑํ ๋ก์ง์ ์ ์ ํ ๋ถ๋ฆฌํด์ผ ํ๋ค.
๋ฐ๋ก ์ปค์คํ ํ ์ ์ฌ์ฉํ๋ ๊ฒ์ด๋ค!
queries ํด๋ ์์ ์๋ js ํ์ผ์ ์ปดํฌ๋ํธ๊ฐ ์๋ ์ปค์คํ ํ ์ด๋ค.
์ด์ ์ ์ปค์คํ ํ ์ ๊ธฐ๋ณธ ์๋ฆฌ์ ๋ํด ์ ๋ฆฌํด ๋ ๊ธ์ด ์๋ค.
์ด๋ฒ์ ๋ง๋ ์ปค์คํ ํ ๋ ๋ก์ง์ ๋ณด๋ฉฐ ์ ๋ฆฌํด๋ณด๊ฒ ๋ค.
// GetHotplaces.js
import supabase from '@/lib/api/supabaseAPI';
import { useQuery } from '@tanstack/react-query';
export const useGetHotplaces = () => {
const getHotplaces = async () => {
const { data, error } = await supabase.from('hotplaces').select('*');
if (error) {
throw new Error(error.message);
}
return data;
};
return useQuery({
queryKey: ['hotplaces'],
queryFn: getHotplaces,
});
};
์ฐ์ ์ด ํ ์ useQuery๋ฅผ ๋ฐํํ๋ค.
useQuery๋ Key์ ํด๋นํ๋ ์ด๋ฆ์ผ๋ก ๋น๋๊ธฐ ํจ์๋ฅผ ์คํํ ๊ฐ์ ์บ์ฑํ๋ค.
๋น๋๊ธฐ ํจ์ getHotplaces๋ supabase์์ 'hotplaces' ๋ผ๋ ํ ์ด๋ธ์ ๋ชจ๋ ๊ฐ์ ๊ฐ์ ธ์จ๋ค.
์ฆ, ์ด ํ ์ supabase์์ ๊ฐ์ ธ ์จ ํซํ๋ ์ด์ค์ ์ ๋ณด๋ฅผ ์บ์ฑํ๋ค.
// HotplaceList.jsx
const HotplaceList = () => {
const { data: hotplaces = [], isPending, isError } = useGetHotplaces();
if (isPending) {
return <div>๋ก๋ฉ ์ค...</div>;
}
if (isError) {
return <div>์๋ฌ๊ฐ ๋ฐ์ํ์ต๋๋ค.</div>;
}
return(
...
์์์ ๋ง๋ ์ปค์คํ ํ ์ ์ ์ฉ์ํค๋ ์ปดํฌ๋ํธ์ด๋ค.
์ปค์คํ ํ ์ด ๋ฐํํ๋ ์บ์ฑ๋ ์ ๋ณด ์ค, ์ฐ๋ฆฌ์๊ฒ ํ์ํ ์ธ ๊ฐ์ง๋ฅผ ๊ตฌ์กฐ๋ถํดํ ๋น์ผ๋ก ๋ฝ์๋ธ๋ค.(data, isPending, isError)
data๋ hotplaces๋ผ๋ ์ด๋ฆ์ผ๋ก ์ด๊ธฐ๊ฐ์ ๋น ๋ฐฐ์ด๋ก ์ง์ ํ๋ค. ์ด ๋ฐฐ์ด๋ก ๋ฆฌ์คํธ๋ฅผ mapping ํ๋ค.
isPending์ state๋ก ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ค๋ ์ค์ผ ๋ return์ ๋ฐํํ๋ค.
isError๋ state๋ก ์๋ฌ ์ํฉ์ผ ๋ return์ ๋ฐํํ๋ค.
+ ๋ฆฌ์คํธ ๋ฟ๋ง ์๋๋ผ ๋ถ๋งํฌ ํ์ด์ง๋ ๋ง์ฐฌ๊ฐ์ง๋ก ์ปค์คํ ํ ์ ์ด์ฉํ์ฌ ๊ด๋ฆฌํ๋ค.
[์ปค์คํ ํ ]
// GetBookmarks.js
import supabase from '../api/supabaseAPI';
import { useQuery } from '@tanstack/react-query';
export const useGetBookmarks = () => {
const getBookmarks = async () => {
const { data, error } = await supabase.from('bookmarks').select('*, hotplaces(name, img_url)');
if (error) {
throw new Error(error.message);
}
return data;
};
return useQuery({
queryKey: ['bookmarks'],
queryFn: getBookmarks,
});
};
[๋ถ๋งํฌ ํ์ด์ง]
// BookMark.jsx
const BookMark = () => {
const { data: bookmarkList = [], isPending, isError } = useGetBookmarks();
if (isPending) {
return <div>๋ก๋ฉ ์ค...</div>;
}
if (isError) {
<div>์๋ฌ๊ฐ ๋ฐ์ํ์ต๋๋ค.</div>;
return(
...'Project' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| [LoL info App_Final] LoL ์ ๋ณด ํ์ด์ง (0) | 2025.03.19 |
|---|---|
| [Outsourcing_Final] HotPlace In Seoul ๐ฅ (1) | 2025.03.05 |
| [Outsourcing_Day 2] HotPlace In Seoul ๐ฅ (0) | 2025.03.01 |
| [Outsourcing_Day 1] HotPlace In Seoul ๐ฅ (0) | 2025.02.27 |
| [MBTI-Test PJ_Final] MBTI Test (0) | 2025.02.25 |


