인프런 커뮤니티 질문&답변

hush park님의 프로필 이미지

작성한 질문수

이득우의 언리얼 프로그래밍 Part1 - 언리얼 C++의 이해

13강 언리얼 오브젝트 관리 I - 직렬화

직렬화 관련하여 궁금한 점이 있습니다.

작성

·

34

·

수정됨

0

22:43 부분에서, 제가 실수로

void UStudent::Serialize(FArchive& Ar)

{

Super::Serialize(Ar);

 

Ar << Name;

}

 

처럼 Ar<<Order; 를 적지 않은 상태에서 31:19 부분까지 진행을 하였고, 출력 결과가 놀랍게도 "[ObjectData] 이름 이득우 순번 59" 가 출력이 되었습니다.

분명 StudentSrc->Serialize(MemoryWriterAr); 에서 Ar << Order; 가 누락되어서 직렬화를 진행하지 못했고, BufferArray 에는 59 라는 Order가 저장되지 않았을 것일텐데, 따라서 결과적으로 print함수를 통해 순번이 59가 아닌 생성자에서 설정한 -1로 떠야 한다고 생각하지만 이상하게도 59라는 숫자가 출력됩니다.

이 말은 Ar<<Order; 를 누락했더라도 내부적으로 알아서 직렬화를 진행했다고 볼수밖에 없는 것일까요...?

만약 그렇다면 어떤 코드부분에서 StudentSrc의 Order를 MemoryWriterAr가 소유한 BufferArray 에 저장하게 되는 것인지 궁금합니다.

 

다음은 저의 코드입니다.

(제가 실수하지 않았다면, 사실상 Student.cpp ->void UStudent::Serialize(FArchive& Ar) 함수에서 Ar << Order; 가 누락된 것을 제외하고 전부 동일합니다.)

 


//MyGameInstance.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"

#include "Engine/GameInstance.h"

#include "MyGameInstance.generated.h"

struct FStudentData

{

FStudentData() {}

FStudentData(int32 InOrder, const FString& InName) : Order(InOrder), Name(InName) {}

 

friend FArchive& operator<<(FArchive& Ar, FStudentData& InStudentData)

{

Ar << InStudentData.Order;

Ar << InStudentData.Name;

return Ar;

}

int32 Order = -1;

FString Name = TEXT("홍길동");

};

/**

*

*/

UCLASS()

class UNREALSERIALIZATION_API UMyGameInstance : public UGameInstance

{

GENERATED_BODY()

public:

UMyGameInstance();

virtual void Init() override;

private:

UPROPERTY()

TObjectPtr<class UStudent> StudentSrc;

};


//MyGameInstance.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "MyGameInstance.h"

#include "Student.h"

void PrintStudentInfo(const UStudent* InStudent, const FString& InTag)

{

UE_LOG(LogTemp, Log, TEXT("[%s] 이름 %s 순번 %d"), InTag, InStudent->GetName(), InStudent->GetOrder());

}

UMyGameInstance::UMyGameInstance()

{

}

void UMyGameInstance::Init()

