Spring 프레임워크는 다양한 디자인 패턴을 활용하여 모듈화, 확장성, 유지보수성 등을 증가시키고자 노력합니다. 스프링은 빠르게 웹 애플리케이션을 개발하기에 좋은 프레임워크이지만, 내부 구현 원리도 학습가치가 있을 정도로 디자인 패턴을 사용해 잘 작성되어있습니다.
이번 포스팅에서는 Spring 프레임워크에서 자주 사용되는 디자인 패턴을 전체적으로 살펴보고, 가장 기본적인 생성 패턴인 Singleton Pattern
에 대해 먼저 포스팅하겠습니다.
디자인 패턴은 개발하면서 발생하는 반복적인 문제들을 어떻게 해결할 것인지에 대한 해결 방안으로 실제 현업에서 비즈니스 요구 사항을 프로그래밍으로 처리하면서 만들어진 다양한 해결책(패턴) 중에서 많은 사람들이 인정한 모범 사례(Best Practice)입니다.
일반적으로 우리가 말하는 디자인 패턴은 GoF(Gang Of Four)
가 집필한 Design Patterns에 나오는 23개의 패턴을 말합니다.
《디자인 패턴》(Design Patterns, ISBN 0-201-63361-2)은 소프트웨어 설계에 있어 공통된 문제들에 대한 표준적인 해법과 작명법을 제안한 책이다.
이 분야의 사인방(Gang of Four, 줄여 GoF) 으로 불리는 에리히 감마(Erich Gamma)
, 리처드 헬름(Richard Helm)
, 랄프 존슨(Ralph Johnson)
, 존 블리시데스(John Vlissides)
가 같이 썼고, 한국어 판은 김정아의 번역으로 피어슨 에듀케이션 코리아를 통해 출판되었다.
GoF가 제안한 디자인 패턴은 23가지의 디자인 패턴이 존재하고, 각각의 디자인 패턴은 생성(Creational)
, 구조(Structural)
, 행위(Behavioral)
3가지로 패턴으로 분류할 수 있습니다.
디자인 패턴 분류
출저 - [Design pattern] 많이 쓰는 14가지 핵심 GoF 디자인 패턴의 종류
1. 생성 패턴 (Creational Pattern)
생성 패턴은 객체 인스턴스를 생성하는 패턴입니다.
객체의 생성과 조합을 캡슐화하여 특정 객체가 생성되거나 변경되어도 프로그램 구조에 영향을 크게 받지 않도록 유연성을 제공합니다.2. 구조 패턴 (Strutural Pattern)
구조 패턴은 클래스와 객체를 더 큰 구조로 만들 수 있게 구성을 사용하는 패턴입니다.
3. 행위 패턴 (Behavioral Pattern)
행위 패턴은 클래스와 객체들이 상호작용하는 방법과 역할을 분담하는 방법을 다루는 패턴입니다.
생성 패턴 (5개) | 구조 패턴 (7개) | 행동 패턴 (11개) |
---|---|---|
|
|
|
싱글톤 패턴은 객체지향 디자인 패턴 중 하나로, 어떤 클래스가 최대 한 번만 인스턴스화되고, 이 인스턴스에 대한 전역적인 접근이 가능하도록 하는 패턴입니다. 이는 특정 클래스의 인스턴스를 전역적으로 사용해야 할 때 유용하며, 자원 절약과 성능 향상에 기여할 수 있습니다.
스프링 프레임워크에서는 컨테이너에 빈(Bean)을 싱글톤 관리하는데 사용합니다.
전략 패턴(Strategy Pattern)은 디자인 패턴 중 행위 패턴에 해당하며, 알고리즘을 정의하고 각각을 캡슐화하여 상호교환이 가능하게 만드는 패턴입니다.
이 패턴은 특정한 알고리즘을 사용하는 부분과 독립적으로 알고리즘을 변경할 수 있도록 합니다. 전략 패턴은 런타임에 동적으로 알고리즘을 변경할 수 있는 유연성을 제공하며, 코드 재사용성과 확장성을 증가시킵니다.
스프링 프레임워크에서는 스프링의 빈(Bean) 의존성 주입(Dependency Injection)에서 전략 패턴이 사용됩니다.
템플릿 메소드 패턴(Template Method Pattern)은 객체지향 디자인 패턴 중 하나로, 알고리즘의 구조를 정의하면서 일부 단계를 서브클래스로 미루는 패턴입니다.
이를 통해 알고리즘의 골격을 정의하고 각 단계의 구체적인 구현은 서브클래스에서 할 수 있도록 합니다. 템플릿 메소드 패턴은 추상 클래스를 사용하여 구현됩니다.
스프링에서는 JdbcTemplate, RestTemplate, TransactionTemplate, RedisTemplate 처럼 다양한 구현체에 템플릿 패턴이 사용됩니다. 스프링에서 이름에 xxxTemplate가 있다면 템플릿 콜백 패턴으로 만들어져있다 생각하면 됩니다.
프록시 패턴(Proxy Pattern)은 객체 지향 디자인 패턴 중 하나로, 한 객체가 다른 객체에 대한 인터페이스 역할을 수행하여 해당 객체에 대한 간접적인 접근을 제공하는 구조적인 패턴입니다.
특정한 객체의 operation을 접근하기 전에 Proxy객체를 경유하여 접근하는 패턴입니다.
Proxy는 사전적인 의미로 대리, 대리인이라는 뜻이 있습니다. 그래서 Client가 원래 사용하려고하는 객체를 직접 접근하는 것이 아니라 대리인인 Proxy 객체를 거쳐서 쓰는 패턴입니다. 이렇게 중간에 Proxy를 사용하면 객체에 대한 접근제어를 한다거나 그 객체가 생성하는데 많은 리소스가 필요한다고 하면 그 객체를 사용하기전까지 생성을 미뤄 초기화를 지연할 수 있습니다.
Spring 프레임워크에서 프록시 패턴은 주로 AOP (Aspect-Oriented Programming)을 통해 구현됩니다.
AOP는 횡단 관심사(cross-cutting concerns)를 분리하여 모듈화하고, 이를 재사용 가능한 측면(Aspect)으로 정의하여 핵심 비즈니스 로직과 분리시키는 기술입니다.
데코레이터 패턴(Decorator Pattern)은 디자인 패턴 중 구조 패턴에 해당하며, 기존 객체의 동작을 수정하지 않고 새로운 기능을 추가할 때 사용됩니다.
이 패턴은 상속을 통해 기능을 확장하는 대신, 객체를 감싸는(decorating) 방식을 사용하여 유연하게 기능을 확장할 수 있도록 합니다.
옵저버 패턴은 디자인 패턴의 행위 패턴에 해당하며, 객체 사이에 일대다의 종속 관계를 정의하는 패턴입니다.
이 패턴은 어떤 객체(주체, Subject)의 상태가 변경될 때, 그 객체에 종속된 다수의 객체(옵저버, Observer)들에게 자동으로 알림이 가도록 하는 것을 목적으로 합니다. 주체 객체의 상태가 변경되면, 해당 주체는 등록된 모든 옵저버에게 상태 변화를 통지하고, 옵저버들은 자동으로 업데이트를 수행합니다.
Spring 프레임워크는 옵저버 패턴과 유사한 이벤트 리스너 패턴을 통해 이벤트 기반 프로그래밍을 지원합니다.
- 디자인 패턴 분류 중 생성 패턴에 해당
- 객체의 인스턴스가 오직 1개만 생성되는 패턴
싱글톤(Singleton) 패턴이란 객체의 인스턴스가 오직 1개만 생성되는 패턴을 의미합니다.
아래는 간단한 자바 코드로 싱글톤 패턴을 구현한 예시입니다
public class Singleton {
static Singleton object; // 정적 참조 변수
// private 생성자
// 외부에서 호출하지 못하도록 private으로 지정
private Singleton() {};
// 객체 반환 정적 메서드
public static Singleton getInstance() {
if(object == null) {
object = new Singleton();
}
return object;
}
}
public class SingletonTest {
public static void main(String[] args) {
// private 생성자이므로 new를 통해 인스턴스를 생성할 수 없음
// Singleton s = new Singleton();
// 싱글톤으로 객체 생성없이 인스턴스를 가져올 수 있음!
Singleton s = Singleton.getInstance();
}
싱글톤 객체를 이용하면 최초 한번의 new 연산자를 통해서 고정된 메모리 영역을 사용하기 때문에 추후 해당 객체에 접근할 때 메모리 낭비를 방지할 수 있습니다.
또한, 싱글톤 인스턴스는 전역으로 사용되는 인스턴스이기 때문에 다른 클래스의 인스턴스들이 접근하여 사용할 수 있습니다.
Java로 기본적인 싱글톤 패턴을 구현하고자 하면 다음과 같은 단점들이 발생합니다.
private 생성자를 갖고 있어 상속이 불가능하다.
싱글톤 패턴은 생성자를 private으로 제한한다. private 생성자를 가진 클래스는 객체지향의 장점인 상속과 이를 이용한 다형성을 적용할 수 없다.
테스트하기 힘들다.
싱글톤은 초기화 과정에서 생성자 등을 통해 사용할 오브젝트를 직접 주입하기도 힘들기 때문에 필요한 오브젝트는 직접 오브젝트를 만들어 사용할 수 밖에 없다. 이런 경우 테스트용 오브젝트로 대체하기가 힘들다.
서버환경에서는 싱글톤이 하나만 만들어지는 것을 보장하지 못한다.
여러 개의 JVM에 분산돼서 설치가 되는 경우에도 각각 독립적으로 오브젝트가 생기기 때문에 싱글톤으로서의 가치를 보장하지 못한다.
전역 상태를 만들 수 있기 때문에 객체지향적이지 못하다.
싱글톤의 static 메소드를 이용해 언제든지 싱글톤에 접글할 수 있어 전역 상태로 사용될 수 있다. 아무 객체나 자유롭게 접근하고 수정하고 공유할 수 있는 전역 상태를 갖는 것은 객체지향 프로그래밍에서는 권장되지 않는 프로그래밍 모델이다.
Spring
에서 관리되는 Bean
들은 기본적으로 Singleton 객체로 생성됩니다.
Spring에서는 Bean을 singleton 객체로 하나만 생성하고, 해당 객체를 애플리케이션 컨텍스트에서 공유하는 디자인 패턴을 나타냅니다. Spring에서 이 개념은 Singleton Scope
라고도 불립니다.
만약 우리가 만든 웹 애플리케이션이 요청이 들어올 때마다 Spring 컨테이너에 새로운 객체를 생성한다고 가정해봅시다.
요청 1번에 5개의 객체가 만들어진다고 하고, 1초에 500번 요청이 온다고 하면 초당 2500개의 새로운 객체가 생성됩니다. 아무리 GC의 성능이 좋아졌다 하더라도 많은 요청으로 부하가 걸리면 애플리케이션이 감당할 수 없게됩니다.
이러한 이유로 스프링 프레임워크는 기본적으로 빈 객체 인스턴스를 생성하고 이를 어플리케이션 내에서 공유하여 사용합니다.
Spring
에서는 기존 싱글톤 패턴의 문제점을 해결하기 위해 직접 싱글톤 형태의 오브젝트를 만들고 관리하는 기능을 제공하는데, 그것이 바로 싱글톤 레지스트리(Singleton Registry)
입니다. 스프링 컨테이너는 싱글톤을 생성하고, 관리하고 공급하는 컨테이너이기도 합니다.
싱글톤 레지스트리는 private 생성자를 사용해야 하는 방법이 아닌 평법한 자바 클래스를 싱글톤으로 활용할 수 있게 해줍니다.
IoC 방식의 컨테이너를 사용해서 생성과 관계설정, 사용 등에 대한 제어권을 컨테이너에게 넘기면서 손쉽게 싱글톤 방식으로 관리되게 할 수 있게되었습니다. 오브젝트 생성에 관한 모든 권한은 IoC 기능을 제공하는 애플리케이션 컨텍스트에 있기 때문입니다.
💡 스프링의 빈의 Scope는 모두 싱글톤일까?
bean에는 여러가지 scope가 존재합니다. bean의 scope는 언제 생성되고, 언제까지 존재하는지 생명주기를 의미합니다.
scope | dependency | 설명 |
---|---|---|
singleton | core | (Default) 컨테이너당 하나의 인스턴스 생성 |
prototype | core | 컨테이너에 빈을 요청할 때마다 새로운 인스턴스 생성 |
request | web | http 요청이 생길 때마다 생성 |
session | web | HttpSession 당 하나 생성 |
application | web | ServletContext 당 하나 생성 |
websocket | web | WebSocket Session 당 하나 생성 |
bean scope에는 기본적으로 singleton
, prototype
으로 2개가 있습니다. 이는 기본적으로 설정할 수 있는 것으로 별도의 설정이 필요하지 않습니다.
하지만, request
, session
등 web dependency에 해당하는 scope는 스프링에서 이를 감지할 수 없기 때문에 추가적인 설정해주어야 사용할 수 있습니다.
또한, bean scope는 직접 만들어서도 사용이 가능합니다.
포스팅을 작성하면서 스프링에 사용되는 여러가지 패턴들에 대해 살펴보는 시간을 가졌습니다.😊
특히 이번 포스팅에서 상세하게 다뤘던 싱글톤 패턴과 스프링 컨테이너가 어떤 식으로 싱글톤 패턴을 활용했는지가 가장 인상 깊었던 것 같습니다.
읽어주셔서 감사합니다.😊