스프링은 기본적으로 별다른 설정을 하지 않으면 내부에서 생성하는 빈 오브젝트를 모두 싱글톤으로 만든다. 여기서 싱글톤이라는 것은 디자인 패턴에서 나오는 싱그톤 패턴과 비슷한 개념이지만 구현 방법은 확연히 다르다.
싱글톤 패턴의 원리는 애플리케이션 안에 제한된 수, 대개 한 개의 오브젝트만 만들어서 사용하는 것이다. 매번 클라이언트에서 요청이 올 때마다 각 로직을 담당하는 오브젝트를 새로 만들어서 사용한다고 가정하면 요청 한 번에 5개의 오브젝트가 새로 만들어지고 초당 500개의 요청이 들어오면 초당 2500개의 새로운 오브젝트가 생성된다. 아무리 환경이 좋아졌다고 한들 이렇게 부하가 걸리면 서버가 감당하기 힘들어진다. 따라서 서버환경엣는 서비스 싱글톤의 사용이 권장된다.
하지만 디자인 패턴에 소개된 싱글톤 패턴은 사용하기가 까다롭고 여러 가지 문제점이 존재한다. 심지어 싱글톤 패턴을 피해야 할 패턴이라는 의미로 안티패턴이라고 부르는 사람도 있다.
※ 싱글톤 패턴
싱글톤 패턴은 어떤 클래스를 애플리케이션 내에서 제한된 인스턴스 개수, 이름처럼 주로 하나만 존재하도록 강제하는 패턴이다. 이렇게 하나만 만들어지는 클래스의 오브젝트는 애플리케이션 내에서 전역적으로 접근이 가능하다. 단일 오브젝트만 존재해야 하고, 이를 애플리케이션의 여러 곳에서 공유하는 경우에 주로 사용한다.
※ 싱글톤 패턴의 한계
자바에서 싱글톤을 구현하는 방법은 보통 이렇다.
- 클래스 밖에서는 오브젝트를 생성히지 못하도록 생성자를 private으로 만든다.
- 생성된 싱글톤 오브젝트를 저장할 수 있는 자신과 같은 타입의 스태틱 필드를 정의한다.
- 스태틱 팩토리 메소드인 getInstance()를 만들고 이 메소드가 최초로 호출되는 시점에서 한 번만 오브젝트가 만들어지게 한다. 생성된 오브젝트는 스태틱 필드에 저장된다. 또는 스태틱 필드의 초기값으로 오브젝트를 미리 만들어둘 수도 있다.
- 한 번 오브젝트(싱글톤)가 만들어지고 난 후에는 getInstance() 메소드를 통해 이미 만들어져 스태틱 필드에 저장해둔 오브젝트를 넘겨준다.
일반적으로 싱글톤 패턴 구현 방식에는 다음과 같은 문제가 있다.
▶ private 생성자를 갖고 있기 때문에 상속할 수 없다.
- 싱글톤 패턴은 생성자를 private로 제한한다. 오직 싱글톤 클래스 자신만이 자기 오브젝트를 만들도록 제한하는 것이다. 문제는 private 생성자를 가진 클래스는 다른 생성자가 없다면 상속이 불가능하다는 점이다. 객체지향의 장점인 상속과 이를 이용한 다형성을 적용할 수 없다.
▶ 싱글톤은 테스트하기가 힘들다.
- 싱글톤은 테스트하기가 어렵거나 테스트 방법에 따라 아예 테스트가 불가능하다. 싱글톤은 만들어지는 방식이 제한적이기 때문에 테스트에서 사용될 때 목 오브젝트 등으로 대체하기가 힘들다. 싱글톤은 초기화 과정에서 생성자 등을 통해 사용할 오브젝트를 다이내믹하게 주입하기도 힘들기 때문에 필요한 오브젝트는 직접 오브젝트를 만들어 사용할 수 밖에 없다. 이런 경우 테스트용 오브젝트로 대체하기가 힘들다.
▶ 서버환경에서는 싱글톤이 하나만 만들어지는 것을 보장하지 못한다
- 서버에서 클래스 로더를 어떻게 구성하고 있느냐에 따라서 싱글톤 클래스임에도 하나 이상의 오브젝트가 만들어질 수 있다. 따라서 자바 언어를 이용한 싱글톤 패턴 기법은 서버환경에서는 싱글톤이 꼭 보장된다고 볼 수 없다. 여러 개의 JVM에 분산돼서 설치가 되는 경우에도 각각 독립적으로 오브젝트가 생기기 때문에 싱글톤으로서의 가치가 떨어진다.
▶ 싱글톤의 사용은 전역 상태를 만들 수 있기 때문에 바람직하지 못하다
- 싱글톤은 사용하는 클라이언트가 정해져 있지 않다. 싱글톤의 스태틱 메소드를 이용해 언제든지 싱글톤에 쉽게 접근할 수 있기 때문에 애플리케이션 어디서든지 사용될 수 있고, 그러다보면 자연스럽게 전역 상태(global state)로 사용되기 쉽다. 아무 객체나 자유롭게 접근하고 수정하고 공유할 수 있는 전역 상태를 갖는 객체지향 프로그래밍에서는 권장되지 안흔 프로그래밍 모델이다.
※ 싱글톤 레지스트리
스프링은 서버환경에서 싱글톤이 만들어져서 서비스 오브젝트 방식으로 사용되는 것은 적극 지지한다. 하지만 자바의 기본적인 싱글톤 패턴의 구현 방식은 위에서 봤듯이 여러 가지 단점이 있기 때문에, 스프링은 직접 싱글톤 형태의 오브젝트를 만들고 관리하는 기능을 제공한다. 그것이 바로 싱글톤 레지스트리다.
싱글톤 레지스트리의 장점은 스태틱 메소드와 private 생성자를 사용해야 하는 비정상적인 클래스가 아니라 평범한 자바 클래스를 싱글톤으로 활용하게 해준다는 점이다. 평범한 자바 클래스라도 IoC 방식의 컨테이너를 사용해서 생성과 관계설정, 사용 등에 대한 제어권을 컨테이너에게 넘기면 손쉽게 싱글톤 방식으로 만들어져 관리되게 할 수 있다. 오브젝트 생성에 관한 모든 권한은 IoC 기능을 제공하는 애플리케이션 컨텍스트에게 있기 때문이다.
스프링은 IoC 컨테이너일 뿐만 아니라, 고전적인 싱글톤 패턴을 대신해서 싱글톤을 만들고 관리해주는 싱글톤 레지스트리라는 점을 기억해두자. 스프링이 빈을 싱글톤으로 만드는 것은 결국 오브젝트의 생성 방법을 제어하는 IoC 컨테이너로서의 역할이다.
※ 싱글톤과 오브젝트의 상태
싱글톤은 멀티스레드 환경이라면 여러 스레드가 동시에 접근해서 사용할 수 있다. 따라서 상태 관리에 주의를 기울여야 한다.
기본적으로 싱글톤이 멀티스레드 환경에서 서비스 형태의 오브젝트로 사용되는 경우에는 상태정보를 내부에 갖고 있지 않은 무상태(stateless) 방식으로 만들어져야 한다. 다중 사용자의 요청을 한꺼번에 처리하는 스레드들이 동시에 싱글톤 오브젝트의 인스턴스 변수를 수정하는 것은 매우 위험하다. 저장할 공간이 하나 뿐이니 서로 값을 덮어쓰고 자신이 저장하지 않는 값을 읽어올 수 있기 때문이다.
따라서 싱글톤은 기본적으로 인스턴스 필드의 값을 변경하고 유지하는 상태유지(stateful) 방식으로 만들지 않는다.
이를 지키지 않으면, 개발자 혼자서 개발하고 테스트할 때는 아무런 문제가 없겠지만, 서버에 배포되고 여러 사용자가 동시에 접속하면 데이터가 엉망이 돼버리는 등의 심각한 문제가 발생할 것이다.
※ 싱글톤 빈의 스코프
스프링이 관리하는 오브젝트, 즉 빈이 생성되고, 존재하고, 적용하는 범위를 스프링에서는 빈의 스코프(scope)라고 한다. 스프링 빈의 기본 스코프는 싱글톤이다. 싱글톤 스코프는 컨테이너 내에 한 개의 오브젝트만 만들어져서, 강제로 제거하지 않는 한 스프링 컨테이너가 존재하는 동안 계속 유지된다. 스프링에서 만들어지는 대부분의 빈은 싱글톤 스코프를 갖는다.
경우에 따라서는 싱글톤 외의 스코프를 가질 수 있다. 대표적으로 프로토타입(prototype) 스코프가 있다. 프로토타입은 싱글톤과 달리 컨테이너에 빈을 요청할 때마다 매번 새로운 오브젝트를 만들어준다. 그 외에도 웹을 통해 새로운 HTTP 요청이 생길 때마다 요청되는 요청(request) 스코프가 있고, 웹의 세션과 스코프가 유사한 세션(session) 스코프도 있다.
'Spring > 스프링의 이해와 원리' 카테고리의 다른 글
[스프링의 이해와 원리] 2.1 테스트 (0) | 2023.02.02 |
---|---|
스프링의 디자인 패턴 (0) | 2022.12.16 |
오브젝트와 의존관계 - 3. DB 커넥션 독립 / 디자인 패턴 (0) | 2022.07.13 |
오브젝트와 의존관계 - etc. 스프링 IoC의 용어 정리 (0) | 2022.03.14 |
오브젝트와 의존관계 - 5. 제어의 역전(IoC) / Spring's IoC (0) | 2022.03.14 |