Skip to content

Commit 549b035

Browse files
committed
fix: 서버사이드 에러 해결
1 parent 586a610 commit 549b035

File tree

5 files changed

+63
-33
lines changed

5 files changed

+63
-33
lines changed

packages/lib/src/router/ServerRouter.ts

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,11 @@ export class ServerRouter<Handler extends AnyFunction> {
5454
return url;
5555
}
5656

57-
private readonly routes = new Map<string, Route<Handler>>();
57+
private readonly _routes = new Map<string, Route<Handler>>();
5858
private readonly baseUrl;
5959
private currentRoute: CurrentRouter<Handler> | null = null;
6060
private currentUrl = "/";
61+
private _query: StringRecord = {};
6162

6263
constructor(initRoutes: Record<string, Handler>, baseUrl = "") {
6364
this.baseUrl = baseUrl.replace(/\/$/, "");
@@ -68,16 +69,11 @@ export class ServerRouter<Handler extends AnyFunction> {
6869
}
6970

7071
get query(): StringRecord {
71-
if (!this.currentUrl) {
72-
throw new Error("setter를 통해 url 값을 할당해 주세요!");
73-
}
74-
75-
return ServerRouter.parseQuery(this.currentUrl);
72+
return this._query;
7673
}
7774

7875
set query(newQuery: QueryPayload) {
79-
const newUrl = ServerRouter.getUrl(newQuery, this.currentUrl, this.baseUrl);
80-
this.push(newUrl);
76+
this._query = newQuery as StringRecord;
8177
}
8278

8379
get params() {
@@ -96,10 +92,14 @@ export class ServerRouter<Handler extends AnyFunction> {
9692
return (fn: AnyFunction) => () => fn();
9793
}
9894

95+
get routes() {
96+
return this._routes;
97+
}
98+
9999
public addRoute(path: string, handler: Handler) {
100100
if (path === "*") {
101101
const regex = new RegExp(".*");
102-
this.routes.set(path, {
102+
this._routes.set(path, {
103103
regex,
104104
paramNames: [],
105105
handler,
@@ -119,7 +119,7 @@ export class ServerRouter<Handler extends AnyFunction> {
119119

120120
const regex = new RegExp(`^${regexPath}$`);
121121

122-
this.routes.set(path, {
122+
this._routes.set(path, {
123123
regex,
124124
paramNames,
125125
handler,
@@ -144,8 +144,11 @@ export class ServerRouter<Handler extends AnyFunction> {
144144
private findRoute(url = this.baseUrl) {
145145
const { pathname } = new URL(url, "http://localhost");
146146

147-
for (const [routePath, route] of this.routes) {
148-
const match = pathname.match(route.regex);
147+
const pathToMatch =
148+
this.baseUrl && pathname.startsWith(this.baseUrl) ? pathname.slice(this.baseUrl.length) || "/" : pathname;
149+
150+
for (const [routePath, route] of this._routes) {
151+
const match = pathToMatch.match(route.regex);
149152
if (match) {
150153
// 매치된 파라미터들을 객체로 변환
151154
const params: StringRecord = {};

packages/react/src/entities/products/productStore.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,6 @@ const productReducer = (state: typeof initialProductState, action: any) => {
123123
}
124124
};
125125

126-
/**
127-
* 상품 스토어 생성
128-
*/
129-
export const productStore = createStore(productReducer, initialProductState);
126+
export const createProductStore = () => createStore(productReducer, initialProductState);
127+
128+
export const productStore = createProductStore();

packages/react/src/main-server.tsx

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,39 @@
1+
import { ServerRouter } from "@hanghae-plus/lib";
12
import { renderToString } from "react-dom/server";
23

34
import { getCategories, getProduct, getProducts } from "./api";
45
import { App } from "./App";
6+
import { BASE_URL } from "./constants";
57
import { initialProductState, PRODUCT_ACTIONS, productStore, type Product } from "./entities";
68
import { HomePage, NotFoundPage, ProductDetailPage } from "./pages";
79
import { router } from "./router";
810

911
export class SSRService {
1012
constructor() {
11-
router.addRoute("/", HomePage);
12-
router.addRoute("/product/:id/", ProductDetailPage);
13-
router.addRoute(".*", NotFoundPage);
13+
if (router instanceof ServerRouter && (router.routes?.size === 0 || !router.routes)) {
14+
router.addRoute("/", HomePage);
15+
router.addRoute("/product/:id/", ProductDetailPage);
16+
router.addRoute(".*", NotFoundPage);
17+
}
1418
}
1519

1620
async render(url: string, query: Record<string, string>) {
17-
router.push(url);
21+
productStore.dispatch({
22+
type: PRODUCT_ACTIONS.SETUP,
23+
payload: initialProductState,
24+
});
25+
26+
const fullUrl = BASE_URL && BASE_URL !== "/" ? `${BASE_URL}${url}` : url;
27+
router.push(fullUrl);
1828
router.query = query;
1929

2030
const route = router.route;
2131

22-
if (!route || route.path === ".*") {
23-
return this.#renderNotFoundPage();
24-
}
25-
26-
if (route.path === "/") {
32+
if (route?.path === "/") {
2733
return this.#renderHomePage(query);
2834
}
2935

30-
if (route.path === "/product/:id/") {
36+
if (route?.path === "/product/:id/") {
3137
const productId = route.params?.id;
3238
if (productId) {
3339
return this.#renderProductDetailPage(productId);
@@ -44,7 +50,7 @@ export class SSRService {
4450
productStore.dispatch({
4551
type: PRODUCT_ACTIONS.SETUP,
4652
payload: {
47-
products: products.products,
53+
products,
4854
categories,
4955
totalCount: products.pagination.total,
5056
loading: false,
@@ -53,13 +59,19 @@ export class SSRService {
5359
});
5460

5561
const state = productStore.getState();
62+
5663
return {
5764
head: /* HTML */ `<title>쇼핑몰 - 홈</title>`,
5865
html: renderToString(<App />),
5966
data: {
6067
products: state.products,
61-
categories: state.categories,
6268
totalCount: state.totalCount,
69+
currentProduct: state.currentProduct,
70+
relatedProducts: state.relatedProducts,
71+
loading: state.loading,
72+
error: state.error,
73+
status: state.status,
74+
categories: state.categories,
6375
},
6476
};
6577
} catch (error) {
@@ -89,7 +101,12 @@ export class SSRService {
89101
limit: "20",
90102
page: "1",
91103
});
92-
relatedProducts = relatedResponse.products.filter((p) => p.productId !== productId);
104+
relatedProducts = relatedResponse.products
105+
.filter((p) => p.productId !== productId)
106+
.map((p) => ({
107+
...p,
108+
image: p.image?.replace(/\.(\d+\.)?jpg$/, ".jpg"),
109+
}));
93110
} catch (error) {
94111
console.error("관련 상품 로드 실패:", error);
95112
}
@@ -110,10 +127,20 @@ export class SSRService {
110127
});
111128

112129
const state = productStore.getState();
130+
113131
return {
114132
head: /* HTML */ `<title>${product.title} - 쇼핑몰</title>`,
115133
html: renderToString(<App />),
116-
data: state,
134+
data: {
135+
products: state.products,
136+
totalCount: state.totalCount,
137+
currentProduct: state.currentProduct,
138+
relatedProducts: state.relatedProducts,
139+
loading: state.loading,
140+
error: state.error,
141+
status: state.status,
142+
categories: state.categories,
143+
},
117144
};
118145
} catch (error) {
119146
console.error("상품 상세 페이지 렌더링 실패:", error);

packages/react/src/router/router.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { BASE_URL } from "../constants";
55

66
function createRouter() {
77
const RouterClass = isServer() ? ServerRouter : SPARouter;
8-
return new RouterClass<FunctionComponent>(BASE_URL);
8+
return new RouterClass<FunctionComponent>({}, BASE_URL);
99
}
1010

1111
export const router = createRouter();

packages/react/static-site-generate.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,10 @@ export class SSGBuilder {
4747

4848
async #buildProductPages() {
4949
const { getProducts } = await this.#viteServer.ssrLoadModule("./src/api/productApi.ts");
50-
const { products } = await getProducts();
50+
const response = await getProducts();
51+
const products = response.products || [];
5152

52-
const buildTasks = (products ?? []).map(({ productId }) => this.#buildPage(`/product/${productId}/`));
53+
const buildTasks = products.map(({ productId }) => this.#buildPage(`/product/${productId}/`));
5354
await Promise.all(buildTasks);
5455
}
5556

0 commit comments

Comments
 (0)