[UE5] 이득우 Part 1. 13강 언리얼 오브젝트 관리Ⅰ - 직렬화

공부 스파이럴·2023년 12월 26일
0

직렬화(Serialization)

  • 오브젝트나 연결된 오브젝트의 묶음(오브젝트 그래프)을 디스크나 네트워크로 전송할 수 있는 바이트 스트림으로 변환하는 과정
    • 복잡한 데이터를 일렬로 세우기 때문에 직렬화
  • 변환 작업 + 복구 작업 포함해서 직렬화
    • Serialization: 오브젝트 그래프 -> 바이트 스트림
    • DeSerialization: 바이트 스트림 -> 오브젝트 그래프
  • 직렬화 장점
    • 현재 프로그램 상태를 저장하고 필요할 때 복원 가능
      • 게임 저장
    • 현재 객체의 정보를 클립보드에 복사해서 다른 프로그램으로 전송 가능
    • 네트워크를 통해 현재 프로그램의 상태를 다른 컴퓨터에 복원 가능
      • 멀티플레이 게임
    • 데이터 압축, 암호화를 통해 데이터를 효율적이고 안전하게 보관 가능

직렬화 구현시 고려할 점

  • 다양한 상황을 고려해 일관성있게 동작하는 표준 동작을 기획하고 구현해야 함
    • 데이터 레이아웃: 어떤 오브젝트가 가지고 있는 데이터를 어떤 식으로 일렬로 세울 것인가
    • 이식성: 변환된 데이터가 다른 시스템의 전송에도 똑같이 인식
    • 버전 관리: 새로운 기능이 추가될 때 이를 어떻게 확장하고 처리할 것인가
    • 성능: 네트워크 비용을 줄이기 위해 어떤 데이터 형식을 사용
      • 양자화: 데이터를 최대한 줄여서 네트워크를 통해 상대방에게 정보를 보냄
    • 보안: 네트워크를 통해 데이터가 전송될 때 다른 사람들도 볼 수 있기 때문에 어떻게 안전하게 보호할 것인가
    • 에러 처리: 전송 과정에서 문제가 발생할 경우 어떻게 인식하고 처리할 것인가

언리얼 엔진의 직렬화 시스템

  • 언리얼 엔진은 이러한 상황을 모두 고려한 직렬화 시스템을 자체적으로 제공
  • 직렬화 시스템을 위해 제공하는 클래스 FArchive와 연산자
    • 아카이브 클래스(FAchive)
    • Shift operator(<<)
  • 다양한 아카이브 클래스의 제공
    • 메모리 아카이브(FMemoryReader, FMemoryWriter)
    • 파일 아카이브(FArchiveFileReaderGeneric, FArchiveFileWriterGeneric)
    • 기타 언리얼 오브젝트와 관련된 아카이브 클래스(FArchiveUObject)
  • Json 직렬화 기능: 별도의 라이브러리를 통해 제공

실습

  • 저장할 파일 경로
    • FPlatformMisc::ProjectDir 을 통해 프로젝트 경로
    • FPaths::Combine 을 통해 경로 및 파일 이름 합치기
    • FPaths::MakeStandardFilename 을 통해 경로를 정상화
      • 아마 이상한 상대경로를 절대경로로 바꿔주는 듯
  • Writer 열기 및 쓰기
    • FArchive 의 상속을 받으므로 FArchive로 선언
    • IFileManager::Get().CreateFileWriter 를 통해 경로의 파일을 열음
    FArchive* Writer = IFileManager::Get().CreateFileWriter(*경로);
    • << 연산자를 통해서 데이터를 쓸 수 있음
    • 데이터에 << 연산자를 오버로딩해서 사용할 수 있음
    friend FArchive& operator<<(FArchive & Ar, 구조체& In구조체)
    {
    	Ar << 구조체.Data;
      return Ar;
    }
    • 사용 후 Close 호출 및 deletenullptr 대입
    if (nullptr != Writer)
    {
      *Writer << 구조체데이터
      Writer->Close();
      delete Writer;
      Writer = nullptr;
    }
  • Reader 열기 및 읽기
    • Writer와 거의 동일
    • IFileManager::Get().CreateFileReader 로 열음

UObject 실습

  • 오브젝트 직렬화
    • TArray< uint8 >버퍼 생성
    • FMemoryWriter 생성
    • Serialize
TArray<uint8> BufferArray;
FMemoryWriter MemoryWriterAr(BufferArray);
오브젝트->Serialize(MemoryWriterAr);
  • 데이터 쓰기
    • delete, nullptr 안쓰려면 TUniquePtr 사용
    • 읽기도 읽어올 버퍼 생성하고 Reader로 진행
