백엔드

패스트캠퍼스 챌린지 6일차 - 스트림과 노드

꾸준이 2021. 9. 11. 15:31

0.공부 기록

패캠 시작 12 : 20

점심시간 13:00 ~ 14 :00

패캠 끝~! 15 :30

 

인증샷

 

공부 노트 작성 = 코드블럭입니다.

컴퓨터 공부 특성 상 노트를 따록 작성하는 것보는 블로그의 코드블럭에 저장하는 것이 더 효과적인 것 같습니다

 

 

1. Stream 이란

스트림은 스트림 가능한 소스로부터 데이터를 작은 청크로 쪼개 처리할 수 있음!

큰 데이터(영상 등)를 처리해야 하거나 비동기적(네트워크 등)으로만 얻을 수 있는 데이터를 처리해야 할 때 유용함.

-> 큰 데이터를 쪼개어서 연산 실행

 

스트림의 일반적인 구현 형태

const fs = require('fs')
const rs = fs.createReadStream('file.txt')

rs.on('data', data => {})

rs.on('error', error => {})

rs.on('end', () => {})

// 여러 이벤트 핸들러를 달아서 처리함.
// 특별히 지정하지 않으면 data는 Buffer가 됨.

const rs = fs.createReadStream('file.txt', { encoding : 'utf-8' })
// 인코딩을 지정하면 data를 string으로 받아올 수 있음

 

 

2. 스트림의 종류와 스탠다드 라이브러리 구현체들

Readable 스트림으로부터 읽을 수 있음.

-> fs.createReadStream

-> process.stdin

-> 서버 입장의 HTTP 요청

-> 클라이언트 입장의 HTTP 응답

 

Writable 스트림을 출력할 수 있음.

-> fs.createWriteStream

-> process.stdout

-> 클라이언트 입장의 HTTP 요청

-> 서버 입장의 HTTP 응답

 

Duplex 입 출력이 가능한 스트림

-> TCP Socket

-> zlib stream :: 압축 관련

-> crypto stream :: 암호화 관련

 

Transform 입력받은 스트림을 새로운 스트림으로~

-> zlib stream :: 압축이라니깐~

-> crypto stream :: 암호화를 거치고 나오니깐~

 

3.스트림으로 사용해서 큰 데이터 처리해보기

500mb 파일을 생성하기

const fs = require('fs')

const ws = fs.createWriteStream('local/big-file')


const NUM_MB = 500 //500MB 파일을 만들것임.

for (let i = 0; i < NUM_MB; i += 1 ){
	ws.write('a'.repeat(1024 * 1024)) //1바이트짜리가 1024 = 1mb, 
}

* time node index.js :: 프로그램이 작동하는데 걸린 시간을 측정할 수 있음

 

500MB 파일을 읽어보기

const fs = require('fs')

const rs = fs.createReadStream('local/big-flie')

rs.on('data', data => {
	log("EVE", data[0])
    // 97 -> 버퍼의 0번째 자리를 읽은 것
    // 97 은 소문자a의 아스키코드
})

rs.on('end', () => {
	log("end")
    // 모든 파일을 다 읽게 되면 출력됨
})

인코딩 값을 설정하고 문자열의 형태로 받아오기

const fs = require('fs')

const rs = fs.createReadStream('local/big-flie', {
	encoding : 'utf-8'
})

let chunkCount = 0

rs.on('data', data => {
	chunkCount += 1
	log("EVE", data[0])
	// a :: 스트링으로 출력이 되니깐~
})

rs.on('end', () => {
	log(chunkCount)
    // 8000 :: 8000번을 나눠서 데이터를 읽어온 것임
    
	log("end")
})

스트링 파일에서 a의 연속 구간의 개수와 b의 연속 구간의 개수를 세는 프로그램

-> 버퍼로 처리를 해야한다면, 500mb를 램에 넣어야 했음.

-> 스트림 덕에 그러지 않아도 됨.

 

a 블럭과 b 블럭을 생성하는 코드

// @ts-check

const fs = require('fs')

const ws = fs.createWriteStream('local/big-file')

const NUM_MB = 500 //500MB 파일을 만들것임.

/** @type {Object.<string, number>} */
const numBlockPerChar = {
	a : 0,
    b : 0
}


for (let i = 0; i < NUM_MB; i += 1 ){
	const blockLength = Math.floor(800 + Math.random() * 200)
    const char = i % 2 === 0 ? 'a' : 'b'
	ws.write(char.repeat(1024 * blockLength)) //1바이트짜리가 1024 = 1mb, 
 
 	numBlockPerChar[char] += 1
}

console.log(numBlockPerChar)

a 블럭과 b 블럭의 개수를 세어보는 코드

스트림을 통해서 읽어오는 버퍼의 값을 키울 수도 있음(옵션값으로 highWaterMark 를 사용하면 됨 (기본 값 : 65536))

// @ts-check

