CS

[디자인 패턴] 싱글톤 패턴(Singleton Pattern)에 대하여

술단고 2023. 4. 1. 21:54

 

이 오브젝트는 어디에서든지 반드시 하나만 존재했으면 좋겠어

 

 

이번 포스팅에선 유명한 디자인 패턴 중 하나인 싱글톤 패턴(Singelton Pattern)에 대해 알아보려고 한다.

그 전에 잠깐, 가볍게 디자인 패턴이 무엇인지 알아보고 넘어가도록 한다.

 


디자인 패턴이란?

디자인 패턴(Design Pattern) 혹은 프로그래밍 패턴(Programming Pattern)이라 불리우는 이것은, 주로 객체지향 언어로 프로그래밍을 할 때 자주 마주치는 문제들을 해결하기 위해 미리 만들어둔 코드 패턴 이라고 이해하면 된다.

 

정말 여러가지 각종 디자인 패턴이 존재하고, 그 중 정말 자주 사용되는 디자인 패턴 중 하나인 싱글톤 패턴에 공부하려고 한다.

 

싱글톤 패턴이란?

간단히 요약하자면, 클래스 하나에 반드시 하나의 인스턴스만 존재하도록 하는 디자인 패턴이다.

 

여기에서도, 저기에서도 똑같은 클래스의 인스턴스를 만들어 사용한다면, 그 인스턴스의 클래스에 따라 수많은 메모리를 차지하게 될 것이다. 따라서 반드시 여러 개 만들어야 하는 인스턴스가 아니라, 딱 하나만 존재해야 하는 인스턴스라면 바로 이 싱글톤 패턴으로 클래스를 구현해두면 이후 아무리 인스턴스를 제작하더라도 이미 제작되어있는 딱 하나의 오브젝트만을 가져오게 된다.

 

아래 간단한 JavaScript 코드를 하나 준비했다.

class Cat {
    // 생성자
    constructor(name) {
        this.name = name
    }

    playSound() {
        console.log(this.name, " : 야옹")
    }
}

let tom = new Cat('tom')
let kitty = new Cat('kitty')

고양이 클래스를 하나 만들었다.

해당 고양이 클래스는 '자신의 이름 : 야옹' 이라는 울음소리를 낼 수 있는 기능이 존재한다.

 

그리고 해당 클래스를 new 객체를 사용해 서로 다른 독립된 인스턴스로 만든다면, 해당 코드의 tom과 kitty는 같은 Cat 클래스를 선언받았지만 엄연히 다른 인스턴스로서 메모리에 각각 존재하게 된다.

 

뭐, 고양이는 원래 개체마다 이름도 다르고 성격도 다르니 당연히 고양이 클래스를 싱글톤 패턴으로 구현할 이유는 전혀 없다. 그러나 우선은 싱글톤 패턴을 구현하는 방법에 대해 설명하기 위해, 오직 메모리에 하나의 고양이만 존재하도록 하는 싱글톤 패턴이 사용된 고양이 클래스를 제작해보았다.

class Cat {
    // 여러 인스턴스에서 사용할 수 있는, 한 번만 생성되는 변수 형태 static
    static instance;

    // 생성자
    constructor(name) {
        this.name = name
        
        if (!Cat.instance) {
            Cat.instance = this;
        }
        return Cat.instance
    }

    playSound() {
        console.log(this.name, " : 야옹")
    }
}

let tom = new Cat('tom')
let kitty = new Cat('kitty')

위 코드와 다른게 있다면, 역시 static instance 변수의 존재이다.

static 변수를 하나 초기화하는데, 아무것도 할당하진 않는다.

 

만약 해당 클래스가 생성 호출을 받았을 때, static 변수가 비어있다면 그 때 static 변수에 해당 인스턴스를 넣고 static 변수를 리턴하도록 한다.

그러나, 두 번째로 생성 호출을 받았다면 static 변수가 비어있지 않으므로 별도의 작업 없이 바로 원래 할당되어있던 static 변수를 그대로 리턴하게 하면 싱글톤 패턴으로 클래스를 제작하게 된 것이다.

 

그러나 이 코드는 문제가 있다. 엄밀히 말하면 싱글톤 패턴으로 구현한 것이 아니다.

