๐Ÿ“– TIL - Tanstack Query ๋ฐ์ดํ„ฐ ํ•„ํ„ฐ๋ง๊ณผ React Router ๊ตฌ์„ฑํ•˜๊ธฐ

์Š˜ยท2025๋…„ 2์›” 24์ผ

๐Ÿ“– TIL

๋ชฉ๋ก ๋ณด๊ธฐ
64/89

๐Ÿ“Œ ์•Œ์•„์•ผ ํ•  ๊ฐœ๋…

tanstack-query์—์„œ useQuery๋ฅผ ์‚ฌ์šฉํ•˜๋˜ ๋„์ค‘ ์ผ๋ถ€ ๋ฐ์ดํ„ฐ๋งŒ ํ•„์š”ํ•œ ์ƒํ™ฉ์ด ๋ฐœ์ƒํ•˜์˜€๋‹ค. ๊ทธ๋ ‡๊ธฐ์— ๋ฐ์ดํ„ฐ๋ฅผ ํ•„ํ„ฐ๋ง ํ•˜๋Š” ์—ฌ๋Ÿฌ๊ฐ€์ง€์˜ ๋ฐฉ๋ฒ•๋“ค์„ ์ •๋ฆฌํ•ด๋ณด๋ ค๊ณ  ํ•œ๋‹ค.

1. Tanstack Query ๋ฐ์ดํ„ฐ ํ•„ํ„ฐ๋ง์˜ 3๊ฐ€์ง€ ๋ฐฉ๋ฒ•

// 1. ํด๋ผ์ด์–ธํŠธ์—์„œ ์ง์ ‘ ํ•„ํ„ฐ๋ง
const { data } = useQuery({
  queryKey: ["items"],
  queryFn: getItems
});
const filteredData = data?.filter(item => /* ํ•„ํ„ฐ๋ง ์กฐ๊ฑด */);

// 2. ์„œ๋ฒ„์— ํ•„ํ„ฐ๋ง ์กฐ๊ฑด ์ „๋‹ฌ
const { data } = useQuery({
  queryKey: ["items", filterCondition],
  queryFn: ({ queryKey }) => getItems(queryKey[1])
});

// 3. select ์˜ต์…˜์œผ๋กœ ํ•„ํ„ฐ๋ง
const { data } = useQuery({
  queryKey: ["items"],
  queryFn: getItems,
  select: (data) => data.filter(item => /* ํ•„ํ„ฐ๋ง ์กฐ๊ฑด */)
});

2. React Router์˜ ๋ผ์šฐํŠธ ๊ตฌ์„ฑ ๋ฐฉ๋ฒ•

// 1. ๊ธฐ๋ณธ ๋ผ์šฐํŠธ
<Route path="/items" element={<ItemList />} />
<Route path="/items/:id" element={<ItemDetail />} />

// 2. ์ค‘์ฒฉ ๋ผ์šฐํŠธ (์ถ”์ฒœ!)
<Route path="/items">
  // index routes ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด, ์ผ์น˜ํ•˜๋Š” ๊ฒฝ๋กœ๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ์— ๊ธฐ๋ณธ ํŽ˜์ด์ง€๋ฅผ ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ๊ณผ ์œ ์ง€ ๋ณด์ˆ˜์„ฑ๋„ ํ–ฅ์ƒ๋œ๋‹ค.
  <Route index element={<ItemList />} />
  <Route path=":id" element={<ItemDetail />} />
</Route>

2-1. ์ƒ์„ธ ํŽ˜์ด์ง€ ๊ตฌํ˜„ํ•  ๋•Œ

const ProductDetail = () => {
  const { id } = useParams();
  const { data } = useQuery({
    queryKey: ['product', id],
    queryFn: () => getProduct(id)
  });

  if (!data) return <div>๋กœ๋”ฉ์ค‘...</div>;

  return (
    <div>
      <h1>{data.name}</h1>
      <p>{data.description}</p>
    </div>
  );
};

โœจ ์–ธ์ œ ์–ด๋–ค ๋ฐฉ์‹์„ ์จ์•ผ ํ• ๊นŒ?

