[CS] 싱글톤 패턴
개발 공부를 하다 보면 여러 디자인 패턴에 대해 많이 알게 된다. 그중 대체로 초반부에 자주 언급되며 배우게 되는 게 아마 이 “싱글톤 패턴”이 아닐까 싶다. 본인도 개발을 하면서 싱글톤 패턴을 잘 사용하는지에 대해, 또 잘 이해하고 쓰고 있는게 맞는지 의문을 가질 때가 있다. 그렇기에 이렇게 포스팅을 통해 싱글톤 패턴을 정리해보려 한다.
싱글톤 패턴
- 싱글톤 패턴은 오직 한 개의 클래스 인스턴스만 갖도록 보장
- 인스턴스가 필요할 때, 똑같은 인스턴스를 생성하는 것이 아닌 기존의 인스턴스를 활용하게 함
- 생성자가 여러번 호출돼도, 실제로 생성되는 객체는 하나
- 이로 인해서 메모리 낭비를 방지
- 전역적인 접근을 제공하기에 데이터 공유가 가능
이렇게 여러가지 기능들과 의미가 존재한다. 우리는 이러한 디자인 패턴을 통해 유니티에서는 아래와 같이 활용할 수 있다.
유니티 활용
전역변수
예를 들어서 RPG게임에서는 여러 맵(Scene)으로 이동을 할 텐데, 내가 1번 Scene에서 벌은 재화가 2번 Scene에서 소멸된다면 그건 문제일 것이다. 이런 경우 싱글톤 패턴을 이용하여 재화를 정의한다면 Scene을 이동하더라도 재화가 유지될 것이다. 이렇듯 전역적으로 Scene에서 사용될 것들을 싱글톤 패턴을 통해 정의해주면 된다.
게임매니저
Scene을 관리하는 매니저가 만약 여러개라면 분명 이는 게임 구조적으로 문제가 생길 수 있는 큰 요인이다. 여러분들의 일을 가르쳐주는 상사가 두명이라 생각하면 1번 상사는 나한테 이렇게 하라 했는데, 2번 상사는 다르게 말한다면 혼동의 요인이 될 수 있듯(물론 실제로는 상사가 여럿인게 맞긴 하지만), 게임을 관리하는 Manager도 여럿이면 문제가 생길 수 있다.
예제 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
// 게임 오버 상태를 표현하고, 게임 점수와 UI를 관리하는 게임 매니저
// 씬에는 단 하나의 게임 매니저만 존재할 수 있다.
public class GameManager : MonoBehaviour {
public static GameManager instance; // 싱글톤을 할당할 전역 변수
public bool isGameover = false; // 게임 오버 상태
public Text scoreText; // 점수를 출력할 UI 텍스트
public GameObject gameoverUI; // 게임 오버시 활성화 할 UI 게임 오브젝트
private int score = 0; // 게임 점수
// 게임 시작과 동시에 싱글톤을 구성
void Awake() {
// 싱글톤 변수 instance가 비어있는가?
if (instance == null) {
// instance가 비어있다면(null) 그곳에 자기 자신을 할당
instance = this;
}
else {
// instance에 이미 다른 GameManager 오브젝트가 할당되어 있는 경우
// 씬에 두개 이상의 GameManager 오브젝트가 존재한다는 의미.
// 싱글톤 오브젝트는 하나만 존재해야 하므로 자신의 게임 오브젝트를 파괴
Debug.LogWarning("씬에 두개 이상의 게임 매니저가 존재합니다!");
Destroy(gameObject);
}
}
void Update() {
// 게임 오버 상태에서 게임을 재시작할 수 있게 하는 처리
if(isGameover && Input.GetMouseButtonDown(0)) {
SceneManager.LoadScene(SceneManager.GetActiveScene().name); // 현재 씬의 이름을 가져와 로드(= 재실행)
}
}
// 점수를 증가시키는 메서드
public void AddScore(int newScore) {
if(!isGameover) {
score += newScore;
scoreText.text = "Score : " + score;
}
}
// 플레이어 캐릭터가 사망시 게임 오버를 실행하는 메서드
public void OnPlayerDead() {
isGameover = true;
gameoverUI.SetActive(true);
}
}
위의 코드는 현재 책을보고 따라 개발중인 “유니런”이라는 단순 횡스크롤 점프 게임의 GameManager 스크립트이다. 게임 매니저는 싱글톤 패턴으로 디자인되어 있고, 매니저는
- 게임 오버(캐릭터 사망)
- 게임 스코어
두가지를 관리하고 있다. 이 둘은 다른 매니저가 관리하게 된다면 분명 스코어가 40이어야 되는 상황에서 다른 매니저와 충돌이 나 스코어가 줄어들거나, 아직 사망하지 않았지만 사망하는 판정이 나는 경우가 발생할 수 있다. 그렇기에 싱글톤 패턴을 이용하여 여러 Manager가 생기지 않도록 Awake() 함수에서 처리를 해준 것이다.
주의할 점
- 여러 클래스의 커플링이 발생될 수 있음
- 하나의 코드를 수정했을 때 싱글톤과 연결된 다양한 곳들에서 문제가 발생
- 모든 곳에서 접근이 가능하기에 Race condition이 발생할 수 있음
- Race condition은 하나의 자원을 여러곳에서 사용하려는 상황을 말함
- 이를 막기 위해 mutex lock, unlock이 반복적으로 이루어지면서 코드의 성능 하락
뭔가 싹다 싱글톤 클래스 안에 때려박으면 편할거 같고 하지만, 그렇게 무지성으로 사용한다면 분명 코드 구조적으로 문제가 여럿 생길것이다. 위와같은 여러 문제점들이 나타날 수 있다. 그렇기에 주의할 점에 대해서 많이 찾아보고, 익히고 이해하며 사용해야 된다.
정리
싱글톤 패턴은 분명 좋은 장점도 가지지만, 남용했을때의 단점도 존재한다. 모든 코드적 편의기능들은 다들 이러한 장단점이 존재하는 것은 당연하다 생각한다. 그렇기에 각 패턴, 기능들에 대해 잘 이해하고 사용한다면 분명 나쁜것은 아니니 본인도, 여러분들도 여러 포스팅 참고하며 잘 이해해서 좋은 코드, 좋은 게임 만드셨으면 좋겠습니다.
참고링크
블로그 1 : https://stickode.tistory.com/178 블로그 2 : https://art-life.tistory.com/130 블로그 3 : https://ansohxxn.github.io/design%20pattern/chapter2/#-싱글톤-패턴이란