제품을 더이상 배열에 저장하지 않고, 파일에 저장하는 것으로 변경해 보겠다.
따라서 save method를 호출시에 파일에 저장하는 로직으로 변경한다.
우선, node core module인 fs(filesSystem)을 import 한다.
//변경전
const products = [];
module.exports = class Product {
constructor(t) { //매개변수 : title
this.title = t //this.title을 인수로 수신함
//this == Product 객체를 가리킴
}
save() {
products.push(this)
}
static fetchAll() {
return products
}
}
//변경후
const fs = require("fs"); //node core module import
const path = require("path"); //경로 구축
module.exports = class Product {
constructor(t) {
//매개변수 : title
this.title = t; //this.title을 인수로 수신함
//this == Product 객체를 가리킴
}
save() {
const p = path.join(
path.dirname(require.main.filename),
"data",
"products.json"//확장자를 JSON으로 지정하여, data를 json 형식으로 저장
);
//새 제품을 저장하기 위해서 기존 제품 배열을 가져온다.
fs.readFile(p, (err, fileContent) => {
console.log(err);
} )
}
static fetchAll() {
return products;
}
};
err
로그를 보면, products is not defined
이 출력된다. 그 이유는 data폴더안에 products.json 파일이존재 하지 않기 때문인데, 나는 존재하지 않더라도 계속 진행하고 싶다.
fs.readFile(p, (err, fileContent) => {
let products = [];
if (!err) {
products = JSON.parse(fileContent)
}
products.push(this)
fs.writeFile(p, JSON.stringify(products), (err) => {
console.log(err)
})
} )
errlog로는 null이 출력돼고, data/products.json 파일이 생성이 되면서, 사용자가 입력한 data값이 기록이 되는 것을 확인하였다.
이제 실행을 해보면, 될 줄 알았는데
TypeError: Cannot read properties of undefined (reading 'length')
length문제가 발생한다.
static fetchAll() {
const p = path.join(
path.dirname(require.main.filename),
"data",
"products.json"
);
fs.readFile(p, (err, fileContent) => {
if (err) {
return [];
}
return JSON.parse(fileContent)
})
//return products;
}
fetchAll()에서 return [] , return JSON.parse(fileContent)게 두 경우 모두 data를 반환하고 있지만, 비동기식 코드이기에,
fs.readFile(p, (err, fileContent) => {..})
(err, fileContent) => {..}의 화살표 함수내에 존재하는 return JSON.parse(fileContent)
는 안쪽 함수에 포함되는 것이고 fetchAll()에 반환값으로 적용되지 않는것이기 때문에 fetchAll()가 종료된 후에는 아무것도 반환되지 않는것이다. 따라서 undefined
인 상태이고
exports.getProduct = (req, res, next) => {
const products = Product.fetchAll();//저장된 배열내 data call
res.render("shop", {
prods: products,
docTitle: "Shop",
path: "/",
hasProducts: products.length > 0,
activeShop: true,
productCSS: true,
});
}
products
상수에는 undefined
의 값이 대입되어 있고, view를 render()시에 정의되지 않은 상태에서 해당 값의 길이를 읽지 못하는 오류가 발생하는 것이다. 이를 해결하기 위해 fetchAll()
에 매계변수로 콜백함수를 전달하도록 수정했다. 이제 fetchAll()
에 함수를 전달할 수 있게 되고 함수가 끝난뒤 fetchAll()
이 함수를 실행한다.
//models/product.js
static fetchAll(cb) {
const p = path.join(
path.dirname(require.main.filename),
"data",
"products.json"
);
fs.readFile(p, (err, fileContent) => {
if (err) {
cb([]);
} else {
cb(JSON.parse(fileContent));
}
});
}
//controllers/products.js
exports.getProduct = (req, res, next) => {
Product.fetchAll((products) => {
res.render("shop", {
prods: products,
docTitle: "Shop",
path: "/",
hasProducts: products.length > 0,
activeShop: true,
productCSS: true,
});
});//저장된 배열내 data call
}