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>
4.5 KiB
4.5 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 |
|
null | C | 0.85 | conceptual | 2026-05-09 | 2026-05-09 | 2026-05-09 |
|
|
|
순수 함수 실전
부작용을 함수의 가장자리로 밀어내고, 핵심 로직은 입력 → 출력 매핑으로만 만든다. 이게 테스트와 리팩터링을 가장 싸게 만든다.
📖 핵심 개념
순수 함수의 두 조건:
- 같은 입력에는 항상 같은 출력 (참조 투명성)
- 외부 상태에 부작용을 주지 않음 (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;
}
이제 applyDiscount 와 markProcessed 는 모킹 없이 단위 테스트 가능. 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추가.