SEO-Booster๋ฅผ create-react-app์์ Vite 5๋ก ์ฎ๊ฒผ๋ค. ๋จ์ ์ค์ ๊ต์ฒด๊ฒ ๊ฑฐ๋ ํ๋๋ฐ, ์ธ ๊ตฐ๋ฐ์ ์กฐ์ฉํ ๋งํ๋ค. ๊ณตํต์ ์ด ์์๋ค. ์๋ฌ ๋ฉ์์ง๊ฐ ์์ธ์ ๊ฐ๋ฆฌํค์ง ์์๋ค.
ํจ์ 1: JSX ํ์ผ ํ์ฅ์
Vite๋ ๋ด๋ถ์ ์ผ๋ก Rollup ๊ธฐ๋ฐ์ด๋ค. ๊ทธ๋ฆฌ๊ณ Rollup์ .js ํ์ผ์์ JSX๋ฅผ ๊ธฐ๋ณธ์ผ๋ก ์ฒ๋ฆฌํ์ง ์๋๋ค. @vitejs/plugin-react๋ฅผ ์ค์นํด๋ ๋ง์ฐฌ๊ฐ์ง๋ค. ํ๋ฌ๊ทธ์ธ์ด JSX๋ฅผ ์ฒ๋ฆฌํ๋ ๊ฑด ํ์ฅ์๊ฐ .jsx(๋๋ .tsx)์ผ ๋๋ฟ์ด๋ค.
CRA๋ react-scripts ๋ด๋ถ์ Babel ์ค์ ์ด .js ํ์ผ์์๋ JSX๋ฅผ ์์์ ์ฒ๋ฆฌํด์คฌ๋ค. ๊ทธ๋์ CRA ํ๋ก์ ํธ์์ ์ปดํฌ๋ํธ ํ์ผ์ด .js์ฌ๋ ์๋ฌด ๋ฌธ์ ์์ด ๋์๊ฐ๋ค. ์ด๊ฒ Vite๋ก ๋์ด์ค๋ ์๊ฐ ํญํ์ด ๋๋ค.
src/components/Editor.js โ (Vite/Rollup) โ JSX ์ฒ๋ฆฌ ์ ํจ โ ๋น๋ ์คํจ ๋๋ ๋ฐํ์ ์ค๋ฅ์ฆ์์ ํ๋ก์ ํธ๋ง๋ค ๋ค๋ฅด๋ค. ๋น๋ ์์ฒด๊ฐ ๊นจ์ง๊ธฐ๋ ํ๊ณ , ๊ฐ๋ฐ ์๋ฒ๋ ๋จ๋๋ฐ ํ๋ฉด์ด ๋น ์ฑ๋ก ์ฝ์์ ํ์ฑ ์ค๋ฅ๊ฐ ๋จ๊ธฐ๋ ํ๋ค. ์ฒ์์ ์ค์ ํ์ผ ๋ฌธ์ ๋ก ์ฐฉ๊ฐํ๊ธฐ ์ฝ๋ค.
ํด๊ฒฐ: .js โ .jsx๋ก ์ ๋ถ ๋ฐ๊พผ๋ค. ์ผ๊ด rename์ด๋ผ ๋ฒ๊ฑฐ๋กญ์ง๋ง, import ๊ฒฝ๋ก๋ ํจ๊ป ๋ฐ๊ฟ์ค์ผ ํ๋ค.
# ์ผ๊ด ํ์ธ: JSX ๋ฌธ๋ฒ์ด ๋ด๊ธด .js ํ์ผ ๋ชฉ๋ก
grep -rl "return (" src --include="*.js" | grep -v ".test.js"vite.config.js์์ optimizeDeps.esbuildOptions๋ก .js๋ JSX๋ก ์ฒ๋ฆฌํ๊ฒ ๊ฐ์ ํ ์ ์๊ธด ํ๋ค. ํ์ง๋ง ํ์ฅ์๊ฐ ์ญํ ์ ๋๋ฌ๋ด๋ ๊ฒ ๋ซ๋ค. ๊ฐ์ ์ค์ ์ ๋ค์ ์ฌ๋์ด ์ ์ด๊ฒ ์๋์ง ์ดํด ๋ชป ํ๋ค.
// vite.config.js โ ํ์ฅ์ ๊ฐ์ ๋ฐฉ๋ฒ (๋น๊ถ์ฅ, ์์ ์ฐํ์ฉ)
export default defineConfig({
optimizeDeps: {
esbuildOptions: {
loader: { '.js': 'jsx' },
},
},
});ํจ์ 2: %PUBLIC_URL%
CRA๋ public/ ํด๋ ์์ ํ์ผ์ ์ฐธ์กฐํ ๋ %PUBLIC_URL%์ด๋ผ๋ ํน์ ๋ณ์๋ฅผ ์ ๊ณตํ๋ค. public/index.html์์ ์์ฃผ ์ฐ์ธ๋ค.
<!-- CRA: public/index.html -->
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta property="og:image" content="%PUBLIC_URL%/logo192.png" />Vite๋ ์ด๊ฑธ ๋ชจ๋ฅธ๋ค. ๊ทธ๋๋ก ๋๋ฉด ์ค์ URL์ %PUBLIC_URL%์ด๋ผ๋ ๋ฌธ์์ด์ด ๊ทธ๋๋ก ๋ฐํ๋ค. ํ๋น์ฝ์ด ์ ๋จ๊ณ OG ์ด๋ฏธ์ง๊ฐ ๊นจ์ง๋ค.
ํด๊ฒฐ: Vite์ index.html์ ํ๋ก์ ํธ ๋ฃจํธ์ ๋๊ณ , public/ ํ์ผ์ ๊ทธ๋ฅ /๋ก ์ฐธ์กฐํ๋ค.
<!-- Vite: index.html (๋ฃจํธ์ ์์น) -->
<link rel="icon" href="/favicon.ico" />
<meta property="og:image" content="/logo192.png" />๋น๋ ๊ฒฝ๋ก๋ฅผ ์ปค์คํ
ํ๊ฑฐ๋ ์๋ธ๋๋ ํ ๋ฆฌ์ ๋ฐฐํฌํ๋ค๋ฉด import.meta.env.BASE_URL์ ์ด๋ค.
<link rel="icon" href="<%= import.meta.env.BASE_URL %>favicon.ico" />ํจ์ 3: react-helmet
react-helmet์ <head> ํ๊ทธ๋ฅผ ์ปดํฌ๋ํธ์์ ์ ์ดํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค. CRA ํ๋ก์ ํธ์์ ๋ฉํ ํ๊ทธ, OG, ํ์ดํ ๋ฑ์ ๋ค๋ฃจ๋ ๊ฐ์ฅ ํํ ์ ํ์ด๋ค.
๊ทธ๋ฐ๋ฐ react-helmet์ ์ค๋์ ๋ถํฐ ์ ์ง๋ณด์๊ฐ ๋ฉ์ถฐ ์๋ค. React 18์ StrictMode ์ด์ค ๋ง์ดํธ์์ ๋ด๋ถ ์ํ ๊ด๋ฆฌ๊ฐ ์ด๊ธ๋๊ณ , ํนํ SSR ํ๊ฒฝ์์ ๋ฉ๋ชจ๋ฆฌ ๋์๊ฐ ๋ณด๊ณ ๋๋ค. CRA ํ๊ฒฝ์์ ํฐ๊ฐ ์ ๋๋ค๊ฐ Vite๋ก ์ฎ๊ธฐ๋ฉฐ ๊ฐ๋ฐ ์ค์ ์ ์ ๋ฆฌํ๋ ๊ณผ์ ์์ ๊ฒฝ๊ณ ๊ฐ ๋์ ๋๊ธฐ ์์ํ๋ค.
๋์ฒด์ ๋ react-helmet-async ๋ค. API๊ฐ ๊ฑฐ์ ๊ฐ์์ ๊ต์ฒด ๋น์ฉ์ด ์๋ค.
npm uninstall react-helmet
npm install react-helmet-async// โ react-helmet (์ ์ง๋ณด์ ์ค๋จ)
import { Helmet } from "react-helmet";
// โ
react-helmet-async (React 18 StrictMode ํธํ)
import { Helmet, HelmetProvider } from "react-helmet-async";์ฑ ์ต์๋จ์ <HelmetProvider>๋ก ๊ฐ์ธ์ค์ผ ํ๋ค.
// main.jsx
import { HelmetProvider } from "react-helmet-async";
root.render(
<React.StrictMode>
<HelmetProvider>
<App />
</HelmetProvider>
</React.StrictMode>
);CRA ๋ง์ด๊ทธ๋ ์ด์ ์ด ๋ถํ์ํ ํจํค์ง๋ฅผ ๋๋ฌ๋ธ๋ค
react-scripts๋ ํธ๋ฆฌํ์ง๋ง ์์กด์ฑ์ ๋ธ๋๋ฐ์ค๋ก ์จ๊ธด๋ค. Vite๋ก ๋์ด์ค๋ฉด์ package.json์ ๋ค์ ๋ค์ฌ๋ค๋ณด๋ฉด, ์ค์ ๋ก ์ฐ์ง ์๋ ํจํค์ง๋ค์ด ๋ช
์์ ์ผ๋ก ๋ณด์ด๊ธฐ ์์ํ๋ค.
SEO-Booster๋ฅผ ์ฎ๊ธฐ๋ฉฐ ์ ๊ฑฐํ ํจํค์ง๋ค:
axios, redux, react-redux, react-router-dom,
react-dnd, tui-editor, react-helmet, dotenv, web-vitals9๊ฐ๊ฐ ํ ๋ฒ๋ ์ค์ ๊ธฐ๋ฅ์ ์ฐ์ด์ง ์์๋ค. ์ด๊ธฐ ์ธํ
์ "๋์ค์ ์ธ ๊ฒ ๊ฐ์์" ๋ฃ์ด ๋ ๊ฒ๋ค์ด๋ค. CRA ๋๋ react-scripts๊ฐ ๋น๋๋ฅผ ์์์ ํด์ค์ ์ด๊ฒ ๋น๋ ์๊ฐ์ด๋ ๋ฒ๋ค ํฌ๊ธฐ์ ์ผ๋ง๋ ์ํฅ์ ์ฃผ๋์ง ์ฒด๊ฐํ๊ธฐ ์ด๋ ต๋ค. Vite๋ก ๋์ด์ค๋ฉด ๋ฒ๋ค ๋ถ์๋ ์ฌ์์ง๊ณ , ๋ถํ์ํ ๊ฒ๋ค์ด ๋์ ํ ๋ค์ด์จ๋ค.
# Vite ๋น๋ ํ ๋ฒ๋ค ๋ถ์
npx vite-bundle-visualizer๋ง์ด๊ทธ๋ ์ด์ ์์ ์ ๋ฆฌ
react-scripts์ ๊ฑฐ,vite+@vitejs/plugin-react์ค์นvite.config.js์์ฑ (root: '.',publicDir: 'public')index.html์ ๋ฃจํธ๋ก ์ด๋,%PUBLIC_URL%์ ๊ฑฐ- ์ปดํฌ๋ํธ ํ์ผ
.jsโ.jsx์ผ๊ด rename + import ๊ฒฝ๋ก ์์ - ํ๊ฒฝ๋ณ์ prefix
REACT_APP_โVITE_, ์ฐธ์กฐ ์ฝ๋process.env.โimport.meta.env. react-helmetโreact-helmet-async๊ต์ฒด (์ฐ๊ณ ์๋ค๋ฉด)package.jsonscripts ์์ :"start": "vite","build": "vite build","preview": "vite preview"- ํ ์คํธ ํ๋ ์์ํฌ๋ฅผ Jest์์ Vitest๋ก ์ ํ (์ ํ, ํ์ง๋ง ๊ถ์ฅ)
๋ง๋ฌด๋ฆฌ
์ธ ํจ์ ๋ชจ๋ CRA๊ฐ ๋ด๋ถ์์ ํด์ฃผ๋ ๊ฑธ Vite๋ ์ ํด์ค๋ค๋ ํจํด์ด๋ค. CRA๋ ํธ์๋ฅผ ์ํด ๋ง์ ๊ฑธ ์จ๊ฒผ๊ณ , ๊ทธ ๋์ ์์์ ๋น ๋ฅด์ง๋ง ์์์ ๋ฌด์จ ์ผ์ด ์ผ์ด๋๋์ง ์ ์ ๋ณด์๋ค. Vite๋ ๋ ํฌ๋ช ํ๊ณ ๋น ๋ฅด์ง๋ง, ๊ทธ ํฌ๋ช ํจ์ด ์จ๊ฒจ์ ธ ์๋ ์ ์ ๋ค์ ํ๊บผ๋ฒ์ ๋๋ฌ๋ธ๋ค.
๋ง์ด๊ทธ๋ ์ด์
์์ฒด๋ ์ด๋ ต์ง ์๋ค. ๋ค๋ง ์๋ฌ ๋ฉ์์ง๊ฐ ์์ธ์ ์ง์ ์ ์๋ ค์ค ๋๊ฐ ๋ง๋ค. "JSX ํ์ฑ ์ค๋ฅ"๊ฐ ๋จ๋ฉด ํ์ฅ์๋ฅผ, ํ๋น์ฝ์ด ๊นจ์ง๋ฉด %PUBLIC_URL%์, StrictMode ๊ฒฝ๊ณ ๊ฐ ๋ณด์ด๋ฉด react-helmet์ ๋จผ์ ํ์ธํ๋ค.
