티스토리 뷰
[node js] Express: Model 에서 file형태로 데이터 저장 및 읽어오기
무니웜테일패드풋프롱스 2020. 6. 21. 14:11기존에 MVC 패턴 구현에서 한걸음 더 나아가, Model 에서 클라이언트로부터 받아온 파라미터를 array로 저장하지 않고 file로 저장해보도록 한다.
models / product.js 파일 ( product 관련 데이터를 다루는 모델 ) 에 아래와 같이 파일 처리를 위해 fs 를 import 해준다.
그리고, 파일이 저장 될 ( 된) 위치에 접근하기 위해 path와 rootDir도 import 해준다.
const fs = require('fs');
const path = require('path');
const rootDir = require('../util/path');
이전과 같이 class의 형태로 exports를 해주되 , class 내 save와 fetch가 다르게 구현되어야 한다.
먼저 save 메서드의 흐름은 아래와 같다.
- readFile 을 통해서 json파일을 읽어온다.
- json파일이 생성되어있다면 (이미 하나 이상의 데이터가 존재) json 파일 내부에 있는 데이터를 자바스크립트 array형식으로 파싱하여 array 변수 products에 저장한다
- json파일이 생성되어있지 않다면 자바스크립트 array를 빈 상태로 저장해놓는다. let products=[]
- 해당 자바스크립트 array에 현재의 새로운 인스턴스(this)를 추가해준다. ( 빈 상태면 첫 데이터가 될 것이고, 빈 상태가 아니라면 추가 되는 것)
- 새로 update된 products를 다시 파일에 쓰기 위해서 products변수를 다시 json 파일 형태로 파싱해준후, json파일에 저장한다.
save(){
// products.push(this);
const p = path.join(rootDir,'data','products.json'); //파일위치
fs.readFile(p, (err, fileContent)=>{ // 파일 읽기
let products=[]; // 저장될 array
if(!err){ //해당 fileContent가 이미 존재한다면 ==> 존재하지 않는다면 err 날림
products = JSON.parse(fileContent); //json 파일 내 데이터를 js array로 파싱하여 product에 저장
}
products.push(this); // array에 인스턴스 넣기
// *** 새로 update된 products를 다시 파일에 쓰기 **** //
// js -> json 파싱 (stringfiy)
fs.writeFile(p, JSON.stringify(products), (err)=>{ //다시 파일에 저장
console.log(err); //arrowfunction을 통해서 err check
});
});
}
그리고 데이터를 외부로 내보내는 fetchAll 메서드를 살펴보도록 한다.
fetchAll 메서드에서 아래와 같이 fileContent를 파싱해서 바로 return하게 되면 비동기 처리에 의해서 에러가 발생한다.
괄호 친 부분은 readFile의 콜백함수이므로 비동기적으로 처리되어 fetchAll함수가 결국 아무것도 return하지 않고 끝나게 된다. 따라서 Controller는 아무런 데이터도 받을 수 없어 에러가 발생한다.
따라서 fetchAll함수의 흐름을 아래와 같이 변경한다.
- fetchAll함수의 인자로 함수를 받는다.
- 데이터를 리턴하는 대신 , 인자로 들어온 함수의 인자로 데이터를 넣어주고 해당 함수를 실행한다.
- Controller에서는 콜백함수를 fetchAll의 인자로 넣어주고, 해당 콜백함수 실행시 받을 인자를 설정하고, 받은 인자 *모델로부터 추출한 데이터* 를 통해- render를 콜백 프로세스로 넣어준다.
exports.getProducts=(req,res,next)=>{
Product.fetchAll((products)=>{ //이 익명의 콜백함수를 fetchAll에 인자로 넣는다.
// 해당 콜백함수가 실행되면 데이터를 담은 or 빈 array가 products자리에 인자로 들어온다.
res.render('shop',{
pageTitle:'shop',
prods:products,
path:'/'
});
}); //products array 가져오기
};
static fetchAll(cb){ //콜백함수를 인자로 받아온다.
const p = path.join(rootDir,'data','products.json'); //데이터가 저장된 파일의 위치
fs.readFile(p,(err,fileContent)=>{ //파일을 읽는다.
if(err){ //파일이 존재 X 즉, 데이터가 없다면
return cb([]); //해당 콜백함수 인자에 [] 을 넣어서 실행
}
cb(JSON.parse(fileContent)); //해당 콜백함수 인자에 stream 데이터가 parsing된 형태를 넣어서 실행
});
}
따라서 전체 model 소스코드는 아래와 같이 변경된다.
const fs = require('fs');
const path = require('path');
const rootDir = require('../util/path');
module.exports= class Product{
constructor(title){
this.title=title;
}
save(){
// products.push(this);
const p = path.join(rootDir,'data','products.json'); //파일위치
fs.readFile(p, (err, fileContent)=>{
let products=[];
if(!err){ //해당 filecontent가 이미 존재한다면
products = JSON.parse(fileContent); //json 파일을 js array로 파싱
}
products.push(this); // array에 넣기
// js -> json 파싱 (stringfiy)
fs.writeFile(p, JSON.stringify(products), (err)=>{ //다시 파일에 저장
console.log(err); //arrowfunction을 통해서 err check
});
});
}
static fetchAll(cb){ //콜백함수를 인자로 받아옴
const p = path.join(rootDir,'data','products.json');
fs.readFile(p,(err,fileContent)=>{
if(err){
cb([]); //해당 콜백함수 인자에 [] 을 넣어서 실행
}
cb(JSON.parse(fileContent)); //해당 콜백함수 인자에 stream 데이터가 parsing된 형태를 넣어서 실행
});
}
}
그리고 json 파일에는 아래와 같이 저장된다.
[{"title":"first book"},{"title":"second book"}]
위와같이 완성된 model 소스를 refactoring 해보도록 하겠다.
먼저 겹치는 부분은 fatchAll 에서 readFile하는 코드와 save에서 readFile하는 코드다. 따라서 겹치는 코드를 하나의 다른 함수로 만들어보도록 하겠다.
먼저 , 함수의 인자로 함수를 받도록 한다. 따라서 fetchAll 메서드에서 그랬듯이, 데이터를 cb 함수의 인자로 넘겨준다
const getProductfromFile = cb =>{
fs.readFile(p,(err,fileContent)=>{
if(err){
return cb([]);
}
cb(JSON.parse(fileContent)); //해당 콜백함수 인자에 stream 데이터가 parsing된 형태를 넣어서 실행
});
}
그러면 fetchAll 메서드는 아래와 같이 변경되고, 결과는 같다.
static fetchAll(cb){ //콜백함수를 인자로 받아옴
getProductfromFile(cb);
}
save 역시 fileRead와, read된 file에서 fileContents를 받아오는 것이 같기 때문에,
getProductfromFile 함수의 인자(cb)로 콜백함수를 정의해주는데, getProductfromFile 내부로부터 인자(데이터)를 받아온 후, 콜백함수 프로세스 내에 해당 데이터에 새로운 인스턴스를 push해준 후 , writeFile을 해주면 된다.
save(){
getProductfromFile(products=>{ // 인자로 콜백함수 넣고, getProductfromFile에서
// 읽은 데이터를 porducts에다 전달해줌
products.push(this);
fs.writeFile(p, JSON.stringify(products), (err)=>{ //다시 파일에 저장
console.log(err);
});
});
}
전체 model / product.js 코드는 아래와 같다.
const fs = require('fs');
const path = require('path');
const rootDir = require('../util/path');
const p = path.join(rootDir,'data','products.json');
const getProductfromFile = cb =>{
fs.readFile(p,(err,fileContent)=>{
if(err){
return cb([]);
}
cb(JSON.parse(fileContent));
});
}
module.exports= class Product{
constructor(title){
this.title=title;
}
save(){
getProductfromFile(products=>{
products.push(this);
fs.writeFile(p, JSON.stringify(products), (err)=>{
console.log(err);
});
});
}
static fetchAll(cb){
getProductfromFile(cb);
}
}
'web : back-end > node js' 카테고리의 다른 글
[node js] Express: 저장된 파일 내용 삭제하기 (0) | 2020.06.24 |
---|---|
[node js] Express: 동적 페이지 처리하기 (0) | 2020.06.23 |
[node js] Express: MVC 패턴 (0) | 2020.06.21 |
[node js] Express: EJS 템플릿 엔진 (0) | 2020.06.20 |
[node js] Express: Handlebars 템플릿 엔진 (0) | 2020.06.19 |