본문 바로가기
DEV/FE

[Next.js] SSR이 뭔데?

by krokerdile 2024. 5. 24.

SSR이 뭘까?

ICT 인턴십으로 근무 중인 회사에서 내가 진행하게 되는 서비스의 기술 스택은 Next.js 12와 Node 14가 메인이었습니다. 그러다보니 Next.js에 대해서 공부도 해야 되고 Next.js를 사용하는 이유 중 하나인 SSR, SEO에 대해서도 공부를 해야 되었습니다. 3월 중에 그렇게 공부를 하면서 정리를 했던 글을 모아서 올리려고 합니다. 밑에 내용 부터는 혼자 공부하면서 적은 글이라 말투가 반말인 점 양해 부탁드립니다. 

네이버 블로그의 경우

SSR을 검색했을 때 제일 먼저 나오는 네이버 기술 블로그

  • 구글에 SSR을 검색했는데 네이버 블로그 글이 먼저 나오길래 한번 읽어 봤다. 밑에 내용은 읽으면서 정리한 부분 그리고 궁금한게 생겼거나 간단하게 의견을 추가로 달아봤다.

어서 와, SSR은 처음이지? - 도입 편

SSR이란?

  • 서버에서 사용자에게 보여줄 페이지를 모두 구성하여 사용자에게 페이지를 보여주는 방식. JSP/Servelet 등의 아키텍처에서 이 방식을 사용했다.
  • (인프콘 영상에서 발표를 하시는 분 말에 따르면 생각보다 SSR을 다루고 있는 프레임워크가 프론트엔드로 완전히 넘어오기 전에도 다양하게 있다고 한다)
  • SSR을 사용하면 모든 데이터가 매핑된 서비스 페이지를 클라이언트(브라우저)에게 바로 보여줄 수 있다. 서버를 이용해서 페이지를 구성하기 때문에, 클라이언트에서 페이지를 구성하는 CSR(Client-Side Rendering) 보다 페이지를 구성하는 속도는 늦어질 수 있어도 사용자에게 보여주는 콘텐츠 구성이 완료되는 시점은 빨라진다는 장점이 있다. 그리고 SEO(search engine optimization) 또한 쉽게 구성할 수가 있다.

  • CSR과 SSR을 비교했을 때 CSR이 초기에 전송되는 페이지의 속도는 빠르지만 페이지가 전송된 후에 서비스에서 필요한 데이터를 클라이언트에서 다시 추가 요청해서 불러와야 되기 때문에 전체적인 페이지 완료 시점이 SSR 보다 느려지는

SPA(single-page application)

  • 단일 페이지 어플리케이션으로 현재의 페이지를 동적으로 작성함으로써 사용자와 소통하는 웹 애플리케이션이다. 연속되는 페이지 간의 사용자 경험을 향상 시키고 웹 어플리케이션이 데스크톱 애플리케이션 처럼 동작하도록 도와준다.
  • 네이버 블로그에서는 이러한 SPA를 구현해주기 위해서 프론트엔드와 백엔드 영역의 분리를 한다고 한다.
  • 페이지 덩어리(JSP/Servlet)를 'CSR View 영역'과 'SSR View 영역', 그리고 'API'로 분리함으로써 프런트엔드와 백엔드 영역에서 담당하는 페이지의 역할을 나누어야 한다.
    • 검색 했을 때 16에서 SSR을 지원하는 걸 보니 정말 몇년 사이에 많은 게 변했구나 싶다.네이버 블로그가 2010년대 중반만 해도 angular.js 프레임워크로 구성되어져 있었고 angular.js가 당시만 해도 SSR을 지원하지 않고 CSR만 지원했다고 한다.
      네이버 블로그가 로딩되는 과정에 대한 스샷
    • 네이버 블로그는 지금 생각해도 정말 많은 콘텐츠가 왔다갔다 할 거 같은데 CSR로 이걸 구현하게 된다면 로딩을 하는 페이지가 정말 오래 떠있지 않을까 싶었는데 실제로도 그런거 같다
  • 그래서 그 대체제로 선택한 것 → Node.js 기반의 SSR
    • 참고한 글이 2020년 글인데 당시에는 프론트에 SSR이라는 개념이 핫하지는 않았던 기억이 있는 것 같다.
    • 그래도 React 기반의 SSR이 있었고 이걸 선택해서 진행한 것 같다.
    • 이렇게 선택하면 장점이 몇 가지 있다고 한다.
      1. Node.js 기반의 SSR은 universal language인 JavaScript를 최대한 활용할 수 있다.
      2. SSR을 사용하면 프런트엔드 영역과 백엔드 영역을 완전히 분리함으로써 생산성을 높일 수 있다.
        • 이걸 처음에 읽었을 때 무슨 말인가 했더니, 이 글이 쓰여진 2020년 당시에는 SSR 페이지를 JSP나 Servlet으로 진행했기 때문에 CSR은 프론트에서 SSR은 백에서 하는 문제가 있었다고 한다. → 그러면 백엔드 개발자가 프론트도 같이 한건가?
        • 그래서 이걸 아예 CSR, SSR을 통합해서 프론트에서 처리를 한다면 당연히 더 효율적일 것 같긴 하다
      3. SSR 아키텍처를 구성하면 다른 여러 가지 대안을 활용할 수 있는 토대가 된다
  • 이렇게 도입 했을 때 얻은 장점
    • 프론트엔드와 백엔드 사이의 커뮤니케이션 → API 명세에 대한 커뮤니케이션으로 국한
    • 프론트엔드 작업 완성도 및 이슈 대응 생산성도 높아짐
    • 프론트 부분에 대한 QA 처리 시간의 축소

