자바/스프링(Spring)

스프링(Spring): 싱글톤(Singleton) 패턴

류창 2021. 10. 1. 22:58

이번 포스팅은 디자인패턴 중 하나인 싱글톤 패턴에대해 다뤄보겠습니다.

 

 

싱글톤 패턴(Singleton Pattern)은 무엇일까? 

 

위키피디아:

싱글턴 패턴(Singleton pattern)을 따르는 클래스는, 생성자가 여러 차례 호출되더라도 실제로 생성되는 객체는 하나이고 최초 생성 이후에 호출된 생성자는 최초의 생성자가 생성한 객체를 리턴한다. 이와 같은 디자인 유형을 싱글턴 패턴이라고 한다.

 

즉! 여러사용자가 매번 객체를 호출할때 매번 객체를 생성하지말고, 이미 생성된 객체 하나를 재사용 하는 것입니다.

 

 

글로 설명하는것보단 코드로 한번 봐볼까요?

 

우선 아주 간단한 클래스 Menu를 설정하겠습니다.

입력값으로 메뉴 이름만 받도록 설정했습니다.

 

우선 싱글톤 없이 객체를 생성해보겠습니다.

 

자장면이라는 이름이담긴 menu1과 자장면이라는 이름이담긴 menu2를 생성했습니다.

 

과연, menu1과 menu2는 같을까요?

 

정답은 No입니다. 

menu1은 Menu@87a8531에 저장되어있고

menu2는 Menu@36d585c에 저장되어있습니다.

 

지금은 2개지만, 만약 하루 손님이 같은메뉴를 1만개 이상 주문하면, 컴퓨터는 똑같이 1만개의 객체를 생성합니다.

같은걸 재사용만하면되는데, 쓸데없이 1만개넘게 객체를 생성하고  엄청난 메모리 낭비가발생합니다.

 

 

이 문제를 해결하고자 나온것이 싱클톤 패턴입니다.

그렇다면, 위 코딩에서 싱클톤 패턴을 적용해보겠습니다.

상당히 많이 바뀌었죠?

 

핵심은 private static final Menu instance =new Menu(); 부분입니다.

 

생성할 객체를 딱 1개만 사용하겠다라는 의미를 static final로 전역변수로 만들어버리는겁니다.

 

그리고, getInstance()메소드로 생성한 객체를 재활용하는겁니다.

 

Getter Setter는 객체의 정보를 설정하고 접근하는용도입니다.

 

Menu1 과 Menu2를 생성하고 getInstance로 객체를 불러옵니다.

 

그리고 테스트로 두 객체가 같은건지 테스트해보겠습니다.

 

통과했습니다! 한번 생성한 객체를 getInstance를 사용하여 재사용하는 패턴

그게바로 싱글톤 패턴입니다!

 

 

 

싱글톤패턴 주의점(단점)

 

싱글톤패턴의 장점을 지금까지 살펴보았는데, 하지만 여러 단점이있습니다.

첫번째, 너무 깁니다.. 너무길어요.. Singleton설정하는데 벌써 10줄이넘어갑니다 .

솔직하게말해서 귀찮습니다. 프로젝트를 만들면 수많은 객체를 설정해야하는데 피곤하죠

 

두번째, OCP,DIP를 위반합니다.

DIP : getInstance()가 들어있는 구현체(Menu)에 접근해야합니다(X). ->인터페이스에 접근해야합니다.(O) 

//DIP는 중간에 Config를 넣으면 해결하긴하는데 많이 복잡하다

OCP:  DIP와 연계되는 문제입니다. 구현체를 접근하다가 구현체가 바뀌어버리면, 클라이언트 코드도 바꿔야합니다.

//(Menu)를 접근하다가, 새로운 (Menu2)로 바꾸면,

클라이언트코드도 Menu.getInstance()를 Menu2.getInstance()로 바꿔야겟죠?

 

그러므로, 싱글톤패턴은 테스트하기도 어렵고 내부속성변경도 힘들고, 유연성이 떨어집니다.

 

하지만 이런 싱글톤패턴의 문제점들을 모두 해결해주는 것이 스프링 컨테이너 입니다.

 

스프링 컨테이너에 설정된 스프링 빈
싱글톤 패턴이 적용된 스프링컨테이너

@Configuration으로 설정된 Appconfig 안에다

스프링컨테이너에 있는 @Bean 어노테이션을 설정합니다. 

이렇게 저장한 객체들은 모두 이전에 가진 단점을 모두없앤 싱글톤패턴을 적용받습니다!

 

 

 

하지만.. 싱글톤 패턴이 적용된 스프링컨테이너에게도 아직 주의해야할 점이 있습니다...

객체의 상태를 유지(stateful)하게 설정하면 정말 큰일납니다.

싱글톤 패턴을 적용한 객체는 단 1개이기때문에, 객체의 상태가 계속 바뀌면 치명적인 오류가발생합니다.

반드시 무결성(stateless)하게 짜야합니다!

 

코드로 예시를 보여드리겠습니다.

손님1이 자장면을 주문했고

손님2가 볶음밥을 주문했다고 가정합시다.

 

그리고, 손님1이 주문한 메뉴이름과 손님2가 주문한 메뉴 이름을 출력해볼까요?

 

엥..? 분명히 menu1과 menu2가쓰는 객체가 같게 싱글톤패턴을 적용하여 짯는데

 

menu1이 주문한 자장면이 볶음밥으로 바뀌었습니다

 

//이게 실제 일어났다 생각해보십쇼.... 난 자장면을시켯는데 배달로 볶음밥이 오는 경우랑 똑같습니다..;

 

 

싱글톤패턴을 사용하면 이런 실수가 일어날 가능성이 큽니다.

 

그래서 무결성(stateful)하게 짜셔야합니다.

setName을 설정하자마자, 바로 retrun 하게 짜줍시다. 

받는 타입도 void->String으로

사용자1이 메뉴를 결정하면, 메뉴의 이름을 menu1_name 지역변수로 저장합니다.

사용자2가 메뉴를 결정하면, 메뉴의 이름을 menu2_name 지역변수로 저장합니다.

 

그리고 사용자1과 사용자2의 메뉴 이름들을 출력해봅시다

 

사용자1이 주문한 자장면이 제대로나오고, 사용자 2가 주문한 볶음밥이 제대로나옵니다.

 

그리고 싱글톤패턴 테스트도 제대로 통과했습니다!

 

 

요약:

1. 싱글톤 패턴은 객체를 단 하나 생성하고 재활용할수있게하는 디자인 패턴이다.

2. 메모리를 아낄수있는 장점이있지만, 동시에 많은 단점을 보유하고있다.

3. 하지만, 장점을 살리고 단점을 모두없앤  스프링 컨테이너가 탄생했다.

4. 싱글톤 패턴(스프링 컨테이너) 는 무결성하게 짜야한다. 

 

 

이상으로, 싱글톤패턴이 무엇인지, 장단점과 해결방안, 주의할 점을 소개해드렸습니다.

다음 포스팅에서 뵙겠습니다. 감사합니다