반응형

Programming 

유니티에서의 싱글톤 패턴 활용

 

[이 포스트의 내용은 유튜브 영상으로도 시청하실 수 있습니다]

 

여러 종류의 프로그램을 만들다 보면 어떤 문제를 해결하기 위해서 비슷한 형태의 코드를 만들게 되는 경우가 자주 있다. 이런 비슷한 유형의 문제를 해결하는 방법을 묶어낸 것을 바로 프로그래밍 패턴이라고 한다. 이러한 프로그래밍 패턴은 유니티 엔진에서 스크립트를 작성하는데도 똑같이 유효하기 때문에 프로그래밍 패턴을 공부하는 것이 상당한 도움이 될 것이다.

 

 

일반적인 싱글톤 패턴(Singleton Pattern)

 

싱글톤 패턴은 프로그래밍 패턴 중에서 상당히 자주 쓰이는 패턴 중 하나로, 클래스의 오브젝트를 단 하나만 생성하고자 할 때 주로 사용된다.

 

public class SingletonClass

{

    private SingletonClass()

    { }

 

    public static SingletonClass Instance

    {

        get

        {

            if (Instance == null)

            {

                Instance = new SingletonClass();

            }

            return Instance;

        }

        private set

        {

            Instance = value;

        }

    }

}

 

일반적인 C# 프로그래밍에서는 싱글톤 패턴이 위의 코드와 같이 구현된다. 기본 생성자를 private으로 지정해서 클래스 외부에서 호출하지 못하게 하여서 클래스 외부에서는 오브젝트를 생성할 수 없게 한다. 그리고 Instance라는 프로퍼티를 호출했을 때, Instance 프로퍼티가 비어있다면 처음 한 번 생성하고 그 뒤로를 생성한 Instance를 호출하는 식으로 구현한다.

 

위와 같은 방법으로 오브젝트를 하나만 생성가능하게 만드는 것이 바로 싱글턴 패턴이다. 싱글톤 패턴은 예를들어 센서와 통신하는 클래스처럼 단 하나만 존재해야하는 경우에 사용된다.

 

 

유니티 엔진에서의 싱글톤 패턴

 

유니티 엔진에서도 싱글톤 패턴을 응용해서 사용할 수 있다. 일단 유니티 엔진에서 일반 C# 클래스로 싱글톤 패턴을 사용한다면 위의 코드 예시와 똑같이 구현하면 된다. 하지만 모노비헤이비어를 상속받는 컴포넌트 클래스에서의 싱글톤 패턴 구현은 조금 달라진다.

 

우선 모노비헤이비어를 상속받는 컴포넌트 클래스와 일반 C# 클래스의 차이점을 알아둬야 한다.

 

var newSingleton = new GameObject("Singleton Class");

newSingleton.AddComponent<SingletonClass>();

 

우선, new 연산자를 통해서 생성되는 일반 C# 클래스의 오브젝트와 달리 컴포넌트 클래스의 오브젝트를 생성할 때는 게임 오브젝트를 생성한다음에 AddComponent() 함수를 이용해서 게임 오브젝트에 컴포넌트를 붙여줘야 한다. 그렇기 때문에 일반 C# 클래스와 같이 생성자를 private으로 바꾸는 방법으로는 클래스 외부에서의 오브젝트의 생성을 막을 수 없다.

 

뿐만 아니라, 이미 컴포넌트가 부착된 오브젝트가 씬에 만들어져 있는 경우도 있을 수 있고, 컴포넌트가 부착된 프리팹이 생성되는 경우까지 있다. 그렇기 때문에 일반 C# 클래스와 같이 생성자로 외부 생성을 막는 방법은 전혀 유효하지 않다.

 

public class SingletonClass : MonoBehaviour

{

    private static SingletonClass instance;

    public static SingletonClass Instance

    {

        get

        {

            if (instance == null)

            {

                var obj = FindObjectOfType<SingletonClass>();

                if (obj != null)

                {

                    instance = obj;

                }

                else

                {

                    var newSingleton = new GameObject("Singleton Class").AddComponent<SingletonClass>();

                    instance = newSingleton;

                }

            }

            return instance;

        }

        private set

        {

            instance = value;

        }

    }

 

    private void Awake()

