참조 문서
https://tecoble.techcourse.co.kr/post/2020-11-07-singleton/
싱글톤(Singleton) 패턴이란?
이번 글에서는 디자인 패턴의 종류 중 하나인 싱글톤 패턴에 대해 알아보자. 싱글톤 패턴이 무엇인지, 패턴 구현 시 주의할 점은 무엇인지에 대해 알아보는 것만으로도 많은 도움이 될 것이라
tecoble.techcourse.co.kr
https://greatzzang21.tistory.com/38
Spring에서 싱글톤을 사용하는 이유
[ Spring에서 싱글톤을 사용하는 이유 ] 애플리케이션 컨텍스트에 의해 등록된 빈은 기본적으로 싱글톤으로 관리된다. 즉, 스프링에 여러 번 빈을 요청하더라도 매번 동일한 객체를 돌려준다는 것
greatzzang21.tistory.com
이번 글에서는 디자인 패턴의 종류 중 하나인 싱글톤 패턴에 대해 알아보자. 싱글톤 패턴이 무엇인지, 패턴 구현 시 주의할 점은 무엇인지에 대해 알아보는 것만으로도 많은 도움이 될 것이라 생각한다.
싱글톤 패턴이란?
싱글톤(Singleton) 패턴의 정의는 단순하다. 객체의 인스턴스가 오직 1개만 생성되는 패턴을 의미한다. 싱글톤 패턴을 사용하는 이유나 주의할 점은 조금 후에 살펴보기로 하고, 어떻게 생겼는지 그 모습을 코드로 먼저 만나보자.
싱글톤 패턴을 구현하는 방법은 여러가지가 있지만, 여기서는 객체를 미리 생성해두고 가져오는 가장 단순하고 안전한 방법을 소개하겠다.
public class Singleton {
// 자신에 참조 값을 저장할 수 있는 static 변수를 선언한다.
private static Singleton instance;
private Singleton() {
// 생성자는 외부에서 호출못하게 private 으로 지정해야 한다.
}
// 객체에 메모리에 올릴 코드를 작성하고 외부에서 접근할 수 있는 메서드를 만들어 준다.
// 단, 정적 메서드로 만든다.
public static synchronized Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
public void say() {
System.out.println("hi, there");
}
}
싱글톤 패턴의 사용하는 이유
위와 같이 인스턴스를 오직 한 개로만 가져가면 어떤 이점이 있을까?
가장 먼저 떠올릴 수 있는 이점은 아무래도 메모리 측면일 것이다. 최초 한번의 new 연산자를 통해서 고정된 메모리 영역을 사용하기 때문에 추후 해당 객체에 접근할 때 메모리 낭비를 방지할 수 있다. 뿐만 아니라 이미 생성된 인스턴스를 활용하니 속도 측면에서도 이점이 있다고 볼 수 있다.
또다른 이점은 다른 클래스 간에 데이터 공유가 쉽다는 것이다. 싱글톤 인스턴스가 전역으로 사용되는 인스턴스이기 때문에 다른 클래스의 인스턴스들이 접근하여 사용할 수 있다. 하지만 여러 클래스의 인스턴스에서 싱글톤 인스턴스의 데이터에 동시에 접근하게 되면 동시성 문제가 발생할 수 있으니 이점을 유의해서 설계하는 것이 좋다.
이 외에도 도메인 관점에서 인스턴스가 한 개만 존재하는 것을 보증하고 싶은 경우 싱글톤 패턴을 사용하기도 한다.
싱글톤 패턴의 문제점
싱글톤 패턴을 적용하면 위와 같은 효율에서의 이점을 얻을 수 있다. 하지만 싱글톤 패턴이 다음과 같은 많은 문제점들을 수반하기 때문에 trade-off를 잘 고려해야 한다.
먼저 싱글톤 패턴을 구현하는 코드 자체가 많이 필요하다. 앞서 소개한 구현 방법외에도 정적 팩토리 메서드에서 객체 생성을 확인하고 생성자를 호출하는 경우에 멀티스레딩 환경에서 발생할 수 있는 동시성 문제 해결을 위해 syncronized 키워드를 사용해야 한다.
두 번째는 테스트하기 어렵다는 것이다. 싱글톤 인스턴스는 자원을 공유하고 있기 때문에 테스트가 결정적으로 격리된 환경에서 수행되려면 매번 인스턴스의 상태를 초기화시켜주어야 한다. 그렇지 않으면 어플리케이션 전역에서 상태를 공유하기 때문에 테스트가 온전하게 수행되지 못한다.
세 번째로는 의존 관계상 클라이언트가 구체 클래스에 의존하게 된다. new 키워드를 직접 사용하여 클래스 안에서 객체를 생성하고 있으므로, 이는 SOLID 원칙 중 DIP를 위반하게 되고 OCP 원칙 또한 위반할 가능성이 높다.
이외에도 자식클래스를 만들수 없다는 점과, 내부 상태를 변경하기 어렵다는 점 등 여러가지 문제들이 존재한다. 결과적으로 이러한 문제들을 안고있는 싱글톤 패턴은 유연성이 많이 떨어지는 패턴이라고 할 수 있다.
참조. 각각의 자원이 하나의 instance를 동시에 접근 한다면?
자바에서 멀티스레드를 이용하면 여러작업을 동시에 처리할 수 있기 때문에 작업효율이 좋아집니다. 하지만 하나의 공유자원을 여러 스레드에서 동시에 접근하여 사용하게 되면 때때로 우리가 예상치 못한 결과가 나타나게 됩니다. 여기에서는 이러한 문제를 해결하기 위해 사용되는 스레드 동기화 (Thread Synchronized)에 대해서 알아보도록 하겠습니다.
1. 스레드 동기화란?
스레드 동기화는 멀티스레드 환경에서 여러 스레드가 하나의 공유자원에 동시에 접근하지 못하도록 막는것을 말합니다. 공유데이터가 사용되어 동기화가 필요한 부분을 임계영역(critical section)이라고 부르며, 자바에서는 이 임계영역에 synchronized 키워드를 사용하여 여러 스레드가 동시에 접근하는 것을 금지함으로써 동기화를 할 수 있습니다.
2. synchronized 사용방법
synchronized 키워드는 동기화가 필요한 메소드나 코드블럭앞에 사용하여 동기화 할 수 있습니다. synchronized로 지정된 임계영역은 한 스레드가 이 영역에 접근하여 사용할때 lock이 걸림으로써 다른 스레드가 접근할 수 없게 됩니다. 이후 해당 스레드가 이 임계영역의 코드를 다 실행 후 벗어나게되면 unlock 상태가 되어 그때서야 대기하고 있던 다른 스레드가 이 임계영역에 접근하여 다시 lock을 걸고 사용할 수 있게 됩니다.
lock은 해당 객체당 하나씩 존재하며, synchronized로 설정된 임계영역은 lock 권한을 얻은 하나의 객체만이 독점적으로 사용하게됩니다.
Spring 에서 싱글톤 패턴을 왜 사용할까?
1. Spring에서 싱글톤을 사용하는 이유
애플리케이션 컨텍스트에 의해 등록된 빈은 기본적으로 싱글톤으로 관리된다. 즉, 스프링에 여러 번 빈을 요청하더라도 매번 동일한 객체를 돌려준다는 것이다. 애플리케이션 컨텍스트가 싱글톤으로 빈을 관리하는 이유는 대규모 트래픽을 처리할 수 있도록 하기 위함이다.
스프링은 최초에 설계될 때 부터 대규모의 엔터프라이즈 환경에서 요청을 처리할 수 있도록 고안되었다. 그리고 그에 따라 계층적으로 처리 구조(Controller, Service, Repository 등) 가 나뉘어지게 되었다.
그런데 매번 클라이언트에서 요청이 올 때마다 각 로직을 처리하는 빈을 새로 만들어서 사용한다고 생각해보자. 요청 1번에 5개의 객체가 만들어진다고 하고, 1초에 500번 요청이 온다고 하면 초당 2500개의 새로운 객체가 생성된다. 아무리 GC의 성능이 좋아졌다 하더라도 부하가 걸리면 감당이 힘들 것이다.
그래서 이러한 문제를 해결하고자 빈을 싱글톤 스코프로 관리하여 1개의 요청이 왔을 때 여러 쓰레드가 빈을 공유해 처리하도록 하였다.
2. Spring에서 관리하는 싱글톤의 장점
Java로 기본적인 싱글톤 패턴을 구현하고자 하면 다음과 같은 단점들이 발생한다.
- private 생성자를 갖고 있어 상속이 불가능하다.
- 테스트하기 힘들다.
- 서버 환경에서는 싱글톤이 1개만 생성됨을 보장하지 못한다.
- 전역 상태를 만들 수 있기 때문에 바람직하지 못하다.
그래서 스프링은 직접 싱글톤 형태의 오브젝트를 만들고 관리하는 기능을 제공하는데, 그것이 바로 싱글톤 레지스트리(Singleton Registry) 이다. 스프링 컨테이너는 싱글톤을 생성하고, 관리하고 공급하는 컨테이너이기도 하다. 싱글톤 레지스트리의 장점은 다음과 같다.
- static 메소드나 private 생성자 등을 사용하지 않아 객체지향적 개발을 할 수 있다.
- 테스트를 하기 편리하다.
기본적으로 싱글톤이 멀티쓰레드 환경에서 서비스 형태의 객체로 사용되기 위해서는 내부에 상태정보를 갖지 않는 무상태(Stateless) 방식으로 만들어져야 한다. 만약 여러 쓰레드들이 동시에 상태를 접근하여 수정한다면 상당히 위험하기 때문이다.
직접 싱글톤을 구현한다면 상당히 많은 단점들이 있겠지만, Spring 프레임워크에서 직접 싱글톤으로 객체를 관리해주므로, 우리는 더욱 객체지향적인 개발을 할 수 있게 된 것이다.
결론
싱글톤 패턴이 무엇이고, 어떤 이점과 문제점이 있는지 살펴보았다. 오직 한 개의 인스턴스 생성을 보증하여 효율을 찾을 수 있지만 그에 못지많게 수반되는 문제점도 많다. 싱글톤 패턴은 안티패턴으로 불릴 만큼 단독으로 사용한다면 객체 지향에 위반되는 사례가 많다. 스프링 컨테이너 같은 프레임워크의 도움을 받으면 싱글톤 패턴의 문제점들을 보완하면서 장점의 혜택을 누릴 수 있다. 실제로 스프링 빈은 컨테이너의 도움을 받아 싱글톤 스콥으로 관리되고 있다.
프레임워크 도움없이 싱글톤 패턴을 적용하고 싶다면, 위에서 살펴본 장단점의 trade-off를 잘 고려하여 사용하는 것이 좋을 것이다.
'JAVA' 카테고리의 다른 글
디자인 패턴 - 어댑터 패턴 (Adapter Pattern) (1) | 2024.02.07 |
---|---|
디자인 패턴 - 빌더 패턴 (Builder Pattern) (0) | 2024.02.07 |
Java - 디자인 패턴 개념의 이해 (0) | 2024.02.07 |
Java - Timestamp <-> String , 날짜 포맷 (0) | 2024.02.01 |
Java - Thread ( Thread 클래스 이용 ) (0) | 2023.10.02 |