1 분 소요

싱글톤 컨테이너

스프링이 가장 많이 사용되는 분야는 웹, 앱 애플리케이션이다. 웹과 앱같은 경우 일반적으로 다수의 클라이언트가 동시에 서버에 요청을 하게 된다.

그런데 모든 요청마다 객체가 생성된다면 어떨까? 트래픽이 많아질수록 서버의 부담은 커지고 메모리 낭비가 심할 것이다. 때문에 스프링 컨테이너는 기본적으로 빈을 싱글톤으로 관리한다. (싱글톤에 대해 잘 모른다면 싱글톤 패턴을 참고하자)

요청 흐름

img1

클라이언트A, B, C가 동시에 memberService객체에 요청을 한다고 해보자. 싱글톤 패턴을 사용하지 않는다면 모든 요청마다 memberService객체를 생성해야 한다.

만약 클라이언트의 수가 1억이라면 어떨까? 1억번의 메모리 할당이 생길 것이다. 심지어 이 객체는 1회용으로 쓰이고 버려질 것이다. 이는 굉장히 비효율적이다.

스프링은 싱글톤으로 객체를 관리하기 때문에 클라이언트의 요청이 얼마나 들어오던 동일한 객체가 작업을 수행하게 된다. 때문에 추가적인 메모리 할당이 발생하지 않는다. 위 그림과 같이 말이다.

주의점

스프링 컨테이너가 빈을 싱글톤으로 관리하기 때문에 신경써야 할 점이 있다. 빈들을 반드시 무상태로 설계해야 한다는 것이다.

그렇다면 왜 무상태로 설계해야 할까?

예를 들어 설명하겠다. 만약 스프링 빈이 상태를 가지고 있는 상황을 가정해보겠다.

img1

ReviewService라는 빈이 있다. 그리고 comment라는 상태를 가지고 있는 상황이다.

멀티 스레드 환경에서 다수의 클라이언트가 ReviewService에 접근해서 review()메소드를 호출하게 되면 어떤 일이 발생할 수 있을까?

스레드A가 review()메소드를 호출하는 상황에서 다른 스레드B에서 review()를 호출하는 상황을 가정하자. (여기서 스레드A는 reviewServiceA, 스레드 B는 reviewServiceB이다.)

img1

스레드A에서는 comment로 “하이”이라는 값을, 스레드B에서는 comment로 “바이”라는 값을 넘겼다.

이 메소드의 실행이 끝나면 스레드 A, B는 comment에 각자 자신이 넘긴 스트링(“하이”, “바이”)이 저장됐다고 생각할 것이다.

img1

그런데 스레드A에서 getComment()를 호출하여 comment를 조회했더니 “하이”가 아니라 “바이”가 나온다. 스레드A는 자신이 설정한 값(“하이”)이 아닌 다른 값(“바이”)이 나와서 당황하게 된다.

단순한 예시를 들었지만 위 코드가 멀티 스레드 환경에서 문제가 있을 수 있다는 것은 이해했을 것이다. 기본적으로 스프링 빈 내의 맴버 변수는 Thread safe하지 않다. 때문에 race condition문제가 발생할 수 있다. 이를 피하기 위해 스프링 컨테이너에 등록하는 빈들은 반드시 무상태로 설계해야 한다.

  • race condition : 두 개 이상의 프로세스가 공통 자원을 병행적으로 읽거나 쓸 때, 공용 데이터에 대한 접근이 어떤 순서에 따라 이루어졌는지에 따라 그 실행 결과가 달라지는 상황

정리

오늘은 스프링 컨테이너가 빈들을 어떻게 관리하는지, 그리고 우리가 빈을 설계할 때 무엇을 주의해야 하는지 알아봤다.

위에서 설명한 멀티 스레드 환경에서 발생할 수 있는 race condition 문제는 실무에서도 자주 발생한다고 하니 꼭 숙지하도록 하자.

참고

스프링 핵심 원리

태그:

카테고리:

업데이트: