๐Ÿ“ฒ React-query ์‚ฌ์šฉํ•˜๋Š” ์ด์œ , useInfiniteQuery ๋กœ ๋ฌดํ•œ ์Šคํฌ๋กค ๊ตฌํ˜„ํ•˜๊ธฐ (+ react-infinite-scroller)

leehyunjuยท2023๋…„ 3์›” 28์ผ
149
post-thumbnail
post-custom-banner

ํ˜„์žฌ ๋™๋ฃŒ๋“ค๊ณผ ํ•จ๊ป˜ ์ง„ํ–‰ํ•˜๊ณ  ์žˆ๋Š” ์‚ฌ์ด๋“œ ํ”„๋กœ์ ํŠธ์— React-query๋ฅผ ๋„์ž…ํ•ด๋ดค์Šต๋‹ˆ๋‹ค. React-query๋ฅผ ์™œ ์‚ฌ์šฉํ–ˆ๋Š”์ง€? ์— ๋Œ€ํ•ด์„œ ์†Œ๊ฐœํ•˜๊ณ  ์‚ฌ์šฉ ๋ฐฉ๋ฒ•๊ณผ ํšจ์œจ์ ์ธ ๊ธฐ๋Šฅ๋“ค ์†Œ๊ฐœ์™€ ํ•จ๊ป˜ React-query๋ฅผ ์ด์šฉํ•˜์—ฌ ๋ฌดํ•œ ์Šคํฌ๋กค์„ ๊ตฌํ˜„ํ•ด๋ณธ ๋ฐฉ๋ฒ•์„ ๊ธฐ๋กํ•˜๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค. โญ๏ธ๋งŽ์€ ๋„์›€ ๋˜๊ธธ ๋ฐ”๋ž๋‹ˆ๋‹คโญ๏ธ

๐Ÿ”ฅ React-query

ํ˜„์žฌ๋Š” React-query ๊ฐ€ ๋ฒ„์ „ 4๊ฐ€ ๋ฆด๋ฆฌ์Šค ๋œ ์ƒํƒœ๊ณ , ์ด๋ฆ„ ๋˜ํ•œ ๋ฐ”๋€Œ์—ˆ๋‹ค.
๋ฒ„์ „ 4์—์„œ๋Š” React-query๊ฐ€ ์•„๋‹Œ TanStack Query๋กœ ๋ถˆ๋ฆฐ๋‹ค. (๊ทธ๋ž˜๋„ ๋‚œ ๋ฆฌ์•กํŠธ ์ฟผ๋ฆฌ๋ผ ๋ถ€๋ฅผ๊ฑฐ๋‹ค!)

๐Ÿ’๐Ÿปโ€โ™€๏ธ 3๋ฒ„์ „๊ณผ 4๋ฒ„์ „์˜ ์ฐจ์ด

  • v4์—์„œ ํ•ด๋‹น ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ import ํ•˜๋ ค๋ฉด @tanstack/react-query๋กœ ๊ฐ€์ ธ์˜จ๋‹ค.
import { useQuery } from '@tanstack/react-query';
  • v4์—์„œ์˜ ์ฟผ๋ฆฌํ‚ค๋Š” ๋ฐฐ์—ด์ด์–ด์•ผ ํ•œ๋‹ค. ์–ด์ฉ์ง€ 3๋ฒ„์ „์—์„œ๋Š” ์ฟผ๋ฆฌํ‚ค๋ฅผ ์ผ๋ฐ˜ ๋ฌธ์ž์—ด๋กœ๋„ ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ–ˆ๋Š”๋ฐ 4๋ฒ„์ „์—์„œ๋Š” ๋ฌธ์ž์—ด๋กœ๋งŒ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค. ๋ฌธ์ž์—ด์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ ๋ฐ˜๋“œ์‹œ ๋ฐฐ์—ด๋กœ ๋ฌถ์–ด์„œ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.
3๋ฒ„์ „ -> useQuery('todos', fetchTodos)
4๋ฒ„์ „ -> useQuery(['todos'], fetchTodos)
  • ๊ฐœ๋ฐœ์ž ๋„๊ตฌ๋ฅผ ๋ณ„๋„๋กœ ์„ค์น˜ํ•˜๊ณ  import ํ•  ๋• @tanstack/react-query-devtools๋กœ ๊ฐ€์ ธ์™€์•ผํ•œ๋‹ค. 3๋ฒ„์ „์—์„œ๋Š” ๊ฐœ๋ฐœ์ž ๋„๊ตฌ ๋ณ„๋„๋กœ ์„ค์น˜ํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค.
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';

๐Ÿ” React-query๋ฅผ ์“ฐ๋Š” ์ด์œ 

  1. ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ๋ฐ์ดํ„ฐ ํŽ˜์นญ
  2. ๐Ÿ”ฅํ•ต์‹ฌ ๊ธฐ๋Šฅ์€ ๋ฐ์ดํ„ฐ ์บ์‹ฑ์ด๋‹ค.๐Ÿ”ฅ ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ณ ๋ฐ›์€ ๊ฒฐ๊ณผ๋ฅผ ์บ์‹œ๋กœ ์ €์žฅํ•˜์—ฌ, ๋™์ผํ•œ ์š”์ฒญ์ด ๋ฐ˜๋ณต๋˜์–ด๋„ ์ƒˆ๋กœ์šด ์š”์ฒญ์„ ๋ณด๋‚ด์ง€ ์•Š๊ณ  ์ €์žฅ๋˜์–ด ์žˆ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ๋„คํŠธ์›Œํฌ ์‚ฌ์šฉ๋Ÿ‰์„ ์ค„์ด๊ณ , ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„ฑ๋Šฅ์„ ํ–ฅ์ƒ์‹œํ‚ค๋Š” ๋ฐ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค.
  3. ๋กœ๋”ฉ ๋ฐ ์˜ค๋ฅ˜ ์ƒํƒœ ๊ด€๋ฆฌ (๋ฐ์ดํ„ฐ๊ฐ€ ๋กœ๋”ฉ๋˜์—ˆ์„ ๋•Œ์™€ ์˜ค๋ฅ˜ ๋ฐœ์ƒ์‹œ ์‚ฌ์šฉ์ž์—๊ฒŒ ์•Œ๋ ค์ค€๋‹ค)
  4. ๋ฆฌ์•กํŠธ ์ฟผ๋ฆฌ ๊ฐœ๋ฐœ์ž ๋„๊ตฌ๋ฅผ ํ†ตํ•ด ์ฟผ๋ฆฌ์— ๋ฌด์Šจ ์ผ์ด ์ผ์–ด๋‚ฌ๋Š”์ง€ ์ถ”์ ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.
  5. ๊ฒŒ์‹œํŒ ๊ฐ™์€ ๊ฒƒ์„ ๋งŒ๋“ค ๋•Œ Pagination ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜์—ฌ ํ”„๋ฆฌํŒจ์นญ(Prefetching)์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.
    *ํ”„๋ฆฌํŒจ์นญ(Prefetching)์ด๋ž€ ? ๋‹ค์Œ ํŽ˜์ด์ง€ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฏธ๋ฆฌ ๊ฐ€์ ธ์™€์„œ, ๋‹ค์Œ ํŽ˜์ด์ง€๋กœ ๋„˜์–ด๊ฐˆ ๋•Œ ๋ฏธ๋ฆฌ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์™”๊ธฐ ๋•Œ๋ฌธ์— ๋งค๋„๋Ÿฝ๊ฒŒ ์ฒ˜๋ฆฌ ๋œ๋‹ค.
  6. ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰์…˜์„ ์ด์šฉํ•˜์—ฌ ์ž๋™์œผ๋กœ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๊ด€๋ฆฌํ•ด์ค€๋‹ค.

