프로젝트에서 Next.js 사용하다 클라이언트 사이드 렌더링과 서버 사이드 렌더링에 대한 정리가 필요하겠다 싶어 글을 남깁니다. 참고로 이 글은 각 라이브러리, 프레임워크 동작에 대한 깊은 내용은 없습니다. 렌더링 방식에 대한 큰 그림을 그리기 위해 정리한 글이니 이 점 참고해주시면 감사하겠습니다.
frontend server, backend server
먼저 웹에서의 각 구성요소들이 어떤 상호작용을 갖는지 전체적인 그림을 살펴봅시다.
일단 서버(server)는 네트워크를 통해 클라이언트에게 자원을 제공하는 컴퓨터를 의미하고, 어떤 대상이 요청을 보내면 요청에 대한 응답을 넘겨주는 모든 주체를 서버(server)라고 합니다. 웹 환경에서는 웹 브라우저가 클라이언트(client), 웹 콘텐츠가 있는 컴퓨터가 서버인데 서버는 크게 프론트엔드 서버와 백엔드 서버로 나눠집니다.

📢 프론트엔드 서버 : 클라이언트에게 HTTP 요청을 받아 정적 파일(HTML, CSS, Javascript, 이미지 등)을 제공하는 역할을 하고, 웹 서버라고 하기도 합니다.
📢 백엔드 서버 : 클라이언트가 요청한, 비즈니스 로직을 통해 가공된 데이터를 제공합니다. 즉 페이지에서 필요한 동적 데이터를 제공하는 역할을 하고, API 서버에 가깝다고 할 수 있습니다.
Interaction
브라우저, 프론트엔드 서버, 백엔드 서버의 상호작용
그렇다면 이제 각각 요소가 어떻게 동작하는지 살펴봅시다. 먼저 사용자가 브라우저에 접속해 특정 URL에 접근하는 전체 흐름을 알아볼텐데요, 우리가 자주 사용하는 React의 상호작용을 기준으로 설명할 예정입니다. (이러면 클라이언트 사이드 렌더링에 대한 설명에 가깝겠군요! 참고로 이 글은 React 18 미만 버전 기준으로 설명합니다. 다른 렌더링 방식과의 차이점은 후술하겠습니다.)

