상세 컨텐츠

본문 제목

[Spring] 싱글톤?

JAVA\Spring

by 박집실 2022. 11. 23. 01:37

본문

# 싱글톤

 

- 싱글톤이란 일반적으로 단 한 개의 오브젝트만을 생성하여 그 오브젝트를 공유하여 사용하는 것이라 할 수 있다. 흔히 싱글톤이라고 불리는 개념은 두 가지가 있다. 싱글톤 패턴과 싱글톤 레지스트리인데 이 둘은 본질적으로 비슷하다.

 

먼저, 싱글톤 패턴이다.

 

- 싱들톤 패턴:

이 패턴은 디자인 패턴 중에 가장 자주 활용되기도 하지만 가장 많은 비판을 받는 패턴이기도 하다. 기본적으로 어떤 클래스의 인스턴스 갯수를 제한된 숫자로 주로 한 개로 제한한다, 이렇게 만들어진 객체는 전역적으로 접근 가능하다. 

 

자바에서 싱글톤 패턴을 구현하는 단계는 다음과 같다.

1. 클래스 밖에서는 오브젝트를 생성하지 못하도록 생성자를 private으로 만든다.

2. 생성된 싱글톤 오브젝트를 저장할 수 있는 자신과 같은 타입의 스태틱 변수를 정의한다.

3. 스태틱 팩토리 메소드인 getInstace()를 만들고 이 메소드가 최초로 호출되는 시점에서 오브젝트가 이미 만들어져 스태틱 변수에 값이 들어가있는지 유무를 확인하여 한 번만 오브젝트가 만들어지는 걸 보장함. 생성된 오브젝트는 스태틱 변수에 저장. 또는 스태틱 변수의 초기값으로 오브젝트를 미리 만들어둘 수도 있음.

4. 한번 오브젝트(싱글톤 상태인)가 만들어지고 난 후에는 getInstance() 메소드를 통해 이미 만들어져 스태틱 변수에 저장해둔 오브젝트를 넘겨줌.

 

public class Dao{

   private static Dao INSTANCE;

   private static ConnectiinMaker ctm;

   ...

   private Dao(ConnectionMaker ctm) {

       this.ctm = ctm;

   }

 

   public static synchronized Dao getInstance() {

        if (INSTANCE == null) INSTANCE  = new Dao(???);

        return INSTANCE;

   }

}

 

근데 여기 크나 큰 문제가 있다.

 

1. 생성자로 private을 사용해서 싱글톤 클래스 자기 자신만이 getInstance() 호출로 자기 오브젝트를 만들 수 있다는 거다. 이래서 상속이 불가능하고 이로 인해서 객체지향의 장점인 다형성을 적용이 불가능하다.

 

2. 상속과 다형성 같은 객체지향의 특성이 적용되지 않는 스태틱 변수(필드)와 메소드를 사용하는 것이다.

 

3. Junit4 같은 테스트용 외부 라이브러리는 작동할 때 여러 개 오브잭트를 생성하는데 이 넘은 하나 밖에 생성이 안되니 테스트가 불가능하다. 테스트는 개발의 핵심인테 많이 곤란하다..

 

4. 싱글톤 클래스임에도 서버 환경에서 JVM아 여러 개의 분산되어 작동하는 경우가 많기에 하나만 꼭 생성된다는 보장이 없다.

 

5. 스태틱 메소드로 만들면 전역적 상태로 사용될 수 있는데 아무 객체나 자유롭게 접근해서 공유할 수 있는 즉, 전역 상태는 객체지향에서는 권장되지 않는다. 이를 사용할 바에는 스태틱 변수나 메소드로 이루어진 게 낫다.

 

이러한 문제들이 너무나도 많아 객체지향 환경에서는 쓰기 많이 곤란한 것을 볼 수 있다... 그럼 우리 킹갓스프링은 이를 어떻게 해결했을까?

 

- 싱글톤 레지스트리:

스프링은 태생적으로 엔터프라이즈 시스템을 위해 고안된 기술이기에 서버환경에서 동작한다, 서버는 여러 클라이언트가 접근하고 다시 결과물을 리턴해준다. 이러한 과정에서 새로운 클라이언트들의 요청이 들어올 때마다 오브젝트들이 각각 하나씩 생긴다고 생각해보자 처음 한 두개에서 수 백개 까지는 괜찮을 것이다. 하지만 이 요청이 수 만개 단위로 뛴다고 하면 같은 작업을 진행하는 같은 클래스이지만 이러한 오브젝트들이 수 만개가 생길 것이고 이는 메모리를 잡아먹을 뿐만 아니라 서버 환경에도 심각한 부담을 줄 것이다. 이 모든게 현재 같은 작업을 하고 있는데도 말이다! 이 얼마나 심각한 낭비란 말인가! 이를 위해 같은 작엉을 하는 오브젝트를 단 한 개만 생성해서 수 만명의 클라이언트들이 이를 공유하게 한다, 이를 통하면 많은 리소스들이 줄어든다.

대표적으로 스프링 말고도 서블릿이 있는데 서블릿 클래스당 하나의 오브젝트만 만들고 사용자의 요청을 담당하는 여러 스레드에서 하나의 오브젝트틀 공유하여 동시에 사용한다. 

 

그런데 위에서 장황하게 나열해놨던 싱글톤 패턴의 처참한 상태를 질리도록 봤을 것이다. 이에 대한 스프링의 해답은 다음과 같다.

 

스프링은 직접 싱글톤 형태의 오브젝트를 만들고 관리하는 기능을 한다. 그리고 이것을 싱글톤 레지스트리라 부른다. 이로 힌해서 스프링 컨테이너는 싱글톤 관리 컨테이너라고 부르기도한다. 쨋든, 싱글톤 패턴에서는 private 생성자를 써서 문제들이 심했는데 스프링은 private이 아니더라도 평범한 일반 클래스를 싱글톤으로 활용하게 해준다. 이는 오브젝트 생성에 관한 모든 권한이 IoC로 인해 애플리케이션 컨텍스트에 위치하기 때문이다, 이로 통해서 생성된 클래스들은 생성자로 public을 가질 수 있고 이를 통해 상속이 가능해진다. 이로 인해서 굳이 싱글톤을 활용하지 않아도 되는 상태라면 간단하게 오브젝트를 생성할 수 있다. 즉, 테스트 환경에서도 작동하는 것을 보장한다.  다시 정리하자면 싱글톤으로 생성되었으나 동시에 테스트환경에서도 사용될 수 있을 정도로 모든 상황에서 쓰일 수 있는 엄청난 유연성을 보여준다.

 

그런데 주의할 점이 하나 있다. 싱글톤은 기본적으로 단 한 개의 오브젝트만 생성이 된다. 멀티쓰레드 환경에서는 이를 고유해서 동일한 오브젝트를 여러 쓰레드가 접근하여 사용하는데 동시에 여러 사용자 혹은 쓰레드가 이 오브젝트에 접근해서 데이터를 임의로 계속해서 수정해버리면 여러 사용자의 임이로 바뀐 데이터들로 인해서 완전 엉망진창이 되어버릴 것이다. 이로 인해 싱글톤은 멀티스레드 환경에서는 상태정보를 오브젝트 내부의 가지고 있지 않은 무상태( stateless) 방식으로 구현해야한다. 이를 위해서는 파라미터나 지역 변수, 리턴 값을 활용하면된다. 지역변수는 매번 새로운 값을 저장할 독립적 공간이 만들어지기에 데이터가 꼬일 걱정이 없다. 

관련글 더보기