디자인 패턴

지송·2025년 6월 23일

프로그래밍

목록 보기
3/4

안녕하세요 이번에는 디자인 패턴을 정리해 보려고 합니다.
사실 우테코 6기 시절, 크루 "커찬"의 발표가 저에게는 굉장히 인상 깊었는데요.
따라서 디자인 패턴을 따로 공부하기보다는 프로그래밍을 하며 자연스럽게 익혀왔습니다.

하지만
스프링에서 주요하게 사용되는 싱글톤 패턴,
우테코 체스 미션 중 공부하였던 전략 패턴,
Nginx 트러블 슈팅을 하며 공부하였던 프록시 패턴 등

다양한 패턴을 학습하게 되며 한번쯤은 정리해 보고자 하여 글로 남겨봅니다.

디자인 패턴이란?

디자인 패턴은 자주 발생하는 소프트웨어 설계 문제를 해결하기 위한 재사용 가능한 설계 템플릿입니다.
특정 상황에서 검증된 설계 방법을 패턴으로 정리해두면, 복잡한 문제를 효율적으로 해결할 수 있고 코드의 유지보수성과 확장성을 높일 수 있습니다.


주요 디자인 패턴

✅ 싱글톤 패턴

  • 목적: 애플리케이션에서 단 하나의 인스턴스만 존재하도록 보장
  • 활용 예시: 설정 클래스, 로깅, 캐시 등
  • 관련 개념: 의존성 주입을 통해 싱글톤 객체를 안전하게 주입 가능
public class Singleton {
    private static final Singleton instance = new Singleton();

    private Singleton() {}

    public static Singleton getInstance() {
        return instance;
    }
}

✅ 팩토리 패턴

  • 목적: 객체 생성 로직을 별도의 클래스(팩토리)로 분리
  • 활용 예시: 다양한 형태의 객체 생성이 필요한 경우 (예: 인터페이스 기반 객체 생성)
interface Product {
    void use();
}

class ConcreteProductA implements Product {
    public void use() {
        System.out.println("Using Product A");
    }
}

class ProductFactory {
    public static Product createProduct(String type) {
        if ("A".equals(type)) return new ConcreteProductA();
        throw new IllegalArgumentException("Unknown type");
    }
}

✅ 전략 패턴

  • 목적: 런타임에 알고리즘을 선택할 수 있도록 전략을 인터페이스로 분리
  • 활용 예시: 정렬 방식 선택, 결제 수단 선택 등
interface Strategy {
    void execute();
}

class ConcreteStrategyA implements Strategy {
    public void execute() {
        System.out.println("Strategy A executed");
    }
}

class Context {
    private Strategy strategy;

    public Context(Strategy strategy) {
        this.strategy = strategy;
    }

    public void performStrategy() {
        strategy.execute();
    }
}

✅ 옵저버 패턴

  • 목적: 한 객체의 상태 변화에 따라 여러 객체가 자동으로 갱신되도록 설계
  • 활용 예시: 이벤트 리스너, 게시판 알림 시스템 등
interface Observer {
    void update(String message);
}

class Subject {
    private List<Observer> observers = new ArrayList<>();

    public void addObserver(Observer o) {
        observers.add(o);
    }

    public void notifyObservers(String message) {
        for (Observer o : observers) {
            o.update(message);
        }
    }
}

✅ 프록시 패턴 & 프록시 서버

  • 목적: 실제 객체 대신 대리 객체가 요청을 제어하도록 함
  • 활용 예시: 접근 제어, 캐싱, 지연 로딩
interface Service {
    void request();
}

class RealService implements Service {
    public void request() {
        System.out.println("RealService request executed");
    }
}

class ProxyService implements Service {
    private RealService realService = new RealService();

    public void request() {
        System.out.println("Proxy before");
        realService.request();
        System.out.println("Proxy after");
    }
}

✅ 이터레이터 패턴

  • 목적: 컬렉션 내부 구조를 노출하지 않고 순회할 수 있도록 함
  • 활용 예시: List, Set 등의 반복자
List<String> list = Arrays.asList("a", "b", "c");
Iterator<String> iterator = list.iterator();

while (iterator.hasNext()) {
    System.out.println(iterator.next());
}

✅ 노출 모듈 패턴 (JavaScript 예시)

  • 목적: 필요한 것만 외부에 노출하고 나머지는 은닉하여 캡슐화 강화
  • 활용 예시: 모듈 패턴 기반 JavaScript 코드 구조 등
const MyModule = (function() {
    const privateVar = 'secret';

    function privateFunc() {
        console.log(privateVar);
    }

    return {
        publicFunc: function() {
            privateFunc();
        }
    };
})();

MyModule.publicFunc();

UI 설계 패턴

✅ MVC 패턴

  • 구성: Model, View, Controller
  • 역할: 비즈니스 로직과 UI를 분리하여 유지보수 용이
User → Controller → Model → View → User

✅ MVP 패턴

  • 구성: Model, View, Presenter
  • 특징: View는 Presenter를 통해서만 Model과 통신
User → View ↔ Presenter ↔ Model

✅ MVVM 패턴

  • 구성: Model, View, ViewModel
  • 특징: 양방향 데이터 바인딩으로 View와 ViewModel 간 동기화
View ↔ ViewModel ↔ Model

생각

디자인 패턴이 항상 좋은 것은 아닙니다.

코드가 복잡해졌을 때, 문제를 분리하고 구조화하는 과정에서 자연스럽게 디자인 패턴이 등장할 수 있습니다.
패턴을 억지로 외우고 적용하기보다는, 객체 지향적으로 고민하고 리팩토링을 진행하다 보면 적절한 패턴 형태로 정리되는 경우가 많습니다.

결국, 디자인 패턴은 문제를 해결하기 위한 도구이지 목표 자체는 아니라는 점을 잊지 않아야 합니다.

profile
💻 늘 공부하고 발전하는 개발자

0개의 댓글