    {

        var objs = FindObjectsOfType<SingletonClass>();

        if (objs.Length != 1)

        {

            Destroy(gameObject);

            return;

        }

        DontDestroyOnLoad(gameObject);

    }

}

 

컴포넌트 클래스의 특성에 유의해서 싱글톤 패턴을 작성하면 위의 예시 코드와 같이 구현된다.

 

우선, 일반 C# 클래스에서와 같이 instance가 null인지 검사를 하되 곧바로 오브젝트를 생성하지 말고 씬에 이미 게임 오브젝트에 부착된 싱글톤 클래스가 있는지 검사하고 만약, 이미 있다면 instance에 넣어준 뒤, instance를 반환한다. 씬에 존재하지 않는 상태라면, 싱글톤 클래스의 오브젝트가 컴포넌트로 부착된 게임 오브젝트를 생성하고 이를 반환한다.

 

그리고 게임 오브젝트가 생성되거나 컴포넌트가 게임 오브젝트에 부착되었을 때 가장 먼저 실행되는 Awake() 함수에서 씬에 이미 존재하는 같은 싱글톤 오브젝트가 있는지 검사를 한 뒤, 이미 존재하는 오브젝트가 있다면 지금 생성된 오브젝트를 파괴하는 작업을 진행한다.

 

마지막으로 씬을 전환할때 싱글톤 패턴이 적용된 오브젝트가 파괴되는 것을 막기 위해 DontDestroyOnLoad()를 적용해준다. 보통 유니티에서 싱글톤 패턴을 적용하는 경우는 시스템 매니저와 같이 시스템 전체를 관리하는 오브젝트처럼 여러 씬에서 살아있어야 하는 오브젝트가 대부분이다.

 

 

싱글톤 패턴의 장점과 유용성

 

public class SingletonTester : MonoBehaviour

{

    void Start()

    {

        var singleton = SingletonClass.Instance;

        singleton.Function();

    }

}

 

싱글톤 패턴의 장점과 유용성은 게임이나 프로그램에 단 하나만 존재해야하는 클래스를 다룰 때 나타난다. 단 하나만 존재해야 하는 클래스의 생성을 적절하게 통제할 수 있으며, 전체에 단 하나이기 때문에 따로 매번 오브젝트를 생성하거나 탐색해서 찾을 필요없이 instance 프로퍼티를 통해서 빠르게 접근할 수 있다.

 

 

싱글톤 패턴의 단점과 해악

 

싱글톤 패턴의 장점은 프로그램에 단 하나여야 하는 클래스의 오브젝트를 다루는데 뛰어난 효율성을 보여준다는 것인데, 어떤 개발자들은 빠르고 편하게 사용할 수 있다는 점에 주목한다. 그리고 싱글톤 패턴의 모든 단점과 해악성은 여기에서 나온다.

 

오브젝트를 따로 탐색하거나 생성할 필요가 없이 바로 instance 호출로 바로 가져올 수 있다는 점에 취해서 쉽게 남용되는 패턴이 바로 싱글톤 패턴이다. 이 때문에 조금이라도 가져오기 어려운 오브젝트가 있으면 싱글톤 패턴으로 만들어버리거나, 기능을 싱글톤 패턴을 가진 오브젝트에 합쳐버리는 우를 범하게 된다.

 

일이 이렇게 진행되기 시작하면, 여기저기서 각기 다른 클래스의 instance를 호출하는 구조로 코드의 흐름이 알기 어려워지고 싱글톤 패턴을 가진 오브젝트의 코드 크기는 점점 거대해져서 기형적으로 비대한 구조가 만들어지며, 나중에는 따로 분리해내거나 정리하기 어려워진다.

 

때문에 싱글톤 패턴을 사용할 때는 편리함에 너무 취하지 않아야 하며, 해당 클래스가 너무 비대화되지 않는지, 처리하기로 설계한 기능 이외의 것을 처리하려고 하고 있지 않은지를 끊임없이 경계해야 한다.

반응형
  1. ㅇㅇㅇㅇ 2021.09.22 18:33

    안녕하세요 ~. 글 잘 읽었습니다.
    질문이 한가지 있는데요! 혹시 오브젝트 풀링에서 사용하신 싱글턴에서는
    초기화 방지하는 부분이 빠진것 같은데 이유가 무엇인지 여쭈어보아도 될까요?

+ Recent posts