const fs = require('fs')

const rs = fs.createReadStream('local/big-flie', {
	encoding : 'utf-8',
 	highWaterMark : 65536 * 2 // 스크림의 청크의 개수가 절반으로 줄어듬
})

/** @type {Object.<string, number>} */
const numBlockChar = {
	a : 0,
    b : 0
}

/** @type {string | undifined} */
let prevChar

rs.on('data', data => {
	if(typeof data !== 'string') return

	for(let i = 0; i < data.length; i+=1){
    	if(data[i] !== prevChar){
        	const newChar = data[i]
            
            if(!newChar) countinue
            
            prevChar = newChar
            numBlockChar[newChar] += 1
        }
    }
})

rs.on('end', () => {
	log(chunkCount)
	log(numBlockChar)
})

 

 

4. 퍼포먼스의 차이가 있을까? (스트림 VS 버퍼)

버퍼를 통해서 한번에 파일을 읽어오기

const data = fs.readFileSync('file', 'utf-8')

-> 버퍼의 경우 모든 내용을 램에 올려두고 작업이 끝날때까지 내리지 못하지만,
스트림의 경우에는 청크단위로 램에 올리기에 훨씬 메모리 쪽 부하가 덜함.

 

 

5. 스트림 활용의 주의점들

정상 JSON들에 한하여 data값을 모두 더하기

문제점 1 : 어디서 chunk 가 짤릴지 모름 !

예시 : (highWaterMark 의 기본 값이 충분히커서 에러X : 에러는 하기 예시 참고)

const { log } = console

const fs = require('fs')

const rs = fs.createReadStream('json', {encoding : "utf-8"})

let totalSum = 0

rs.on('data', data => {
	if(typeof data !== 'string') return
    
    // 이러면 에러가 발생함!
    totalSum =data.split('/n')
    	.map(jsonLine => {
        	try{
            	retrun JSON.parse(jsonLine)
            }catch{
            	return undifined
            }
        })
        // 어라...이게 뭐지 filter는 뭐야!
        .filter(json => json)
        .map(json => json.data)
        // reduce 는 그 전값과 지금 값을 가져올 수 있음.
        // 그래서 덧셈이나 이런 작업을 하기에 좋음
        .reduce( (sum, cur) => sum + cur, 0)
})

rs.ons('end', () => {
	log("finished")
    log(totalSum)
})

highWaterMark 의 값을 조절하고, 에러가 발생하는 부분을 보강하기

const { log } = console

const fs = require('fs')

const rs = fs.createReadStream('json', {
	encoding : "utf-8",
    highWaterMark : 6 // 한번에 읽어오는 문자가 6글자임.
    // 이렇게 설정하면 정상적인 계산이 불가능 함.
    // 
})

let totalSum = 0

let accumJsonStr = ""

rs.on('data', chunk => {
	if(typeof chunk !== 'string') return
    
    accumJsonStr += chunk
    
	const lastNewLineIdx = accumJsonStr.lstIndexOf('\n')
    
	const jsonLineStr = accumJsonStr.substring(0, lastNewLineIdx) // JSON 이 여러개 있을 수 있는 블럭
    accumJsonStr = accumJsonStr.substring(lastNewLineIdx) // 아직 JSON 이 완성되지 않은 블럭
    
    
    totalSum =jsonLineStr.split('/n')
    	.map(jsonLine => {
        	try{
            	retrun JSON.parse(jsonLine)
            }catch{
            	return undifined
            }
        })
        // 어라...이게 뭐지 filter는 뭐야!
        .filter(json => json)
        .map(json => json.data)
        // reduce 는 그 전값과 지금 값을 가져올 수 있음.
        // 그래서 덧셈이나 이런 작업을 하기에 좋음
        .reduce( (sum, cur) => sum + cur, 0)
})

rs.ons('end', () => {
	log("finished")
    log(totalSum)
})

 

5-2 : 파이프라인과 프로미스

파이프 라인을 통해서 압축 다루기 (복잡한 콜백을 프로미스로 전환하여 사용하기)

const { log, error } = console

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

const util = require('util')

async function gzip(){
  util.promisity(stream.pipeline)(
    fs.createReadStream('fileName')
    zlib.createGzip(),
    fs.createWriteStream('fileName.gz'),
  )
}

gzip()

 

이런거는 뭘까 / promisfy(stream.pipeline)(~~)

 

 

 

환급 챌린지 (6/30)

 

 

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

https://bit.ly/37BpXiC

 

패스트캠퍼스 [직장인 실무교육]

프로그래밍, 영상편집, UX/UI, 마케팅, 데이터 분석, 엑셀강의, The RED, 국비지원, 기업교육, 서비스 제공.

fastcampus.co.kr

#패스트캠퍼스 #패캠챌린지 #직장인인강 #직장인자기계발 #패스트캠퍼스후기 #한번에끝내는Node.js웹프로그래밍초격차패키지