f8b21af4be
10_Wiki/Topics 대규모 정리: - 오류 캡처/미완성 stub 문서 227개 제거 - 교차폴더 중복 43클러스터 병합 (63파일 → redirect) - 링크명 정규화: 깨진 링크 수정·redirect 직결·개념 매핑 ~2,400건 - 카테고리 MOC 6개 신규 생성 - Graph 섹션 미해결 related-keyword 링크 10,058건 제거 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
236 lines
6.5 KiB
Markdown
236 lines
6.5 KiB
Markdown
---
|
|
id: wiki-2026-0508-dry-principle
|
|
title: DRY Principle (Don't Repeat Yourself)
|
|
category: 10_Wiki/Topics
|
|
status: verified
|
|
canonical_id: self
|
|
aliases: [DRY, Don't Repeat Yourself, single source of truth, WET, AHA]
|
|
duplicate_of: none
|
|
source_trust_level: A
|
|
confidence_score: 0.98
|
|
verification_status: applied
|
|
tags: [software-engineering, principles, dry, clean-code, abstraction, refactoring]
|
|
raw_sources: []
|
|
last_reinforced: 2026-05-10
|
|
github_commit: pending
|
|
tech_stack:
|
|
language: Universal
|
|
applicable_to: [Code, Schema, Config, Documentation]
|
|
---
|
|
|
|
# DRY Principle
|
|
|
|
## 매 한 줄
|
|
> **"매 every piece of knowledge 의 single, unambiguous, authoritative representation"**. Hunt & Thomas (Pragmatic Programmer). 매 duplication = 매 update bug. 매 modern caveat: 매 AHA (Avoid Hasty Abstraction) — 매 too-early DRY 의 wrong abstraction. 매 rule: 매 3rd repetition 의 abstract.
|
|
|
|
## 매 핵심
|
|
|
|
### 매 origin
|
|
- Hunt & Thomas, Pragmatic Programmer (1999).
|
|
- 매 "knowledge" 의 emphasize — 매 code line 의 X.
|
|
|
|
### 매 vs WET / AHA
|
|
- **WET**: Write Everything Twice / We Enjoy Typing.
|
|
- **AHA**: Avoid Hasty Abstraction (Sandi Metz).
|
|
- **Rule of 3**: 매 3 repetition 의 abstract.
|
|
- **Quote**: "Duplication is far cheaper than the wrong abstraction." — Sandi Metz.
|
|
|
|
### 매 type of duplication
|
|
- **Imposed**: 매 environment 의 force.
|
|
- **Inadvertent**: 매 unaware.
|
|
- **Impatient**: 매 deadline.
|
|
- **Interdeveloper**: 매 communication 의 X.
|
|
|
|
### 매 응용
|
|
1. **Constants**: 매 magic number 의 named.
|
|
2. **Functions**: 매 logic 의 extract.
|
|
3. **Templates / generics**: 매 type 의 abstract.
|
|
4. **Schema**: 매 single source.
|
|
5. **Config**: 매 env-specific override.
|
|
6. **Doc**: 매 generate from code.
|
|
|
|
### 매 modern context
|
|
- **Microservices**: 매 service 의 own data → 매 controlled duplication.
|
|
- **CQRS**: 매 read / write model 의 separate.
|
|
- **Type-driven**: 매 schema 의 source.
|
|
- **Codegen**: 매 OpenAPI → client / server.
|
|
- **LLM era**: 매 context-aware 의 inline 의 fine (token cost).
|
|
|
|
## 💻 패턴
|
|
|
|
### Extract function
|
|
```python
|
|
# 매 ❌ duplicated
|
|
def order_total(items):
|
|
total = 0
|
|
for i in items: total += i.price * i.qty
|
|
tax = total * 0.08
|
|
return total + tax
|
|
|
|
def cart_total(cart):
|
|
total = 0
|
|
for i in cart: total += i.price * i.qty
|
|
tax = total * 0.08
|
|
return total + tax
|
|
|
|
# 매 ✅ DRY
|
|
def calculate_total(items, tax_rate=0.08):
|
|
subtotal = sum(i.price * i.qty for i in items)
|
|
return subtotal * (1 + tax_rate)
|
|
```
|
|
|
|
### Single source of truth (schema)
|
|
```typescript
|
|
// 매 Zod schema → 매 type + 매 validation
|
|
import { z } from 'zod';
|
|
const UserSchema = z.object({ id: z.string(), email: z.string().email() });
|
|
type User = z.infer<typeof UserSchema>;
|
|
// 매 runtime validation + compile-time type
|
|
```
|
|
|
|
### Config (env override)
|
|
```typescript
|
|
const config = {
|
|
apiUrl: process.env.API_URL ?? 'http://localhost:3000',
|
|
timeout: Number(process.env.TIMEOUT ?? 5000),
|
|
};
|
|
// 매 single config object, env-specific override
|
|
```
|
|
|
|
### OpenAPI codegen
|
|
```yaml
|
|
# openapi.yaml — single source
|
|
paths:
|
|
/users/{id}: { get: { responses: { 200: { schema: { $ref: '#/User' } } } } }
|
|
|
|
# 매 → openapi-typescript / openapi-generator → client + server stubs + docs
|
|
```
|
|
|
|
### Database migration as code
|
|
```typescript
|
|
// 매 schema 의 ORM model 의 single source
|
|
@Entity()
|
|
class User {
|
|
@PrimaryColumn() id: string;
|
|
@Column({ unique: true }) email: string;
|
|
}
|
|
// 매 migration 의 generate from model
|
|
```
|
|
|
|
### Template (Mustache / Liquid)
|
|
```handlebars
|
|
{{!-- email-template.hbs --}}
|
|
Hi {{user.name}}, your order #{{order.id}} ships {{order.shipDate}}.
|
|
```
|
|
|
|
### Inheritance / mixin (cautious)
|
|
```python
|
|
class TimestampMixin:
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
class User(Base, TimestampMixin): pass
|
|
class Post(Base, TimestampMixin): pass
|
|
```
|
|
|
|
### Decorator (cross-cutting)
|
|
```python
|
|
def retry(times=3):
|
|
def decorator(func):
|
|
def wrapper(*args, **kwargs):
|
|
for attempt in range(times):
|
|
try: return func(*args, **kwargs)
|
|
except: if attempt == times - 1: raise
|
|
return wrapper
|
|
return decorator
|
|
|
|
@retry(times=3)
|
|
def fetch_data(url): ...
|
|
```
|
|
|
|
### AHA — when NOT to abstract
|
|
```python
|
|
# 매 ❌ premature abstraction
|
|
class GenericResourceHandler:
|
|
def __init__(self, kind, ...18 params...): ...
|
|
|
|
# 매 ✅ duplicate first, abstract later
|
|
def handle_user(req): ...
|
|
def handle_post(req): ...
|
|
# 매 → 매 3rd similar handler 의 의 emerge 의 pattern 의 see → 매 then abstract
|
|
```
|
|
|
|
### Detect duplication (PMD / jscpd)
|
|
```bash
|
|
# 매 jscpd
|
|
npx jscpd --threshold 5 --reporters html src/
|
|
# 매 reports clones >= 5 lines
|
|
```
|
|
|
|
### Refactor — Pull Up Method
|
|
```python
|
|
# 매 before
|
|
class Dog:
|
|
def name(self): return self._name
|
|
class Cat:
|
|
def name(self): return self._name
|
|
|
|
# 매 after
|
|
class Animal:
|
|
def name(self): return self._name
|
|
class Dog(Animal): pass
|
|
class Cat(Animal): pass
|
|
```
|
|
|
|
### Pragmatic limit (microservices)
|
|
```python
|
|
# 매 service A
|
|
class UserDTO_A: id: str; email: str
|
|
|
|
# 매 service B (separate codebase)
|
|
class UserDTO_B: id: str; email: str
|
|
|
|
# 매 ✅ controlled duplication — 매 services 의 independent deploy
|
|
# 매 ❌ shared library 의 coupling 의 anti-pattern
|
|
```
|
|
|
|
## 매 결정 기준
|
|
| 상황 | DRY Rule |
|
|
|---|---|
|
|
| 2nd occurrence | Wait |
|
|
| 3rd occurrence | Extract |
|
|
| Cross-service | Controlled duplicate |
|
|
| Schema | Single source + codegen |
|
|
| Config | Env-override |
|
|
| Wrong abstraction | Inline first |
|
|
|
|
**기본값**: 매 rule of 3 + 매 AHA (avoid hasty abstraction) + 매 wrong abstraction > duplication.
|
|
|
|
## 🔗 Graph
|
|
- 부모: [[Software-Engineering-Principles]] · [[Clean-Code]]
|
|
- 변형: [[AHA]] · [[Rule-of-Three]] · [[Single-Source-of-Truth]]
|
|
- 응용: [[Refactoring_Best_Practices|Refactoring]] · [[Codegen]]
|
|
- Adjacent: [[SOLID]] · [[YAGNI]] · [[KISS]] · [[Conventional-Commits]]
|
|
|
|
## 🤖 LLM 활용
|
|
**언제**: 매 refactor. 매 abstraction decision. 매 code review.
|
|
**언제 X**: 매 < 3 occurrence. 매 cross-domain.
|
|
|
|
## ❌ 안티패턴
|
|
- **Premature DRY**: 매 wrong abstraction → 매 update 의 cascade.
|
|
- **Inheritance abuse**: 매 IS-A 의 X 의 force.
|
|
- **Cross-service shared lib**: 매 deploy coupling.
|
|
- **DRY at all cost**: 매 readability < deduplication.
|
|
- **Magic abstraction**: 매 reader 의 confuse.
|
|
|
|
## 🧪 검증 / 중복
|
|
- Verified (Pragmatic Programmer, Sandi Metz).
|
|
- 신뢰도 A.
|
|
|
|
## 🕓 Changelog
|
|
| 날짜 | 변경 |
|
|
|---|---|
|
|
| 2026-04-20 | Auto-reinforced |
|
|
| 2026-05-08 | Phase 1 |
|
|
| 2026-05-10 | Manual cleanup — DRY + AHA + 매 extract / Zod / OpenAPI / detect / refactor code |
|