Next.js

Next.js의 렌더링 전략: SSR과 CSR, SSG (Hydration, Pre-rendering, Dynamic Routes, Build Time, ISR )

code2772 2024. 11. 11. 07:45
728x90
반응형

1. 웹 렌더링의 이해

1.1 렌더링이란?

렌더링은 코드를 사용자가 볼 수 있는 인터페이스로 변환하는 과정입니다. 웹에서는 HTML, CSS, JavaScript를 브라우저가 이해하고 화면에 표시할 수 있는 형태로 바꾸는 것을 의미합니다.

1.2 주요 용어 설명

  • Hydration: 서버에서 보낸 정적 HTML에 JavaScript 이벤트 핸들러와 상태를 입히는 과정
  • Pre-rendering: 미리 HTML을 생성하는 과정
  • Dynamic Routes: URL 패턴에 따라 다른 콘텐츠를 보여주는 페이지
  • Build Time: 애플리케이션을 배포용으로 빌드하는 시점

 

2. 클라이언트 사이드 렌더링 (CSR)

2.1 CSR의 작동 방식

const ProductPage = () => {
// 상태 관리
    const [products, setProducts] = useState([]);
    const [loading, setLoading] = useState(true);

// 데이터 fetching
    useEffect(() => {
        const fetchProducts = async () => {
            try {
                setLoading(true);
                const response = await fetch('/api/products');
                const data = await response.json();
                setProducts(data);
            } catch (error) {
                console.error('Failed to fetch products:', error);
            } finally {
                setLoading(false);
            }
        };

        fetchProducts();
    }, []);

// 로딩 상태 처리
    if (loading) return <div>Loading...</div>;

    return (
        <div>
            {products.map(product => (
                <ProductCard key={product.id} product={product} />
            ))}
        </div>
    );
};

2.2 CSR의 특징

  1. 장점
    • 초기 로드 후 빠른 페이지 전환
    • 서버 부하 감소
    • 풍부한 상호작용
  2. 단점
    • 초기 로딩 시간 증가
    • SEO 취약
    • 빈 HTML로 인한 깜빡임

3. 서버 사이드 렌더링 (SSR)

3.1 Next.js의 SSR 구현

// pages/products.js
export default function Products({ products }) {
    return (
        <div>
            {products.map(product => (
                <ProductCard key={product.id} product={product} />
            ))}
        </div>
    );
}

// 서버 사이드 데이터 fetching
export async function getServerSideProps() {
    try {
        const response = await fetch('https://api.example.com/products');
        const products = await response.json();

        return {
            props: {
                products,
                lastUpdate: new Date().toISOString(),
            }
        };
    } catch (error) {
        return {
            props: {
                products: [],
                error: 'Failed to fetch products'
            }
        };
    }
}
 

3.2 SSR의 특징

  1. 장점
    • 뛰어난 SEO
    • 빠른 초기 컨텐츠 로드
    • 더 나은 사용자 경험
  2. 단점
    • 서버 자원 사용 증가
    • TTFB (Time To First Byte) 증가
    • 페이지 전환시 전체 새로고침

4. Static Site Generation (SSG)

4.1 정적 생성 구현

// pages/products/[id].js
export default function Product({ product }) {
    return (
        <div>
            <h1>{product.name}</h1>
            <p>{product.description}</p>
            <span>Price: ${product.price}</span>
        </div>
    );
}

// 빌드 타임에 실행
export async function getStaticProps({ params }) {
    const response = await fetch(`https://api.example.com/products/${params.id}`);
    const product = await response.json();

    return {
        props: {
            product,
        },
// 10초마다 페이지 재생성
        revalidate: 10,
    };
}

// 생성할 경로 지정
export async function getStaticPaths() {
    const response = await fetch('https://api.example.com/products');
    const products = await response.json();

    const paths = products.map((product) => ({
        params: { id: product.id.toString() },
    }));

    return {
        paths,
        fallback: 'blocking'
    };
}

4.2 ISR (Incremental Static Regeneration)

ISR은 정적 페이지를 주기적으로 업데이트하는 Next.js의 기능입니다.

export async function getStaticProps() {
    const response = await fetch('<https://api.example.com/data>');
    const data = await response.json();

    return {
        props: {
            data,
        },
        revalidate: 60// 60초마다 페이지 재생성
    };
}

5. 하이브리드 렌더링 전략

5.1 페이지별 최적화

// 정적 페이지 (블로그 포스트)
export async function getStaticProps({ params }) {
    const post = await getPost(params.slug);
    return { props: { post } };
}

// 동적 페이지 (대시보드)
export async function getServerSideProps() {
    const stats = await getRealTimeStats();
    return { props: { stats } };
}

5.2 컴포넌트 레벨 최적화

// 정적 컨텐츠와 동적 컨텐츠 조합
function HomePage({ staticContent }) {
    const { data: dynamicContent } = useSWR('/api/dynamic-content');

    return (
        <div>
            <StaticSection content={staticContent} />
            <DynamicSection content={dynamicContent} />
        </div>
    );
}

6. 성능 최적화 전략

6.1 이미지 최적화

import Image from 'next/image';

function ProductImage({ product }) {
    return (
        <Image
            src={product.image}
            alt={product.name}
            width={500}
            height={300}
            placeholder="blur"
            priority={true}
        />
    );
}

6.2 코드 스플리팅

import dynamic from 'next/dynamic';

// 필요할 때만 로드되는 컴포넌트
const DynamicComponent = dynamic(() => import('../components/Heavy'), {
    loading: () => <p>Loading...</p>,
    ssr: false// 클라이언트 사이드에서만 렌더링
});

7. 결론

Next.js는 다양한 렌더링 전략을 제공하여 각 페이지와 컴포넌트에 가장 적합한 방식을 선택할 수 있게 합니다:

  1. 정적 콘텐츠
    • 블로그 포스트
    • 제품 카탈로그
    • 문서 페이지 → SSG 사용
  2. 실시간 데이터
    • 대시보드
    • 분석 페이지
    • 개인화된 콘텐츠 → SSR 사용
  3. 상호작용이 많은 페이지
    • 관리자 패널
    • 편집기
    • 복잡한 폼 → CSR 사용
반응형