Files
2nd/10_Wiki/Topic_Programming/Coding/Node_Streams_Patterns.md
T
Antigravity Agent 9148c358d0 docs(10_Wiki): 위키 전체 재구성 — Topic_* 폴더를 4개 카테고리로 통합 + 대규모 중복 제거
Topic_Agent/Topic_Blog/Topics/Topics_Biz/Topics_Meeting/Topics_Rag의 마크다운 지식 문서를
Topic_General/Topic_Programming/Topic_Graphic/Topic_Business 4개 카테고리로 재분류.

- 중복 제거: frontmatter의 status:duplicate/merged + duplicate_of/redirect_to 필드로
  자기 자신을 중복으로 선언한 리다이렉트 stub 1032개 제거, 완전 동일 내용 파일 472개 제거,
  동일 파일명·다른 내용 충돌 시 더 큰(완전한) 버전만 유지(162개 제거) — 총 1639개 중복 제거.
- 분류: 폴더 단위로 명확한 항목(AI_and_ML/Coding/Architecture 등 → Programming,
  Comfyui/Visual_Effects → Graphic, Topics_Biz/Topics_Meeting/사업 등 → Business,
  Poetic_Blog_Writing/창의성/Game_Design 등 → General)은 폴더 우선순위로,
  나머지 혼재 폴더(Topic_Agent/Topic_Blog/Topics 루트/Thinking & Reasoning/Other/UI_UX_Assets)는
  title/tags 키워드 스코어링으로 파일 단위 분류(불명확한 경우 General로 폴백).
  원본 폴더명은 "From_*" 서브폴더로 보존해 추적 가능성 유지.
- 최종 배치: Programming 2784 / General 1608 / Graphic 285 / Business 249 = 4926개 문서.
- 에이전트 운영 상태(.astra/.agent/.obsidian/sessions/memory/_company/docs/lessons/_shared/src)는
  지식 콘텐츠가 아니므로 재분류 대상에서 제외하고 원위치 유지.
- Topics/Topic_email(상위 보호 폴더 Topic_email과 파일명 100% 중복) 삭제 — 보호 폴더 자체는 미변경.
- 완전히 비게 된 Topic_Agent/Topic_Blog/Topics_Biz/Topics_Rag 폴더 제거.
2026-07-05 00:33:48 +09:00

4.5 KiB

id, title, category, status, source_trust_level, verification_status, created_at, updated_at, tags, tech_stack, applied_in, aliases
id title category status source_trust_level verification_status created_at updated_at tags tech_stack applied_in aliases
node-streams-patterns Node Streams — Readable / Writable / Transform / Web Streams Coding draft B conceptual 2026-05-09 2026-05-09
node
streams
async-iterator
vibe-coding
language applicable_to
TS / Node
Backend
Readable
Writable
Transform
pipeline
async iterator
Web Streams
ReadableStream

Node Streams

큰 파일 / 무한 데이터 = stream. pipeline() + async iterator + Web Streams 가 modern. Backpressure 자동 처리. 옛 .pipe() / event listener 보다 안전.

📖 핵심 개념

  • Readable: 읽기 (file, network response).
  • Writable: 쓰기.
  • Transform: 변환 (gzip, parser).
  • Async iterator: for await (const chunk of stream).
  • Backpressure: writable 가 느리면 readable 도 멈춤.

💻 코드 패턴

pipeline (가장 안전)

import { pipeline } from 'node:stream/promises';
import { createReadStream, createWriteStream } from 'node:fs';
import { createGzip } from 'node:zlib';

await pipeline(
  createReadStream('input.txt'),
  createGzip(),
  createWriteStream('output.txt.gz'),
);
// 한쪽 에러 → 모든 stream cleanup

Async iterator (read)

import { createReadStream } from 'node:fs';
import { createInterface } from 'node:readline';

const rl = createInterface({ input: createReadStream('big.txt'), crlfDelay: Infinity });
for await (const line of rl) {
  if (line.includes('error')) console.log(line);
}

Transform (변환)

import { Transform } from 'node:stream';

const upper = new Transform({
  transform(chunk, _enc, cb) {
    cb(null, chunk.toString().toUpperCase());
  },
});

await pipeline(stdin, upper, stdout);

직접 Readable 만들기

import { Readable } from 'node:stream';

async function* generate() {
  for (let i = 0; i < 1_000_000; i++) yield `${i}\n`;
}

const stream = Readable.from(generate());
await pipeline(stream, createWriteStream('out.txt'));

Web Streams (modern, fetch / 브라우저 호환)

const r = await fetch('https://example.com/big.json');
const reader = r.body!.getReader();
const decoder = new TextDecoder();

while (true) {
  const { value, done } = await reader.read();
  if (done) break;
  process(decoder.decode(value, { stream: true }));
}

// 또는 async iterator (Node 18+)
for await (const chunk of r.body!) { ... }

Web ↔ Node 변환

import { Readable, Writable } from 'node:stream';

const nodeReadable = Readable.fromWeb(webReadable);
const webReadable = Readable.toWeb(nodeReadable);

Backpressure 직접 제어

async function writeLines(writable: Writable, lines: AsyncIterable<string>) {
  for await (const line of lines) {
    if (!writable.write(line)) {
      await new Promise(r => writable.once('drain', r));
    }
  }
  writable.end();
}

CSV streaming parse

import { parse } from 'csv-parse';

await pipeline(
  createReadStream('big.csv'),
  parse({ columns: true }),
  async function* (rows) {
    for await (const row of rows) {
      yield JSON.stringify(row) + '\n';
    }
  },
  createWriteStream('out.ndjson'),
);

HTTP response streaming

import { Readable } from 'node:stream';

app.get('/big', async (req, res) => {
  res.setHeader('content-type', 'application/x-ndjson');
  const data = streamFromDB();
  await pipeline(data, res); // 자동 backpressure
});

Error 처리

try {
  await pipeline(...);
} catch (e) {
  // 모든 stream 에서 발생한 에러 캡처
}

🤔 의사결정 기준

상황 추천
큰 파일 read/write createReadStream + pipeline
HTTP body for await (chunk of req)
변환 Transform 또는 async generator
Browser 호환 Web Streams
메모리 한계 stream 필수
작은 데이터 string / Buffer 직접

안티패턴

  • .pipe(...) 직접 + error handler 누락: dangling stream.
  • String concat 으로 GB 파일 read: OOM.
  • on('data') 만 + flow mode: backpressure 무시. async iterator.
  • Transform _flush 누락 + 마지막 chunk: 데이터 일부 잃음.
  • paused readable 그대로 await: hang.
  • String chunk 가정: encoding 불일치 가능. setEncoding('utf8').

🤖 LLM 활용 힌트

  • pipeline + async iterator + Web Streams 3종.
  • 큰 데이터는 항상 stream.
  • Error = pipeline catch.

🔗 관련 문서