useQuery์˜ isLoading์ด SSR์—์„œ๋„ true๊ฐ€ ๋  ์ˆ˜ ์žˆ์„๊นŒ?

 

์ตœ๊ทผ ๋™๋ฃŒ๋ถ„์ด "SSR ํŽ˜์ด์ง€์—์„œ ๋ฏธ๋ฆฌ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์˜ค๋Š” ๊ฒฝ์šฐ useQuery ์˜ isLoading ์ด true ์ธ ๊ฒฝ์šฐ๊ฐ€ ์žˆ์–ด์š”?" ๋ผ๋Š” ์งˆ๋ฌธ์„ ๋˜์กŒ๋Š”๋ฐ, ์ด ์งˆ๋ฌธ์— ๋Œ€ํ•œ ๋Œ€๋‹ต์„ ์ƒ๊ฐํ•˜๋‹ค๊ฐ€ ์—ฌ๋Ÿฌ ์˜๋ฌธ์ด ์ƒ๊ฒจ ๊ธ€๋กœ ์ •๋ฆฌํ•ด๋ณธ๋‹ค.

 

์šฉ์–ด ์ •๋ฆฌ

- ์„œ๋ฒ„์‚ฌ์ด๋“œ : ์›น์„œ๋ฒ„์˜ ์„œ๋ฒ„ ์ชฝ ์ฝ”๋“œ ์‹คํ–‰

- ํด๋ผ์ด์–ธํŠธ์‚ฌ์ด๋“œ : ์›น์„œ๋ฒ„์˜ ํด๋ผ์ด์–ธํŠธ ์ชฝ ์ฝ”๋“œ ์‹คํ–‰

- SSR : ServerSideRendering

- CSR : ClientSideRendering

- ์„œ๋ฒ„ : api ๋ฅผ ์ œ๊ณตํ•˜๋Š” ์‹ค์ œ ์„œ๋น„์Šค ์„œ๋ฒ„

 

์˜ˆ์‹œ ์ฝ”๋“œ: SSR + useQuery

๋‚˜๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ์ฝ”๋“œ๋ฅผ ์ฃผ๋กœ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์—ˆ๋‹ค.

// Next.js ์˜ ํŽ˜์ด์ง€ ์ปดํฌ๋„ŒํŠธ

export const getServerSideProps = async () => {
  const data = await fetchData();
  return { props: { dehydratedState: dehydrate(queryClient) } };
};

const Page = () => {
  const { data, isLoading } = useQuery(['key'], fetchData);

  if (isLoading) {
    return <Spin />;
  }

  return <div>{data}</div>;
};

 

getServerSideProps ๋‚ด๋ถ€ ๋กœ์ง์€ ์›น์„œ๋ฒ„(์„œ๋ฒ„์‚ฌ์ด๋“œ)์—์„œ ์‹คํ–‰๋œ๋‹ค.

์„œ๋ฒ„์‚ฌ์ด๋“œ์—์„œ ์„œ๋ฒ„์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฏธ๋ฆฌ ๋ฐ›์•„์˜ค๊ณ , dehydrate ๋ฅผ ํ†ตํ•ด React Query ์บ์‹œ์— ๋„ฃ์–ด์ค€๋‹ค.

์„œ๋ฒ„์‚ฌ์ด๋“œ์—์„œ ๋ฏธ๋ฆฌ ๋ฐ์ดํ„ฐ๋ฅผ ๋„ฃ์–ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์—, ํด๋ผ์ด์–ธํŠธ์—์„œ๋Š” useQuery ์˜ ๋ฐ˜ํ™˜๊ฐ’์ธ isLoading ์ด ํ•ญ์ƒ false ์ด๋‹ค.

 

๊ทธ๋ ‡๋‹ค๋ฉด ์ด ์ฝ”๋“œ๊ฐ€ ์˜๋ฏธ๋Š” ์—†์—ˆ๋˜๊ฑธ๊นŒ? ๐Ÿค”

 

๋ฐ˜๋ฉด CSR์˜ ๊ฒฝ์šฐ๋Š”?

CSR ํŽ˜์ด์ง€์—์„œ๋Š” ์„œ๋ฒ„์‚ฌ์ด๋“œ์—์„œ ๋‚ด๋ ค์˜ค๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์—, useQuery๋Š” ํด๋ผ์ด์–ธํŠธ์—์„œ ์ฒ˜์Œ์œผ๋กœ fetching ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค. ๋”ฐ๋ผ์„œ ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ ์ „์—๋Š” isLoading ๊ฐ’์ด true ์ด๋‹ค. ์ฆ‰, isLoading ์„ ๊ฐ€์ง€๊ณ  ๋กœ๋”ฉ UI ๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ์ฝ”๋“œ๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

 

