스프링은 기업용 온라인 서비스 기술을 지원하기 위해 만들어졌기 때문에 대부분의 스프링 애플리케이션이 웹 애플리케이션이다. 웹 애플리케이션은 보통 여러 고객이 동시에 요청을 하게 된다. 때문에 동시에 여러 클라이언트가 동시에 요청을 하게 되면 DI 컨테이너에 3개의 요청이 들어오게 되는데 요청에 따라 새로운 객체를 생성해서 반환하게 되면 문제가 생긴다.
고객 트레픽이 초당 100이 나온다고 가정했을 때 초당 100개의 객체를 생성하고 소멸하게 된다면 메모리 낭비가 심해지기 때문이다.
때문에 해당 객체를 1개만 생성해서 객체를 공유하도록 설계를 해서 문제점을 해결하면 되는데 이렇게 하나의 객체만 생성해서 공유하도록 하는 것을 싱글톤 패턴이라고 한다.
클래스의 인스턴스가 딱 1개만 만들어지는 것을 보장하는 디자인 패턴을 말한다. 인스턴스가 1개만 만들어지도록 하기 위해서 생성자의 접근 제한자를 private
으로 지정하여 외부에서 임의로 new
생성자를 이용해서 만들지 못하도록 막을 수 있다.
public class SingletonService {
private static final SingletonService instance=new SingletonService();
//static 영역에 하나의 인스턴스만 만들 수 있게 한다.
public static SingletonService getInstance() {
//SingletonService의 인스턴스가 필요하면
//getInstance() 메소드를 통해서만 조회할 수 있다.
//->항상 같은 인스턴스를 반환
return instance;
}
private SingletonService() {
//private 생성자를 만들어서 외부에서 SingletonService를 생성할 수 없게 한다.
}
}
위와 같은 객체를 미리 생성해서 더 이상 생성할 수 없게 하는 단순하고 안전한 방법 외에도 객체가 없다면 생성하고 있으면 생성된 객체를 사용하는 등 다양한 방법이 존재한다.
위 코드처럼 구현한다고 하면 객체 하나만 생성해서 쓰려고 해도 구현하는 코드 자체가 많이 들어가게 된다. 또한 의존관계상 클라이언트가 구체 클래스에 의존하게 되기 때문에 DIP를 위반하게 되며 클라이언트가 구체 클래스에 의존하게 되면서 OCP 원칙을 위반할 가능성이 높아진다.
내부 속성을 변경하거나 초기화하기 어렵기도 어렵고 private
생성자로 구현하면서 자식 클래스를 만들기 어려워진다는 단점이 존재한다.
결과적으로 유연성이 굉장히 떨어진다는 문제점이 크다.
스프링 컨테이너는 이런 싱글톤의 문제점을 해결하면서 객체 인스턱스를 1개로(싱글톤 패턴) 관리한다. 스프링 컨테이너에 이미 빈을 등록해놓고 해당하는 빈을 호출해주기 때문이다. 이렇게 스프링 컨테이너는 싱글톤 컨테니어 역할을 수행한다. 그리고 이렇게 싱글톤 객체를 생성, 관리하는 기능을 싱글톤 레지스트리라 한다.
💡 스프링 컨테이너가 기본 빈 등록 방식을 싱글톤 패턴으로 지원하지만 요청 할 때마다 새로운 객체를 생성, 반환하는 기능도 제공한다.
싱글톤 패턴을 사용하기 위해 만들었던 지저분한 코드들이 들어가지 않아도 되며, 스프링 컨테이너가 관리하기 때문에 DIP, OCP를 위반하지 않고 테스트가 힘들어지지도 않는다. private
생성자로부터 자유로워지기 때문에 유연성이 떨어진다는 문제점으로부터 자유로워진다.
싱글톤 패턴이나 스프링 같은 싱글톤 컨테이너를 사용할 때는 객체의 인스턴스를 하나만 생성해서 공유하기 때문에 싱글톤 객체가 상태를 유지(stateful)하게 설계해서는 안 되고 무상태(stateless)로 설계해야 한다.
무상태로 설계한다는 것은 특정 클라이언트에 의존적인 필드가 없어야 하고, 값을 변경할 수 있는 필드가 있으면 안 된다는 것이다. 가급적 읽기만 허용하도록 하되, 필드 대신 자바에서 공유되지 않는 지역변수, 파라미터, ThreadLocal 등을 사용해야 한다.
스프링 빈의 필드에 공유 값을 설정하면 큰 장애가 발생할 수 있기 때문이다.
스프링 컨테이너는 싱글톤 레지스트리로 스프링 빈이 싱글톤이 되도록 보장시킨다. 하지만 Configuration 클래스에 정의했던 자바 코드대로라면 여러 번 생성이 되었어야 한다. 이런 객체들을 싱글톤 패턴을 유지할 수 있게 만드는 것은 @Configuration
애너테이션이다.
AnnotationConfigApplicationContext
에 파라미터로 넘긴 값은 스프링 빈으로 등록되는데 이 때 @Configuration
으로 지정한 Configuration 클래스도 빈으로 등록이 된다. 그래서 빈으로 등록한 설정 클래스를 조회해서 정보를 출력해보면 일반적인 hello.test.AppConfig…
처럼 출력되지 않고 hello.test.AppConfig$$EnhancerBySpringCGLIB$$…
처럼 출력 되는 것을 볼 수 있다.
스프링이 바이트를 조작하는 CGLIB라는 라이브러리를 사용해서 AppConfig를 상속 받은 임의의 클래스를 만들고 그 클래스를 스프링 빈으로 등록한다. 이 임의의 클래스가 싱글톤이 보장되도록 해준다.
실제 내부 라이브러리 기술은 복잡하기 때문에 @Configuration
애너테이션을 사용함으로써 싱글톤 패턴을 보장할 수 있다고 인지하면 된다.
강의 : 스프링 핵심 원리 - 기본편 (김영한 님)