22sook00 logo
SookDev

Next.js - SSR&SSG&ISR 의 이해

tag
next.js
seo
react
date
Apr 2, 2023

🧩 웹의 역사

90년대 중반까지는 static sites

→ 서버에서 잘 만들어진 HTML 문서들이 있고 사용자가 어떤 사이트의 브라우저에 접속하면 서버에서 이미 만들어진 HTML 을 받아와서 보여주는 형식
🚨 페이지 내 다른링크 클릭 시 이미 만들어진 HTML을 또 받아와야 하는데 페이지 전체가 업데이트 되어야 하기 때문에 사용성에 좋지 않았다

96년, Iframe 의 등장

→ 문서 내에서 또다른 문서를 담을 수 있는 iframe 의 등장으로 페이지 내에서 부분적으로 받아와서 문서를 업데이트 할 수 있게 된다.

98년 XMLHttpRequest (fetch 의 원조) 개발

→ HTML 전체문서가 아니라 JSON 포맷으로 서버에서 필요한 데이터만 가볍게 받아올 수 있다.
자바스크립트를 이용하여 동적으로 HTML 생성하여 페이지에 업데이트하는 방식

05년 AJAX

→ 위에서 말했던 자바스크립트를 이용하여 동적으로 HTML 생성 후 페이지에 업데이트 하는 방식이 AJAX 라는 이름으로 공식화 되었고 gmail, google map 과 같은 웹 어플리케이션을 만든다.
⇒ 이것이 우리가 알고있는 SPA(Single Page Application)!
SPA에서는 보통 CSR 방식으로 렌더링을 한다.
초기에 웹어플리케이션에 필요한 자바스크립트를 서버로부터 받아 다운로드 하고,
사용자는 새로운 필요한 요청이 있을경우 부분적으로 필요한 데이터만 업데이트 한다.
하나의 어플리케이션을 사용하듯 보이기 때문에 사용성이 좋아졌다.

어느쪽에서 렌더링을 준비하냐, 방식의 차이에 따라 크게 CSR,SSR,SSG 로 나뉠 수 있다.

🧩 CSR

서버에서 index 라는 HTML 파일을 클라이언트에게 보내준다
예시로 리액트의 index.html 을 보면 body 내부가 매우 깔끔하다.
<body> <noscript>You need to enable JavaScript to run this app.</noscript> <div id="root"></div> </body>
위와같이 HTML은 텅텅 비어져있기 때문에 처음에 접속하면 빈화면만 보이게 된다.
유저가 웹사이트에 방문하면 브라우저는 서버에 콘텐츠를 요청하고 서버는 빈 뼈대의 HTML 을 응답으로 보내준다. 브라우저는 연결된 JS 소스를 통해 자바스크립트를 서버로부터 다운로드 받게 되는데,
JS 를 이용해 동적으로 페이지를 만들어서 브라우저에 띄워주게 된다.
이 자바스크립트에는 프레임워크, 라이브러리의 소스코드를 모두 포함하기 때문에 상당히 사이즈가 커 다운로드 받을 때 시간이 걸릴 수 있다.
또한 동적으로 DOM 을 생성하는 시간까지 있기 때문에 초기 로딩속도가 느리다.
대신 HTML과 자바스크립트가 완전히 연결된 상태에서 보이기 때문에 TTI===TTV 가 성립된다.
pros
👍🏻 초기로딩 이후 페이지 일부를 변경할때는 추가로 필요한 데이터만 서버에 요청하여 받아오기 때문에 이후의 구동속도는 빠르다.
👍🏻 서버는 빈 뼈대의 HTML 만 보내주면 되기 때문에 서버부담이 적다.
👍🏻  연산, 라우팅 등 모두 클라이언트 측에서 처리하기 때문에 반응속도와 UX 측면에서 좋다
cons
🚨 사용자가 첫 화면을 보기까지 시간이 오래 걸릴 수 있다.
🚨 SEO 에 좋지 않다. 검색엔진들은 서버에 등록된 웹사이트의 HTML 문서를 분석해서 검색엔진에 등록해야겠다고 판단하고 빠르게 검색할 수 있도록 도와주는데 CSR은 HTML이 텅텅 비어져있기 때문에 검색엔진들이 분석하는것에 제한이 있다.
solution
🌟 code splitting, tree-shaking, chunk 분리하여 JS 번들크기를 줄여 초기 DOM 생성속도를 줄여 초기 로딩속도 보완
🌟 pre-rendering 방식 사용. 라이브러리나 웹팩 플러그인을 통해, 각 페이지에 대한 HTML 파일을 미리 생성하여 사전에 렌더링 된 HTML 버전 페이지를 보여주는 방식을 사용하여 SEO 개선
🌟 SSR, SSG 도입하여 보완.
  • 프레임워크 없이 보완하는 방법 : express, nest 와 같이 별도의 서버 직접운영
  • 프레임워크 사용 :
    • 리액트 : Next.js(SSR,SSG 제공), Gatsby(SSG, lazy loading 빌드시점에 정적 HTML을 만드는 방식이기 때문에 페이지가 적고 작은서비스일 경우 적합)
    • 앵귤러 : Universal (앵귤러 4 부터는 합쳐짐)
    • 뷰 : Nuxt

