최근에 ECS라는 키워드를 굉장히 자주 듣습니다.
Unity DOTS, Unreal Mass, Data Oriented Design 같은 키워드들을 살펴 보면 거의 반드시 등장합니다.
대표적인 상용 엔진들은 컴포넌트 기반(Component Based)으로 게임 객체를 설계했습니다.
유니티의 경우 GameObject가 MonoBehaviour를 상속하는 컴포넌트로 기능을 구현합니다.
언리얼 엔진의 경우 Actor가 컴포넌트를 가질 수 있도록 게임 객체의 구조를 설계했습니다.
ECS는 Entity Component System의 약자인데 이렇게만 보면 기존의 게임 객체의 구조를 발전시킨 새로운 설계 기법인가?하는 생각이 드실 수도 있을 겁니다.
(여기에서 Entity는 게임 객체를 지칭하는 용어입니다. 유니티의 GameObject, 언리얼 엔진의 Actor가 바로 Entity입니다.)
하지만, 실제로 ECS를 설명하는 자료들을 보면 굉장히 복잡하게 느껴지는 경우가 많습니다.
저도 처음에 ECS를 접했을 때는 “왜 이렇게까지 구조를 복잡하게 바꾸려고 하는 걸까?”싶은 생각도 들었습니다.
왜냐하면 기존 방식으로도 충분히 게임을 잘 만들고, 성과를 잘 내고 있었기 때문입니다.
실제로 지금도 대부분의 게임은 기존의 컴포넌트 기반의 객체지향 기반 구조 위에서 잘 동작합니다.
그런데 최근의 데이터 기반 프로그래밍 설계 기법들을 공부하다 보면,
게임 엔진들이 왜 ECS에 관심을 가지기 시작했는지 조금씩 이해하게 됩니다.
그리고 이 내용들은 결국 이전 글들에서 이야기했던 Cache Locality, AoS와 SoA, Data Oriented Design 같은 내용들과 자연스럽게 연결됩니다.
전통적인 게임 객체 구조는 보통 이런 형태였다
게임 개발을 처음 시작하면 보통 아래와 같은 구조를 먼저 떠올리게 됩니다.
class Monster
{
public:
Transform transform;
Renderer renderer;
Animator animator;
AIController ai;
Health health;
};
그리고 게임 안에는 이런 Monster 객체가 여러 개 존재합니다.
std::vector<Monster*> monsters;
이 구조는 굉장히 자연스럽습니다.
Monster라는 객체가 있고, 그 안에 필요한 데이터와 기능들이 모두 들어 있습니다.
객체 하나만 보면 “이 몬스터가 어떤 상태를 가지고 있는지”바로 이해할 수 있습니다.
그리고 객체지향 프로그래밍 관점에서도 굉장히 직관적입니다.
몬스터마다 Transform, Renderer, HP, AI가 있습니다.
실제로 많은 게임들이 지금도 이런 구조를 기반으로 잘 동작합니다.
문제는 게임 규모가 점점 커지기 시작하면서 등장했습니다.
게임 엔진은 같은 작업을 엄청나게 많이 반복한다
게임 엔진 내부에서는 생각보다 같은 종류의 작업을 굉장히 많이 반복합니다.
예를 들어 렌더링 시스템은 모든 오브젝트의 Transform 데이터를 읽습니다.
물리 시스템은 모든 오브젝트의 위치와 속도를 읽습니다.
애니메이션 시스템은 모든 캐릭터의 애니메이션 상태를 읽습니다.
즉, 엔진 내부에서는 “객체 하나를 자세히 들여다보는 작업”보다,
“특정 데이터를 대량으로 반복 처리하는 작업”이 훨씬 많이 발생합니다.
그리고 바로 이 시점부터 기존 객체지향 구조의 한계가 조금씩 드러나기 시작합니다.
객체 중심 구조는 데이터가 흩어질 가능성이 높다
이전 글에서 AoS와 SoA에 대해 이야기했습니다.
전통적인 객체 구조는 대부분 AoS에 가깝습니다.
즉, 객체 하나 안에 위치, 속도, 애니메이션, 체력, AI 상태 같은 데이터들이 모두 함께 들어 있습니다.
문제는 실제 시스템이 특정 데이터만 필요로 하는 경우가 굉장히 많다는 점입니다.
예를 들면, 렌더링 시스템은 Transform만 필요할 수도 있습니다.
그런데 Monster 객체 안에는 AI 데이터도 있고, 체력 데이터도 있고, 애니메이션 데이터도 있습니다.
즉, CPU는 실제로 필요하지 않은 데이터까지 함께 읽게 될 가능성이 높습니다.
그리고 이런 상황이 수천 개, 수만 개의 객체에서 반복되기 시작하면 Cache 효율이 점점 나빠질 수 있습니다.
최근 게임 엔진들이 데이터 배치 방식에 관심을 가지기 시작한 이유도 결국 여기에 있습니다.
ECS는 객체보다 데이터를 중심으로 본다