준비단계
React 라이브러리는 브라우저 단에서 실행되는 정적 파일을, 특히 JavaScript를 편리하게 코딩할 수있도록 도와주는 수단이라고 생각하면 편합니다. 이를 활용해 클라이언트에게 제공할 JavaScript 파일들을 작성하면, Babel과 같은 트랜스파일러가 그것들을 대부분의 브라우저에서 호환이 가능한 문법(ES5)의 코드로 변환해줍니다. 또한 Webpack 등의 모듈 번들러는 HTML, CSS, JavaScript 등의 파일들을 효율적인 방식으로 번들링 하여 프로젝트 루트에 준비해두는데요, 실제 배포 시에는 이렇게 프론트엔드 서버의 루트에 번들링 되어있는 파일들을 웹 서버가 브라우저에 제공할 수 있도록 설정해 놓습니다.
🔅 참고 : CRA으로 생성한 React 프로젝트의 개발 환경에서는 흔히 npm start (package.json에 등록된 스크립트)로 실행되는 개발 서버(webpack-dev-server)가 파일이 수정될 때마다 위와 같은 변환 작업들을 자동으로 수행해줍니다. 이 서버는 라이브 리로드 기능을 제공하는 개발용 서버로, 원래 빌드를 실행하면 번들링된 파일을 생성하기 때문에 규모가 큰 프로젝트라면 시간이 걸립니다. 그러나 webpack-dev-server는 실제 번들링 된 파일을 생성하지 않고, 번들링된 결과를 메모리에 저장하기 때문에 빌드 속도가 빠릅니다. 따라서 개발 시에 webpack-dev-server를 둔다면 매번 빌드를 실행하지 않고도 유사한 환경을 제공할 수 있다고 합니다.
렌더링 과정
유저가 브라우저 상에서 요청을 보내면, 즉 클라이언트가 요청을 보내면 프론트엔드 서버는 미리 준비해둔 static 파일들을 클라이언트에게 제공하는데요, 그러면 클라이언트의 브라우저는 전달받은 JavaScript 파일을 실행하여 페이지에 렌더링을 시작합니다. 앞서 React 라이브러리를 활용하여 코딩했던 JavaScript 코드는 동적으로 DOM에 렌더링을 해주기 위한 코드였죠. 렌더링 과정에서 브라우저가 자바스크립트 코드를 한 줄 한 줄 읽다가 DB의 데이터가 필요한 경우, 백엔드 서버에게 API 요청을 보내서 필요한 데이터를 요청합니다. 이러한 맥락에서는 백엔드 서버를 API 서버라고 부릅니다.
이후, 부분적으로만 리렌더링이 필요해지는 경우에는 페이지를 리로드 하지 않고 사용자 액션(cf. 버튼 클릭)을 감지하여 JavaScript가 마찬가지 방식으로 백엔드 서버에게 API 요청을 보내게 됩니다. 이때는 딱 해당 부분의 리렌더링에 필요한 데이터만 요청하게 되고, 그러면 JavaScript가 그 응답 데이터를 가지고 필요한 부분만 리렌더링을 해주게 됩니다.
주로 하는 질문
Q. 클라이언트(웹 브라우저)에서 필요한 데이터는 누구를 통해 얻어오는 것인가요?
브라우저 ↔ 프론트엔드 서버 ↔ 백엔드 서버인지, 브라우저 ↔ 백엔드 서버인지 조금 헷갈리는데요.
A. 어떤 라이브러리와 프레임워크를 사용하느냐에 따라 다르고, 둘 다 가능합니다.
브라우저가 페이지 렌더링을 할 때, 일단 최초 한 번은 프론트엔드 서버에서 HTML을 전달받고, HTML을 받으면서 필요한 CSS와 JavaScript 소스코드들을 프론트엔드 서버에서 함께 다운로드 받게됩니다. 그 이후부터는 자바스크립트를 읽으며 서버의 API를 호출하는 것이죠. 여기에서 처음 HTML과 CSS, JS를 내려주는 서버와 API 서버는 같은 서버일 수도 있고, 서로 다른 서버일 수도 있습니다.
1번과 같이 완전히 분리되어 있을 수도 있고, 2번과 같이 구성되어 있을 수도 있는데 1번의 경우 각 서버의 도메인이 다르다면 CORS 같은 부분을 신경써야 하는 특징이 있습니다.
곧 나올 서버 사이드 렌더링, 클라이언트 사이드 렌더링과도 연결되는 내용이니 잠시만 기다려주세요!
방식 1️. 사용자 ↔ 프론트엔드 서버
방식 1. 사용자 ↔ API 서버(1), API 서버(2)
방식 2. 사용자 ↔ 프론트엔드 서버(UI 제공, API 연동) ↔ API 서버(1), API 서버(2)
각 서버를 분리한 이유?
데이터가 많아질 경우를 대비해 분리해서 따로 컨트롤을 하기 위함인데요, 크게 속도적 차원과 관리적 차원의 이유가 있습니다. 서버를 한 곳에 모아둘 경우 속도가 느려지기 때문도 있고, 화면을 그려주는 방식과 데이터를 제공하는 방식은 많이 달라서 따로 분리해 관리하는게 편리하다는 이유도 있습니다. 큰 서비스를 관리할 때는 구조 파악이 빨라 유지보수에도 좋을 것이라 생각합니다.
CSR과 SSR
드디어 CSR(클라이언트 사이드 렌더링), SSR(서버 사이드 렌더링)을 알기 위한 준비가 끝났습니다.
이제 각 렌더링이 어떤 특징을 가지고 있는지 알아볼텐데요, 웹이 어떤 렌더링 과정을 거쳐 왔는지 가볍게 흐름을 따라가봅시다.
역사와 함께 CSR, SSR 살펴보기
1990년대 static 사이트의 시대를 지나, 90년대 말 fetch API의 원조 XMLHttpRequest API가 개발 됩니다. HTML 문서 전체가 아니라 JSON과 같은 포맷으로 서버에서 가볍게 필요한 데이터만 받아올 수 있게 되는 것이죠. 그 데이터를 이용해 동적으로 자바스크립트가 HTML 요소를 생성, 페이지에 업데이트 하는 방식이 많이 쓰이게 되는데 이런 방식을 AJAX라고 합니다.
이러한 방식이 점차 고도화 되면서 현재 널리 쓰이고 있는 SPA(Single Page Application) 싱글 페이지 어플리케이션까지 발전하게 되는데요, 사용자가 한 페이지 내에서 머무르면서 필요한 데이터를 서버에서 받아와서 부분적으로만 업데이트하는 방식의 웹 어플리케이션이 바로 SPA입니다.

