ํ‹ฐ์Šคํ† ๋ฆฌ ๋ทฐ

 

๋กœ๋”ฉ๊นŒ์ง€ ์‹œ๊ฐ„์ด ๊ฑธ๋ฆฌ๋Š” ํŽ˜์ด์ง€๋กœ ์ด๋™์„ ํ•œ๋‹ค๊ณ  ํ•ด๋ณด์ž.

 

 

์˜์ƒ์—์„œ ๋ณผ ์ˆ˜ ์žˆ๋“ฏ์ด ํŽ˜์ด์ง€๊ฐ€ ๋ชจ๋‘ ๊ทธ๋ ค์งˆ ๋•Œ๊นŒ์ง€ ๋„˜์–ด๊ฐ€์ง€ ์•Š๊ณ  ์Šคํ”ผ๋„ˆ๊ฐ€ ๋Œ๊ฒŒ ๋œ๋‹ค.

์ด๋ฅผ ๋ณด์™„ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค.

 

๋น„๋™๊ธฐ ๋ฐ์ดํ„ฐ ๋กœ๋”ฉ [ํด๋ผ์ด์–ธํŠธ ์‚ฌ์ด๋“œ]

 

๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š” ๊ฒƒ์—์„œ ๋น„๋™๊ธฐ๋ฅผ ๋งํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹Œ

ํŽ˜์ด์ง€๋ฅผ ์ˆœ์ฐจ์ ์œผ๋กœ ๋ณด์—ฌ์ฃผ๋Š” ๊ฒƒ์— ์žˆ์–ด์„œ ๋น„๋™๊ธฐ ํ˜•์‹์„ ๋งํ•œ๋‹ค.

 

isLoading ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ๋น„๋™๊ธฐ์ ์œผ๋กœ ๊ฐ€์ ธ์˜ค๋Š” ๋™์•ˆ 'Loading...' ์„ ํ‘œ์‹œํ•˜๊ณ  ๋ฐ์ดํ„ฐ๊ฐ€ ์ค€๋น„๋˜๋ฉด UI๋ฅผ ์—…๋ฐ์ดํŠธํ•œ๋‹ค.

๋”๋ณด๊ธฐ
๋”๋ณด๊ธฐ
'use client';

import React, { useState, useEffect } from 'react';
import { Product } from '@/types';

