최적화는 실기기 측정에서 시작한다
Unity 프로젝트에서 최적화를 바라볼 때 중요한 기준인 실기기 측정, 지속적 관찰, 시스템 전반의 비용을 정리합니다.
들어가기에 앞서
최적화라는 단어는 묘하게 사람을 조급하게 만든다. 프레임이 떨어지면 당장 무언가를 줄여야 할 것 같고, 메모리가 높아 보이면 오브젝트 풀링이나 압축 설정부터 떠올리기 쉽다. 하지만 최적화에서 가장 먼저 해야 할 일은 코드를 고치는 것이 아니라, 무엇이 실제 문제인지 확인하는 것이다.
Unity 프로젝트에서 특히 중요한 원칙이 있다.
모든 데이터는 실기기에서 측정되어야 한다.
에디터에서 보는 수치, 개발 PC에서 보는 수치, 특정 기능만 떼어낸 테스트 결과는 참고 자료가 될 수는 있다. 하지만 최종 판단의 기준이 되어서는 안 된다. 게임은 결국 사용자의 기기에서 실행된다. 성능 문제도, 발열도, 메모리 부족도, 입력 지연도 그곳에서 발생한다.
에디터의 숫자는 충분하지 않다
Unity Editor는 개발하기에 편리한 환경이지만, 실제 빌드와는 많은 부분이 다르다. 에디터 자체의 오버헤드가 있고, 디버그용 코드가 더 많이 실행되며, 에셋 로딩 방식이나 스크립트 실행 환경도 빌드와 완전히 같지 않다.
PC에서는 아무 문제 없이 60 FPS가 나오던 기능이 모바일 기기에서는 쉽게 무너질 수 있다. 반대로 에디터에서 무거워 보이던 작업이 실제 IL2CPP 빌드에서는 큰 문제가 아닐 수도 있다. 그래서 에디터에서 보이는 수치를 보고 바로 결론을 내리면 잘못된 방향으로 시간을 쓸 위험이 있다.
최적화 판단에 필요한 데이터는 최소한 다음 조건에서 확인하는 편이 좋다.
- 실제 타겟 기기
- 실제 빌드 설정
- 실제 해상도와 품질 옵션
- 실제 플레이 흐름
- 충분히 긴 플레이 시간
짧은 테스트에서는 보이지 않던 문제가 10분 뒤에 나타나는 경우도 있다. 메모리 누수, GC 스파이크, 발열로 인한 클럭 저하, 씬 전환 후 남는 리소스 같은 문제는 순간 캡처보다 긴 관찰에서 더 잘 드러난다.
최적화는 추측이 아니라 비교다
최적화를 할 때 가장 위험한 말은 "아마 이것 때문일 것이다"다. 경험이 쌓이면 어느 정도 감은 생기지만, 감은 출발점일 뿐 결론이 될 수 없다.
예를 들어 프레임 드랍이 발생했다고 해서 무조건 렌더링 문제라고 볼 수는 없다. 실제 원인은 스크립트일 수도 있고, 물리 연산일 수도 있고, 애니메이션 업데이트일 수도 있고, Addressables 로딩일 수도 있다. 모바일에서는 GPU 병목처럼 보이던 문제가 발열로 인한 CPU 클럭 저하와 함께 나타날 수도 있다.
그래서 최적화는 항상 비교 가능한 데이터가 필요하다.
수정 전 측정
→ 변경 적용
→ 같은 조건에서 다시 측정
→ 수치 비교
→ 유지할지 되돌릴지 판단
이 과정이 없으면 최적화는 쉽게 취향 싸움이 된다. 코드는 더 복잡해졌는데 실제 프레임은 나아지지 않았거나, 특정 기기에서는 좋아졌지만 다른 기기에서는 더 나빠지는 일도 생긴다.
시스템 전반의 문제는 비용이 크다
최적화에서 가장 무서운 문제는 한 지점에 갇혀 있는 문제가 아니라, 시스템 전반에 퍼져 있는 문제다. 특정 함수 하나가 느린 경우에는 고치기 쉽다. 하지만 잘못된 구조가 여러 콘텐츠, 여러 씬, 여러 팀의 작업 방식에 스며들어 있다면 이야기가 달라진다.
예를 들어 모든 UI가 매 프레임 불필요하게 레이아웃을 갱신하고 있다고 해보자. 처음에는 작은 비용처럼 보일 수 있다. 하지만 같은 패턴이 모든 팝업, HUD, 인벤토리, 상점, 결과 화면에 반복되면 전체 프로젝트의 비용이 된다.
또 다른 예로, 모든 캐릭터가 필요하지 않은 상황에서도 무거운 탐색 로직을 계속 실행하고 있다면 어떨까. 캐릭터가 5개일 때는 괜찮아 보이지만, 콘텐츠가 늘어나 50개, 100개가 되면 문제가 갑자기 드러난다. 이때는 단순히 한 클래스만 고치는 것이 아니라 생성 방식, 업데이트 방식, 데이터 구조, 에디터 사용 방식까지 함께 봐야 한다.
어떠한 문제가 시스템 전반에 걸쳐져 있다면 이는 어마어마한 비용이 발생한다. 따라서 지금 당장 수정하지 않더라도 이를 추적하기 위해 지속적 측정이 필요하다.
중요한 것은 모든 문제를 발견 즉시 고쳐야 한다는 뜻이 아니다. 프로젝트 일정상 지금은 넘어가야 할 수도 있다. 하지만 추적하지 않는 문제는 점점 형태를 잃는다. 어느 순간 "뭔가 느리다"라는 느낌만 남고, 언제부터 나빠졌는지, 어떤 변경이 영향을 줬는지 찾기 어려워진다.
지속적으로 측정해야 하는 이유
성능은 한 번 확인하고 끝나는 값이 아니다. 프로젝트는 계속 변한다. 새로운 이펙트가 추가되고, UI가 늘어나고, 스테이지가 커지고, 몬스터 수가 많아지고, 라이브러리가 바뀐다. 어제 괜찮았던 수치가 오늘도 괜찮다는 보장은 없다.
그래서 성능 측정은 이벤트가 아니라 습관에 가까워야 한다. 큰 기능이 들어간 뒤에만 확인하는 것이 아니라, 일정한 기준으로 계속 기록해야 한다.
내가 중요하게 보는 지표는 보통 다음과 같다.
- 평균 FPS보다 프레임 타임
- CPU와 GPU 중 어느 쪽이 병목인지
- GC Alloc 발생 위치와 빈도
- 메모리 사용량과 씬 전환 후 회수 여부
- 로딩 시간과 로딩 중 프레임 정지
- 장시간 플레이 후 발열과 프레임 하락
평균 FPS만 보면 순간적인 끊김을 놓치기 쉽다. 사용자에게 더 크게 느껴지는 것은 평균이 아니라 스파이크다. 60 FPS로 잘 돌다가 1초에 한 번씩 크게 끊기면 플레이 감각은 좋지 않다. 그래서 프레임 타임, 최대 지연, 스파이크 발생 위치를 함께 봐야 한다.
Unity에서 측정할 때 보는 도구들
Unity에는 성능을 보기 위한 도구가 이미 많이 있다. 중요한 것은 도구의 이름을 아는 것보다, 어떤 질문을 던지고 어떤 데이터를 볼지 정하는 것이다.
Profiler는 CPU, GPU, Rendering, Memory, Physics, UI 같은 큰 영역을 볼 때 가장 기본이 된다. 에디터에서 먼저 흐름을 보고, 가능하다면 Development Build를 실제 기기에 올려 Profiler를 연결해 확인한다.
Memory Profiler는 메모리 사용량과 오브젝트 참조 관계를 볼 때 유용하다. 씬을 여러 번 이동한 뒤에도 사라져야 할 리소스가 남아 있는지 확인할 수 있다.
Frame Debugger는 렌더링 과정에서 어떤 드로우콜이 발생하는지 확인할 때 도움이 된다. 머티리얼이나 셰이더, 배칭 상태를 볼 때 자주 사용한다.
모바일에서는 Unity 도구만으로 부족한 경우도 있다. Android라면 Android Studio Profiler나 adb 로그를 함께 보고, iOS라면 Xcode Instruments를 같이 사용한다. 결국 목적은 하나다. 실제 기기에서 실제 병목을 확인하는 것이다.
정리
Unity 최적화는 실기기 측정에서 시작한다. 에디터와 개발 PC에서 얻은 정보는 힌트가 될 수 있지만, 최종 판단은 사용자가 플레이할 기기에서 내려야 한다.
또한 최적화는 한 번의 작업이 아니라 지속적인 관찰에 가깝다. 특히 문제가 시스템 전반에 퍼져 있다면 수정 비용은 시간이 지날수록 커진다. 지금 당장 고치지 않더라도, 그 문제가 어디에 있고 얼마나 커지는지 계속 측정해야 한다.
좋은 최적화는 코드를 무작정 줄이는 일이 아니다. 문제를 재현하고, 실기기에서 측정하고, 같은 조건에서 비교하고, 비용이 커지는 지점을 놓치지 않는 것이다. 결국 최적화는 빠른 코드를 만드는 기술이기도 하지만, 프로젝트가 느려지는 순간을 놓치지 않는 관찰의 습관이기도 하다.
참고자료
- https://www.youtube.com/watch?v=1mJtoceqvro
- https://www.youtube.com/watch?v=RLcSRuZsZQU
- https://unity.com/kr/how-to/project-configuration-and-assets