ECS는 Entity Component System의 약자입니다.
ECS가 최근 트렌드이기 때문에 이를 따라가기 위해서 관련 개념을 공부하다 보면 굉장히 어려운 구조처럼 느껴질 수 있습니다.
그런데 핵심 개념 자체는 생각보다 단순합니다.
기존 객체지향 구조는 “객체 하나가 자신의 데이터를 모두 가진다”는 방식에 가깝습니다.
반면, ECS는 “같은 종류의 데이터를 따로 모아서 관리한다”는 방향에 가깝습니다.
예를 들어, 위치 데이터는 위치 데이터끼리 모읍니다.
속도 데이터는 속도 데이터끼리 모읍니다.
체력 데이터는 체력 데이터끼리 모읍니다.
이전 글에서 설명했던 SoA 구조와 굉장히 가까워집니다.
예를 들면 아래와 같은 느낌입니다.
struct TransformComponent
{
std::vector<float> x;
std::vector<float> y;
std::vector<float> z;
};
struct VelocityComponent
{
std::vector<float> velocityX;
std::vector<float> velocityY;
std::vector<float> velocityZ;
};
이 구조에서는 렌더링 시스템이 Transform 데이터만 연속적으로 순회할 수 있습니다.
물리 시스템은 위치와 속도 데이터만 순회합니다.
즉, 시스템이 필요한 데이터만 집중적으로 처리할 수 있게 됩니다.
최근 엔진들이 ECS에 관심을 가지는 이유
최근 게임들은 처리해야 하는 데이터 양이 굉장히 많아졌습니다.
오픈월드 게임에서는 수많은 객체가 동시에 존재합니다.
대규모 전투 게임에서는 엄청난 수의 유닛이 동시에 움직입니다.
파티클, AI, 애니메이션, 렌더링, 물리 시스템은 매 프레임 엄청난 양의 데이터를 반복적으로 처리합니다.
그리고 이런 상황에서 성능 최적화를 고민 하다보니 “객체를 얼마나 예쁘게 설계할 것인가”보다,
“데이터를 얼마나 효율적으로 처리할 수 있는가”가 더 중요해지기 시작했습니다.
특히, 최근 CPU 구조에서는 Cache 효율이 굉장히 중요합니다.
그리고 ECS는 이런 상황에서 캐시 지역성(Cache Locality)을 높이는 데 도움을 줍니다.
같은 종류의 데이터를 연속적으로 배치하기 쉽기 때문입니다.
또한, 멀티스레드 구조와도 잘 맞는 경우가 많습니다.
왜냐하면 서로 다른 시스템들이 서로 다른 데이터 집합을 병렬로 처리하기 쉬워지기 때문입니다.
그래서 최근 엔진 구조를 보다 보면 ECS 이야기가 점점 더 자주 등장하게 됩니다.
ECS가 모든 문제의 정답은 아니다
여기서 굉장히 중요한 부분이 있습니다.
ECS가 무조건 기존 객체지향 구조보다 좋은 것은 아닙니다.
이 부분은 정말 중요합니다.
ECS는 데이터 중심 처리에서는 굉장히 강력할 수 있습니다.
특히, 대량 데이터 처리 | Cache 효율 | 병렬 처리 | 반복적인 시스템 처리와 같은 상황에서는 큰 장점을 가질 수 있습니다.
하지만 코드 복잡도가 높아질 수도 있습니다.
그리고 개별 객체를 중심으로 복잡한 로직을 작성해야 하는 경우에는 기존 객체 구조가 더 직관적일 수 있습니다.
실제로 많은 게임 엔진들은 전통적인 객체 구조와 ECS 구조를 혼합해서 사용합니다.
중요한 것은 “무조건 ECS를 사용해야 한다”가 아닙니다.
왜 최근 엔진들이 이런 방향에 관심을 가지기 시작했는지를 이해하는 것이 더 중요합니다.
결국 ECS도 메모리 구조 이야기
지금까지 시리즈를 계속 따라왔다면, 생각보다 많은 이야기들이 결국 한 가지 개념을 이야기한다는 점을 이해하실 수 있을 겁니다.
왜 Cache Miss가 중요한지,
왜 allocator를 구현하는지,
왜 Object Pool을 사용하는지,
왜 SoA 구조가 등장했는지,
왜 Data Oriented Design이 중요해졌는지.
결국 ECS도 위의 나열한 많은 고민과 다르지 않습니다.
결국 데이터를 메모리에 어떻게 배치하고 처리할 것인가?에 대한 고민입니다.
최근 게임 엔진들은 점점 “객체를 어떻게 설계할 것인가”보다,
“데이터를 어떻게 효율적으로 처리할 것인가”를 더 중요하게 고민하기 시작했습니다.
ECS는 결국 이런 과정에서 등장한 구조 중 하나라고 볼 수 있습니다.
마무리
처음 ECS를 접하면 굉장히 거대한 새로운 아키텍처처럼 느껴질 수도 있습니다.
저도 “기존 객체지향 구조를 완전히 대체하려는 건가?”싶었던 것 같습니다.
그런데 ECS 역시 결국 데이터 배치와 Cache 효율 문제에서 출발했다는 것을 이해하게 됩니다.
왜 최근 엔진들이 SoA 구조에 관심을 가지는지, 왜 Data Oriented Design 이야기가 계속 등장하는지,
왜 대량 데이터 처리 구조가 중요해졌는지도 결국 맥락이 같습니다.
그리고 이런 최근의 트렌드를 따라가다보면 요즘의 게임 엔진은 점점
“객체 중심 설계”보다, “데이터 중심 설계”를 더 중요하게 다루기 시작했다는 것도 이해하실 수 있을거라 생각합니다.