[TIL / node.js] Buffer 이해하기

알락·2022년 10월 21일
0
post-thumbnail

Vanilla node.js를 이용하여 간단한 HTTP 통신을 배우고 있었다. 아니 분명 간단하다고 생각했었는데, 오히려 node.jsphp보다 신경쓸게 더 많아 보였다. body로 전달받는 문자열 하나를 띄우려고 글쎄 이런 코드를 작성해야 한다.

// body 받기 코드
const http = require('http');

const PORT = 3000 ;

const server = http.createServer((req, res)=>{
    let body = [];
  
    req.on('data', (data)=>{
        body.push(data);
    }).on('end', ()=>{
        body = Buffer.concat(body).toString();
        console.log(body)
    })

    res.statusCode = 200;
    res.end(body);
})

server.listen(PORT, ()=>{console.log("This server is opened!")});

req.on(...)으로 데이터를 받고 있다. 그리그 그 과정 중 알게모르게 낯익은 녀석 하나가 껴있다. 바로 Buffer 이다. 그리고 Buffer는 생각보다 존재감이 컸다. 코드에 등장하자마자 이 코드가 어려워보이기 시작했다. 나는 Buffer와 조금 친해져보려고 한다.

Stream

버퍼를 얘기하기 전, Stream이라는 개념도 명확이 짚고 넘어가야 한다. 사실 Stream이라는 단어는 우리가 이미 일상적으로 잘 쓰고 있는 단어이기도 하다. 넷플릭스나, 디즈니플러스 같은 스트리밍 서비스의 스트리밍이 지금 살펴보려는 Stream과 관련이 깊다.
컴퓨터 과학에서 말하는 Stream은 어떤 한 데이터가 다른 지점으로 이동되는 과정, 혹은 흐름이라고 얘기할 수 있겠다. 정확히 이동되는 일련의 '데이터'들이라고 정리해보자.
4K 화질 영화를 본다고 하면 한 편에 어마어마한 크기의 데이터양을 가질 것이다. 우리가 이 영화를 스트리밍 서비스로 볼 때, 꼭 이 영화의 모든 데이터가 컴퓨터에 있어야 영화를 시청할 수 있을까? 아니다, 처음 얼마간에 데이터가 모인 이후에는 불편없이 감상할 수 있다.
Stream은 잘깨 쪼개진 데이터의 흐름이라고 인지를 해야 한다. '잘개 쪼개진 데이터'와 '흐름'을 분리해놓고는 Stream이라고 말할 수 없다 생각된다.

Buffer

Buffer Class는 바이너리 데이터들의 스트림을 직접 다루기 위해 node.js API 에 추가되었습니다.

-- 'node.js 버퍼를 제대로 이해해보자' 중

Stream이 이동하는 일련의 데이터들이라고 했는데, 이제는 어떻게 이동하는 지에 대해서 생각해보려고 한다. 우리가 생각하는 것처럼 데이터들이 움직이기 위해서 필요한 것이 Buffer이다.
Buffer는 데이터의 일시적인 저장 장소이다. 이렇게만 정의하면 메모리와 차이가 없어 보인다. 여기에 '이동하는 데이터를 위한 별도의 일시적인 메모리의 일부 영역' 라고 하면 이제 메모리와 구별해낼 수 있겠다.
실제로 Buffer는 작업이 느린 장치와 빠른 장치 사이에서 데이터를 주고 받을 때 사용된다. 보통 주기억 장치인 RAM은 속도가 빠르고, 보조기억 장치인 ROM은 속도가 느리기 때문에, 파일을 읽어 들어올 때도 Buffer가 사용되겠다. 이외에 위에서 예를 든 스트리밍 서비스에서도, 우리가 감상하는 시간은 정해져있는데, 영화의 데이터는 들어오니까 버퍼에 저장되어 다음 장면들을 재생시킬 것이다. Buffer라는 개념이 사용될 때는 양자 간에 속도 차이가 적어도 존재한다고 생각하면 될 것이다. 이 때 Buffer가 사이즈가 크고, 많을 수록 데이터의 전송 속도는 빨라진다. 하지만 전송속도와 가용 메모리 공간은 상충한다는 걸 잊지말자.

