싱글톤 패턴이든, 스프링 같은 싱글톤 컨테이너를 사용하든, 객체 인스턴스를 하나만 생성해서 공유하는 싱글톤 방식은 여러 클라이언트가 하나의 같은 객체 인스턴스를 공유하기 때문에 싱글톤 객체는 상태를 유지(stateful)하게 설계하면 안된다.
무상태(stateless)로 설계해야 한다!! (Restful API에 대해 알아볼 때 본 그 stateless!!)
- 특정 클라이언트에 의존적인 필드가 있으면 안된다.
- 특정 클라이언트가 값을 변경할 수 있는 필드가 있으면 안된다.
- 가급적 읽기만 가능해야 한다.
- 가급적이면 수정을 하면 안된다는 의미
- 필드 대신에 자바에서 공유되지 않는 지역변수, 파라미터, ThreadLocal 등을 사용해야 한다.
참고 : docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/ThreadLocal.html
스프링 빈의 필드에 공유 값을 설정하면 큰 장애가 발생할 수 있다!!!
예시를 보며 살펴보자!
위와 같이 단순히 사용자명과 주문금액을 받아 주문을 생성하는 서비스가 있다고 가정하겠다.
이를 임시 설정파일 TestConfig를 통하여 스프링 컨테이너에 등록시킨다. ==> Singleton 방식 사용
그 후 서로 다른 객체에서 빈을 꺼내면 같은 인스턴스 객체가 반환될 것이다. 이것이 Singleton이다
이제 문제점을 확인해보자. 각각 다른 사용자가 다른금액의 물품을 주문하였다.
그 후 사용자 A가 주문한 금액을 출력해보면 10000원이 나와야하지만 사용자 B, 즉 다른 Thread에 의해 이미 값이 변경되었기 때문에 20000원이 출력된다.
이것이 바로 동시성 문제이다. 이것이 multi Thread로 생성되는 등 더욱 복잡해진다면 문제는 겉잡을 수 없이 커진다.
동시성 : 동시성 문제란 두 개 이상의 세션이 공통된 자원에 대해 모두 읽고 쓰는 작업(Read→Write) 을 하려고 하는 경우 발생할 수 있는 문제를 말한다.
위 코드는 싱글톤의 주의점을 살펴보기 위함으로 실제 Thread를 사용하진 않았다.
StatefulService의 price필드는 공유 필드인데, 특정 클라이언트가 값을 변경한다.
공유 필드는 굉장히 조심해야함. 스프링 빈은 항상 무상태(Stateless)로 설계
다음과 같이 바로 공유되는 필드를 없애고 바로 return시켜 지역변수로써 사용하게 한다.
반환된 값을 각각의 지역변수에 저장하여 사용하면 정상적으로 고객에 따른 주문금액을 뽑아낼 수 있다.
필드 대신에 자바에서 공유되지 않는 지역변수, 파라미터, ThreadLocal 등을 사용해야 한다.
굉장히 중요한 개념 숙지하자!
반응형
'Spring > Spring Core' 카테고리의 다른 글
컴포넌트 스캔과 의존관계 자동 주입 (0) | 2021.03.13 |
---|---|
@Configuration (0) | 2021.03.12 |
Singleton Pattern & Singleton Container (0) | 2021.03.11 |
Web Application & Singleton (0) | 2021.03.11 |
스프링 빈 설정 메타 정보 - BeanDefinition (0) | 2021.03.10 |