그래서 SSR, CSR이랑 다른 것들 다 정리를 해보자


1. SSR

  • Next.js를 사실 이번에 ICT 학점연계 인턴십으로 인턴으로 활동하기 전까지는 사용해본 적이 없다. 지난 겨울에 진행했던 스마일게이트 데브캠프에서 함께했던 프론트 분들과 매주 진행한 대담에서 우리 감자를 좋아하시는 xx님께 SSR도 알아야 되요 라고 얘기를 듣긴 했었다. 하지만 이게 캠프 끝나자마자 바로 업보로 돌아올 줄은 몰랐다.
  • 그래서 SSR이 뭔지 위에 글도 그렇고 이런저런 곳을 읽어보면서 정리를 해보려고 한다.
  • SSR = Server-Side-Rendering
  • 위의 단어 말 그대로 HTML 파일을 서버에서 렌더링을 한다는 것이다. 사실 처음에 이 정의를 읽었을 때 HTML을 서버에서 렌더링을 해준다는게 무슨 말인가 했다. 처음에 관련된 내용을 읽기 전에는 백에서 그럼 HTML 파일 관련된 걸 string 처리해서 날려준다는 줄 알았다. 그게 아니고 클라이언트 쪽 서버에서 이걸 미리 pre-redering 해서 html 페이지를 만들어주고 난 후에 js 파일을 다운 받는 형식이었다.

    • 위의 사진은 그 과정이 포함되어져 있는 사진인데 위의 사진 처럼 HTML을 미리 렌더링 하고요청이 오면 Chunk 단위로 자바스크립트를 보내줘서 이벤트가 작동하게 되는 것이 Hydration이라고 한다. 이 빌드 방식 = SSR은 아니고 여기서 이제 SSR과 SSG로 나눠진다.
  • 그래서 이게 장단점이 뭘까?
    • 장점
      • SEO가 좋아진다? why?
      • 브라우저에서 요청을 날리는 것이 아니라 서버에서 API 요청을 처리하기 때문에 보안이 좋아진다. → 이건 맞는 것 같다.
    • 단점
      • 페이지를 전환 할때 마다 네트워크 요청을 하므로 사이즈가 커지면 네트워크 요청 부담이 커진다.
      • html을 pre-rendering 하기 때문에 ui는 빠르게 보이지만 동적인 데이터를 가져오기 까지 시간이 걸리기 때문에 인터렉티브 동작까지 시간이 걸린다 → 하이드레이션 과정
      • MPA 방식이라 페이지가 전환되면 새로고침 된다. → MPA?
  • 그래서 언제 사용할까?
    • 항상 최신 상태를 유지해야 되는 웹 페이지나 분석차트(대시보드 같은?)
    • 사용자의 요청 마다 동적으로 페이지를 생성 해서 다른 내용을 보여줘야 되는 경우에 사용
  • CSR과의 차이
    • 기존에 React에서는 CSR 방식으로 데이터를 가져올 때 useEffect를 사용했음
      • dependency 배열 값에 의존해서 렌더링 되거나 값이 바뀔 때마다 다시 요청을 날리는 방식으로 흔히 사용 했었던 것 같음.
    • Next.js에서 getServerSideProps, getStaticProps, getStaticPaths 등을 활용해서 데이터를 불러와줌.
  • getServerSideProps
    • pre-rendering 과정에서 getServerSideProps 함수를 발견하면 컴포넌트 함수 호출 전에 getServerSideProps를 먼저 호출해주게 됨
    • 처음에 이 개념을 봤을 때 getServerSideProps로 컴포넌트를 만들어주는 건가 했는데 그게 아니고 공통적으로 사용할 수 있는 함수의 개념으로 불러와주는 거였음.
    • 호출 되어지면 API 통신을 통해 데이터를 받아온 후에 컴포넌트에 props로 데이터를 전달하여줌.
    • 해당 함수는 매 요청 마다 호출되어지고 서버에서 실행되어진다.
      export const getServerSideProps = withSession(async function ({ req, res }) { 
      	const hello = req.session.get('hello'); 
      	return { 
      		props: { 
      			initialState: { 
      				hello: hello || null, 
      			}, 
      		}, 
      	}; 
      });

