본문 바로가기
IT/react

[react] ssr 프로젝트 만들기 (4) - react router 추가

by 내일은교양왕 2024. 7. 2.

코드 확인

https://github.com/insidedw/react-webpack5-ssr/commit/1d97ecbcb6681db250940cf52cdf90411e171a9a

 

integrate react-router-dom · insidedw/react-webpack5-ssr@1d97ecb

insidedw committed Jul 7, 2024

github.com


의존성 설치

npm i react-router-dom

 

 

client.js 수정

client side 부터 설정해보자.

SSR이니 hash보단 browser router로

import React from 'react'
import ReactDOM from 'react-dom'
import App from './components/App'
import { BrowserRouter } from 'react-router-dom'

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

 

App.js 수정

path 마다 다른 마크업 적용. 

csr를 확인해보기 위해 link 태그 추가

import React from 'react'
import './App.css'
import { Link, Route, Routes } from 'react-router-dom'
const App = () => {
  return (
    <Routes>
      <Route
        path={'/'}
        element={
          <div>
            <h1>Hello, SSR!</h1>
            <Link to={'/about'}>About</Link>
          </div>
        }
      />
      <Route
        path={'/about'}
        element={
          <div>
            <h1>SSR is sever side rendering</h1>
            <Link to={'/'}>Home</Link>
          </div>
        }
      />
    </Routes>
  )
}

export default App

 

 

실행해보면 다음과 같은 에러가 노출된다.

이유는 ssr에서 route 컴포넌트를 읽을 때 에러가 나기 때문이다.

csr에서는 BrowserRouter로 감싸줬지만, ssr에서는 감싸주는 컴포넌트가 없기 때문이다.

Error: useRoutes() may be used only in the context of a <Router> component.
    at Object.invariant [as UNSAFE_invariant] (C:\Users\USER\IdeaProjects\react-ssr\node_modules\@remix-run\router\dist\router.cjs.js:315:11)
    at useRoutesImpl (C:\Users\USER\IdeaProjects\react-ssr\node_modules\react-router\dist\umd\react-router.development.js:348:36)
    at useRoutes (C:\Users\USER\IdeaProjects\react-ssr\node_modules\react-router\dist\umd\react-router.development.js:343:12)
    at Routes (C:\Users\USER\IdeaProjects\react-ssr\node_modules\react-router\dist\umd\react-router.development.js:1232:12)
    at renderWithHooks (C:\Users\USER\IdeaProjects\react-ssr\node_modules\react-dom\cjs\react-dom-server-legacy.node.development.js:5662:16)
    at renderIndeterminateComponent (C:\Users\USER\IdeaProjects\react-ssr\node_modules\react-dom\cjs\react-dom-server-legacy.node.development.js:5736:15)
    at renderElement (C:\Users\USER\IdeaProjects\react-ssr\node_modules\react-dom\cjs\react-dom-server-legacy.node.development.js:5961:7)
    at renderNodeDestructiveImpl (C:\Users\USER\IdeaProjects\react-ssr\node_modules\react-dom\cjs\react-dom-server-legacy.node.development.js:6119:11)
    at renderNodeDestructive (C:\Users\USER\IdeaProjects\react-ssr\node_modules\react-dom\cjs\react-dom-server-legacy.node.development.js:6091:14)
    at renderIndeterminateComponent (C:\Users\USER\IdeaProjects\react-ssr\node_modules\react-dom\cjs\react-dom-server-legacy.node.development.js:5790:7)

 

 

server.js 수정

https://reactrouter.com/en/main/guides/ssr#without-a-data-router 가이드에 따라 코드 추가

import path from 'path'
import fs from 'fs'
import React from 'react'
import express from 'express'
import { renderToString } from 'react-dom/server'
import App from './components/App'
import { StaticRouter } from 'react-router-dom/server'

const app = express()

app.use(express.static('dist'))

const html = `
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>React SSR</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
`

const manifest = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../dist/manifest.json'), 'utf8'))

const scripts = Object.keys(manifest)
  .filter((key) => key.endsWith('.js'))
  .map((key) => `<script defer src="${manifest[key]}"></script>`)
  .join('\n')

const styles = Object.keys(manifest)
  .filter((key) => key.endsWith('.css'))
  .map((key) => `<link rel="stylesheet" type="text/css" href="${manifest[key]}">`)
  .join('\n')

app.get('*', (req, res) => {
  const appString = renderToString(
    <StaticRouter location={req.url}>
      <App />
    </StaticRouter>,
  )
  return res.send(
    html
      .replace('<div id="root"></div>', `<div id="root">${appString}</div>`)
      .replace('</head>', `${styles}</head>`)
      .replace('</body>', `${scripts}</body>`),
  )
})

app.listen(3000, () => {
  console.log('Server is listening on port 3000')
})

 

테스트 후 잘 동작하는 걸 확인 할 수 있다.

path: /
path: /about