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 폴더 제거.
This commit is contained in:
Antigravity Agent
2026-07-05 00:33:48 +09:00
parent 1cfd3bbb56
commit 9148c358d0
6455 changed files with 1 additions and 86875 deletions
@@ -0,0 +1,177 @@
---
id: node-streams-patterns
title: Node Streams — Readable / Writable / Transform / Web Streams
category: Coding
status: draft
source_trust_level: B
verification_status: conceptual
created_at: 2026-05-09
updated_at: 2026-05-09
tags: [node, streams, async-iterator, vibe-coding]
tech_stack: { language: "TS / Node", applicable_to: ["Backend"] }
applied_in: []
aliases: [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 (가장 안전)
```ts
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)
```ts
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 (변환)
```ts
import { Transform } from 'node:stream';
const upper = new Transform({
transform(chunk, _enc, cb) {
cb(null, chunk.toString().toUpperCase());
},
});
await pipeline(stdin, upper, stdout);
```
### 직접 Readable 만들기
```ts
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 / 브라우저 호환)
```ts
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 변환
```ts
import { Readable, Writable } from 'node:stream';
const nodeReadable = Readable.fromWeb(webReadable);
const webReadable = Readable.toWeb(nodeReadable);
```
### Backpressure 직접 제어
```ts
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
```ts
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
```ts
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 처리
```ts
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.
## 🔗 관련 문서
- [[Backend_WebSocket_Scaling]]
- [[AI_Streaming_LLM_Response]]
- [[Backpressure_Patterns]]