전체 페이지 리로드가 아닌 갈아 끼우는 방식이 됨으로써 사용성이 개선되는 등의 이점 덕분에 Angular 프레임워크, React 라이브러리, Vue 프레임워크와 같은 친구들이 등장합니다. SPA에 대해 설명한 이유는, 싱글 페이지일 때와 멀티 페이지일 때 (렌더링할 HTML이 여러 개 일 때) 렌더링 방식에 차이가 있기 때문입니다.

CSR (Client Side Rendering)

클라이언트 사이드 렌더링은 앞서 말한 SPA(Single Page Application)에서 쓰이는 기법으로, 클라이언트(브라우저) 에서 화면을 구성합니다. 프론트 서버에서는 처음에 index라는 HTML 파일을 클라이언트에게 보내는데, 그 HTML 파일의 body 안에는 id가 root인 박스 하나만 들어가 있습니다.
<!--index.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
...
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
이 태그를 통해 우리가 필요로 하는, 번들링된 static 파일들을 가져오게 됩니다.
// index.tsx를 보면 id root를 통해 연결된 것을 확인할 수 있다.
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
const rootElement = document.getElementById('root');
if (!rootElement) throw new Error('Failed to find the root element');
const root = ReactDOM.createRoot(rootElement);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
// 대부분의 로직은 <App /> 컴포넌트 안에 작성되어 있다.
);
🔅과정 3줄 요약
1️⃣ 브라우저가 전달받은 초기 HTML은 텅텅 비어져 있기 때문에 일단 처음 접속시 빈 화면만 보입니다.
2️⃣ 그 HTML에 링크된 번들링된 자바스크립트 코드들을 프론트엔드 서버로부터 다운로드 받습니다.
3️⃣ 브라우저는 자바스크립트 코드를 한 줄 한 줄 읽으며 UI 생성, 이벤트 등록과 같은 기능 붙여주기, 필요한 데이터가 있으면 API 서버(주로 백엔드 서버)에 요청해 데이터 받아오기 등을 통해 동적으로 HTML을 생성해 사용자에게 최종적인 어플리케이션을 보여주는 과정을 반복합니다.
브라우저가 클라이언트라고 했죠?
간단히 말해 이렇게 브라우저에서 HTML이 최종적으로 완성되니 클라이언트 사이드 렌더링이라고 부르는 것입니다.
이 때 전달되는 자바스크립트는 우리 어플리케이션에서 필요한 로직들 뿐만 아니라 어플리케이션을 구동하는 프레임워크와 라이브러리의 소스 코드들도 다 포함이 되어 있기 때문에 굉장히 사이즈가 크고, 다운로드 받는데도 시간이 소요될 수 있습니다. (이 부분이 CSR의 단점으로 작용하는데, 최근에는 코드 스플리팅 (초기에 받아오는 자바스크립트를 잘 쪼개려는 과정)을 통해 개선하려는 움직임이 있습니다만..)
다르게 생각하면 초기 진입 속도는 느리지만, 그 후론 필요한 데이터만 갱신하면 되기 때문에 서버 부하를 덜 수 있어 좋다고 생각할 수 있습니다. 또한 초기 진입을 하고 나면 사이트 내에서 돌아다닐 때 로드되는 과정이 적어 사용성이 좋다는 장점도 있고요.

