Skip to content

Commit 072fceb

Browse files
author
YeongseoYoon
committed
feat: 정적 사이트 생성을 위한 SSR 페이지 렌더링 로직 추가 및 파일 시스템 관리 기능 개선
1 parent a279b95 commit 072fceb

File tree

2 files changed

+64
-15
lines changed

2 files changed

+64
-15
lines changed

packages/react/src/mocks/handlers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { http, HttpResponse } from "msw";
22
import items from "./items.json" with { type: "json" };
3-
import type { StringRecord } from "../types.ts";
3+
import type { StringRecord } from "@hanghae-plus/lib";
44
import type { Product } from "../entities";
55

66
const delay = async () => await new Promise((resolve) => setTimeout(resolve, 200));
Lines changed: 63 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,67 @@
1-
import { renderToString } from "react-dom/server";
2-
import { createElement } from "react";
3-
import fs from "fs";
1+
import fs from "node:fs";
2+
import path from "node:path";
3+
import { createServer } from "vite";
44

5-
async function generateStaticSite() {
6-
// HTML 템플릿 읽기
7-
const template = fs.readFileSync("../../dist/react/index.html", "utf-8");
5+
const DIST_DIR = "../../dist/react";
86

9-
// 어플리케이션 렌더링하기
10-
const appHtml = renderToString(createElement("div", null, "안녕하세요"));
7+
const createOutputPath = (route) => {
8+
if (route.endsWith(".html")) {
9+
return path.join(DIST_DIR, route);
10+
}
11+
return path.join(DIST_DIR, route, "index.html");
12+
};
1113

12-
// 결과 HTML 생성하기
13-
const result = template.replace("<!--app-html-->", appHtml);
14-
fs.writeFileSync("../../dist/react/index.html", result);
15-
}
14+
const ensureDirectoryExists = (filePath) => {
15+
const dir = path.dirname(filePath);
16+
if (!fs.existsSync(dir)) {
17+
fs.mkdirSync(dir, { recursive: true });
18+
}
19+
};
1620

17-
// 실행
18-
generateStaticSite();
21+
const buildHtmlContent = (template, pageData) => {
22+
return template
23+
.replace("<!--app-head-->", pageData.head || "")
24+
.replace("<!--app-html-->", pageData.html || "")
25+
.replace(
26+
"<!--app-data-->",
27+
`<script>window.__INITIAL_DATA__ = ${JSON.stringify(pageData.__INITIAL_DATA__ || {})};</script>`,
28+
);
29+
};
30+
31+
const writePage = async (route, renderFn, template) => {
32+
const pageData = await renderFn(route, {});
33+
const htmlContent = buildHtmlContent(template, pageData);
34+
const outputPath = createOutputPath(route);
35+
36+
ensureDirectoryExists(outputPath);
37+
fs.writeFileSync(outputPath, htmlContent);
38+
};
39+
40+
const generateStaticPages = async () => {
41+
const viteServer = await createServer({
42+
server: { middlewareMode: true },
43+
appType: "custom",
44+
});
45+
46+
const { server: mswServer } = await viteServer.ssrLoadModule("./src/mocks/nodeServer.ts");
47+
mswServer.listen({ onUnhandledRequest: "bypass" });
48+
49+
try {
50+
const { render } = await viteServer.ssrLoadModule("./src/main-server.tsx");
51+
const template = fs.readFileSync(path.join(DIST_DIR, "index.html"), "utf-8");
52+
53+
await writePage("/404.html", render, template);
54+
await writePage("/", render, template);
55+
56+
const { getProducts } = await viteServer.ssrLoadModule("./src/api/productApi.ts");
57+
const { products } = await getProducts();
58+
59+
const productTasks = products.map((product) => writePage(`/product/${product.productId}/`, render, template));
60+
await Promise.all(productTasks);
61+
} finally {
62+
mswServer.close();
63+
viteServer.close();
64+
}
65+
};
66+
67+
generateStaticPages();

0 commit comments

Comments
 (0)