본문 바로가기
IT/react

[react] ssr 프로젝트 만들기 (3) - css, js 번들에 hash 추가

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

[react] ssr 프로젝트 만들기 (1) - hello world 찍기

[react] ssr 프로젝트 만들기 (2) - css, 번들에 추가하기

에서는 client 번들링 파일 이름이 동일하여 cdn에 올릴 때 이슈가 발생할 수 있다. 

이 이슈를 해결해보자

 

소스 코드

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

 

inject css on ssr using mini-css-extract-plugin and webpack-manifest-… · insidedw/react-webpack5-ssr@bbf8ea9

…plugin

github.com

 


시작하기 전 스타일링이 ssr에 포함되도록 준비해보자

 

webpack-manifest-plugin 설치

MiniCssExtractPlugin 을 이용해서 css를 별도의 파일로 추출해보자

npm i -D mini-css-extract-plugin

 

webpack.client.js 설정

MiniCssExtractPlugin 설정과  output에 filename, clean 항목 추가해서 js, css 파일을 번들링해보자

const path = require('path')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')

module.exports = {
  mode: 'development',
  entry: './src/client.js',
  output: {
    path: path.resolve(__dirname, '../dist'),
    filename: '[name].[hash].js',
    clean: true,
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
        },
      },
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader'],
      },
    ],
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].[hash].css',
    })
  ],
}

 

이 상태로 빌드하면, 페이지가 정상적으로 로드 되지 않는다. 

이유는 public/index.html에서 client.bundle.js를 항상 주입하기 때문이다.

우리가 번들링 된 파일 이름을 동적으로 변경되도록 설정했기 때문에 매치가 안되는 상황이다.

 

그렇다면 매번 변경되는 번들링 파일을 어떻게 동적으로 ssr에 주입 할 수 있을까?

webpack-manifest-plugin 를 활용하면 된다.

 

webpack.client.js 설정

번들링시 생성되는 코드에 대한 정보를 json파일로 저장하여 관리하는 webpack-manifest-plugin을 추가

npm i -D webpack-manifest-plugin
const path = require('path')
const { WebpackManifestPlugin } = require('webpack-manifest-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')

module.exports = {
  mode: 'development',
  entry: './src/client.js',
  output: {
    path: path.resolve(__dirname, '../dist'),
    filename: '[name].[hash].js',
    clean: true,
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
        },
      },
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader'],
      },
    ],
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].[hash].css',
    }),
    new WebpackManifestPlugin({
      fileName: 'manifest.json',
      publicPath: '/',
    }),
  ],
}

 

빌드해보면 dist에 manifest.json 파일이 생성된다.

manifest.json

{
  "main.css": "/main.d46c0ceaea7442158cfe.css",
  "main.js": "/main.d46c0ceaea7442158cfe.js"
}

 

server.js 코드 추가

manifest.json 파일을 읽어와서 해당 값을 가공 후 html에 주입시키는 작업을 해야한다.

public/index.html파일은 더이상 필요가 없다.

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'

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(<App />)
  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')
})

 

 

테스트 해보면, css 주입도 ssr에 잘 된걸 확인 할 수 있다.

 

style-loader는 더이상 필요하지 않으므로 삭제하자

npm uninstall style-loader