{

Super::Init();

FStudentData RawDataSrc(16, TEXT("이득우"));

const FString SavedDir = FPaths::Combine(FPlatformMisc::ProjectDir(), TEXT("Saved"));

UE_LOG(LogTemp, Log, TEXT("저장할 파일 폴더 : %s"), *SavedDir);

{

const FString RawDataFileName(TEXT("RawData.bin"));

FString RawDataAbsolutePath = FPaths::Combine(*SavedDir, *RawDataFileName);

UE_LOG(LogTemp, Log, TEXT("저장할 파일 전체 경로 : %s"), *RawDataAbsolutePath);

FPaths::MakeStandardFilename(RawDataAbsolutePath);

UE_LOG(LogTemp, Log, TEXT("변경된 파일 전체 경로 : %s"), *RawDataAbsolutePath);

FArchive* RawFileWriterAr = IFileManager::Get().CreateFileWriter(*RawDataAbsolutePath);

if (nullptr != RawFileWriterAr)

{

*RawFileWriterAr << RawDataSrc;

/*RawDataSrc << RawFileWriterAr;/

RawFileWriterAr->Close();

delete RawFileWriterAr;

RawFileWriterAr = nullptr;

 

}

FStudentData RawDataDest;

FArchive* RawFileReaderAr = IFileManager::Get().CreateFileReader(*RawDataAbsolutePath);

if (nullptr != RawFileReaderAr)

{

*RawFileReaderAr << RawDataDest;

RawFileReaderAr->Close();

delete RawFileReaderAr;

RawFileReaderAr = nullptr;

UE_LOG(LogTemp, Log, TEXT("[RawData] 이름 %s 순번 %d"), *RawDataDest.Name, RawDataDest.Order);

}

}

StudentSrc = NewObject<UStudent>();

StudentSrc->SetName(TEXT("이득우"));

StudentSrc->SetOrder(59);

{

const FString ObjectDataFileName(TEXT("ObjectData.bin"));

FString ObjectDataAbsolutePath = FPaths::Combine(*SavedDir, *ObjectDataFileName);

FPaths::MakeStandardFilename(ObjectDataAbsolutePath);

TArray<uint8> BufferArray;

FMemoryWriter MemoryWriterAr(BufferArray);

StudentSrc->Serialize(MemoryWriterAr);

if (TUniquePtr<FArchive> FileWriterAr = TUniquePtr<FArchive>(IFileManager::Get().CreateFileWriter(*ObjectDataAbsolutePath)))

{

*FileWriterAr << BufferArray;

FileWriterAr->Close();

}

TArray<uint8> BufferArrayFromFile;

if (TUniquePtr<FArchive> FileReaderAr = TUniquePtr<FArchive>(IFileManager::Get().CreateFileReader(*ObjectDataAbsolutePath)))

{

*FileReaderAr << BufferArrayFromFile;

FileReaderAr->Close();

}

FMemoryReader MemoryReaderAr(BufferArrayFromFile);

UStudent* StudentDest = NewObject<UStudent>();

StudentDest->Serialize(MemoryReaderAr);

PrintStudentInfo(StudentDest, TEXT("ObjectData"));

}

}


//Student.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"

#include "UObject/NoExportTypes.h"

#include "Student.generated.h"

/**

*

*/

UCLASS()

class UNREALSERIALIZATION_API UStudent : public UObject

{

GENERATED_BODY()

public:

UStudent();

int32 GetOrder() const { return Order; }

void SetOrder(int32 InOrder) { Order = InOrder; }

const FString& GetName() const { return Name; }

void SetName(const FString& InName) { Name = InName; }

virtual void Serialize(FArchive& Ar) override;

private:

UPROPERTY()

int32 Order;

UPROPERTY()

FString Name;

};


//Student.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "Student.h"

UStudent::UStudent()

{

Order = -1;

Name = TEXT("홍길동");

}

void UStudent::Serialize(FArchive& Ar)

{

Super::Serialize(Ar);

 

Ar << Name;

}

 

 

 

답변 2

1

이득우님의 프로필 이미지
이득우
지식공유자

좋은 질문입니다.
FArchive 클래스가 보관하는 데이터는 변수 이름과 값의 쌍으로 구성되어 있습니다. 그리고 순서는 기본적으로 UPROPERTY의 배치 순서를 따릅니다. 이것이 상위클래스 함수인 Super::Serialize(Ar) 가 진행하는 내용으로 간략하게 이해해주시면 될 것 같습니다.
그렇기에 현재 예제에서 Super::Serialize(Ar) 아래의 코드는 사실 큰 의미는 없지요.
( 보통은 로컬 변수를 선언해서 기본 UPROPERTY가 아닌 부가 데이터를 심거나 불러오는데 사용합니다. )
만일 질문 주신대로 특정 필드 값만 넣고 싶다면 Super::Serialize(Ar) 라인을 삭제한 후에 원하는 UPROPERTY에 대해서만 << 연산자를 사용해주면 됩니다.
조금 더 고급적인 내용은 네트워크 Part3에서 다루고 있습니다.
질문을 듣고보니 예제 구성에서 Super::Serialize(Ar) 라인을 삭제하거나 관련 설명을 추가하는 것이 보다 좋겠네요.

감사합니다!

0

안녕하세요, 인프런 AI 인턴입니다.

이번 질문에서는 직렬화에 관한 부분을 살펴보고 싶으신 것 같습니다. 언급하신 문제에서 가장 중요한 부분은 Ar << Order; 가 누락되었음에도 정상적인 값이 출력되는 이유에 대한 의문입니다.

  1. 직렬화와 기본 생성자: UStudent 클래스에서는 Order가 기본적으로 -1로 설정되어 있지만, StudentSrc->SetOrder(59);로 명시적으로 값을 설정하셨습니다. 여기서 핵심은 StudentDest 객체가 초기화될 때, Serialize 함수 내에서 Order 값을 제대로 저장하거나 읽어오지 않더라도, 이전에 설정한 값이 계속 유지된다는 점입니다.

  2. 직렬화의 동작 방식: 기본적으로, 언리얼 엔진에서 Serialize 함수는 부모 클래스의 Serialize도 호출하지만 이때는 해당 클래스에서 명시한 멤버 데이터들만 직렬화합니다. Ar << Name;만 호출하면 Order 값은 변경되거나 저장되지 않으므로, 원래 저장된 값을 유지하게 됩니다.

  3. 메모리 상태 유지: StudentSrcOrder에 59를 설정한 상태고, Serialize에서 Order가 변경되지 않았습니다. 따라서 이 설정이 직렬화 이전과 이후에도 지속되어 출력값이 영향을 받은 것으로 보입니다.

문제를 해결하기 위해서는 UStudent::Serialize 함수 내에서 Ar << Order; 부분도 반드시 포함되어야 함을 기억하세요. 이는 직렬화를 통해 객체의 상태를 온전히 저장하고 불러오는 데 필수적입니다.

아래는 질문의 이해를 돕기 위한 관련된 질문 URL입니다:
- 13강 언리얼 오브젝트 직렬화와 관련된 질문

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏 추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.