[G1-Sync] Manual knowledge update
This commit is contained in:
@@ -2,136 +2,182 @@
|
||||
id: wiki-2026-0508-탭과-싱크-taps-and-sinks
|
||||
title: 탭과 싱크(Taps and Sinks)
|
||||
category: 10_Wiki/Topics
|
||||
status: needs_review
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: []
|
||||
aliases: [Tap, Sink, Side-effect observation, Stream sink]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.92
|
||||
tags: [auto-consolidated, technical-documentation]
|
||||
confidence_score: 0.85
|
||||
verification_status: applied
|
||||
tags: [streams, reactive, effect, observability, architecture]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-08
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
inferred_by: Claude Opus 4.7 (auto-normalize 2026-05-08)
|
||||
tech_stack:
|
||||
language: unspecified
|
||||
framework: unspecified
|
||||
language: TypeScript
|
||||
framework: Effect / RxJS
|
||||
---
|
||||
|
||||
# 수도꼭지와 배수구(Taps and Sinks)
|
||||
# 탭과 싱크(Taps and Sinks)
|
||||
|
||||
## 📌 한 줄 통찰 (The Karpathy Summary)
|
||||
수도꼭지(Taps/Faucets)와 배수구(Sinks)는 가상 게임 경제에서 자원의 생성과 소멸을 관리하는 가장 핵심적인 아키텍처입니다 [1]. 수도꼭지는 게임 내로 재화가 무에서 생성되어 유입되는 지점을 뜻하며, 배수구는 유통되는 재화를 시스템에서 제거하거나 소모하게 만드는 장치입니다 [1, 2]. 성공적인 게임 경제 설계는 이 두 메커니즘의 평형을 정교하게 맞추어 인플레이션을 억제하고 재화의 가치를 보존하며, 궁극적으로 플레이어의 인앱 결제(IAP) 매력도를 유지하는 것을 목표로 합니다 [1, 3].
|
||||
## 매 한 줄
|
||||
> **"매 stream 을 손대지 않고 들여다보는 tap, 매 stream 을 끝내며 결과를 모으는 sink."**. Reactive / functional stream architecture 의 매 두 핵심 primitive. Tap 은 매 transparent observer (logging, metrics) — 매 값 변형 X. Sink 는 매 terminal consumer — 매 stream 을 result 로 collapse.
|
||||
|
||||
---
|
||||
## 매 핵심
|
||||
|
||||
'탭과 싱크(Taps and Sinks)' 또는 '수도꼭지와 배수구(Faucets and Sinks)'는 가상 경제 시스템에서 자원의 생성과 소멸을 관리하는 가장 기본적인 아키텍처이다 [1]. 탭(수도꼭지)은 플레이어에게 통화나 자원을 부여하는 활동을 뜻하며, 싱크(배수구)는 플레이어가 그 자원을 소비하는 시스템을 의미한다 [2]. 게임 경제 디자이너는 이 두 가지 요소의 균형을 맞추어 인플레이션을 억제하고 재화의 가치를 보존하며, 나아가 플레이어의 참여와 수익화 기회를 창출해야 한다 [1, 2].
|
||||
### 매 Tap (passive)
|
||||
- 매 stream 의 element 를 매 side-effect 로 관찰.
|
||||
- 매 element 자체는 매 unchanged 통과.
|
||||
- 매 logging, debugging, metrics, audit trail 의 사용.
|
||||
- Effect 의 `Effect.tap`, RxJS 의 `tap()`, Java Stream 의 `peek()`.
|
||||
|
||||
## 📖 구조화된 지식 (Synthesized Content)
|
||||
* **자원의 생성: 수도꼭지(Taps/Faucets)**
|
||||
수도꼭지는 가상 세계 내에서 재화가 생성되는 시스템으로, 플레이어의 사냥이나 퀘스트 완료 등 핵심 루프(Core Loop)와 연관된 '능동적 수도꼭지'와 오프라인 상태에서도 시간의 흐름에 따라 재화를 생성하는 '수동적 수도꼭지'로 구분됩니다 [1]. 현실 세계의 자원과 달리 게임 내 수도꼭지는 이론적으로 무한한 재화를 생성할 수 있으므로, 적절한 통제 없이 유입량이 증가하면 통화 가치가 급락하여 경제 붕괴를 초래할 위험이 있습니다 [1, 4].
|
||||
### 매 Sink (terminal)
|
||||
- 매 stream 을 매 single value (or unit) 로 reduce.
|
||||
- 매 stream 종결 시점 — 매 downstream 더 X.
|
||||
- Reduction (sum, count, list), I/O write, DB insert 의 형태.
|
||||
- Effect 의 `Sink<A, R, E>`, Akka Streams 의 `Sink[T, M]`.
|
||||
|
||||
* **자원의 소멸: 배수구(Sinks)**
|
||||
배수구는 플레이어가 획득한 재화를 소비하게 하여 시스템에서 영구적으로 소멸시키는 역할을 합니다 [1, 2]. 이는 크게 두 가지로 나뉩니다 [1].
|
||||
* **소프트 싱크(Soft Sinks):** 플레이어 간 거래나 경매장 물품 구매처럼 재화가 시스템 밖으로 사라지지 않고 이동만 하는 형태로, 전체 통화량에는 변화가 없어 인플레이션 억제 효과가 낮습니다 [1].
|
||||
* **하드 싱크(Hard Sinks):** NPC 상점 구매, 장비 수리비, 경매장 수수료 등 재화가 영구적으로 소멸하는 형태로, 통화량을 직접적으로 줄여 인플레이션을 제어하고 재화 가치를 방어합니다 [1].
|
||||
### 매 응용
|
||||
1. Observability — 매 production stream 에 매 tap 으로 metrics 주입.
|
||||
2. Data pipeline 종착점 — 매 sink 로 file/DB write.
|
||||
3. Test instrumentation — 매 tap 으로 intermediate state 검증.
|
||||
4. Backpressure 의 demand source — 매 sink 가 매 pull rate 결정.
|
||||
|
||||
* **수도꼭지와 배수구의 균형 및 스케일링**
|
||||
게임 경제 디자이너는 수도꼭지가 배수구를 흥미롭게 유지할 만큼 충분한 자원을 제공하되, 인앱 결제의 필요성을 떨어뜨릴 정도의 잉여 자원을 주지 않도록 핀치 포인트(Pinch Point)를 잘 관리해야 합니다 [3]. 특히 플레이어의 자산 규모가 커지면 고정된 가격의 배수구는 더 이상 유의미한 역할을 하지 못하므로, 퍼센트(%) 기반의 경매장 수수료나 자산 가치에 연동된 수리비처럼 하드 싱크가 플레이어의 자산에 비례하여 확장(Scaling)되도록 설계해야 합니다 [1].
|
||||
## 💻 패턴
|
||||
|
||||
* **점진적 메커니즘(Incremental Mechanics)을 통한 인플레이션 방어**
|
||||
자원 획득량(수도꼭지)과 업그레이드 비용(싱크)이 함께 비례하여 증가하는 점진적 메커니즘을 도입하면 인플레이션을 효과적으로 상쇄할 수 있습니다 [5]. 예를 들어, 더 많은 자원을 캐는 도구를 얻기 위해 점점 더 큰 비용을 지불하게 함으로써, 게임 내로 유입되는 통화가 많아지더라도 배수구의 규모가 함께 커져 경제적 균형을 맞춥니다 [6, 7].
|
||||
### Pattern 1 — Effect.tap
|
||||
```typescript
|
||||
import { Effect, Console } from 'effect';
|
||||
|
||||
* **경제 불균형의 위험성 사례**
|
||||
수도꼭지를 통한 자원 유입이 과도하고 배수구가 부족하면 화폐 가치가 폭락하여, 과거 '디아블로 2'나 '애셔론즈 콜'처럼 플레이어들이 골드를 버리고 특정 아이템을 통한 물물교환 경제를 형성하게 됩니다 [8, 9]. 반대로 '뉴 월드(New World)'의 초기 사례처럼 고레벨 구간에서 수도꼭지(재화 공급)는 줄어드는데 세금이나 수리비 같은 배수구가 너무 공격적으로 설정되면, 플레이어들이 지출을 극도로 꺼리는 유동성 위기가 발생합니다 [1].
|
||||
|
||||
---
|
||||
|
||||
* **자원의 생성: 탭 또는 수도꼭지 (Faucets/Taps)**
|
||||
* 가상 세계 내에서 재화가 무(無)에서 생성되어 유입되는 지점이다 [3].
|
||||
* 사냥, 퀘스트 완료, 자원 채굴 등 플레이어의 핵심 루프 활동에 의존하는 '능동적 수도꼭지'와 은행 이자나 시간당 생산 기지처럼 오프라인 상태에서도 재화를 생성하는 '수동적 수도꼭지'로 구분된다 [3].
|
||||
* 이론적으로 코드를 통해 무한히 생성될 수 있기 때문에, 유입되는 재화량이 통제 없이 증가할 경우 화폐 가치가 급격히 하락하여 경제 붕괴(인플레이션)를 초래할 위험이 있다 [3].
|
||||
|
||||
* **자원의 소멸: 싱크 또는 배수구 (Sinks)**
|
||||
* 게임 내에 유통되는 재화를 시스템에서 영구적으로 삭제하거나 회수하여 소비하게 만드는 장치이다 [4].
|
||||
* **소프트 싱크(Soft Sinks):** 플레이어 간의 개인 거래나 경매장 물품 구매 대금처럼 재화가 시스템 밖으로 사라지지 않고 이동만 하는 형태로, 전체 통화량에는 변화가 없어 인플레이션 억제 효과가 낮다 [4].
|
||||
* **하드 싱크(Hard Sinks):** NPC 상점 구매, 장비 수리비, 경매장 수수료, 제작 실패 시 소모되는 재료처럼 재화를 영구적으로 소멸시켜 통화량을 직접적으로 줄이고 가치를 방어하는 형태이다 [4]. 잉여 자금을 소비할 수 있는 수리비, 고급 지역 입장료, 희귀 아이템 등은 하드 싱크의 좋은 예시이다 [5].
|
||||
* 효과적인 경제 관리를 위해서는 고정된 가격이 아닌, 플레이어의 자산 규모에 비례하여 확장되는 백분율 기반의 싱크(예: 가치 연동형 수리비, 5~15%의 경매장 수수료)를 사용하여 경제 수명 주기 전반에 걸쳐 효과를 유지해야 한다 [4].
|
||||
|
||||
* **탭과 싱크의 균형 및 핀치 포인트 (Balance and Pinch Point)**
|
||||
* 탭은 플레이어가 게임을 진행하는 데 충분한 자원을 제공해야 하지만, 잉여 자원이 넘쳐 인앱 결제(IAP)의 필요성을 느끼지 못하게 할 정도로 많이 제공해서는 안 된다 [2].
|
||||
* 이를 통해 자원 수요가 극대화되는 지점인 '핀치 포인트(Pinch Point)'를 형성해야 한다 [6]. 탭에서 공급되는 자원이 싱크를 흥미롭게 유지하면서도 잉여를 남기지 않아야 플레이어는 결제 욕구를 느끼게 된다 [6].
|
||||
|
||||
* **인플레이션 억제를 위한 탭과 싱크 활용 전략**
|
||||
* **점진적 메커니즘(Incremental Mechanics):** 탭과 싱크를 비례적으로 확장하는 방식이다 [7]. 예를 들어, 플레이어가 더 많은 자원을 캐는 도구(탭 확장)를 얻으려면 점점 더 큰 비용(새로운 싱크)을 지불하도록 설계하여 인플레이션을 상쇄한다 [7, 8].
|
||||
* **과세(Taxation) 및 회수:** PvP 베팅, 경매장 수수료, 부활 비용 등에 소액의 세금을 부과하여 수많은 플레이어로부터 지속적으로 통화를 회수한다 [9].
|
||||
* **프리미엄 통화 브릿지:** 인게임 통화로 살 수 있는 프리미엄 통화(예: WoW 토큰, PLEX)를 도입하여 잉여 자원을 보유한 플레이어의 통화를 대량으로 싱크(회수) 시켜 인플레이션을 방어할 수 있다 [10].
|
||||
|
||||
## ⚠️ 모순 및 업데이트 (Contradictions & Updates)
|
||||
No trade-offs available.
|
||||
|
||||
## 🔗 지식 연결 (Graph)
|
||||
- **Related Topics:** [[인플레이션(Inflation)|인플레이션(Inflation]], 하드 싱크와 소프트 싱크(Hard Sinks and Soft Sinks), 점진적 메커니즘(Incremental Mechanics
|
||||
- **Projects/Contexts:** [[알비온 온라인(Albion Online)|알비온 온라인(Albion Online]], 이브 온라인(EVE Online), [[뉴 월드(New World)|뉴 월드(New World]]
|
||||
- **Contradictions/Notes:** 고정된 수치나 가격으로 설정된 배수구는 경제 초반에는 유효할 수 있으나, 시간이 지나 플레이어의 자산이 축적되면 인플레이션을 억제하는 기능을 상실합니다. 따라서 경제 설계 시 시장의 공급량이나 플레이어의 자산에 따라 수수료나 가격이 유동적으로 변하는 동적이고 자동화된 평형 장치를 도입하는 것이 필수적입니다 [1].
|
||||
|
||||
---
|
||||
*Last updated: 2026-04-28*
|
||||
|
||||
---
|
||||
|
||||
- **Related Topics:** [[게임 경제 인플레이션(Game Economy Inflation)|게임 경제 인플레이션(Game Economy Inflation)]], [[핀치 포인트(Pinch Point)|핀치 포인트(Pinch Point)]], 하드 싱크와 소프트 싱크(Hard Sinks and Soft Sinks), 인앱 결제(In-App Purchases, IAP)
|
||||
- **Projects/Contexts:** 알비온 온라인(Albion Online)과 EVE 온라인의 경제 시스템, 뉴 월드(New World)의 유동성 위기 사례
|
||||
- **Contradictions/Notes:** 탭을 통한 재화 공급이 통제되지 않으면 화폐 가치가 폭락하는 하이퍼인플레이션이 발생하지만, 반대로 뉴 월드(New World)의 사례처럼 초기 고레벨 구간에서 탭(재화 공급원)은 줄어드는데 싱크(주택 세금, 수리비 등)가 너무 공격적으로 설정되면 플레이어들이 지출을 극도로 꺼리는 유동성 함정(위기)에 빠질 수 있으므로 정교한 균형 조절이 필수적이다 [11].
|
||||
|
||||
---
|
||||
*Last updated: 2026-04-29*
|
||||
|
||||
## 🤖 LLM 활용 힌트 (How to Use This Knowledge)
|
||||
|
||||
**언제 이 지식을 쓰는가:**
|
||||
- *(TODO)*
|
||||
|
||||
**언제 쓰면 안 되는가:**
|
||||
- *(TODO)*
|
||||
|
||||
## 🧪 검증 상태 (Validation)
|
||||
|
||||
- **정보 상태:** needs_review
|
||||
- **출처 신뢰도:** A
|
||||
- **검토 이유:** *(P-Reinforce Phase 1 자동 정규화. 본문 검증 필요.)*
|
||||
|
||||
## 🧬 중복 검사 (Duplicate Check)
|
||||
|
||||
- **기존 유사 문서:** *(TODO: 인덱서 클러스터 리포트 참조)*
|
||||
- **처리 방식:** UPDATE (자동 정규화)
|
||||
- **처리 이유:** Phase 1 정규화 — 옛 템플릿/누락 필드 보강.
|
||||
|
||||
## 🕓 변경 이력 (Changelog)
|
||||
|
||||
| 날짜 | 변경 내용 | 처리 방식 | 신뢰도 |
|
||||
|------|-----------|-----------|--------|
|
||||
| 2026-05-08 | P-Reinforce Phase 1 정규화 (frontmatter + 헤더 표준화) | UPDATE | A |
|
||||
|
||||
## 💻 코드 패턴 (Code Patterns)
|
||||
|
||||
**패턴 1:** *(TODO: 이 프로젝트 컨벤션 반영한 구조 스켈레톤)*
|
||||
|
||||
```text
|
||||
# TODO
|
||||
const program = Effect.succeed(42).pipe(
|
||||
Effect.tap((n) => Console.log(`got ${n}`)),
|
||||
Effect.map((n) => n * 2),
|
||||
);
|
||||
// 매 log 출력 + 매 84 반환. 매 tap 은 value 변형 X.
|
||||
```
|
||||
|
||||
## 🤔 의사결정 기준 (Decision Criteria)
|
||||
### Pattern 2 — RxJS tap (debugging)
|
||||
```typescript
|
||||
import { from } from 'rxjs';
|
||||
import { tap, map, filter } from 'rxjs/operators';
|
||||
|
||||
**선택 A를 써야 할 때:**
|
||||
- *(TODO)*
|
||||
from([1, 2, 3, 4]).pipe(
|
||||
tap((v) => console.log(`before filter: ${v}`)),
|
||||
filter((v) => v % 2 === 0),
|
||||
tap((v) => console.log(`after filter: ${v}`)),
|
||||
map((v) => v * 10),
|
||||
).subscribe(console.log);
|
||||
// 매 pipeline 의 각 stage 에 매 invasive 한 logging.
|
||||
```
|
||||
|
||||
**선택 B를 써야 할 때:**
|
||||
- *(TODO)*
|
||||
### Pattern 3 — Effect Sink (collect)
|
||||
```typescript
|
||||
import { Stream, Sink, Effect } from 'effect';
|
||||
|
||||
**기본값:**
|
||||
> *(TODO)*
|
||||
const result = Stream.range(1, 100).pipe(
|
||||
Stream.run(Sink.sum),
|
||||
);
|
||||
// Effect<number> — 매 5050.
|
||||
```
|
||||
|
||||
## ❌ 안티패턴 (Anti-Patterns)
|
||||
### Pattern 4 — Custom Sink (foldLeft)
|
||||
```typescript
|
||||
const collectSink = Sink.foldLeft<number[], number>(
|
||||
[],
|
||||
(acc, n) => [...acc, n * 2],
|
||||
);
|
||||
|
||||
- **[안티패턴]:** *(TODO: 무엇을 하면 안 되는가 + 이유 + 대신 무엇을)*
|
||||
const collected = Stream.range(1, 5).pipe(
|
||||
Stream.run(collectSink),
|
||||
);
|
||||
// Effect<[2, 4, 6, 8, 10]>.
|
||||
```
|
||||
|
||||
### Pattern 5 — Tap for metrics
|
||||
```typescript
|
||||
import { Metric } from 'effect';
|
||||
|
||||
const requestCounter = Metric.counter('http_requests');
|
||||
|
||||
const handler = (req: Request) =>
|
||||
Effect.succeed(req).pipe(
|
||||
Effect.tap(() => Metric.increment(requestCounter)),
|
||||
Effect.flatMap(processRequest),
|
||||
);
|
||||
// 매 모든 request 에 매 counter ++. 매 main flow 의 변형 X.
|
||||
```
|
||||
|
||||
### Pattern 6 — Sink to file (Node)
|
||||
```typescript
|
||||
import { createWriteStream } from 'node:fs';
|
||||
import { Stream, Sink } from 'effect';
|
||||
|
||||
const fileSink = Sink.fromWritable(() => createWriteStream('out.txt'));
|
||||
|
||||
await Stream.range(1, 1000).pipe(
|
||||
Stream.map((n) => `line ${n}\n`),
|
||||
Stream.run(fileSink),
|
||||
Effect.runPromise,
|
||||
);
|
||||
// 매 1000 lines 의 file 에 sink.
|
||||
```
|
||||
|
||||
### Pattern 7 — tapError (failure observation)
|
||||
```typescript
|
||||
const fetchUser = (id: string) =>
|
||||
Effect.tryPromise(() => fetch(`/users/${id}`)).pipe(
|
||||
Effect.tapError((err) =>
|
||||
Console.error(`fetch failed: ${err}`),
|
||||
),
|
||||
);
|
||||
// 매 error path 만 관찰 — 매 success 는 매 untouched.
|
||||
```
|
||||
|
||||
### Pattern 8 — Sink composition
|
||||
```typescript
|
||||
const dualSink = Sink.zip(Sink.sum, Sink.count);
|
||||
|
||||
const [total, count] = await Stream.range(1, 10).pipe(
|
||||
Stream.run(dualSink),
|
||||
Effect.runPromise,
|
||||
);
|
||||
// 매 single pass 의 sum + count.
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Tap or Sink |
|
||||
|---|---|
|
||||
| 값 변형 없이 관찰 | **Tap** |
|
||||
| Stream 종결 + 결과 collect | **Sink** |
|
||||
| 매 logging / metrics | Tap |
|
||||
| 매 file / DB write | Sink |
|
||||
| Error path 만 관찰 | tapError |
|
||||
| 다중 결과 동시 collect | Sink.zip |
|
||||
|
||||
**기본값**: 매 observation = tap, 매 termination = sink. 매 tap 의 안에서 매 mutation 금지.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Reactive Streams]] · [[Effect 시스템]]
|
||||
- 변형: [[Sink (Effect)]] · [[Akka Streams Sink]] · [[RxJS tap]]
|
||||
- 응용: [[Observability Pipeline]] · [[ETL Sink]] · [[Backpressure]]
|
||||
- Adjacent: [[Pure vs Side-Effect]] · [[Stream Fusion]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 stream pipeline 의 instrumentation, terminal consumer 설계.
|
||||
**언제 X**: 매 tap 안에서 value mutate (매 functional contract 위반). 매 sink 를 매 중간 transformation 으로 오용.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Tap 안 mutation**: 매 element field 의 변경 — 매 pipeline 의 referential transparency 손상.
|
||||
- **Sink 누락**: 매 stream 정의만 하고 매 run 안 함 — Effect 의 매 lazy 의 의해 매 nothing happens.
|
||||
- **다중 sink 의 sequential 처리**: 매 single pass 가능한데 매 stream 을 매 두번 traverse.
|
||||
- **Tap 에 heavy I/O**: 매 main flow blocking — 매 fork 또는 async sink 로 분리.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Effect docs 2026, RxJS 8, Akka Streams).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — Tap/Sink 의 distinction + 8 patterns |
|
||||
|
||||
Reference in New Issue
Block a user