2. CSR

  • CSR = Client Side Rendering
  • 얘는 클라이언트에서 렌더링을 하는 방식이다.
  • 우리가 리액트 프로젝트를 만들면 보게 되는 index.html을 브라우저에 접속하면 다운받게 되고 index.html에 모든 페이지의 파일이 들어있기 때문에 이 파일이 로딩되면 모든 파일을 다운 받았다는 것이다. → 이후에 화면이 렌더링 된다.
  • 얘는 장단점이 뭘까?
    • 장점
      • 번들링된 파일 안에 모든 페이지의 js 파일이 있어 처음 로딩만 해준다면 새로고침 없이 페이지 전환이 가능해진다 → SPA?
      • index.html 파일만 네트워크 요청을 하면되서 네트워크의 서버 부담이 적어지게 된다.
    • 단점
      • 처음 렌더링 할 때 번들링된 큰 파일을 다운 받는 과정이 있어 첫 렌더링이 느리다
      • index.html 파일이 빈파일이기 때문에 SEO가 좋지 않다. → why?
      • 클라이언트에서 api 요청이 이뤄져서 보안에 취약해진다.

3. SSG

  • SSG = Server-Side-Generate?
  • SSG는 빌드 시점에 미리 페이지를 렌더링 한다. → 빌드 시에 HTML을 생성하고 매 요청마다 HTML을 재 사용한다. 그에 비해 SSR은 매 요청 마다 HTML을 생성하기 떄문에 응답 속도가 느리고 서버에 더 많은 부담이 가게 된다고 한다.
    • HTML 생성 시점? → next build 명령어를 사용할 때 생성 → 이후에는 CDN 캐시 → HTML 재사용
  • SSG에서 이제 재빌드가 필요하면 ISR을 사용하면 된다고 한다. → 얘는 또 뭐
  • 얘는 장단점이 뭘까?
    • 장점
      • 정적인 사이트를 구축할 때 좋은 효율을 낼 수 있다.
      • SEO가 굉장히 좋다
      • 렌더링 속도가 빠르다.
    • 단점
      • 동적인 페이지에 쓰면 ISR을 써도 성능상 문제가 된다.
  • 언제 사용하는 걸까?
    • 블로그 게시물과 같이 정적으로 생성된 정보를 요청마다 동일한 정보로 반환 해주는 경우에 사용되어진다.
  • getStaticProps
    • Next.js에서 SSG를 사용해 데이터를 받아오려면 getStaticProps를 사용하면 된다.
      • 서버 측에서만 실행되고, 클라이언트에서는 실행되지 않는다.
    • API를 통해 외부 데이터를 받는 용도로 Static Generation 하기 위해 사용하기 때문에 빌드 시에 한 번만 호출되며, static file로 빌드 된다.
    • 해당 페이지가 호출 되면 getStaticProps가 먼저 실행되고 데이터를 받아와서 props를 통해서 값을 담아 전달하게 된다.
    • 이렇게 만들고 build를 하게 되면 서버에서 API 호출을 해서 데이터를 담고 그 데이터가 담긴 HTML을 생성하게 된다.
    export async function getStaticProps() {
    	const res = await fetch(`https://.../data`);
    	const hello = await res.json();
    
      return {
        props: {
          initialState: {
            hello: hello,
          },
        },
      };
    }
    
  • 만약에 동적 라우터를 통해서 pages/hello/[id].js를 만들면?
    • id 별로 빌드 시에 id에 맞게 data를 불러와서 pre-render해줘야 한다.
    • id가 바뀌게 되면 그에 맞춰서 다시 요청을 날리고 pre-render 해줄 필요가 있다.
    • ⇒ 이러한 경우가 page의 path가 외부 데이터에 의존 하는 경우에 해당 한다
    • 이런 경우에는 getStaticPaths를 통해서 pre-render 되는 path를 명시해주면 된다.
    • path에 경로 값을 기준으로 해당하는 id 값들은 static file로 생성 되어진다.
    export async function getStaticPaths() {
      const API_URL = `/api/data`;
      const res = await (await fetch(API_URL)).json();
    
      return {
        paths: res.map((item) => ({
          params: {
            id: item.id.toString(),
          },
        })),
        fallback: true,
      };
    }
    
    export const getStaticPaths = async () => {
      return {
        paths: [
          { params: { id: '1' } },
          { params: { id: '2' } },
          { params: { id: '3' } },
        ],
        fallback: true,
      };
    };
    

