여러 클라이언트가 하나의 객체 인스턴스를 공유하기 때문에 싱글톤 객체는 상태를 유지하게 설계하면 안된다.

 

이는 아주 중요한 개념입니다.

 

왜 중요할까요 ??

 

공유값이 바뀌는 에러가 발생

코드로 예를 들어보겠습니다.

[ SingleTon class ]

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;
    }
}

 

[ Test code ]

class StateFulServiceTest {
    @Test
    void FailSigleTon() {
        ApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
        StateFulService stateFulService1 = ac.getBean("stateFulService", StateFulService.class);
        StateFulService stateFulService2 = ac.getBean("stateFulService", StateFulService.class);

        stateFulService1.order("parkA", 10000);
        stateFulService2.order("parkB", 20000);

        int price = stateFulService1.getPrice();

        System.out.println("price: " + price);

        Assertions.assertThat(stateFulService1.getPrice()).isEqualTo(20000);
    }

    static class TestConfig{

        @Bean
        public StateFulService stateFulService() {
            return new StateFulService();
        }
    }
}

위의 코드에서 보면 과정은 이렇습니다.

 

[ 테스트 코드에서 생성된 스프링 컨테이너는 기본적으로 싱글톤을 사용할 수 있게 설계되어 있습니다. ]

 

1)  parkA라는 주문을 후 getPrice함수로 바로 가격을 출력해줘야하는데

 

2) 그 중간에 parkB라는 주문이 들어옵니다.

 

3) 그러면 price 라는 객체를 공유하고 있기 때문에 10000 -> 20000으로 바꿔버리게 됩니다.

 

4) 그래서 stateFulService1 객체의 getPrice() 함수를 사용해도 20000이 출력되게 되는 오류입니다.

 

 

 

 

예시의 싱글톤이 이러한 에러가 발생하는 이유는 설계에 아주 큰 문제점이 있습니다.

 

*** " 테스트 코드 (클라이언트) 가 값을 변경한다 " 라는 것입니다. ***

 

그래서 스프링 빈은 항상 무상태로 설계해야 하는 것입니다.

 

그렇다면 위의 예시를 다시 무상태로 설계해보겠습니다.

 

 

[ SingleTon class ]

public class StateFulService {

    public int order(String name, int _price) {
        System.out.println("name:" + name + " price:" + _price);
        int price =  _price;
        return price;
    }
}

 

[ 테스트 코드 ]

class StateFulServiceTest {
    @Test
    void FailSigleTon() {
        ApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
        StateFulService stateFulService1 = ac.getBean("stateFulService", StateFulService.class);
        StateFulService stateFulService2 = ac.getBean("stateFulService", StateFulService.class);

        int price1 = stateFulService1.order("parkA", 10000);
        int price2 = stateFulService2.order("parkB", 20000);

        Assertions.assertThat(price1).isEqualTo(10000);
    }

    static class TestConfig{

        @Bean
        public StateFulService stateFulService() {
            return new StateFulService();
        }
    }
}

 

이렇게 공유된 필드를 사용하는게 아니라 지역변수를 통해서 공유하지 못하게 해결할 수 있습니다.

 

 

이 외에 싱글톤의 문제점을 해결하는 방법으로 @Configuration 에노테이션을 사용하는 방법이 있습니다.
이 부분은 검색해보시면 많은 자료를 찾아볼 수 있습니다.

 

 

 

 

+ Recent posts