데이터 중심 설계는 만능 최적화가 아니라 병목이 분명한 곳에서 빛난다

데이터 중심 설계는 데이터를 어떻게 배치하고 어떤 순서로 읽고 쓰는지가 성능을 크게 좌우한다는 관점의 설계 방식이다. 영어로는 데이터 지향 설계(Data-Oriented Design, DOD)라고 부른다. 이 접근은 객체를 어떻게 표현할지보다, CPU가 실제로 어떤 메모리 패턴을 더 빠르게 처리하는지를 먼저 본다.
게임 개발자 노엘 로피스(Noel Llopis)는 2010년 Game Developer 기고에서, 엔티티 업데이트 함수를 재구성하면 캐시 활용과 병렬화 가능성을 크게 개선할 수 있다고 설명했다. Unity도 데이터 지향 기술 묶음(Data-Oriented Technology Stack, DOTS) 문서에서, 데이터 지향 설계가 게임 아키텍처의 처리를 더 크게 확장하고 높은 성능으로 스케일하도록 돕는다고 말한다. 즉 DOD는 새로운 유행어라기보다, 업데이트가 많은 데이터를 어떻게 다룰 것인가에 대한 오래된 성능 문제의 실무적 해법에 가깝다.
다만 여기서 중요한 전제가 있다. DOD는 “객체지향은 나쁘다”는 선언이 아니라, 핫 루프(hot loop)에 있는 데이터를 더 잘 다루기 위한 최적화 관점이다. 모든 시스템이 DOD를 요구하는 것은 아니다.
DOD가 주목하는 것은 클래스가 아니라 메모리 접근 패턴이다
Unity의 DOTS Best Practices는 캐시 미스를 줄이려면 작은 버퍼 여러 개보다 큰 선형 버퍼를 순차적으로 순회하는 편이 좋다고 설명한다. CPU가 캐시에 없는 데이터를 만나면 메모리에서 불러와야 하고, 이 과정이 느리기 때문이다. 반대로 메모리상에 연속적으로 놓인 데이터를 앞에서 뒤로 예측 가능하게 읽으면, CPU가 미리 가져와 둘 수 있어 캐시 효율이 좋아진다.
이 관점에서 보면 DOD의 핵심 질문은 이런 쪽으로 바뀐다.
- 이 데이터는 프레임마다 얼마나 자주 갱신되는가
- 같은 종류의 데이터를 한꺼번에 순회하는가
- 포인터를 여러 번 따라가며 트리처럼 탐색하는가
- 같은 연산을 수천 개 엔티티에 반복하는가
게임에서 총알, 파티클, 군중 시뮬레이션, 위치·속도 갱신 같은 작업이 대표적이다. 이런 시스템은 “하나의 객체가 얼마나 그럴듯하게 보이는가”보다, 같은 형태의 계산을 얼마나 많이, 얼마나 예측 가능하게 반복하는가가 더 중요하다.
객체지향이 문제라기보다, 깊은 객체 그래프가 병목이 되기 쉽다
여기서 흔히 나오는 과장은 “객체지향은 느리고 DOD는 빠르다”는 식의 이분법이다. 그렇게 말하면 실제 도움이 안 된다.
문제는 객체지향 자체보다, 성능이 민감한 코드가 깊은 객체 그래프와 흩어진 메모리 배치에 묶여 있을 때다. Noel Llopis가 설명한 것도 같은 흐름이다. 엔티티 업데이트 안에서 레이캐스트 호출과 여러 하위 데이터 접근이 뒤섞이면 호출 트리가 길어지고 캐시 활용이 나빠진다. 이럴 때는 “객체를 없애라”보다 같이 처리되는 데이터를 같이 놓고, 업데이트 패스를 분리하라가 더 정확한 처방이다.
예를 들어 다음 같은 상황은 DOD 관점이 잘 맞는다.
- 적 수만 마리의 위치와 속도를 매 프레임 갱신할 때
- 발사체 충돌 후보를 일괄 계산할 때
- 애니메이션 상태 값을 대량으로 평가할 때
- 시뮬레이션 결과를 여러 코어에 나눠 처리할 때
반대로 설정 메뉴, 퀘스트 대화, 저장 파일 메타데이터, 관리자 툴처럼 갱신 빈도가 낮고 사람이 읽기 쉬운 구조가 더 중요한 코드는 굳이 DOD 형태로 바꿀 필요가 없다.
데이터 주도 설계와도 다르다
이 지점에서 자주 헷갈리는 개념이 데이터 주도 설계(Data-Driven Design)다. 이름은 비슷하지만 초점이 다르다.
- 데이터 중심 설계는 메모리 배치와 접근 패턴을 최적화하는 쪽에 가깝다.
- 데이터 주도 설계는 게임 규칙이나 밸런스 값을 코드 밖 데이터로 빼서 수정과 운영을 쉽게 만드는 쪽에 가깝다.
예를 들어 아이템 능력치와 몬스터 수치를 JSON이나 테이블로 분리하는 것은 데이터 주도 설계에 가깝다. 반면 위치, 속도, 체력 배열을 순차 순회하게 다시 배치해 캐시 미스를 줄이는 것은 데이터 중심 설계에 가깝다. 둘은 함께 쓸 수 있지만 같은 말은 아니다.
Unity ECS가 보여주는 실전 포인트는 “연속성”과 “예외의 절제”다
Unity의 ECS 문서는 같은 메모리 배치를 가진 엔티티들이 청크(chunk) 안에 모이고, 한 청크 안에서의 컴포넌트 접근은 완전히 선형적이라고 설명한다. 또 Best Practices 문서는 청크 단편화와 버퍼 재할당이 캐시 미스를 늘릴 수 있다고 경고한다. 이 문서들이 반복해서 강조하는 메시지는 꽤 일관적이다.
- 같은 종류의 데이터를 한데 모아라.
- 큰 선형 집합을 순회하라.
- 조건 분기와 자료구조 파편화를 줄여라.
- 모든 것을 엔티티로 만들려고 하지 마라.
특히 Unity는 “모든 것이 엔티티일 필요는 없다”고 직접 말한다. 여기서 엔티티 컴포넌트 시스템(Entity Component System, ECS)은 데이터를 잘게 나눈 컴포넌트와 대량 순회에 맞춘 처리 방식을 뜻한다. 이미 연속적인 버퍼로 잘 표현되는 데이터라면 ECS 바깥의 네이티브 컨테이너와 작업 시스템만으로도 충분할 수 있다는 뜻이다. 이 점은 중요하다. DOD를 도입한다는 것이 곧 코드베이스 전체를 ECS로 갈아엎는다는 말은 아니기 때문이다.
그래서 도입 순서도 작게 가는 편이 맞다
지금 기준으로 가장 현실적인 순서는 이렇다.
- 먼저 프로파일러로 진짜 병목을 찾는다.
- 병목이 데이터 순회와 캐시 미스 성격인지 확인한다.
- 핫 루프 하나를 골라 연속 배열이나
배열의 구조(Structure of Arrays, SoA)형태로 분리해 본다. - 개선폭을 측정한 뒤, 그 다음 범위를 넓힌다.
즉 DOD는 “처음부터 세계관처럼 믿고 들어가는 철학”보다 반복 계산이 많은 곳에서 데이터 배치를 바꿔 보는 실험으로 시작하는 편이 안전하다. 실제로는 전체 게임이 아니라 몇 개의 대량 처리 시스템만 DOD 관점으로 재구성해도 성능 차이가 나는 경우가 많다.
핵심 정리
데이터 중심 설계는 객체지향을 부정하는 종교가 아니라, 메모리 접근 패턴과 캐시 효율을 중시하는 성능 설계 방식이다. Noel Llopis와 Unity DOTS 문서가 공통으로 보여주는 것도 같이 처리되는 데이터를 같이 두고, 선형적으로 순회하라는 원칙이다.
하지만 그렇다고 모든 코드를 ECS나 SoA로 바꿀 필요는 없다. DOD가 특히 강한 곳은 총알, 군중, 파티클, 대량 상태 갱신처럼 같은 계산을 많은 데이터에 반복하는 영역이다. 반대로 저빈도 로직이나 사람이 읽기 쉬운 구조가 더 중요한 곳에서는 과한 선택이 될 수 있다.
좋은 결론은 하나다. DOD는 만능 해법이 아니라, 병목이 분명한 곳에서 매우 강력한 도구다.