본문 바로가기

Web/Node.JS

[패스트캠퍼스 챌린지 22일차] NodeJS - Stream(2)

Fast campus Logo

 

Fastcampus_Node.js 수강 목록

 

패스트캠퍼스 작심 30일 챌린지 22일차.

 


 

21일차에 이어 오늘도 Stream에 대해 학습하였다.

 

오늘의 주제는 두가지!

첫번째, Stream을 통한 파일 읽기와 Buffer를 통한 파일 읽기의 퍼포먼스 차이 확인.

두번째, Stream 사용시/구현시 주의점.

 


 

먼저 첫번째 주제부터 알아보자.

21일차 강의를 통해 file.txt라는 용량이 큰 파일을 만들었을 것이다.

이 파일을 읽기 위해 아래처럼 소스코드를 작성하였다.

(간략한 예시코드를 보여주기 위해 소스코드 일부는 제거하였다.)

const rs = fs.createReadStream('./file.txt', {  encoding: 'utf-8'})

rs.on('data', (data) => {
    for (let i = 0; i < data.length; i += 1) {    
        if (data[i] !== prevCharacter) {      
            const newCharacter = data[i]      
            prevCharacter = newCharacter
            numBlocksPerCharacter[newCharacter] += 1    
        }  
    }
})
const data = fs.readFileSync('./file.txt', 'utf-8')

for (let i = 0; i < data.length; i += 1) {
    if (data[i] !== prevCharacter) {
        const newCharacter = data[i]
        prevCharacter = newCharacter
        numBlocksPerCharacter[newCharacter] += 1
    }
}

 

fs 함수를 통해 첫번째로 작성한 코드가 Stream을 이용하는 것이고, 두번째 코드가 Buffer를 이용한 것임을 알 수 있다.

소스코드를 보면 두 코드가 같은 기능을 수행하는 것 또한 쉽게 알 수 있을 것이다.

 

그럼 두 코드의 차이점은 무엇일까?

바로 파일을 읽는 속도, 즉 처리 속도에서 차이가 난다.

 

Stream이 이전 강의를 통해 Chunk라는 작은 조각으로 나누어 데이터를 처리한다고 배웠다.

여기서 용량이 큰 file.txt 또한 작은 조각(Chunk)으로 나누어 처리하는데,

이때 작은 조각(Chunk) 하나씩 메모리에 올라 처리된다.

(만약 100MB 파일의 Chunk가 100개면 메모리에 1MB의 작은 조각만 올려 데이터를 처리하는 것이다.)

 

반면 Buffer는 파일 전체를 메모리에 올려놓고 파일의 맨 앞부터 맨 뒤까지 읽으면서 처리한다.

즉 100MB 파일을 모두 메모리에 할당해서 데이터를 처리하며,

데이터 처리가 끝나기 전까지 메모리는 파일 사이즈만큼 사용할 수 없게 된다.

 

그러므로 대용량 데이터를 처리할 경우 Buffer보다 Stream을 통해 메모리를 효율적으로 사용하여 처리해야 한다.

 


 

Stream 처리 시 주의점 학습

 

두번째로 Stream 사용시/구현시 주의점.

 

앞서 Stream은 Chunk라는 작은 조각으로 나누어 데이터를 처리한다고 하였다.

그럼 얼마만큼의 사이즈로 Chunk를 나누는 것일까?

개발자가 별도로 설정하지 않으면 알 수 없다.

 

'Hello World!'를 Chunk 단위로 화면에 출력할 때 'Hello', 'World', '!'가 출력될지,

'Hell', 'o', 'World!'가 출력될지 알 수 없다.

이럴 때 아래 코드와 같이 highWaterMark 옵션으로 Chunk 사이즈를 설정할 수 있다.

const rs = fs.createReadStream('./files/jsons', {
    encoding: 'utf-8',
    highWaterMark: 5
})

[jsons 파일의 내용]
{"size": 1}
{"size": 10}

코드에서는 Chunk 사이즈를 5로 세팅하였는데, 이대로 화면에 출력하면 JSON 형태가 깨져서 출력될 것이다.

내가 원하는 사이즈로 나눠 출력하였는데 정상적인 JSON 데이터가 아니라서

데이터를 처리할 수 없게 되버린다.

 

이런 경우를 대비해 highWaterMark 값에 상관없이 원하는 형태로 데이터를 추출하고

추출한 데이터를 정상적으로 처리할 수 있도록 구현해야 한다.

 


 

아! 그리고 하나 더!

Stream을 통해 대용량 데이터를 효율적으로 처리할 수 있다는 것은 앞의 내용을 통해 충분히 학습하였다.

그리고 Stream의 종류에 대해서도 이미 학습하였다.

 

그럼 특정 대용량 파일을 압축파일로 만드는 기능도 Stream을 통해 할 수 있을 것이다.

강의에서 특정 파일을 읽어(Input Steam) 압축 파일로 변경하고(Transform) 변경된 파일을 생성하는(Output Stream) 기능에 대해 알려줬는데, util 모듈의 promisify 함수와 Stream의 pipeline 함수를 활용하여 쉽게 처리할 수 있다.

 

먼저 필요한 모듈을 가져오고,

const stream = require('stream')
const zlib = require('zlib')
const util = require('util')

 

util에서 제공하는 promisify 함수와 Stream 함수를 통해 압축파일을 생성한다.

util.promisify(stream.pipeline)(
    fs.createReadStream('압축대상 파일'),
    zlib.createGzip(),
    fs.createWriteStream('압축파일 이름')
)

 

Promisify는 이전에 배운 promise 함수와 같은 역할을 하므로 설명 Skip.

 

Pipline은 연속된 stream을 받아 Data Transform을 쉽게 활용할 수 있도록 도와주는 역할을 하며,

Gzip은 GNU 프로젝트를 통해 제공되는 모듈로 NodeJS에서 사용이 가능한 모듈이다.

 


 

이것으로 오늘 학습 내용은 끝.

 

Stream은 파일뿐만 아니라 API나 다른 Resource로부터 얻을 수 있는 대용량 데이터를 처리하는데

효율적인 기능을 제공하는 모듈인만큼 NodeJS를 학습하는 동안

별도로 찾아보며 더욱 깊게 공부해봐야겠다.

 


 

[오늘의 학습통계]

패스트캠퍼스 작심 30일 챌린지 22일차 학습통계

 


 

 


- 패스트캠퍼스 URL : https://bit.ly/37BpXiC

* 본 포스팅은 패스트캠퍼스 환급 챌린지 참여를 위해 작성되었습니다.