diff --git a/Assets/Scripts/Base.meta b/Assets/Scripts/SampleProject.meta
similarity index 100%
rename from Assets/Scripts/Base.meta
rename to Assets/Scripts/SampleProject.meta
diff --git a/Assets/Scripts/Base/AppMain.cs b/Assets/Scripts/SampleProject/AppMain.cs
similarity index 98%
rename from Assets/Scripts/Base/AppMain.cs
rename to Assets/Scripts/SampleProject/AppMain.cs
index d69fd3ee..d0b32fa6 100644
--- a/Assets/Scripts/Base/AppMain.cs
+++ b/Assets/Scripts/SampleProject/AppMain.cs
@@ -6,7 +6,7 @@ using UVC.network;
using UVC.Network;
using UVC.Tests;
-namespace Base
+namespace SampleProject
{
public class AppMain : MonoBehaviour
{
diff --git a/Assets/Scripts/Base/AppMain.cs.meta b/Assets/Scripts/SampleProject/AppMain.cs.meta
similarity index 100%
rename from Assets/Scripts/Base/AppMain.cs.meta
rename to Assets/Scripts/SampleProject/AppMain.cs.meta
diff --git a/Assets/Scripts/UVC/Data/DataArray.cs b/Assets/Scripts/UVC/Data/DataArray.cs
new file mode 100644
index 00000000..a2983df4
--- /dev/null
+++ b/Assets/Scripts/UVC/Data/DataArray.cs
@@ -0,0 +1,318 @@
+using Newtonsoft.Json.Linq;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+
+namespace UVC.Data
+{
+ ///
+ /// DataObject 객체 컬렉션의 변경사항을 추적하는 데이터 배열 클래스
+ ///
+ public class DataArray : List, IDataObject
+ {
+ // 추가 된 항목 목록
+ protected List addedList = new List();
+ // 제거 된 항목 목록
+ protected List removedList = new List();
+ // 수정 된 항목 목록
+ protected List modifiedList = new List();
+
+
+ // 추가된 항목에 접근할 수 있는 읽기 전용 컬렉션
+ public ReadOnlyCollection AddedItems => addedList.AsReadOnly();
+
+ // 제거된 항목에 접근할 수 있는 읽기 전용 컬렉션
+ public ReadOnlyCollection RemovedItems => removedList.AsReadOnly();
+
+ // 제거된 항목에 접근할 수 있는 읽기 전용 컬렉션
+ public ReadOnlyCollection ModifiedList => modifiedList.AsReadOnly();
+
+
+ ///
+ /// 변경 추적 활성화 여부
+ ///
+ public bool IsChangeTracking { get; set; } = false;
+
+ ///
+ /// 기본 생성자
+ ///
+ public DataArray() : base()
+ {
+ }
+
+ ///
+ /// 초기 용량을 지정하는 생성자
+ ///
+ /// 초기 용량
+ public DataArray(int capacity) : base(capacity)
+ {
+ }
+
+ ///
+ /// 기존 컬렉션으로부터 생성하는 생성자
+ ///
+ /// 초기 항목을 포함하는 컬렉션
+ public DataArray(IEnumerable collection) : base(collection)
+ {
+ }
+
+ public DataArray(string jsonString) : base()
+ {
+ if (!string.IsNullOrEmpty(jsonString))
+ {
+ try
+ {
+ JArray jArray = JArray.Parse(jsonString);
+ foreach (var item in jArray)
+ {
+ Add(ConvertToDataObject(item));
+ }
+ }
+ catch (Exception ex)
+ {
+ throw new ArgumentException("Invalid JSON string format.", nameof(jsonString), ex);
+ }
+ }
+ }
+
+ ///
+ /// JArray로부터 DataArray를 생성하는 생성자
+ ///
+ /// JSON 배열
+ public DataArray(JArray jArray) : base()
+ {
+ if (jArray != null)
+ {
+ foreach (var item in jArray)
+ {
+ Add(ConvertToDataObject(item));
+ }
+ }
+ }
+
+ ///
+ /// JToken을 DataObject로 변환합니다.
+ ///
+ private DataObject ConvertToDataObject(JToken token)
+ {
+ if (token.Type == JTokenType.Object)
+ {
+ return new DataObject((JObject)token);
+ }
+ else
+ {
+ // JObject가 아닌 경우, 새 DataObject를 만들고 값을 넣어줍니다
+ var dataObject = new DataObject();
+ dataObject.Add("value", ConvertJTokenToObject(token));
+ return dataObject;
+ }
+ }
+
+ private object ConvertJTokenToObject(JToken token)
+ {
+ switch (token.Type)
+ {
+ case JTokenType.String:
+ return token.ToString();
+ case JTokenType.Integer:
+ return token.ToObject();
+ case JTokenType.Float:
+ return token.ToObject();
+ case JTokenType.Boolean:
+ return token.ToObject();
+ case JTokenType.Object:
+ return new DataObject((JObject)token);
+ case JTokenType.Array:
+ JArray array = (JArray)token;
+ return new DataArray(array);
+ default:
+ return token.ToString();
+ }
+ }
+
+ ///
+ /// 항목을 추가합니다.
+ ///
+ /// 추가할 항목
+ public new void Add(DataObject item)
+ {
+ base.Add(item);
+
+ // 변경 사항을 추적하고 이벤트 발생
+ if (IsChangeTracking)
+ {
+ int index = Count - 1;
+ addedList.Add(item);
+ }
+ }
+
+ ///
+ /// 항목을 제거합니다.
+ ///
+ /// 제거할 항목
+ /// 제거 성공 여부
+ public new bool Remove(DataObject item)
+ {
+ int index = IndexOf(item);
+ if (index >= 0)
+ {
+ RemoveAt(index);
+ return true;
+ }
+ return false;
+ }
+
+ ///
+ /// 지정된 인덱스의 항목을 제거합니다.
+ ///
+ /// 제거할 항목의 인덱스
+ public new void RemoveAt(int index)
+ {
+ if (index < 0 || index >= Count)
+ throw new ArgumentOutOfRangeException(nameof(index));
+
+ DataObject removedItem = this[index];
+ base.RemoveAt(index);
+
+ if (IsChangeTracking)
+ {
+ removedList.Add(removedItem);
+ }
+ }
+
+ ///
+ /// 컬렉션의 모든 항목을 제거합니다.
+ ///
+ public new void Clear()
+ {
+ if (Count > 0)
+ {
+ var oldItems = new List(this);
+ base.Clear();
+
+ if (IsChangeTracking)
+ {
+ // 모든 항목을 제거 목록에 추가
+ removedList.AddRange(oldItems);
+ }
+ }
+ }
+
+ ///
+ /// 지정된 인덱스에 항목을 삽입합니다.
+ ///
+ /// 삽입할 위치
+ /// 삽입할 항목
+ public new void Insert(int index, DataObject item)
+ {
+ base.Insert(index, item);
+
+ if (IsChangeTracking)
+ {
+ addedList.Add(item);
+ }
+ }
+
+ ///
+ /// 지정된 인덱스의 항목을 교체합니다.
+ ///
+ /// 교체할 위치
+ /// 새 항목
+ public new DataObject this[int index]
+ {
+ get { return base[index]; }
+ set
+ {
+ if (index < 0 || index >= Count)
+ throw new ArgumentOutOfRangeException(nameof(index));
+
+ DataObject oldItem = base[index];
+ base[index] = value;
+
+ if (IsChangeTracking)
+ {
+ addedList.Add(value);
+ removedList.Add(oldItem);
+ }
+ }
+ }
+
+ ///
+ /// 범위의 항목을 추가합니다.
+ ///
+ /// 추가할 항목의 컬렉션
+ public new void AddRange(IEnumerable collection)
+ {
+ if (collection == null)
+ throw new ArgumentNullException(nameof(collection));
+
+ int startIndex = Count;
+ base.AddRange(collection);
+
+ if (IsChangeTracking)
+ {
+ var items = collection.ToList();
+
+ for (int i = 0; i < items.Count; i++)
+ {
+ addedList.Add(items[i]);
+ }
+ }
+ }
+
+
+ ///
+ /// 변경된 인덱스 목록을 초기화합니다.
+ ///
+ public void ResetChangedIndices()
+ {
+ addedList.Clear();
+ removedList.Clear();
+ modifiedList.Clear();
+ }
+
+
+ ///
+ /// 변경 내역을 저장하지 않고 항목을 추가합니다.
+ ///
+ /// 추가할 항목
+ public void AddWithoutTracking(DataObject item)
+ {
+ bool oldTracking = IsChangeTracking;
+ IsChangeTracking = false;
+ Add(item);
+ IsChangeTracking = oldTracking;
+ }
+
+
+ ///
+ /// DataArray를 JArray로 변환합니다.
+ ///
+ public JArray ToJArray()
+ {
+ JArray array = new JArray();
+ foreach (var item in this)
+ {
+ array.Add(item);
+ }
+ return array;
+ }
+
+ ///
+ /// DataArray를 JArray로 암시적 변환합니다.
+ ///
+ public static implicit operator JArray(DataArray dataArray)
+ {
+ return dataArray.ToJArray();
+ }
+
+ ///
+ /// JArray를 DataArray로 암시적 변환합니다.
+ ///
+ public static implicit operator DataArray(JArray jArray)
+ {
+ return new DataArray(jArray);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/Scripts/UVC/Data/DataArray.cs.meta b/Assets/Scripts/UVC/Data/DataArray.cs.meta
new file mode 100644
index 00000000..ff735fe3
--- /dev/null
+++ b/Assets/Scripts/UVC/Data/DataArray.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 5ea1772f89851a04fa8a65b43169561b
\ No newline at end of file
diff --git a/Assets/Scripts/UVC/Data/DataMapper.cs b/Assets/Scripts/UVC/Data/DataMapper.cs
index 00c6ccd3..d0c219e8 100644
--- a/Assets/Scripts/UVC/Data/DataMapper.cs
+++ b/Assets/Scripts/UVC/Data/DataMapper.cs
@@ -1,5 +1,6 @@
using Newtonsoft.Json.Linq;
using System;
+using System.Linq;
namespace UVC.Data
{
@@ -7,7 +8,7 @@ namespace UVC.Data
/// 서로 다른 JSON 데이터 구조 간에 매핑 기능을 제공하는 클래스입니다.
///
///
- /// 이 클래스는 JSON 데이터를 원하는 형식으로 변환하거나, 중첩된 구조(nested structure)도 처리할 수 있습니다.
+ /// 이 클래스는 JSON 데이터를 DataObject와 DataArray 형식으로 변환하며, 중첩된 구조(nested structure)도 처리할 수 있습니다.
/// 소스 JSON 객체의 속성을 가이드 객체에 정의된 타입에 따라 적절히 변환합니다.
///
///
@@ -21,63 +22,93 @@ namespace UVC.Data
/// }");
///
/// // 가이드 객체 (타입 지정용)
- /// var guideJson = JObject.Parse(@"{
+ /// var maskJson = JObject.Parse(@"{
/// ""name"": """",
/// ""age"": 0,
/// ""isActive"": false
/// }");
///
- /// var mapper = new DataMapper(sourceJson, guideJson);
- /// JObject result = mapper.Map();
+ /// var mapper = new DataMapper(maskJson);
+ /// DataObject result = mapper.Map(sourceJson);
///
/// // result는 원본과 동일한 구조이며 각 속성이 가이드에 따라 타입 변환됨
///
///
public class DataMapper
{
- private JObject source;
- private JObject guide;
+ private JObject mask;
///
/// DataMapper 클래스의 새 인스턴스를 초기화합니다.
///
- /// 매핑할 원본 JSON 객체
- /// 타입 변환을 위한 가이드 JSON 객체
+ /// 타입 변환을 위한 가이드 JSON 객체
///
/// 가이드 객체는 원본 JSON 객체와 동일한 구조를 가질 필요는 없지만,
/// 변환하려는 속성들에 대한 타입 정보를 제공해야 합니다.
///
- public DataMapper(JObject source, JObject target)
+ public DataMapper(JObject mask)
{
- this.source = source;
- this.guide = target;
+ this.mask = mask;
}
///
- /// 소스 객체를 가이드 객체를 기반으로 매핑하여 새로운 JSON 객체를 생성합니다.
+ /// 소스 객체를 가이드 객체를 기반으로 매핑하여 새로운 DataObject를 생성합니다.
///
- /// 매핑된 JSON 객체
+ /// 매핑할 원본 JSON 객체
+ /// 매핑된 DataObject 객체
///
///
- /// var mapper = new DataMapper(sourceJson, guideJson);
- /// JObject result = mapper.Map();
+ /// var mapper = new DataMapper(maskJson);
+ /// DataObject result = mapper.Map(sourceJson);
/// Debug.Log(result["name"].ToString()); // "김철수"
/// Debug.Log(result["age"].ToObject<int>()); // 30
///
///
- public JObject Map()
+ public DataObject Map(JObject source)
{
- return MapObject(source, guide);
+ return MapObject(source, mask);
+ }
+
+ ///
+ /// 소스 배열을 가이드 객체를 기반으로 매핑하여 새로운 DataArray를 생성합니다.
+ ///
+ /// 매핑할 원본 JSON 배열
+ /// 매핑된 DataArray 객체
+ ///
+ /// 이 메서드는 가이드 객체로부터 새로운 배열을 생성하여 소스 배열의 각 항목을 매핑합니다.
+ ///
+ ///
+ ///
+ /// var sourceArray = JArray.Parse(@"[
+ /// { ""name"": ""김철수"", ""age"": 30 },
+ /// { ""name"": ""이영희"", ""age"": 25 }
+ /// ]");
+ ///
+ /// var maskJson = JObject.Parse(@"{
+ /// ""name"": """",
+ /// ""age"": 0
+ /// }");
+ ///
+ /// var mapper = new DataMapper(maskJson);
+ /// DataArray result = mapper.Map(sourceArray);
+ /// // result는 원본 배열과 동일한 구조의 DataArray
+ ///
+ ///
+ public DataArray Map(JArray source)
+ {
+ JArray arr = new JArray(mask);
+ return MapArray(source, arr);
}
///
/// 객체를 재귀적으로 매핑합니다.
///
/// 원본 JSON 객체
- /// 가이드 JSON 객체
- /// 매핑된 JSON 객체
+ /// 가이드 JSON 객체
+ /// 매핑된 DataObject 객체
///
/// 이 메서드는 중첩된 객체와 배열을 포함하여 JSON 구조를 재귀적으로 처리합니다.
+ /// 가이드 객체에 포함되지 않은 속성은 원본 값을 그대로 사용합니다.
///
///
/// 중첩 객체 매핑 예시:
@@ -92,7 +123,7 @@ namespace UVC.Data
/// }
/// }");
///
- /// var guideJson = JObject.Parse(@"{
+ /// var maskJson = JObject.Parse(@"{
/// ""user"": {
/// ""name"": """",
/// ""address"": {
@@ -102,53 +133,90 @@ namespace UVC.Data
/// }
/// }");
///
- /// var mapper = new DataMapper(sourceJson, guideJson);
- /// var result = mapper.Map();
+ /// var mapper = new DataMapper(maskJson);
+ /// var result = mapper.Map(sourceJson);
/// // result는 sourceJson과 동일한 중첩 구조를 유지
///
///
- private JObject MapObject(JObject sourceObject, JObject guideObject)
+ private DataObject MapObject(JObject sourceObject, JObject maskObject)
{
- JObject target = new JObject();
+ DataObject target = new DataObject();
foreach (var property in sourceObject.Properties())
{
- if (guideObject.ContainsKey(property.Name))
+ if (maskObject.ContainsKey(property.Name))
{
- JToken guideValue = guideObject[property.Name];
+ JToken maskValue = maskObject[property.Name];
JToken sourceValue = property.Value;
// 중첩된 객체 처리
- if (sourceValue.Type == JTokenType.Object && guideValue.Type == JTokenType.Object)
+ if (sourceValue.Type == JTokenType.Object && maskValue.Type == JTokenType.Object)
{
- target[property.Name] = MapObject((JObject)sourceValue, (JObject)guideValue);
+ target[property.Name] = MapObject((JObject)sourceValue, (JObject)maskValue);
}
// 중첩된 배열 처리
- else if (sourceValue.Type == JTokenType.Array && guideValue.Type == JTokenType.Array)
+ else if (sourceValue.Type == JTokenType.Array && maskValue.Type == JTokenType.Array)
{
- target[property.Name] = MapArray((JArray)sourceValue, (JArray)guideValue);
+ var arr = MapArray((JArray)sourceValue, (JArray)maskValue);
+ target[property.Name] = arr;
}
else
{
- MapProperty(property.Name, sourceValue, guideValue, target);
+ MapProperty(property.Name, sourceValue, maskValue, target);
}
}
else
{
- target[property.Name] = property.Value;
+ continue; // 가이드에 없는 속성은 무시
}
}
return target;
}
+ ///
+ /// JToken을 실제 객체로 변환하는 헬퍼 메서드
+ ///
+ private object ConvertJTokenToObject(JToken token)
+ {
+ switch (token.Type)
+ {
+ case JTokenType.Object:
+ return new DataObject((JObject)token);
+ case JTokenType.Array:
+ JArray array = (JArray)token;
+ if (array.All(item => item.Type == JTokenType.Object))
+ {
+ return new DataArray((JArray)token);
+ }
+ return array.ToObject