카테고리 없음

VITE REACT 프로젝트 SSG로 빌드하기

Ahyeon, Jung 2024. 10. 17. 01:45

영어로 쓰면 어휘력이 실종된다. 원래없었던거같기도 ~_~

 

/*
  사실상 결과물이 같은데 굳이 고집할 이유가 없어서 Next로 이동했슴니다
  next.config.js에 output: export만 추가하면 SSG가,,
*/

CSR(Client Side Rendering)

Think about Single Page Application(SPA) and Client Side Rendering(CSR). They provide an empty HTML file to the client. In this HTML file, JavaScript files are called. CSR means that, by using this empty HTML, the web page loads quickly, and the content of website is generated by javascript.

 

However, this has a big problem. Below is the empty HTML file example.

 

You can’t understand the website without the expected meta tags. This makes it difficult for both you and search engines like Google to retrieve information, which is detrimental to SEO optimization. Nowadays, many frontend developers choose Next.js to address this issue.

 

In my case, I received my first job creating a landing page for a company. However, I feel that the company wasn’t eager to spend money. As a result, I chose React, Vite, and TypeScript instead of Next.js, which requires a server for web applications.

 

It’s quite a good choice because I can easily develop with JavaScript instead of just using vanilla HTML. However, the problem still isn’t resolved.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>반응형 홈페이지 디자인+개발 맞춤제작해드립니다</title>
    <!-- Open Graph Tags -->
    <meta
      property="og:title"
      content="반응형 홈페이지 디자인+개발 맞춤제작해드립니다"
    />
    <meta
      property="og:description"
      content="2~3인 팀 구성으로 디자이너와 개발을 편리하게 맞춤형 페이지제작합니다."
    />
    <meta property="og:url" content="https://kmong.com/gig/608690" />
    <meta property="og:type" content="website" />
    <meta property="og:image" content="https://example.com/path/to/image.jpg" />
    <meta property="og:site_name" content="Your Site Name" />
    <meta property="og:locale" content="ko_KR" />
    <script type="module" crossorigin src="/assets/index-TeMmoqBZ.js"></script>
    <link rel="stylesheet" crossorigin href="/assets/index-CdLqWKOC.css">
  </head>
  <body>
    <div id="root"></div>
    <div id="portal"></div>
  </body>
</html>

 

 

You might have completed this work, but it’s not good for SEO, which could hinder future opportunities.

 

The solution always exists :D

 

SSG

해결책은 사실 단순하다. 자바스크립트가 만들어낸 정보들을 모두 담은 HTML 파일을 클라이언트에게 바로 제공하면 된다. 그렇다고 HTML에 몰아넣는건 그냥 다시는 수정 안하겠다는거다. 개발은 리액트로 하고, 제공은 채워진 후의 HTML을 주면된다. 그게 SSG다.

 

Static Site Generation(SSG)는 웹 페이지의 HTML 파일을 미리 생성하여 클라이언트에게 제공하는 방식이다. 빈 HTML을 만들고 자바스크립트로 불러와서 빈 HTML에 넣는 것을 자바스크립트가 하는게 아니라 빌드 시점에 서버가 진행하는거라고 보면 된다.

 

1. 빈 HTML을 빌드한다.

2. 서버를 만들어서 바꿔치기한다.

3. 채워진 HTML을 배포한다.

 

서버 진입점 파일 만들기

서버 진입점 파일이 해야할 일은, 라우팅된 파일들을 개별로 라우팅해야한다. 이미 react-router-dom이 제공하고 있다.

import App from "./App";
import ReactDOMServer from "react-dom/server";
import { StaticRouter } from "react-router-dom/server";

export function render(url) {
  return ReactDOMServer.renderToString(
    <StaticRouter location={url}>
      <App />
    </StaticRouter>
  );
}

 

교체할 자리 만들기

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
  </head>
  <body>
    <div id="root">
      <!--app-html-->
    </div>
    <div id="portal"></div>
    <script type="module" src="/src/main.tsx"></script>
  </body>
</html>

서버 만들기

import { fileURLToPath } from "node:url";
import fs from "node:fs";
import path from "node:path";

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const toAbsolute = (p) => path.resolve(__dirname, p);

const template = fs.readFileSync(toAbsolute("dist/static/index.html"), "utf-8");

const entryServerPath = `file://${toAbsolute("./dist/server/entry-server.js")}`;
const { render } = await import(entryServerPath);

const routesToPrerender = fs
  .readdirSync(toAbsolute("src/pages"))
  .map((file) => {
    const name = file.replace(/\.tsx$/, "").toLowerCase();
    return name === "main" ? `/` : `/${name}`;
  });

(async () => {
  for (const url of routesToPrerender) {
    const appHtml = await render(url);

    const html = template.replace(`<!--app-html-->`, appHtml);

    const filePath = `dist/static${url === "/" ? "/index" : url}.html`;
    fs.writeFileSync(toAbsolute(filePath), html);
    console.log("pre-rendered:", filePath);
  }
})();

 

이게 끝이다.

 

vite build --outDir dist/static

vite build --ssr src/entry-server.jsx --outDir dist/server

node prerender


사실 나도 거의 가져온거지만,, 하필 BrowserProvider 쓰고 있고 구조도 다르고 path도 달라서 고생을 했다,, 

근데 이미 돈을 받아버려서 진짜 HTML로만 코딩해야하나 싶었다ㅋ.ㅋ

 

SSG를 위한 Vite 셋업 (velog.io)