본문 바로가기
Next.js

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

by code2772 2024. 11. 11.

[ 목차 ]

    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 사용
    반응형