[G1-Sync] Manual knowledge update
This commit is contained in:
@@ -2,95 +2,127 @@
|
||||
id: wiki-2026-0508-3의-법칙-rule-of-three
|
||||
title: 3의 법칙 (Rule of Three)
|
||||
category: 10_Wiki/Topics
|
||||
status: needs_review
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: []
|
||||
aliases: [Rule of Three, WET, Three Strikes Refactor]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.92
|
||||
tags: [uncategorized]
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [refactoring, dry, code-smell, abstraction]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-08
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
inferred_by: Claude Opus 4.7 (auto-normalize 2026-05-08)
|
||||
tech_stack:
|
||||
language: unspecified
|
||||
framework: unspecified
|
||||
language: python
|
||||
framework: general
|
||||
---
|
||||
|
||||
# [[3의 법칙 (Rule of Three)]]
|
||||
# 3의 법칙 (Rule of Three)
|
||||
|
||||
## 📌 한 줄 통찰 (The Karpathy Summary)
|
||||
3의 법칙(Rule of Three)은 돈 로버츠(Don Roberts)가 제안하고 마틴 파울러(Martin Fowler)가 대중화한 소프트웨어 리팩토링의 핵심 경험 법칙(Rule of Thumb)이다 [1, 2]. 이 법칙은 유사한 형태의 코드가 세 번 반복해서 사용될 때 비로소 중복을 피하기 위해 리팩토링을 수행해야 한다고 명시한다 [1, 3]. 이는 무분별한 리팩토링으로 인한 오버 엔지니어링과 기술 부채 사이의 균형을 맞추고, 실용적인 설계 조정을 권장하는 가이드라인이다 [3].
|
||||
## 매 한 줄
|
||||
> **"매 두 번까지는 복사, 세 번째 등장하면 추출."** Don Roberts 가 *Refactoring* (Fowler, 1999) 에 정식화한 휴리스틱. 매 premature abstraction 을 피하면서 매 진짜 duplication 만 제거하는 trade-off rule. 2026 LLM 기반 codegen 시대에도 매 the cheapest signal — 매 abstraction 의 timing 을 결정.
|
||||
|
||||
## 📖 구조화된 지식 (Synthesized Content)
|
||||
* **3단계 진행 방식 (Three strikes and you refactor)**:
|
||||
1. 첫 번째로 무언가를 구현할 때는 단순히 기능을 완성하는 데 집중하여 일단 진행한다 [4-6].
|
||||
2. 두 번째로 비슷한 작업을 수행할 때는 코드 중복으로 인해 다소 꺼림칙하거나 불편함을 느끼더라도, 같은 방식으로 중복을 허용하여 코드를 작성한다 [2, 4, 6].
|
||||
3. 세 번째로 동일한 형태의 작업이 반복되는 시점이 오면, 그때 비로소 리팩토링을 시작하여 중복된 코드를 새로운 프로시저나 클래스로 추출 및 공통화한다 [1, 2, 4, 5].
|
||||
* **패턴 발견과 정확한 추상화 유도**: 단 두 번의 사례만으로는 코드의 공통점을 명확히 찾기 어려울 수 있다. 하지만 중복이 세 번 이상 발생할 경우, 공통점과 차이점의 패턴을 훨씬 쉽게 파악할 수 있어 더 정확하고 올바른 추상화 수준을 결정하는 데 도움이 된다 [2, 7, 8].
|
||||
* **경제성 관점**: 코드의 중복은 코드를 유지보수하기 어렵게 만들지만, 3의 법칙은 세 개의 복사본이 존재하게 될 때 발생하는 유지보수 비용이 리팩토링을 수행하는 비용 및 잠재적인 나쁜 설계의 위험성을 확실히 초과하게 된다는 것을 내포하고 있다 [1, 9].
|
||||
## 매 핵심
|
||||
|
||||
## ⚠️ 모순 및 업데이트 (Contradictions & Updates)
|
||||
* **성급한 추상화(Premature Refactoring)의 위험성**: 세 번의 중복이 나타나기 전에 너무 일찍 리팩토링을 시도하면 잘못된 추상화를 선택할 위험이 커진다 [2, 8]. 잘못된 추상화 모델이 시스템에 고착되면, 새로운 요구사항(유스케이스)을 처리하기 위해 부자연스러운 매개변수나 if 문 등을 억지로 추가해야 하므로 코드 유연성이 저하된다 [2, 10]. 결론적으로 잘못된 추상화보다 차라리 코드의 중복을 남겨두는 것이 유지보수 비용 측면에서 훨씬 저렴하다 [11].
|
||||
* **도그마화(Dogmatic)에 대한 경계**: 3의 법칙은 반드시 100% 지켜야 하는 엄격한 교리가 아니라 지침으로 활용해야 한다 [12, 13]. 세 번의 중복이 발견되었더라도 올바른 추상화 방법을 찾기 어렵거나 새로운 개념에 명확한 이름을 붙일 수 없다면, 억지로 추상화(over-abstraction)를 강요해서는 안 된다 [12]. 이런 경우에는 상황에 대한 더 많은 컨텍스트를 얻고 추가적인 중복 사례가 나타날 때까지 리팩토링을 유보하고 기다리는 것이 더 안전하다 [10].
|
||||
### 매 왜 "3"인가
|
||||
- **2회는 coincidence**: 매 same-shape 의 코드 두 개는 매 future 에 diverge 할 가능성이 높음. 매 abstraction 으로 묶으면 매 wrong abstraction lock-in.
|
||||
- **3회는 pattern**: 매 third occurrence 에서 매 invariant 가 무엇인지 보임. 매 axis of variation 이 명확해짐.
|
||||
- **AHA principle**: "Avoid Hasty Abstractions" (Sandi Metz). 매 duplication is far cheaper than the wrong abstraction.
|
||||
|
||||
### 매 vs DRY
|
||||
- DRY: "Every piece of knowledge must have a single, authoritative representation" — 매 *knowledge* 에 대한 rule.
|
||||
- Rule of 3: 매 *code shape* 에 대한 rule.
|
||||
- 매 두 코드가 same-shape 이지만 different-knowledge 일 수 있음 → 매 DRY 가 적용되지 않음.
|
||||
|
||||
---
|
||||
*Last updated: 2026-05-03*
|
||||
### 매 응용
|
||||
1. Helper function 추출 timing 결정.
|
||||
2. Component / Module 추출 timing 결정.
|
||||
3. Configuration parameter 도입 timing 결정.
|
||||
|
||||
## 🤖 LLM 활용 힌트 (How to Use This Knowledge)
|
||||
## 💻 패턴
|
||||
|
||||
**언제 이 지식을 쓰는가:**
|
||||
- *(TODO)*
|
||||
### 1) 2회까지는 inline (resist abstraction)
|
||||
```python
|
||||
# Occurrence 1
|
||||
user_email = data["user"]["email"].strip().lower()
|
||||
|
||||
**언제 쓰면 안 되는가:**
|
||||
- *(TODO)*
|
||||
|
||||
## 🧪 검증 상태 (Validation)
|
||||
|
||||
- **정보 상태:** needs_review
|
||||
- **출처 신뢰도:** A
|
||||
- **검토 이유:** *(P-Reinforce Phase 1 자동 정규화. 본문 검증 필요.)*
|
||||
|
||||
## 🧬 중복 검사 (Duplicate Check)
|
||||
|
||||
- **기존 유사 문서:** *(TODO: 인덱서 클러스터 리포트 참조)*
|
||||
- **처리 방식:** UPDATE (자동 정규화)
|
||||
- **처리 이유:** Phase 1 정규화 — 옛 템플릿/누락 필드 보강.
|
||||
|
||||
## 🔗 지식 연결 (Graph)
|
||||
|
||||
- **Parent:** [[10_Wiki/Topics]]
|
||||
- **Related:** *(TODO: 최소 2개)*
|
||||
- **Opposite / Trade-off:** *(TODO)*
|
||||
- **Raw Source:** 직접 입력
|
||||
|
||||
## 🕓 변경 이력 (Changelog)
|
||||
|
||||
| 날짜 | 변경 내용 | 처리 방식 | 신뢰도 |
|
||||
|------|-----------|-----------|--------|
|
||||
| 2026-05-08 | P-Reinforce Phase 1 정규화 (frontmatter + 헤더 표준화) | UPDATE | A |
|
||||
|
||||
## 💻 코드 패턴 (Code Patterns)
|
||||
|
||||
**패턴 1:** *(TODO: 이 프로젝트 컨벤션 반영한 구조 스켈레톤)*
|
||||
|
||||
```text
|
||||
# TODO
|
||||
# Occurrence 2 — DO NOT extract yet
|
||||
admin_email = data["admin"]["email"].strip().lower()
|
||||
```
|
||||
|
||||
## 🤔 의사결정 기준 (Decision Criteria)
|
||||
### 2) 3회째에 추출 — invariant 가 분명해진 시점
|
||||
```python
|
||||
def normalize_email(raw: str) -> str:
|
||||
return raw.strip().lower()
|
||||
|
||||
**선택 A를 써야 할 때:**
|
||||
- *(TODO)*
|
||||
user_email = normalize_email(data["user"]["email"])
|
||||
admin_email = normalize_email(data["admin"]["email"])
|
||||
support_email = normalize_email(data["support"]["email"]) # ← trigger
|
||||
```
|
||||
|
||||
**선택 B를 써야 할 때:**
|
||||
- *(TODO)*
|
||||
### 3) 매 wrong-abstraction 의 anti-pattern
|
||||
```python
|
||||
# 2회만에 추출했다가 3회째가 다른 shape 으로 등장
|
||||
def process_record(r, mode): # mode 가 점점 늘어남 — leaky abstraction
|
||||
if mode == "user": ...
|
||||
elif mode == "admin": ...
|
||||
elif mode == "audit": ... # 완전히 다른 logic
|
||||
```
|
||||
|
||||
**기본값:**
|
||||
> *(TODO)*
|
||||
### 4) 매 React component 의 Rule of 3
|
||||
```tsx
|
||||
// 2개 까지는 copy-paste
|
||||
<Card title="A"><p>...</p></Card>
|
||||
<Card title="B"><p>...</p></Card>
|
||||
// 3번째 → extract <UserCard> abstraction
|
||||
```
|
||||
|
||||
## ❌ 안티패턴 (Anti-Patterns)
|
||||
### 5) 매 LLM-assisted refactoring 의 trigger
|
||||
```python
|
||||
# Claude / Cursor 에게 "extract this duplication" 을 매 3회 occurrence 부터 요청.
|
||||
# 2회 occurrence 에서는 "leave inline" 을 명시.
|
||||
```
|
||||
|
||||
- **[안티패턴]:** *(TODO: 무엇을 하면 안 되는가 + 이유 + 대신 무엇을)*
|
||||
### 6) 매 test 에서의 예외
|
||||
```python
|
||||
# Test 코드는 Rule of 3 보다 readability 우선.
|
||||
# 매 fixture 추출은 매 2회에도 OK (DAMP > DRY in tests).
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| 매 same-shape 코드 2회 | Inline 유지 |
|
||||
| 매 same-shape + same-knowledge 3회 | Extract |
|
||||
| 매 same-shape + different-knowledge 3회 | Inline 유지 (DRY 위반 아님) |
|
||||
| Test fixture | 2회에도 추출 OK |
|
||||
| Cross-module duplication | 3회 + 매 stable interface 확인 후 |
|
||||
|
||||
**기본값**: 매 3회까지 wait, 매 4번째에 후회하지 않을 abstraction 만 추출.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Refactoring]] · [[DRY]]
|
||||
- 변형: [[AHA Principle]] · [[YAGNI]]
|
||||
- 응용: [[AI-Assisted Refactoring]] · [[Code Smell]]
|
||||
- Adjacent: [[Premature Abstraction]] · [[Wrong Abstraction]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 codegen 의 review 시 — "이거 2회만에 추출됐는데 3회 기다려야 하나?" 매 sanity check.
|
||||
**언제 X**: 매 test 코드 / 매 boilerplate / 매 framework-required pattern 에는 적용 X.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Premature abstraction**: 매 1-2회 occurrence 에서 추출 → 매 wrong axis 로 lock-in.
|
||||
- **Eternal copy-paste**: 매 5+ occurrence 인데도 추출 안 함 → 매 maintenance burden.
|
||||
- **Mode-flag explosion**: 매 추출한 함수에 매 boolean / enum flag 가 계속 늘어남 → 매 wrong abstraction signal.
|
||||
- **DRY-religious**: 매 "any duplication is sin" → 매 shape 만 같은 code 까지 강제 통합.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Fowler *Refactoring* 2nd ed. 2018, Sandi Metz "The Wrong Abstraction" 2016).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — Rule of 3 trade-off + AHA + LLM refactor trigger 정리 |
|
||||
|
||||
Reference in New Issue
Block a user