웹 애플리케이션과 싱글톤
[ 배경 ]
- 스프링은 태생이 기업용 온라인 서비스 기술을 지원하기 위해 탄생함
- 대부분의 스프링 애플리케이션은 웹 애플리케이션임
- 물론 웹이 아닌 애플리케이션 개발도 얼마든지 개발할 수 있음
- 웹 애플리케이션은 보통 여러 고객이 동시에 요청을 함
package hello.core.singleton;
import hello.core.AppConfig;
import hello.core.member.MemberService;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.*;
public class SingletonTest {
@Test
@DisplayName("스프링 없는 순수한 DI 컨테이너")
void pureContainer(){
AppConfig appConfig = new AppConfig();
//1. 조회 : 호출할 때마다 객체를 생성
MemberService memberService1 = appConfig.memberService();
//2. 조회 : 호출할 때마다 객체를 생성
MemberService memberService2 = appConfig.memberService();
//참조값이 다른 것을 확인
System.out.println("memberService1 = " + memberService1);
System.out.println("memberService2 = " + memberService2);
//memberService1 != memberService2
assertThat(memberService1).isNotSameAs(memberService2);
}
}
// 결과
memberService1 = hello.core.member.MemberServiceImpl@780cb77
memberService2 = hello.core.member.MemberServiceImpl@691a7f8f
Process finished with exit code 0
[ 위 코드의 문제점 ]
- 우리가 만들었던 스프링 없는 순수한 DI 컨테이너인 AppConfig는 요청을 할 때마다 객체를 새로 생성함
- 고객 트래픽이 초당 100이 나오면 초당 100개의 객체가 생성되고 소멸됨 -> 메모리 낭비가 심함!!!
[ 해결방안 ]
- 해당 객체가 딱 1개가 생성되고, 공유하도록 설계하면 됨 = 싱글톤 패턴
싱글톤 패턴
싱글톤 패턴은 "클래스의 인스턴스가 딱 1개만 생성되는 것을 보장하는 디자인 패턴"임
- 그래서 객체 인스턴스를 2개 이상 생성하지 못하도록 막아야 함
- ex) private 생성자를 사용해서 외부에서 임의로 new 키워드를 사용하지 못하도록 막아야 함
package hello.core.singleton;
public class SingletonService {
// 1. static 영역에 객체를 딱 1개만 생성해둔다.
private static final SingletonService instance = new SingletonService();
// 2. public으로 열어서 객체 인스턴스가 필요하면 이 static 메서드를 통해서만 조회하도록 허용한다.
public static SingletonService getInstance() {
return instance;
}
// 3. 생성자를 private으로 선언해서 외부에서 new 키워드를 사용한 객체 생성을 못하게 막는다.
private SingletonService() {
}
public void logic(){
System.out.println("싱글톤 객체 로직 호출");
}
}
1. static 영역에 객체 instance를 미리 하나 생성해서 올려둠
2. 이 객체 인스턴스가 필요하면 오직 getInstance() 메서드를 통해서만 조회할 수 있음
이 메서드를 호출하면 항상 같은 인스턴스를 반환함
3. 딱 1개의 객체 인스턴스만 존재해야 하므로, 생성자를 private으로 막아서 혹시라도 외부에서 new 키워드로 객체 인스턴스가 생성되는 것을 막음
@Test
@DisplayName("싱글톤 패턴을 적용한 객체 사용")
void singletonServiceTest(){
SingletonService singletonService1 = SingletonService.getInstance();
SingletonService singletonService2 = SingletonService.getInstance();
// 같은 객체 인스턴스 반환!
System.out.println("singletonService1 = " + singletonService1);
System.out.println("singletonService2 = " + singletonService2);
assertThat(singletonService1).isSameAs(singletonService2);
// 테스트 결과
singletonService1 = hello.core.singleton.SingletonService@5af97850
singletonService2 = hello.core.singleton.SingletonService@5af97850
Process finished with exit code 0
-> 자바가 뜰 때 객체 인스턴스 생성해놓은 것을 가져다 쓰는 거임!!!
테스트 결과를 보면 동일한 싱글톤 객체를 참조하고 있다는 것을 알 수 있음
참고) 싱글톤 패턴을 구현하는 방법은 여러가지가 있음
여기서는 객체를 미리 생성해두는 가장 단순하고 안전한 방법을 선택했음
- 싱글톤 패턴을 적용하면 고객의 요청이 올 때마다 객체를 생성하는 것이 아니라, 이미 만들어진 객체를 공유해서 효율적으로 사용할 수 있지만, 싱글톤 패턴은 다음과 같은 수많은 문제점들을 가지고 있음
[ 싱글톤 패턴의 문제점 ]
- 싱글톤 패턴을 구현하는 코드 자체가 많이 들어감
- 의존관계 상 클라이언트가 구체 클래스에 의존함 -> DIP를 위반함
- 클라이언트가 구체 클래스에 의존해서 OCP 원칙을 위반할 가능성이 높음
- 유연하게 테스트 하기 어려움
- 내부 속성을 변경하거나 초기화하기 어려움
- private 생성자로 자식 클래스를 만들기 어려움
- 결론적으로 유연성이 떨어짐
- 안티패턴으로 불리기도 함
'Backend' 카테고리의 다른 글
[스프링 핵심 원리 이해] 싱글톤 방식의 주의점 - Stateless로 설계하자 (0) | 2025.02.03 |
---|---|
[스프링 핵심 원리 이해] 싱글톤 컨테이너 (0) | 2025.02.03 |
[스프링 MVC 1편] 자바 백엔드 웹기술 역사 (0) | 2025.02.02 |
[스프링 MVC 1편] HTML, HTTP API, CSR, SSR (0) | 2025.02.02 |
[스프링 MVC 1편] 동시 요청 - 멀티 쓰레드 (0) | 2025.02.02 |