본문 바로가기
SPA/Next

[Next] Data Fetching에 대해 알아보기 (1) - CSR / SSR

by J4J 2023. 2. 12.
300x250
반응형

안녕하세요. J4J입니다.

 

이번 포스팅은 Data Fetching의 첫 번째인 CSR과 SSR에 대해 적어보는 시간을 가져보려고 합니다.

 

 

 

Data Fetching에 대해 알아야 하는 이유

 

최근 Next에 대해 하나, 둘 배워가면서 Next를 제대로 활용하지 못하고 있었다는 것을 깨닫게 되었습니다.

 

그중 하나가 Data Fetching에 관련된 것입니다.

 

 

 

React대신 Next를 사용하는 개발자분들의 95% 이상은 SSR 때문일 것이라고 생각합니다.

 

그리고 저 또한 어떤 사람이 "React대신 Next를 왜 사용하냐?"라고 묻는다면 당연히 "SSR 때문에 사용합니다."라고 말할 것입니다.

 

그런데 만약, 다음 질문으로 "API를 통해 서버로부터 가져온 데이터들은 어떻게 SSR에 활용될 수 있냐?" 라고 묻는다면 이전까지 저의 답변은 "Next를 사용하기만 하면 활용되는 거 아닌가요?"라고 답변했을 것입니다.

 

최근 Data Fetching에 대해 공부하면서 이 답변은 절대적으로 틀렸다는 것을 느낄 수 있었고 만약 이 글을 보신분들 중 이전의 저와 같은 생각을 가지고 계신 분이 있다면 지금 당장 Data Fetching에 대해 공부를 시작하셔야 합니다.

 

 

 

먼저, 이전까지의 제 답변이 틀린 이유에 대해 설명해보겠습니다.

 

SSR이라 하면 단순하게 얘기했을 때 렌더링을 할 때 HTML 파일을 파싱 하여 렌더링을 하는 것입니다.

 

그리고 SSR을 사용하는 이유는 우리 서비스의 HTML 파일에 담겨있는 데이터들을 검색 엔진들의 크롤링 봇들이 수집하여 검색 결과에 활용될 수 있도록 하는 SEO를 위해서입니다.

 

Next를 사용하면 React와 달리 JS를 이용하여 렌더링을 하지 않고 HTML 파일을 이용하여 렌더링을 한다고 많이들 알고 계실 겁니다.

 

그리고 이 내용들에 대한 구체적인 사례로 Next와 React를 이용하여 Index 페이지를 간단하게 구성하고 실행해 보면 다음과 같은 결과들을 확인할 수 있습니다.

 

Next 렌더링 결과

 

React 렌더링 결과

 

 

 

페이지가 렌더링 되었을 때 Devtools의 Network 탭을 통해 렌더링 된 HTML 파일들을 확인할 수 있습니다.

 

그리고 위의 결과처럼 Next는 SSR로 동작되어 HTML 파일에 우리가 작성한 코드들이 담겨있어 Preview를 통해 확인할 수 있습니다.

 

하지만 반대로 React는 CSR로 동작되어 HTML 파일에 코드들이 담겨있지 않고, HTML파일 대신 JS를 이용해 화면을 그리기 때문에 Preview를 통해 확인할 수 없습니다.

 

결국 동일한 페이지를 검색 엔진 크롤링 봇들이 확인했을 때 Next로 만든 페이지에 담겨있는 데이터들만 수집하게 되고 React로 만든 페이지에 담겨있는 데이터들은 수집할 수 없습니다.

 

 

반응형

 

 

그리고 Data Fetching을 할 때도 동일한 결과가 나올 것이라고 생각했습니다.

 

이를 위한 테스트로 다음과 같은 코드를 작성하여 실행해 봤습니다.

 

호출 API

 

import axios from 'axios';
import { useEffect, useState } from 'react';

const Csr = () => {
    /**
     * useState
     */
    const [productNos, setProductNos] = useState<number[]>([]);

    /**
     * useEffect
     */
    useEffect(() => {
        getFetch();
    }, []);

    /**
     * getFetch
     */
    const getFetch = async () => {
        try {
            const res = await axios.get('http://localhost:8080/product/no');
            const productNos: number[] = res.data;

            setProductNos(productNos);
        } catch (error) {
            //
        }
    };
    return (
        <div>
            {productNos.map((productNo) => (
                <div>
                    <h2>데이터 Fetch로 가져온 상품번호 : {productNo}</h2>
                </div>
            ))}
        </div>
    );
};

export default Csr;

 

 

 

Next에서 코드를 작성한 뒤 서버를 실행하여 확인을 해봤더니 다음과 같은 결과가 나온 것을 확인할 수 있었습니다.

 

데이터 Fetch 결과

 

 

 

기대했던 결과와 달리 Preview에는 API와 관련된 데이터가 정상적으로 보이지 않았습니다.

 

 

 

또한 다음과 같이 HTML의 Response 값에도 API와 관련된 데이터가 한 줄도 보이지 않았습니다.

 

데이터 Fetch Response

 

 

 

즉, API로 부터 가져온 데이터들은 HTML 파일에 존재하지 않기 때문에 크롤링이 되었을 때 정상적으로 데이터 수집이 이루어지지 않습니다.

 

다른 말로는 Data Fetching을 하는 결과만 봤을 땐 Next를 사용하나, React를 사용하나 동일한 결과를 만들기에 Next를 사용하여 기대했던 SSR의 결과물을 얻을 수 없습니다.

 

그래서 결과적으로 저의 답변이 틀렸다고 얘기할 수 있습니다.

 

 

 

 

Next의 Data Fetching 종류

 

Next 공식문서에 따르면 Next의 Data Fetching의 종류는 다음과 같이 있습니다.

 

  • SSR
  • SSG
  • CSR
  • Dynamic Routing
  • ISR

 

 

 

