본문 바로가기
React.js

React SSR

by Zih0 2021. 10. 23.

저는 React를 입문했을 때부터, Next.js가 존재했고 Next.js가 SSR 방식을 지원한다는 걸 들어서 SSR를 쓰려면 Next.js를 써야겠다고만 생각했습니다.

하지만 얼마 전에 한 블로그에서 직접 express.js를 활용하여 WAS를 만들어 SSR 방식을 적용한 글을 봤습니다.

그 글을 읽고, SSR에 대해 좀 더 자세하게 알아보고 싶었고, Next.js는 어떻게 SSR을 구현했는지 찾아보았습니다.

SSR (Server Side Rendering)

먼저 SSR의 개념에 대해 다시 상기시키고 글을 작성하고자 합니다.

SSR은 서버에서 페이지를 구성한 다음 사용자에게 보여주는 방식입니다. 서버에서 구성하기 때문에 CSR보다 페이지 전환 시, UX 측면에서 단점이 있지만 콘텐츠 구성이 완료되는 시점은 빨라진다는 장점이 있습니다. 게다가 SEO도 CSR보다 훨씬 쉽게 구성할 수 있습니다.

React에 SSR 도입

요즘 프론트엔드에서 자주 쓰이는 방식은 CSR과 SSR을 혼합하여 사용합니다. 이를 Universal SSR Rendering 방식이라고 합니다.

Node.js 사용

서버와 클라이언트 둘 다 Node.js를 사용하면 됩니다. React에서 ReactDOMServer 객체를 제공하고 있는데 이 객체는 컴포넌트를 정적 마크업으로 렌더링 할 수 있게 해줍니다. Node.js에서 ReactDOMServer를 사용하면 SSR을 구현할 수 있습니다.

// Node.js

import path from 'path';
import fs from 'fs';

import React from 'react';
import express from 'express';
import ReactDOMServer from 'react-dom/server';

import App from '../src/App';

const PORT = process.env.PORT || 3006;
const app = express();

app.get('/', (req, res) => {
  const app = ReactDOMServer.renderToString(<App />);

  const indexFile = path.resolve('./build/index.html');
  fs.readFile(indexFile, 'utf8', (err, data) => {
    if (err) {
      console.error('Something went wrong:', err);
      return res.status(500).send('Oops, better luck next time!');
    }

    return res.send(
      data.replace('<div id="root"></div>', `<div id="root">${app}</div>`)
    );
  });
});

app.use(express.static('./build'));

app.listen(PORT, () => {
  console.log(`Server is listening on port ${PORT}`);
});

React 컴포넌트를 파악하고 HTML 문자열로 변환시켜주는 동작을 합니다.

React로 구성한 프론트엔드 단에서 ReactDOM.hydrate()를 호출하면, React는 SSR에 의해 렌더링 되어 온 부분에는 이벤트 리스너만 등록함으로써 효율적으로 동작하게 됩니다.

// index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.hydrate(<App />, document.getElementById('root'));

+) Hydrate는 서버렌더링된 정적페이지와 번들링된 JS파일을 클라이언트에게 보낸 뒤, 클라이언트단에서 HTML 코드와 React JS 코드를 서로 매칭 시키는 과정

Next.js의 SSR 방식

next는 초기 렌더링만 서버에서 담당하고, 이후에는 클라이언트에서 렌더링합니다.

초기 렌더링만을 서버에서 담당하는 것만으로도 SPA의 긴 초기 렌더링 시간을 줄일 수 있습니다.

 

좀 더 자세히 서술하자면, 브라우저로 사용자가 접속하면, node 서버가 해당 페이지에 대한 요청을 받아 서버 렌더링을 합니다.

그렇게 만들어진 html을 브라우저로 전송하고, 사용자에게 표시되게 됩니다.

( Document만 먼저 전송하기 때문에 사용자 입장에서는 빠르게 화면을 볼 수 있습니다 )

그 후에 번들링된 main.js 파일을 클라이언트에 전송합니다.

그리고 전송받은 번들링 파일은 클라이언트에서 한번 더 렌더링하면서 각자 자기 자리를 찾아 매칭이 됩니다. ( Hydrate )

 

결론

Next.js가 React 기반이므로 Next.js에서 사용한 Hydrate 과정도 사실 ReactDOM의 hydrate 함수를 사용합니다.

Node.js로 SSR을 구현하는 방법을 공부하고 Next.js를 살펴보니 좀 더 이해가 잘 되었습니다. :)

 

ref:

https://velog.io/@0307kwon/CSR-앱에서-SSR-CSR-환경으로-이주하기

https://d2.naver.com/helloworld/7804182

https://minoo.medium.com/next-js-처럼-server-side-rendering-구현하기-7608e82a0ab1

https://www.digitalocean.com/community/tutorials/react-server-side-rendering

https://blueshw.github.io/2018/04/15/why-nextjs/

https://helloinyong.tistory.com/315

댓글