스프링은 대부분 기업용 온라인 서비스 기술을 지원하기 위해 만들어졌습니다. 스프링 애플리케이션의 대부분은 웹 애플리케이션이며, 웹 애플리케이션은 보통 여러 고객이 동시에 요청하는 경우가 많습니다.
웹 애플리케이션의 특징은 고객의 요청이 많고 빈번합니다.
웹 애플리케이션과 싱글톤은 서로 다른 개념입니다. 각각에 대해 간단히 설명해드리겠습니다.
- 웹 애플리케이션(Web Application) 웹 애플리케이션은 인터넷 브라우저를 통해 접근할 수 있는 애플리케이션입니다. 일반적으로 웹 애플리케이션은 클라이언트-서버 아키텍처를 따르며, 웹 브라우저에서 요청을 보내고 서버에서 처리한 결과를 다시 브라우저로 응답하는 방식으로 동작합니다. 웹 애플리케이션은 주로 HTML, CSS, JavaScript 등을 사용하여 사용자 인터페이스를 제공하고, 서버 측에서는 서버 사이드 프레임워크를 사용하여 비즈니스 로직을 처리합니다.
- 싱글톤(Singleton) 싱글톤은 디자인 패턴 중 하나로, 애플리케이션에서 특정 클래스의 인스턴스를 오직 하나만 생성하고 사용하는 것을 의미합니다. 이렇게 하면 여러 곳에서 동일한 인스턴스에 접근하여 상태를 공유하고 객체 간의 일관성을 유지할 수 있습니다. 싱글톤은 주로 자원의 공유나 중복 생성을 피하기 위해 사용됩니다. 스프링 프레임워크에서는 빈(Bean)의 기본 스코프로 싱글톤을 사용하며, 컨테이너 내에서 하나의 빈 인스턴스를 생성하고 관리합니다.
따라서 웹 애플리케이션은 웹 환경에서 동작하는 애플리케이션을 의미하고, 싱글톤은 객체의 인스턴스 생성과 관리 방식 중 하나로 애플리케이션에서 공유되는 유일한 인스턴스를 생성하는 패턴입니다. 웹 애플리케이션에서도 싱글톤 패턴을 활용하여 객체를 생성하고 관리할 수 있습니다.
public class SingletonTest {
@Test
@DisplayName("스프링 없는 순수한 DI 컨테이너")
void pureContainer() {
//DI컨테이너에 있는 Bean객체의 사용이 필요하여 AppConfig 객체를 생성
AppConfig appConfig = new AppConfig();
//1. 조회: 호출할 때마다 객체를 생성하는지 조회
MemberService memberService1 = appConfig.memberService();
//2. 조회: 호출할 때마다 객체를 생성하는지 조회
MemberService memberService2 = appConfig.memberService();
//참조 값이 다른 것을 확인해 보도록 합니다.
//AppcConfig한테서 memberService를 호출을 힐 때마다 참조값(주소가) 다르게 호출
System.out.println("memberService1 = " + memberService1);
System.out.println("memberService2 = " + memberService2);
}
}
출력 : memberService1 = Spring.core.member.MemberServiceImpl@5dda768f
memberService2 = Spring.core.member.MemberServiceImpl@7a8c8dcf
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
스프링 없는 순수한 DI 컨테이너인 AppConfig는 요청을 할 때마다 객체를 새로 생성합니다. 고객 트래픽이 초당 100이 나오면 100개의 객체가 생성되고 소멸됩니다. 메모리 낭비가 심하게 발생합니다. 해결 방안으로는 해당 객체가 딱 1개만 생성되고, 공유하도록 설계하기 위해 싱글톤 패턴이 등장하였습니다.
👉 싱글톤 패턴
싱글톤 패턴(Singleton Pattern)은 소프트웨어 디자인 패턴 중 하나로, 클래스의 인스턴스가 단 하나만 생성되도록 보장하는 패턴입니다. 즉, 해당 클래스의 인스턴스를 전역적으로 접근 가능하고 재사용할 수 있습니다. 객체 인스턴스를 2개 이상 생성하지 못하도록 막아야 하며, private 생성자를 사용해서 외부에서 임의로 new 키워드를 사용하지 못하도록 막아야 합니다.
🎈 Singleton객체를 생성해보고, Test를 진행해보자
public class SingletonService {
//1. static 영역에 객체를 딱 1개만 생성해둔다.
//자기 자신을 내부에 static private으로 선언하고 가지고 있게 됨
//(클래스 레벨에 올라가게 되면서 딱 하나만 존재)
private static final SingletonService instance = new SingletonService();
//JVM이 실행될 때, 바로 싱글톤 서비스의 스태틱 영역에 자기 자신의 객체를 생성하여 인스턴스를 담음
//2. public으로 열어서 객체 인스턴스가 필요하면 이 static 메서드를 통해서만 조회하도록 허용한다.
public static SingletonService getInstance() {
return instance;
}
//3. 생성자를 private로 선언해서 외부에서 new 키워드를 사용한 객체 생성을 못하게 음
private SingletonService() {
}
public void logic() {
System.out.println("싱글톤 객체 로직 호출");
}
}
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡSingleTon 객체 생성 Test 코드ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
@Test
@DisplayName("싱글톤 패턴을 적용한 객체 사용")
void singletonServiceTest() {
SingletonService singletonService1 = SingletonService.getInstance();
System.out.println("singletonService1 = " + singletonService1);
SingletonService singletonService2 = SingletonService.getInstance();
System.out.println("singletonService2 = " + singletonService2);
assertThat(singletonService1).isSameAs(singletonService2);
// same == : 두 객체가 정확히 동일한 객체인지를 확인 / 두 객체가 메모리에서 같은 위치를 참조하는지를 비교
// equal : 두 객체의 값이 동등한지를 확인 / 객체의 실제 값을 비교하여 동등성을 판단
}
출력 : singletonService1 = Spring.core.singleton.SingletonService@55fe41ea
singletonService2 = Spring.core.singleton.SingletonService@55fe41ea
- static 영역에 객체 instance를 미리 하나 생성해서 올려둡니다. 이 객체 인스턴스가 필요하면 오직 getInstance() 메서드를 통해서만 조회할 수 있습니다.
- getInstance() 메서드를 호출하면 항상 같은 인스턴스를 반환합니다. 딱 1개의 객체 인스턴스만 존재해야 하므로, 생성자를 private으로 막아서 혹시라도 외부에서 new 키워드로 객체 인스턴스가 생성되는 것을 막습니다.
싱글톤 패턴은 다음과 같은 특징을 가집니다:
- 단일 인스턴스: 싱글톤 패턴은 클래스의 인스턴스를 하나만 생성합니다. 이 인스턴스는 애플리케이션 전체에서 공유되어 사용됩니다.
- 전역 접근: 싱글톤 인스턴스는 어디서든 전역적으로 접근할 수 있습니다. 다른 객체들은 해당 인스턴스를 사용하여 데이터를 공유하거나 해당 인스턴스의 메서드를 호출할 수 있습니다.
- 지연 로딩: 일반적으로 싱글톤 인스턴스는 처음 필요할 때 생성됩니다. 이를 지연 로딩(Lazy Loading)이라고 합니다. 따라서 인스턴스 생성 비용을 줄이고 필요할 때만 생성하여 자원을 효율적으로 관리할 수 있습니다.
싱글톤 패턴은 다양한 상황에서 활용될 수 있습니다. 예를 들어, 데이터베이스 연결, 로깅, 캐시 등과 같이 애플리케이션 전체에서 공유되어야 하는 객체들을 관리하기 위해 싱글톤 패턴이 사용될 수 있습니다.
싱글톤 패턴을 구현하기 위해서는 다양한 방법이 있지만, 대표적으로는 다음과 같은 방식을 사용합니다:
- 정적 멤버 변수: 클래스 내부에 정적 멤버 변수를 선언하고, 해당 변수에 인스턴스를 생성하여 저장합니다.
- 지연 초기화: 인스턴스 생성을 처음 요청받을 때까지 지연시키고, 해당 요청 시에만 인스턴스를 생성합니다.
- 스레드 안전성: 멀티스레드 환경에서의 동시 접근 문제를 해결하기 위해 동기화 처리를 추가합니다.
싱글톤 패턴의 단점으로는
1. 싱글톤 패턴을 구현하는 코드가 길어지게 됩니다.
2. 의존 관계상 클라이언트가 구체 클래스에 의존합니다. (DIP를 위반하게 됩니다.)
3. 클라이언트가 구체 클레스에 의존해서 OCP 원칙을 위반할 가능성이 높고, 테스트 하기가 어렵습니다.
4. 내부 속성을 변경하거나 초기화 하기 어렵고, private 생성자로서 자식 클래스를 만들기 어렵습니다. 그 결과 유연성이 떨어지며, 안티 패턴으로 불리기도 합니다.
📄 싱글톤 컨테이너
스프링 컨테이너는 싱글톤 패턴의 문제점을 해결하면서 객체 인스턴스를 싱글톤(1개만 생성)으로 관리합니다. 스프링 빈이 바로 싱글톤으로 관리되는 빈을 의미합니다.
싱글톤 컨테이너(Singleton Container)는 객체를 싱글톤 패턴으로 관리하는 디자인 패턴과 관련된 개념입니다. 싱글톤 패턴은 애플리케이션 전체에서 단 하나의 인스턴스만 생성하여 공유하고, 이를 통해 객체 생성과 메모리 사용을 최적화하는 패턴입니다. 싱글톤 객체를 생성하고 관리하는 기능을 싱글톤 레지스트리라고 합니다.
싱글톤 컨테이너는 이러한 싱글톤 패턴을 구현하는데 사용되는 컨테이너입니다. 스프링 컨테이너는 싱글톤 컨테이너의 역할을 합니다. 주로 의존성 주입(Dependency Injection)을 사용하는 프레임워크나 라이브러리에서 제공되며, 객체의 인스턴스를 관리하고 필요한 곳에서 공유할 수 있도록 지원합니다.
싱글톤 컨테이너의 주요 특징은 다음과 같습니다:
- 객체 인스턴스의 단일성: 싱글톤 컨테이너는 애플리케이션 전체에서 단 하나의 인스턴스만 생성하여 사용합니다. 이를 통해 여러 곳에서 동일한 객체를 공유하고, 객체 생성과 소멸에 따른 오버헤드를 줄일 수 있습니다.
- 라이프사이클 관리: 싱글톤 컨테이너는 객체의 라이프사이클을 관리합니다. 객체의 생성, 초기화, 소멸 등의 작업을 컨테이너가 담당하므로, 개발자는 객체의 라이프사이클에 대해 직접 관리할 필요가 없습니다.
- 의존성 주입: 싱글톤 컨테이너는 의존성 주입(Dependency Injection)을 통해 객체 간의 의존성을 관리합니다. 다른 객체에 대한 의존성을 주입받을 수 있으며, 이를 통해 코드의 유연성과 테스트 용이성을 개선할 수 있습니다.
싱글톤 컨테이너는 대표적으로 스프링(Spring) 프레임워크에서 제공하는 스프링 컨테이너가 있습니다. 스프링 컨테이너는 객체의 싱글톤 패턴을 지원하며, @Component, @Service, @Repository 등의 어노테이션을 사용하여 싱글톤 빈을 등록하고 관리할 수 있습니다. 이를 통해 스프링은 객체의 라이프사이클을 관리하고, 필요한 곳에서 싱글톤 빈을 주입하여 사용할 수 있도록 지원합니다.
'[ BACKEND] > Spring' 카테고리의 다른 글
[SPRING] Web Application (0) | 2023.07.31 |
---|---|
[SPRING] 싱글톤 방식의 주의점 💥 (0) | 2023.07.25 |
[SPRING] Bean 메타 정보 ( Bean Factory & Application Context) (0) | 2023.07.23 |
[SPRING] 스프링 빈 조회 - 동일한 타입이 2개 이상 (0) | 2023.07.22 |
[SPRING] 스프링 컨테이너와 Bean 조회 (0) | 2023.07.21 |