빌드·프로파일링·최적화

유니티 빌드·프로파일링·최적화: 성능과 안정성의 과학

목표: 게임이 개발에서 실제 배포로 나아가기 위해서는, 코드보다 중요한 세 가지 역량 — 빌드(Build), 프로파일링(Profiling), 최적화(Optimization) — 를 이해해야 합니다. 본 장에서는 유니티 프로젝트를 효율적으로 빌드하고, 성능 병목을 분석하며, 실무 수준의 최적화를 수행하는 방법을 다룹니다.

1. 빌드 파이프라인 (Build Pipeline)

유니티의 빌드 과정은 단순히 “게임 실행 파일을 만드는 단계”가 아닙니다. 프로젝트 설정, 플랫폼별 리소스 변환, 압축, 코드 스트리핑 등 다양한 프로세스가 통합되어 있습니다.

1.1 빌드 설정 개요

File → Build Settings에서 다음을 설정합니다:

  • 플랫폼 선택: Windows, Android, iOS, WebGL 등.
  • 씬 목록(Scene In Build) 등록.
  • 압축 포맷 및 개발 빌드 옵션 설정.

1.2 플랫폼별 고려사항

플랫폼특징최적화 포인트
PC/콘솔고해상도, 고성능 GPU텍스처 스트리밍, LOD
모바일메모리 제약, 저전력드로우콜 최소화, 압축 포맷 사용
WebGL브라우저 기반빌드 크기 축소, HTTP 압축

1.3 스크립트 빌드 파이프라인 자동화

using UnityEditor; using UnityEditor.Build.Reporting;

public class BuildAutomation {
[MenuItem("Build/Windows")]
public static void BuildWindows() {
string path = "Builds/Windows/MyGame.exe";
BuildReport report = BuildPipeline.BuildPlayer(
EditorBuildSettings.scenes,
path,
BuildTarget.StandaloneWindows64,
BuildOptions.None
);
Debug.Log("빌드 완료: " + report.summary.result);
}
}

이 방식은 CI/CD 환경(GitHub Actions, Jenkins 등)에서도 자동화 빌드가 가능합니다.

💡 Tip: “Development Build + Autoconnect Profiler” 옵션을 활성화하면 빌드 후 자동으로 프로파일링 세션이 연결됩니다.

2. 프로파일링 (Profiling)

프로파일링은 최적화의 출발점입니다. “느리다”라는 감각이 아닌, 숫자 기반의 병목 분석이 진짜 성능 개선을 이끕니다.

2.1 주요 유니티 프로파일링 도구

  • Profiler — CPU, GPU, 메모리, 렌더링, 오디오 등 런타임 통계.
  • Frame Debugger — 프레임 단위 렌더링 과정 시각화.
  • Memory Profiler — 메모리 사용량 및 GC 힙 구조 분석.
  • Deep Profile — 함수 단위 실행 시간 추적 (성능 하락 유의).

2.2 CPU 프로파일링 기본

using UnityEngine; using Unity.Profiling;

public class PerformanceTest : MonoBehaviour {
static readonly ProfilerMarker marker = new ProfilerMarker("CustomLogic");
void Update() {
    using (marker.Auto()) {
        // 특정 로직 측정 구간
    }
}
}

ProfilerMarker를 사용하면 커스텀 코드 구간의 실행 시간을 추적할 수 있습니다.

2.3 Frame Debugger

렌더링 과정을 단계별로 시각화하여, 드로우콜(Draw Call)오버드로우(Overdraw) 문제를 찾는 데 사용됩니다.

⚙️ 드로우콜 최적화 기준:
  • 모바일: 100~300 이하.
  • PC/콘솔: 1000 이하 권장.
  • UI는 Canvas 분리로 Rebatching 방지.

3. 최적화 (Optimization)

최적화는 “빠르게 만드는 것”이 아니라, 필요한 만큼만 계산하게 하는 것입니다. 코드, 그래픽, 메모리, 로드 타임 등 다양한 영역별로 접근해야 합니다.

3.1 코드 최적화

  • Update 최소화: 필요 시 이벤트 기반 처리.
  • GC 할당 제거: string.Concat() 대신 StringBuilder 사용.
  • Pooling: 오브젝트 재사용(Instantiate/Destroy 최소화).
public class ObjectPool : MonoBehaviour { public GameObject prefab; private Queue pool = new(); public GameObject Get() { if (pool.Count > 0) return pool.Dequeue(); return Instantiate(prefab); } public void Release(GameObject obj) { obj.SetActive(false); pool.Enqueue(obj); } }

3.2 렌더링 최적화

  • Static Batching / Dynamic Batching 활성화.
  • LOD Group 설정으로 원거리 모델 폴리곤 감소.
  • Lightmap과 Reflection Probe로 조명 연산 감소.

3.3 메모리 최적화

  • Addressables로 비사용 에셋 언로드(Addressables.Release).
  • 텍스처 압축 포맷 선택(ETC2, ASTC).
  • 오디오 스트리밍(Streaming Load Type) 사용.
⚙️ 실무 팁: Scene 전환 시 Resources.UnloadUnusedAssets() 호출로 미사용 메모리를 해제하세요. 단, Addressables를 사용할 경우 자동 관리되므로 중복 호출은 피해야 합니다.

4. 최적화 루프: “측정 → 개선 → 검증”

최적화는 반복적인 사이클입니다. “느리다”는 감각적 피드백이 아닌, 측정 가능한 수치 기반 프로세스를 구축해야 합니다.

  1. 문제 구간 측정 (Profiler, Frame Debugger).
  2. 의심 코드 리팩터링 또는 자산 구조 변경.
  3. 개선 결과를 동일한 조건에서 재측정.

이 과정을 “한 번” 하는 것이 아니라, 빌드 주기마다 자동 검증하도록 CI에 포함하는 것이 이상적입니다.

5. 품질(QA) & 안정성 테스트

성능만큼 중요한 것이 안정성입니다. 게임은 끊기지 않아야 하며, 예측 가능한 결과를 보여야 합니다.

  • 플랫폼별 FPS 기준: 모바일(30~60fps), PC(60fps 이상).
  • 플레이 세션 중 메모리 누수 검사 (Memory Profiler).
  • 에셋 누락, Null Reference 예외 자동 검출 스크립트 추가.
void OnApplicationQuit() { Debug.Log("프로세스 종료 - 세션 시간: " + Time.realtimeSinceStartup); }
💡 자동화 아이디어: 테스트 플레이 후 Profiler Log를 JSON으로 내보내고, 이전 빌드 대비 성능 변화율을 그래프로 시각화하면 “성능 회귀(Regression)”를 자동으로 탐지할 수 있습니다.

정리: 최적화는 마지막이 아니라, 처음부터의 태도다

빌드는 결과물이 아니라 “출발점”이고, 프로파일링은 “측정의 습관”이며, 최적화는 “설계의 철학”입니다. 빠른 코드를 쓰는 것보다, 측정 가능한 성능 구조를 설계하는 것이 더 중요합니다.

결국 최적화는 기술이 아니라 ‘사고의 효율성’입니다.

다음 학습 추천:

Comments