- Add MCP servers for Redmine, Jenkins, Gitea, Obsidian - Add setup_mcp.sh to generate .mcp.json from .env (no secrets in VCS) - Add .env.example with required variable names for team onboarding - Add .gitignore to exclude secrets (.env, .mcp.json, settings.local.json) - Fix obsidian_mcp_server.py stdio_server async context manager usage Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
145 lines
6.9 KiB
Markdown
145 lines
6.9 KiB
Markdown
# 소프트웨어 디자인 패턴 참조 가이드
|
|
|
|
디자인 패턴은 반복적인 문제를 해결하는 검증된 방법론입니다.
|
|
코드 작성 전, 아래 **빠른 선택 가이드**로 패턴을 먼저 고르세요.
|
|
|
|
---
|
|
|
|
## 빠른 선택 가이드
|
|
|
|
| 상황 | 적합한 패턴 | 분류 |
|
|
|------|------------|------|
|
|
| 인스턴스를 하나만 유지해야 할 때 | 싱글톤 | 생성 |
|
|
| 객체 생성이 복잡하거나 조건부일 때 | 팩토리 | 생성 |
|
|
| 매개변수가 많거나 선택적인 객체 생성 | 빌더 | 생성 |
|
|
| 기존 객체를 복제해서 확장할 때 | 프로토타입 | 생성 |
|
|
| 복잡한 API를 단순하게 감싸야 할 때 | 퍼사드 | 구조 |
|
|
| 호환되지 않는 인터페이스를 연결할 때 | 어댑터 | 구조 |
|
|
| 접근 제어·캐싱·지연 로딩이 필요할 때 | 프록시 | 구조 |
|
|
| 상태 변화를 여러 곳에 알려야 할 때 | 옵저버 | 행위 |
|
|
| 컬렉션을 표준 방식으로 순회할 때 | 이터레이터 | 행위 |
|
|
| 알고리즘을 런타임에 교체해야 할 때 | 전략 | 행위 |
|
|
| 복잡한 조건문(if/switch)을 제거할 때 | 스테이트 | 행위 |
|
|
| 객체 간 직접 통신을 줄이고 싶을 때 | 메디에이터 | 행위 |
|
|
|
|
---
|
|
|
|
## 1. 생성 패턴 (Creational Patterns)
|
|
|
|
객체를 **어떻게 만들지**를 다루는 패턴
|
|
|
|
### 싱글톤 (Singleton)
|
|
- **목적**: 클래스 인스턴스를 전역에서 단 하나만 보장
|
|
- **사용 예**: 앱 설정 객체, 전역 로거, 캐시 관리자
|
|
- **특징**: 어디서 접근해도 동일한 인스턴스 반환
|
|
|
|
### 팩토리 (Factory)
|
|
- **목적**: 객체 생성 로직을 캡슐화, 클라이언트에서 `new`를 직접 사용하지 않음
|
|
- **사용 예**: 플랫폼별 UI 컴포넌트 생성, 버거 주문 시스템
|
|
- **특징**: 생성할 구체 클래스를 호출자로부터 분리
|
|
|
|
### 빌더 (Builder)
|
|
- **목적**: 복잡한 객체를 단계별로 조립
|
|
- **사용 예**: SQL 쿼리 빌더, HTTP 요청 객체, 설정이 많은 DTO
|
|
- **특징**: 메서드 체이닝 → `build()`로 최종 객체 완성
|
|
|
|
### 프로토타입 (Prototype)
|
|
- **목적**: 기존 객체를 복제(clone)하여 새 객체 생성
|
|
- **사용 예**: JavaScript 프로토타입 상속, 깊은 복사가 필요한 객체
|
|
- **특징**: 클래스 상속 없이 기능 확장 가능
|
|
|
|
---
|
|
|
|
## 2. 구조 패턴 (Structural Patterns)
|
|
|
|
객체와 클래스를 **어떻게 조합할지**를 다루는 패턴
|
|
|
|
### 퍼사드 (Facade)
|
|
- **목적**: 복잡한 내부 시스템을 단순한 인터페이스로 노출
|
|
- **사용 예**: 외부 API 래퍼, 복잡한 라이브러리의 진입점
|
|
- **특징**: 내부 구현 세부사항을 숨기고 고수준 API 제공
|
|
|
|
### 어댑터 (Adapter)
|
|
- **목적**: 호환되지 않는 두 인터페이스를 연결
|
|
- **사용 예**: 레거시 코드 통합, 서드파티 라이브러리 래핑
|
|
- **특징**: 기존 클래스를 수정하지 않고 새 인터페이스에 맞춤
|
|
|
|
### 프록시 (Proxy)
|
|
- **목적**: 실제 객체 앞에 대리 객체를 두어 접근 제어·부가 기능 추가
|
|
- **사용 예**: Vue.js 반응형 시스템, 지연 로딩(Lazy Loading), API 캐싱
|
|
- **특징**: 실제 객체 교체 없이 동작 가로채기 가능
|
|
|
|
---
|
|
|
|
## 3. 행위 패턴 (Behavioral Patterns)
|
|
|
|
객체 간 **통신과 책임 분배**를 다루는 패턴
|
|
|
|
### 옵저버 (Observer / Pub-Sub)
|
|
- **목적**: 한 객체의 상태 변화를 구독자들에게 자동으로 알림
|
|
- **사용 예**: Firebase 실시간 업데이트, 유튜브 구독 알림, 이벤트 버스
|
|
- **특징**: 1:N 관계, 발행자와 구독자의 느슨한 결합
|
|
|
|
### 이터레이터 (Iterator)
|
|
- **목적**: 컬렉션 내부 구조 노출 없이 요소를 순회
|
|
- **사용 예**: 배열·연결 리스트·트리의 통일된 순회 인터페이스
|
|
- **특징**: 자료구조 구현과 순회 로직을 분리
|
|
|
|
### 전략 (Strategy)
|
|
- **목적**: 알고리즘을 외부에서 주입받아 런타임에 교체 가능하게 함
|
|
- **사용 예**: 정렬 알고리즘 선택, 결제 수단 선택, 압축 방식 선택
|
|
- **특징**: Open-Closed 원칙 준수, 기존 코드 수정 없이 동작 확장
|
|
|
|
### 메디에이터 (Mediator)
|
|
- **목적**: 객체들이 서로 직접 통신하지 않고 중재자를 통해 소통
|
|
- **사용 예**: Express.js 미들웨어 체인, 채팅방 서버, 항공 관제 시스템
|
|
- **특징**: 객체 간 결합도 감소, 관계가 M:N → 1:N으로 단순화
|
|
|
|
### 스테이트 (State)
|
|
- **목적**: 객체 내부 상태에 따라 동작이 완전히 달라지게 분리
|
|
- **사용 예**: 게임 캐릭터 상태 머신, 주문 처리 흐름, UI 컴포넌트 모드
|
|
- **특징**: 복잡한 `if/switch` 조건문을 상태 클래스로 대체
|
|
|
|
---
|
|
|
|
## 모듈 설계 원칙: 단순하고 테스트하기 쉽게
|
|
|
|
> 좋은 코드는 읽기 쉽고, 좋은 모듈은 테스트하기 쉽습니다.
|
|
> 복잡한 패턴보다 **작고 명확한 단위**로 나누는 것이 먼저입니다.
|
|
|
|
### 단순하게 만들기
|
|
|
|
- **하나의 모듈 = 하나의 책임**: 파일 하나가 하는 일을 한 문장으로 설명할 수 없다면 쪼개야 함
|
|
- **의존성은 주입받기**: 모듈 안에서 직접 `new` 하거나 전역 참조 대신, 필요한 객체는 인자로 받기
|
|
- **외부와의 접점 최소화**: 공개 함수(export)는 꼭 필요한 것만, 나머지는 내부에 숨기기
|
|
- **설정과 로직 분리**: 환경변수·상수는 별도 파일로, 핵심 로직 안에 하드코딩 금지
|
|
|
|
### 테스트하기 쉽게 만들기
|
|
|
|
- **순수 함수 우선**: 같은 입력 → 항상 같은 출력, 부작용 없는 함수는 테스트가 단 한 줄
|
|
- **I/O는 가장자리로**: DB·파일·HTTP 호출은 모듈 경계 바깥으로 밀어내고, 핵심 로직은 순수하게 유지
|
|
- **인터페이스 기반 설계**: 구체 구현 대신 인터페이스(프로토콜)에 의존하면 테스트 시 Mock으로 쉽게 교체 가능
|
|
- **작은 단위로 분리**: 함수 하나가 20줄을 넘기 시작하면 쪼갤 신호
|
|
|
|
### 체크리스트
|
|
|
|
테스트 작성 전, 아래 질문에 "예" 가 되도록 설계하세요.
|
|
|
|
| 질문 | 목표 |
|
|
|------|------|
|
|
| 이 함수를 Obsidian·Jenkins 없이 단독 실행할 수 있는가? | 외부 의존성 격리 |
|
|
| Mock 없이 입력값만 바꿔 결과를 검증할 수 있는가? | 순수 함수화 |
|
|
| 이 모듈의 역할을 한 문장으로 말할 수 있는가? | 단일 책임 |
|
|
| 추가 기능이 생겼을 때 기존 코드를 수정하지 않아도 되는가? | 확장 가능성 |
|
|
|
|
---
|
|
|
|
## 주의사항
|
|
|
|
> 패턴은 도구이지 목적이 아닙니다. 단순한 문제에 복잡한 패턴을 적용하면
|
|
> 오히려 코드 가독성과 유지보수성이 떨어집니다.
|
|
|
|
- **과도한 패턴 남용 금지**: 문제가 패턴을 정당화할 만큼 복잡한지 먼저 판단
|
|
- **성능 고려**: 프록시·옵저버 등 일부 패턴은 런타임 오버헤드 발생 가능
|
|
- **팀 이해도 우선**: 팀원 모두가 이해할 수 있는 수준의 패턴을 선택
|