# 커스텀 직렬화

> How to serialize additional items not supported by Unity's serializer.

Unity의 시리얼라이저가 지원하지 않는 요소(C# 딕셔너리 등)를 직렬화하려는 경우, 클래스에 [ISerializationCallbackReceiver](/engine/6000.0/script-reference/unity-engine/i-serialization-callback-receiver.md) 인터페이스를 구현할 수 있습니다. 이를 통해 Unity가 직렬화와 역직렬화 시 키 포인트에서 호출하는 콜백을 다음과 같이 구현할 수 있습니다.

직렬화 콜백을 사용하면 직렬화하기 어려운 데이터를 런타임과 직렬화 시점에 서로 다른 형식으로 표현할 수 있습니다. Unity가 데이터를 직렬화하기 직전에 해당 데이터를 Unity가 이해하는 데이터로 변환할 수 있습니다. Unity가 데이터를 필드에 작성한 후에는 직렬화된 데이터를 런타임 시 원하는 형태로 다시 변환할 수 있습니다.

1. 오브젝트를 직렬할 때 Unity는 `OnBeforeSerialize()` 콜백을 호출합니다. 이 콜백에서 데이터를 Unity가 이해하는 데이터로 변환할 수 있습니다. 예를 들어 C# 딕셔너리를 직렬화하려면 딕셔너리의 데이터를 키 배열과 값 배열로 복사합니다.
2. `OnBeforeSerialize()` 콜백이 종료되면 Unity는 배열을 직렬화합니다.
3. 나중에 오브젝트가 역직렬화될 때 Unity는 `OnAfterDeserialize()` 콜백을 호출합니다. 이 콜백에서는 메모리에 있는 오브젝트에 적합한 형태로 데이터를 다시 변환할 수 있습니다. 예를 들어 키와 값 배열을 사용하여 C# Dictionary를 다시 채울 수 있습니다.

## 예제1: Unity의 기본 직렬화로 인해 성능 문제 발생##example-1:-unity's-default-serialization-causes-performance-issues

트리 데이터 구조를 원한다고 가정해 보겠습니다. Unity가 데이터 구조를 직접 직렬화하도록 하면 'null 지원 없음' 제약으로 인해 데이터 스트림이 매우 비대해져 많은 시스템에서 성능이 저하됩니다.

```lang-C#

using UnityEngine;
using System.Collections.Generic;
using System;

public class VerySlowBehaviourDoNotDoThis : MonoBehaviour {
	[Serializable]
	public class Node {
		public string interestingValue = "value";
		//The field below is what makes the serialization data become huge because
		//it introduces a 'class cycle'.
		public List<Node> children = new List<Node>();
	}
	//this gets serialized
	public Node root = new Node();
	void OnGUI() {
		Display (root);
	}
	void Display(Node node) {
		GUILayout.Label ("Value: ");
		node.interestingValue = GUILayout.TextField(node.interestingValue, GUILayout.Width(200));
		GUILayout.BeginHorizontal ();
		GUILayout.Space (20);
		GUILayout.BeginVertical ();
		foreach (var child in node.children) {
			Display (child);
		}
		if (GUILayout.Button ("Add child")) {
			node.children.Add (new Node ());
		}
		GUILayout.EndVertical ();
		GUILayout.EndHorizontal ();
	}
}
```

## 예제2: 커스텀 직렬화로 성능 문제 방지##example-2:-custom-serialization-avoids-performance-issues

이 경우 Unity가 직접 트리를 직렬화하지 않도록 하고 별도의 필드에 Unity의 시리얼라이저에 적합하도록 직렬화된 포맷으로 트리를 저장합니다.

```lang-C#

using System.Collections.Generic;
using System;

public class BehaviourWithTree : MonoBehaviour, ISerializationCallbackReceiver {
	// Node class that is used at runtime.
	// This is internal to the BehaviourWithTree class and is not serialized.
	public class Node {
		public string interestingValue = "value";
		public List<Node> children = new List<Node>();
	}
	// Node class that we will use for serialization.
	[Serializable]
	public struct SerializableNode {
		public string interestingValue;
		public int childCount;
		public int indexOfFirstChild;
	}
	// The root node used for runtime tree representation. Not serialized.
	Node root = new Node();
	// This is the field we give Unity to serialize.
	public List<SerializableNode> serializedNodes;
	public void OnBeforeSerialize() {
		// Unity is about to read the serializedNodes field's contents.
		// The correct data must now be written into that field "just in time".
		if (serializedNodes == null) serializedNodes = new List<SerializableNode>();
		if (root == null) root = new Node ();
		serializedNodes.Clear();
		AddNodeToSerializedNodes(root);
		// Now Unity is free to serialize this field, and we should get back the expected 
		// data when it is deserialized later.
	}
	void AddNodeToSerializedNodes(Node n) {
		var serializedNode = new SerializableNode () {
			interestingValue = n.interestingValue,
			childCount = n.children.Count,
			indexOfFirstChild = serializedNodes.Count+1
		}
		;
		serializedNodes.Add (serializedNode);
		foreach (var child in n.children) {
			AddNodeToSerializedNodes (child);
		}
	}
	public void OnAfterDeserialize() {
		//Unity has just written new data into the serializedNodes field.
		//let's populate our actual runtime data with those new values.
		if (serializedNodes.Count > 0) {
			ReadNodeFromSerializedNodes (0, out root);
		} else
		root = new Node ();
	}
	int ReadNodeFromSerializedNodes(int index, out Node node) {
		var serializedNode = serializedNodes [index];
		// Transfer the deserialized data into the internal Node class
		Node newNode = new Node() {
			interestingValue = serializedNode.interestingValue,
			children = new List<Node> ()
		}
		;
		// The tree needs to be read in depth-first, since that's how we wrote it out.
		for (int i = 0; i != serializedNode.childCount; i++) {
			Node childNode;
			index = ReadNodeFromSerializedNodes (++index, out childNode);
			newNode.children.Add (childNode);
		}
		node = newNode;
		return index;
	}
	// This OnGUI draws out the node tree in the Game View, with buttons to add new nodes as children.
	void OnGUI() {
		if (root != null) {
			Display (root);
		}
	}
	void Display(Node node) {
		GUILayout.Label ("Value: ");
		// Allow modification of the node's "interesting value".
		node.interestingValue = GUILayout.TextField(node.interestingValue, GUILayout.Width(200));
		GUILayout.BeginHorizontal ();
		GUILayout.Space (20);
		GUILayout.BeginVertical ();
		foreach (var child in node.children) {
			Display (child);
		}
		if (GUILayout.Button ("Add child")) {
			node.children.Add (new Node ());
		}
		GUILayout.EndVertical ();
		GUILayout.EndHorizontal ();
	}
}
```

## 추가 리소스##additional-resources

* [직렬화 규칙](/engine/6000.0/manual/scripting/compilation-and-code-reload/script-serialization/rules.md)
* [Unity가 직렬화를 사용하는 방법](/engine/6000.0/manual/scripting/compilation-and-code-reload/script-serialization/how-unity-uses.md)
* [JSONSerialization](/engine/6000.0/manual/scripting/compilation-and-code-reload/script-serialization/json-serialization.md)
* [직렬화 오류](/engine/6000.0/manual/scripting/compilation-and-code-reload/script-serialization/errors.md)
* [직렬화 베스트 프랙티스](/engine/6000.0/manual/scripting/compilation-and-code-reload/script-serialization/best-practices.md)
