일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- text
- iterator
- jpa
- java
- foreach
- 데이터 타입
- Synchronized
- 동시성 문제
- Lock
- 자바
- Varchar
- 백엔드
- iterable
- Locking Read
- reflection
- 가비지 컬렉션
- 가비지 컬렉터
- 동시성
- CAS
- MVCC
- 스프링
- db
- gc
- MySQL
- Atomic Type
- Di
- Today
- Total
과정을 즐기자
스프링 빈을 싱글톤으로 유지하는 방법과 이유 본문
싱글톤이란 클래스의 인스턴스를 하나만 생성하는 것을 말합니다.
스프링 컨테이너가 관리하는 스프링 빈들은 기본적으로 싱글톤으로 관리됩니다.
이번 글에서는 스프링에서 객체를 싱글톤으로 등록하는 방법, 싱글톤으로 관리하는 이유, 싱글톤 사용시 주의할 점,
싱글톤을 직접 만드는 것의 단점과 스프링이 관리해주는 이유에 대해 알아보겠습니다.
📕 싱글톤으로 등록하는 방법
스프링 프레임워크를 사용하다보면 크게 2가지 방법으로 스프링 빈을 등록합니다.
등록하는 방법부터 간단하게 살펴 보겠습니다.
1. @Component 어노테이션 기반
@Component, @Controller, @Service, @Repository와 같은 어노테이션으로 등록하고 가장 많이 사용하는 방법입니다.
@Component의 메타 어노테이션 중에서 @Target을 보면 대상이 클래스, 인터페이스, Enum에 붙일 수 있습니다.
이러한 방식으로 등록한 클래스는 컴포넌트 스캔의 대상이 되며 기본적으로 런타임 시점에 하나의 객체만 생성되고
스프링 컨테이너에 의해 관리됩니다. 개발자가 직접 생성한 클래스를 스프링 빈으로 관리하고 싶을 때 자주 사용하는 방법입니다.
2. @Configuration, @Bean 사용하여 등록하기
@Configuration, @Bean을 사용해서 스프링 빈으로 등록할 수 있습니다.
@Tartget을 보면 메소드와 어노테이션에 적용할 수 있다는 것을 알 수 있습니다.
이 방식은 개발자가 직접 조정할 수 없는 외부 라이브러리와 같이 이미 등록되어 있는 클래스를 사용하기 위해 적용합니다.
물론 보통 이렇다는 것이고 개발자가 생성한 클래스도 이러한 방식으로 생성할 수 있습니다.
📘 싱글톤으로 관리하는 이유
그렇다면 왜 기본적으로 싱글톤으로 관리하는 것일까요?
만약 싱글톤으로 생성하지 않고 매 요청마다 객체를 생성한다면 어떨까요?
요청을 처리하는 비용보다 객체를 생성하고 파괴하는 비용이 더 클수도 있으며 많은 객체들이 생성되면
불필요하게 메모리를 많이 사용할 수 있습니다.
요청을 처리하기 위한 생성한 클래스들을 스프링 빈으로 관리하여 싱글톤으로 생성한다면 여러 요청이 오더라도
같은 객체를 사용하여 처리합니다. 이렇게 하면 적은 메모리를 사용하여 많은 요청을 처리할 수 있습니다.
📒 싱글톤 객체 사용시 주의할 점
싱글톤은 적은 메모리를 사용하여 많은 요청을 처리할 수 있도록 하였지만 주의해야할 점이 있습니다.
멀티 쓰레드 환경에서 싱글톤 객체를 사용하게 되므로 동시성 문제에 대해 항상 주의해야 합니다.
스프링 컨테이너는 SingletonBadExample 클래스에 대한 싱글톤 객체를 생성하고 관리합니다.
이때 많은 요청이 동시에 들어온다면 numbers 필드를 여러 쓰레드가 동시에 접근하게 됩니다.
ArrayList는 내부적으로 동기화를 보장하지 않기 때문에 이러한 경우 원하는 대로 동작하지 않을 수 있습니다.
그렇기 때문에 싱글톤 객체를 사용할 때는 되도록이면 필드 값을 가지지 않도록 해야합니다.
이것을 싱글톤 패턴에서 Stateless (무상태) 하도록 설계해야 한다고도 말합니다.
물론 java.util.concurrent 패키지의 클래스들을 활용하여 동기화를 보장할 수도 있지만 이것도 역시 한 서버 내에서만
적용되는 것이므로 가능하면 Stateless 하도록 하고 상태는 DB를 통해서 유지하도록 하는 것이 좋을 것 같습니다.
📗 싱글톤을 직접 만드는 것의 단점과 스프링이 관리해주는 이유
싱글톤으로 등록하는 방법, 이유, 주의할 점에 대해 알아보았습니다.
조금만 더 나아가 싱글톤을 직접 만드는 것의 단점과 스프링에서 관리해주는 이유에 대해 알아보겠습니다.
싱글톤 객체를 아래와 같은 다양한 방식으로 개발자가 직접 만들 수도 있습니다.
- 이른 초기화를 하는 가장 단순한 방식
- 이른 초기화를 하지 않고 멀티 쓰레드 환경에서 안전한 방식
- Enum을 사용하여 완전한 싱글톤을 보장하는 방식
이렇게 만들었을 때의 문제점은 무엇일까요? 일단 코드 자체가 많아지게 됩니다.
이것보다 더 큰 2가지 문제점이 있는데 바로 의존관계 주입이 어렵다는 점과 테스트가 어려워진다는 점입니다.
의존 관계 주입은 스프링 프레임워크를 사용하는 가장 큰 이유 중 하나로 런타임 시점에 객체들의 관계를 주입해줍니다.
SingleTon1 클래스, SingleTon2Interface 인터페이스, SingleTon2 클래스가 있다고 해봅시다.
SingleTon1 클래스에 SingleTon2Interface 인터페이스를 주입하고 런타임 시점에 SingleTon2를 사용하고 싶더라도
이러한 방식으로는 인터페이스에 의존하지 않고 구체적인 클래스인 SingleTon2에 의존할 수 밖에 없습니다.
- SingleTon1
- SingleTon2 인터페이스
- SingleTon2
싱글톤 패턴은 테스트를 하기 어렵다는 단점도 있는데 싱글톤이어서 객체를 따로 생성할 수 없기 때문입니다.
물론 생성자가 private이어도 리플렉션을 활용하면 객체를 생성할 수는 있지만 바람직한 방법은 아닌 것 같습니다.
또 Enum의 경우는 리플렉션으로도 객체를 생성할 수 없는 완전한 싱글톤입니다.
이러한 이유로 인해 스프링 컨테이너가 직접 객체를 관리하고 싱글톤을 유지해줍니다.
스프링 컨테이너 내부에는 싱글톤 레지스트리라는 데이터 구조가 존재하며 싱글톤 빈 인스턴스를 저장하는 Map과
같은 형태로 존재합니다. 이 레지스트리는 각 빈의 이름, ID와 그에 해당하는 인스턴스를 매핑하여 관리합니다.
이러한 방식으로 private 생성자로 객체 생성을 막지 않고 스프링 컨테이너를 활용할 때는 싱글톤으로 관리되도록 해주는 것입니다.
'Spring' 카테고리의 다른 글
Spring에서 공통 로직을 처리하는 방법 - 로그인 방식으로 알아보기 (0) | 2023.12.23 |
---|---|
Spring에서 멀티 쓰레드 비동기 프로그래밍 해보기 (2) | 2023.12.08 |
바이너리 데이터를 처리하는 방법 (0) | 2023.10.28 |
Spring과 Node.js 비교하기 (0) | 2023.08.26 |
내부 클래스를 스프링 빈으로 등록할 수 있을까? (0) | 2023.07.07 |