바로 그게 Node.js에서의 Buffer입니다! Node.js는 데이터가 도착하는 시간이나 전송되는 속도를 제어할 수는 없습니다. Node.js가 결정할 수 있는건 언제 데이터를 내보내느냐 입니다. 버스를 언제 출발시킬 수 있는 제어권이 있는 것과 동일합니다. 아직 데이터를 내보낼 때가 아니면, Node.js는 데이터들을 일종의 대기영역인 RAM에 작은 영역인 buffer에 데이터를 넣어놓습니다.

일상생활에서 버퍼작동을 볼 수 있는 예로는 온라인 영상을 시청할 때 입니다. 유튜브를 보는 순간을 상상해보세요. 우리의 인터넷 연결상태가 매우 좋을때에는 영상 스트리밍이 끝날때까지 버퍼를 채우고 데이터가 처리될 수 있게 빠르게 내보내고, 다시 버퍼를 채우고 빠르게 내보내고를 반복합니다.

그러나 인터넷 연결상태가 좋지 못할때에는 첫번째 데이터셋을 처리하고 나서, 영상플레이어는 로딩 아이콘을 띠우면서 ‘buffering’이라는 텍스트를 보여줄 것입니다. 이것은 데이터가 더 모이고 도착할때 까지 기다린다는 의미입니다. 만약에 버퍼가 채워지고 데이터가 처리되면, 영상이 다시 보여지게 될 것입니다. 영상을 보여주는 동안에도, 계속해서 다음 데이터가 도착하고, 버퍼에 채워질 것입니다.

-- 'node.js 버퍼를 제대로 이해해보자' 중

node.js의 Buffer

node.js에서 Buffer는 내장함수다. 어디서든 불러올 수 있다.

const buf = Buffer.from("너의 그 한 마디 말도");

console.log(buf);
// <Buffer eb 84 88 ec 9d 98 20 ea b7 b8 20 ed 95 9c 20 eb a7 88 eb 94 94 20 eb a7 90 eb 8f 84>

console.log(buf.toString());
// 너의 그 한 마디 말도

console.log(buf.toString("hex"));
// eb8488ec9d9820eab7b820ed959c20eba788eb949420eba790eb8f84

console.log(buf.toString("base64"));
// 64SI7J2YIOq3uCDtlZwg66eI65SUIOunkOuPhA==

buf를 출력해보니 16진수로 데이터가 저장되고 있는 것을 확인할 수 있다. 이것조차도 사실 사람의 편의를 위해서 쓰인 것이고, 실상 2진수로 저장이 되어 있을 것이다. 따로 지정을 안한다면 Bufferutf-8로 문자열을 인코딩해서 저장한다. 인코딩이나 디코딩을 다른 체계로 하고 싶다면 따로 지정해 주는 방법이 있으니 참고하면 좋을 것이다.

cf. utf-8은 가변 인코딩 방식을 채택한다. 글자마다 사용하는 데이터 양이 다르다는 것이다. 맨 처음 쓰인 바이트 eb를 풀어 쓰면 11101011이다. 그 중 앞의 '1'이 연속 3개로 끝나니까 한글 '너'를 표현하기 위해 3 바이트를 쓰고 있다는 것을 알 수 있다. 참고로 영어 대소문자는 UTF-8로 1 바이트를 사용한다.

그럼, 다시 제일 위에 쓴 예시를 살펴보자. 콘솔에 띄우는 명령을 하기 전, 데이터를 우리가 알아볼 수 있는 문자열로 변환하는 작업을 주석처리하고 서버를 실행해보겠다. 그리고 이 서버에 똑같이 "너의의 그 한 마디 말도"라고 평문을 body에 넣어 request를 보냈다. 결과는 다음과 같이 나온다.
server example

이를 통해서 node.js http 모듈의 reqest가 데이터를 Buffer의 형식으로 가지고 오고 있다는 것을 알 수 있다.
정리해보자면 node.js에서의 Buffer 클래스는 바이너리 데이터들을 저장, 처리, 가공하기 위한 인터페이스를 제공한다고 생각하면 되겠다.

참고

profile
블록체인 개발 공부 중입니다, 프로그래밍 공부합시다!

0개의 댓글

관련 채용 정보