if (TUniquePtr<FArchive> FileWriterAr = TUniquePtr<FArchive>(IFileManager::Get().CreateFileWriter(*경로)))
{
  *FileWriterAr << BufferArray;
  FileWriterAr->Close();
}
  • 읽어온 파일로 UObject 생성
    • FMemoryReader 생성
    • 오브젝트 생성 및 Serialize로 데이터 채워주기
FMemoryReader MemoryReaderAr(파일 읽은 버퍼);
언리얼오브젝트* 오브젝트 = NewObject<언리얼오브젝트>();
오브젝트->Serialize(MemoryReaderAr);

Json 직렬화

  • JavaScript Object Notation
  • 웹 환경에서 서버와 클라이언트 사이에서 데이터를 주고 받을 때 사용하는 텍스트 기반 데이터 포맷
  • 장점
    • 텍스트지만 데이터가 가벼움
    • 읽기 편해서 데이터를 보고 이해 가능
    • 웹 통신의 표준 수준으로 널리 사용
  • 단점
    • 지원하는 타입이 몇 안됨
      • 문자, 숫자, boolean, null, 배열, 오브젝트만 사용 가능
      • 숫자는 특히 타입을 사실 상 구분 불가능
    • 텍스트 형식으로만 사용 가능
  • 언리얼 엔진에서는 Json, JsonUtilities 라이브러리 활용

Json 데이터 예시

  • 오브젝트: {}
    • 오브젝트 내 데이터는 키, 벨류 조합으로 구성
    • {"key":10}
  • 배열: []
    • 배열 내 데이터는 벨류로만 구성
    • ["value1","value2","value3"]
  • 이외 데이터
    • 문자열("string")
    • 숫자(10 또는 3.14)
    • 불리언(true 또는 false)
    • 널(null)

언리얼 스마트 포인터 개요

  • Json 라이브러리를 사용하려면 언리얼 스마트 포인터 라이브러리 알아두면 좋음
  • TUniquePtr(유니크포인터): 지정한 곳에서만 메모리를 관리
    • 특정 오브젝트에게 명확하게 포인터 해지 권한을 줌
    • delete 구문 없이 함수 실행 후 자동으로 소멸
  • TSharedPtr(공유포인터): 더 이상 사용되지 않으면 자동으로 메모리를 해지
    • 여러 로직에서 할당된 오브젝트가 공유해서 사용
    • 다른 함수로부터 할당된 오브젝트를 Out으로 받음
    • Null 일 수 있음
  • TSharedRef(공유레퍼런스): 공유포인터와 동일하지만, 유효한 객체를 항상 보장
    • 여러 로직에서 할당된 오브젝트가 공유해서 사용
    • Not Null을 보장받으며 오브젝트를 편리하게 사용

Json 실습

  • #include "JsonObjectConverter.h" 해야함
  • 프로젝트.Build.cs 파일에 모듈 이름에 "Json", "JsonUtilities" 추가해야함

  • FJsonObject 생성
TSharedRef<FJsonObject> Json오브젝트 = 
MakeShared<FJsonObject>();
  • UObject를 FJsonObject로 변환
FJsonObjectConverter::UStructToJsonObject(
U오브젝트->GetClass(), U오브젝트, Json오브젝트);
  • 문자열로 Json오브젝트 변환 및 파일 저장
    • Json Writer 생성
    • Serialize
    • FFileHelper::SaveStringToFile
TSharedRef<TJsonWriter<TCHAR>> JsonWriterAr =
TJsonWriterFactory<TCHAR>::Create(&스트링);
if (FJsonSerializer::Serialize(Json오브젝트, JsonWriterAr))
{
	FFileHelper::SaveStringToFile(스트링, *경로);
}
  • 읽어들이기
    • 스트링으로 불러오기
    • Json Reader 생성
    • Deserialize
    • FJsonObjectConverter::JsonObjectToUStruct
FFileHelper::LoadFileToString(저장스트링, *경로);
TSharedRef<TJsonReader<TCHAR>> JsonReaderAr =
TJsonReaderFactory<TCHAR>::Create(저장스트링);
TSharedPtr<FJsonObject> 저장Json오브젝트;
if (FJsonSerializer::Deserialize(
JsonReaderAr, 저장Json오브젝트))
{
	U오브젝트* 저장오브젝트 = NewObject<U오브젝트>();
	if (FJsonObjectConverter::JsonObjectToUStruct(
    저장Json오브젝트.ToSharedRef(),
    저장오브젝트->GetClass(),
    저장오브젝트))
	{
    	//...
	}
}

0개의 댓글