그러나 앞서 말한 사용자가 첫 화면을 보기까지 시간이 오래 걸릴 수 있다는 점과 더불어
썩 좋지 않은 SEO(Search Engine Optimization) (구글, 네이버의 검색 엔진들은 HTML의 body 부분을 보고 이 웹사이트가 어떤 정보를 담고 있구나 하고 알아차려 사용자가 검색을 했을 때 적절한 내용을 담고 있으면 검색 결과의 상단에 올려주는데, CSR에서의 HTML body는 대부분 텅텅 비어있기 때문에 검색 엔진들이 CSR로 작성된 웹페이지 분석에 어려움을 겪습니다.) 에 불편함을 느껴 결국 이전 static Sites에서 영감을 받은 SSR(Server Side Rendering), 서버사이드 렌더링이 등장합니다.
SSR (Server Side Rendering)

서버 사이드 렌더링 은 서버로부터 완전하게 만들어진 HTML 파일을 받아와, 페이지 전체를 렌더링 하는 방식입니다. 클라이언트에서 대부분의 것들을 처리하는 방식과는 다르게 서버 사이드 렌더링은 사용자가 일단 웹 사이트에 접속하면, 서버에서는 필요한 데이터를 모두 갖춰진 완성된 HTML 파일을 만들게 되고, 이렇게 잘 만들어진 HTML 파일을 동적으로 제어할 수 있는 소스코드와 함께(cf. JavaScript) 클라이언트에게 보내주게 됩니다. 클라이언트 측에서는 잘 만들어진 HTML 문서를 받아와서 바로 사용자에게 보여줄 수 있게 되는 것입니다.

그 과정을 자세히 보면, 웹 페이지에 사용자가 접속하면 클라이언트인 브라우저는 초기 화면을 로드하기 위해 프론트 서버에 요청을 보냅니다. 프론트 서버는 화면을 표시하는데 필요한 데이터를 백엔드 서버에서 얻어와 모두 삽입하고 CSS 까지 모두 적용해서 렌더링 준비를 마친 HTML과 JavaScript 코드들을 브라우저에 응답으로 전달 합니다. 코드를 전달받은 브라우저는 JavaScript를 한 줄 한 줄 읽으며 이벤트, 화면 구현을 위한 기능을 붙여주며 페이지를 완성합니다. (클라이언트 사이드 렌더링과 다르게 화면 UI는 동적으로 만들 필요없이 이미 완성되어 있습니다.) JavaScript 실행은 브라우저에서 해야하는 것이므로 서버는 서버단에서 해줄 수 있는 일을 대부분 다해주고 클라이언트에게 자원을 넘겨주는 것이죠.
이런 SSR을 이용하게 되면 CSR을 사용할때 보다 첫 페이지 로딩이 빨라지는 장점이 있고, 모든 컨텐츠가 HTML에 담겨져 있기 때문에 조금 더 효율적인 SEO를 할 수 있습니다.
그러나 SSR에도 큰 문제점이 있는데
1️⃣ static sites에서 있던 깜빡임 이슈(Blinking issue, 화면이 로드될 때마다 변경된 HTML을 로드해줘야 해서 화면이 백지로 변하고 다시 화면이 출력되는 이슈)가 여전히 존재합니다.
2️⃣ 화면에서 바뀌지 않아도 되는 부분도 다시 요청하고 렌더링 되기 때문에 서버 부하가 클 수 있습니다.
3️⃣ TTV(Time To View)와 TTI(Time To Interact) 간에 시간 차가 발생합니다. 사용자가 웹페이지를 볼 수 있는 시점을 Time To View라고 하고, 동적으로 인터랙션을 할 수 있는 시점을 Time To Interact라고 합니다. 즉, 사용자가 빠르게 웹사이트를 확인할 수는 있지만 동적으로 데이터를 처리하는 자바스크립트를 아직 다운로드 받지 못해 여기저기 클릭했는데 반응이 없는 경우가 발생할 수 있어 사용자 경험에 좋지 않을 수 있습니다.
아래 위 이미지는 CSR로 구현된 페이지, 아래는 SSR로 구현된 페이지입니다. 오른쪽에서는 자원을 요청할 때마다 찰나지만 깜빡임 현상이 있는 것을 확인할 수 있죠?


