간단한 데이터 직렬화와 역직렬화 기법
게임 상태를 저장 가능한 형태로 직렬화하고 다시 객체 상태로 복원하는 흐름과 데이터 변경 시 안정성을 익힙니다.
지난 절에서 Save Game Object를 통해 데이터를 저장하고 불러오는 방법을 익혔다면, 이번에는 그 내부 원리를 짚어볼 차례입니다.
핵심은 직렬화(Serialization)와 역직렬화(Deserialization)입니다.
이 개념을 이해하면 저장 시스템에서 어떤 데이터가 안전하게 보존되는지, 어떤 데이터는 별도 처리가 필요한지 훨씬 명확해집니다.
직렬화(Serialization)란 무엇인가?
직렬화는 메모리(RAM) 상에 존재하는 복잡한 객체(Object)의 데이터를 디스크에 저장하거나 네트워크를 통해 전송할 수 있는 형태로 변환하는 과정입니다.
메모리 상의 객체는 여러 변수가 서로 다른 메모리 주소에 흩어져 있거나, 복잡한 포인터 관계로 얽혀 있을 수 있습니다.
이러한 구조를 그대로 디스크에 저장하거나 다른 컴퓨터로 보내는 것은 불가능합니다.
직렬화는 이러한 복잡한 메모리 구조를 연속적인 바이트 스트림(Byte Stream), 즉 하나의 긴 일렬 데이터로 변환합니다.
이렇게 변환된 데이터는 파일로 저장되거나 네트워크를 통해 전송될 수 있습니다.
언리얼 엔진의 저장 게임 오브젝트는 바로 이 직렬화 과정을 거쳐 디스크에 파일 형태로 저장됩니다.
블루프린트 개발자는 Save Game to Slot 노드를 호출함으로써 이 복잡한 직렬화 과정을 언리얼 엔진에게 맡기는 것입니다.
역직렬화(Deserialization)란 무엇인가?
역직렬화는 직렬화된 바이트 스트림 데이터를 다시 메모리 상의 원래 객체 형태로 복원하는 과정입니다.
디스크에 저장된 파일이나 네트워크를 통해 수신된 바이트 스트림을 해석하여, 게임 내에서 다시 사용할 수 있는 객체로 재구성하는 것이죠.
언리얼 엔진의 Load Game From Slot 노드를 호출하면, 디스크의 저장 파일이 역직렬화 과정을 거쳐 SaveGame Object 형태로 메모리에 복원됩니다.
블루프린트와 직렬화/역직렬화
블루프린트를 사용할 때 대부분의 직렬화/역직렬화 과정은 언리얼 엔진 내부적으로 자동으로 처리됩니다.
개발자가 직접 바이트 스트림을 다룰 필요는 거의 없습니다.
하지만 어떤 데이터가 직렬화될 수 있고 없는지 이해하는 것은 중요합니다.
- 저장 게임 오브젝트의 변수:
Save Game to Slot은 SaveGame 객체의 non-transient 속성을 기록하며, 이 API는SaveGame속성 플래그만 골라 쓰지 않습니다. 숫자·텍스트·벡터·배열·구조체 같은 저장용 값을 담되, 참조를 기록하는 것과 참조 대상의 전체 상태를 저장하는 것은 구별합니다. - 복잡한 오브젝트 참조: 저장 게임 오브젝트에 특정 액터(
Actor Reference)를 직접 넣을 때는 주의해야 합니다. 일반적인 액터 참조만 저장해도 다음 실행에서 그 액터와 내부 상태가 자동 재생성되는 것은 아닙니다. 프로젝트가 실행 간 안정성을 관리하는 식별자를 저장하고, 로드 후 액터를 생성하거나 찾아 연결합니다.Get Unique ID는 객체가 살아 있는 동안만 유일하며 재사용될 수 있고, 자동 생성 이름도 영구 저장 키로 보장되지 않습니다.- 에셋 경로:
Soft Object Reference와Soft Class Reference는 UE5 이전부터 제공된 경로 기반 참조입니다. 메시·머티리얼 같은 에셋을 가리키는 데 유용하지만, 경로가 현재 패키지에 존재하고 로드에 성공하는지 확인해야 합니다. 임의의 런타임 액터 상태를 보존하는 기능은 아닙니다.
- 에셋 경로:
- 컴포넌트의 상태: 이 절의 SaveGame 슬롯 API가 월드 액터와 컴포넌트를 자동 순회해 저장하지는 않습니다. 필요한 컴포넌트 상태를 읽어 SaveGame의 값 필드에 복사하고, 로드 뒤 새 월드의 컴포넌트에 다시 적용합니다. 반드시 액터의 중간 변수를 거칠 필요는 없습니다.
직렬화와 역직렬화의 활용 (고급 개념)
언리얼 엔진의 내장 저장 시스템 외에도, 직렬화/역직렬화는 다음과 같은 상황에서 간접적으로 활용되거나 이해가 필요합니다.
- 네트워크 통신: 멀티플레이어 게임에서 클라이언트와 서버 간에 데이터를 주고받을 때, 메모리상의 게임 상태는 네트워크를 통해 전송 가능한 바이트 스트림으로 직렬화되어야 합니다.
- 에셋 관리: 언리얼 엔진의 모든 에셋(Static Mesh, Texture, Material, Blueprint 등)은 디스크에 파일 형태로 저장될 때 직렬화됩니다. 엔진이 이 파일을 로드할 때 역직렬화하여 메모리 상의 에셋 객체로 만듭니다.
- 커스텀 저장 형식:
SaveGame시스템 외에 완전히 커스텀된 저장 형식을 만들고자 할 때, 파일을 직접 읽고 쓰는 로직을 구현해야 하며, 이때 어떤 데이터를 어떤 순서와 형태로 바이트로 변환하고 다시 복원할지 직렬화/역직렬화의 개념이 필요합니다.
블루프린트 개발에서는 대부분 SaveGame Object를 통해 직렬화/역직렬화의 복잡성을 추상화하여 사용하지만, 이 개념을 이해하고 있으면 왜 특정 타입의 데이터가 저장될 수 있고 없는지, 그리고 고급 저장 기능을 구현할 때 어떤 문제가 발생할 수 있는지에 대한 통찰력을 얻을 수 있습니다.
이는 데이터를 다루는 게임 개발의 근간이 되는 중요한 원리입니다.
저장 안정성 검증 루틴
직렬화/역직렬화 로직을 적용한 뒤에는 다음 순서로 검증하면 안정성이 높아집니다.
- 저장 직후 즉시 로드하여 동일 값 재현 여부 확인
- 게임 재시작 후 로드 테스트로 슬롯 유효성 확인
- 필드 추가/삭제 시 기본값 처리와 버전 호환 정책 점검
- 참조형 데이터는 ID 기반 복원으로 null 참조 여부 확인
이 루틴을 배포 전 체크 항목으로 고정하면 저장 데이터 회귀를 줄일 수 있습니다.
이번 절에서는 데이터 직렬화와 역직렬화의 기본 개념, 그리고 언리얼 엔진 블루프린트에서 이들이 어떻게 작동하는지에 대해 알아보았습니다.