그리고 이번 글에서는 SSR과 CSR에 대해 다뤄보도록 하겠습니다.

 

 

 

CSR

 

Next에서 CSR 활용 방식은 다음과 같이 2개로 소개하고 있습니다.

 

  • useEffect
  • SWR

 

 

 

먼저 useEffect는 위에서 봤던 예시처럼 Data Fetching을 수행하는 것입니다.

 

import axios from 'axios';
import { useEffect, useState } from 'react';

const Csr = () => {
    /**
     * useState
     */
    const [productNos, setProductNos] = useState<number[]>([]);

    /**
     * useEffect
     */
    useEffect(() => {
        getFetch();
    }, []);

    /**
     * getFetch
     */
    const getFetch = async () => {
        try {
            const res = await axios.get('http://localhost:8080/product/no');
            const productNos: number[] = res.data;

            setProductNos(productNos);
        } catch (error) {
            //
        }
    };
    return (
        <div>
            {productNos.map((productNo) => (
                <div>
                    <h2>데이터 Fetch로 가져온 상품번호 : {productNo}</h2>
                </div>
            ))}
        </div>
    );
};

export default Csr;

 

 

 

즉, React에서 하던 것처럼 Data Fetching 코드를 작성하는 것입니다.

 

그리고 이 방식을 사용할 경우 위에서 봤던 것처럼 HTML에 Fetching 된 데이터가 담기지 않았던 이유는 CSR을 이용하여 데이터를 가져오는 방식이기 때문입니다.

 

 

 

다른 방식 중 하나인 SWR은 위와 유사하게 Data를 Fetching해오지만 Fetching된 데이터를 캐싱 처리하는 기능 등이 담겨있습니다.

 

마치 useQuery를 사용하는 것과 비슷한 느낌을 받습니다.

 

사용 방법은 다음과 같습니다.

 

[ 1. 패키지 설치 ]

 

$ npm install swr

 

 

 

[ 2. 코드 작성 ]

 

import axios from 'axios';
import swr from 'swr';

const Swr = () => {
    /**
     * getFetch
     */
    const getFetch = () => {
        try {
            const res = swr('http://localhost:8080/product/no', (url) => axios.get(url).then((res) => res.data));

            if (res.isLoading) return <div>Loading...</div>;
            if (res.error) return <div>Error</div>;
            if (res.data) {
                const productNos: number[] = res.data;
                return (
                    <div>
                        {productNos.map((productNo) => (
                            <div>
                                <h2>데이터 Fetch로 가져온 상품번호 : {productNo}</h2>
                            </div>
                        ))}
                    </div>
                );
            }
        } catch (error) {
            //
        }
    };

    return (
        <div>{getFetch()}</div>
    )
};

export default Swr;

 

 

 

코드를 작성하고 실행해 보면 useEffect를 이용하여 렌더링 하는 것과 동일한 결과를 확인할 수 있습니다.

 

 

 

 

SSR

 

Next에서 SSR 활용 방식은 GetServerSideProps가 있습니다.

 

사용방법은 기존 페이지에서 Fetching 해오는 데이터들을 getServerSideProps에서 Prerendering 하여 페이지 컴포넌트의 props로 전달해 주면 됩니다.

 

그리고 주의사항으론 getServerSideProps는 page에서만 사용 가능하기 때문에 단순 재사용 컴포넌트에서는 사용이 불가합니다.

 

SSR을 활용하여 CSR과 동일한 결과물을 다음과 같이 만들어 낼 수 있습니다.

 

import axios from 'axios';
import { GetServerSideProps } from 'next';

export const getServerSideProps: GetServerSideProps = async (context) => {
    try {
        const res = await axios.get('http://localhost:8080/product/no');
        const productNos: number[] = res.data;

        return {
            props: {
                productNos,
            },
        };
    } catch (error) {
        return {
            notFound: true,
        };
    }
};

interface Props {
    productNos: number[];
}

const Ssr = (props: Props) => {
    return (
        <div>
            {props.productNos.map((productNo) => (
                <div>
                    <h2>데이터 Fetch로 가져온 상품번호 : {productNo}</h2>
                </div>
            ))}
        </div>
    );
};

export default Ssr;

 

 

 

코드를 작성하고 서버를 실행해 보면 다음과 같이 HTML 파일 안에 Fetching 된 데이터가 정상적으로 담겨 있는 것을 확인할 수 있습니다.

 

Data Fetching Prerender Preview

 

Data Fetching Prerender Response

 

 

 

즉, Data Fetching에서 Next를 사용했을 때 생각했던 결과물을 얻기 위해서는 React에서처럼 useEffect를 통해 데이터를 Fetching 해오는 것이 아니라 getServerSideProps를 활용하여 데이터를 Fetching해와야 합니다.

 

getServserSideProps를 통해 Prerendering이 되어 HTML에 Fetching된 데이터가 담기게 되고, 이렇게 만들어진 HTML 파일을 크롤링 봇들이 데이터 수집을 해가면서 SEO에 정상적으로 활용됩니다.

 

 

 

추가적으로 getServerSideProps 내부에서 페이지로 넘어온 query, pathname 등을 확인하거나 캐싱 처리를 하고 싶을 때는 다음과 같이 props로 전달받는 context를 활용할 수 있습니다.

 

query → context.query.productNo
pathname → context.resolvedUrl
cache → context.res.setHeader('Cache-Control', 'public, s-maxage=10, stale-while-revalidate=59');

 

 

 

 

 

 

 

이상으로 Data Fetching의 첫 번째인 CSR과 SSR에 대해 간단하게 알아보는 시간이었습니다.

 

읽어주셔서 감사합니다.

 

 

 

728x90
반응형

댓글