development/design pattern

[Java] Design-Pattern Singleton

bokshiri 2024. 9. 19. 14:35

Java, Spring Framework (Spring Boot) 기반의 개발을 진행할 때 유용하게 사용할 수 있는 디자인 패턴에 대해 정리할 것이다.

첫 시작은 대망의 싱글톤 패턴이다. 스프링은 Ioc 컨테이너에서 관리하는 객체를 싱글톤으로 관리한다. 뿐만 아니라, 환경 설정을 할 때 사용하는 @Configuration 어노테이션이 붙은 클래스 역시 Spring CGlib Proxy 방식을 적용하여 싱글톤으로 관리한다. 도대체 싱글톤이 무엇이고, 왜 스프링은 시키지도 않은 싱글톤 방식을 Default로 적용하는걸까?

 

우선 싱글톤이 무엇인지 정리한 후 왜, 그리고 어떻게 사용하는지 순차적으로 살펴보자.


 싱글톤이란 무엇인가?

  특정 클래스에 대해 객체(인스턴스)를 단 한 개만 생성하여 관리하는 것을 의미한다. 최초에 생성자를 호출하여 객체가 생성되면, 이 후에 같은 요청이 들어오더라도 객체를 생성하지 않고 기 생성된 객체의 주소값을 반환하는 것이다.


JVM이 사용하는 메모리 영역은 크게 세 가지로 분류할 수 있다. 이를 T 메모리 구조라고 부른다.

 

  • Static (Method) Area : 특정 클래스의 사용 시점에 패키지, 클래스 정보가 로드된다. 소스 단에서 static 멤버를 선언하였다면, 해당 값들도 함께 로드된다.
  • Stack Area : 변수들이 저장되는 공간이다. 원시 타입의 경우에는 실제 값이 함께 저장된다. ex) int a = 5; 에서 변수 a와 실제값 5는 Stack 영역에 함께 저장된다. 참조 타입의 경우에는 Heap Area에 생성된 객체의 참조 주소값이 변수와 함께 저장된다.
  • Heap Area : 인스턴스 (객체)가 저장되는 공간이다. 생성자를 호출해서 객체를 생성하면, Heap영역에 객체가 올라가며 해당 객체의 참조 주소값과 함께 변수가 Stack 영역에 저장된다.

 

스프링에서 관리하는 빈이 싱글톤이 아니라면 다음과 같은 문제가 발생할 것이다.

1. 이벤트나 프로모션 등으로 인해 다량의 트래픽이 몰리게 된다.

2. 자바 애플리케이션은 요청마다 생성자를 호출하여 객체를 생성한 후 로직을 수행한다.

3. 지나친 Heap 메모리 영역 사용으로 인해 서버가 다운된다.

 

객체를 매번 생성하여 사용한다는 것은 하드웨어에 무리를 줄 수 있고, 전체적인 애플리케이션의 퍼포먼스에도 영향을 줄 수 있다. 따라서 스프링에서는 안정적인 서비스를 제공하기 위해 Ioc 컨테이너에서 관리하는 객체를 싱글톤으로 유지하는 것이다.

 

https://siyoon210.tistory.com/124

 

자바(JVM)의 메모리 사용 방식 (T 메모리 구조)

자바(JVM)의 메모리 사용 방식 프로그램을 실행시킬 때 CPU와 조화로운(?) 작업을 위해서 저장장치에 있던 내용들이 메모리에 올리가기 시작합니다. 메모리에 데이터들이 어떤 방식으로 올라가는

siyoon210.tistory.com

https://velog.io/@jaeeunxo1/spring-singleton

 

스프링 핵심원리 - 싱글톤패턴

웹 애플리케이션과 싱글턴 1. 싱글턴 패턴(Singleton pattern) > 소프트웨어 디자인 패턴에서 싱글턴 패턴을 따르는 클래스는, 생성자가 여러 차례 호출되더라도 실제로 생성되는 객체는 하나이고 최

velog.io

 

이제, 싱글톤 클래스를 만들고 테스트 코드로 검증해보자.

public class SingletonObject {

    private final String name;
    private static final SingletonObject object = new SingletonObject();

    private SingletonObject() {
        this.name = "SingleTon Object 입니다...";
    }

    public static SingletonObject getInstance() {
        return object;
    }

    public String getName() {
        return this.name;
    }

}

static 필드로 객체를 생성한 후, 생성자는 외부에서 객체를 생성할 수 없도록 private으로 선언한다. 인스턴스에 접근하기 위한 통로는 getInstance() 메서드로 제한하고, 해당 메서드에서는 이미 생성된 인스턴스를 반환한다.

 

@DisplayName("객체 주소값이 모두 일치하는지 검증한다.")
@Test
void validAddress() {
    String message = "SingleTon Object 입니다...";

    SingletonObject object = SingletonObject.getInstance();
    SingletonObject object2 = SingletonObject.getInstance();
    SingletonObject object3 = SingletonObject.getInstance();

    log.info("first object address - {}", object);
    log.info("second object address - {}", object2);
    log.info("third object address - {}", object3);

    assertAll(
            () -> assertThat(object).isEqualTo(object2),
            () -> assertThat(object2).isEqualTo(object3),
            () -> assertThat(object.getName()).isEqualTo(message)
    );
}

 

객체의 주소값을 확인해보면 모두 동일한 것을 확인할 수 있다.

'development > design pattern' 카테고리의 다른 글

[Java] Design-Pattern Observer  (3) 2024.09.24