어느날 로그파일을 읽어 json으로 가져와야 하는 과제(?)가 주어졌습니다.
또한 로그 파일이 생각보다 큰 경우도 존재합니다.
fs모듈의 readFile과 같은건 파일 전체를 메모리에 올려버리기 때문에 메모리 사용량이 엄청 늘어납니다.(대용량 비추)
음..
Nodejs의 fs와 event-stream이라는 stream을 쉽게 사용할수 있도록 도와주는 라이브러리를 이용해서
특정 파일을 읽어서, 객체화하는 방법을 정리합니다.
순서는 아래와 같습니다.
- 파일 스트립으로 읽기
- chunk 단위로 수행하고자하는 작업 수행
- stream을 promise로 변경
- 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 await의 await부분은 무조건 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 사용자라면 알아두면 좋을듯 합니다.
'개발 이야기 > NodeJS' 카테고리의 다른 글
Gulp 사용 시 Nodejs 구버전 빌드 실패 (0) | 2024.08.19 |
---|---|
초간단 Express + Mysql 환경 셋팅 (0) | 2019.05.14 |
NodeJS 콜백을 사용한 비동기 제어 흐름 패턴 (0) | 2019.03.09 |
NodeJS 동작 방식과 개념 정리 (0) | 2019.03.02 |
NodeJS 인기있는 Logging 모듈 Winston (0) | 2019.02.20 |