diff --git a/packages/vanilla/index.html b/packages/vanilla/index.html
index 483a6d5e..56f70659 100644
--- a/packages/vanilla/index.html
+++ b/packages/vanilla/index.html
@@ -5,18 +5,19 @@
-
+
+
diff --git a/packages/vanilla/server.js b/packages/vanilla/server.js
index b9a56d98..10acf34d 100644
--- a/packages/vanilla/server.js
+++ b/packages/vanilla/server.js
@@ -1,34 +1,78 @@
+import compression from "compression";
import express from "express";
+import fs from "fs/promises";
+import sirv from "sirv";
+import { mswServer } from "./src/mocks/mswServer.js";
-const prod = process.env.NODE_ENV === "production";
+// 환경 변수 및 설정
+const isProd = process.env.NODE_ENV === "production";
const port = process.env.PORT || 5173;
-const base = process.env.BASE || (prod ? "/front_6th_chapter4-1/vanilla/" : "/");
-
+const baseUrl = process.env.BASE || (isProd ? "/front_6th_chapter4-1/vanilla/" : "/");
+const templateHtml = isProd ? await fs.readFile("dist/vanilla/index.html", "utf-8") : "";
const app = express();
-const render = () => {
- return `안녕하세요
`;
-};
-
-app.get("*all", (req, res) => {
- res.send(
- `
-
-
-
-
-
- Vanilla Javascript SSR
-
-
-${render()}
-
-
- `.trim(),
- );
+let vite;
+
+// MSW 서버 시작 (API 모킹을 위해)
+mswServer.listen({
+ onUnhandledRequest: "bypass",
+});
+
+// 환경별 정적 파일 서빙 및 미들웨어 설정
+if (isProd) {
+ // 프로덕션 환경: 빌드된 정적 파일 서빙 (assets만 서빙하도록 제한)
+ app.use(compression());
+ app.use(`${baseUrl}assets`, sirv("dist/vanilla/assets", { dev: false }));
+ app.use(`${baseUrl}mockServiceWorker.js`, sirv("dist/vanilla", { dev: false }));
+} else {
+ // 개발 환경: Vite 개발 서버를 미들웨어로 사용
+ const { createServer } = await import("vite");
+ vite = await createServer({
+ server: { middlewareMode: true },
+ appType: "custom",
+ baseUrl,
+ });
+
+ app.use(vite.middlewares);
+}
+
+// 모든 라우트를 처리하는 SSR 핸들러
+app.get(/^(?!.*\/api).*/, async (req, res) => {
+ try {
+ let template;
+ let render;
+
+ if (!isProd) {
+ // 개발 환경: 매 요청마다 템플릿을 다시 읽고 변환
+ template = await fs.readFile("./index.html", "utf-8");
+ template = await vite.transformIndexHtml(req.originalUrl, template);
+ render = (await vite.ssrLoadModule("./src/main-server.js")).render;
+ } else {
+ // 프로덕션 환경: 미리 로드된 템플릿과 빌드된 모듈 사용
+ template = templateHtml;
+ render = (await import("./dist/vanilla-ssr/main-server.js")).render;
+ }
+
+ // SSR 렌더링 실행
+ const rendered = await render(req.originalUrl, req.query);
+
+ // HTML 템플릿에 렌더링된 내용 삽입
+ const html = template
+ .replace(``, rendered.head ?? "")
+ .replace(``, ``)
+ .replace(``, rendered.html ?? "");
+
+ // 클라이언트에 완성된 HTML 응답
+ res.status(200).set({ "Content-Type": "text/html" }).send(html);
+ } catch (error) {
+ console.error(error);
+ res.status(500).send("Internal Server Error");
+ }
});
-// Start http server
+// HTTP 서버 시작
app.listen(port, () => {
- console.log(`React Server started at http://localhost:${port}`);
+ console.log(`🚀 Server started at http://localhost:${port}`);
+ console.log(`📁 Environment: ${isProd ? "production" : "development"}`);
+ console.log(`📍 Base URL: ${baseUrl}`);
});
diff --git a/packages/vanilla/src/api/productApi.js b/packages/vanilla/src/api/productApi.js
index c2330fbe..78cc5762 100644
--- a/packages/vanilla/src/api/productApi.js
+++ b/packages/vanilla/src/api/productApi.js
@@ -1,7 +1,33 @@
+// API 기본 URL을 환경별로 동적 설정
+const getBaseUrl = () => {
+ // 클라이언트 환경: 상대 경로 사용 (같은 도메인의 API 호출)
+ if (typeof window !== "undefined") {
+ return ""; // 브라우저에서는 빈 문자열로 상대 경로 사용
+ }
+
+ // 서버 환경: 절대 URL 필요 (서버에서 서버로 호출)
+ const prod = process.env.NODE_ENV === "production";
+ return prod ? "http://localhost:4174" : "http://localhost:5174"; // 환경별 포트 설정
+};
+
+const BASE_URL = getBaseUrl(); // 런타임에 환경에 맞는 BASE_URL 결정
+
+/**
+ * 상품 목록 조회 API - 검색, 필터링, 페이징 지원
+ * @param {Object} params - 쿼리 파라미터 객체
+ * @param {number} params.limit - 페이지당 상품 수 (기본값: 20)
+ * @param {string} params.search - 검색 키워드
+ * @param {string} params.category1 - 1차 카테고리 필터
+ * @param {string} params.category2 - 2차 카테고리 필터
+ * @param {string} params.sort - 정렬 방식 (기본값: "price_asc")
+ * @param {number} params.current|params.page - 현재 페이지 번호
+ * @returns {Promise