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

9.3 KiB

id, title, category, status, source_trust_level, verification_status, created_at, updated_at, tags, tech_stack, applied_in, aliases
id title category status source_trust_level verification_status created_at updated_at tags tech_stack applied_in aliases
quality-refactoring Refactoring — 작은 step / 안전 변경 Coding draft B conceptual 2026-05-09 2026-05-09
quality
refactoring
vibe-coding
language applicable_to
Various
Engineering
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 (가장 자주)

// 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

// 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:

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.
// 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 제거.
// 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

// 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

// 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 제거)

// Before
function getEmail(user: User): string {
  return user.email;
}
const email = getEmail(user);  // 의미 없는 wrapping

// After
const email = user.email;

Rename (semantic)

// Before
function calc(x: number, y: number) { ... }

// After
function calculateTotalPrice(quantity: number, unitPrice: number) { ... }

→ 의미 명확.

Extract variable

// 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

// 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

// 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

// 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

// Before
class Reader extends FileSystem { ... }

// After
class Reader {
  constructor(private fs: FileSystem) { ... }
}

Simplifying conditionals

// 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 큰 변경.

🔗 관련 문서