CSR & SSR 특징 및 차이점 (feat.Next.js)

숩딩·2023년 2월 15일
0

CSR (Client Side Rendering)

Client Side Rendering 은 말 그대로 클라이언트 쪽에서 랜더링이 일어난다.
즉, 서버는 요청을 받으면 클라이언트에 HTML, JS를 보내주고 클라이언트는 그것을 받아 렌더링을 시작한다.

CSR 의 렌더링 과정

  1. 유저가 요청을 보낸다.
  2. CDN이 빠르게 HTML파일 ,JS 링크를 클라이언트(브라우저)로 보낸다.
  3. 클라이언트가 HTML 문서에 포함된 함께 정적 리소스(CSS, 이미지 등), 번들 자바스크립트 파일을 다운로드한다.
    유저에게는 전혀 이 과정이 보이지 않는다.
  4. 클라이언트에서 번들 자바스크립트 파일을 실행하여 페이지가 렌더링 되며, 이 때 사용자는 렌더링 된 페이지를 볼 수 있다. 하지만 API 호출 전이라 로딩(빈 화면) 혹은 placeholder을 보게 된다.
  5. 페이지 렌더링이 완료된 후, API 요청 코드가 실행된다.
  6. API에서 받아온 데이터로 state를 업데이트하고 페이지를 리렌더링한다.
  7. 페이지의 interaction 이 가능해진다.

CSR의 특징

  • 최초 페이지 로딩 시간이 SSR 보다 오래 걸린다.
  • 페이지 이동에 걸리는 시간은 짧다
  • 서버에서의 처리 없이 클라이언트 사이드에서 처리해주기 때문에 자바스크립트가 모두 다운되고 실행이 끝나기 전 까지 사용자는 볼 수 있는게 없다.

SSR (Server Side Rendering)

Server Side Rendering도 말 그래도 서버쪽에서 렌더링 준비를 끝마친 상태로 클라이언트에 전달하는 방식이다.

SSR 의 렌더링 과정

  1. 유저가 특정 페이지를 요청한다.
  2. 서버에서는 해당 페이지에 필요한 데이터를 포함한 HTML 파일을 만들어(리소스 체크, 컴파일 후 완성된 HTML 컨텐츠로 만듦) 클라이언트에 전달한다.
  3. 클라이언트에서는 해당 HTML 파일을 파싱하면서 필요한 정적 리소스(CSS, 이미지 등)와 자바스크립트 파일을 다운로드하며, 페이지를 렌더링한다.
    이 떄 사용자는 렌더링된 페이지를 볼 수는 있지만 아직 클릭 등의 interaction은 할 수 없다.
  4. 브라우저가 JavaScript 프레임워크를 실행한다.
  5. 페이지의 interaction 이 가능해진다.

서버에서 이미 '렌더 가능한' 상태로 클라이언트에 전달되기 때문에, JavaScript가 다운로드 되는 동안 사용자는 무언가를 보고 있을 수 있다.

SSR 특징

  • 최초 페이지 로딩 시간이 짧음
    왜냐? 요청한 페이지에 필요한 리소스만을 다운받아서 페이지를 생성하기 때문
    -자바스크립트 실행이 완료될 때까지 사용자 조작(인터렉션) 불가능
  • 페이지 이동에 걸리는 시간이 CSR보다 길다
  • 검색엔진 최적화(SEO)에 적합하다
    서버에서 페이지를 생성하기 때문에 검색엔진에 빈 HTML 파일이 아니라 완성된 HTML문서를 수집할 수 있어서 검색엔진 최적화에 용이하다!

사용자 인터렉션이 중요한 페이지라면 CSR 방식이 적절하다!
CSR은 API에서 데이터를 받아온 후 화면 리렌더링이 완료되면 즉시 화면에서 인터렉션이 가능하다.
SSR은 화면은 보이지만 인터렉션이 되지 않는 것 보다는 화면이 아예 렌더링이 되지 않는 편이 사용자에게는 더 직관적일 수 있다.

Next.js 에서의 렌더링

React는 기본적으로 CSR 방식으로 동작하는 UI 라이브러리이고,
Next.js 는 SSR 과 Static Generation 을 지원하는 React Framework 이다.

NextJS는 페이지 렌더링 방법 제어에 사용하는 두가지 형태의 사전 렌더링을 제공한다.

1. 정적 생성

정적 생성에서 페이지 컴포넌트가 사전 렌더링 되는 시점은 애플리케이션을 빌드하거나 Next 프로젝트를 빌드하는 시점 즉 프로덕션용으로 빌드하는 시점

중요! 정적 생성에서는 기본적으로 요청이 서버에 도달했을 때 서버에서 즉각적으로 페이지를 사전 렌더링하지 않는다. 대신에 개발자가 프로덕션용 사이트를 빌드할 때 사전 렌더링을 함.

즉 사이트가 배포되고 나면 사전 렌더링한 페이지는 변경되지 않는다는 뜻

- getStaticProps

NextJS는 페이지를 마련한다. 기본적으로 이미 정적인 페이지를 생성하고 기본적으로 빌드 프로세스 중에 페이지를 생성한다. 하지만 데이터를 기다려야 한다면, 즉 페이지 컴포넌트에 데이터를 가져와서 추가해야 한다면 페이지 컴포넌트 파일 안에서 특수 함수를 export로 내보내면 된다.

  • pages 폴더 안에 있는 컴포넌트 파일들에서만 가능하다.
  • 이 함수는 실제로 이 페이지에서 사용할 props를 준비
  • 이 props는 페이지에서 필요한 데이터를 포함할 수 있다
  • 비동기적으로 설정될 수 있어서 유용
  • 여기에서 promise를 반환할 수 있다
