직렬화 Serialization
- 오브젝트나 연결된 오브젝트의 묶음을 바이트 스트림으로 변경하는 과정.
- 복잡한 데이터를 일렬로 세워버린다.(복잡 → 간단)
Object → Byte Stream : 직렬화 (Serialization), Byte Stream → Object : 역직렬화 (Deserialization) - 즉, 직렬화로 복잡한 데이터를 간단하게 만들면, 데이터를 전송하거나 맞바꿀 때
1줄로 되어있는 간단한 바이트 스트림으로 변환하고 복구할 수 있다.
직렬화 장점
- 프로그램의 상태 저장, 복원 가능 (게임 세이브)
- 객체 정보를 클립보드에 복사해서 다른 프로그램으로 전송가능
- 네트워크를 통해 프로그램의 상태를 복원할 수 있음 (멀티게임에서 자주 사용)
- 직렬화된 데이터는 압축과 암호화를 통해서 데이터를 안전하게 보관 가능
직렬화 구현시 고려할 점
- 데이터 레이아웃
오브젝트가 가지고 있는 데이터를 어떻게 변환할 것인가? - 이식성
서로 다른 시스템에 전송해도 이식 가능? (데이터저장방식인 엔디안이 서로 다르면 곤란) - 버전 관리
새로운 기능이 추가될 때 이를 어떻게 확장할 것인가? (새로운 장비가 생기면 이걱ㄴ 어떻게 처리할건지) - 성능
네트워크 비용을 줄이기 위해서 어떤 데이터 형식을 사용할 것인가? (회전의 경우 16개중 4개만 보냄(양자화)) - 보안
데이터를 어떻게 안전하게 보호할 것인가? - 에러 처리
전송 과정에서 문제가 발생할 경우 어떻게 인식하고 처리할 것인가? (접속 끊기면 어떻게 할건지)
언리얼 엔진의 직렬화 시스템
언리얼 엔진에서는 직렬화 시 고려할 점을 알아서 처리한 직렬화 시스템을 제공한다.(미친 편의성)
- FArchive
직렬화 시스템을 위해 제공되는 아카이브 클래스
Shift(<<) operator를 이용해서 데이터 자체를 바이트 스트림으로 바꿀 수 있다. - 메모리 아카이브 (FMemoryReader, FMemoryWriter) : 메모리에 전송하는 방법
- 파일 아카이브 (FArchiveFileReaderGeneric, FArchiveFileWriterGeneric) : 파일에 읽고 쓰는 방법
- 기타 언리얼 오브젝트와 관련된 아카이브 클래스 (FArchiveUObject)
- Json 직렬화 기능 : 별도의 라이브러리를 통해서 제공
이 직렬화를 2가지로 실습하는데, 하나는 구조체를 파일에 읽고 쓰기, 하나는 언리얼 오브젝트를 파일에 읽고 쓴다.
실습1 : 구조체데이터 직렬화
1. 구조체 데이터 설계
- 구조체 생성자2개, 변수Order, Name을 초기화한다.
- FArchive는 파일에 데이터를 읽고 쓸때 필요하다. Serialization하는 operator<<을 만들어 둔것.
2. 구조체 데이터 설정 및 저장할 폴더경로와 파일 이름 설정
- 21줄 : 프로젝트 디렉토리를 얻어오는 함수에 Saved라는 폴더를 묶어서 구조체를 저장할 파일 경로를 지정해준다.
- 26줄 : 구조체를 저장할 파일을 만든다. 이름은 RawData.bin
- 27줄 : 저장할 경로(Saved)와 파일을 같이 묶는다.
- 근데 이렇게만 지정하면 정확한 파일 경로를 파악할 수 가 없어서
29줄 MakeStandardFilename으로 파일의 전체 경로를 파악한다.
3. 파일에 데이터 쓰고 불러오기(출력으로 확인 가능)
이제 파일에 데이터를 쓰고 읽어내야하는데,
- 33줄 : IFileManager라는 인터페이스에서 파일을 쓸 수 있는 아카이브를 생성
- 39줄 : 구조체에 만들어둔 operator<< 덕분에 파일에 2가지 변수데이터를 한번에 작성
- 41줄 : 당연히 파일에 데이터 다 썼으면 파일 닫아야지
- 43, 44줄 : 파일에 데이터 다 썼으면, RawFileWriterAr가 필요없어지므로
메모리 누수를 방지하기 위해 삭제, 댕글링 포인터를 방지하기 위해 객체의 포인터 리셋 한다. - 읽어오는 것도 같은 방법인데, 여기서 신기한 점은
파일에 데이터를 불러오든 저장하든 둘다 operator <<을 사용하면 된다는 점이다.
이렇게 구조체에 작성한 데이터를 파일에 적고, 파일 내용을 불러올 수 있다.
실습2 : 언리얼오브젝트 직렬화
1. 언리얼 오브젝트 설계
- 26줄 : 언리얼오브젝트를 파일에 저장, 불러올 때(직렬화 할 때) 사용할 Serialize 함수를 지정했다.
override한 이유는 이미 UObject에 Serialize함수가 구현되어 있기 때문
- 14줄 : Super을 통해 부모클래스인 Serialize를 먼저 실행하면, 이 때 언리얼 오브젝트를 직렬화하는 작업이 포함되어 있다.
- 15, 16줄 : 언리얼오브젝트에서 내가 따로 만든 정보를 추가적으로 구현한다. 여기서는 Operator<<을 따로 만들지 않았으므로, 만든 변수를 하나씩 넣어준다.
2. 오브젝트 생성
MyGameInstance에 프로퍼티로 오브젝트 객체를 선언한다.
3. 오브젝트 데이터 설정 및 저장할 파일 이름 설정
- 선언한 프로퍼티에 오브젝트를 생성하고, 필요한 정보를 Set함수로 넣어준다.
- 파일경로는 구조체를 넣은 경로와 같고 파일 이름은 ObjectData로 정한다.
4. 메모리에 데이터 저장
구조체를 저장할 때는 메모리에 저장을 하진 않았는데, 여기선 한번 해본다.(반드시해야한다! 이건아닌데 그냥 해봄)
- 77줄 : 메모리(버퍼)를 사용할 것이기 때문에 TArray로 버퍼 생성
- 79줄 : 메모리에 데이터를 적는 FMemoryWriter에 내가 만든 BurrderArray를 연동
- 81줄 : 그리고 Serialize해주면 끝 (아까 Student에서 ovrride해줘서)
5. 파일에 데이터 쓰고 불러오기(출력으로 확인 가능) with 스마트 포인터
여기서는 TUniquePtr이라는 스마트 포인터를 사용한다.
잘 보면, 여긴 아카이브를 delete하고 null로 초기화한게 없는데 이걸 TUniquePtr 이 자동으로 해주기 때문
- 89줄 : 스마트 포인터 FileWriterAr을 정하고 이게 파일을 읽을 수 있으면,
- 92줄 : 데이터를 저장하는데, 아까 버퍼에서 byte로 저장했기 때문에 <<연산자로 쉽게 보낼 수 있다.
- 94줄 : 마찬가지로 데이터를 다 저장했으면 파일 닫기
- 97줄 : 데이터를 읽어올 때도 메모리를 사용해서 메모리로 불러온다.
- 108줄 : 메모리에 적은 데이터를 다시 직렬화해서 출력한다.
Json 직렬화
: 웹 환경에서 서버와 클라이언트 사이에 데이터를 주고받을 때 사용하는 텍스트 기반의 데이터 포맷
: 언리얼에서 Json을 사용하려면, Json, JsonUtility를 사용할 수 있다.
장점
- 텍스트임에도 데이터의 크기가 가볍다
- 읽기가 편해서 데이터를 보고서 이해할 수 있다
- 사실상 웹통신의 표준으로도 널리 사용된다
단점
- 지원하는 타입이 몇개 안된다(문자, 숫자, bool, null, 배열, 오브젝트)
- 텍스트 형식으로만 사용이 가능하다. 숫자 타입의 경우에는 int float타입을 구분못함
Json데이터 유형
- 오브젝트 : 키, 밸류 조합으로 구성 {"key" : 10}
- 배열 : 밸류로만 구성 ["value1", "value2"]
- 문자열 : "string"
- 숫자 : 10 or 3.14
- 불리언 : true or false
- 널 : null
언리얼 스마트 포인터 라이브러리 (Json을 사용하기 위함)
- 스마트 포인터 : 일반 C++ 오브젝트의 포인터 문제를 해결해주는 언리얼 엔진의 라이브러리
- TUniquePtr(유니크포인터) : 지정한 곳에서만 메모리를 관리하는 포인터
- 특정한 오브젝트에게 명확하게 포인터 해지 권한을 주고 싶은 경우
- delete 구문 없이 자동으로 소멸시키고 싶을 때 사용
- TSharedPtr(공유포인터) : 더 이상 사용하지 않는다면 자동으로 메모리를 해지하는 포인터
- 여러 로직에서 할당된 오브젝트가 공유해서 사용되는 경우
외부에서 사용을 안한다? 자동으로 해제(삭제) - 다른 함수로부터 할당된 오브젝트를 Out으로 받는 경우
- 포인터 값이 null일 수 있음
- 여러 로직에서 할당된 오브젝트가 공유해서 사용되는 경우
- TSharedRef(공유레퍼런스) : 공유포인터는 동일한데, 항상 유효한 객체를 가지는 레퍼런스
- 포인터와 다르게 레퍼런스로 사용, 여러 로직에서 할당된 오브젝트가 공유해서 사용되는 경우
- Not Null을 보장을 받으며 오브젝트를 편리하게 사용하고 싶은 경우에 사용
실습 : Json 직렬화
1. 먼저 언리얼에서 Json을 사용하기 위해서는 언리얼 오브젝트를 Json으로 변환해주는 헬퍼 라이브러리를 헤더에 추가한다. #include "JsonObjectConverter.h"
2. 저장할 경로, 파일 이름 설정
3. 언리얼 오브젝트를 Json오브젝트로 변환
- 122줄 : 공유 레퍼런스 TSharedRef 으로 JsonObjectSrc를 지정한다.
= 뒤에 MakeShared라는 API로 object를 초기화?생성?해서 null이 아님을 보장받는다. - 124줄 : JsonObjectConverter.h 헬퍼 라이브러리를 통해 공유레퍼런스를 넘겨서 언리얼 오브젝트를 Json오브젝트로 변환한다.
언리얼 오브젝트도 UStruct를 상속받기 때문에 UStructToJsonObject라고 설정
이 변환하는 과정에서 그냥 빌드를 진행하면,
헤더에 있는 구현부가 없다는 에러가 난다.
→ 프로젝트이름의 모듈(라이브러리)에 Json과 관련된 라이브러리를 연동해야한다.
여기서 ModuleNames에 "Json", "JsonUtilities" 를 추가한다.
4. Json데이터 파일에 저장
- 127줄 : Json데이터가 문자열이니까 FString으로 구성
- 128줄 : JsonString으로 연동시킨 아카이브 생성
(SharedRef을 사용하는데, TJsonWriterFactory에 의해서 Json으로 써주는 아카이브가 생성됨) - 130줄 : 직렬화를 하는 과정으로 JsonObject와 아카이브를 넣어준다.
- 133줄 : 직렬화가 되었다면 JsonText String이 만들어졌을 것이고, 그걸 file에 저장한다.
5. 파일에 저장한 Json데이터 불러오기
- 136줄 : 불러올 정보인 JsonInString변수 선언
- 137줄 : FFileHelper로 문자열을 불러온다.
- 138줄 : 불러온 문자열 JsonInString으로 reader아카이브를 설정한다.
- 140줄 : reader아카이브로부터 실제로 변환할 JsonObject를 만드는데, Json값이 없을 수 도 있어서 공유포인터로 선언
- 141줄 : 아카이브에서 JsonObject로 연동
- 143줄 : Json오브젝트를 언리얼 오브젝트로 변환하기 위해서 먼저 언리얼 오브젝트를 생성
- 144줄 : JsonObjectConverter로 다시 Json오브젝트에서 언리얼 오브젝트로 변환
공유포인터를 공유레퍼런스로 변환하고, JsonStudentDest에 있는 클래스정보(스키마)를 넣고, 객체 정보를 집어넣는다. - 146줄 : 호출 함수로 출력
'하고싶은거 > Unreal' 카테고리의 다른 글
Part1 15. 언리얼 빌드 시스템 (0) | 2024.03.16 |
---|---|
Part1 14. 언리얼 오브젝트 관리 : 패키지, 애셋 (0) | 2024.03.16 |
Part1 12. 언리얼 엔진 메모리 관리 (0) | 2024.03.13 |
Part1 11. 언리얼 컨테이너 라이브러리 UCL : UStruct, TMap (0) | 2024.03.11 |
Part1 10. 언리얼 컨테이너 라이브러리 UCL : TArray, TSet (1) | 2024.03.11 |
#개발 #게임 #일상
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!