티스토리 뷰

기존에 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 메서드의 흐름은 아래와 같다.

  1. readFile 을 통해서 json파일을 읽어온다.
  2. json파일이 생성되어있다면 (이미 하나 이상의 데이터가 존재) json 파일 내부에 있는 데이터를 자바스크립트 array형식으로 파싱하여 array 변수 products에 저장한다
  3. json파일이 생성되어있지 않다면 자바스크립트 array를 빈 상태로 저장해놓는다. let products=[]
  4. 해당 자바스크립트 array에 현재의 새로운 인스턴스(this)를 추가해준다. ( 빈 상태면 첫 데이터가 될 것이고, 빈 상태가 아니라면 추가 되는 것)
  5. 새로 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함수의 흐름을 아래와 같이 변경한다.

  1. fetchAll함수의 인자로 함수를 받는다.
  2. 데이터를 리턴하는 대신 , 인자로 들어온 함수의 인자로 데이터를 넣어주고 해당 함수를 실행한다.
  3. 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);
    }
}
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함