Node.js 교재 · 11편 · Buffer

Buffer — 바이너리 데이터 다루기 기초

fs·crypto·HTTP·이미지 — Node 의 모든 바이너리는 결국 Buffer.

바이너리 바이트가 버퍼 그리드에 쌓이는 컨셉 아이소메트릭 일러스트

6편에서 fs.readFile 에 인코딩 인자를 빼면 Buffer 가 돌아왔다. 10편에선 Stream 의 청크가 또 Buffer. 이미지 업로드·암호화 해시·HTTP 본문 — 자바스크립트 문자열이 아닌 이진 데이터를 다루는 모든 경계에서 Buffer 가 등장한다.

이번 편에서 Buffer 의 정체를 푼다. 결론부터 — Buffer 는 고정 크기 바이트 배열. Uint8Array 의 Node 확장판이라 보면 정확하다.

1. 정체 — Uint8Array 의 Node 확장

브라우저 자바스크립트에 Uint8Array 가 있다 — 0~255 사이 정수가 들어가는 고정 길이 배열. Buffer 는 그걸 상속해 Node 의 인코딩·변환 메서드를 더 얹은 클래스.

const buf = Buffer.from('안녕'); console.log(buf); // <Buffer ec 95 88 eb 85 95> console.log(buf instanceof Uint8Array); // true console.log(buf.length); // 6 (UTF-8 6바이트) console.log('안녕'.length); // 2 (문자 수)

주목 — 문자 수와 바이트 수가 다르다. 한글 한 글자는 UTF-8 로 3바이트. 영문은 1바이트. 이 차이가 모든 인코딩 함정의 근원.

Buffer vs string — 문자열은 의미 (글자), Buffer 는 표현 (바이트). 같은 의미가 인코딩에 따라 다른 바이트로 표현됨. UTF-8 의 "안녕" 6바이트, EUC-KR 의 "안녕" 4바이트, UTF-16 은 4바이트. 의미가 같지만 파일 크기가 다른 이유.

2. 생성하기 — from·alloc·allocUnsafe

// ① 문자열·배열·기존 Buffer 에서 Buffer.from('hello'); // utf-8 Buffer.from('hello', 'base64'); // base64 decode Buffer.from([0x48, 0x69]); // [72, 105] = 'Hi' Buffer.from(otherBuffer); // 복사 // ② 빈 버퍼 (0 으로 채움, 안전) Buffer.alloc(10); // 10 바이트, 모두 0 Buffer.alloc(10, 'a'); // 10 바이트, 'aaaaaaaaaa' // ③ 빈 버퍼 (0 채움 X, 빠르지만 위험) Buffer.allocUnsafe(10); // 메모리에 남은 쓰레기 데이터 그대로
allocUnsafe 주의 — 메모리 초기화를 생략해서 빠르지만 전 프로세스의 잔여 데이터가 그대로 들어있을 수 있다. 보안 이슈 (비밀번호 해시 잔존 등). 정말 성능이 중요하고 즉시 덮어쓰는 경우만 사용. 평소엔 무조건 Buffer.alloc.

3. 변환 — 인코딩 한 줄로 바꾸기

가장 자주 쓰는 패턴. Buffer 와 문자열을 오가는 길.

// Buffer → 문자열 const buf = Buffer.from('hello'); buf.toString(); // 'hello' (기본 utf-8) buf.toString('hex'); // '68656c6c6f' buf.toString('base64'); // 'aGVsbG8=' // 문자열 → Buffer Buffer.from('aGVsbG8=', 'base64').toString(); // 'hello' // 16진수 ↔ Buffer const hex = Buffer.from([0xde, 0xad, 0xbe, 0xef]).toString('hex'); // 'deadbeef'

지원 인코딩 — utf-8(기본) · utf-16le · latin1 · base64 · base64url · hex · ascii. EUC-KR / CP949 같은 한국 인코딩은 기본 미지원iconv-lite npm 패키지 필요.

4. 합치고 자르고 — concat·subarray

Buffer 는 길이 고정. 크기를 바꾸려면 새 Buffer 를 만든다.

// 합치기 const a = Buffer.from('foo'); const b = Buffer.from('bar'); const merged = Buffer.concat([a, b]); // 'foobar' // 자르기 (zero-copy view, 원본 공유!) const view = merged.subarray(3); // 'bar' (같은 메모리 가리킴) // 진짜 복사 const copied = Buffer.from(merged.subarray(3)); // 'bar' (독립 메모리) // 비교·검색 buf.equals(other); // 정확히 같은가 buf.indexOf('llo'); // 시작 위치 또는 -1 buf.includes(Buffer.from([0xff])); // 포함 여부
subarray 의 함정 — 옛 slice 와 같이 원본 메모리를 공유. view 를 수정하면 원본도 바뀜. 옛 코드에 buf.slice(...) 가 보이면 deprecated, subarray 또는 Buffer.from(buf.subarray(...)) 로 명시.

5. 실전 사용처

Buffer 가 등장하는 5가지 일상 시나리오.

  • 이미지·파일 업로드 — multer 등이 받은 파일을 Buffer 로 핸들. DB 에 BLOB 으로 저장하거나 S3 업로드.
  • 해시·암호화crypto.createHash('sha256').update(buf).digest() — 입출력 모두 Buffer.
  • HTTP 본문 raw 처리 — express 의 express.raw() 가 body 를 Buffer 로 전달. 웹훅 서명 검증에 필수.
  • 바이너리 프로토콜 — TCP socket, WebSocket frame, 게임 패킷 — 모두 Buffer 의 바이트 직접 조작.
  • Base64 인코딩 — 이메일 첨부, JWT, Data URI — 바이너리를 텍스트로 안전하게 옮길 때.

실전 예 — JWT 페이로드 빠르게 풀기

const jwt = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjMifQ.sig'; const [, payload] = jwt.split('.'); const decoded = Buffer.from(payload, 'base64url').toString('utf-8'); console.log(JSON.parse(decoded)); // { sub: "123" }

외부 라이브러리 없이 Base64url → 문자열 → JSON 한 줄에 끝. JWT 서명 검증은 crypto 가 필요하지만, 페이로드 내용만 보는 거면 이걸로 충분.

요약 — 11편 좌표

여기까지 정리. Buffer 는 Uint8Array 의 Node 확장 — 고정 크기 바이트 배열. 생성은 Buffer.from(데이터에서) · Buffer.alloc(빈, 안전). 인코딩 변환은 toString('base64') 한 줄. 합치기는 Buffer.concat, 자르기는 subarray(원본 공유 주의). 이미지·해시·HTTP raw·바이너리 프로토콜이 다 Buffer. 한국 인코딩(EUC-KR·CP949)은 iconv-lite. 다음 편에서 raw http 모듈로 직접 서버를 만들어 본다.

다음 편 예고 — HTTP 서버 직접 만들기

Express 없이 http 모듈로 서버 만드는 법. 12편.

© 2026 주나이테크(주) @JUNAITECH