๐Ÿ“๊ทธ๋Ÿผ ์ด์ œ ํ”„๋กœ์ ํŠธ์— React-query๋ฅผ ๋„์ž…ํ•ด๋ณด์ž = ์ดˆ๊ธฐ์„ธํŒ…

  • ์„ค์น˜ ๋ช…๋ น์–ด
npm install react-query
npm install react-query@^3 // ๋ฒ„์ „ 3

  • App.tsx ๋˜๋Š” Next ๊ฒฝ์šฐ _app.tsx
import { QueryClient, QueryClientProvider } from "react-query";  //๐Ÿ“์ถ”๊ฐ€

const queryClient = new QueryClient();//๐Ÿ“์ถ”๊ฐ€

function App() {
  return (
    <QueryClientProvider client={queryClient}> //๐Ÿ“์ถ”๊ฐ€
      <div className="App">
        (...)
      </div>
    </QueryClientProvider>
  );
}

export default App;

<QueryClientProvider> </QueryClientProvider>๋กœ ๊ฐ์‹ธ๊ธฐ! ๊ทธ๋Ÿผ ์„ธํŒ… ๋์ด๋‹ค. ์™„์ „ ์ดˆ๊ฐ„๋‹จ


๐Ÿ“React-query ๊ฐœ๋ฐœ์ž ๋„๊ตฌ๋„ ์ถ”๊ฐ€ํ•ด๋ณด์ž

๋ฆฌ์•กํŠธ์ฟผ๋ฆฌ ๊ฐœ๋ฐœ์ž ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ 

  • ์ฟผ๋ฆฌ ํ‚ค๋กœ ์ฟผ๋ฆฌ๋ฅผ ํ‘œ์‹œํ•ด์ค€๋‹ค.
  • ๊ฐœ๋ฐœ ์ค‘์ธ ๋ชจ๋“  ์ฟผ๋ฆฌ์˜ ์ƒํƒœ๋ฅผ ํ‘œ์‹œํ•ด์ค€๋‹ค.
    ์ด๊ฒƒ์— ๋Œ€ํ•œ ์ƒํƒœ๋ž€ ํ™œ์„ฑ, ๋น„ํ™œ์„ฑ, ๋งŒ๋ฃŒ(stale) ๋“ฑ ๋ชจ๋“  ์ฟผ๋ฆฌ์˜ ์ƒํƒœ๋ฅผ ์•Œ๋ ค์ค€๋‹ค.
  • ๋งˆ์ง€๋ง‰์œผ๋กœ ์—…๋ฐ์ดํŠธ ๋œ ํƒ€์ž„ ์Šคํƒฌํ”„๋„ ์•Œ๋ ค์ค€๋‹ค.
  • ๋ฐ์ดํ„ฐ ํƒ์ƒ‰๊ธฐ๋„ ์žˆ๋‹ค.
  • ์ฟผ๋ฆฌ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ๋Š” ์ฟผ๋ฆฌ ํƒ์ƒ‰๊ธฐ๋„ ์žˆ๋‹ค.

๋ฒ„์ „ 4์—์„œ ๊ฐœ๋ฐœ์ž ๋„๊ตฌ ์„ค์น˜ํ•  ๋•Œ ๋ช…๋ น์–ด (๋ฒ„์ „3์€ ์„ค์น˜ํ•  ํ•„์š” ์—†๋‹ค)

npm i @tanstack/react-query-devtools
or
yarn add @tanstack/react-query-devtools

  • App.tsx ๋˜๋Š” Next ๊ฒฝ์šฐ _app.tsx
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; // ๐Ÿ“ ์ถ”๊ฐ€

return (
        <>
            <QueryClientProvider client={queryClient}>
                (... ๋‹ค๋ฅธ ์ฝ”๋“œ๋Š” ์ƒ๋žต)
                <ReactQueryDevtools /> // ๐Ÿ“ ์ถ”๊ฐ€
            </QueryClientProvider>
        </>
    );

๊ฐœ๋ฐœ์ž ๋„๊ตฌ๊นŒ์ง€ ์„ธํŒ…์ด ์™„๋ฃŒ๋˜๋ฉด ๋ธŒ๋ผ์šฐ์ €์— ๊ฝƒ๋ชจ์–‘ ์•„์ด์ฝ˜์ด ์ƒ๊ธฐ๋Š”๋ฐ ํด๋ฆญํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์€ ํ™”๋ฉด์ด ๋‚˜์˜จ๋‹ค. ๊ทธ๋ฆฌ๊ณ  devtools๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๊ฐœ๋ฐœํ™˜๊ฒฝ์—์„œ๋งŒ ์ œ๊ณต๋˜๋ฏ€๋กœ ํ”„๋กœ๋•์…˜ ๋นŒ๋“œ ์ค‘์— ์ œ์™ธํ•˜๋Š” ๊ฒƒ์— ๋Œ€ํ•ด ๊ฑฑ์ •ํ•  ํ•„์š” ์—†๋‹ค.

