Files
2nd/10_Wiki/Topics/Coding/Quality_Refactoring.md
T
2026-05-09 21:08:02 +09:00

443 lines
9.3 KiB
Markdown

---
id: quality-refactoring
title: Refactoring — 작은 step / 안전 변경
category: Coding
status: draft
source_trust_level: B
verification_status: conceptual
created_at: 2026-05-09
updated_at: 2026-05-09
tags: [quality, refactoring, vibe-coding]
tech_stack: { language: "Various", applicable_to: ["Engineering"] }
applied_in: []
aliases: [refactoring, extract function, rename, move, code smell, inline, parallel change]
---
# Refactoring
> **작은 reversible step**. 매 step 후 test. Behavior 변경 X — 단지 structure. Martin Fowler 의 카탈로그 + IDE refactoring tools.
## 📖 핵심 개념
- Code smell: 변경 신호.
- Extract / Inline / Move / Rename.
- Test first — 안전망.
- Parallel change — 큰 변경 점진.
## 💻 코드 패턴
### 가장 일반 refactoring
```
1. Extract function/method: 긴 function → 작은 function
2. Inline: 과도한 indirection 제거
3. Rename: 의미 명확
4. Move: 적절 위치
5. Extract variable: 복잡 expression 명명
6. Replace conditional with polymorphism
7. Replace magic number with constant
8. Encapsulate field
```
### Extract function (가장 자주)
```ts
// Before
function processOrder(order: Order) {
// Calculate tax (10 lines)
let tax = 0;
for (const item of order.items) {
tax += item.price * 0.08;
}
// Calculate shipping (15 lines)
let shipping = 0;
if (order.weight > 10) shipping = 20;
else if (order.weight > 5) shipping = 10;
else shipping = 5;
// Apply discount (8 lines)
// ...
return order.subtotal + tax + shipping - discount;
}
// After
function processOrder(order: Order) {
const tax = calculateTax(order.items);
const shipping = calculateShipping(order.weight);
const discount = applyDiscount(order);
return order.subtotal + tax + shipping - discount;
}
function calculateTax(items: Item[]): number {
return items.reduce((sum, i) => sum + i.price * 0.08, 0);
}
function calculateShipping(weight: number): number {
if (weight > 10) return 20;
if (weight > 5) return 10;
return 5;
}
```
→ 의미 명명 + test 가능.
### Replace conditional with polymorphism
```ts
// Before
class Animal {
sound(): string {
if (this.type === 'dog') return 'Woof';
if (this.type === 'cat') return 'Meow';
if (this.type === 'cow') return 'Moo';
return '';
}
}
// After
abstract class Animal {
abstract sound(): string;
}
class Dog extends Animal { sound() { return 'Woof'; } }
class Cat extends Animal { sound() { return 'Meow'; } }
class Cow extends Animal { sound() { return 'Moo'; } }
```
또는 lookup table:
```ts
const sounds: Record<AnimalType, string> = {
dog: 'Woof', cat: 'Meow', cow: 'Moo',
};
function sound(type: AnimalType): string {
return sounds[type];
}
```
### Parallel change (큰 변경)
```
3 phase:
1. Expand: New 가 추가됨 (old 도 keep)
2. Migrate: 사용자가 new 로 이동
3. Contract: Old 제거
각 phase = deployable.
```
```ts
// Phase 1: Add new method
class API {
oldMethod(x: number) { return x * 2; }
newMethod(x: number, y: number) { return x * 2 + y; }
}
// Phase 2: Migrate callers (PR by PR)
api.oldMethod(5) api.newMethod(5, 0)
// Phase 3: Remove old method
class API {
newMethod(x: number, y: number) { ... }
}
```
→ Big bang 보다 안전.
### Strangler fig
```
큰 system 의 일부 점진 교체:
Phase 1: Old system 그대로.
Phase 2: New system + facade — 일부 traffic.
Phase 3: 점진 traffic 이동.
Phase 4: Old system 제거.
```
```ts
// Facade
class OrderService {
async create(order: Order) {
if (featureFlag('use-new-system')) {
return newSystem.create(order);
}
return oldSystem.create(order);
}
}
```
### Code smells (refactor 신호)
```
1. Long method (50+ lines): extract function
2. Large class (500+ lines): extract / split
3. Long parameter list (5+): parameter object
4. Duplicate code: extract + reuse
5. Switch statements: polymorphism / lookup
6. Feature envy (다른 class 의 데이터): move method
7. Data clumps (같은 그룹 매번): data class
8. Primitive obsession: value object
9. Comments explaining what: extract function (이름 붙임)
10. Dead code: delete
```
### Test first
```ts
// 1. Test 작성 (현재 behavior)
test('processOrder calculates correctly', () => {
const order = makeOrder(...);
expect(processOrder(order)).toBe(95.20);
});
// 2. Refactor
function processOrder(order: Order) {
const tax = calculateTax(order.items);
// ...
}
// 3. Test 통과 = behavior 같음
```
### IDE refactoring (안전)
```
VS Code / WebStorm:
- F2: Rename (모든 reference)
- Ctrl+Shift+R: Extract method
- Ctrl+Alt+M: Extract variable
- Move file (drag)
- Auto-import organize
→ Manual 보다 안전 + 빠름.
```
### TS 의 점진 typing
```ts
// Phase 1: any 만
function process(input: any): any { ... }
// Phase 2: 입력 type
function process(input: Order): any { ... }
// Phase 3: 출력 type
function process(input: Order): Result { ... }
// Phase 4: 내부 변수 type
```
→ 점진 strict.
### Inline (과도 indirection 제거)
```ts
// Before
function getEmail(user: User): string {
return user.email;
}
const email = getEmail(user); // 의미 없는 wrapping
// After
const email = user.email;
```
### Rename (semantic)
```ts
// Before
function calc(x: number, y: number) { ... }
// After
function calculateTotalPrice(quantity: number, unitPrice: number) { ... }
```
→ 의미 명확.
### Extract variable
```ts
// Before
if (order.items.length > 10 && order.totalAmount > 1000 && order.user.tier === 'gold') {
// ...
}
// After
const isLargeOrder = order.items.length > 10;
const isHighValue = order.totalAmount > 1000;
const isVipUser = order.user.tier === 'gold';
if (isLargeOrder && isHighValue && isVipUser) {
// ...
}
```
### Encapsulate field
```ts
// Before
class User {
email: string; // 누가나 변경 가능
}
user.email = '...'; // validation 없음
// After
class User {
#email: string;
get email(): string { return this.#email; }
setEmail(value: string) {
if (!isValid(value)) throw new Error('invalid');
this.#email = value;
}
}
```
### Replace magic number
```ts
// Before
if (statusCode === 429) { ... }
// After
const TOO_MANY_REQUESTS = 429;
if (statusCode === TOO_MANY_REQUESTS) { ... }
// 또는
import { StatusCode } from './http-status';
if (statusCode === StatusCode.TooManyRequests) { ... }
```
### Move method to better class
```ts
// Before — Order class 가 customer 데이터 사용
class Order {
getCustomerDiscount() {
return this.customer.tier === 'gold' ? 0.1 : 0;
}
}
// After — Customer class 안
class Customer {
getDiscount() {
return this.tier === 'gold' ? 0.1 : 0;
}
}
```
→ Feature envy 제거.
### Replace inheritance with composition
```ts
// Before
class Reader extends FileSystem { ... }
// After
class Reader {
constructor(private fs: FileSystem) { ... }
}
```
### Simplifying conditionals
```ts
// Before
if (x) {
return true;
} else {
return false;
}
// After
return Boolean(x);
// Before
if (x === null || x === undefined) return defaultVal;
return x;
// After
return x ?? defaultVal;
```
### Refactor in sprint
```
Friday: refactor day.
또는 매 PR 의 작은 cleanup.
큰 refactor = 별 sprint 또는 분기.
```
### Continuous refactoring
```
Boy scout rule + 매 PR + IDE 도구 = 코드 점차 개선.
Big-bang refactor 거의 안 함.
```
### When NOT to refactor
```
- 작동하는 코드 + 안 변경할 영역
- Critical path + test 없음
- Deadline 임박
- 이해 안 됐을 때 (먼저 이해)
- 명확 owner X
```
→ Refactor = 변경 시 우선.
### Communication
```
PR description:
"This PR is pure refactor — no behavior change.
Reduces complexity score from 25 → 12."
→ Reviewer 가 변경 검사.
```
### Test gap (refactor 전)
```
Test 없으면:
1. 작은 black-box test (입력/출력)
2. Refactor
3. Test 가 behavior lock
```
### Tools
```
- VS Code / WebStorm refactoring
- ESLint --fix
- ts-prune (unused exports)
- knip (unused files / deps)
- prettier (style — refactor 의 friend)
- AI assist (Cursor, Copilot)
```
### Refactor categories (Fowler)
```
1. Composing methods
2. Moving features between objects
3. Organizing data
4. Simplifying conditional expressions
5. Making method calls simpler
6. Dealing with generalization
7. Big refactorings
```
→ refactoring.com 무료.
## 🤔 의사결정 기준
| 상황 | 추천 |
|---|---|
| 매 PR | Boy scout (작은 cleanup) |
| Code review 발견 | 즉시 또는 follow-up issue |
| Hot spot (자주 변경) | 우선 refactor |
| 큰 architecture | Strangler fig + parallel change |
| Library version | Phased migration |
| Critical path | Test 먼저 |
## ❌ 안티패턴
- **Big bang refactor**: 깨짐 위험.
- **Test 없는 refactor**: behavior 변경 모름.
- **Refactor + new feature 같은 PR**: review 어려움.
- **Style only refactor**: lint / formatter 가 처리.
- **모든 코드 refactor 시도**: focus 잃음.
- **이해 못 한 코드 refactor**: bug 가능성.
- **Refactor 미공지 (대규모)**: surprise.
## 🤖 LLM 활용 힌트
- 작은 reversible step.
- IDE refactoring 도구 활용.
- Test first 안전.
- Parallel change / strangler 큰 변경.
## 🔗 관련 문서
- [[Quality_Tech_Debt]]
- [[Productivity_Code_Review]]
- [[Testing_Test_Pyramid]]