[spring] 싱글톤방식의 주의점 (스프링 기본편 by 김영한)

su_y2on·2022년 1월 21일
0

Spring

목록 보기
18/30
post-thumbnail

싱글톤방식의 주의점

싱글톤은 하나의 객체를 생성해서 공유를 하기 때문에 상태를 유지(stateful)하게 설계하면 🔥절대🔥 안됩니다!!

그렇다면 여러 유저들이 동시에 요청을 할 때 문제가 생길 수 있기 때문에 특정 유저에 의존적이게 설게를 하면 안됩니다. 예를 들어 전역변수를 만들어서 그 상태를 저장한다거나 특정 유저들에 따라 그 변수 값이 바뀌게 하는 설계는 피해야합니다. 지역변수나 파라미터등 일시적으로만 존재하게 하여 해결할 수 있습니다.




stateful한 클래스

예를 들어 아래와 같이 price를 저장하고 또 이를 유저의 주문에 따라 변하게 만든 것은 stateful하게 설계한 것입니다.

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



✔️테스트 코드
아래 클래스만을 위한 TestConfig를 만들어서 스프링 컨테이너를 만들어서 테스트를 진행했습니다. 물론 지금 멀티쓰레드는 아니지만 멀티쓰레드라고 가정하면 사용자1이 주문을하고 금액을 조회하는 사이에 사용자2가 주문을 해버린 상황입니다. 이렇게 되면 price가 바뀌고 사용자1의 price가 20000이 나와버립니다..

class StatefulServiceTest {

    @Test
    void statefulServiceTest() {
        ApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);

        StatefulService statefulService1 = ac.getBean(StatefulService.class);
        StatefulService statefulService2 = ac.getBean(StatefulService.class);

        // ThreadA : 사용자A가 10000원 주문
        statefulService1.order("userA", 10000);
        // ThreadB : 사용자B가 20000원 주문
        statefulService2.order("userB", 20000);

        // ThreadA : 사용자가A가 주문 금액 조회
        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();
        }
    }

}




무상태로(stateless) 수정

StatefulService를 아래와 같이 변경하면 문제가 해결됩니다. order의 반환값으로 price를 주고 끝내는 것입니다. order후에 반환값을 따로 저장해놓으면 사용자끼리 섞이는 경우가 없어집니다.

public class StatefulService {

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

}

0개의 댓글