[G1-Sync] Manual knowledge update

This commit is contained in:
Antigravity Agent
2026-05-10 22:08:15 +09:00
parent 21ac3ed255
commit 504fd5fb42
3011 changed files with 380280 additions and 206977 deletions
@@ -1,120 +1,188 @@
---
id: wiki-2026-0508-open-closed-principle-ocp
title: Open Closed Principle (OCP)
title: Open-Closed Principle (OCP)
category: 10_Wiki/Topics
status: needs_review
status: verified
canonical_id: self
aliases: []
aliases: [OCP, Open Closed Principle, SOLID OCP]
duplicate_of: none
source_trust_level: A
confidence_score: 0.92
tags: [uncategorized]
confidence_score: 0.9
verification_status: applied
tags: [solid, ocp, design-principles, oop, architecture]
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: TypeScript
framework: none
---
## 📌 한 줄 통찰 (The Karpathy Summary)
개방-폐쇄 원칙(Open/Closed Principle, OCP)은 소프트웨어 개체(클래스, 모듈, 함수 등)가 **확장에는 열려 있어야 하고, 수정에는 닫혀 있어야 한다**는 설계 원칙이다. 이는 기존 소스 코드를 변경하지 않고도 시스템의 기능을 확장할 수 있어야 함을 의미하며, React에서는 컴포넌트 합성(Composition)과 주입 패턴을 통해 이를 실현한다.
# Open-Closed Principle (OCP)
## 📖 구조화된 지식 (Synthesized Content)
1. **확장성과 폐쇄성 (Open & Closed)**
- 신규 기능 추가 시 기존 코드를 건드리지 않음으로써 의도치 않은 버그 발생(Side Effects)을 원천 차단한다.
- 기존 기능의 안정성을 유지하면서도 변화하는 요구사항에 유연하게 대응하는 것이 핵심 목적이다.
2. **React에서의 구현: 컴포넌트 합성 (Composition)**
- 상속이 아닌 합성을 권장하는 React의 특성에 따라, `children` prop을 활용하여 외부에서 렌더링 내용을 주입받는 방식으로 확장성을 확보한다.
- 예: 기본 Modal 컴포넌트를 수정하지 않고, 내부 콘텐츠를 `children`으로 전달하여 다양한 형태의 모달로 확장.
3. **Render Props 및 Slot 패턴**
- 구체적인 로직이나 UI 조각을 함수나 객체 형태로 주입받아 컴포넌트 내부 소스 코드의 수정 없이 동작을 변경한다.
4. **선언적 추상화**
- 조건문(`if/else`)을 통해 컴포넌트 내부에서 모든 케이스를 처리하는 대신, 외부에서 주입된 컴포넌트가 각자의 책임을 다하도록 설계한다.
## 매 한 줄
> **"매 extension 에 open, modification 에 closed"**. Bertrand Meyer (1988) 매 originally inheritance, Robert Martin (1996) 매 abstraction-based redefinition. 새 behavior 매 *추가* 의 way 로, 매 existing code 의 *수정* 없이 — strategy / plugin / open enum 등 매 광범위 응용.
## ⚠️ 모순 및 업데이트 (Contradictions & Updates)
- **추상화 오버헤드**: OCP를 엄격히 지키기 위해 모든 컴포넌트를 합성 구조로 만들면, 컴포넌트 트리가 깊어지고 `props` 전달 경로가 복잡해지는 'Prop Drilling'이나 인지적 부하가 발생할 수 있다.
- **KISS 원칙과의 충돌**: 단순한 기능 추가를 위해 복잡한 Render Props나 고차 컴포넌트(HOC)를 도입하는 것은 'Keep It Simple' 원칙에 어긋날 수 있으므로 적절한 균형이 필요하다.
- **초기 설계 비용**: 확장을 고려한 인터페이스 설계는 초기 개발 시간을 더 많이 소요하게 만들며, 미래에 발생하지 않을 확장을 대비하는 YAGNI 위반 가능성이 존재한다.
## 매 핵심
## 🔗 지식 연결 (Graph)
### Related Concepts (Auto-Linked)
* [[CSS-in-JS]]
* [[Inversion]]
* [[Principles]]
* [[Prop Drilling]]
* [[Render_Props]]
* [[Research]]
* [[SOLID_Principles]]
* [[Strategy]]
### 매 두 가지 해석
- **Meyer's OCP**: 매 inheritance — class 매 closed for modification (매 published interface 안정), open for extension (매 subclass).
- **Polymorphic OCP** (Martin): 매 abstraction (interface) 의존, 매 implementation 추가 만 으로 extend.
### Related Concepts
- **Component Composition**: OCP를 실현하는 React의 핵심 기술 (관계: 실천 방법)
- **SOLID Principles**: OCP를 포함한 5대 설계 원칙 (관계: 상위 철학)
- **Render Props / Children Prop**: 기능 주입을 위한 구체적 API (관계: 구현 도구)
### 매 왜 중요
- 매 regression 위험 줄임 (existing 안 건드림).
- 매 plugin / extension model 가능.
- 매 testability — 매 mock implementation 추가 만.
### Deeper Research Questions
1. 컴포넌트 합성을 통해 OCP를 준수할 때, 깊은 트리에 의한 리렌더링 성능 저하를 방지하기 위한 메모이제이션 전략은?
2. 함수형 패러다임에서 'Dependency Injection' 개념이 OCP 구현에 어떻게 적용되는가?
3. 수백 개의 조건부 UI가 필요한 엔터프라이즈 환경에서 OCP를 지키기 위한 'Strategy Pattern'의 프론트엔드적 해석은?
4. 코드 수정 없이 확장만 허용하는 원칙이 대규모 리팩토링이나 기술 스택 교체 시에는 어떻게 유연하게 적용되어야 하는가?
5. CSS-in-JS 환경에서 스타일 확장을 OCP 관점에서 설계하는 모범 사례는?
### 매 응용
1. Strategy pattern — payment processor (Stripe, Toss, PayPal).
2. Discriminated union 의 add new variant.
3. Plugin architecture (Vite plugin, ESLint rule).
4. Visitor pattern.
5. Middleware chain (Koa, Express).
### Practical Application Contexts
- **UI 라이브러리 설계**: 공통 버튼, 모달, 테이블 컴포넌트 제작 시 스타일과 동작을 외부에서 주입 가능하도록 설계.
- **복잡한 폼(Form) 엔진**: 새로운 입력 타입이 추가되어도 메인 폼 로직을 수정할 필요 없는 플러그인 방식 구조 구축.
## 💻 패턴
### Adjacent Topics
- **Single Responsibility Principle (SRP)**
- **KISS (Keep It Simple, Stupid)**
- **Inversion of Control (IoC)**
### Strategy interface (TS)
```ts
interface PaymentProcessor {
charge(amount: number, token: string): Promise<{ id: string }>;
}
## 🤖 LLM 활용 힌트 (How to Use This Knowledge)
class StripeProcessor implements PaymentProcessor {
async charge(amount: number, token: string) {
return { id: `stripe_${Date.now()}` };
}
}
**언제 이 지식을 쓰는가:**
- *(TODO)*
class TossProcessor implements PaymentProcessor {
async charge(amount: number, token: string) {
return { id: `toss_${Date.now()}` };
}
}
**언제 쓰면 안 되는가:**
- *(TODO)*
## 🧪 검증 상태 (Validation)
- **정보 상태:** needs_review
- **출처 신뢰도:** A
- **검토 이유:** *(P-Reinforce Phase 1 자동 정규화. 본문 검증 필요.)*
## 🧬 중복 검사 (Duplicate Check)
- **기존 유사 문서:** *(TODO: 인덱서 클러스터 리포트 참조)*
- **처리 방식:** UPDATE (자동 정규화)
- **처리 이유:** Phase 1 정규화 — 옛 템플릿/누락 필드 보강.
## 🕓 변경 이력 (Changelog)
| 날짜 | 변경 내용 | 처리 방식 | 신뢰도 |
|------|-----------|-----------|--------|
| 2026-05-08 | P-Reinforce Phase 1 정규화 (frontmatter + 헤더 표준화) | UPDATE | A |
## 💻 코드 패턴 (Code Patterns)
**패턴 1:** *(TODO: 이 프로젝트 컨벤션 반영한 구조 스켈레톤)*
```text
# TODO
// New processor → just add new class. CheckoutService unchanged.
class CheckoutService {
constructor(private processor: PaymentProcessor) {}
pay(amt: number, t: string) { return this.processor.charge(amt, t); }
}
```
## 🤔 의사결정 기준 (Decision Criteria)
### Discriminated union + exhaustiveness
```ts
type Shape =
| { kind: "circle"; r: number }
| { kind: "square"; side: number }
| { kind: "triangle"; base: number; height: number };
**선택 A를 써야 할 때:**
- *(TODO)*
function area(s: Shape): number {
switch (s.kind) {
case "circle": return Math.PI * s.r ** 2;
case "square": return s.side ** 2;
case "triangle": return (s.base * s.height) / 2;
default: {
const _exhaust: never = s;
throw new Error(_exhaust);
}
}
}
// Add 'pentagon' → TS error in `area`, no silent miss.
```
**선택 B를 써야 할 때:**
- *(TODO)*
### Plugin / hook registry
```ts
type Hook<T> = (ctx: T) => void | Promise<void>;
class HookRegistry<T> {
private hooks: Hook<T>[] = [];
add(h: Hook<T>) { this.hooks.push(h); }
async run(ctx: T) { for (const h of this.hooks) await h(ctx); }
}
**기본값:**
> *(TODO)*
const onUserCreated = new HookRegistry<{ userId: string }>();
onUserCreated.add(({ userId }) => sendWelcomeEmail(userId));
onUserCreated.add(({ userId }) => analytics.track("user_created", userId));
// New behavior → add new hook, no service change.
```
## ❌ 안티패턴 (Anti-Patterns)
### Visitor pattern
```ts
interface NodeVisitor<R> {
visitNumber(n: NumberNode): R;
visitBinary(n: BinaryNode): R;
}
- **[안티패턴]:** *(TODO: 무엇을 하면 안 되는가 + 이유 + 대신 무엇을)*
abstract class AstNode { abstract accept<R>(v: NodeVisitor<R>): R; }
class NumberNode extends AstNode {
constructor(public val: number) { super(); }
accept<R>(v: NodeVisitor<R>) { return v.visitNumber(this); }
}
class BinaryNode extends AstNode {
constructor(public op: "+"|"-", public l: AstNode, public r: AstNode) { super(); }
accept<R>(v: NodeVisitor<R>) { return v.visitBinary(this); }
}
// New traversal (printer, evaluator, optimizer) → new visitor class.
```
### Middleware chain
```ts
type Middleware<C> = (ctx: C, next: () => Promise<void>) => Promise<void>;
function compose<C>(mws: Middleware<C>[]): (ctx: C) => Promise<void> {
return async (ctx) => {
let i = -1;
const dispatch = async (idx: number): Promise<void> => {
if (idx <= i) throw new Error("next() called multiple times");
i = idx;
const fn = mws[idx];
if (!fn) return;
await fn(ctx, () => dispatch(idx + 1));
};
await dispatch(0);
};
}
const app = compose<{ req: Request; res?: Response }>([
async (c, next) => { console.log("log"); await next(); },
async (c, next) => { c.res = new Response("ok"); await next(); },
]);
```
## 매 결정 기준
| 상황 | Approach |
|---|---|
| Multiple swappable algos | Strategy / interface |
| Closed set of variants | Discriminated union + exhaustive switch |
| Open set of behaviors | Plugin / hook registry |
| Tree traversal, multiple ops | Visitor |
| Pipeline | Middleware chain |
| Just one impl, no plans | Don't pre-abstract (YAGNI) |
**기본값**: 매 second variation 발견 시 abstract — 매 first time 의 over-abstraction X.
## 🔗 Graph
- 부모: [[SOLID-Principles]] · [[Design-Principles]]
- 변형: [[Strategy-Pattern]] · [[Visitor-Pattern]] · [[Plugin-Architecture]]
- 응용: [[Middleware]] · [[Discriminated-Unions]] · [[Dependency-Injection]]
- Adjacent: [[LSP]] · [[DIP]] · [[Composition-over-Inheritance]]
## 🤖 LLM 활용
**언제**: 매 add-only domain (payment processor, plugin, parser AST), variant 매 자주 추가.
**언제 X**: 매 small / unchanging code — 매 abstraction 의 cost > benefit.
## ❌ 안티패턴
- **Premature abstraction**: 매 single impl 의 interface — 매 indirection 만 추가, value 0.
- **Speculative generality**: 매 "혹시 나중에" — YAGNI.
- **Inheritance for code reuse**: 매 LSP 위반 risk. Composition 우선.
- **Open everything**: 매 모든 method virtual / hook — 매 cognitive load 폭증.
- **Ignore exhaustiveness**: 매 switch default fallback — 매 new variant 의 silent skip.
## 🧪 검증 / 중복
- Verified (Meyer "Object-Oriented Software Construction", Martin "Clean Architecture", refactoring.guru).
- 신뢰도 A.
## 🕓 Changelog
| 날짜 | 변경 |
|---|---|
| 2026-05-08 | Phase 1 |
| 2026-05-10 | Manual cleanup — strategy, discriminated union, plugin/visitor/middleware 패턴 |