refactor(topics): 멀티 에이전트용 지식 재편 — _Common(공통 기본기) + Domain_* 구조
에이전트 8종(대화형/프로그래머 C·S/디자이너/설계자/기획자/QA/PD/PM)에게 [공통 기본 능력 + 롤별 Specialty] 2층으로 지식을 주입하기 위한 재분류. 문서 내용·포맷은 무수정, 폴더 이동만 (6,372개 문서 수 보존 확인). - Topic_Programming → Domain_Programming (내부 구조 보존) - Topic_Graphic → Domain_Design - Topic_Business → Domain_Product - Topic_General → Domain_General - _Common 신설: Math(구 Topic_Math_Specialty), Reasoning(구 General/From_Thinking & Reasoning), Reasoning_Creativity(구 General/From_창의성), Communication(Poetic_Blog_Writing + From_writing) - 타 도메인의 From_* 폴더는 유지 (출처 표기일 뿐, 이미 도메인에 맞게 분류된 문서) - 빈 폴더 정리 (memory/procedures) - 에이전트→폴더 매핑은 workspace의 .astra/agent-knowledge-map.json (9개 에이전트) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
---
|
||||
id: wiki-2026-0508-primitive-obsession-기본-타입-집착
|
||||
title: Primitive Obsession (기본 타입 집착)
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Primitive Obsession, 기본 타입 집착, stringly-typed]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [refactoring, code-smell, type-system, ddd]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: python-typescript-rust
|
||||
framework: refactoring
|
||||
---
|
||||
|
||||
# Primitive Obsession (기본 타입 집착)
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 string/int이 매 domain concept를 매 가장한다"**. Primitive Obsession은 매 `UserId`, `Email`, `Money` 같은 매 domain concept를 매 raw `str`/`int`로 매 표현해 매 type system이 매 invariant 보장 못 하는 매 code smell. Fowler "Refactoring" (1999, 2nd ed. 2018) 의 매 classic smell — 매 modern fix는 매 newtype / value object.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 증상
|
||||
- **매 String 폭주**: 매 `email: str`, `phone: str`, `country_code: str` 매 swap 가능.
|
||||
- **매 Magic numbers**: 매 `status: int = 3` 매 의미 불명.
|
||||
- **매 Validation duplication**: 매 every callsite마다 매 `if "@" in email`.
|
||||
- **매 Type confusion**: 매 `transfer(from_id, to_id)` 매 인자 swap 매 컴파일러 못 잡음.
|
||||
|
||||
### 매 Fix 전략
|
||||
- **매 Newtype**: Rust `struct UserId(u64);`.
|
||||
- **매 Value Object**: DDD 의 매 `Email`, `Money` immutable class.
|
||||
- **매 Branded type**: TypeScript `type UserId = string & { __brand: "UserId" }`.
|
||||
- **매 NewType pattern**: Python `typing.NewType("UserId", int)`.
|
||||
|
||||
### 매 응용
|
||||
1. Domain modeling (DDD) — Bounded context의 매 first-class concept.
|
||||
2. Money handling (currency + amount tied).
|
||||
3. Identifier safety (UserId vs OrderId mix-up 방지).
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Python NewType + dataclass
|
||||
```python
|
||||
from typing import NewType
|
||||
from dataclasses import dataclass
|
||||
from decimal import Decimal
|
||||
|
||||
UserId = NewType("UserId", int)
|
||||
OrderId = NewType("OrderId", int)
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Money:
|
||||
amount: Decimal
|
||||
currency: str
|
||||
def __post_init__(self):
|
||||
if self.amount < 0: raise ValueError("negative")
|
||||
if len(self.currency) != 3: raise ValueError("ISO-4217")
|
||||
|
||||
def transfer(src: UserId, dst: UserId, m: Money): ...
|
||||
# transfer(OrderId(1), UserId(2), Money(...)) # 매 mypy error
|
||||
```
|
||||
|
||||
### Rust newtype
|
||||
```rust
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct UserId(u64);
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct OrderId(u64);
|
||||
|
||||
fn fetch_user(id: UserId) { /* ... */ }
|
||||
// fetch_user(OrderId(7)); // 매 compile error
|
||||
```
|
||||
|
||||
### TypeScript branded types
|
||||
```typescript
|
||||
type Brand<T, B> = T & { readonly __brand: B };
|
||||
type Email = Brand<string, "Email">;
|
||||
type UserId = Brand<number, "UserId">;
|
||||
|
||||
function parseEmail(s: string): Email {
|
||||
if (!/^[^@]+@[^@]+$/.test(s)) throw new Error("invalid");
|
||||
return s as Email;
|
||||
}
|
||||
```
|
||||
|
||||
### Value object with invariant
|
||||
```python
|
||||
@dataclass(frozen=True)
|
||||
class Email:
|
||||
value: str
|
||||
def __post_init__(self):
|
||||
if "@" not in self.value: raise ValueError("invalid email")
|
||||
object.__setattr__(self, "value", self.value.lower())
|
||||
```
|
||||
|
||||
### Refactor: Replace Type Code with Subclass
|
||||
```python
|
||||
# Before — magic int status
|
||||
class Order:
|
||||
status: int # 0=pending, 1=paid, 2=shipped
|
||||
|
||||
# After — sealed states
|
||||
class OrderStatus: pass
|
||||
class Pending(OrderStatus): pass
|
||||
class Paid(OrderStatus): pass
|
||||
class Shipped(OrderStatus):
|
||||
tracking: str
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Domain identifier | Newtype |
|
||||
| Domain value (Money, Email) | Value Object |
|
||||
| 매 enum-like int code | Sealed subclass / Enum |
|
||||
| Throwaway script | 매 raw primitive OK |
|
||||
|
||||
**기본값**: Newtype for IDs, Value Object for domain values.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Code-Smell]] · [[Refactoring_Best_Practices|Refactoring]]
|
||||
- 변형: [[Value-Object]] · [[Branded-Types]]
|
||||
- 응용: [[DDD]] · [[Type-Safety]]
|
||||
- Adjacent: [[Stringly-Typed]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: domain model 설계, 매 API boundary type 선택, 매 refactoring 제안.
|
||||
**언제 X**: 매 1-time script.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **매 Stringly-typed everything**: 매 모든 domain concept를 매 str.
|
||||
- **매 Validation lazy**: 매 boundary 통과 후 매 raw str 그대로 흘림.
|
||||
- **매 Tuple as struct**: 매 `(int, str, bool)` 매 의미 모름.
|
||||
- **매 매 시점 validation**: 매 매번 caller가 매 validate 수행.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Fowler "Refactoring" 2nd ed. 2018, Evans "DDD" 2003).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — newtype/value-object patterns + refactoring guide |
|
||||
Reference in New Issue
Block a user