1. ํด๋ผ์ด์–ธํŠธ ํ•„ํ„ฐ๋ง ์‚ฌ์šฉํ•  ๋•Œ

  • ๋ฐ์ดํ„ฐ๊ฐ€ ์ ์„ ๋•Œ (์ˆ˜๋ฐฑ ๊ฐœ ์ดํ•˜)
  • ์—ฌ๋Ÿฌ ํ•„ํ„ฐ๋ง ์กฐ๊ฑด์„ ์ž์ฃผ ๋ฐ”๊ฟ”์•ผ ํ•  ๋•Œ
  • ์ฆ‰๊ฐ์ ์ธ UI ์‘๋‹ต์ด ํ•„์š”ํ•  ๋•Œ

2. ์„œ๋ฒ„ ํ•„ํ„ฐ๋ง ์‚ฌ์šฉํ•  ๋•Œ

  • ๋ฐ์ดํ„ฐ๊ฐ€ ๋งŽ์„ ๋•Œ
  • ํŽ˜์ด์ง€๋„ค์ด์…˜์ด ํ•„์š”ํ•  ๋•Œ
  • ๊ฒ€์ƒ‰ ๊ธฐ๋Šฅ์ด ํ•„์š”ํ•  ๋•Œ

3. select ์˜ต์…˜ ์‚ฌ์šฉํ•  ๋•Œ

  • ์›๋ณธ ๋ฐ์ดํ„ฐ๋„ ํ•„์š”ํ•˜๊ณ , ํ•„ํ„ฐ๋ง๋œ ๋ฐ์ดํ„ฐ๋„ ํ•„์š”ํ•  ๋•Œ
  • ์บ์‹œ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์žฌํ™œ์šฉํ•˜๊ณ  ์‹ถ์„ ๋•Œ
๋ฐฉ๋ฒ•ํŠน์ง•
filter() ์‚ฌ์šฉํด๋ผ์ด์–ธํŠธ์—์„œ ํ•„ํ„ฐ๋ง (๊ฐ„๋‹จํ•˜๊ณ  ๋น ๋ฆ„)
queryKey ํ™œ์šฉ๋ฐฑ์—”๋“œ์—์„œ ํ•„ํ„ฐ๋ง (๋ถˆํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ ์š”์ฒญ ๋ฐฉ์ง€)
select ํ™œ์šฉuseQuery์—์„œ ๋ฐ”๋กœ ํ•„ํ„ฐ๋ง (๊น”๋”ํ•œ ์ฝ”๋“œ)

๐Ÿš€ ์‹ค์ œ ์‚ฌ์šฉ ์˜ˆ์‹œ

// ๊ฒ€์ƒ‰์–ด๋กœ ํ•„ํ„ฐ๋ง์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ
const SearchResults = () => {
  const [searchTerm, setSearchTerm] = useState('');
  
  const { data } = useQuery({
    queryKey: ['search', searchTerm],
    queryFn: () => searchItems(searchTerm),
    enabled: searchTerm.length > 0  // ๊ฒ€์ƒ‰์–ด๊ฐ€ ์žˆ์„ ๋•Œ๋งŒ API ํ˜ธ์ถœ
  });

  return (
    <div>
      <input 
        value={searchTerm}
        onChange={(e) => setSearchTerm(e.target.value)}
      />
      <div>
        {data?.map(item => (
          <div key={item.id}>
            <Link to={`/items/${item.id}`}>{item.name}</Link>
          </div>
        ))}
      </div>
    </div>
  );
};

โญ๏ธ ์ค‘์š” ํฌ์ธํŠธ

  1. ๋ฐ์ดํ„ฐ ํฌ๊ธฐ์™€ ์‚ฌ์šฉ ํŒจํ„ด์— ๋”ฐ๋ผ ์ ์ ˆํ•œ ํ•„ํ„ฐ๋ง ๋ฐฉ์‹ ์„ ํƒ
  2. ์ค‘์ฒฉ ๋ผ์šฐํŠธ๋กœ ๊ด€๋ จ ํŽ˜์ด์ง€๋“ค์„ ๊ตฌ์กฐํ™”
  3. params๋‚˜ queryKey๋ฅผ ํ™œ์šฉํ•ด ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋งŒ ๊ฐ€์ ธ์˜ค๊ธฐ
profile
์ฃผ๋‹ˆ์–ด ํ”„๋ก ํŠธ์—”๋“œ ์„ฑ์žฅ๊ธฐ ๊ธฐ๋ก๊ธฐ๋ก

0๊ฐœ์˜ ๋Œ“๊ธ€