본문 바로가기
C#

Unity 에서 Singleton 클래스에 Instance로 접근하는 이유

by Oz Driver 2025. 4. 30.

 

C# 또는 Unity에서 싱글톤 클래스를 만들 때, 우리는 보통 두 가지 접근 방식을 선택하게 됩니다.

하나는 Instance를 공개하여 Manager.Instance.SomeFunction()처럼 접근하는 방식이고, 다른 하나는 Instance를 숨기고 변수나 함수를 static으로 포장하여 Manager.SomeFunction()처럼 접근하는 방식입니다. 두 방법 모두 실제로 동작하지만, 장기적으로 보면 Instance를 명시적으로 사용하는 방식이 훨씬 낫습니다.

 

Instance를 공개하는 방식

public class GameManager : MonoBehaviour 
{ 
    public static GameManager Instance { get; private set; } 
    
    public void StartGame() { } 
}

 

사용법 :

GameManager.Instance.StartGame();

 

항상 Instance를 거쳐서 접근하게 됩니다.

 

Instance를 숨기고 static으로 포장하는 방식

public class GameData : MonoBehaviour 
{ 
    private static GameData _instance; 
    
    public static bool IsGameStarted 
    { 
        get { return _instance.isGameStarted; } 
    } 
    
    private bool isGameStarted; 
}​
 

사용법 :

if (GameData.IsGameStarted) 
{ 
    // 처리 
}

 

겉으로는 그냥 static처럼 보입니다.

 

왜 static으로만 포장해서 사용하는 건 안 좋을까요?

정말로 static으로만 포장해서 사용하는 게 가능하긴 하지만, 시간이 지나면 몇 가지 문제가 생길 수 있습니다.

실제로는 Instance를 사용하고 있다.

static으로 포장해도, 내부에서는 결국 _instance를 참조하게 됩니다.

이 _instance는 Unity 오브젝트에 의존하고 있기 때문에, 씬 전환이나 수동으로 Destroy() 호출, 에디터에 Play/Stop 반복 같은 상황에서 이 오브젝트가 파괴되면, Instance는 null이 되고, static처럼 믿고 접근하는 순간 크래시가 발생할 수 있습니다.

겉은 static처럼 보이지만, 실제로는 인스턴스를 사용하고 있기 때문에 코드에서 상태 관리와 싱글톤의 철학을 무시할 위험이 큽니다.

Unity의 수명주기와 static 포장 방식은 맞지 않다.

Unity는 오브젝트를 동적으로 생성하고 파괴하는 구조입니다. 하지만 static 변수는 프로그램 메모리에 고정되어 존재합니다.

DontDestroyOnLoad()와 같은 메서드를 사용하지 않으면 씬 전환 시 static 변수로 참조하고 있던 오브젝트가 파괴되므로, 불안정한 동작이나 메모리 누수가 발생할 수 있습니다.

 

싱글톤 철학과 어긋난다.

싱글톤 패턴은 하나의 살아있는 객체를 관리하는 디자인 패턴입니다.

싱글톤은 객체가 존재해야 하고, 필요할 때 살아있음을 보장할 수 있어야 하며, 때로는 적절하게 종료될 수 있어야 합니다.

static 포장 방식은 객체의 생명주기를 무시하게 만들어 싱글톤의 설계 철학에 어긋나게 됩니다.

 

static 으로만 이루어진 클래스와의 차이점

여기서 정말 중요한 차이가 있습니다. Mathf 같은 클래스는 애초에 **상태(state)**를 가질 필요가 없어서 static으로만 구현됩니다.

float clamped = Mathf.Clamp(value, min, max);

 

Mathf는 어떤 값을 저장하지도 않고, 과거의 계산 결과를 기억하지 않으며, 단순히 입력값을 계산해서 돌려주는 함수입니다. 이러한 클래스는 인스턴스를 만들 필요가 없으므로 static으로 구현하는 게 맞습니다.

 

반면, GameManager 나 GameData 같은 클래스는 게임 상태를 저장하고, 이벤트를 관리하며, 살아있는 객체처럼 동작해야 합니다.

따라서 살아있는 객체를 다루면서 static처럼 포장하는 것은 Mathf와 같은 순수 기능 클래스와는 설계 목적 자체가 완전히 다릅니다.

 

마무리

static으로 포장된 방식은 편리해 보일 수 있지만, 클래스의 수명주기와 상태 관리를 고려할 때는 Instance를 명시적으로 거쳐서 접근하는 방식이 더 안정적이고 일관성이 있습니다.

 

싱글톤은 하나의 살아있는 객체를 관리해야 하기 때문에, Instance를 통해 접근하는 것이 설계의 의도에 맞고, 게임 매니저나 데이터 관리 클래스는 Instance를 외부에 공개하여 설계 의도를 명확히 해야 합니다.

 

따라서, Instance로 접근하는 것은 귀찮음을 참는 문제가 아니라, 코드의 일관성과 시스템 안정성을 지키기 위한 기본 원칙입니다.