본문 바로가기
기타

싱글톤 패턴과 문제점

by 이곳느 2021. 12. 30.

안녕하세요 :)
싱글톤 패턴은 흔히 사용하는 디자인 패턴중 하나입니다. 이러한 싱글톤 패턴을 왜 안티패턴이라고 하는지 알아보려고 합니다.

싱글톤 패턴이란?

  • 싱글톤 패턴은 전역 변수를 사용하지 않고 객체를 하나만 생성합니다.
  • 생성된 객체를 어디에서든지 참조할 수 있도록 하는 패턴입니다.
  • 생성자(Constructor)가 여러 차례 호출되어도 실제로 객체는 하나만 생성됩니다.
  • 최초 생성 이후에 호출된 생성자는 최초에 생성자가 생성한 객체를 리턴합니다. (위 특징과 같습니다.)
  • 환경설정 관리 클래스나 커넥션 풀과 같이 pool 형태로 관리되는 공통 클래스에 사용되는 것이 일반적입니다.

싱글톤의 특징

  • 고정된 메모리 영역을 얻으면서 동시에 단 한번만 `new` 를 사용하여 메모리 낭비를 방지할 수 있습니다.
  • 싱글톤으로 만들어진 인스턴스는 전역으로 사용됩니다. 즉, 다른 클래스의 인스턴스 들이 데이터를 공유하고 변경할 수 있습니다.
  • 너무 많은 일을 위임하거나 공유할 경우 Coupling이 많아지고 결합도가 높아집니다.

싱글톤 기본 예제

Javascript

Javascript 싱글톤 패턴

Javascript 에서의 싱글톤 패턴 간단예제는 위와 같이 구현할 수 있습니다.

멤버 메서드들을 init() 메서드에 정의하고, getInstance 메서드를 통해 인스턴스 생성 시 최초 생성인지 아닌지를 판단합니다.

최초 생성이라면 멤버 멤서드를 인스턴스에 삽입하고, 아니라면 기존에 삽입했던 인스턴스를 삽입합니다.

실제로 first와 second에 싱글턴 인스턴스를 생성하고, first 인스턴스만 변경을 시도해 본 후 출력해보면 second 인스턴스의 값도 변경이 된 것을 알 수 있습니다.

`static' 키워드를 사용한  싱글톤

static 키워드를 이용한 싱글톤

위 예제의 TestClass1 은 Singleton을 적용하지 않은 클래스이고 TestClass2 는 Singleton 을 적용한 메서드입니다.

Singleton 을 적용한 TestClass2 를 살펴보면, static 이라는 키워드를 사용하여 instance 라는 변수를 메모리의 Data 영역에 저장합니다. 메모리의 Data 영역은 프로그램이 종료될 때 까지 메모리에 남아있기 때문에 메모리공간을 공유할 수 있습니다.

constructor 를 보면, 위 예제와 비슷합니다.
instance가 정의되어 있다면 정의되었던 instance를 반환하고. 그게 아니라면 this 를 instance에 삽입합니다.

즉, 최초 생성시엔 Class를 자체를 instance에 할당하고, 두번째 부턴 첫번째 할당한 instance를 반환하는 것입니다.

싱글톤 패턴이 필요한 이유

싱글턴 패턴이 필요한 이유

그림 출처 : https://injae-kim.github.io/dev/2020/08/06/singleton-pattern-usage.html

 

Injae's devlog

현실의 문제를 해결하는 엔지니어

injae-kim.github.io

예를들어 사무실에 있는 1대의 프린터를 여러명이 사용하는 경우를 예시로 들어보겠습니다.

이러한 상황에서 가장 정상적인 프린터 사용법은, 1대만 존재하는 프린터를 여러 사람이 함께 공유하며 사용하는 방법이 좋습니다.

위 사진에서 오른쪽의 경우, 프린터를 사용하려는 사람들이 프린터를 각자 생성해서 사용하는것은 불가능합니다. 프린터는 단 1대만 존재하기 때문입니다.

조금 더 실전에 가까운 예는, 사용자 정보를 처음에만 로딩해주는 UserManager 가 있다고 가정한다면, 매번 이 인스턴스를 생성하는 것은 자원 낭비 또는 인스턴스가 꼬이는 일이 생길 수 있습니다.

또한 DB Connection Pool같은 객체가 있을때 한번만 연결한 후 한번만 끊는 것이 더 효율적이라고 할 수 있겠습니다.

싱글톤 패턴에 주의할 점

Singleton Pattern 사용시 주의해야 할 점은, 상태를 가진 객체를 Singleton 으로 만들면 안된다는 것입니다. 앱 내의 단 한개의 인스턴스가 존재하고, 이를 전역에서 접근할 수 있다면 각기 다른 스레드에서 객체의 상태를 마구잡이로 변경시킬 여지가 있습니다. 상태가 공유된다는 것은 매우 위험한 일이기 때문에 무상태 객체 혹은 설계상 유일해야하는 시스템 컴포넌트를 Singleton으로 만들어야 합니다.

싱글톤 패턴이 안티패턴이라고 불리는 이유

SOLID 원칙의 대부분은 인터페이스 설계와 관련이 되어 있습니다. 의존성을 Interface에 두면 실제 구현 클래스의 구현이 변경되어도 이를 사용한 코드는 큰 영향을 받지 않습니다. 그렇기 때문에 SOLID원칙을 지키기 위해서는 인터페이스로 설계하는게 좋은 설계입니다.

하지만 Singleton을 사용하는 경우 대부분 인터페이스가 아닌 구현 클래스의 객체를 미리 생성해놓고 정적 메소드를 이용하여 구현하게 됩니다. 이는 SOLID 원칙을 위반할 수 있는 가능성이 있으며, 동시에 Singleton을 사용하는 곳과 Singleton Class 사이에 의존성이 생기게 됩니다.

이는 결합도를 높이는 행위로, 수정 및 단위테스트의 어려움이 생기게 됩니다.

객체지향의 의도와 맞지 않습니다

Singleton의 사용은 Global state를 만들 수 있기 때문에 바람직한 방법은 아닙니다. 아무 객체나 자유롭게 접근하고 수정하고 공유할 수 있는 전역 상태를 갖는 것은 객체지향 프로그래밍에서는 지양되어야 할 모델입니다. 또한 Singleton은 Private 생성자를 가지고 있기 때문에 상속할 수 없습니다. 이는 다형성같은 객체지향의 특징을 적용할 수 없게 됩니다.

리팩토링에서의 영향도는 프로그램 전체

Service A 를 위해 만들어졌던 Func 1 을 다른 서비스들이 사용하기 시작합니다. Service A 의 요구사항이 변경되어 Func 1 이 변경되면 이를 사용하는 모든 서비스들에게 변경 전파(Shotgun Surgery)가 이루어집니다. 그리고 이러한 Coupling 문제를 개선하기 위해 Singleton을 리팩토링 할 때의 영향도(Force)는 어플리케이션 전체가 됩니다.

'기타' 카테고리의 다른 글

씽크웨이 토체티 BW 갈축 리뷰  (0) 2022.03.25