Next.js 에서 CSR 사용하기

🧸
useEffect 안에서 받아온 데이터를 상태에 담아 보여주는 방식

🧩 SSR

SEO 가 안좋아? 다시 Static Sites 로!
HTML이 텅텅 비어져 있기 때문에 SEO에 좋지 않았던 CSR 방식을 개선하기 위해 다시 90년대 중반까지 쓰이던 Static Sites 에서 영감을 받았다.
웹사이트에 접속하면 즉시 서버에서 필요한 데이터를 모두 가져와 HTML 파일을 만들고 CSS 까지 모두 적용한 HTML파일을 동적으로 제어할 수 있는 소스코드(JS)와 함께 클라이언트에게 보내주게 된다.
클라이언트 측에서는 HTML 문서를 받아와 바로 사용자에게 보여줄 수 있고 그동안 자바스크립트를 다운받아 HTML의 JS 로직에 연결한다.
 
SSR은 요청 시 서버에서 즉시 HTML 을 만들고 응답하기 때문에 데이터가 달라지거나 자주 바뀌어서 미리 만들어 두기 어려운 페이지에 적합하다.
pros
🌟 CSR 에 비해 더 빨라진 초기 로딩속도
🌟 모든 컨텐츠가 HTML 에 담겨있기 때문에 효율적인 SEO 를 할 수 있다.
cons
🚨 Static Site의 고질적인 문제인 Blinking issue 가 여전히 발생한다.
→ 사용자가 클릭 시 전체 데이터를 서버에서 다 받아오기 때문에 썩 좋지않은 사용자경험
🚨 서버과부하
→ 사용자가 클릭 할 때마다 서버에서 필요한 데이터를 가져와 HTML 을 만들어야 하기때문
🚨 사용자가 빠르게 초기 웹사이트를 확인할 수 있다는 장점은 있지만 동적으로 데이터를 처리하는 자바스크립트를 다운로드 받지 못해 아직 처리하지 못할 경우 아무액션 없이, 이벤트가 일어나지 않을 수가 있다.
 

TTV (Time To View) 와 TTI (Time To Interact)

CSR 의 처리시간 흐름
사이트접속 → 서버에게서 텅빈 인덱스파일을 받아옴 → 사용자에게 아무화면도 보여지지 않는다. → HTML 파일에 링크되어있는, 웹사이트에서 필요한 모든 로직이 담겨있는 자바스크립트를 서버에 요청 → 동적으로 HTML을 생성할 수 있는, 웹어플리케이션 로직이 담긴 자바스크립트를 받아온다 → 웹사이트가 사용자에게 보여지게 된다.
⇒ 사용자가 웹사이트를 볼수있음(TTV)과 동시에 클릭/인터렉션(TTI)이 가능하게 된다.
⇒ 최종적으로 번들링해서 사용자에게 보내주는 자바스크립트 파일을 어떻게하면 효율적으로 많이 분할해서 첫번째로 사용자가 보기위해 필요한 필수적인 아이만 보낼 수 있을지 고민해보면 좋다.
SSR 의 처리시간 흐름
사이트 접속 → 서버에게서 이미 잘 만들어진 HTML 받아오게 된다. → 사용자가 웹사이트를 볼 수 있다. → 동적으로 제어할 수 있는 자바스크립트 파일은 받아오지 않았으므로 사용자가 클릭을해도 아무런 액션이 발생하지 않음 → 최종적으로 자바스크립트 파일을 서버에서 받아와야 TTI 가 가능해진다.
⇒사용자가 볼 수있는 시간(TTV)과 이용할 수 있는 시간(TTI) 의 공백기간이 꽤 긴편
⇒사용자가 보고 인터렉션하는 시간의 단차를 줄이기 위해 어떤노력을 해야 하고, 더 매끄러운 UI,UX 를 제공할 수 있을 지 고민하면 좋을 것 같다.
🧸
웹사이트의 성능을 볼 때 TTV 와 TTI 도 중요한 척도가 될 수 있다.