4. Universal Rendering

  • SSR을 통해 빠른 FCP를 구현하고 클라이언트에서 하이드레이션을 통해 다시 렌더링 하는 방식
  • 초기 로딩시에는 SSR 처럼 작동하고 그 이후에는 CSR로 작동 하는 방식
  • 얘는 장단점이 뭘까?
    • 장점
      • SSR을 통해 빠른 FCP(얘는 또 뭘까..?)를 구현하여 CSR의 단점 개선
    • 단점
      • 별도의 서버가 필요
      • 프레임워크 학습에 러닝커브가 높다.
      • 실제로 클라이언트에서 자바스크립트가 실행되고 이벤트 헨들러가 적용될 때 까지 입력에 응답할 수가 없어 사용자 경험이 안좋아질 수 있다.

5. ISR

  • ISR = Incremental Static Regeneration → 증분 정적 재생성..? → 이게 무슨 의미여?
  • 런타임 중에 정적 페이지를 만들거나 업데이트 되도록 해주는 SSG와 SSR의 하이브리드 솔루션
  • Next.js에서 제공하는 기능이기도 하고, 다시 필드할 필요 없이 원하는 페이지별로 정적 생성을 사용할 수 있게 해준다.
    • 캐싱을 통해서 데이터가 확인 되면 최종 페이지가 캐시되고 사용자는 SSG와 마찬가지로 캐시된 버전의 페이지를 받는다
    • 재검증 시에도 사용자는 먼저 캐시된 버전의 페이지를 받고 업데이트 된 친구를 나중에 받는다. → State While Revalidate 전략
  • 얘는 장단점이 뭘까?
    • 장점
      • SSR과 달리 즉시 제공 되어진다.
      • 빠른 렌더링(?)으로 사용자 경험도 좋아진다.
    • 단점
      • 페이지 디자인에 따라서는 의미가 있는 요소도 페인팅을 지연시킬 수도 있다.

SSR 개념을 항상 쓰는게 좋은 걸까?

  • 위의 내용을 정리하고 나서 생긴 궁금함이 SSR을 쓰는게 장점이 많아 보이긴 하는데 항상 쓰는게 좋을까라는 궁금증이 좀 들었던 것 같다.
  • 마침 우아콘 영상을 돌려보던 중에 관련된 영상을 봤고 참고해서 내용을 좀 적어볼까 한다.

웹프론트 개발자들을 위한 '우아한스펙트럼' 서비스 제작기 #우아콘2023 #우아한형제들

SSR의 장단점 및 SEO에 강한 이유를 들여다보다