-
Spring 기본 15 - 싱글톤 방식의 주의점FrameWork/Spring&Spring-boot 2023. 11. 13. 09:25
싱글톤 방식의 주의점
- 싱글톤 패턴이든, 스프링 같은 싱글톤 컨테이너를 사용하든, 객체 인스턴스를 하나마 생성해서 공유하는 싱글톤 방식은 여러 클라이언트가 하나의 같은 객체 인스턴스를 공유하기 때문에 싱글톤 객체는 상태를 유지(stateful)하게 설계하면 안된다.
- ⭐무상태(stateless)로 설계해야 한다.
- 특정 클라이언트에 의존적인 필드가 있으면 안된다.
- 특정 클라이언트가 값을 변경할 수 있는 필드가 있으면 안된다.
- 가급적 읽기만 해야 한다.
- 필드 대신에 자에서 공유되지 않는, 지역변수, 파라미터, ThreadLocal등을 사용해야 한다.
- 스프링 빈 필드에 공유 값을 설정하면 정말 ⛔큰 장애가 발생할 수 있다.
상태를 유지할 경우 발생하는 문제점 예
클라이언트 의도
주문한다음에 price에 값을 저장을 한다.
StatefulService.java
package com.hello.core.singleton; 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.java
class StatefulServiceTest { @Test void statefulServiceSingleton(){ ApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class); StatefulService statefulService1 = ac.getBean(StatefulService.class); StatefulService statefulService2 = ac.getBean(StatefulService.class); //1.TreadA: A사용자가 10000원 주문 statefulService1.order("userA",10000); //2.TreadA: B사용자가 10000원 주문( A사용자가 금액을 주문하기 전에) statefulService2.order("userB",20000); //3.TreadA: A사용자가 주문금액을 조회 int price = statefulService1.getPrice(); System.out.println("price = " + price); } static class TestConfig{ @Bean public StatefulService statefulService(){ return new StatefulService(); } } }
위 테스트코드를 실행하면 중간에서 사용자 B의 주문으로 price의 가격이 바뀌었기 때문에 price가 2000으로 출력되고 사용자A가 주문하려고 했던 가격 10000원은 사라진다.
각각 다른 statefulService1과 statefulService2를 사용한것 같기만 사실 tatefulService1과 statefulService2 은 싱글톤 객체 인스턴스로 같은 인스턴스이다.
- 최대한 단순히 설명하기 위해, 실제 쓰레드는 사용하지 않았다.
- ThreadA가 사용자A 코드를 호출하고 ThreadB가 사용자B 코드를 호출한다고 가정하자
- StateService의 price 필드는 공유되는 필드인데 특정 클라이런트가 값을 변경한다.
- 사용자A의 준무금액은 10000원이 되어야 하는데, 20000원이라는 결과가 나왔다.
- 실무에서 이런 경우는 종종 보이는데, 이로인해 정말 해결하기 어려운 큰 문제들이 발생한다.
- 진짜 ⭐공유필드는 조심해야 한다. 스프링 빈은 항상 무상태(stateless)로 설계하자.
모 포털에 서 발생했던 문제
-> 나의 아이디로 로그인 했는데, 다른 사람의 이름이 보이는
-> 나의 결제 내역에 들어 갔는 다른 사람의 결제내역이 보이는것[출처 - 스프링 핵심 원리 - 기본편]
'FrameWork > Spring&Spring-boot' 카테고리의 다른 글
스프링 기본 17 - 컴포넌트 스캔 (0) 2023.11.13 Spring 기본 15 - @Configuration과 싱글 (1) 2023.11.13 Spring 기본 14 - 싱글톤 컨테이너 (0) 2023.11.12 도메인 주도 개발4 - Entity와 Value1 (0) 2023.11.12 Spring 기본 13 - 스프링 빈 설정 메타 정보 - BeanDefinition (0) 2023.11.10