CSR과 SSR의 장단점 정리하기
이제까지 얘기한 각 렌더링 방식의 장단점을 간단히 표로 정리해봤습니다. 결국 CSR과 SSR의 차이는 표시해줄 화면을 어디서, 어떻게 그리냐의 차이가 있다는 것, 각각의 장단점에는 무엇이 있는지 알고 가면 좋을 듯 합니다!

보완책
CSR과 SSR는 어떻게 보완할 수 있을까?
클라이언트 사이드 렌더링과 서버 사이드 렌더링은 위에서도 살펴봤다시피 각각의 장단점이 존재하는데요, 요즘에는 꼭 CSR 또는 SSR만을 고집해서 사용하기보다는 SSG(Static Site Generation) 의 기조로 각각의 단점을 보완해 사용하기도 합니다.
SSG(Static Site Generation) 방식

회사, 학교 소개 페이지 같은 정적 페이지의 경우는 모든 유저에게 항상 동일한 화면이 보이기 때문에 처음 한 번만 생성한 이후에 어딘가에 저장해 두고 필요할 때마다 로드해도 큰 문제가 없습니다. 이렇게 정적 웹페이지를 빌드한 후 결과물을 CDN으로 배포하고, 필요할 때마다 CDN으로부터 전달받은 정적 페이지를 유저에게 빠르게 제공하는 방식을 SSG라고 합니다. CDN에 배포한 이 정적 페이지에 동적인 행동 매 번 새롭게 추가되는 경우에는 해당 프로젝트를 다시 빌드해주면 됩니다. (React에서는 Next.js나 Gatsby.js 등을 활용하여 쉽게 정적 페이지를 생성할 수 있습니다.)
클라이언트 사이드 렌더링 (CSR) 을 중심으로 한 보완책
Gatsby

리액트(React)같은 경우는 클라이언트 사이드 렌더링에 특화된 라이브러리이지만, 개츠비(Gatsby)라는 프레임워크와 함께 사용한다면 클라이언트 사이드 렌더링의 단점을 어느정도 보완할 수 있습니다.
개츠비는 SSG에 최적화된 React 기반 정적 페이지를 생성 프레임워크입니다. 용어를 하나씩 뜯어보면 그 의미를 쉽게알 수 있는데요, React 기반으로 정적인 페이지를 만들어 미리 생성해둘 수 있게 하는 도구라고 생각하시면 편합니다. 프로젝트 빌드 시점에 static한 HTML을 미리 생성해 호스팅을 제공해주는 CDN 서버에 배포해 놓을 수가 있습니다. 이러한 특징 때문에 React 기반이지만 SEO 에 좋습니다. (HTML의 body에 정보들이 있어야 검색 엔진이 웹페이지를 분석하기 좋다고 했던 것 기억하실 겁니다!)
그리고 이런 방식으로 만들어진 웹사이트는 JavaScript 파일을 함께 가지고 있기 때문에 혹시 추가적으로 데이터를 서버에서 받아오거나 또는 동적으로 처리해야 되는 로직이 있다면 충분히 추가할 수가 있습니다.
react-helmet
또한 리액트 헬멧(react-helmet)을 통해서 최적화 할 수도 있습니다. 리액트 헬멧은 웹 문서의 헤더 값을 변경할 때 사용하는 리액트 컴포넌트인데요, 프로젝트 내에서 react-helmet 모듈을 설치해 사용할 수 있고 gatsby에서는 react-helmet과 gatsby-plugin-react-helmet을 통해 매타 태그들을 수정할 수 있습니다.
import { Helmet } from 'react-helmet';
export const Post = () => {
return (
<Wrapper>
<Helmet>
<meta property="og:image" content="" />
<meta property="og:url" content="" />
<title>Post</title>
</Helmet>
// ...
</Wrapper>
);
};
아래는 react-helmet의 동작방식에 대해 깊게 다룬 좋은 글이 있어 첨부합니다!
‣ react-helmet의 동작 방식
그 외
이 외에도 번들을 여러 조각내어 처음에 가장 필요한 부분만 전해주는 Code Splitting, 페이지 내에서 실제 필요할 때까지 자원의 로딩을 미루는 lazy loading 등이 있습니다.
서버 사이드 렌더링 (SSR) 을 중심으로 한 보완책