์ฐธ๊ณ  : https://react-query-v3.tanstack.com/devtools


๐Ÿง˜โ€โ™€๏ธ ์ด์ œ ์„ธํŒ…์ด ์™„๋ฃŒ๋์œผ๋‹ˆ ๋ณธ๊ฒฉ์ ์œผ๋กœ ์‚ฌ์šฉ๋ฒ•์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์ž

react-query ์‚ฌ์šฉ ์ธํ„ฐํŽ˜์ด์Šค

const { data } = useQuery("์ฟผ๋ฆฌ๋ช…", ์ฟผ๋ฆฌํ•จ์ˆ˜ = ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ํ•จ์ˆ˜, ์˜ต์…˜);
  • ์ฒซ ๋ฒˆ์งธ ์ธ์ˆ˜ : ์ฟผ๋ฆฌ๋ช…์„ ์„ ์–ธ -> ๊ณ ์œ ํ•œ ํ‚ค๊ฐ’์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹น
  • ๋‘ ๋ฒˆ์งธ ์ธ์ˆ˜ : ๋ฐ์ดํ„ฐ ํ•จ์ˆ˜๋ฅผ ์„ ์–ธ
  • ์„ธ ๋ฒˆ์งธ ์ธ์ˆ˜ : ์ฒซ๋ฒˆ์งธ์™€ ๋‘๋ฒˆ์งธ ์ธ์ˆ˜๋Š” ํ•„์ˆ˜์ง€๋งŒ ์„ธ๋ฒˆ์งธ๋Š” ์˜ต์…˜์ด๋‹ค. ์ด ๊ตฌ๊ฐ„์—๋Š” staleTime, cacheTime๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

๐Ÿ”ฅ staleTime ๋ž€?

const { data, isError, isLoading } = useQuery("posts", fetchPosts, {staleTime: 2000})

if(isLoading) return <h3>๋กœ๋”ฉ์ค‘...</h3>
  • ์ด๋ ‡๊ฒŒ ์˜ต์…˜์„ ์คŒ์œผ๋กœ์จ 2์ดˆ๋™์•ˆ fresh์˜€๋‹ค๊ฐ€ -> stale๋กœ ๋ณ€ํ•œ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.
  • ๋ฐ์ดํ„ฐ๊ฐ€ ๋งŒ๋ฃŒ๋˜์ง€ ์•Š์œผ๋ฉด ๋ฆฌํŽ˜์นญ์€ ์‹คํ–‰๋˜์ง€ ์•Š๋Š”๋‹ค. ๋ฐ์ดํ„ฐ๊ฐ€ ๋งŒ๋ฃŒ๋œ ๊ฒฝ์šฐ์—๋งŒ ์‹คํ–‰๋œ๋‹ค.
  • staleTime์˜ ๊ธฐ๋ณธ๊ฐ’์€ ์™œ 0ms์ด๋ƒ๋ฉด, ๋ฐ์ดํ„ฐ๋Š” ํ•ญ์ƒ ๋งŒ๋ฃŒ ์ƒํƒœ์ด๋ฏ€๋กœ ์„œ๋ฒ„์—์„œ ๋‹ค์‹œ ๊ฐ€์ ธ์™€์•ผ ํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•œ๋‹ค๋Š” ๋œป์ด๋‹ค.
  • staleTime ์œˆ๋„์šฐ๊ฐ€ ๋‹ค์‹œ ํฌ์ปค์Šค๋  ๋•Œ ๊ฐ™์€ ํŠน์ • ํŠธ๋ฆฌ๊ฑฐ์—์„œ ์ฟผ๋ฆฌ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์‹œ ๊ฐ€์ ธ์˜ฌ์ง€ ๊ฒฐ์ •ํ•œ๋‹ค.
    ๐Ÿ‘‰ ํŠธ๋ฆฌ๊ฑฐ๋ž€? ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ€ ๋ฏธ๋ฆฌ ์ •ํ•ด๋†“์€ ์กฐ๊ฑด์„ ๋งŒ์กฑํ•˜๊ฑฐ๋‚˜ ์–ด๋–ค ๋™์ž‘์ด ์ˆ˜ํ–‰๋˜๋ฉด ์ž๋™์ ์œผ๋กœ ์ˆ˜ํ–‰๋˜๋Š” ๋™์ž‘์ด๋‹ค.

์ ์šฉ๋๋Š”์ง€๋ฅผ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๊ฐœ๋ฐœ์ž๋„๊ตฌ์—์„œ 2์ดˆ ๋’ค์— ๋ฐ์ดํ„ฐ๊ฐ€ ํŒจ์นญ๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•ด๋ณผ ์ˆ˜ ์žˆ๋‹ค !

๐Ÿ”ฅ cacheTime ?

  • ์บ์‹œ๋Š” ๋‚˜์ค‘์— ๋‹ค์‹œ ํ•„์š”ํ•  ์ˆ˜๋„ ์žˆ๋Š” ๋ฐ์ดํ„ฐ์šฉ์ด๋‹ค.

  • cacheTime์€ ๋ฐ์ดํ„ฐ๊ฐ€ ๋น„ํ™œ์„ฑํ™”๋œ ์ดํ›„ ๋‚จ์•„ ์žˆ๋Š” ์‹œ๊ฐ„์„ ๋งํ•œ๋‹ค.
    ํŠน์ • ์ฟผ๋ฆฌ์— ๋Œ€ํ•œ ํ™œ์„ฑ useQuery๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ, ํ•ด๋‹น ๋ฐ์ดํ„ฐ๋Š” ์ฝœ๋“œ ์Šคํ† ๋ฆฌ์ง€๋กœ ์ด๋™ํ•œ๋‹ค.
    ๐Ÿ‘‰ ์ฝœ๋“œ ์Šคํ† ๋ฆฌ์ง€๋ž€? ๋‚จ๊ฒจ์ง„ ๋ฐ์ดํ„ฐ๋ฅผ ๋งํ•จ

  • ๊ตฌ์„ฑ๋œ cacheTime์ด ์ง€๋‚˜๋ฉด ์บ์‹œ์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ๋งŒ๋ฃŒ๋˜๋ฉฐ ์œ ํšจ ์‹œ๊ฐ„์˜ ๊ธฐ๋ณธ๊ฐ’์€ 5๋ถ„์ด๋‹ค.

  • ์บ์‹œ๊ฐ€ ๋งŒ๋ฃŒ๋˜๋ฉด ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰์…˜์ด ์‹คํ–‰๋˜๊ณ  ํด๋ผ์ด์–ธํŠธ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค.
    ๋ฐ์ดํ„ฐ๊ฐ€ ์บ์‹œ์— ์žˆ๋Š” ๋™์•ˆ์—๋Š” ํŽ˜์นญํ•  ๋•Œ ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ๋‹ค.

  • ์„œ๋ฒ„์˜ ์ตœ์‹  ๋ฐ์ดํ„ฐ๋กœ ์ƒˆ๋กœ ๊ณ ์นจ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.


