Unity Profiler
앱인토스 Unity 게임 개발에서 Unity Profiler를 효과적으로 활용하여 성능 이슈를 진단하고 해결하는 완전한 가이드입니다.
1. Profiler 설정 및 연결
앱인토스 환경 Profiler 설정
c#
public class AppsInTossProfilerSetup : MonoBehaviour
{
[Header("Profiler 연결 설정")]
public bool enableAutoConnect = true;
public string remoteIP = "192.168.1.100";
public int profilerPort = 54998;
[Header("프로파일링 영역")]
public bool enableCPUProfiling = true;
public bool enableMemoryProfiling = true;
public bool enableGPUProfiling = true;
public bool enableAudioProfiling = true;
public bool enableNetworkProfiling = true;
void Start()
{
#if DEVELOPMENT_BUILD || UNITY_EDITOR
SetupProfilerConnection();
ConfigureProfilerAreas();
#endif
}
void SetupProfilerConnection()
{
if (enableAutoConnect)
{
// 자동 연결 시도
ProfilerDriver.profileEditor = false;
ProfilerDriver.connectedProfiler = 0;
Debug.Log($"Unity Profiler 자동 연결 시작: {remoteIP}:{profilerPort}");
}
// 앱인토스 환경 정보 로깅
LogAppsInTossEnvironmentInfo();
}
void ConfigureProfilerAreas()
{
List<ProfilerArea> activeAreas = new List<ProfilerArea>();
if (enableCPUProfiling) activeAreas.Add(ProfilerArea.CPU);
if (enableMemoryProfiling) activeAreas.Add(ProfilerArea.Memory);
if (enableGPUProfiling) activeAreas.Add(ProfilerArea.GPU);
if (enableAudioProfiling) activeAreas.Add(ProfilerArea.Audio);
if (enableNetworkProfiling) activeAreas.Add(ProfilerArea.NetworkOperations);
Debug.Log($"활성화된 Profiler 영역: {string.Join(", ", activeAreas)}");
}
void LogAppsInTossEnvironmentInfo()
{
Debug.Log("=== 앱인토스 환경 정보 ===");
Debug.Log($"플랫폼: {Application.platform}");
Debug.Log($"기기 모델: {SystemInfo.deviceModel}");
Debug.Log($"메모리: {SystemInfo.systemMemorySize}MB");
Debug.Log($"GPU: {SystemInfo.graphicsDeviceName}");
Debug.Log($"GPU 메모리: {SystemInfo.graphicsMemorySize}MB");
Debug.Log($"프로세서: {SystemInfo.processorType}");
Debug.Log($"코어 수: {SystemInfo.processorCount}");
Debug.Log($"배터리 레벨: {SystemInfo.batteryLevel * 100}%");
Debug.Log("========================");
}
}2. 커스텀 Profiler 마커
앱인토스 특화 성능 마커
c#
using Unity.Profiling;
public static class AppsInTossProfilerMarkers
{
// 앱인토스 특화 성능 마커들
public static readonly ProfilerMarker TossLoginMarker = new ProfilerMarker("AppsInToss.Login");
public static readonly ProfilerMarker TossPaymentMarker = new ProfilerMarker("AppsInToss.Payment");
public static readonly ProfilerMarker TossAnalyticsMarker = new ProfilerMarker("AppsInToss.Analytics");
public static readonly ProfilerMarker AssetLoadingMarker = new ProfilerMarker("AppsInToss.AssetLoading");
public static readonly ProfilerMarker SceneTransitionMarker = new ProfilerMarker("AppsInToss.SceneTransition");
public static readonly ProfilerMarker UIUpdateMarker = new ProfilerMarker("AppsInToss.UIUpdate");
public static readonly ProfilerMarker NetworkRequestMarker = new ProfilerMarker("AppsInToss.NetworkRequest");
public static readonly ProfilerMarker GameLogicMarker = new ProfilerMarker("AppsInToss.GameLogic");
// 메모리 관련 마커
public static readonly ProfilerMarker MemoryCleanupMarker = new ProfilerMarker("AppsInToss.MemoryCleanup");
public static readonly ProfilerMarker GarbageCollectionMarker = new ProfilerMarker("AppsInToss.GC");
// 렌더링 관련 마커
public static readonly ProfilerMarker BatchingMarker = new ProfilerMarker("AppsInToss.Batching");
public static readonly ProfilerMarker LODUpdateMarker = new ProfilerMarker("AppsInToss.LODUpdate");
public static readonly ProfilerMarker CullingMarker = new ProfilerMarker("AppsInToss.Culling");
}
// 사용 예제
public class AppsInTossGameManager : MonoBehaviour
{
void Update()
{
using (AppsInTossProfilerMarkers.GameLogicMarker.Auto())
{
UpdateGameLogic();
}
using (AppsInTossProfilerMarkers.UIUpdateMarker.Auto())
{
UpdateUI();
}
}
void UpdateGameLogic()
{
// 게임 로직 처리
}
void UpdateUI()
{
// UI 업데이트 로직
}
public void ProcessTossLogin()
{
using (AppsInTossProfilerMarkers.TossLoginMarker.Auto())
{
// 토스 로그인 처리
AppsInToss.Login((result) => {
Debug.Log($"토스 로그인 결과: {result}");
});
}
}
public void ProcessPayment(float amount)
{
using (AppsInTossProfilerMarkers.TossPaymentMarker.Auto())
{
// 토스페이 결제 처리
AppsInToss.ProcessPayment(amount, (success) => {
Debug.Log($"결제 결과: {success}");
});
}
}
}3. 자동화된 성능 분석
Profiler 데이터 자동 분석
c#
#if UNITY_EDITOR
using UnityEngine;
using UnityEditor;
using UnityEditorInternal;
using System.Collections.Generic;
using System.Linq;
public class AppsInTossProfilerAnalyzer : EditorWindow
{
[System.Serializable]
public class PerformanceIssue
{
public string category;
public string description;
public float severity; // 0-1
public string suggestion;
public int frameNumber;
}
private List<PerformanceIssue> detectedIssues = new List<PerformanceIssue>();
private Vector2 scrollPosition;
private bool isAnalyzing = false;
[MenuItem("AppsInToss/Profiler Analyzer")]
public static void ShowWindow()
{
GetWindow<AppsInTossProfilerAnalyzer>("Profiler Analyzer");
}
void OnGUI()
{
GUILayout.Label("앱인토스 Profiler 자동 분석", EditorStyles.boldLabel);
GUILayout.Space(10);
if (GUILayout.Button(isAnalyzing ? "분석 중..." : "성능 분석 시작", GUILayout.Height(30)))
{
if (!isAnalyzing)
{
StartPerformanceAnalysis();
}
}
GUILayout.Space(10);
// 이슈 목록 표시
DrawIssueList();
GUILayout.Space(10);
// 최적화 제안
DrawOptimizationSuggestions();
}
void StartPerformanceAnalysis()
{
isAnalyzing = true;
detectedIssues.Clear();
// Profiler 데이터 분석
AnalyzeProfilerData();
isAnalyzing = false;
Repaint();
}
void AnalyzeProfilerData()
{
int frameCount = ProfilerDriver.lastFrameIndex - ProfilerDriver.firstFrameIndex;
for (int i = ProfilerDriver.firstFrameIndex; i <= ProfilerDriver.lastFrameIndex; i++)
{
AnalyzeFrame(i);
}
Debug.Log($"성능 분석 완료: {frameCount}프레임, {detectedIssues.Count}개 이슈 발견");
}
void AnalyzeFrame(int frameIndex)
{
// CPU 사용량 분석
float cpuTime = ProfilerDriver.GetFormattedStatisticsValue(frameIndex, ProfilerArea.CPU, "Total");
if (cpuTime > 33.33f) // 30 FPS 기준
{
detectedIssues.Add(new PerformanceIssue
{
category = "CPU",
description = $"높은 CPU 사용량: {cpuTime:F2}ms",
severity = Mathf.Clamp01(cpuTime / 100f),
suggestion = "CPU 집약적인 작업을 최적화하거나 프레임에 분산하세요",
frameNumber = frameIndex
});
}
// 메모리 사용량 분석
long memoryUsage = ProfilerDriver.GetStatisticsValue(frameIndex, ProfilerArea.Memory, "Total Reserved Memory");
if (memoryUsage > 200 * 1024 * 1024) // 200MB 초과
{
detectedIssues.Add(new PerformanceIssue
{
category = "Memory",
description = $"높은 메모리 사용량: {memoryUsage / (1024*1024)}MB",
severity = Mathf.Clamp01((float)memoryUsage / (300f * 1024 * 1024)),
suggestion = "메모리 사용량을 줄이거나 가비지 컬렉션을 최적화하세요",
frameNumber = frameIndex
});
}
// 렌더링 분석
float renderingTime = ProfilerDriver.GetFormattedStatisticsValue(frameIndex, ProfilerArea.Rendering, "Camera.Render");
if (renderingTime > 16.67f) // 60 FPS 기준
{
detectedIssues.Add(new PerformanceIssue
{
category = "Rendering",
description = $"높은 렌더링 시간: {renderingTime:F2}ms",
severity = Mathf.Clamp01(renderingTime / 50f),
suggestion = "드로우콜을 줄이거나 LOD 시스템을 최적화하세요",
frameNumber = frameIndex
});
}
// 가비지 컬렉션 분석
float gcTime = ProfilerDriver.GetFormattedStatisticsValue(frameIndex, ProfilerArea.Memory, "GC.Collect");
if (gcTime > 1f)
{
detectedIssues.Add(new PerformanceIssue
{
category = "GC",
description = $"가비지 컬렉션 지연: {gcTime:F2}ms",
severity = Mathf.Clamp01(gcTime / 10f),
suggestion = "메모리 할당을 줄이고 오브젝트 풀링을 사용하세요",
frameNumber = frameIndex
});
}
}
void DrawIssueList()
{
GUILayout.Label($"발견된 성능 이슈 ({detectedIssues.Count}개)", EditorStyles.boldLabel);
if (detectedIssues.Count == 0)
{
GUILayout.Label("성능 이슈가 발견되지 않았습니다.");
return;
}
scrollPosition = GUILayout.BeginScrollView(scrollPosition, GUILayout.Height(200));
var groupedIssues = detectedIssues.GroupBy(i => i.category);
foreach (var group in groupedIssues)
{
GUILayout.Label(group.Key, EditorStyles.boldLabel);
foreach (var issue in group.Take(5)) // 카테고리당 최대 5개만 표시
{
Color originalColor = GUI.color;
GUI.color = GetSeverityColor(issue.severity);
GUILayout.BeginVertical("box");
GUILayout.Label($"프레임 {issue.frameNumber}: {issue.description}");
GUILayout.Label($"제안: {issue.suggestion}", EditorStyles.wordWrappedMiniLabel);
GUILayout.EndVertical();
GUI.color = originalColor;
}
GUILayout.Space(5);
}
GUILayout.EndScrollView();
}
Color GetSeverityColor(float severity)
{
if (severity > 0.8f) return Color.red;
else if (severity > 0.5f) return Color.yellow;
else return Color.green;
}
void DrawOptimizationSuggestions()
{
GUILayout.Label("최적화 제안", EditorStyles.boldLabel);
var categoryGroups = detectedIssues.GroupBy(i => i.category);
foreach (var group in categoryGroups)
{
string category = group.Key;
int issueCount = group.Count();
float avgSeverity = group.Average(i => i.severity);
GUILayout.BeginVertical("box");
GUILayout.Label($"{category} 최적화 ({issueCount}개 이슈, 심각도: {avgSeverity:F2})");
switch (category)
{
case "CPU":
GUILayout.Label("• Update 함수 최적화", EditorStyles.miniLabel);
GUILayout.Label("• 코루틴 사용 고려", EditorStyles.miniLabel);
GUILayout.Label("• 물리 연산 최적화", EditorStyles.miniLabel);
break;
case "Memory":
GUILayout.Label("• 오브젝트 풀링 적용", EditorStyles.miniLabel);
GUILayout.Label("• 메모리 누수 체크", EditorStyles.miniLabel);
GUILayout.Label("• 에셋 언로딩", EditorStyles.miniLabel);
break;
case "Rendering":
GUILayout.Label("• 배칭 최적화", EditorStyles.miniLabel);
GUILayout.Label("• LOD 시스템 적용", EditorStyles.miniLabel);
GUILayout.Label("• 컬링 최적화", EditorStyles.miniLabel);
break;
case "GC":
GUILayout.Label("• string 할당 최소화", EditorStyles.miniLabel);
GUILayout.Label("• 배열 재사용", EditorStyles.miniLabel);
GUILayout.Label("• StringBuilder 사용", EditorStyles.miniLabel);
break;
}
GUILayout.EndVertical();
}
}
}
#endifUnity Profiler를 활용한 체계적인 성능 분석을 통해 앱인토스 게임의 성능 병목점을 정확히 진단하고 최적화하세요.