Next.js는 강력한 서버 사이드 렌더링을 지원하는 프레임워크입니다. React 라이브러리의 프레임워크이고, 검색 엔진 최적화가 잘 되어있지 않은 리액트의 한계를 보완해주기 위해 태어났습니다. 요즘에는 SSG도 지원을 하며 CSR과 SSR을 잘 섞어서 우리의 목적에 맞게 사용할 수 있도록 하고 있습니다. CSR과 SSR의 단점을 해결하면서 장점을 살리고자 한 결과물인데요, Next.js를 사용하여 특정 페이지에 처음 접속할 때는 백엔드 서버로부터 데이터를 렌더링하여 빈 HTML이 아닌, 데이터가 채워진 HTML을 받아 검색 최적화 문제를 해결하고 그 다음 페이지부터는 CSR 방식을 적용하여 필요한 데이터 부분만 갱신할 수 있게 함으로써 서버의 부하도 줄이려고 했습니다.
Next.js에서는 정적 생성을 할지, 서버 사이드 렌더링을 할지, 혹은 useEffect 내부에서 불러올지 페이지별로 개발자가 정할 수 있습니다. getStaticProps로 정적 생성도 가능하고, getServerSideProps 내부에서 API 호출 함수를 불러 서버에서 데이터를 가져온 다음 응답값을 그 페이지의 props로 넘겨줄 수 있어 서버 사이드 렌더링도 가능하고, 변동 사항이 큰 데이터에 대해선 useEffect 내부에서 API 호출 함수를 불러올 수도 있어 SSG, SSR, CSR 모두 용이한 프레임워크입니다.
Next.js에서의 Data Fetching
- getServerSideProps
function Page({ data }) {
// Render data...
}
// This gets called on every request
export async function getServerSideProps() {
// Fetch data from external API
const res = await fetch(`https://.../data`);
const data = await res.json();
// Pass data to the page via props
return { props: { data } };
}
export default Page;
- getStaticProps
// posts will be populated at build time by getStaticProps()
function Blog({ posts }) {
return (
<ul>
{posts.map(post => (
<li>{post.title}</li>
))}
</ul>
);
}
// This function gets called at build time on server-side.
// It won't be called on client-side, so you can even do
// direct database queries.
export async function getStaticProps() {
// Call an external API endpoint to get posts.
// You can use any data fetching library
const res = await fetch('https://.../posts');
const posts = await res.json();
// By returning { props: { posts } }, the Blog component
// will receive `posts` as a prop at build time
return {
props: {
posts,
},
};
}
export default Blog;
- Client Side
function Profile() {
const [data, setData] = useState(null);
const [isLoading, setLoading] = useState(false);
useEffect(() => {
setLoading(true);
fetch('/api/profile-data')
.then(res => res.json())
.then(data => {
setData(data);
setLoading(false);
});
}, []);
if (isLoading) return <p>Loading...</p>;
if (!data) return <p>No profile data</p>;
return (
<div>
<h1>{data.name}</h1>
<p>{data.bio}</p>
</div>
);
}
CSR의 최대 단점인 검색 엔진 최적화 실패와 초기 렌더링 속도는 데이터가 채워진 HTML을 브라우저(클라이언트)에 넘겨줌으로써 해결하고, SSR의 단점인 서버 부하와 데이터를 요청할 때마다 발생하는 깜빡임 이슈는 필요한 부분을 갈아끼워줌으로써 해결하는 것입니다. (이 블로그도 Next.js 기반으로 되어있습니다.)
어떤 렌더링 방식을 선택해야 하는가?