const { data, isError, isLoading, error } = useQuery("posts", fetchPosts, {staleTime: 2000})

if(isLoading) return <h3>๋กœ๋”ฉ์ค‘...</h3>
if(isError) return <h3>์ž˜๋ชป๋œ ๋ฐ์ดํ„ฐ์ž…๋‹ˆ๋‹ค. {error.toString()}</h3>

useQuery์—์„œ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์€ ๊ธฐ๋Šฅ๋“ค์„ ๊ตฌ์กฐ ๋ถ„ํ•ด ํ• ๋‹นํ•˜์—ฌ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

  • data : ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ด
  • isError : ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ฌ ๋•Œ ์˜ค๋ฅ˜๊ฐ€ ์žˆ๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ์•Œ๋ ค์ค€๋‹ค.
  • isLoading : ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ์ค‘
  • isFetching : ๋น„๋™๊ธฐ ์ฟผ๋ฆฌ๊ฐ€ ํ•ด๊ฒฐ๋˜์ง€ ์•Š์•˜์Œ์„ ์˜๋ฏธ
  • error : ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด์—ฌ์ค€๋‹ค.

์ด๊ฒƒ ์™ธ์—๋„ ์—ฌ๋Ÿฌ ๊ธฐ๋Šฅ๋“ค์ด ์žˆ์œผ๋‹ˆ ๊ณต์‹๋ฌธ์„œ ํ™•์ธํ•˜๋ฉด ๋œ๋‹ค.

๐Ÿ”‘ ์ฟผ๋ฆฌํ‚ค

์–ด๋– ํ•œ ํŠธ๋ฆฌ๊ฑฐ๊ฐ€ ์žˆ์–ด์•ผ๋งŒ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์‹œ ๊ฐ€์ ธ์˜ค๊ฒŒ ๋œ๋‹ค.
๊ทธ ์–ด๋– ํ•œ ํŠธ๋ฆฌ๊ฑฐ๋ž€ ? ๐Ÿ‘‡

  • ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋‹ค์‹œ ๋งˆ์šดํŠธํ•˜๊ฑฐ๋‚˜ ์œˆ๋„์šฐ๋ฅผ ๋‹ค์‹œ ํฌ์ปค์Šคํ•  ๋•Œ
  • useQuery์—์„œ ๋ฐ˜ํ™˜๋˜์–ด ์ˆ˜๋™์œผ๋กœ ๋ฆฌํŽ˜์นญ์„ ์‹คํ–‰ํ•  ๋•Œ
  • ์ง€์ •๋œ ๊ฐ„๊ฒฉ์œผ๋กœ ๋ฆฌํŽ˜์นญ์„ ์ž๋™ ์‹คํ–‰ํ•  ๋•Œ
  • ๋ณ€์ด๋ฅผ ์ƒ์„ฑํ•œ ๋’ค ์ฟผ๋ฆฌ๋ฅผ ๋ฌดํšจํ™” ํ•  ๋•Œ
  • ํด๋ผ์ด์–ธํŠธ์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ์„œ๋ฒ„์˜ ๋ฐ์ดํ„ฐ์™€ ๋ถˆ์ผ์น˜ํ•  ๋•Œ ๋ฆฌํŽ˜์นญ ํ•  ๊ฒฝ์šฐ
  • ์ฟผ๋ฆฌ๋Š” ๊ฒŒ์‹œ๋ฌผ ์•„์ด๋””๋ฅผ ํฌํ•จํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ฟผ๋ฆฌ๋ณ„๋กœ ์บ์‹œ๋ฅผ ๋‚จ๊ธธ ์ˆ˜ ์žˆ๋‹ค.
  • ๊ฐ ์ฟผ๋ฆฌ์— ํ•ด๋‹นํ•˜๋Š” ์บ์‹œ๋ฅผ ๊ฐ€์ง€๊ฒŒ ๋œ๋‹ค.
  • ๊ฐ ๊ฒŒ์‹œ๋ฌผ์— ๋Œ€ํ•œ ์ฟผ๋ฆฌ์— ๋ผ๋ฒจ์„ ์„ค์ •ํ•ด์ฃผ๋ฉด ๋œ๋‹ค. ๋ฐ”๋กœ ์ฟผ๋ฆฌ ํ‚ค์— ๋ฌธ์ž์—ด ๋Œ€์‹  ๋ฐฐ์—ด์„ ์ „๋‹ฌํ•˜๋ฉด ๊ฐ€๋Šฅํ•˜๋‹ค.
 const { data, isLoading, isError, error } = useQuery(
    ["comments", post.id], () => fetchComments(post.id)
 );

์ด์ฒ˜๋Ÿผ ์˜์กด์„ฑ ๋ฐฐ์—ด๋กœ ์ทจ๊ธ‰ํ•ด์„œ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค. comments๋ฅผ ์ฟผ๋ฆฌํ‚ค๋กœ ์ง€์ •ํ•œ ๊ฒƒ์ด๋‹ค.
๊ทธ๋ฆฌ๊ณ  post.id๊ฐ€ ์—…๋ฐ์ดํŠธ ๋˜๋ฉด ๋ฆฌ์•กํŠธ์ฟผ๋ฆฌ๊ฐ€ ์ƒˆ ์ฟผ๋ฆฌ๋ฅผ ๋งŒ๋“ค๊ณ  staleTime๊ณผ cacheTime์„ ๊ฐ–๊ฒŒ ๋œ๋‹ค.


