Computer Science/Design Pattern

디자인 패턴 총정리

guwon2 2025. 10. 1. 15:51

디자인 패턴


싱글톤 패턴 (Singleton Pattern)

하나의 클래스에 오직 하나의 인스턴스만 생성하도록 보장하는 패턴

장점

  • 인스턴스 생성 비용 절약: 메모리 효율성 향상
  • 전역 접근: 애플리케이션 전체에서 동일한 인스턴스 사용
  • 지연 초기화: 필요한 시점에만 인스턴스 생성

단점

  • 의존성 증가: 전역 상태로 인한 결합도 상승
  • TDD 어려움: 테스트 간 독립성 훼손 (테스트는 서로 독립적이고 순서에 상관없어야 함)
  • 멀티스레딩 이슈: Thread-Safe 구현 필요

사용 사례

  • 데이터베이스 연결 모듈
  • 커넥션 풀
  • 스레드 풀
  • 설정 객체

구현 방법

// Thread-Safe한 싱글톤 (권장)
public class Singleton {
    private Singleton() {}

    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

해결책: 의존성 주입을 통해 테스트 용이성 개선 가능


팩토리 패턴 (Factory Pattern)

객체 생성 부분을 분리하여 추상화한 패턴

  • 상위 클래스: 뼈대 결정
  • 하위 클래스: 구체적인 객체 생성 내용 결정

장점

  • 느슨한 결합 → 유연성 증가
  • 유지보수성 향상
  • 단일 책임 원칙 준수
  • 개방/폐쇄 원칙 준수
  • 캡슐화/추상화 실현

단점

  • 코드 복잡성 증가
  • 서브클래스 수 증가

실제 사용 예시

  • NumberFormat.getInstance() (Java)
  • Calendar.getInstance() (Java)
  • BeanFactory (Spring Framework)

전략 패턴 (Strategy Pattern, Policy Pattern)

개념

객체의 행위를 직접 수정하지 않고, 캡슐화된 알고리즘을 컨텍스트에서 교체하여 상호 교환 가능하게 만드는 패턴

장점

  • OOP 기술의 총집합: OCP, DIP, 합성, 다형성, 캡슐화 모두 활용
  • 런타임 알고리즘 변경 가능
  • 테스트 용이성 향상

단점

  • 알고리즘 증가 → 관리해야 할 객체 수 증가

실제 사용 예시

  • Collections.sort()Comparator
  • HttpServletdoXXX() 메서드들
  • FilterdoFilter() 메서드
  • Spring의 BeanDefinitionParser, PlatformTransactionManager, CacheManager
  • 결제 방식 선택 로직

옵저버 패턴 (Observer Pattern)

주체 객체의 상태 변화를 관찰하다가 변화가 있을 때마다 등록된 옵저버들에게 자동으로 알림을 전달하는 패턴

장점

  • 실시간 감지: 주기적 폴링 없이 상태 변화 자동 감지
  • 확장성: 새로운 옵저버 추가 시 기존 코드 수정 불필요
  • 느슨한 결합: Subject와 Observer 간 독립성

단점

  • 알림 순서 제어 불가
  • 메모리 누수 위험: 옵저버 해지하지 않을 경우

사용 사례

  • SNS 알림 (트위터, 페이스북)
  • 이벤트 기반 시스템
  • MVC 패턴의 Model-View 관계

Java 구현 방법

  • java.lang.reflect.Proxy 사용
  • java.util.Observable, java.util.Observer 활용

프록시 패턴 (Proxy Pattern)

대상 객체에 접근하기 전, 접근 흐름을 가로채서 대상 객체 앞단에서 인터페이스 역할을 수행하는 패턴

주요 사용 목적

  • 보안: 접근 권한 제어
  • 데이터 검증: 입력값 검증
  • 캐싱: 결과 캐싱으로 성능 향상
  • 로깅: 요청/응답 로깅

프록시 서버 활용

서버-클라이언트 사이에서 간접 접속을 가능하게 하는 시스템

대표적인 프록시 서버: Nginx

  • 버퍼 오버플로우 예방
  • CDN (CloudFlare): 캐싱
  • DDoS 방어
  • HTTPS 구축
  • CORS 문제 해결

이터레이터 패턴 (Iterator Pattern)

다양한 자료구조를 하나의 통일된 인터페이스로 순회할 수 있게 해주는 패턴

장점

  • 일관된 순회 방법: ArrayList, LinkedList, HashSet 등 모두 동일한 방식으로 접근
  • 내부 구조 은닉: 컬렉션 내부 구현에 의존하지 않음

노출모듈 패턴 (Revealing Module Pattern)

즉시 실행 함수(IIFE)를 통해 private, public 같은 접근 제어자를 구현하는 패턴

JavaScript 예시

const myModule = (function() {
    let privateVar = 0;

    function privateFunction() {
        console.log('Private function');
    }

    return {
        publicMethod: function() {
            privateVar++;
            privateFunction();
        },
        getCount: function() {
            return privateVar;
        }
    };
})();

아키텍처 패턴

MVC 패턴 (Model-View-Controller)

구성 요소

  • Model: 데이터베이스, 상수, 변수 등 애플리케이션 데이터
  • View: 사용자에게 보여지는 UI 요소
  • Controller: Model과 View를 연결하는 중재자 역할
    • 메인 로직 처리
    • 생명주기 관리

장점

  • 재사용성: 각 구성 요소의 독립적 재사용
  • 확장성: 기능 추가 및 수정 용이

단점

  • 복잡성 증가: 애플리케이션이 커질수록 Model-View 관계 복잡화

MVP 패턴 (Model-View-Presenter)

특징

  • Controller → Presenter로 변경
  • View : Presenter = 1:1 관계
  • 더 강한 결합을 가지지만 테스트 용이성 향상

MVVM 패턴 (Model-View-ViewModel)

특징

  • Controller → ViewModel로 변경 (View를 더욱 추상화)
  • 데이터 바인딩: 자동 UI 업데이트
  • 커맨드 패턴: 사용자 액션 처리

장점

  • UI 재사용성 향상
  • 단위 테스트 용이성
  • View와 로직의 완전한 분리

주요 활용 프레임워크

  • Vue.js
  • Angular
  • WPF/Xamarin (Microsoft)

적용

1. 패턴 선택

  • 싱글톤: 전역 상태가 정말 필요한 경우만 (설정, 캐시, 연결풀)
  • 팩토리: 객체 생성 로직이 복잡하거나 변경 가능성이 높을 때
  • 전략: 알고리즘이나 정책이 자주 바뀌는 경우
  • 옵저버: 이벤트 기반 시스템, 실시간 알림
  • 프록시: 보안, 캐싱, 로깅이 필요한 경우

2. 안티패턴 주의사항

  • 과도한 패턴 사용: 단순한 로직에 불필요한 패턴 적용 금지
  • 싱글톤 남용: 전역 상태로 인한 테스트 어려움
  • 깊은 상속 구조: 합성을 우선 고려

3. 테스트 고려사항

  • 의존성 주입: 테스트 더블(Mock, Stub) 활용
  • 인터페이스 활용: 구현체 교체 가능한 설계
  • 상태 격리: 테스트 간 독립성 보장

출처


디자인 패턴은 도구이다. 문제를 해결하기 위한 수단이지, 그 자체가 목적이 되어서는 안 된다. 항상 "왜 이 패턴을 사용해야 하는가?"를 먼저 생각하고 적용하자!