이 시리즈는 인프런 강의(김영한 님의 ‘스프링 핵심 원리 - 기본편’)로 공부하며 혼자 기록하고, 사람들과도 공유할 수 있도록 작성하는 글이다. 최대한 추가적인 정보는 공식 홈페이지, 문서를 보며 얻을 예정이다.
(개인적인 생각과 이해가 들어가 있기 때문에 저의 ‘무식함’이 있을 수 있습니다😜 혹시라도 이 글을 보게 되시는 분이 계시다면 잘못된 부분 댓글로 많이 알려주시면 너무 감사하겠습니다!!)
GitHub Repository : https://github.com/jcw1031/spring-core-study
객체 인스턴스를 하나만 생성하여 공유하는 싱글톤 방식은 여러 클라이언트가 하나의 객체 인스턴스를 공유하기 때문에 싱글톤 객체는 상태를 유지(stateful)하게 설계해서는 안 된다! 무상태(stateless)로 설계해야 한다.
주의 : 스프링 빈(싱글톤 빈)의 필드에 공유 값을 설정하면 아주 큰 장애가 발생할 수 있다.
stateful 하게 객체를 설계했을 때 발생할 수 있는 문제점을 예시 코드를 통해 살펴보자.
singleton
패키지에 StatefulService
클래스를 만들었다.
StatefulService
는 price
라는 공유 필드를 갖고 있다. order()
메서드를 사용해 주문을 생성하면 이 공유 필드에 값이 저장된다. 이렇게 설계했을 때 여러 클라이언트가 요청을 하게 되면 어떤 문제가 발생하는지 테스트해 보자.
테스트를 위한 StatefulServiceTest
클래스를 생성한다.
테스트 클래스 내부에 테스트용 구성 클래스를 static class
를 사용해 만든다.
@Configuration
을 붙이지 않았다. 왜AppConfig
에는 어노테이션을 사용하고,TestConfig
에는@Bean
어노테이션만 사용했는지 궁금했다. 인프런에도 나와 같은 질문에 대한 질문 글이 있길래 참고해 봤다. 다음 시간에@Configuration
에 대해 공부하기 때문에 그 내용을 공부하면@Configuration
을 사용했을 때와 사용하지 않았을 때의 차이를 알 수 있다.
테스트 코드를 작성한다. TestConfig
를 기반으로 하는 컨테이너를 생성하고 getBean()
을 통해 두 개의 StatefulService
인스턴스가 빈을 참조하도록 한다. 싱글톤이 보장되기 때문에 두 인스턴스가 참조하는 빈은 같다.
사용자 A가 먼저 10000원의 주문을 생성하고, 그 후에 사용자 B가 20000원의 주문을 생성했다.
사용자 A의 주문 금액을 조회해 보자. 금액이 10000원이어야 하지만, 공유 필드를 사용했기 때문에 당연하게도 사용자 B가 주문한 금액이 다시 저장되었기 때문에 테스트가 실패한다.
실제로는 쓰레드가 사용되어야 하지만 해당 예제는 아주 간단하게 테스트한 것이다.
공유 필드를 사용하지 않고 아래처럼 주문을 생성하면 바로 금액을 return해야 정상적으로 조회가 된다.
이처럼 특정 클라이언트가 값을 변경하는 공유 필드가 존재하면 문제가 발생한다. 실무에서 이런 경우가 종종 발생하는 경우가 있다고 한다.
따라서 공유 필드를 항상 조심하도록 하자. 스프링 빈은 항상 무상태(stateless)로 설계하자.
오늘 학습한 내용은 짧지만 매우 중요하고, 우리가 개발을 할 때 주의해야 할 내용이었다. 다음 시간에는 앞에서 언급했던 @Configuration
어노테이션의 역할을 알아본다.