new 연산자를 사용하여 인스턴스를 만들었기 때문에, kitty 오브젝트를 만든 순간 이미 메모리에 인스턴스는 할당되어있고 tom 인스턴스를 리턴받은 것이기 때문이다. 정말 싱글톤 흉내만 낸 평범한 클래스 생성인 셈. 위 코드는 사실 이해를 돕기 위해 쓴 코드이다.

 

아래는 JAVA를 사용해 제대로 된 싱글톤 패턴을 구현한 코드이다.

// Cat.java 파일

public class Cat {
    private static Cat instance = new Cat();

    // 생성자는 반드시 private로 지정한다
    private Cat() {
        
    }

    public static Cat getInstance() {
        return instance;
    }

    public void playSound() {
        System.out.println("야옹");
    }
}
// Main.java 파일

public class Main {
    public static void main(String[] args) {
        Cat tom = Cat.getInstance();
        Cat kitty = Cat.getInstance();

        if (tom == kitty) {
            System.out.println("tom과 kitty는 동일한 인스턴스입니다.");
        }
    }
}

우선 생성자는 비어있다. 애초에 private라서 외부에서 생성자를 호출하지 못하게 막아두었다!

그리고 잘 보면 이미 Cat 클래스 안에 static 변수로 이미 오브젝트를 하나 생성해두었다.

 

즉, 애초에 오브젝트를 하나 만들어두고 new 키워드가 아니라 Cat.getInstance() 메서드를 호출해 오브젝트를 반환받는 방식으로 구현한 것이며, 이게 제대로 된 메모리를 절약하는 싱글톤 패턴의 구현 방법이다.

 

싱글톤 패턴의 기능

1. 하나의 클래스에 하나의 인스턴스만 존재하도록 고정한다.
2. 해당 인스턴스로의 전역 접근이 가능하여, 다른 클래스의 인스턴스들이 데이터를 공유하기 쉽다

 

싱글톤 패턴엔 바로 위에서도 서술한 기능과 더불어 또 다른 중요한 기능이 존재한다.

바로 해당 인스턴스는 전역 인스턴스로 취급되어, 외부에서도 쉽게 접근이 가능하도록 해주는 것이다.

 

싱글톤 패턴의 장단점

당연하겠지만 싱글톤 패턴의 장점이라면 바로 메모리 절약이다. 용량이 거대한 클래스를 가진 인스턴스가 쓸데없이 여러 개 존재한다면, 수가 많을 수록 기하급수적으로 많은 메모리를 낭비하게 된다. 그러나 미리 만들어두거나 최초로 생성된 인스턴스가 딱 하나만 존재하며, 이후에도 해당 인스턴스만을 사용하게 된다면 정말 적은 메모리로 효율적이게 인스턴스를 사용할 수 있게 된다는 것이다.

 

그러나 단점이라면, TDD(Test Driven Development)를 할 때 문제가 생길 수 있다. 단위 테스트는 모든 테스트들이 서로 독립적으로 이루어져야 하고 어떤 순서로든 실행할 수 있어야 하는데, 싱글톤 패턴으로 구현한 코드라면 하나의 최초 인스턴스만을 기반으로 제작되었을 것이기 때문에 독립적인 인스턴스를 만들기 어렵고 원하는 동작이 이루어지지 않을 수 있다.

또한, 의존성이 더해져 모듈간의 결합이 강하게 될 수도 있다. 그러나 이는 의존성 주입(DI, Dependency Injection)으로 해결할 수 있다.

 

결론 및 사용 예시

나는 게임을 개발하는 사람이므로, 유니티에 사용하는 C#에서도 싱글톤 패턴을 구현할 수 있으며, 그 땐 게임의 모든 씬에 하나만 존재해야 하는 게임 시스템 매니저(게임의 흐름 및 필수 요소들을 관리), 사운드 매니저(BGM, SFX 재생)들을 하나만 만들도록 사용한다. 그 외엔 데이터베이스 연결 모듈 등에 사용하고는 한다.

 

디자인 패턴은 자주 마주치는 문제를 해결하기 위해 잘 만들어둔 패턴이지만, 너무 패턴병에 걸려 굳이 디자인 패턴을 사용하지 않아도 됨에도 불구하고 마구 사용해대는 것은 금물이다! 그래도 사용처가 정말 많은 것은 사실이므로 잘 숙지해서 사용하도록 하자.

'CS' 카테고리의 다른 글

파이썬에서 EOF 처리하기  (2) 2023.10.06