function HomePage(props) {
  return (
    <>
      <MeetupList meetups={props.meetups} />
    </>
  );
}

export const getStaticProps = async () => {
  return {
    props: {
      meetups: DUMY_MEETUPS,
    },
    revalidate: 10,
  };
};

export default HomePage;

NextJS는 이 promise가 데이터를 읽어 들일 때까지 기다린다

그다음에 이 컴포넌트 함수에서 사용할 props를 반환 → 이 컴포넌트 함수가 실행되기 전에 데이터를 읽어 들일 수 있어서 이 컴포넌트를 필요한 데이터와 함께 렌더링할 수 있다.

여기에 작성하는 모든 코드는 클라이언트 측에 들어가지 않기 때문에 클라이언트 측에서 절대 실행되지 않는다. (이 코드는 빌드 프로세스 중에 실행되기 때문)

  • revalidate
    : 데이터를 업데이트 해주는 프로퍼티
    revalidate 값이 10이라면 이 페이지에 요청이 들어오면 적어도 10초마다 서버에서 페이지를 다시 생성

-getStaticPaths

동적 페이지에서 필요한 함수고, 넥스트 JS에게 어떤 동적 매개변수 밸류의 어떤 페이지가 프리 제너레이트되어야 하는지 말해주는 중요한 함수

ex.디테일페이지 작업에서의 사용

디테일 페이지 같은 경우 매초 여러번의 데이터 변동은 없지만 동적 페이지이다.

그래서 getStaticProps 를 사용하여 프리패칭 하는데

여기서 getStaticPaths 를 함께 사용해주어야 한다.

왜냐면 데이터를 패치할 때 정확한 데이터를 불러왔는지 확인할 방법이 필요하다.

예를들어 ID같은게 필요하다. URL에 ID 가 인코드 되어있으니 useRouter로 사용할 생각을 할 수도 있지만 이건 함수 밖에 있기 때문에 여기서 훅사용이 불가능하다.

→ context 매개변수 사용!

context.params 를 사용하여 ID객체를 가져올 수 있다.

export const getStaticProps = async (context) => {
  const meetupId = context.params.meetupId;

  return {
    props: {
      meetupData: {
        image:
          'https://cdn.pixabay.com/photo/2022/01/30/17/16/snowman-6981771_1280.jpg',
        title: 'title',
        address: 'address',
        description: 'description',
      },
    },
  };
};

이렇게 해주면

getStaticPaths 를 작성하라고 뜬다.

getStaticPaths 은 next.js 가 이해할 수 있는 또다른 함수이다.

  • getStaticProps는 페이지가 빌드 프로세스 중에 프리 제너레이트 됐다. ⇒ 넥스트 JS가 동적 페이지의 모든 버전의 프리 제너레이트가 필요하다는 말)지원되는 모든 ID에서)
    • 동적이기 때문에 넥스트 JS는 어떤 ID 밸류가 프리 제너레이트 페이지가 되어야 하는지 알아야 한다.
    • ID는 여기 있는 URL에서 얻을 수 있다. 하지만 사용자가 URL의 특정 밸류로 페이지에 방문했을 때 프리 제너레이트되는 게 아니라 빌드 프로세스에서 되는 것이다. 따라서 모든 URL에서 프리 제너레이트해야 한다.
      (만약 프리 제너레이트 하지 않은 페이지인 ID로 입장하면 404 에러를 보게 될 것)
  • getStaticPaths 함수는 객체를 리턴
    모든 동적 세그먼트 밸류가 있는 객체
    export const getStaticPaths = async () => {
      return {
        fallback: false,
        paths: [
          {
            params: {
              meetupId: 'm1',
            },
          },
          {
            params: {
              meetupId: 'm2',
            },
          },
        ],
      };
    };
  • 이 객체는 paths 키가 있어야 함 = 배열
  • 그리고 배열에는 객체가 여러 개 있어야 함
  • 동적 페이지 버전당 객체가 하나, 이 객체는 params 키를 가짐( 꼭 필요. 이건 모든 키 밸류 쌍을 가진 객체다)

-fallback

이 키가 넥스트 JS에게 paths 배열이 모든 지원되는 매개변수를 저장할지 아니면 일부만 저장할지 말해준다.

false : paths에게 모든 지원되는 미트 업 ID 밸류를 포함하라고 하는 것

사용자가 지원되지 않는 것을 입력하면 404 error 이 뜬다.

true : 들어오는 요청에 관해서, 서버에서 미트 업 ID로 페이지를 동적으로 만든다.

2. 서버 사이드 렌더링

동적 → 요청이 있을때만 데이터 업데이트

-getServerSideProps

export const getServerSideProps = async (context) => {
  const req = context.req;
  const res = context.res;

  return {
    props: {
      meetups: DUMY_MEETUPS,
    },
  };
};

getStaticProps와의 차이점:

  • 이 함수는 빌드 프로세스 중에는 실행되지 않는다

  • 요청이 들어올 때마다 실행되기 때문에 시간을 지정해서 revalidate 할 필요가 없다.

  • 매개변수가 필요하다.
    → context 매개변수에서 요청 객체에 접근할 수 있고, 응답 객체가 돌아온다.

  • 응답객체는 prop 키로 객체를 리턴한다. 이 키가 페이지 컴포넌트 함수 prop을 저장하고 있다.

매초 여러 번 바뀌는 데이터를 가지고 있다면 getServerSideProps이 좋은 선택이다.

profile
Front-End Developer ✨

0개의 댓글