1. Prefetching

  • ๋ฐ์ดํ„ฐ๋ฅผ ๋ฏธ๋ฆฌ ๊ฐ€์ ธ์™€ ์บ์‹œ์— ๋„ฃ๋Š”๋‹ค.
  • ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ๋งŒ๋ฃŒ (stale) ์ƒํƒœ๋ฅผ ๋‚˜ํƒ€๋‚ธ๋‹ค.
  • useQueryClient ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
import { useQuery, ๐ŸŒŸuseQueryClient } from "react-query";

  const queryClient = useQueryClient();

  useEffect(() => {
    // 10ํŽ˜์ด์ง€์— ์žˆ๋‹ค๋ฉด ๋ฏธ๋ฆฌ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜จ ํ•„์š”๊ฐ€ ์—†๋‹ค.
    if (currentPage < maxPostPage) {
      const nextPage = currentPage + 1;

      queryClient.prefetchQuery(["posts", nextPage], () =>
        fetchPosts(nextPage)
      );
    }
  }, [currentPage, queryClient]);

    const { data, isError, isLoading, error } = useQuery(
    // ๋ฐฐ์—ด์— ๋‹ด๊ธด ์ฒซ ๋ฒˆ์งธ ์š”์†Œ๋ฅผ ์ฟผ๋ฆฌํ‚ค๋ผ๊ณ  ํ•œ๋‹ค.
    ["posts", currentPage],
    // ์ด ๋ฐฐ์—ด์ด ๋ฐ”๋€Œ๋ฉด ํ•จ์ˆ˜๋„ ๋ฐ”๋€Œ๊ธฐ ๋•Œ๋ฌธ์— ๋ฐ์ดํ„ฐ๊ฐ€ ๋ฐ”๋€” ์ˆ˜๋ฐ–์— ์—†๋‹ค.
    () => fetchPosts(currentPage), // -> ํ•จ์ˆ˜์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ’์„ currentPage๋กœ ํ•จ
    {
      staleTime: 2000,
      // ์ฟผ๋ฆฌํ‚ค๊ฐ€ ๋ฐ”๊ปด๋„ ์ง€๋‚œ ๋ฐ์ดํ„ฐ๋ฅผ ์œ ์ง€ํ•ด์„œ ์ด์ „ ํŽ˜์ด์ง€๋กœ ๋Œ์•„๊ฐ”์„ ๋•Œ ์บ์‹œ์— ํ•ด๋‹น ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๋„๋ก ํ•ด์ค€๋‹ค.
      keepPreviousData: true,
    }
  );

Dev tools๋ฅผ ํ™•์ธํ•ด๋ณด๋ฉด "posts", 9๊ฐ€ "posts", 8๋ณด๋‹ค ๋” ๋ฏธ๋ฆฌ ํŒจ์นญ ๋œ ๊ฒƒ์„ ๋ณด์•„ ๋ฏธ๋ฆฌ ๋ฐ์ดํ„ฐ๋ฅผ fetch ๋œ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๊ฒƒ์ด ๋ฐ”๋กœ ํ”„๋ฆฌํŒจ์นญ์ด๋ผ๊ณ  ํ•œ๋‹ค.


2. Mutations (๋ณ€์ด)

  • ๋ณ€์ด๋Š” ์„œ๋ฒ„์— ๋ฐ์ดํ„ฐ๋ฅผ ์—…๋ฐ์ดํŠธ ํ•˜๋„๋ก ์„œ๋ฒ„์— ๋„คํŠธ์›Œํฌ ํ˜ธ์ถœ์„ ์‹ค์‹œํ•œ๋‹ค.
  • ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€, ์‚ญ์ œ, ์—…๋ฐ์ดํŠธ์— ํ•ด๋‹น๋œ๋‹ค.
  • ์„œ๋ฒ„๋ฅผ ์‹ค์ œ๋กœ ์—…๋ฐ์ดํŠธ ํ•˜์ง€๋Š” ์•Š๋Š”๋‹ค ! ๋ณ€์ด๋ฅผ ์ƒ์„ฑํ•˜๋Š” ์„œ๋ฒ„ ํ˜ธ์ถœ์„ ์ „์†กํ•˜๊ฒŒ ๋œ๋‹ค.
  • ๊ฐ’์ด false์ผ ๊ฒฝ์šฐ ๋กค๋ฐฑ ์ง„ํ–‰
  • ์—…๋ฐ์ดํŠธ ๋œ ํ•ด๋‹น ๋ฐ์ดํ„ฐ๋กœ ๋ฆฌ์•กํŠธ์ฟผ๋ฆฌ ์บ์‹œ๋ฅผ ์—…๋ฐ์ดํŠธ ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.
  • ๊ด€๋ จ ์ฟผ๋ฆฌ๋ฅผ ๋ฌดํšจํ™” ํ•  ์ˆ˜ ์žˆ๋‹ค. -> ์„œ๋ฒ„์—์„œ ๋ฆฌํŽ˜์น˜๋ฅผ ๊ฐœ์‹œํ•˜์—ฌ ํด๋ผ์ด์–ธํŠธ์— ์žˆ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์„œ๋ฒ„์˜ ๋ฐ์ดํ„ฐ์™€ ์ตœ์‹  ์ƒํƒœ๋กœ ์œ ์ง€ํ•œ๋‹ค.

์ ์šฉ ์˜ˆ์‹œ

 import { useQuery, ๐Ÿ‘‰ useMutation, useQueryClient } from "react-query";

 const deleteMutation = useMutation((postId) => deletePost(postId));
 const updateMutation = useMutation((postId) => updatePost(postId));

 <button onClick={() => deleteMutation.mutate(post.id)}>์‚ญ์ œ</button>
 <button onClick={() => updateMutation.mutate(post.id)}>์—…๋ฐ์ดํŠธ</button>

๋˜ ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์œผ๋กœ๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ๋ฐฉ๋ฒ•์œผ๋กœ ์ ์šฉํ•ด๋ณผ ์ˆ˜ ์žˆ๋‹ค.
import { useMutation } from '@tanstack/react-query';

// ๋Œ“๊ธ€ ์‚ญ์ œ
const { mutate } = useMutation((id: any) => deleteComment(id));

