디자인 패턴이란? GoF 분류와 핵심 활용
소프트웨어 개발자가 반복적으로 마주치는 설계 문제들이 있다. 객체를 어떻게 생성할 것인지, 클래스 간 관계를 어떻게 조립할 것인지, 객체들이 어떻게 협력해야 하는지에 대한 고민이 그것이다. 이러한 반복되는 문제에 대해 수많은 선배 개발자들이 시행착오를 거쳐 검증한 해결책을 정리한 것이 바로 디자인 패턴(Design Pattern)이다. 1994년 에리히 감마(Erich Gamma)를 비롯한 네 명의 저자가 출간한 책 『Design Patterns: Elements of Reusable Object-Oriented Software』에서 23개의 패턴을 체계화하면서, 이들은 GoF(Gang of Four)라는 이름으로 불리게 되었다. 정보처리기사 시험에서도 매회 출제되는 핵심 주제이며, 실무에서는 객체지향 설계의 공용어 역할을 수행한다.

디자인 패턴이란 무엇인가
디자인 패턴이란 소프트웨어 설계 과정에서 반복적으로 발생하는 문제들에 대한 검증된 해결책을 일반화한 형태로 정리한 것을 의미한다. 단순한 코드 조각이나 라이브러리가 아니라, 특정 상황에서 적용할 수 있는 설계의 청사진이라고 표현하는 것이 더 정확하다. 같은 패턴이라도 사용하는 프로그래밍 언어에 따라 구현 방식은 달라지지만, 패턴이 해결하려는 문제와 그 해결 구조는 언어를 초월해 동일하게 유지된다. 따라서 디자인 패턴을 학습한다는 것은 특정 코드를 외우는 것이 아니라, 문제를 인식하고 적절한 해결 구조를 떠올릴 수 있는 사고의 틀을 갖추는 일이다.
디자인 패턴이 가져다주는 이점은 크게 세 가지로 요약된다. 첫째, 코드 재사용성과 유지보수성이 향상된다. 검증된 구조를 사용하기 때문에 향후 기능 확장이나 변경 요구가 발생했을 때 큰 폭의 수정 없이 대응할 수 있다. 둘째, 개발자 간 의사소통의 효율이 극적으로 높아진다. "여기에 옵저버 패턴을 적용하자"라는 한 마디가 수십 줄의 설명을 대체할 수 있기 때문이다. 셋째, 잘못된 설계로 인한 함정을 미리 회피할 수 있다. 패턴이 다루는 문제 정의에는 안티패턴(Anti-pattern)에 대한 경고도 함께 담겨 있어, 초보 개발자가 같은 실수를 반복하지 않도록 도와준다.
다만 디자인 패턴을 무분별하게 적용하는 것은 오히려 해가 될 수 있다는 점을 반드시 기억해야 한다. 단순한 문제에 복잡한 패턴을 억지로 끼워 맞추면 코드가 불필요하게 추상화되고, 가독성이 떨어지며, 디버깅이 어려워진다. 이를 패턴 만능주의(Pattern Mania)라고 부르며, 숙련된 개발자일수록 패턴을 도입할 시점과 도입하지 않을 시점을 구분하는 안목을 갖추게 된다. 좋은 설계자는 패턴을 알고 있으면서도 필요할 때만 정확히 적용할 줄 아는 사람이라는 격언이 자주 인용되는 이유가 여기에 있다.
GoF 디자인 패턴의 3가지 분류
GoF의 23개 디자인 패턴은 그 목적에 따라 생성 패턴(Creational), 구조 패턴(Structural), 행위 패턴(Behavioral)이라는 세 가지 큰 분류로 나뉜다. 이 분류는 패턴이 무엇을 해결하려고 하는지에 따른 자연스러운 구분이며, 학습 순서로도 이 흐름을 따르는 것이 일반적이다. 정보처리기사 시험에서도 특정 패턴이 어떤 분류에 속하는지를 묻는 문제가 자주 출제되므로, 분류 체계를 정확히 외워두는 것이 매우 중요하다.
생성 패턴은 객체의 생성 과정을 다루는 다섯 가지 패턴으로 구성된다. 싱글톤(Singleton), 팩토리 메서드(Factory Method), 추상 팩토리(Abstract Factory), 빌더(Builder), 프로토타입(Prototype)이 여기에 속한다. 생성 패턴은 객체를 직접 new 키워드로 만드는 대신, 어떤 객체를 어떻게 만들지에 대한 결정을 별도의 구조로 위임함으로써 시스템이 구체적인 클래스에 결합되지 않도록 돕는다. 이 분류의 패턴들을 통해 객체 생성의 유연성과 확장성을 동시에 확보할 수 있다.
구조 패턴은 클래스나 객체를 조합해 더 큰 구조를 형성하는 일곱 가지 패턴이다. 어댑터(Adapter), 브리지(Bridge), 컴포지트(Composite), 데코레이터(Decorator), 퍼사드(Facade), 플라이웨이트(Flyweight), 프록시(Proxy)가 포함된다. 구조 패턴은 서로 다른 인터페이스를 호환시키거나, 객체에 동적으로 책임을 추가하거나, 복잡한 서브시스템을 단순한 인터페이스로 감싸는 데 사용된다. 행위 패턴은 객체 간 책임 분배와 상호작용을 다루는 가장 큰 분류로, 열한 가지 패턴이 포함된다. 책임 연쇄(Chain of Responsibility), 커맨드(Command), 인터프리터(Interpreter), 이터레이터(Iterator), 미디에이터(Mediator), 메멘토(Memento), 옵저버(Observer), 상태(State), 전략(Strategy), 템플릿 메서드(Template Method), 비지터(Visitor)가 그 면면이다. 행위 패턴은 객체들이 어떻게 협력하고 책임을 분배하는지에 대한 다양한 전략을 제공한다.
가장 널리 쓰이는 패턴: 싱글톤과 팩토리
23개의 GoF 패턴 중에서 실무에서 가장 자주 만나게 되는 두 가지를 꼽으라면 단연 싱글톤과 팩토리 메서드이다. 두 패턴 모두 생성 패턴에 속하며, 객체를 어떻게 만들 것인가에 대한 가장 기본적이면서도 강력한 답을 제공한다. 이 두 패턴을 정확히 이해하는 것만으로도 객체지향 설계 역량의 상당 부분을 끌어올릴 수 있다는 평가를 받는다.
싱글톤 패턴은 어떤 클래스의 인스턴스가 시스템 전체에서 단 하나만 존재하도록 보장하면서, 그 인스턴스에 접근할 수 있는 전역적인 통로를 제공하는 패턴이다. 데이터베이스 연결 풀, 로깅 객체, 애플리케이션 설정 관리자처럼 시스템 전체에서 단 하나여야 의미가 있는 자원을 다룰 때 사용한다. 구현 시에는 생성자를 private로 막고, 클래스 내부에 자기 자신의 인스턴스를 보관할 정적 변수를 두며, getInstance()와 같은 정적 메서드를 통해 그 인스턴스를 반환한다. 다만 멀티스레드 환경에서는 동기화 처리를 빠뜨리면 인스턴스가 두 개 이상 생성될 수 있으므로, double-checked locking이나 정적 내부 클래스 기법을 활용해 안전하게 구현해야 한다.
팩토리 메서드 패턴은 객체를 생성하는 인터페이스는 정의하되, 어떤 구체 클래스의 인스턴스를 만들지는 서브클래스가 결정하도록 위임하는 패턴이다. 클라이언트 코드가 구체적인 클래스 이름에 직접 의존하지 않게 만들어, 새로운 종류의 객체를 추가할 때 기존 코드를 수정할 필요가 없어진다. 예를 들어 결제 모듈에서 카드 결제, 계좌이체, 간편 결제 같은 다양한 결제 수단을 다룰 때, PaymentFactory가 결제 종류에 따라 적절한 결제 객체를 생성해 반환하도록 만들면 새 결제 수단을 추가할 때마다 클라이언트 측을 건드리지 않고 팩토리만 확장하면 된다. 이는 객체지향 설계 5대 원칙 중 개방-폐쇄 원칙(Open-Closed Principle)을 자연스럽게 만족시키는 구조이며, 스프링 프레임워크의 BeanFactory, 자바 표준 라이브러리의 Calendar.getInstance() 같은 핵심 도구들이 모두 이 패턴을 기반으로 설계되어 있다.
메타 디스크립션: GoF 23개 디자인 패턴의 3가지 분류(생성·구조·행위)를 정리하고, 실무에서 가장 자주 쓰이는 싱글톤과 팩토리 메서드 패턴의 핵심을 정보처리기사 출제 기준에 맞춰 설명합니다.