[2/3] Spring과 싱글톤 패턴 - 싱글톤 패턴의 주의점
개요
Spring이 제공하는 방식의 싱글톤 패턴은 많은 단점들을 없애주고 아주 효율적으로 사용할 수 있게 만들어준다.
하지만 이것을 사용함에 있어서도 주의해야 할 점이 있다. 실무에서도 한 번씩은 꼭 발생할정도로 중요하고 기억해야 할 내용이기에 잘 익혀두고 항상 주의해야 할 것이다.
싱글톤 패턴의 주의점
싱글톤 패턴은 하나의 인스턴스를 만들어서 여러 클라이언트 호출에 대응하는 방식이다. 이렇게 하면 인스턴스가 호출 때마다 생성되고 사라지는 것이 아니라 처음 생성된 하나의 인스턴스로만 대응할 수 있기 때문에 꽤나 효울적인 결과를 가져올 수 있다.
주의해야할 점은 싱글톤 패턴은 여러 클라이언트가 하나의 인스턴스를 공유해서 사용하기 때문에 인스턴스 내부에 값을 변경할 수 있는 여지를 주면 안된다는 것이다.
내부에서 값이 변하는 형태가 존재한다면 여러 클라이언트가 보내는 요청에서 이전 클라이언트가 보낸 요청에 대한 값이 다른 클라이언트에게 보여질 수도 있는 상황이 발생한다. 이러한 상황을 방지하기 위해서 싱글톤 패턴의 인스턴스는 항상 무상태(Stateless)를 유지해야 한다.
말로는 조금 어려우니 바로 코드로 살펴보도록 하자. 먼저 테스트를 위한 클래스를 만들어주자.
public class StatefulService {
private int price;
public StatefulService(String name, int price) {
this.price = price; // 문제 지점
}
public int getPrice() {
return price;
}
}
상품을 주문하는 코드라고 가정해보자. 상품을 주문하기 위해서 상품이름과 가격을 입력한다.
그러면 생성자에서 price값이 변하게 되고 getPrice로 입력한 가격을 가져올 수 있다.
만약 A사용자가 10,000원을 입력하고 직후에 B사용자가 20,000원을 입력한 후에 A사용자가 입력한 가격을 가져오면 어떻게 될까? 테스트코드로 살펴보자.
class StatefulServiceTest {
@Test
void 싱글톤_패턴_상태유지() throws Exception {
// given
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
StatefulService statefulService1 = ac.getBean("statefulService", StatefulService.class);
StatefulService statefulService2 = ac.getBean("statefulService", StatefulService.class);
// when
statefulService1.order("UserA", 10000);
statefulService2.order("UserB", 20000);
int price = statefulService1.getPrice();
// then
System.out.println("price = " + price);
Assertions.assertThat(price).isNotEqualTo(10000);
}
}
A사용자가 10,000원을 입력하고 직후에 B사용자가 20,000원을 입력한 뒤 A사용자의 price값을 가져와보니 20,000원이 출력되는 것을 볼 수 있다.
실제로는 이것보다 훨씬 복잡한 형태에서 문제가 발생하기 때문에 문제를 찾아내기도 쉽지 않을 것이다.
이러한 문제점 때문에 싱글톤은 항상 무상태로 설계를 해야하고 공유필드는 항상 조심해야 한다.
문제를 발견했으니 무상태를 유지하도록 코드를 변경해서 다시 테스트해보자.
public class StatefulService {
public int order(String name, int price) {
return price;
}
}
order로 받은 price값을 유지하는 것이 아니라 바로 return받아서 필요 시 사용할 수 있게 수정했다.
테스트코드를 만들어보자
@Test
void 싱글톤_패턴_무상태() throws Exception {
// given
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
StatefulService statefulService1 = ac.getBean("statefulService", StatefulService.class);
StatefulService statefulService2 = ac.getBean("statefulService", StatefulService.class);
// when
int userAPrice = statefulService1.order("UserA", 10000);
int userBPrice = statefulService2.order("UserB", 20000);
// then
System.out.println("price = " + userAPrice);
Assertions.assertThat(userAPrice).isEqualTo(10000);
}
이전과는 다르게 order를 넣고 지역변수로 바로 받아서 사용하기 때문에 B사용자가 값을 입력해도 A사용자의 값이 변하지 않고 사용되는 것을 볼 수 있다.
스프링을 사용하면 스프링 빈에 등록되는 모든 빈들이 싱글톤 형태로 괸리된다. 물론 싱글톤이 아닌 호출 때마다 새로 생성하는 방식으로 관리될 수도 있지만 보통은 싱글톤의 형태로 관리를 한다. 그렇기 때문에 스프링 빈에 등록되는 모든 빈들은 무상태를 유지하도록 설계해야 위와 같은 문제점들을 사전에 예방할 수 있다.
About
해당 게시글은 김영한님의 '스프링 핵심 원리 - 기본편'을 공부하고 작성한 내용입니다.
해당 게시글의 소스코드는 https://github.com/ssung0810/spring_study/tree/main/singleton에서 볼 수 있습니다.
'Spring > Core' 카테고리의 다른 글
스프링과 데이터베이스[1] - JDBC와 커넥션 풀 (0) | 2022.11.08 |
---|---|
스프링 주입의 다양한 방법들 (0) | 2022.06.13 |
스프링 빈 등록의 다양한 방법들 (0) | 2022.06.09 |
[3/3] Spring과 싱글톤 패턴 - @Configuration의 비밀 (0) | 2022.06.07 |
[1/3] Spring과 싱글톤 패턴 (0) | 2022.06.03 |
댓글
이 글 공유하기
다른 글
-
스프링 주입의 다양한 방법들
스프링 주입의 다양한 방법들
2022.06.13 -
스프링 빈 등록의 다양한 방법들
스프링 빈 등록의 다양한 방법들
2022.06.09 -
[3/3] Spring과 싱글톤 패턴 - @Configuration의 비밀
[3/3] Spring과 싱글톤 패턴 - @Configuration의 비밀
2022.06.07 -
[1/3] Spring과 싱글톤 패턴
[1/3] Spring과 싱글톤 패턴
2022.06.03