▼ server/src/dbController.ts
export enum DBfield {
CART = "cart",
PRODUCTS = "products",
}
const basePath = resolve();
const filenames = {
[DBfield.CART]: resolve(basePath, "src/db/cart.json"),
[DBfield.PRODUCTS]: resolve(basePath, "src/db/products.json"),
};
export const readDB = (target: DBfield) => {
try {
return JSON.parse(fs.readFileSync(filenames[target], "utf-8"));
} catch (err) {
console.error(err);
}
};
export const writeDB = (target: DBfield, data: any) => {
try {
fs.writeFileSync(filenames[target], JSON.stringify(data, null, " "));
} catch (err) {
console.error(err);
}
};
▼ server/src/index.ts
(async () => {
const server = new ApolloServer({
typeDefs: schema,
resolvers,
context: {
db: {
products: readDB(DBfield.PRODUCTS),
cart: readDB(DBfield.CART),
},
},
});
▼ server/src/resolvers/type.ts
export type Product = {
id: string;
imageUrl: string;
price: number;
title: string;
description: string;
createdAt: number;
};
export type Products = Product[];
export type CartItem = {
id: string;
amount: number;
};
export type Cart = CartItem[];
export type Resolver = {
[k: string]: {
[key: string]: (
parent: any,
args: { [key: string]: any },
context: {
db: { cart: Cart; products: Products; };
},
info: any
) => any;
};
};
▼ server/src/resolvers/product.ts
const productResolver: Resolver = {
Query: {
products: (parent, args, { db }) => {
return db.products;
},
product: (parent, { id }, { db }) => {
const found = db.products.find((item) => item.id === id);
if (found) return found;
return null;
},
},
};
▼ server/src/resolvers/cart.ts
const setJSON = (data: Cart) => writeDB(DBfield.CART, data);
const cartResolver: Resolver = {
Query: {
cart: (parent, args, { db }) => {
return db.cart;
},
},
Mutation: {
addCart: (parent, { id }, { db }) => {
if (!id) throw Error("상품 id가 없습니다.");
const targetProduct = db.products.find((item) => item.id === id);
if (!targetProduct) {
throw new Error("상품이 없습니다.");
}
const existCartItemIndex = db.cart.findIndex((item) => item.id === id);
if (existCartItemIndex > -1) {
// 카트에 이미 있으면
const newCartItem = {
id,
amount: db.cart[existCartItemIndex].amount + 1,
};
db.cart.splice(existCartItemIndex, 1, newCartItem);
setJSON(db.cart);
return newCartItem;
}
const newItem = {
id,
amount: 1,
};
db.cart.push(newItem);
setJSON(db.cart);
return newItem;
},
updateCart: (parent, { id, amount }, { db }) => {
const existCartItemIndex = db.cart.findIndex((item) => item.id === id);
if (existCartItemIndex < 0) {
throw new Error("없는 데이터입니다.");
}
const newCartItem = {
id,
amount: amount,
};
db.cart.splice(existCartItemIndex, 1, newCartItem);
setJSON(db.cart);
return newCartItem;
},
deleteCart: (parent, { id }, { db }) => {
const existCartItemIndex = db.cart.findIndex((item) => item.id === id);
if (existCartItemIndex < 0) {
throw new Error("없는 데이터입니다.");
}
db.cart.splice(existCartItemIndex, 1);
setJSON(db.cart);
return id;
},
executePay: (parent, { ids }, { db }) => {
const newCartData = db.cart.filter(
(cartItem) => !ids.includes(cartItem.id)
);
db.cart = newCartData;
setJSON(db.cart);
return ids;
},
},
CartItem: {
product: (cartItem, args, { db }) => {
return db.products.find((product) => product.id === cartItem.id);
},
},
};
이제 mock을 사용하지 않을 것이기 때문에 client에서 사용한 mock과 관련된 것들을 모두 지워준다.
yarn remove msw
: msw 삭제const BASE_URL = "http://localhost:8000/graphql";
이후 쿼리를 호출하는 코드, 함수들을 위의 규칙에 맞게 수정하면 서버의 변경사항이 클라이언트에 동일하게 적용된다.
[nodemon] restarting due to changes...
updateCart, addCart 등 db의 내용이 바뀌면 서버가 재시작되는 문제가 발생했다.
불필요하게 서버를 계속 재시작하는 것은 과부하가 올 수 있고,
서버를 재시작하는 도중에 mutataion을 하면
TypeError: Network request failed at xhr.onerror (browser-ponyfill.js:480:16)
POST http://localhost:8000/graphql net::ERR_CONNECTION_REFUSED
위와 같은 오류가 콘솔에서 발생하면서 반영되지 않는 문제도 있었다.
nodemon은 소스코드 변경 시에 자동으로 재시작해주는 기능이 있기 때문에 발생하는 문제다.
해결책은 db 변경 감지를 무시하도록 하는 것이다.
▼ nodemon.json
{
"ignore": ["src/db/*"]
}
db 변경은 src/db에 있는 json 파일들이 변경되므로 위와 같이 설정해주었다.
필요에 따라 경로를 지정하고, 추가하면 에러를 해결할 수 있을 것이다.