Spring

싱글톤(Singleton)

dev-rootable 2023. 5. 8. 20:05

📌 싱글톤 패턴이 왜 필요한가

 

일반적으로 웹 애플리케이션에서 초당 많은 고객의 요청이 발생한다. 만약 코어 기능을 수행하는 서비스나 컨트롤러가

요청이 올 때마다 new를 통해 객체를 생성된다면 JVM 메모리가 Fulled 될 것이고, GC의 부담이 가중될 것이다. 이를 방지하기 위해
해당 객체가 딱 1개만 생성되어 공유되도록 하자는 아이디어에서 탄생하게 되었다.

 

📌 Java 코드를 통해 싱글톤 패턴 구현

 

static 변수를 선언하고, 메서드를 통해서만 객체를 얻도록 한다. 그리고 생성자는 private 으로 선언하여 new를 통해 객체를 얻지 못하도록 한다.

 

public class SingletonService {

    private static final SingletonService instance = new SingletonService();

    public static SingletonService getInstance() {
        return instance;
    }

    private SingletonService() {
    }

    ...

}

 

하지만 이러한 구현은 문제점이 여러 가지 있다.

 

  1. 고정적으로 들어가야 할 코드가 있어 코드가 길어진다.
  2. new를 통해 클라이언트가 직접 명시하는 형태는 구체 클래스에 의존할 수 있어 DIP를 위배한다.
    • 다른 클래스에서도 getInstance() 로 갖고 와야 하므로 의존성 주입 형태로 받을 수 없고, 구체 클래스를 사용해야 한다.
  3. 구체 클래스에 의존하게 되면 역할과 구현을 구분하지 않은 것이므로 OCP도 위배한다.
  4. 테스트 코드도 고정 코드가 생기므로 코드가 길어진다. (=테스트 용이성이 떨어짐)
  5. 구현에 의존하므로 내부 속성을 초기화하거나 변경하기 어렵다.
  6. 생성자에 private가 걸려 있어 자식 클래스(인스턴스)를 만들기 힘들다.

 

📌 스프링 컨테이너

 

그래서 객체를 스프링 빈으로 등록하고, 등록된 빈들을 싱글톤으로 관리하는 스프링 컨테이너가 필요한 것이다.

 

스프링 컨테이너는 객체의 재사용을 가능하게 한다.

 

싱글톤 객체를 생성하고 관리하는 것은 싱글톤 레지스트리가 한다.

 

    @Test
    public void springContainer() throws Exception {

        ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

        //조회: 호출할 때마다 같은 객체를 반환
        MemberService memberService1 = ac.getBean("memberService", MemberService.class);
        MemberService memberService2 = ac.getBean("memberService", MemberService.class);

        //참조값이 동일하다.
        ...
    }

 

📌 싱글톤 방식의 주의점

 

싱글톤 객체는 무상태(stateless)로 설계해야 한다.

 

  • 특정 클라이언트에 의존적인 필드가 있으면 안됨
  • 특정 클라이언트가 값을 변경할 수 있도록 하면 안됨
  • 가능한 ReadOnly
  • 필드 대신 지역 변수, 파라미터, ThreadLocal 등 자바에서 공유되지 않는 방법을 택하자

 

컴퓨팅에서 무상태 프로토콜(stateless protocol)은 어떠한 이전 요청과도 무관한 각각의 요청을 독립적인 트랜잭션으로 취급하는 통신 프로토콜
Reference: 위키백과

 

트랜잭션의 특성인 ACID 중 C는 일관성(Consistence)을 의미한다. 트랜잭션에서 LOCK을 사용하는 이유는 동시 접근을 막기 위함이다.
즉, 동시 접근은 일관성을 헤칠 가능성이 높다는 것이다.

 

싱글톤은 공유 자원이라 볼 수 있다. 공유 자원에서 상태를 유지한다는 것은 이전 작업을 고려하여 현재 작업을 수행한다는 것이다. 그렇다면 이는 동시 접근과
유사한 작업이라 볼 수 있다. 따라서, 싱글톤은 Stateless하게 유지되어야 한다는 결론을 내릴 수 있다.

 

Stateful은 공유 자원의 일관성을 헤칠 수 있다.

 

📌 싱글톤을 보장하는 원리

 

스프링은 CGLIB라는 바이트코드 조작 라이브러리를 통해 @Configuration이 선언된 설정(구성) 클래스를 상속 받은 임의의 다른 클래스를 만들고, 그 다른 클래스를
스프링 빈으로 등록한다.

 

CGLIB가 만든 가짜 클래스는 스프링 컨테이너에 등록된 Bean인지 확인하고, 등록되었다면 그것을 반환하고 등록되지 않았다면 그것을 동적으로 등록한 후 반환하는 식으로 싱글톤을 보장한다.

 

@Configuration 없이 @Bean만으로는 싱글톤을 보장하지 않는다.

 

Reference:

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8/dashboard

 

스프링 핵심 원리 - 기본편 - 인프런 | 강의

스프링 입문자가 예제를 만들어가면서 스프링의 핵심 원리를 이해하고, 스프링 기본기를 확실히 다질 수 있습니다., - 강의 소개 | 인프런

www.inflearn.com