본문 바로가기
개발 이야기/NodeJS

Nodejs 파일 읽어 Promise 다루는 방법

by 농개 2019. 11. 28.
반응형

 

 

어느날 로그파일을 읽어 json으로 가져와야 하는 과제(?)가 주어졌습니다.

또한 로그 파일이 생각보다 큰 경우도 존재합니다.

fs모듈의 readFile과 같은건 파일 전체를 메모리에 올려버리기 때문에 메모리 사용량이 엄청 늘어납니다.(대용량 비추)

음..

Nodejs의 fsevent-stream이라는 stream을 쉽게 사용할수 있도록 도와주는 라이브러리를 이용해서

특정 파일을 읽어서, 객체화하는 방법을 정리합니다.

순서는 아래와 같습니다.

 

  1. 파일 스트립으로 읽기
  2. chunk 단위로 수행하고자하는 작업 수행
  3. stream을 promise로 변경
  4. async await로 promise를 제어

 

 

01. 파일 스트림으로 읽기

const fs = require('fs');
const es = require('event-stream');   // npm install --save event-stream 필요

const stream = fs.createReadStream(fileName)
    .pipe(es.split())                 // default : \n으로 스플릿
    .pipe(es.map(function(line, cb){
      console.log(line)

      //todo : do something with the line
      cb(null, line)
    }))
  ;

fs의 createReadStream을 사용.

pipe를 통해 마치 리눅스명령어의 파이프라인 처럼 Stream을 연결해서 사용할 수 있습니다.

es.split으로 라인별로 Stream을 구분시킵니다.

es.map을 통해 각 라인을 읽어서 원하는 작업을 할 수 있습니다.

 

 

02. 원하는 작업 수행 (ex. line을 읽어서 배열로 저장)

const fs = require('fs');
const es = require('event-stream');

function getFileContents(fileName){
  const results = [];
  const stream = fs.createReadStream(fileName)
    .pipe(es.split())                 // default : \n으로 스플릿
    .pipe(es.map(function(line, cb){
      results.push(line);

      //todo : do something with the line
      cb(null, line)
    }));
  return results;  //! 그러나 결과값은 []이 된다.
}

파일을 라인별로 읽어서 배열로 리턴하는 함수를 만들었습니다.

하지만 실행해보면 return 되는 값은 빈 Array가 될 것입니다.

 

stream은 비동기적으로 파일을 chunk단위로 읽으며 수행되고,

그전에 getFileContents 함수는 이미 빈 배열을 return 해버리기 때문입니다.

 

 

Promise화 해봅시다.

 

 

03. Stream To Promise

function getFileContents(fileName){
  const results = [];
  const stream = fs.createReadStream(fileName)
    .pipe(es.split())                 // default : \n으로 스플릿
    .pipe(es.map(function(line, cb){
      results.push(line);

      //todo : do something with the line
      cb(null, line)
    }))
  ;
  return new Promise((resolve, reject) => {
    stream.on('error', function(err){
      console.log('File read Error.');
      resolve(reject);
    })
  
    stream.on('end', function(){ // nodejs에서 Stream은 기본적으로 event emitter이다.
      console.log('ReadStream End.');
      resolve(results);  // Array 반환
    })
  })
}

위와 같이 작성합니다.

return new Promise를 통해 Promise를 반환하도록 변경했습니다.

Stream은 기본적으로 event emitter이며, on을 통해 특정 이벤트 발생시 실행하는 함수를 등록할 수 있습니다.

end 이벤트 발생시, 라인별로 저장한 배열을 반환도록 했습니다.

 

 

04. async await로 promise 다뤄보기

//! async await
(async function main(){
  const file_name = "log_20191127.txt";
  const data = await getFileContents(file_name);
  console.log(data);
})();

앞서 만든 getFileContents는 Promise를 반환합니다.

async awaitawait부분은 무조건 Promise를 반환하게 되있습니다.

 

실행해보면

// logfile.txt
This is log file.
aaa
bbb
ccccc



// 실행결과
[ 'This is log file.', 'aaa', 'bbb', 'ccccc' ]

async await는 ES10에 추가 되었고, Nodejs 버전도 8이상에서 사용가능합니다.

 

비동기 작업을 쉽게 다루기 위해 Promise(ES6)가 만들어지고,

Promise를 또 쉽게 다루기 위해 async await(ES10)가 생겨났습니다.

 

Javascript 사용자라면 알아두면 좋을듯 합니다.

 

반응형