두개의 단어는 서로의 대척점 에 있다고 볼 수 있다.
CSR <-> SSR
해당 개념에 대해 헷갈리지 않도록 조금 더 쉽게 작성해보기로 하였다.
웹 브라우저(크롬, 웨일, 파이어폭스, IE, ...)에서 실제로 우리가 볼 수 있는 화면이 있다.
이러한 화면을 어디서 렌더링하는 지에 따라서 ①CSR과 ②SSR로 구분지을 수 있다.
대표적인 클라이언트 사이드로 React를 예로 들 수 있다.
CSR, 즉 클라이언트 사이드 렌더링은 SPA(Single Pace Application)에서 주로 쓰이는 기법으로 처음에 브라우저가 서버에 요청할 때 데이터가 없는 문서를 반환한다.
📁 src/App.js
function App() {
return (
<div className="App">
<h1>Hello React</h1>
</div>
);
}
export default App;
우리는 기본적으로 App.js 컴포넌트 내부에 <h1>Hello React</h1> 과 같이 작성하였지만, 넘겨 받은 index.html에는 id가 root인 빈 태그를 리턴받았다.
이는 CRA(Create-React-App)을 통해 초기화된 리액트 디렉토리의 구조때문이다.
우리는 리액트를 통해 빌드된 어플리케이션 구조의 최상위에 index.js로 하여금 index.html에 보내주고 있다.
📁 src/index.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById("root")
);
reportWebVitals();
이를 public/index.html에 DOM API로 하여금 삽입하는 과정을 거친다.
📁 public/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
...
<title>React App</title>
</head>
<body>
<!-- 바로 이곳에 id가 root인 div 태그 내부에 우리가 만든 index.js가 들어가게 된다. 🔥-->
<div id="root"></div>
</body>
</html>
HTML 및 정적인(static) 파일들이 로드 되면서 데이터가 존재한다면, 데이터 또한 서버에 요청하고, 응답이 완료된 데이터들이 화면 상에 나타나게 된다.
웹 브라우저가 서버에 HTML과 정적 파일들 (.css/.js/.svg/.png/ ...)을 요청한 후 로드되면 사용자의 상호작용에 따라 자바스크립트 엔진에 의해 동적으로 렌더링된다. 필요에 따라 데이터를 서버에 요청하고 받아와 이를 렌더링한다.
실제 작성한 h1 태그로 감싸진 데이터는 main.chunk.js에 들어있다.
CSR을 사용할 경우 첫 HTTP 요청이후 HTML과 그외의 정적인 파일들을 다 받을 경우, 바로 브라우저에 렌더링된다.
따라서 TTV(Time to view 보여지는 시간)와 TTI(Time to interaction 상호작용 가능한 시간)이 일치한다.
렌더링이 완료된 상태에서는 모든 이벤트를 통해 정상적인 사용이 가능하다는 이야기이다.
SSR의 경우 완전히 만들어진 HTML 파일을 받아와 렌더링한다.
MVC 패턴 중 View 위주로 만들어진 React/Angular/Vue 를 제외한 일반적인 index.html을 렌더링하는 방법은 SSR이라고 봐도 무방하다.
📁 index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<h1>
Hello HTML!
</h1>
<button type="button" class="btn">index2로 가기</button>
<script>
document.querySelector('.btn').addEventListener('click', () => {
location.href=`http://127.0.0.1:5500/csr/index2.html`
})
</script>
</body>
</html>
📁 index2.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<h1>
Hello Index2!
</h1>
<button type="button" class="btn">되돌아가기</button>
<script>
document.querySelector('.btn').addEventListener('click', () => { history.go(-1)})
</script>
</body>
</html>
서버에 리소스(index.html, index2.html)를 요청할 때마다 브라우저에 새로고침이 일어나고 매 상황마다 서버에 새로운 페이지를 렌더링해주는 방식이다.
해당 리소스의 응답에는 모두 html 파일이 들어있는 상태이다.
하지만 기본적으로 html 을 먼저 반환해주기 때문에 비동기를 통한 Ajax 요청의 기간이 길어지거나, 요청하는 데이터의 양이 많을 경우 TTV(Time to View)와 TTI(Time to Interaction)이 일치하지 않을 수 있다.
렌더링은 되어 이미 보이지만, 렌더링된 해당 요소들에 대해서 이벤트가 전부 실행되지 않을 수 있다는 뜻이다!