Serialization (직렬화)
Serialization 즉, 직렬화는 객체(Object) 데이터를 저장하거나 전송할 수 있는 형태(바이트 스트림)로 변환하는 과정을 의미합니다. 이 과정은 데이터를 일렬로 정리(즉, 직렬화)하고, 이를 다시 원래 상태로 복구하는 과정까지 포함합니다.
언리얼 엔진에서는 데이터를 저장하거나, 네트워크로 전송하거나, 객체를 복제(Clone)하는 데 사용됩니다.
직렬화와 복원(역직렬화)의 간단한 설명
- 직렬화(Serialization): 오브젝트 그래프(복잡한 데이터 구조)를 바이트로 이루어진 데이터 스트림으로 변환
- 역적렬화(DeSerialization): 변환된 바이트 스트림을 다시 원래 오브젝트 그래프로 복구.
✅ 직렬화의 주요 목적
- 게임 데이터 저장 & 로드 (세이브/로드 시스템)
- 네트워크 데이터 전송 (멀티플레이어 동기화)
- 객체 복사 & 복제 (데이터 변환 & 임시 저장)
✅ 직렬화의 장점
- 현재 상태 저장 및 복원:
- 게임 데이터를 저장 후 다시 불러와서 이전 상태로 복구 가능
- 예: 게임 세이브 데이터 저장 & 불러오기
- 데이터 공유:
- 현재 객체 정보를 다른 시스템이나 프로그램으로 전송 가능
- 예: 클립보드로 데이터 공유
- 네트워크 전송:
- 데이터를 네트워크를 통해 다른 클라이언트와 동기화
- 예: 멀티플레이어 게임의 상태 동기화
- 효율적이고 안전한 데이터 관리:
- 데이터를 압축하여 저장 공간 절약
- 암호화를 통해 보안 강화
직렬화 시 고려할 주요 요소
✅ (1) 데이터 레이아웃 (Data Layout)
- 데이터 구조가 너무 복잡하면 직렬화 속도 저하
- 필수 데이터만 직렬화하여 최적화
- 중첩된 객체를 다룰 때 순환 참조(Circular Reference) 문제 해결 필요
✅ (2) 이식성 (Portability)
- 엔디언(Endian) 변환(Big Endian vs. Little Endian) 고려
- UTF-8 인코딩 사용하여 다국어 지원
- JSON, XML 같은 플랫폼 독립적인 데이터 형식 사용
✅ (3) 버전 관리 (Versioning)
- 기존 저장 데이터와의 호환성을 유지해야 함
- 데이터 구조 변경 시, 역직렬화 과정에서 충돌을 방지하기 위한 버전 번호 추가
- 새로운 필드가 추가되면 기본값을 설정하여 처리
✅ (4) 성능 최적화 (Performance)
- 바이너리 직렬화(FArchive) 사용 → 속도 최적화
- 데이터 압축 (LZ4, Zlib) 적용 → 크기 감소
- 필요한 데이터만 저장 → 불필요한 데이터 최소화
✅ (5) 보안 (Security)
- 데이터 암호화(AES, RSA) 적용 → 해킹 방지
- 무결성 체크(SHA256, CRC) 수행 → 데이터 변조 방지
- JSON, XML 등 텍스트 기반 직렬화는 조작 가능성이 높아 추가 보안 필요
✅ (6) 에러 처리 (Error Handling)
- 데이터 손실 또는 파일 손상 방지를 위한 백업 시스템 필요
- 역직렬화할 때 예외 처리(Try-Catch) 적용
- 네트워크 전송 시 패킷 손실을 대비한 재전송(Retry) 메커니즘 적용
요약
✔ 데이터 레이아웃: 효율적인 데이터 저장 구조 설계
✔ 이식성: 다른 플랫폼에서도 동작할 수 있도록 고려
✔ 버전 관리: 데이터 변경 시에도 하위 호환 유지
✔ 성능 최적화: 데이터 크기를 줄이고 직렬화 속도 향상
✔ 보안 강화: 암호화 및 무결성 체크 적용
✔ 에러 처리: 데이터 손실 시 복구 메커니즘 마련
언리얼 엔진의 직렬화 방식
언리얼 엔진에서는 직렬화를 지원하는 다양한 방법이 존재합니다.
방식 | 특징 | 장점 | 단점 |
FArchive 기반 바이너리 직렬화 | 바이너리 데이터를 빠르게 변환 | 빠르고 최적화됨 | 데이터 해석 어려움 |
FJsonObject 기반 JSON 직렬화 | JSON 형식으로 저장 | 사람이 읽기 쉬움, 디버깅 편리 | 속도가 느리고 크기가 큼 |
USaveGame 시스템 | USaveGame을 활용한 자동 저장 | 사용하기 쉬움 | 확장성 제한 |
커스텀 직렬화 (Override Serialize() 함수) | Serialize() 함수 재정의 | 유연한 데이터 저장 가능 | 직접 구현해야 함 |
FArchive를 이용한 바이너리 직렬화
언리얼 엔진의 FArchive 클래스는 바이너리 데이터 직렬화를 담당합니다.
이를 사용하면 객체 데이터를 파일 또는 메모리에 저장하고, 다시 불러올 수 있습니다.
✅ FArchive 직렬화 예제
FArchive& operator<<(FArchive& Ar, FMyData& Data)
{
Ar << Data.PlayerLevel;
Ar << Data.PlayerName;
Ar << Data.Health;
return Ar;
}
📌 << 연산자 오버로딩을 통해 데이터 저장 & 로드 가능
SaveGame 클래스를 활용한 직렬화
언리얼 엔진에서는 USaveGame 클래스를 사용하여 게임 데이터를 손쉽게 저장할 수 있습니다.
✅ USaveGame을 활용한 저장 & 불러오기
// 1. SaveGame 클래스를 상속받아 데이터 구조 정의
UCLASS()
class UMySaveGame : public USaveGame
{
GENERATED_BODY()
public:
UPROPERTY(SaveGame)
int32 PlayerLevel;
UPROPERTY(SaveGame)
FString PlayerName;
UPROPERTY(SaveGame)
float Health;
};
// 2. 저장 기능 구현
void SaveGameData()
{
UMySaveGame* SaveInstance = Cast<UMySaveGame>(UGameplayStatics::CreateSaveGameObject(UMySaveGame::StaticClass()));
SaveInstance->PlayerLevel = 10;
SaveInstance->PlayerName = TEXT("Hero");
SaveInstance->Health = 100.0f;
UGameplayStatics::SaveGameToSlot(SaveInstance, TEXT("SaveSlot1"), 0);
}
// 3. 불러오기 기능 구현
void LoadGameData()
{
UMySaveGame* LoadInstance = Cast<UMySaveGame>(UGameplayStatics::LoadGameFromSlot(TEXT("SaveSlot1"), 0));
if (LoadInstance)
{
UE_LOG(LogTemp, Log, TEXT("Player Level: %d"), LoadInstance->PlayerLevel);
UE_LOG(LogTemp, Log, TEXT("Player Name: %s"), *LoadInstance->PlayerName);
UE_LOG(LogTemp, Log, TEXT("Health: %f"), LoadInstance->Health);
}
}
📌 SaveGame은 직렬화를 쉽게 구현할 수 있는 UE5의 기본 시스템!
FJsonObject를 활용한 JSON 직렬화
JSON 형식은 사람이 읽을 수 있어 디버깅과 파일 공유에 용이합니다.
하지만 바이너리보다 크기가 크고 속도가 느린 단점이 있습니다.
✅ JSON 직렬화 예제
// 1. JSON 객체 생성
TSharedPtr<FJsonObject> JsonObject = MakeShareable(new FJsonObject());
JsonObject->SetStringField("PlayerName", "Hero");
JsonObject->SetNumberField("PlayerLevel", 10);
JsonObject->SetNumberField("Health", 100.0f);
// 2. JSON 문자열로 변환
FString OutputString;
TSharedRef<TJsonWriter<>> Writer = TJsonWriterFactory<>::Create(&OutputString);
FJsonSerializer::Serialize(JsonObject.ToSharedRef(), Writer);
// 3. JSON 저장
FFileHelper::SaveStringToFile(OutputString, *(FPaths::ProjectDir() + "SaveData.json"));
📌 JSON은 디버깅과 데이터 공유가 편리하지만, 바이너리보다 속도가 느림!
언제 어떤 직렬화 방식을 사용할까?
✅ 빠른 데이터 저장 & 최적화가 필요하다면?
➡ FArchive 기반 바이너리 직렬화 사용
✅ 게임 세이브/로드가 필요하다면?
➡ USaveGame 시스템 사용
✅ 디버깅 & 데이터 공유가 필요하다면?
➡ FJsonObject JSON 직렬화 사용
✅ 커스텀 데이터 저장이 필요하다면?
➡ Serialize() 함수 오버라이딩 사용
'Unreal > Unreal For C++' 카테고리의 다른 글
FMath (0) | 2025.02.01 |
---|---|
DELEGATE (0) | 2025.01.17 |