Next.js 에서 SSR 사용하기

🧸
getServerSideProps 를 사용한다.
//example export async function getServerSideProps(){ const res = await get(`/users`) const users = await res.json(); return { props : { users } } }

🧩 SSG & ISR

SSG

CSR과 SSR 의 장점을 묶어서! Static Site Generation (Static Rendering)
서버에서 HTML 을 만들어 보내준다는 측면에서는 SSR 과 비슷하지만
언제 만들어주느냐 (빌드시점) 에 차이가 있다.
페이지들을 서버에 모두 미리 만들어 둔 후 요청 시 해당 페이지에 응답하기 때문에 바뀔일이 거의 없어서 캐싱해두면 좋은 페이지에 적합하다.
 
빌드 시 HTML에 데이터를 담아 미리 파일을 만들고 접속하는 유저들에게 보여준다.
이미 만들어진 페이지를 여러사람에게 보여주는거라 서버부담이 적고 응답속도가 빠르다.
보통 마케팅페이지, 블로그 글등 한번 만들고나면 변화가 거의 없는 서비스에 사용된다. 즉 한번만들어놓으면 변화가 없다.
하지만 뭔가 변경된다면?
빌드와 배포를 다시 해야한다. 디비에서 뭔가 수정이 되어도 이미 만들어진 페이지에는 변화를 줄 수 없다. 다시 빌드하는 방법외엔 없다.
동적컨텐츠가 많을때는 SSG 를 사용하지 않는다.

Next.js 에서 SSG 사용하기

🧸
getStaticProps 를 사용한다.
//example export async function getStaticProps(){ //빌드할 때 해당 API 를 호출하여 HTML을 만들고 여러 //유저들에게 보여주는 방식 const res = await get(`/users`) const users = await res.json(); return { props : { users } } }

ISR (Incremental Static Regeneration)

SSG 와 동일하게 빌드 시점에 페이지를 생성한다.
일정시간이 지난 후, 최신 데이터로 업데이트 하여 페이지 새로 생성

Next.js 에서 ISR 사용하기

🧸
SSG와 같이 getStaticProps 를 사용하지만 revalidate 라는 속성이 추가
//example export async function getStaticProps(){ //빌드할 때 해당 API 를 호출하여 HTML을 만들고 여러 //유저들에게 보여주는 방식 const res = await get(`/users`) const users = await res.json(); return { props : { users }, revalidate: 60 //초를 의미. 즉, 1분뒤에 접속하면 자동으로 재생성 } }
빌드 시 볼 수 있는 부분
notion image
이제 네가지를 살펴봤으니 데이터를 가져올때 새로고침으로 비교해보겠다.

일반 새로고침

CSR은 페이지가 로드되고 API 가 호출되기때문에 새로고침 시 깜빡임 현상이 발생한다. 나머지는 새로고침해도 서버에서 HTML 자체를 가져오기 때문에 깜빡임 현상이 없다.

데이터 변경 후 새로고침 시

CSR 은 깜빡임은 있지만 최신 데이터가 적용된다.
SSR 은 깜빡임 없이 최신데이터가 적용된다. (그러나 약간 느림)
SSG 는 Static 한 파일을 제공하기 때문에 최신데이터 반영이 안된다.
ISR 은 마찬가지로 Static 한 파일을 제공하기 때문에 최신데이터 반영이 안되지만 revalidate 옵션으로 지정해놓은 시간 뒤에 실행이 된다.
 
빌드 된 파일 CLI 창에서 보기
ls -al .next/server/pages/
notion image
isr.html, isr.json 만 수정시간이 변경된것을 볼 수 있다.