Unity – Singleton 패턴의 일반 구현

2025/11/12 수정

using UnityEngine;

/// <summary>
/// 제네릭 싱글톤 베이스 클래스
/// 모든 MonoBehaviour 기반 싱글톤이 상속받을 수 있는 기본 구현체
/// </summary>
/// <typeparam name="T">싱글톤으로 구현할 컴포넌트 타입</typeparam>
public abstract class Singleton<T> : MonoBehaviour where T : Component
{
    // 싱글톤 인스턴스 저장 필드
    private static T instance;

    // 스레드 안전성을 위한 lock 객체
    private static readonly object lockObject = new object();

    // 어플리케이션 종료 상태 플래그 (종료 시 잘못된 접근 방지)
    private static bool applicationIsQuitting = false;

    /// <summary>
    /// 싱글톤 인스턴스에 접근하기 위한 프로퍼티
    /// 인스턴스가 없을 경우 자동으로 생성
    /// </summary>
    public static T Instance
    {
        get
        {
            // 어플리케이션이 종료 중인 경우 null 반환
            if (applicationIsQuitting)
            {
                Debug.LogWarning($"[Singleton] Instance '{typeof(T)}' already destroyed. Returning null.");
                return null;
            }

            // 스레드 안전성을 위해 lock 사용
            lock (lockObject)
            {
                // 인스턴스가 없는 경우 새로 생성
                if (instance == null)
                {
                    // 씬에 이미 존재하는 인스턴스 검색
                    instance = FindAnyObjectByType<T>();

                    // 씬에 존재하지 않는 경우 새로 생성
                    if (instance == null)
                    {
                        // 새로운 게임오브젝트 생성
                        GameObject singletonObject = new GameObject();
                        singletonObject.name = $"{typeof(T).Name} (Singleton)";

                        // 컴포넌트 추가
                        instance = singletonObject.AddComponent<T>();

                        // 씬 전환시 파괴되지 않도록 설정
                        DontDestroyOnLoad(singletonObject);

                        Debug.Log($"[Singleton] Created new instance of {typeof(T).Name}");
                    }
                }
                return instance;
            }
        }
    }

    /// <summary>
    /// 컴포넌트가 활성화될 때 호출됨
    /// 싱글톤 초기화 수행
    /// </summary>
    protected virtual void Awake()
    {
        InitializeSingleton();
    }

    /// <summary>
    /// 싱글톤 초기화 로직
    /// 인스턴스 중복 생성 방지 및 설정 관리
    /// </summary>
    protected virtual void InitializeSingleton()
    {
        // 스레드 안전성을 위해 lock 사용
        lock (lockObject)
        {
            // 인스턴스가 아직 설정되지 않은 경우
            if (instance == null)
            {
                // 현재 인스턴스를 싱글톤으로 설정
                instance = this as T;

                // 씬 전환시 파괴되지 않도록 설정
                DontDestroyOnLoad(gameObject);
            }
            // 이미 인스턴스가 존재하고 현재 인스턴스와 다른 경우 (중복 생성)
            else if (instance != this)
            {
                Debug.LogWarning($"[Singleton] Multiple instances of {typeof(T).Name} found. Destroying duplicate.");

                // 중복 인스턴스 제거
                Destroy(gameObject);
            }
        }
    }

    /// <summary>
    /// 어플리케이션이 종료될 때 호출됨
    /// 인스턴스 접근 방지를 위해 플래그 설정
    /// </summary>
    protected virtual void OnApplicationQuit()
    {
        applicationIsQuitting = true;
    }

    /// <summary>
    /// 컴포넌트가 파괴될 때 호출됨
    /// 인스턴스 정리 수행
    /// </summary>
    protected virtual void OnDestroy()
    {
        // 현재 파괴되는 인스턴스가 싱글톤 인스턴스인 경우
        if (instance == this)
        {
            // 어플리케이션 종료 플래그 설정
            applicationIsQuitting = true;
        }
    }
}

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SingletonMonobehaviour<T> : MonoBehaviour where T : MonoBehaviour
{
    static T _Instance = null;
    public static T Instance
    {
        get
        {
            if (_Instance == null)
            {
                //_Instance가 Null일 경우 찾아본다.
                _Instance = (T)FindObjectOfType(typeof(T));
                if (_Instance == null)
                {
                    var _NewGameObject = new GameObject(typeof(T).ToString());
                    _Instance = _NewGameObject.AddComponent<T>();
                }

            }
            return _Instance;
        }
    }

    protected virtual void Awake()
    {
        if (_Instance == null)
        {
            _Instance = this as T;
        }
        DontDestroyOnLoad(gameObject);

    }
}


// Singleton 상속
public class TestApp : SingletonMonobehaviour<TestApp>
{
    protected override void Awake()
    {
        base.Awake();

    }

    public void TestFunction()
    { 
   
    }
}

public class TestSingleton : MonoBehaviour
{
    private void Awake()
    {
        TestApp.Instance.TestFunction();
    }
}

클래스의 인스턴스를 반환하는 공용 정적 속성

인스턴스가 null이면 장면에서 클래스의 기존 인스턴스를 검색

인스턴스를 찾을 수 없으면 새 인스턴스를 생성

 if (_Instance == null)
            {
                //_Instance가 Null일 경우 찾아본다.
                _Instance = (T)FindObjectOfType(typeof(T));
                if (_Instance == null)
                {
                    var _NewGameObject = new GameObject(typeof(T).ToString());
                    _Instance = _NewGameObject.AddComponent<T>();
                }

            }
            return _Instance;

인스턴스가 장면에 하나만 존재하도록 하는 편리한 방법으로

게임 상태, 컨트롤러 또는 여러 인스턴스를 가져서는 안 되는 기타 구성 요소를 관리하는 데 유용

댓글 달기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

위로 스크롤