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

4.6 KiB

id, title, category, status, canonical_id, aliases, duplicate_of, source_trust_level, confidence_score, verification_status, created_at, updated_at, last_reinforced, review_reason, merge_history, tags, raw_sources, tech_stack, applied_in
id title category status canonical_id aliases duplicate_of source_trust_level confidence_score verification_status created_at updated_at last_reinforced review_reason merge_history tags raw_sources tech_stack applied_in
pure-functions-in-practice 순수 함수 실전 (Pure Functions in Practice) Coding draft pure-functions-in-practice
pure function
side-effect-free
referential transparency
참조 투명성
null C 0.85 conceptual 2026-05-09 2026-05-09 2026-05-09
coding
functional
testability
refactoring
vibe-coding
P-Reinforce session 2026-05-09 — bulk Coding seed batch 1
language applicable_to
TypeScript / JavaScript / Python
모든 도메인

순수 함수 실전

부작용을 함수의 가장자리로 밀어내고, 핵심 로직은 입력 → 출력 매핑으로만 만든다. 이게 테스트와 리팩터링을 가장 싸게 만든다.

📖 핵심 개념

순수 함수의 두 조건:

  1. 같은 입력에는 항상 같은 출력 (참조 투명성)
  2. 외부 상태에 부작용을 주지 않음 (no side effects)

실전에서는 "100% 순수" 보다 순수 영역과 비순수 영역의 경계를 명확히 그리는 것이 중요하다.

핵심 패턴: "불순한 껍데기, 순수한 알맹이(impure shell, pure core)".

  • 함수 가장자리 (HTTP, DB, 시간, 랜덤, 로그) → 불순
  • 핵심 비즈니스 로직 → 순수
  • 불순한 부분이 순수한 부분에 데이터를 넘겨주고, 결과를 다시 받아 부작용을 처리

💻 코드 패턴

나쁜 예 — 비즈니스 로직과 부작용이 섞임:

async function processOrder(orderId: string) {
  const order = await db.findOrder(orderId);
  if (order.total > 100) {
    order.discount = order.total * 0.1;
  }
  order.processedAt = new Date();
  await db.saveOrder(order);
  await sendEmail(order.customerId, '주문이 처리되었습니다');
  return order;
}

이 함수는 테스트하려면 DB 모킹 + 시간 모킹 + 이메일 모킹이 모두 필요.

Impure Shell + Pure Core:

// 순수 — 입출력만 있음
export function applyDiscount(order: Order): Order {
  if (order.total <= 100) return order;
  return { ...order, discount: order.total * 0.1 };
}

export function markProcessed(order: Order, now: Date): Order {
  return { ...order, processedAt: now };
}

// 불순한 껍데기 — 외부 시스템과 통신하는 곳
async function processOrder(orderId: string) {
  const order = await db.findOrder(orderId);
  const discounted = applyDiscount(order);
  const finalized = markProcessed(discounted, new Date());
  await db.saveOrder(finalized);
  await sendEmail(finalized.customerId, '주문이 처리되었습니다');
  return finalized;
}

이제 applyDiscountmarkProcessed 는 모킹 없이 단위 테스트 가능. processOrder 는 통합 테스트.

🤔 의사결정 기준

상황 순수 함수 강제 불순 허용
비즈니스 규칙 (할인, 검증, 변환)
외부 시스템 호출 (DB, HTTP, FS)
시간/랜덤 의존 (파라미터로 주입)
캐시/메모이제이션 적용 대상
동시성 안전 필요

안티패턴

  • 숨은 시간 의존: 함수 본문에 new Date() / Date.now() 직접 호출. 같은 입력에도 다른 결과 → 비순수. 시간을 파라미터로 주입.
  • 숨은 랜덤: Math.random() 직접 호출. 시드를 받거나 randomFn을 주입.
  • 공유 가변 상태 수정: globalCache.set(key, value) 같은 숨은 mutation. 함수 시그니처가 거짓말을 한다.
  • 순수 함수 안에서 console.log: 디버깅 흔적. 출력은 호출자 책임.
  • "semi-pure": input을 직접 mutate하면서 return하기. 호출자가 alias를 가지면 사고. 항상 새 객체 반환.

🤖 LLM 활용 힌트

  • LLM에게 비즈니스 로직 작성을 시킬 때 "시간 / 랜덤 / DB / HTTP 사용 금지, 필요하면 파라미터로 받아라" 라고 못 박기. 그러면 자연스럽게 순수 함수가 나옴.
  • 기존 함수 리팩터링: "부작용을 함수 양 끝으로 밀어내고, 가운데는 순수하게" 패턴 명시.
  • 테스트 작성: "모킹 없이 테스트할 수 있는 형태로 분리해줘" 라고 요청 → 자동으로 shell/core 분리.

🧪 검증 상태

  • verification_status: conceptual
  • functional core / imperative shell 패턴 (Gary Bernhardt) 의 변형.
  • 적용 사례 발견 시 applied_in 추가.

🔗 관련 문서