간단한 데이터 직렬화와 역직렬화 기법
지난 절에서 게임 데이터를 디스크에 저장하고 불러오는 저장 게임 오브젝트(Save Game Object) 에 대해 알아보았습니다. 이 과정의 핵심에는 직렬화(Serialization) 와 역직렬화(Deserialization) 라는 개념이 숨어 있습니다. 이들은 데이터를 효율적으로 저장하고 통신하는 데 필수적인 컴퓨터 과학의 기본 원리이며, 언리얼 엔진이 내부적으로 어떻게 데이터를 다루는지 이해하는 데 도움이 됩니다.
직렬화(Serialization)란 무엇인가?
직렬화는 메모리(RAM) 상에 존재하는 복잡한 객체(Object)의 데이터를 디스크에 저장하거나 네트워크를 통해 전송할 수 있는 형태로 변환하는 과정입니다. 메모리 상의 객체는 여러 변수가 서로 다른 메모리 주소에 흩어져 있거나, 복잡한 포인터 관계로 얽혀 있을 수 있습니다. 이러한 구조를 그대로 디스크에 저장하거나 다른 컴퓨터로 보내는 것은 불가능합니다.
직렬화는 이러한 복잡한 메모리 구조를 연속적인 바이트 스트림(Byte Stream), 즉 하나의 긴 일렬 데이터로 변환합니다. 이렇게 변환된 데이터는 파일로 저장되거나 네트워크를 통해 전송될 수 있습니다.
비유: 마치 살아있는 복잡한 생물(메모리 객체)을 박제하거나, 설계도를 표준화된 문서(바이트 스트림)로 만드는 것과 같습니다. 박제된 생물이나 표준화된 설계도는 보관하거나 이동하기 쉽습니다.
언리얼 엔진의 저장 게임 오브젝트는 바로 이 직렬화 과정을 거쳐 디스크에 파일 형태로 저장됩니다. 블루프린트 개발자는 Save Game to Slot
노드를 호출함으로써 이 복잡한 직렬화 과정을 언리얼 엔진에게 맡기는 것입니다.
역직렬화(Deserialization)란 무엇인가?
역직렬화는 직렬화된 바이트 스트림 데이터를 다시 메모리 상의 원래 객체 형태로 복원하는 과정입니다. 디스크에 저장된 파일이나 네트워크를 통해 수신된 바이트 스트림을 해석하여, 게임 내에서 다시 사용할 수 있는 객체로 재구성하는 것이죠.
비유: 박제된 생물을 다시 살아있는 생물로 복원하거나, 표준화된 설계도를 보고 실제 건물을 다시 짓는 것과 같습니다.
언리얼 엔진의 Load Game From Slot
노드를 호출하면, 디스크의 저장 파일이 역직렬화 과정을 거쳐 SaveGame Object
형태로 메모리에 복원됩니다.
블루프린트와 직렬화/역직렬화
블루프린트를 사용할 때 대부분의 직렬화/역직렬화 과정은 언리얼 엔진 내부적으로 자동으로 처리됩니다. 개발자가 직접 바이트 스트림을 다룰 필요는 거의 없습니다. 하지만 어떤 데이터가 직렬화될 수 있고 없는지 이해하는 것은 중요합니다.
- 저장 게임 오브젝트의 변수:
BP_MySaveGame
과 같은 저장 게임 오브젝트 블루프린트에 선언된 변수들(Integer, Float, Vector, Text, Array, Map, Structure, Object Reference 등)은 언리얼 엔진이 기본적으로 직렬화를 지원합니다. 따라서 이 변수들에 저장된 값들은Save Game to Slot
호출 시 자동으로 저장 파일에 기록됩니다. - 복잡한 오브젝트 참조: 만약 저장 게임 오브젝트 내에서 특정 액터(Actor) 자체에 대한 직접적인 참조(
Actor Reference
)를 저장하려고 한다면 주의해야 합니다. 일반적인Actor Reference
는 레벨이 로드될 때 메모리 주소가 변경될 수 있으므로, 액터 자체를 저장하기보다는 액터의 고유 ID(Get Actor Unique ID
)나 특정 이름(Get Name
)을 저장하고, 로드 시 해당 ID나 이름으로 액터를 다시 찾아 연결하는 방식을 사용하는 것이 훨씬 안정적입니다.- 예외: 언리얼 엔진 5.0부터 도입된
Soft Object Reference
나Soft Class Reference
는 직렬화가 가능하며, 필요한 시점에 로드하여 사용할 수 있도록 설계된 참조 타입입니다. 이는 에셋(Static Mesh, Material 등)에 대한 참조를 저장할 때 유용합니다.
- 예외: 언리얼 엔진 5.0부터 도입된
- 컴포넌트의 변수: 액터 블루프린트 내 컴포넌트의 변수는 기본적으로 해당 액터가 저장될 때 함께 직렬화되지 않습니다. 컴포넌트의 특정 상태를 저장하고 싶다면, 그 상태를 액터 블루프린트의 변수에 복사하여 저장 게임 오브젝트에 담거나, 저장/로드 로직을 직접 구현해야 합니다.
직렬화와 역직렬화의 활용 (고급 개념)
언리얼 엔진의 내장 저장 시스템 외에도, 직렬화/역직렬화는 다음과 같은 상황에서 간접적으로 활용되거나 이해가 필요합니다.
- 네트워크 통신: 멀티플레이어 게임에서 클라이언트와 서버 간에 데이터를 주고받을 때, 메모리상의 게임 상태는 네트워크를 통해 전송 가능한 바이트 스트림으로 직렬화되어야 합니다.
- 에셋 관리: 언리얼 엔진의 모든 에셋(Static Mesh, Texture, Material, Blueprint 등)은 디스크에 파일 형태로 저장될 때 직렬화됩니다. 엔진이 이 파일을 로드할 때 역직렬화하여 메모리 상의 에셋 객체로 만듭니다.
- 커스텀 저장 형식:
SaveGame
시스템 외에 완전히 커스텀된 저장 형식을 만들고자 할 때, 파일을 직접 읽고 쓰는 로직을 구현해야 하며, 이때 어떤 데이터를 어떤 순서와 형태로 바이트로 변환하고 다시 복원할지 직렬화/역직렬화의 개념이 필요합니다.
블루프린트 개발에서는 대부분 SaveGame Object
를 통해 직렬화/역직렬화의 복잡성을 추상화하여 사용하지만, 이 개념을 이해하고 있으면 왜 특정 타입의 데이터가 저장될 수 있고 없는지, 그리고 고급 저장 기능을 구현할 때 어떤 문제가 발생할 수 있는지에 대한 통찰력을 얻을 수 있습니다. 이는 데이터를 다루는 게임 개발의 근간이 되는 중요한 원리입니다.
이번 절에서는 데이터 직렬화와 역직렬화의 기본 개념, 그리고 언리얼 엔진 블루프린트에서 이들이 어떻게 작동하는지에 대해 알아보았습니다.