const handleCommentDelete = () => {
        confirm('๋Œ“๊ธ€์„ ์‚ญ์ œํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?');
       
        mutate(userId, {
            // ๋ฐ์ดํ„ฐ ์š”์ฒญ์— ์„ฑ๊ณตํ–ˆ์„ ๊ฒฝ์šฐ
            onSuccess: () => {
                alert('์‚ญ์ œ ์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.');
            },
        });
    };

3. infinite scroll ๋ฌดํ•œ์Šคํฌ๋กค

  • ์‚ฌ์šฉ์ž๊ฐ€ ์Šคํฌ๋กค ํ•  ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค. (๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฅผ ํ•œ ๋ฒˆ์— ๊ฐ€์ ธ์˜ค๋Š” ๊ฒƒ๋ณด๋‹ค ํ›จ์”ฌ ํšจ์œจ์ )
    ex) ์ธ์Šคํƒ€๊ทธ๋žจ, ํŽ˜์ด์Šค๋ถ, ํŠธ์œ„ํ„ฐ๊ฐ™์€ ํ”Œ๋žซํผ์„ ์˜ˆ์‹œ๋กœ ๋“ค ์ˆ˜ ์žˆ๋‹ค.
  • useInfiniteQuery ๋ผ๋Š” ํ›…์„ ์‚ฌ์šฉํ•œ๋‹ค.
  • ์—…๋ฐ์ดํŠธ๋œ ์ƒํƒœ๊ฐ€ ์ฟผ๋ฆฌ ํ‚ค๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๊ณ  ์ฟผ๋ฆฌ ํ‚ค๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ์—…๋ฐ์ดํŠธ ํ•œ๋‹ค.

์ ์šฉ ์˜ˆ์‹œ

  • useInfiniteQuery ํ›… ์‚ฌ์šฉ ์ธํ„ฐํŽ˜์ด์Šค
useInfiniteQuery(['์ฟผ๋ฆฌ๋ช…'], ({ pageParam = defaultUrl}) => ๋ฐ์ดํ„ฐํ•จ์ˆ˜(pageParam))

  • useInfiniteQueryํ›…์˜ ์ธํ„ฐํŽ˜์ด์Šค์—์„œ data ๊ฐ์ฒด๋Š” ๋‘ ๊ฐœ์˜ ํ”„๋กœํผํ‹ฐ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค.
const { โญ๏ธ data, fetchNextPage, hasNextPage, isLoading, isError } = useInfiniteQuery(
        ['page'],
        ({ pageParam = 0 }) => getFeedPost({ page: pageParam, content: searchText, view: 5 }),
        {
            getNextPageParam: (lastPage, allPosts) => {
                return lastPage.page !== allPosts[0].totalPage ? lastPage.page + 1 : undefined;
            },
        },
    );

(1) pages: ๋ฐ์ดํ„ฐ์— ํ•ด๋‹น
(2) pageParams: ์ด๊ฒƒ์€ ๊ฐ ํŽ˜์ด์ง€์˜ ์ฟผ๋ฆฌ ํ•จ์ˆ˜์— ์ „๋‹ฌ๋˜๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜์ด๋‹ค.

  • ๋ชจ๋“  ์ฟผ๋ฆฌ๋Š” ํŽ˜์ด์ง€ ๋ฐฐ์—ด์— ๊ณ ์œ ํ•œ ์š”์†Œ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๊ณ  ๊ทธ ์š”์†Œ๋Š” ํ•ด๋‹น ์ฟผ๋ฆฌ์— ๋Œ€ํ•œ ๋ฐ์ดํ„ฐ์— ํ•ด๋‹นํ•œ๋‹ค.

๋ฐฑ์—”๋“œ ๋ถ„์ด ์ž‘์—…ํ•ด์ฃผ์‹  ํ”ผ๋“œ ๊ฒŒ์‹œ๋ฌผ api ๋ฐ์ดํ„ฐ๋‹ค. ๋ณด๋‹ค์‹œํ”ผ ํŒŒ๋ผ๋ฏธํ„ฐ ๊ฐ’์— page๋ž‘ totalPage์™€ view๊ฐ€ ์žˆ๋‹ค.

  • page : ํŽ˜์ด์ง€ ์ˆ˜๋ฅผ ์˜๋ฏธ
  • totalPage : ์ด ํŽ˜์ด์ง€ ์ˆ˜๋ฅผ ์˜๋ฏธ
  • view : ํ•œ ํŽ˜์ด์ง€์— ๋ช‡๊ฐœ์˜ ๊ฒŒ์‹œ๋ฌผ์„ ๋ณด์—ฌ์ค„ ์ง€

๊ทธ๋ž˜์„œ ํ•ด๋‹น ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ ์œ„ํ•œ ํŒจ์นญ ํ•จ์ˆ˜๋ฅผ ์ƒ์„ฑํ•˜๊ณ  page ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋ฆฌ์•กํŠธ ์ฟผ๋ฆฌ์— pageParams๋กœ ์ ์šฉํ•˜๊ณ , ๋ฆฌ์•กํŠธ ์ฟผ๋ฆฌ์—์„œ ์ œ๊ณตํ•ด์ฃผ๋Š” ํ•จ์ˆ˜๋“ค๋กœ ์ œ์–ดํ•˜์—ฌ ์Šคํฌ๋กค ์œ„์น˜๊ฐ€ ํŽ˜์ด์ง€ ํ•˜๋‹จ์— ๋งž๋‹ฟ์•˜์„ ๋•Œ๋งˆ๋‹ค page๊ฐ€ +1์ด ๋˜๋ฉด์„œ ๋‹ค์Œ ํŽ˜์ด์ง€์˜ ๋ฐ์ดํ„ฐ๋“ค์ด ์Šคํฌ๋กค ํ•  ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กญ๊ฒŒ ๋ถˆ๋Ÿฌ์™€์ ธ ๋ณด์—ฌ์ง€๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ์Šคํฌ๋กค ์œ„์น˜๊ฐ€ ํŽ˜์ด์ง€ ํ•˜๋‹จ์— ๋งž๋‹ฟ์•˜์„ ๋•Œ๋งˆ๋‹ค ์ง์ ‘ ์ฝ”๋“œ๋ฅผ ๊ตฌํ˜„ํ•ด๋„ ์ข‹์ง€๋งŒ ๋‚˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์„ค์น˜ํ•˜์—ฌ ์ •๋ง ๊ฐ„ํŽธํ•˜๊ฒŒ ๊ตฌํ˜„ํ–ˆ๋‹ค. ๋ฐ”๋กœ react-infinite-scroller ๋ผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์„ค์น˜ํ•˜์—ฌ ์ž‘์—…ํ–ˆ๋‹ค.
(์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ์“ฐ๋กœํ‹€๋ง๋„ ๋‚ด์žฌ๋˜์–ด ์žˆ๋‹ค.)

