싱글톤

2023. 7. 14. 03:45Spring/[inflearn]스프링 핵심 원리 - 기본편

◎ 웹 애플리케이션과 싱글톤

-> 대부분의 스프링 애플리케이션은 웹 애플리케이션이다. 웹 애플리케이션은 일반적으로 여러 고객이 동시에 요청을 한다.

-> 사용자가 memberService를 요청하면 요청할 때마다 컨테이너에서 MemberService객체를 생성하여 memberService를 반환하게 된다.

public class SingletonTest {

    @Test
    @DisplayName("스프링 없는 순수 DI 컨테이너")
    void pureContainer(){
        AppConfig appConfig = new AppConfig();

        // 1. 조회 : 호출할 때마다 객체 생성
        MemberService memberService1 = appConfig.memberService();

        // 2. 조회 : 호출할 때마다 객체 생성
        MemberService memberService2 = appConfig.memberService();

        // memberService1 != memberService2임을 확인
        System.out.println("memberService1 = " + memberService1);
        System.out.println("memberService2 = " + memberService2);

        Assertions.assertThat(memberService1).isNotSameAs(memberService2);
    }
}

- 이 코드는 스프링을 사용하지 않은 순수 DI를 사용한 코드다.

- 결과를 보면 memberService1과 memberService2가 서로 다른 객체임을 알 수 있다. 즉, memberService를 요청할 때마다 새로운 객체를 생성해서 반환하는 것이다.

- 만약 요청이 많아진다면 많아진 만큼 객체를 생성해야 하기 때문에 메모리 낭비가 발생한다.

- 해결 방법은 요청하는 객체가 단 1번만 생성되고 공유하도록 설계한다. 싱글톤 패턴을 사용한다.

 

◎ 싱글톤 패턴

-> 클래스의 인스턴스가 딱 1개만 생성되는 것을 보장하는 패턴이다.

-> 객체 인스턴스를 2개 이상 만들지 못하도록 만든다. 

-> private 생성자를 사용해 외부에서 new 키워드를 사용하지 못하도록 막는다.

 

-> 싱글톤 패턴 적용

public class SingletonService {

    // 1. static 영역에 객체를 1개만 생성한다.
    private static final SingletonService instance = new SingletonService();

    // 2. public으로 지정하여 객체 인스턴스가 필요한 경우 이 메서드를 통해서만 조회하도록 한다.
    public static SingletonService getInstance() {
        return instance;
    }

    // 3. 생성자를 private으로 지정하여 외부에서 new키워드로 객체 생성하는 것을 막는다.
    private SingletonService(){

    }

    public void logic(){
        System.out.println("싱글톤 객체 호출");
    }
}

- static 영역에 객체 인스턴스를 하나 생성하고, 이 객체 인스턴스가 필요하면 getinstance()메서드를 통해서만 조회할 수 있디. 이 메서드를 호출하면 항상 같은 instance를 호출하게 된다.(private static final SingletonService instance = new SingletonService();를 통해 instance는 static 영역에 1개만 생성되도록 하였기 떄문)

- 단 1개의 인스턴스만 존재해야 하기 때문에 생성자를 private으로 하여 외부에서 인스턴스를 생성하는 것을 막는다.

 

-> 싱글톤 패턴 테스트

@Test
    @DisplayName("싱글톤 패턴을 적용한 객체 사용")
    public void SingletonServiceTest(){
        // 1. 조회 : 호출할 때마다 같은 객체 반환
        SingletonService singletonService1 = SingletonService.getInstance();

        // 2. 조회 : 호출할 때마다 같은 객체 반환
        SingletonService singletonService2 = SingletonService.getInstance();

        // singletonService1 == singletonService2임을 확인
        System.out.println("singletonService1 = " + singletonService1);
        System.out.println("singletonService2 = " + singletonService2);

        Assertions.assertThat(singletonService1).isSameAs(singletonService2);
    }

-> 결과

- 싱글톤을 적용하고 테스트를 해보면 같은 객체를 반환한다는 것을 알 수 있다.

 

-> 이렇게 싱글톤 패턴을 적용하면 고객의 요청이 들어올 때 마다 객체를 생성하지 않고, 이미 만들어진 객체를 공유하여 메모리를 효율적으로 사용할 수 있다.

-> 그러나 싱글톤에는 여러 문제점이 있다.

 

◎ 싱글톤의 문제점

-> 싱글톤을 구현하는 코드 자체가 많이 들어간다. - 싱글톤을 구현하기 위해 코드를 새로 작성해야 했다.

-> 의존관계상 클라이언트가 구체 클래스에 의존한다. 싱글톤을 적용한 구체 클래스(SingletonService)를 의존한다. - DIP를 위반한다.

-> 클라이언트가 구체 클래스에 의존하기 떄문에 OCP원칙을 위반할 가능성이 있다.

-> 내부 속성을 변경하거나 초기화하기 어렵다.

-> private 생성자로 자식 클래스를 만들기 어렵다.

-> 이런 문제로 인해 유연성이 떨어진다.

 

☆ 참고

[인프런]스프링 핵심 원리 - 기본편

'Spring > [inflearn]스프링 핵심 원리 - 기본편' 카테고리의 다른 글

@Configuration & 싱글톤  (0) 2023.07.14
싱글톤 컨테이너  (0) 2023.07.14
BeanDefinition  (0) 2023.07.13
BeanFactory & ApplicationContext  (0) 2023.07.13
스프링 컨테이너와 스프링 빈  (0) 2023.07.12