Spring/스프링 핵심 원리 - 기본편
[싱글톤 컨테이너] 싱글톤 방식의 주의점 - 무상태(stateless) 설계
alsruds
2023. 11. 30. 22:16
🧐강의🧐
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8/dashboard
· 싱글톤 객체는 상태를 유지(stateful)하도록 설계해서는 안됨 : 여러 클라이언트가 같은 객체를 공유하기 때문
➡️ 무상태(stateless)로 설계 !!
》 특정 클라이언트에 의존적인 필드 X
》 특정 클라이언트가 값을 변경할 수 있는 필드 X (가급적 읽기만)
》 필드 대신 자바에서 공유되지 않는 지역변수, 파라미터, ThreadLocal 등 사용
☑️ 문제 상황
· 공유 필드의 값을 특정 클라이언트가 변경할 수 있을 때
// StatefulService
public class StatefulService {
// 상태를 유지하는 필드
private int price;
public void order(String name, int price) {
System.out.println("name = " + name + "price = " + price);
// 문제가 되는 부분 !!
this.price = price;
}
public int getPrice() {
return price;
}
}
// StatefulServiceTest
class StatefulServiceTest {
@Test
void statefulServiceSingleton() {
ApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
StatefulService statefulService1 = ac.getBean(StatefulService.class);
StatefulService statefulService2 = ac.getBean(StatefulService.class);
// Thread A : A 사용자 10000원 주문
statefulService1.order("userA", 10000);
// Thread B : B 사용자 20000원 주문
statefulService2.order("userB", 20000);
// Thread A : A 사용자 주문 금액 조회
int price = statefulService1.getPrice();
System.out.println("A price = " + price);
assertThat(statefulService1.getPrice()).isEqualTo(20000);
}
static class TestConfig {
@Bean
public StatefulService statefulService() {
return new StatefulService();
}
}
}
➡️ 공유 필드인 StatefulService 의 price 필드의 값을, 특정 클라이언트가 값을 변경하면서 발생한 장애
✅ 해결 방법
· 스프링 빈은 항상 무상태(stateless)로 설계하기 !!
// StatefulService
public class StatefulService {
// 상태를 유지하는 필드
// private int price;
public int order(String name, int price) {
System.out.println("name = " + name + "price = " + price);
// 문제가 되는 부분 !!
// this.price = price;
return price;
}
// public int getPrice() {
// return price;
// }
}
// StatefulServiceTest
class StatefulServiceTest {
...
@Test
void statefulServiceSingleton() {
ApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
StatefulService statefulService1 = ac.getBean(StatefulService.class);
StatefulService statefulService2 = ac.getBean(StatefulService.class);
// Thread A : A 사용자 10000원 주문
int userAPrice = statefulService1.order("userA", 10000);
// Thread B : B 사용자 20000원 주문
int userBPrice = statefulService2.order("userB", 20000);
// Thread A : A 사용자 주문 금액 조회
// int price = statefulService1.getPrice();
System.out.println("A price = " + userAPrice);
// assertThat(statefulService1.getPrice()).isEqualTo(20000);
}
}