์“ฐ๋กœํ‹€๋ง์ด๋ž€? ๋งˆ์ง€๋ง‰ ํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ๋œ ํ›„ ์ผ์ • ์‹œ๊ฐ„์ด ์ง€๋‚˜๊ธฐ ์ „์— ๋‹ค์‹œ ํ˜ธ์ถœ๋˜์ง€ ์•Š๋„๋ก ํ•˜๋Š” ๊ฒƒ.
์“ฐ๋กœํ‹€๋ง์€ ์Šคํฌ๋กค์„ ์˜ฌ๋ฆฌ๊ฑฐ๋‚˜ ๋‚ด๋ฆด ๋•Œ ๋ณดํ†ต ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค !

// ์„ค์น˜ ๋ช…๋ น์–ด
npm install react-infinite-scroller

ํ•ด๋‹น ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์‚ฌ์šฉ ์„ค๋ช…์„œ ์ฐธ๊ณ 
https://www.npmjs.com/package/react-infinite-scroller


โญ๏ธ ํ”„๋กœ์ ํŠธ์— ์ ์šฉํ•ด๋ณธ ์ฝ”๋“œ

import { useInfiniteQuery } from '@tanstack/react-query';
import InfiniteScroll from 'react-infinite-scroller';

const home = () => {
// ํ”ผ๋“œ API ๋ถˆ๋Ÿฌ์˜ค๋Š” ํ•จ์ˆ˜
  const getFeedPost = async ({ page, content, view, tag }: IDetailPost) => {
    const res = await Axios.get(`/post`, {
        params: {
            page,
            view,
            content,
            tag,
         },
     });
     return res.data.data;
  };

    // ๋ฐ์ดํ„ฐ ํŒจ์นญ
    const { data, fetchNextPage, hasNextPage, isLoading, isError } = useInfiniteQuery(
        ['page', search],
        ({ pageParam = 0 }) => getFeedPost({ page: pageParam, content: search, view: 5 }),
        {
            getNextPageParam: (lastPage, allPosts) => {
                return lastPage.page !== allPosts[0].totalPage ? lastPage.page + 1 : undefined;
            },
        },
    );

    if (isLoading) return <h3>๋กœ๋”ฉ์ค‘</h3>;
    if (isError) return <h3>์ž˜๋ชป๋œ ๋ฐ์ดํ„ฐ ์ž…๋‹ˆ๋‹ค.</h3>;

    return (
        <main>
            {/* ํ”ผ๋“œ ๊ฒŒ์‹œ๋ฌผ */}
            <InfiniteScroll hasMore={hasNextPage} loadMore={() => fetchNextPage()}>
                <FeedItem data={data} />}
            </InfiniteScroll>
        </main>
    );
};

export default home;
  • pageParam : ๊ธฐ๋ณธ ์ดˆ๊ธฐ๊ฐ’์œผ๋กœ ์ง€์ •. fetchNextPage๊ฐ€ ์ด pageParam์˜ ๋‹ค์Œ ํŽ˜์ด์ง€๊ฐ€ ์žˆ๋Š”์ง€ ๊ฒฐ์ •ํ•œ๋‹ค.
  • hasNextPage : ์ˆ˜์ง‘ํ•  ๋ฐ์ดํ„ฐ๊ฐ€ ๋” ์žˆ๋Š”์ง€๋ฅผ ๊ฒฐ์ •ํ•˜๋Š” boolean ํ˜•ํƒœ๋‹ค.
  • fetchNextPage : ๋” ๋งŽ์€ ๋ฐ์ดํ„ฐ๊ฐ€ ํ•„์š”ํ•  ๋•Œ, ์–ด๋Š ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•˜๋Š”์ง€๋Š” infiniteScroll์— ์ง€์‹œํ•˜๋Š” ์—ญํ• ์„ ํ•œ๋‹ค. ๊ทธ๋ž˜์„œ <InfiniteScroll> ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ loadMore์— ์ ์šฉ์‹œํ‚ค๋ฉด, ๋” ๋ถˆ๋Ÿฌ์˜ฌ ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ์„ ๋•Œ๋งˆ๋‹ค ์•Œ์•„์„œ ๋กœ๋“œํ•ด์ค€๋‹ค.
  • getNextPageParam : ๋‹ค์Œ ํŽ˜์ด์ง€๋ฅผ ๊ฐ€์ ธ์˜ค๊ธฐ ์œ„ํ•œ ํ•จ์ˆ˜์ด๋‹ค. ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ lastPage์™€ allPosts๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. lastpage๋Š” ๋‹ค์Œ ํŽ˜์ด์ง€๋ฅผ ์˜๋ฏธํ•˜๊ณ , allPosts๋Š” ์ด ํฌ์ŠคํŠธ ์ˆ˜๋ฅผ ์˜๋ฏธํ•œ๋‹ค.
getNextPageParam: (lastPage, allPosts) => {
                return lastPage.page !== allPosts[0].totalPage ? lastPage.page + 1 : undefined;
            },

์Šคํฌ๋กค์„ ํ•  ๋•Œ๋งˆ๋‹ค ํ˜„์žฌ ํŽ˜์ด์ง€๊ฐ€ allPosts(์ด ํŽ˜์ด์ง€ ์ˆ˜)์™€ ๊ฐ™์ง€ ์•Š๋‹ค๋ฉด page + 1์„ ํ•ด์ฃผ์–ด ์ƒˆ๋กœ์šด ํŽ˜์ด์ง€๋ฅผ ๊ณ„์†ํ•ด์„œ ๋ถˆ๋Ÿฌ์˜ค๊ณ , ํ˜„์žฌ ํŽ˜์ด์ง€์™€ allPosts์˜ ํŽ˜์ด์ง€ ์ˆ˜๊ฐ€ ๊ฐ™๋‹ค๋ฉด undefined๋ฅผ ํ•˜์—ฌ ๋ฐ์ดํ„ฐ ํ˜ธ์ถœ์ด ๋ฉˆ์ถฐ์ง€๋„๋ก ํ–ˆ๋‹ค.


