[Unity] ScriptableObject 파헤치기

ChangBeom·2025년 11월 26일

Unity

목록 보기
14/17
post-thumbnail

Unity에서 게임 데이터를 관리할 때 자주 사용하는 기능이 바로 ScriptableObject이다.

하지만 막상 내부 구조가 어떻게 되어 있는지, 왜 MonoBehaviour와 다르게 동작하는지 확실하게 알지 못해서 이번 기회에 파헤쳐보려고 한다.


[1. ScriptableObject란?]

Unity 공식 문서에는 ScriptableObject를 "클래스 인스턴스를 에셋 파일 형태로 저장할 수 있는 데이터 컨테이너"라고 설명한다.

하지만 이런 문장만 봐서는 정확한 감이 오지 않기 마련이다.

간단히 말하면 ScriptableObject는

씬에 존재하지 않아도 살아 있는 독립적인 C# 객체

이다.

MonoBehaviour처럼 GameObject에 붙을 필요도 없고, Prefab처럼 복사본이 여러 개 생기지도 않는다.


[2. ScriptableObject는 어떻게 메모리에 존재할까?]

이 부분이 가장 궁금한 부분이다.

ScriptableObject는 프로젝트 폴더에 저장된 '에셋 파일'이다. 하지만 런타임에 불러오면 다음과 같은 구조가 된다.

<1. Unity는 ScriptableObject 에셋을 읽어들임>
AssetDatabase 또는 Resources/Addressable 로딩을 통해 메모리에 로드된다.

<2. 메모리에 "단일 인스턴스"가 생성됨>
씬마다 복사본이 생기지 않고, 프로젝트 전체에서 하나의 인스턴스만 존재한다.

따라서 여러 오브젝트가 같은 ScriptableObject를 참조하면 모두 동일한 데이터를 공유하는 효과가 있다.

이게 Prefab과 가장 큰 차이점이다.


[3. MonoBehaviour, Prefab과 비교해 보면 더 명확해진다]

  • MonoBehaviour
    • 항상 GameObject와 함께 존재
    • 씬이 바뀌면 오브젝트도 Destroy
    • 인스턴스는 씬마다 존재
  • Prefab
    • GameObject 구조를 템플릿처럼 저장한 것
    • Instantiate 할 때마다 새로운 복사본이 생성됨
  • ScriptableObject
    • GameObject 필요 없음
    • 프로젝트 전체에서 단일 인스턴스로 존재
    • 씬이 바뀌어도 유지됨
    • 데이터 공유에 최적화됨

즉,

ScriptableObject = 데이터 저장용 싱글 인스턴스 에셋

이라고 이해하면 된다.


[4. ScriptableObject가 "메모리를 아낀다"의 진짜 의미]

많은 개발자들이 ScriptableObject를 사용하면 메모리를 아낀다는 사실은 알고 있을 것이다.

이 말의 본질은 중복 인스턴스가 생기지 않는다 라는 뜻이다.

예를 들어,

public class ItemInfo : ScriptableObject
{
	public string name;
    public int price;
}

이걸 100개의 오브젝트가 다같이 참조해도 인스턴스는 메모리에 딱 1개만 존재한다.

Prefab이나 MonoBehaviour처럼 오브젝트가 늘어날 때마다 데이터가 복사되는 구조가 아니다.

-> 대규모 데이터 기반 시스템에서는 이 점이 매우 중요하다.


[5. ScriptableObject가 왜 데이터 관리에 적합할까?]

ScriptableObject의 장점은 크게 세 가지이다.

<1. 에셋 파일로 저장되어 직렬화 지원이 뛰어남>

  • Unity Inspector에서 바로 편집 가능
  • 값이 자동으로 디스크에 저장됨
  • 빌드시에도 포함됨
  • JSON을 따로 만들 필요 없음

<2. 동일 인스턴스를 여러 곳에서 공유 가능>

  • 여러 UI, 시스템, 로직에서 하나의 데이터를 참조 가능
  • 데이터 변경 -> 즉시 반영
  • Manager 객체와 궁합이 좋음

<3. 씬과 생명주기가 분리되어 있음>
이건 DontDestroyOnLoad와도 비슷할 수 있는데,

"ScriptableObject는 아예 씬에 소속되지 않는다."

그래서

  • 씬을 전환해도 유지됨
  • OnDestroy 영향을 받지 않음
  • 글로벌 데이터처럼 활용 가능

[6. ScriptableObject 사용 시 주의할 점]

당연히 장점만 있는 기능은 아니다. 알고 쓰지 않으면 예상치 못한 문제가 생길 수 있으니 꼭 주의할 점을 숙지하자.

<1. 에디터에서는 값이 원본 에셋에 바로 반영됨>
런타임 중 수정하면 OnValidate를 통해 프로젝트의 에셋 값이 실제로 변경될 수 있다.
따라서 "런타임에서 수정 가능한 데이터"로 쓰는 것은 위험하다.


<2. 런타임 수정은 메모리 인스턴스만 바뀐다>
파일에는 반영되지 않기 때문에 다음 실행 때 값이 초기화된다.


<3. Instantiate를 사용하면 복사본이 생긴다>
ScriptableObject는 기본적으로 단일 인스턴스이지만, Instantiate(SO)를 하면 복사본이 생긴다.
따라서 디자인 의도를 명확히 하여 잘 구분해야 한다.


[7. 정리]

ScriptableObject의 핵심은 다음과 같다.

씬에 속하지 않고, 프로젝트 전체에서 하나의 인스턴스로 존재하는 데이터 에셋

이 구조 덕분에 ScriptableObject는

  • 데이터 중복 제거
  • 메모리 낭비 감소
  • 글로벌 데이터 관리
  • 설계/밸런싱 편의성

같은 다양한 장점들을 가진다.


0개의 댓글