SSR ํŽ˜์ด์ง€ ๊ฐ„ ์ด๋™ ์‹œ์˜ ๋™์ž‘์€?

๊ฐ™์€ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋‹ค๋ฅธ ํŽ˜์ด์ง€๋กœ ์ด๋™ํ•  ๋•Œ, Next.js ์—์„œ๋Š” ๋ณดํ†ต Next Link ๋‚˜ Next Router๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ด๋™ํ•œ๋‹ค.

export const Home = () => {
  return (
    <div>
      <h1>Home</h1>
      <Link href="/about" prefetch={true}>
        Go to About
      </Link>
    </div>
  );
}

 

import { useEffect } from 'react';
import { useRouter } from 'next/router';

export const Home = () => {
  const router = useRouter();

  useEffect(() => {
    router.prefetch('/about');
  }, [router]);

  const goToAbout = () => {
    router.push('/about');
  };

  return (
    <div>
      <h1>Home</h1>
      <button onClick={goToAbout}>Go to About</button>
    </div>
  );
};

 

์ด ๋•Œ, SSR ํŽ˜์ด์ง€๋กœ ์ด๋™ ์‹œ prefetch ๋™์ž‘์ด Next ์˜ ๋ฒ„์ „์— ๋”ฐ๋ผ ๋‹ค๋ฅด๋‹ค.

 

ํ•ญ๋ชฉ Next.js 12 ์ดํ•˜ Next.js 13
prefetch ๋™์ž‘ _next/data JSON ๋ฏธ๋ฆฌ ๋ฐ›์Œ ์—†์Œ
ํด๋ฆญ ์‹œ ์‘๋‹ต ๋ฐฉ์‹ ํด๋ผ์ด์–ธํŠธ์—์„œ hydrate ์„œ๋ฒ„๊ฐ€ HTML์„ ๋ Œ๋”๋ง

์ด์ „์—๋Š” SSR ํŽ˜์ด์ง€์— ๋Œ€ํ•œ Next Link ๊ฐ€ ์žˆ๋‹ค๋ฉด, ๋ฏธ๋ฆฌ ํ•ด๋‹น ํŽ˜์ด์ง€์˜ ์„œ๋ฒ„ ์‚ฌ์ด๋“œ ๋กœ์ง์„ ์‹คํ–‰ํ•˜์—ฌ pageProps ๋ฐ›์•„๋‘์—ˆ๋‹ค.

์‚ฌ๋‹ด์œผ๋กœ ์ด๋กœ ์ธํ•ด ์ด์Šˆ๋ฅผ ๊ฒช์€ ์ ๋„ ์žˆ๋Š”๋ฐ, ๋ฐฐํฌ๋ฅผ ํ•˜๋ฉด ์ด pageProps๋ฅผ ๋ฐ›์•„์˜ค๋Š” hash ๊ฐ’์ด ๋ฐ”๋€Œ๊ฒŒ ๋˜์–ด, ์œ ์ €๊ฐ€ ๋ณด๊ณ  ์žˆ๋˜ ํŽ˜์ด์ง€์˜ Next LInk prefetch ๊ฐ€ ๋ชจ๋‘ 404 ์—๋Ÿฌ๋กœ๊ทธ๋กœ ๋œจ๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ์—ˆ๋‹ค. ์ด ์ด์Šˆ๋ฅผ ๋งˆ์ฃผํ•œ ํ›„ "Next LInk ๋กœ ๋˜์–ด ์žˆ์œผ๋ฉด ์„œ๋ฒ„ ์‚ฌ์ด๋“œ์—์„œ ์‹คํ–‰ํ•  ๋กœ์ง์„ ๋ฏธ๋ฆฌ ์‹คํ–‰ํ•ด๋‘๋Š”๊ตฌ๋‚˜" ํ•˜๊ณ  ์ƒ๊ฐํ•˜๊ณ  ์žˆ์—ˆ๋Š”๋ฐ Next 13 ๋ฒ„์ „ ์ดํ›„์— ์ด ๋™์ž‘์ด ๋ฐ”๋€Œ์—ˆ๋‹ค. 

 

  • 9๋ฒ„์ „ ~ 12๋ฒ„์ „ : SSR ํŽ˜์ด์ง€์— ๋Œ€ํ•œ Next Link ๊ฐ€ ์žˆ๋‹ค๋ฉด, ์„œ๋ฒ„ ์‚ฌ์ด๋“œ ๋กœ์ง์„ ๋ฏธ๋ฆฌ ์‹คํ–‰ํ•˜๋Š” ์‹์œผ๋กœ prefetch๊ฐ€ ๋™์ž‘ํ•œ๋‹ค. ๊ณต์‹๋ฌธ์„œ ๊ณต์‹๋ฌธ์„œ2
  • 13๋ฒ„์ „ : ๊ธฐ๋ณธ์ ์œผ๋กœ prefetch ํ•˜์ง€ ์•Š๊ณ , SSR ํŽ˜์ด์ง€๋กœ ์ด๋™ ์‹œ ์„œ๋ฒ„ ์‚ฌ์ด๋“œ๊ฐ€ ๋™์ž‘ํ•˜์—ฌ HTML์„ ๋ Œ๋”๋งํ•œ๋‹ค. ๊ณต์‹๋ฌธ์„œ
    • prefetch ์„ค์ •์„ ํ†ตํ•ด ์ผค ์ˆ˜ ์žˆ๋‹ค.
    • ๊ฐœ๋ฐœ๋ชจ๋“œ์—์„œ๋Š” prefetch ๊ฐ€ ์‹คํ–‰๋˜์ง€ ์•Š๋Š”๋‹ค.

 

