스프링 빈은 항상 무상태(stateless)로 설계하자

신범철·2023년 6월 26일
0

스프링부트

목록 보기
9/20

서론

스프링 빈을 항상 무상태(statelses)로 설계하라는 말이 있다.
이 말은 즉, 스프링 빈 클래스는 공유가 될 수 있는 전역 변수를 사용하면 안된다를 의미한다.

이유를 알아보자!!!

본론

stateless란?

stateless protocol은 들어 보았다. stateless protocol은 어떠한 이전 요청과도 무관한 각각 요청을 독립적인 트랜잭션으로 취급하는 통신 프로토콜로, 통신이 독립적인 쌍의 요청과 응답을 이룰 수 있게 하는 방식이다. 서버에서 세션 정보를 저장하지 않는 것이 대표적인 예시이다.

Spring Bean의 문제점

스프링 컨테이너는 스프링 빈을 생성할 때 디폴트로 싱글톤으로 생성한다.
싱글톤이란, 메모리에 하나의 인스턴스만 존재하도록 생성하는 방식이다.
이때 여러 스레드에서 동시에 인스턴스의 전역 변수(상태)에 접근한다면 어떻게 될까?
모든 전역 변수는 하나의 인스턴스이기 때문에 값이 꼬여버릴 것이다.

문제 상황을 예시로 확인해보자

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
    void statefulServiceSingleton() {

        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원 주문
        statefulService1.order("userB", 20000);

        //ThreadA: 사용자A 주문 금액 조회
        int price = statefulService1.getPrice();
				//ThreadA: 사용자A는 10000원을 기대했지만, 기대와 다르게 20000원 출력
        System.out.println("price = " + price);

        assertThat(statefulService1.getPrice()).isEqualTo(20000);
    }

사용자A의 주문금액은 10000원이 되었어야 하는데, 20000원이라는 결과가 나왔다.
사용자A는 10000원을 구매했는데 20000원이 결제되었다는 것이기 때문에 큰 문제이다.

해결법

스프링 빈은 항상 무상태(stateless)로 설계하자이다.

public class StatefulService {
//    private int price; //상태를 유지하는 필드

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

공유되었던 상태를 제거해 무상태로 만들어 주면 빈이 상태값을 저장해서 유지하는 것이 아닌 값이 들어오면 바로 return하여 문제가 해결된다.

profile
https://github.com/beombu

0개의 댓글