FeedItem ์ปดํฌ๋„ŒํŠธ ์ฝ”๋“œ

const FeedItem = ({ data }: any) => {
    return (
        <div css={FeedItemStyle}>
            {data.pages.map((page: PageInfo) => {
                return page.list.map((item: FeedItemProps) => {
                    return (
                        <div className="feedLayout" key={item.id}>
                            <div className="feedLayout__bg">
                                <ItemHeader
                                    nickname={item.writer?.nickname}
                                    mbti={item.writer?.mbti}
                                    level={item.writer?.level}
                                    profileImage={item.writer?.profileImage}
                                    createAt={item.createAt}
                                />
                                <Link href={`/home/${item.id}`}>
                                    <ItemContent
                                        id={item.id}
                                        title={item.title}
                                        content={item.content}
                                        pollList={item.pollList && item.pollList}
                                        tags={item.tags && item.tags}
                                    />
                                </Link>

                                <ItemFooter like={item.like?.count} command={item.command?.count} bookmark={item.bookmark?.count} />
                            </div>
                        </div>
                    );
                });
            })}
        </div>
    );
};

export default FeedItem;

์ฝ”๋“œ ๊ธธ์–ด์ง€๋Š”๊ฒŒ ์‹ซ์–ด์„œ ์ปดํฌ๋„ŒํŠธ๋กœ ์ชผ๊ฐœ๋‘ !


๊ฒฐ๊ณผ๋ฌผ

react-query์—์„œ useInfiniteQuery ๋กœ ๋ฌดํ•œ ์Šคํฌ๋กค์„ ๊ตฌํ˜„ํ•œ ๊ฒฐ๊ณผ๋ฌผ์ž…๋‹ˆ๋‹ค.

์กฐ๋งŒ๊ฐ„ ๊ฐ€์˜คํ”ˆ์œผ๋กœ ๋ฐฐํฌํ•  ์˜ˆ์ •....................
์ง„์งœ ์ง„์งœ ์ตœ์ข… ์˜คํ”ˆ์€ 5์›”์ด๋‹ค ํ™”์ดํŒ… ๐Ÿฅณ

profile
์•„๋Š‘ํ•œ ๋‡Œ๊ณต๊ฐ„ ๐Ÿง 
post-custom-banner

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

comment-user-thumbnail
2023๋…„ 4์›” 7์ผ

์ข‹์€ ๊ธ€ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค! ์ฝ”๋“œ ๋ธ”๋ก ์‹œ์ž‘ ๋ถ€๋ถ„
+``` ๋’ค์— javascript ๋‚˜ typescript ์ž…๋ ฅํ•˜์‹œ๋ฉด ์ฝ”๋“œ ๊ฐ€๋…์„ฑ์ด ์ข‹์•„์งˆ ๊ฒƒ ๊ฐ™์•„์š”!

1๊ฐœ์˜ ๋‹ต๊ธ€
comment-user-thumbnail
2023๋…„ 6์›” 21์ผ

๋Œ“๊ธ€ ๋ณด์‹ค์ง€ ๋ชจ๋ฅด๊ฒ ๋Š”๋ฐ.. ํ•œ ๋ฒˆ ๋‚จ๊ฒจ๋ด…๋‹ˆ๋‹ค.
์ œ๊ฐ€ ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ ์ค‘์ธ๋ฐ, react-query๋กœ ๋ฌดํ•œ ์Šคํฌ๋กค์„ ์ ์šฉํ–ˆ๋‹ค๊ฐ€ ์ตœ๊ทผ์— ๊ด€๋ จ๋œ ๋ถ€๋ถ„์„ ๋‹ค์‹œ ์ž‘์—…ํ•  ์ผ์ด ์ƒ๊ฒผ์Šต๋‹ˆ๋‹ค.
api ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋ฉด re-rendering์ด ๋˜๋Š” ๊ฑด ๋‹น์—ฐํ•œ๋ฐ, ์™œ ์ด์ „์— ๋ฐ›์•„์˜จ ๋ฐ์ดํ„ฐ๋“ค๊นŒ์ง€ re-rendering์ด ๋˜๋Š”์ง€ ํ˜น์‹œ ์–ด๋–ป๊ฒŒ ํ•ด๊ฒฐํ•˜๋Š”์ง€ ์•„์‹ค๊นŒ์š”?

๋” ๊ตฌ์ฒด์ ์œผ๋กœ ๋ง์”€๋“œ๋ฆฌ๋ฉด, VideoList ์ปดํฌ๋„ŒํŠธ ๋‚ด์—์„œ VideoItem์ปดํฌ๋„ŒํŠธ ๋ชฉ๋ก์„ ๋ Œ๋”๋ง ํ•ด์ฃผ๋Š”๋ฐ, ์ด VideoList๊ฐ€ ๋ฌดํ•œ์Šคํฌ๋กค์ด ์ ์šฉ๋œ ์ƒํ™ฉ์ž…๋‹ˆ๋‹ค. ์ฒ˜์Œ์—๋Š” ๋ Œ๋”๋ง์ด ์ผ์–ด๋‚  ์ˆ˜ ๋ฐ–์— ์—†๋Š”๋ฐ, ์ดํ›„์— ๋‹ค์Œ ํŽ˜์ด์ง€์— ํ•ด๋‹นํ•˜๋Š” ํ† ํฐ์„ ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ ๋ฐ›์•„์„œ ๋‹ค์Œ api ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์˜จ ๋‹ค์Œ์— ํ™”๋ฉด์— ๋ฟŒ๋ ค์ค„ ๋•Œ ์™œ ์ „์ฒด ๋ Œ๋”๋ง์ด ํ•œ ๋ฒˆ ๋” ์ผ์–ด๋‚˜๋Š”์ง€... ํ˜น์‹œ ์•„์‹ ๋‹ค๋ฉด ๋‹ต์žฅ ๋ถ€ํƒ๋“œ๋ฆฝ๋‹ˆ๋‹ค.

๋‹ต๊ธ€ ๋‹ฌ๊ธฐ