๋ฒ„์ „์— ๋”ฐ๋ผ prefetch ์˜ ๋™์ž‘์ด ๋‹ค๋ฅผ ์ˆ˜๋Š” ์žˆ์ง€๋งŒ, ์–ด๋–ค ๋ฐฉ์‹์œผ๋กœ๋“  SSR ํŽ˜์ด์ง€๋“ค์€ ํ•ญ์ƒ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์˜จ ์ƒํƒœ์ด๋ฏ€๋กœ isLoading ์ด false ์ด๋‹ค.

 

๊ฒฐ๋ก ์ ์œผ๋กœ useQuery์˜ isLoading์€?

์ƒํ™ฉ useQuery์˜ isLoading ๊ฐ’
SSR ์ง„์ž… (getServerSideProps) false
CSR ์ง„์ž… (ํด๋ผ์ด์–ธํŠธ ๋ Œ๋”๋ง) true
SSR ํŽ˜์ด์ง€ ๊ฐ„ ์ด๋™ (13๋ฒ„์ „ ์ดํ›„) false

 

SSR์—์„œ๋Š” ํ•ญ์ƒ false ์ด๊ณ , CSR ์—์„œ๋Š” true ์ด๋‹ค. ๋”ฐ๋ผ์„œ, SSR ๋กœ ๊ตฌ์„ฑ๋œ ํŽ˜์ด์ง€์—์„œ ์„œ๋ฒ„์‚ฌ์ด๋“œ์—์„œ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์„œ๋ฒ„ ๋ฐ์ดํ„ฐ์˜ ๊ฒฝ์šฐ isLoading ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค.

 

์ƒ๊ฐํ•ด๋ณด๋ฉด ๋‹น์—ฐํ•œ ์‚ฌ์‹ค์ด์ง€๋งŒ, ๋ฏธ์ฒ˜ ์˜์‹ํ•˜์ง€ ๋ชปํ–ˆ์„ ๋•Œ๋Š” ๊ด€์„ฑ์ ์œผ๋กœ isLoading์— ๋Œ€ํ•œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์™”๋˜ ๊ฒƒ ๊ฐ™๋‹ค.
์ด๋ฒˆ์— ๋™๋ฃŒ์˜ ์งˆ๋ฌธ์„ ๊ณ„๊ธฐ๋กœ useQuery์™€ SSR์˜ ๊ด€๊ณ„๋ฅผ ๋‹ค์‹œ ๋“ค์—ฌ๋‹ค๋ณด๋ฉด์„œ, ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ Next.js์˜ prefetch ๋™์ž‘ ๋ณ€ํ™”๊นŒ์ง€ ์‚ดํŽด๋ณด๊ฒŒ ๋˜์—ˆ๋‹ค.

 

๋ฐ˜์‘ํ˜•