2025. 3. 29. 10:29ㆍ유니티 unity/디자인패턴
유니티 개발을 하다 보면 한 번쯤은 마주치는 패턴, 바로 싱글톤(Singleton)입니다. 특히 게임 매니저, 오디오 매니저, UI 매니저처럼 게임 전체에서 하나만 존재해야 하는 시스템을 구현할 때 자연스럽게 떠오르죠.
하지만 이 싱글톤 패턴, 많은 분들이 '편리하다'는 이유로 남용하거나, 정확한 개념 없이 사용해서 의존성이 꼬이는 코드로 이어지는 경우가 꽤 많습니다. 이번 글에서는 싱글톤 패턴이 무엇인지, 왜 필요한지, 유니티에서는 어떻게 쓰는 게 바람직한지, 그리고 실전에서 사용할 수 있는 예제 코드까지 정리해드리겠습니다.
싱글톤 패턴이란?
싱글톤 패턴은 클래스의 인스턴스를 단 하나만 생성하도록 보장하고, 전역 접근을 가능하게 하는 디자인 패턴입니다.
즉, 어떤 클래스가 프로젝트 전체에서 딱 하나만 존재하고, 어디서든 쉽게 접근할 수 있어야 할 때 사용하는 구조입니다.
"하나의 인스턴스를 여러 곳에서 공유하고 싶다" → 싱글톤
왜 유니티에서 자주 쓰일까?
유니티에서는 다음과 같은 클래스들이 자연스럽게 싱글톤 형태로 사용됩니다:
- GameManager: 게임 상태를 전반적으로 관리
- AudioManager: 배경음악, 효과음 등 사운드 전반 처리
- UIManager: 공통 UI 열기/닫기, 팝업 관리 등
- ObjectPoolManager: 오브젝트 풀링 처리
- SaveManager: 저장/불러오기 기능
이처럼 여러 곳에서 접근해야 하지만, 인스턴스는 하나여야 하는 경우가 많습니다.
기본 싱글톤 구현 (비 MonoBehaviour)
public class ConfigManager
{
private static ConfigManager instance;
public static ConfigManager Instance
{
get
{
if (instance == null)
instance = new ConfigManager();
return instance;
}
}
private ConfigManager() { }
public void LoadConfig()
{
Debug.Log("설정 불러오기 완료");
}
}
이 방식은 순수 C# 클래스에서 사용할 수 있으며, 일반적인 클래스 로직 관리나 데이터 저장용으로 많이 사용됩니다.
MonoBehaviour 기반 싱글톤 (유니티용)
유니티에서는 대부분의 컴포넌트가 MonoBehaviour를 상속받기 때문에, 다음과 같은 형태로 구현합니다:
public class GameManager : MonoBehaviour
{
public static GameManager Instance { get; private set; }
private void Awake()
{
if (Instance != null && Instance != this)
{
Destroy(gameObject);
return;
}
Instance = this;
DontDestroyOnLoad(gameObject);
}
public void StartGame()
{
Debug.Log("게임 시작!");
}
}
이 구조에서는:
- 한 인스턴스만 존재하게 만들고
- 씬이 전환되어도 살아남도록 DontDestroyOnLoad로 설정합니다.
실전 예제 1: 오디오 매니저
public class AudioManager : MonoBehaviour
{
public static AudioManager Instance { get; private set; }
[SerializeField] private AudioSource bgmSource;
[SerializeField] private AudioClip[] bgmClips;
private void Awake()
{
if (Instance != null && Instance != this)
{
Destroy(gameObject);
return;
}
Instance = this;
DontDestroyOnLoad(gameObject);
}
public void PlayBGM(int index)
{
if (index < bgmClips.Length)
{
bgmSource.clip = bgmClips[index];
bgmSource.Play();
}
}
}
다른 스크립트에서 다음과 같이 사용할 수 있습니다:
AudioManager.Instance.PlayBGM(0);
실전 예제 2: UI 매니저
public class UIManager : MonoBehaviour
{
public static UIManager Instance { get; private set; }
[SerializeField] private GameObject pausePanel;
private void Awake()
{
if (Instance != null && Instance != this)
{
Destroy(gameObject);
return;
}
Instance = this;
DontDestroyOnLoad(gameObject);
}
public void TogglePauseUI(bool show)
{
pausePanel.SetActive(show);
}
}
UIManager.Instance.TogglePauseUI(true);
자주 하는 실수들
실수 문제점
DontDestroyOnLoad 남용 | 여러 씬에서 중복 생성 가능 → 의도치 않은 다중 인스턴스 |
Instance = FindObjectOfType<T>() | 느리고 예외 발생 가능 (비추천) |
정적 필드 남용 | 테스트 불가능, 구조 경직 |
싱글톤을 너무 많이 생성 | 결국 전역 상태 객체 남발 → 유지보수 지옥 |
언제 싱글톤을 쓰면 안 될까?
- 다수 인스턴스가 필요한 경우 (예: 적 캐릭터, 아이템 등)
- 상태 기반 클래스(State 패턴 등)에 직접 접근해야 할 경우
- 테스트 가능한 구조를 만들고 싶을 때 (DI가 더 적합)
- 멀티플레이어에서 클라이언트마다 개별 동작이 필요한 경우
무조건 싱글톤으로 만들기보다, 왜 전역이어야 하는가? 정말 단 하나여야 하는가? 를 먼저 고민해야 합니다.
싱글톤과 함께 쓰기 좋은 패턴들
- 퍼사드 패턴: 여러 매니저를 하나의 진입점으로 통합
- 서비스 로케이터: 런타임에 싱글톤을 찾아 제공
- DI(의존성 주입): 테스트 가능하고 유연한 구조로 싱글톤을 주입
싱글톤은 유니티 개발에서 매우 강력한 도구입니다. 하지만 제대로 이해하지 않고 남용하면, 구조를 오히려 망칠 수 있습니다.
정말 단 하나만 존재해야 하고, 어디서든 접근해야 하는 객체에만 신중하게 적용하세요.
그렇다면 싱글톤은 분명 단순하지만 강력한 무기가 될 수 있습니다.
'유니티 unity > 디자인패턴' 카테고리의 다른 글
유니티에서 빌더(Builder) 패턴 활용하기 (0) | 2025.03.29 |
---|---|
유니티에서 템플릿 메서드(Template Method) 패턴 활용하기 (0) | 2025.03.29 |
유니티에서 퍼사드(Facade) 패턴을 제대로 활용해보자 (0) | 2025.03.29 |
유니티에서 이벤트 버스(Event Bus) 패턴을 활용한 유연한 시스템 만들기 (0) | 2025.03.29 |
유니티에서 옵저버 패턴(Observer Pattern)을 제대로 활용해보세요 (0) | 2025.03.29 |