그렇다면 어떤 방식을 사용하는 게 좋을까요? 보완 방법이라고 등장한 Next.js도 초기 깜빡임 문제, TTV와 TTI 사이의 시간 간격 문제 등 개선할 여지들이 남아 있는 것 같아 완벽하다고 보긴 아직 어려울 것 같은데요..
결론은 서비스와 콘텐츠, 프로젝트의 성격에 알맞는 방식을 선택하는 것입니다!
검색 엔진을 최적화하는 것이 중요한 맛집, 온라인 쇼핑몰 등의 경우에는 서버 사이드 렌더링 기반을 선호할 수도 있고 / 초기 동적인 행동이 많아 TTV와 TTI의 간극을 줄여야 하는 경우, 즉 사용자가 웹사이트를 볼 수 있음과 동시에 인터랙션이 가능해야 할 때는 클라이언트 사이드 렌더링 기반을 선호할 수도 있습니다. 우리의 사이트의 특성을 고려해 (정적인지, 동적인지, 서버에서 얼마나 데이터를 많이 받아오는지, 얼마나 자주 얼마나 많은 사용자가 있는지, TTV와 TTI의 시점 일치가 중요한지 등) 유연하게 개발하면 좋을 것 같습니다. 이를 위해서 각 렌더링의 특징을 명확히 이해하고 어떻게 동작하는지 안다면 더 적절한 판단을 내릴 수 있을 것입니다.🤩
참고) SEO, 중요한 이유는?
아래는 클라이언트 사이드 렌더링의 SEO가 썩 좋지 않다는 내용을 찾다가,
서비스 운영의 측면에서 검색 엔진에 걸리는 게 왜 중요한지 작성한 좋은 글이 있어 부분적으로 가져왔습니다.
아래 글은 그 일부입니다.
‣ 브라우저 동작원리를 알아야 하는 이유가 무엇인가요?
"검색 엔진 최적화를 하는 것, 그리고 검색 결과에 더 노출이 잘 되는게 중요한가요?"

검색 엔진 최적화.. 서비스마다 다르겠지만 대부분 중요합니다!
예를 들어, 검색창에 직접 '콴다'를 쳐서 검색 결과에 콴다가 나오는 것은, 검색 결과로 보여줄 타겟이 확실하므로 SEO와는 연관이 적습니다. (아예 연관이 없다고는 하지 않겠습니다..) 그러나 우리가 SEO에 신경 써야 할 요소는 '타겟이 확실하지 않은 검색 결과' 입니다. 이해를 돕기 위해 아래에 예시를 하나 들어볼게요.
현재 우리가 맛집 정보를 제공하는 서비스를 운영 중이라고 합시다. 유저들이 구글에 '강남역 맛집'이라고 쳤을 때 우리 사이트의 정보가 검색 결과 1페이지 상단에 있을 것이란 보장이 있을까요? 만약 저 키워드로 우리 서비스가 검색 결과 최상단에 뜬다면 많은 유저들이 우리 서비스로 유입시키는 것이 가능할 것입니다.

