Files
claude-workspace/SOFTWARE_GUIDE.md
joungmin 3eb685c6f8 chore: initial project setup with MCP server configs
- 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>
2026-02-27 19:16:54 +09:00

6.9 KiB

소프트웨어 디자인 패턴 참조 가이드

디자인 패턴은 반복적인 문제를 해결하는 검증된 방법론입니다. 코드 작성 전, 아래 빠른 선택 가이드로 패턴을 먼저 고르세요.


빠른 선택 가이드

상황 적합한 패턴 분류
인스턴스를 하나만 유지해야 할 때 싱글톤 생성
객체 생성이 복잡하거나 조건부일 때 팩토리 생성
매개변수가 많거나 선택적인 객체 생성 빌더 생성
기존 객체를 복제해서 확장할 때 프로토타입 생성
복잡한 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 없이 입력값만 바꿔 결과를 검증할 수 있는가? 순수 함수화
이 모듈의 역할을 한 문장으로 말할 수 있는가? 단일 책임
추가 기능이 생겼을 때 기존 코드를 수정하지 않아도 되는가? 확장 가능성

주의사항

패턴은 도구이지 목적이 아닙니다. 단순한 문제에 복잡한 패턴을 적용하면 오히려 코드 가독성과 유지보수성이 떨어집니다.

  • 과도한 패턴 남용 금지: 문제가 패턴을 정당화할 만큼 복잡한지 먼저 판단
  • 성능 고려: 프록시·옵저버 등 일부 패턴은 런타임 오버헤드 발생 가능
  • 팀 이해도 우선: 팀원 모두가 이해할 수 있는 수준의 패턴을 선택