const HomePage = () => {
  const [isLoading, setIsLoading] = useState(false);
  const [data, setData] = useState<Product[]>([]);

  useEffect(() => {
    setIsLoading(true);
    fetch('http://localhost:4000/products')
      .then((res) => res.json())
      .then((data) => {
        setData(data);
        setIsLoading(false);
      });
  }, []);

  if (isLoading) return <div>Loading...</div>;

  return (
    <div>
      <h1>Products</h1>
      <div className="p-8 m-4">
        {data.map((product) => (
          <div className="flex border p-4 gap-4 rounded-md" key={product.id}>
            <img
              className="rounded-sm"
              width={150}
              height={150}
              src={product.images}
              alt={product.title}
            />
            <div className="flex flex-col justify-between">
              <div>
                <h2 className="text-xl font-bold">{product.title}</h2>
                <p className="text-sm">{product.description}</p>
                <p className="mt-4 text-2xl">{product.price.amount}$</p>
              </div>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
};

export default HomePage;

 

 

์บ์‹ฑ ํ™œ์šฉ [์„œ๋ฒ„ ์‚ฌ์ด๋“œ]

 

์บ์‹ฑ ํ™œ์šฉ = SSG

๋ฐ์ดํ„ฐ๊ฐ€ ์ž์ฃผ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š๋Š”๋‹ค๋ฉด, ์บ์‹ฑ์„ ํ™œ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ ๋กœ๋”ฉ ์‹œ๊ฐ„์„ ๋‹จ์ถ•ํ•  ์ˆ˜ ์žˆ๋‹ค.

๋‹ค๋งŒ ๋นŒ๋“œ ์‹œ, ์‹œ๊ฐ„์ด ์กฐ๊ธˆ ๊ฑธ๋ฆฌ๋ฉฐ ์ •์ ์ธ ํŽ˜์ด์ง€๋กœ๋งŒ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ๋‹ค.

๋”๋ณด๊ธฐ
๋”๋ณด๊ธฐ
import ProductList from '@/components/ProductList';
import { Product } from '@/types';

const ProductsPage = async () => {
  const response = await fetch('http://localhost:4000/products',
  {cache: "force-cache"});
  const products: Product[] = await response.json();

  return (
    <div>
      <h1>Products</h1>
      <ProductList products={products} />
    </div>
  );
};

export default ProductsPage;

 

 

Loading UI [์„œ๋ฒ„ ์‚ฌ์ด๋“œ]

 

์‹ค์‹œ๊ฐ„์œผ๋กœ ์„œ๋ฒ„์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์•ผ ํ•  ๋•Œ = SSR

์‚ฌ์šฉํ•˜๊ณ ์ž ํ•˜๋Š” route segment ํด๋” ๋‚ด์— loading.tsx ํŒŒ์ผ์„ ๋งŒ๋“ค์–ด ๋กœ๋”ฉ ์ƒํƒœ๋ฅผ ํ‘œ์‹œํ•  ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

๊ทธ๋Ÿผ ๊ทธ ํŽ˜์ด์ง€๋ฅผ ๋ถˆ๋Ÿฌ์˜ฌ ๋•Œ ์ž๋™์ ์œผ๋กœ loading.tsx ํŒŒ์ผ์˜ ๋‚ด์šฉ์ด ํ™”๋ฉด์— ์ถœ๋ ฅ๋œ๋‹ค.

๋”๋ณด๊ธฐ
๋”๋ณด๊ธฐ
// loading.tsx
export default function Loading() {
  return <div>Loading...</div>;
}

 

์ด๋ ‡๊ฒŒ loading.tsx ํŒŒ์ผ๋งŒ์„ ์ ์šฉ์‹œํ‚ค๋ฉด ํ•ด๋‹น ํŽ˜์ด์ง€ ์ „์ฒด์— ๋กœ๋”ฉ UI๊ฐ€ ์ ์šฉ๋œ๋‹ค.

ํ•˜์ง€๋งŒ ํŽ˜์ด์ง€ ์ผ๋ถ€์—๋งŒ ๋กœ๋”ฉ UI๋ฅผ ์ ์šฉํ•˜๋ ค๋ฉด Suspense๋ฅผ ํ™œ์šฉํ•ด์•ผ ํ•œ๋‹ค.

 

Suspense

 

์„œ์ŠคํŽœ์Šค ์ปดํฌ๋„ŒํŠธ๋Š” ๋น„๋™๊ธฐ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•  ๋•Œ ๋Œ€์ฒด UI๋ฅผ ์ œ๊ณตํ•˜๋Š” ์—ญํ• ์ด๋‹ค.

import NewProductList from "@/components/NewProductList";
import { Suspense } from "react";
import Loading from "./loading";

export default async function Home() {
  return (
    <div className="p-8 m-4">
      <h1>Sparta Shop</h1>
      <Suspense fallback={<Loading />}>
        <NewProductList />
      </Suspense>
    </div>
  );
}

 

'Sparta Shop' ํ…์ŠคํŠธ๊ฐ€ ๋จผ์ € ํ‘œ์‹œ๋˜๋Š” ๋™์•ˆ NewProductList ๋ถ€๋ถ„์€ ๋กœ๋”ฉ UI๋ฅผ ๋ณด์—ฌ์ค€๋‹ค.

์ด์ฒ˜๋Ÿผ ๋ถ€๋ถ„์ ์œผ๋กœ ํŽ˜์ด์ง€์˜ ๋กœ๋”ฉ UI๋ฅผ ๋ณด์—ฌ์ฃผ์–ด ์‚ฌ์šฉ์ž๊ฐ€ ์™„์„ฑ๋˜์ง€ ์•Š์€ ํŽ˜์ด์ง€๋ผ๊ณ  ํ•ด๋„ ํŽ˜์ด์ง€ ์ž์ฒด๋ฅผ ๋น ๋ฅด๊ฒŒ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

 

 

Streaming SSR

 

Next.js 14์—์„œ๋Š” React์˜ ์„œ์ŠคํŽœ์Šค์™€ ์ŠคํŠธ๋ฆฌ๋ฐ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜์—ฌ SSR์„ ๋”์šฑ ํšจ์œจ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.

์ŠคํŠธ๋ฆฌ๋ฐ์€ ์„œ๋ฒ„์—์„œ ์ค€๋น„๋œ ์ฝ˜ํ…์ธ ๋ฅผ ์กฐ๊ฐ์กฐ๊ฐ ํด๋ผ์ด์–ธํŠธ์— ๋ณด๋‚ด๋Š” ๋ฐฉ์‹์ด๋‹ค.

 

ํ•˜๋‚˜์˜ ์„œ์ŠคํŽœ์Šค๋งŒ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ํ•œ ๊ฐœ์˜ ๋น„๋™๊ธฐ ์ปดํฌ๋„ŒํŠธ๋งŒ ํฌ๊ฒŒ ๋ถ„๋ฆฌํ•ด์„œ ๋ถˆ๋Ÿฌ์˜ค๊ฒ ์ง€๋งŒ

์—ฌ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋‚˜๋ˆ ์„œ ๋ถˆ๋Ÿฌ์˜ค๋ ค๋ฉด ์ŠคํŠธ๋ฆฌ๋ฐ SSR์„ ํ™œ์šฉํ•ด์•ผ ํ•œ๋‹ค.

export default async function Home() {
  return (
    <div className="p-8 m-4">
      <h1>Sparta Shop</h1>
      <Suspense fallback={<Loading />}>
        <NewProductList />
      </Suspense>
      <Suspense fallback={<Loading />}>
        <ProductList />
      </Suspense>
    </div>
  );
}

 

 

๐Ÿ‘‡๐ŸปNext ๊ณต์‹๋ฌธ์„œ

๋”๋ณด๊ธฐ
๋”๋ณด๊ธฐ

์ŠคํŠธ๋ฆฌ๋ฐ์€ ๊ธด ๋ฐ์ดํ„ฐ ์š”์ฒญ์œผ๋กœ ์ธํ•ด ํŽ˜์ด์ง€ ๋ Œ๋”๋ง์ด ์ฐจ๋‹จ๋˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•˜๊ณ ์ž ํ•  ๋•Œ ํŠนํžˆ ์œ ์šฉํ•˜๋ฉฐ,

TTFB(Time To First Byte)์™€ FCP(First Contentful Paint)๋ฅผ ์ค„์ผ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

๋˜ํ•œ ํŠนํžˆ ๋А๋ฆฐ ๋””๋ฐ”์ด์Šค์—์„œ ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒ ์‹œ๊ฐ„(TTI)์„ ๊ฐœ์„ ํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค.

๊ณต์ง€์‚ฌํ•ญ
์ตœ๊ทผ์— ์˜ฌ๋ผ์˜จ ๊ธ€
์ตœ๊ทผ์— ๋‹ฌ๋ฆฐ ๋Œ“๊ธ€
Total
Today
Yesterday
๋งํฌ
TAG
more
ยซ   2026/03   ยป
์ผ ์›” ํ™” ์ˆ˜ ๋ชฉ ๊ธˆ ํ† 
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
๊ธ€ ๋ณด๊ด€ํ•จ