검색 결과에 뜨기 쉬운 만큼 많은 유저들에게 우리 서비스가 많이 노출되며 인지도가 높아질 것이고, 우리 서비스를 많은 사람들에게 알리기 위해 사용될 광고 비용과 마케팅에 신경 써야 할 리소스를 줄일 수 있습니다. 하지만 반대로 검색 결과 9페이지 정도에 머물러 있다 하면, 유저는 우리 사이트를 발견할 확률이 굉장히 낮을 것이고, 그만큼 우리 서비스를 알리기가 힘들어질 겁니다. SEO는 서비스 운영에 대한 큰 비용과 리소스를 절감시킬 수 있는 가장 중요한 수단이고 SEO는 얼마나 검색 엔진(Search Engine)에 최적화되어 있는지에 대한 다른 경쟁 사이트와의 score 싸움입니다.
이러한 SEO를 위해 프론트엔드 개발자가 해야만 했던 임무는, 가능한 모든 액션을 취해 웹 성능을 최대한 끌어올려 타 경쟁 사이트보다 조금이라도 더 빠르게 웹 페이지를 보여주도록 하는 작업이라고 봅니다. (물론 웹 성능이 SEO score의 전부는 아닙니다. SEO Score에 미치는 요소는 다양하고, 웹 성능은 그 중 일부라고 생각합니다.)
요약정리
- 웹페이지는 클라이언트 역할인 브라우저 그리고 프론트엔드 서버, 백엔드 서버로 이루어져 있다.
- 브라우저가 기본 정적 자원들 즉 HTML, CSS, JavaScript을 받아올 때
클라이언트•브라우저 ↔ 프론트엔드 서버 - 서버 사이드 렌더링을 할 때
클라이언트•브라우저 ↔ 프론트엔드 서버 ↔ 백엔드 서버 ↔ DB - 클라이언트 사이드 렌더링에서 API 요청 시
클라이언트•브라우저 ↔ 백엔드 서버 ↔ DB - CSR 중심 보완책 - Gatsby, code splitting, lazy loading 등
- SSR 중심 보완책 - Next.js 등
- 서비스와 콘텐츠, 프로젝트의 성격에 알맞는 렌더링 방식을 선택하자.
마치며
여러 글들을 찾아보고 읽으면서 브라우저와 서버 사이에서 어떤 플로우로 동작하는지 생각해볼 수 있었던 시간이었습니다. 클라이언트 사이드 렌더링에서는 어떻게하면 번들링을 잘해서 초기 접속 시 사용자에 필요한 필수적인 친구들만 내보낼 수 있을지, 서버 사이드 렌더링 같은 경우에서는 사용자의 TTV와 TTI의 격차를 줄이기 위해 어떤 작업을 할 수 있을지, 좋은 사용자 경험을 위해 어떻게 하면 렌더링을 최적화 할 수 있을 것이며 특히 정적인 페이지와 동적인 페이지에서 각각 렌더링을 어떻게 최적화할 수 있을까 등 깊게 고민해볼 내용들이 참 많다고 생각합니다.
작업을 정교하고 빠르게 하는 것, 상태 설계 잘하기, 컴포넌트 잘 나누기 등 코드와 직접적으로 맞닿아 있는 역량을 기르는 것도 정말 중요한 문제지만, 결국 그 기저에 그것들이 돌아가는 환경과 구조에 대한 고민이 무조건 수반되어야 튼튼하고도 살기 좋은 집을 지을 수 있겠다고 생각했습니다. 읽어주셔서 감사합니다.
Reference
‣ 드림코딩 엘리 - 서버 사이드 렌더링
‣ 프론트 서버와 백엔드 서버의 의미
‣ webpack-dev-server
‣ [React] 프론트 엔드와 백 엔드 분리 시 동작 원리 (vs 풀 스택)
‣ 인프런 김영한 선생님 답변
‣ 웹에서의 프론트 서버 / 백엔드 서버 (개념)
‣ SSR vs CSR 비교 설명, Next.js가 탄생하게 된 이유
‣ SSR(서버사이드 렌더링)과 CSR(클라이언트 사이드 렌더링)
‣ react-helmet의 동작 방식
‣ CSR & SSR 개념잡기
'프론트엔드 > Others' 카테고리의 다른 글
| React에서 scroll 위치를 감지해 동적으로 키워드 강조하기 (0) | 2023.08.25 |
|---|---|
| Figma의 multiplayer technology works (0) | 2023.06.20 |