오늘은 Spring Framework에서 무의식적으로 사용하는 Singleton 패턴에 대해 알아보겠습니다. 이 패턴은 Head First Design Pattern의 207 ~ 229p에 기록되어 있습니다.
싱글턴 패턴이란 인스턴스가 하나뿐인 특별한 객체를 만들 수 있게 해주는 패턴입니다. 따라서 생성자가 여러번 호출해도 실제로 생성된 인스턴스는 하나입니다.
그렇다면 디자인 패턴에는 왜 싱글턴 패턴이 존재하고 Spring framework에서는 싱글턴을 사용하는지 알아볼까요?
제가 만든 예약서비스를 한 명의 고객분이 사용한다고 하면 다음과 같은 상황이 발생할 것입니다. 만약 이게 Singleton 패턴이 아닐 경우, n명의 사용자가 동시에 예약을 하면 어떻게 될까요?
다음과 같이 매번 똑같은 예약 서비스 클래스를 요청마다 호출할 것입니다. 이럴 경우 어떠한 문제가 발생할까요?
그런데 생각해보면 결국 예약서비스의 기능을 구현한 ReservationService
는 동일한 기능을 수행하는데 굳이 여러개를 생성할 필요가 있을까요? 이럴 때 사용하는 것이 바로 Singleton 패턴입니다.
Spring Framework에서는 이 Singleton 패턴을 이용해 어플리케이션 실행시 최초 1회만 인스턴스를 생성하여 Bean으로 관리하기 때문에 메모리를 효율적으로 사용할 수 있습니다.
좋은 질문입니다. 싱글턴 패턴을 공부하면서 문득 전역변수랑 싱글턴 패턴이 비슷하다는 생각이 들 수도 있습니다. 그렇다면 왜 전역변수로 사용하지 않고 싱글턴 패턴으로 사용할까요?
프로세스는 다음과 같은 구조로 메모리를 관리합니다. 여기서 전역변수는 data section에 들어가 고정적인 메모리를 할당합니다. 이는 곧 어플리케이션이 종료될 때까지 해당 영역을 차지한다는 의미입니다.
전역 변수는 어플리케이션이 종료될 때까지 해당 영역을 차지한다.
반면에 heap 영역은 유동적입니다. 서비스에서 사용할 경우 인스턴스를 생성해 heap 메모리에 할당하고 사용이 다 끝나면 메모리 해제를 하여 다시 사용할 수 있도록 관리합니다.
만약 메모리를 엄청 많이 차지하는데 정작 호출은 만번에 한 번 꼴로 되는 static 클래스가 있을 경우 얼마나 불필요할까요? 이럴 경우 싱글턴 패턴을 이용해 사용할 때만 메모리를 할당해주는 것이 보다 더 효율적이라는 것을 알 수 있습니다.
싱글턴과 전역 클래스의 차이점
사용 유무와는 다르게 전역변수는 메모리를 독과점합니다. -> 어플리케이션 서비스 실행시 기본적으로 차지하는 메모리 공간이 커집니다.
이뿐만 아니라 정적 변수의 초기화 처리, static class는 내부 메소드 또한 static이어야 한다 등 다양한 차이점이 존재합니다.
자 그럼 이제 예시를 통해 싱글톤 패턴에 대해 알아볼까요?
public class Singleton {
private static Singleton uniqueInstance;
private Singleton(){};
public static Singleton getInstance() {
if(uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
다음 예시를 볼 경우 uniqueInstance
가 null일 경우에만 새로운 인스턴스를 생성해 초기화 해줌으로서 싱글턴 패턴을 적용한 것을 알 수 있습니다.
if(uniqueInstance == null) {
uniqueInstance = new Singleton();
}
이렇게 개꿀인 싱글턴 패턴에도 문제점은 존재합니다. 바로 멀티쓰레드 환경에서의 동기화문제입니다. Spring의 @Bean을 통해 싱글턴 패턴을 적용했지만 멀티쓰레드 환경에서 고정적인 클래스가 아닌 Map, Collection
같은 가변적인 클래스를 싱글턴으로 적용하면 어떨까요?
쓰레드마다 가변적으로 변하는 @Bean으로 생성한 단일 인스턴스에 삽입,삭제, 교체를 요청할 경우 쓰레드 실행 순서에 따라 결과값이 다양하게 출력될 것입니다. 따라서 멀티 쓰레딩 환경을 고려해 개발이 필요할 경우, 동기화인 synchronize
또는 volatile
과 같은 동기화 키워드를 사용해 구현이 필요합니다.
오늘은 싱글턴 패턴에 대해 알아보았습니다. 서비스 내에서 단일 인스턴스를 유지한다는 것은 메모리 측면에서 굉장히 경제적입니다.
하지만 싱글턴 패턴의 적용은 때로는 어플리케이션 코드의 복잡성을 증가시키는 문제및 멀티스레드 환경에서의 동기화 문제를 야기할수도 있기 때문에 해당 패턴을 적용하고 싶을 경우 여러 경우의 수를 고려하시길 바랍니다.