처음 JSON으로 오브젝트가 만들어지는 구조를 봤을 때는 데이터 주도라는 말이 꽤 거창하게 들렸다. 실제로 따라가 보니 JSON은 화면에 무엇이 있어야 하는지 적은 명세였고, C# 팩토리가 그 값을 읽어 Prefab을 생성했다.
그 뒤 Unity의 ECS가 정식 버전으로 나왔다는 소식을 봤다. ECS도 데이터를 중심에 둔다고 하니 처음에는 JSON 생성 구조와 비슷한 것처럼 느껴졌다. 둘을 작은 예제로 비교해 보니 같은 방향을 바라보는 부분은 있지만 담당하는 층은 전혀 달랐다.
JSON은 생성 전의 설명
예를 들어 화면 요소를 다음처럼 적을 수 있다.
{
"id": "status-label",
"prefab": "TextLabel",
"position": { "x": 24, "y": -40 },
"text": "연결 중"
}
이 데이터는 Unity 객체가 아니다. 직렬화 가능한 ObjectSpec으로 읽고, 해석기가 실제 GameObject를 만든다.
[Serializable]
public sealed class ObjectSpec
{
public string id;
public string prefab;
public Vector2 position;
public string text;
}
GameObject Build(ObjectSpec spec, Transform parent)
{
var source = Resources.Load<GameObject>($"UI/{spec.prefab}");
var instance = Instantiate(source, parent);
instance.name = spec.id;
instance.GetComponent<RectTransform>().anchoredPosition = spec.position;
instance.GetComponentInChildren<TMP_Text>().text = spec.text;
return instance;
}
핵심은 JSON과 GameObject 사이에 해석 경계가 있다는 점이다. 같은 JSON을 OpenGL 쪽에서도 읽으려면 그 환경에 맞는 별도 해석기를 만들 수 있다. 공유하는 것은 생성 의도이고, 실제 렌더링 객체는 각 환경이 소유한다.
ECS는 실행 중의 데이터 배치
ECS에서 Entity는 식별자에 가깝고, 상태는 Component에, 동작은 System에 나뉜다. GameObject마다 컴포넌트 인스턴스가 붙어 동작하는 구조와 달리 같은 모양의 데이터를 모아 처리하기 좋게 만든다.
학습용으로 JSON 위치 값을 ECS 데이터로 옮기면 다음처럼 표현할 수 있다.
public struct UiPosition : IComponentData
{
public float2 Value;
}
public struct UiElementId : IComponentData
{
public FixedString64Bytes Value;
}
var entity = entityManager.CreateEntity(
typeof(UiPosition),
typeof(UiElementId)
);
entityManager.SetComponentData(entity, new UiPosition {
Value = new float2(spec.position.x, spec.position.y)
});
이 코드는 JSON을 ECS로 바꾸면 성능이 좋아진다는 뜻이 아니다. JSON은 파일 교환과 생성 설정을 위한 형식이고, ECS는 런타임에서 많은 데이터를 어떻게 배치하고 처리할지에 관한 모델이다. JSON을 읽은 결과가 GameObject가 될 수도 있고 Entity가 될 수도 있지만, 그것은 해석기가 선택하는 구현이다.
| 질문 | JSON 생성 명세 | ECS |
|---|---|---|
| 무엇을 설명하나 | 생성할 요소와 초기값 | 실행 중 상태와 처리 단위 |
| 주된 시점 | 로드·생성 시점 | 매 프레임 또는 시스템 갱신 |
| 환경 의존성 | 비교적 낮게 설계 가능 | Unity Entities 런타임에 의존 |
| 성능의 핵심 | 파싱·검증·생성 횟수 | 데이터 배치와 일괄 처리 |
| 잘 맞는 대상 | 화면 정의, 레벨 설정 | 대량의 유사한 객체와 시뮬레이션 |
MonoBehaviour를 바로 버릴 필요는 없었다
UI 버튼 몇 개를 ECS Entity로 바꾼다고 바로 이득이 생기지는 않는다. 오히려 EventSystem, TextMeshPro, 기존 Inspector 연결과 경계를 만드는 일이 더 복잡할 수 있다. 반대로 같은 규칙으로 움직이는 수천 개의 데이터가 있다면 ECS의 일괄 처리 방식이 의미를 가진다.
그래서 혼합 구조를 그려 봤다.
JSON 화면 명세
├─ 일반 UI → GameObject / MonoBehaviour
└─ 대량 시뮬레이션 데이터 → Entity / Component
표현 계층은 필요한 결과만 받아 화면에 표시
여기서 조심할 부분은 양쪽이 같은 상태를 동시에 수정하지 않게 하는 것이다. GameObject와 Entity에 같은 위치나 선택 상태를 복제한다면 누가 원본인지 정해야 한다. 동기화 코드는 짧아 보여도 프레임 순서와 수명 문제를 만들기 쉽다.
다시 본 데이터 주도 설계
처음에는 데이터를 밖으로 빼면 모두 데이터 주도 설계라고 생각했다. 지금은 세 가지 질문을 먼저 한다.
- 이 데이터는 생성 전 명세인가, 실행 중 상태인가?
- 이 값을 읽고 실제 객체를 만드는 주체는 누구인가?
- 같은 값이 여러 표현에 있다면 어느 쪽이 원본인가?
JSON 생성 구조는 여러 렌더러가 같은 설계도를 읽게 만드는 데 의미가 있었다. ECS는 실행 중 비슷한 데이터를 모아 시스템이 처리하게 만드는 데 의미가 있다. 둘을 구분하고 나니 “데이터 중심”이라는 표현보다 데이터가 어느 시점에 누구의 소유인지가 더 중요한 질문이라는 걸 알게 됐다.