당니의 개발자 스토리

웹 애플리케이션과 싱글톤 본문

스프링/스프링 핵심 원리 - 기본편

웹 애플리케이션과 싱글톤

clainy 2024. 1. 23. 18:05

5. 싱글톤 컨테이너

이번 시간부터는 싱글톤 컨테이너에 대해서 알아보겠습니다. 아마 싱글톤 패턴이라고 들어보셨을 거에요. 바로 그 싱글톤이구요.

싱글톤객체 인스턴스가 현재 나의 java JVM 안에 딱 하나만 있어야 되는 패턴을 말합니다.

목차가 이렇게 쭉 있는데요.

먼저 웹 어플리케이션에서 왜 싱글톤이 많이 사용되는지에 대한 이유들, 그리고 디자인 패턴인 싱글톤 패턴에 대해서 간단하게 알아보고, 싱글톤 컨테이너와 싱글톤 방식의 주의점, 이런 내용들을 쭉 알아보고, 스프링이 제공하는 @Configuration의 비밀에 대해서도 파헤쳐 보겠습니다.


이번 시간은 웹 애플리케이션과 싱글톤인데요.

Spring은 태생이 기업용 온라인 서비스 기술을 지원하기 위해서 탄생한 기술이에요.

지금은 대부분 애플리케이션이이 웹 애플리케이션으로 많이 있는데, 애플리케이션이 되게 종류가 많거든요. 온라인에서 처리하는 것만 있는 게 아니라, 마치 서버에 하나가 떠있어가지고 처리해주는 demon 같은 프로세스도 있고, 한 번에 여러 개를 묶어서 배치로 처리해주는 작업도 있고요. 그래서 이런 여러 종류의 애플리케이션이 있어요.

그래서 대부분의 스프링 애플리케이션은 사실 웹 애플리케이션이에요. 현대에는 대부분 웹으로 되어있으니까요.

물론 웹이 아닌 애플리케이션도 스프링으로 얼마든지 개발할 수 있습니다. 배치 애플리케이션이나 demon 애플리케이션도 개발할 수 있고, 얼마든지 개발할 수 있습니다.

그런데 보통 웹 애플리케이션여러 고객이 동시에 요청을 하죠.

그래서 이 그림을 보시면,

클라이언트 A, B, C가 동시에 memberService를 스프링한테 요청을 해요. 그러면 어떻게 됩니까?

세 명이 동시에 요청을 하면, 우리가 직접 만들었던 DI 컨테이너(AppConfig) 기억나시죠?

memberService를 요청을 하면,

객체를 new로 생성해서 반환을 하죠. 그러고 클라이언트 A한테 반환을 해줍니다.

그럼 클라이언트 B가 똑같은 요청을 해요. memberService와 관련된 어떤 기능을 하려고 하는 거겠죠? 그래서 B가 "memberService 주세요!" 라고 하면, 우리가 만들었던 config는 어떻게 합니까?

new MemberServiceImpl 해서 반환을 해주죠.

클라이언트 C가 요청을 해도 마찬가지입니다. 그러면 어떻게 되나요?

고객이 3번 요청을 하면, 객체 3개가 생성이 되는 거예요.

이게 어떤 문제가 있냐면, 웹 애플리케이션은 고객이 계속 요청을 하는 애플리케이션이거든요. 그래서 계속 요청이 올 때마다 뭔가를 계속 만들어야 돼요. 이게 문제인 거고요.

그럼 "진짜 고객 요청이 올 때마다 이렇게 매번 생성되나요?" 맞는지 확인해 보겠습니다.


스프링이 없는 순수한 DI 컨테이너를 만들어서 테스트를 해볼게요.

singleton 이라는 패키지를 test에다가 만들 겁니다. 이제 여기다가 Singleton과 관련된 건 다 만들 거예요.

그리고 테스트 클래스SingletonTest 라고 만들겠습니다. 그리고나서,

이렇게 적어줍니다. 그러니까 우리가 만들었던 순수한 DI 컨테이너가 가진 문제점을 볼 거에요. 그리고 나중에 스프링이랑 비교를 해볼겁니다.

AppConfig가 먼저 필요하니까 AppConfig를 new 해서 만듭니다. 그리고 나서 조회를 해봅시다.

정말 호출할 때마다 객체를 생성하는지 조회를 해볼게요.

우리가 AppConfig에서 memberService를 조회하면 어떻게 되나요?

일단 조회를 하나 더 해보겠습니다.

이름을 바꾸고, 똑같이 memberService를 호출한 다음에 참조값이 다른지를 확인해보겠습니다.

만약 참조값이 다르다면 새로운 객체를 생성해서 반환해준 거겠죠. soutv 해서,

이렇게 하고 출력해보겠습니다. 이건 단위 테스트니까 금방 실행이 되겠죠.

제가 appConfig 한테 memberService 주세요. 할 때마다 어떻게 되나요?

지금 보시면,

참조값이 다릅니다. 즉 요청할 때마다 다른 게 생성이 되죠. 이렇게 되면 JVM 메모리계속 객체가 생성해서 올라가겠죠.

스프링은 대부분 웹 애플리케이션을 주로 만든단 말이에요. 그런데 웹 어플리케이션의 특징고객 요청이 참 많아요. 배민 같은 경우는 초당 5만 개의 객체가 생성이 돼야 되는 거예요. 그러니까 이거는 말이 안 되는 거죠. 이런 식으로 계속 객체를 생성하고 하는 건 별로 효율적이지 않겠죠. 메모리 낭비입니다.

일단 여기까지 해놓은 걸 한번 검증해 볼게요. 아까는 눈으로 확인했지만, 테스트는 자동화가 되게 만들어야 됩니다.

당연히 memberService1과 memberService2는 달라야겠죠. Same은 참조값 비교입니다. 돌려보면,

성공했습니다. 호출을 할 때마다 무조건 객체를 계속 새로 생성 하는 거예요.

심지어 memberService만 생성하는 게 아니라, 여기 들어가면,

얘까지 같이 생성하잖아요. 그래서 2번 요청하면 객체가 총 4개가 생성된 거예요.

우리가 과거에 만들었던 스프링 없는 순수한 DI 컨테이너인 AppConfig요청을 할 때마다 객체를 계속 새로 생성합니다.

고객 트래픽이 초당 100이 나오면 초당 100개 객체가 생성되고 소멸돼요. 메모리 낭비가 정말 심하죠.

그런데 실제로는 100개가 아니라 더 많이 생성되겠죠. 내부적으로 memberService를 생성하면, memberRepository 까지 생성해야했잖아요.

이거를 해결할 수 있는 좋은 방안이 있어요. 해결방안은 해당 객체가 딱 1개만 생성되고, 공유하도록 설계하면 돼요.

그러니까 뭐냐면,

제가 지금 memberService 구현체를 계속 생성하잖아요. 그게 아니라, 딱 하나만 생성해 놓고, 생성된 객체 인스턴스를 공유해서 쓰면 돼요. 그렇게 하면 객체를 계속 생성할 필요 없이 효율적으로 개발할 수가 있겠죠.

만약 효율적으로 안하면 어떻게 됩니까? 요청이 100개 오면 100개 다 생성되고, 또 나가면 나중에 Java GC, 가비지 컬렉션되고 하는 과정이 계속 일어나야 되잖아요. 이렇게 말은 했지만 사실 gc가 굉장히 빠르기 때문에 뭐 이정도는 끄떡도 없습니다. 하지만 그래도 더 효율적인 방법으로 개발하는게 낫겠죠.


그래서 다음 시간에 싱글톤 패턴을 적용을 해서 이 문제를 해결해 보겠습니다.