next.js가 왜 생겨났는지, 또 어떤 기능을 하는지 이해하기 위해서는, 웹 애플리케이션의 페이지 구성 방식(SPA, MPA)과 렌더링 방식(CSR, SSR)에 대해서 우선 알아야 한다. 그래서 이번 글에서는 SPA, MPA, CSR, SSR에 대해 설명한다.
1990년대 중반까지는 모두 static한 사이트를 사용했음
=> 서버에 이미 잘 만들어진 html 문서들이 있고, 사용자가 브라우저에서 이 HTML을 계속 받아와 보여주는 방식
=> 즉, 여러 HTML 페이지로 어플리케이션을 구성하는 MPA 방식을 사용했음.
이러한 정적인 사이트의 문제점
iframe 태그의 도입
=> 페이지 내 부분적인 업데이트 가능
XMLHttpRequest API (fetch API의 원조격) 의 등장
=> html 문서 전체가 아닌 json 같은 포맷으로 서버에서 필요한 data만 받아올 수 있게 됨
Ajax의 등장
=> 서버에서 data를 동적으로 받아와 웹 페이지를 갱신하는 방식이 Ajax라는 명칭을 가지게 되고, 구글이 이 Ajax를 적극적으로 사용하면서 유행하게 됨(구글지도, Gmail)
=> Ajax를 기반으로 한 SPA가 등장하기 시작
SPA의 등장
=> 사용자가 한 페이지 내에서 머무르면서 필요한 data를 서버에서 받아와 부분적으로만 업데이트 => 하나의 어플리케이션을 사용하듯 웹 사이트에서도 사용성이 좋아지게 됨
CSR의 등장
SPA를 구현하기에 알맞는 렌더링 방식인 CSR을 기반으로 한 Anlgular, Vue, React 등의 프레임워크의 등장 => CSR 시대로 접어든다.
말 그대로 페이지가 하나인 어플리케이션 => 기본 HTML 파일을 하나로 두는 것
다른 페이지로 이동할 때 새로운 HTML 파일을 가져오는 것이 아니라 필요한 부분만을 수정하여 바꿔끼우는 방식
initial request 시에 모든 정적 리소스들(이미지, HTML, JS 등)를 다운받고, 해당 페이지에 필요한 데이터는 클라이언트에서 요청하여 서버로부터 동적으로 받아온다.
프론트엔드 프레임워크 3대장인 React, Vue, Angular가 SPA 방식을 채택하면서 유명해졌다.
SPA를 구현하는 데 주로 CSR 렌더링 방식이 사용된다 .
=> 하지만 SPA가 항상 CSR의 방식으로 렌더링되지는 않는다. SPA에서 SSR을 구현하도록 하는 장치들도 있다(ex) Next.js). 그러나 대부분의 SPA가 필요한 내용을 client 측에서 서버 측에게 요청을 보내기 때문에, SPA는 CSR로 렌더링한다는 얘기가 붙여지는 것 같다. 이는 클라이언트에 렌더링 책임을 넘긴다는 의미와 일맥상통한다.
여러 개의 페이지를 두는 방식
유저가 페이지를 새로고침하거나 다른 페이지로 이동할 때마다 해당 페이지의 HTML file을 서버로부터 다시 받아야 한다.
이때 HTML 파일은 이미 컨텐츠가 들어있는 상태의 HTML file을 받는다. 그러므로 이미 server에서 rendering된 파일들을 주기 때문에, MPA는 SSR이라는 렌더링 방식을 채택한다.
=> MPA는 SSR의 방식으로 렌더링될 수 밖에 없다.
MPA 방식이 전통적인 웹사이트 구현 방식이었다. 이는 CSR보다 SSR이 전통적인 렌더링 방식이었음을 알 수 있다.
SPA | MPA |
---|---|
CSR로 구현 O | CSR로 구현 X |
SSR로 구현 O | SSR로 구현 O |
일반적으로는 SPA로 페이지를 구성할 경우 CSR 렌더링 방식을 채택하고, MPA로 페이지를 구성할 경우 SSR 렌더링 방식을 사용한다.
SPA는 웹 어플리케이션에 필요한 리소스들을 초반 한 번에 모두 받고, 그 이후 새로운 페이지 요청이 있을 때 필요한 Data만 전달받아 클라이언트 페이지를 갱신하기 때문에, 자연스럽게 렌더링 방식으로 CSR을 사용하게 되는 것
MPA는 새로운 요청이 있을 때 마다 서버에서 미리 렌더링 된 정적 리소스를 받아오기 때문에, 렌더링 방식으로 SSR을 사용하는 것.
그렇다면 CSR과 SSR이 정확히 어떤 개념인지 알아보자 !
페이지 이동 시 깜빡임이 없다.
새로운 HTML 파일을 받아오는 것이 아니라 필요한 부분만 서버에 요청하여 업데이트하기 때문
서버에 부담이 적다.
필요한 부분만 요청하기 때문에, 서버 리소스를 상대적으로 덜 낭비한다. 한 번 다운로드 받은 정적 리소스들은 cache로 저장하기 때문에, 다시 받아오지 않는다.
첫 방문 후에는 로딩이 빠르다.
첫 방문 시에 필요한 모든 리소스들을 다운 받고, 이후 다른 페이지로 라우팅할 때는 그 페이지에 해당되는 클라이언트에서 서버로 요청하면 된다. 그러므로 다른 페이지로 이동할 때마다 모든 파일을 다시 요청할 필요가 없다.
초기 로딩 속도가 느림
HTML 파일도 받고, JS 파일도 받고 그 이후에서야 렌더링 되기 때문
좋지 않은 SEO
보통 CSR은 맨 처음에 텅텅 비어있는 HTML 파일을 받기 때문에, 검색 엔진이 색인을 할만한 컨텐츠가 존재하지 않음.
=> 이러한 문제점들이 생기니, 최근에는 예전 static 사이트에서 영감을 받은 SSR이 도입됨
서버에서 렌더링을 담당하는 방식
서버에서 필요한 data를 가지고 HTML 파일을 만들고, 이 HTML 파일과 이 파일을 동적으로 제어할 수 있는 소스코드를 함께 클라이언트에게 보내줌
=> 브라우저에서는 이를 바로 사용자에게 보여줄 수 있음
화면 깜빡임 이슈
사용자가 클릭 시, 전체적인 HTML 파일을 다시 서버에서 받아와야 하기 때문
서버 과부하
사용자가 많은 서비스일수록, 사용자 클릭마다 서버에 요청해서 필요한 HTML을 만들어야 하기 때문
사용자가 페이지를 볼 수는 있지만, 사용자가 Interaction을 해도 반응이 없을 수 있음
HTML 파일은 받아왔지만, 동적으로 data를 처리하는 JS파일은 아직 다운로드 받지 못한 상태이기 때문
=> SSR은 TTV ~ TTI 사이의 간격이 좀 있기 때문
TTV(Time To View) : 사용자가 웹사이트를 보는데까지 걸리는 시간
TTI(Time To Interact) : 사용자가 클릭을 하거나 인터렉션이 가능하게 되는데 걸리는 시간
CSR은 화면이 보일 때부터, 유저가 상호작용할 수 있음
=> HTML 파일을 처음 받아올 때는 보통 텅텅 비어있고, 동적으로 HTML을 생성할 수 있는 로직이 담긴 JS 파일을 받아와 브라우저가 이를 실행해야 화면이 보이고, 유저와 상호작용할 수 있기 때문
=> TTV = TTI
=> 반대로 SSR은 우선 렌더링된 HTML 파일을 받아오면, 그때부터 Viewable 하지만, 이후 js 파일을 받아와 이를 실행해야 상호작용 가능
=> TTV와 TTI 사이의 간격이 꽤 있는 편
=> TTV < TTI
기존 SSR의 문제점
=> Server-side와 Client-side에서 각각 렌더링을 위한 코드를 따로 만들어야 한다.
=> 이를 해결하기 위해 Isomorphic(Universal)이라는 방식이 등장, 이는 같은 코드로 Server와 Client에서 동일하게 실행되는 환경을 의미한다.
=> 그래서 SSR을 구축할 때는 같은 언어로 동작하는 Node.js를 서버로 사용하는 경우가 많다.
=> 이러한 SSR을 Isomorphic 방식으로 제공하는 것이 Next.js이다.