ํฐ์คํ ๋ฆฌ ๋ทฐ
๋ก๋ฉ๊น์ง ์๊ฐ์ด ๊ฑธ๋ฆฌ๋ ํ์ด์ง๋ก ์ด๋์ ํ๋ค๊ณ ํด๋ณด์.
์์์์ ๋ณผ ์ ์๋ฏ์ด ํ์ด์ง๊ฐ ๋ชจ๋ ๊ทธ๋ ค์ง ๋๊น์ง ๋์ด๊ฐ์ง ์๊ณ ์คํผ๋๊ฐ ๋๊ฒ ๋๋ค.
์ด๋ฅผ ๋ณด์ํ ์ ์๋ ๋ฐฉ๋ฒ์ด ์๋ค.
๋น๋๊ธฐ ๋ฐ์ดํฐ ๋ก๋ฉ [ํด๋ผ์ด์ธํธ ์ฌ์ด๋]
๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ค๋ ๊ฒ์์ ๋น๋๊ธฐ๋ฅผ ๋งํ๋ ๊ฒ์ด ์๋
ํ์ด์ง๋ฅผ ์์ฐจ์ ์ผ๋ก ๋ณด์ฌ์ฃผ๋ ๊ฒ์ ์์ด์ ๋น๋๊ธฐ ํ์์ ๋งํ๋ค.
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)์ ๊ฐ์ ํ๋ ๋ฐ ๋์์ด ๋ฉ๋๋ค.
'Language > Next' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| [/\/] Tanstack Query + Zustand ์ ์ญ ์ํ ๊ด๋ฆฌํ๊ธฐ (0) | 2025.03.21 |
|---|---|
| [/\/] Next.js์์ ๋งํ๋ '์๋ฒ'๋? (0) | 2025.03.19 |
| [/\/] Error Handling (0) | 2025.03.19 |
| [/\/] Next์ Tanstack Query (0) | 2025.03.19 |
| [/\/] SSG, ISR, SSR, CSR (0) | 2025.03.10 |