앱인토스 개발자센터 로고
Skip to content

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();
        }
    }
}
#endif

Unity Profiler를 활용한 체계적인 성능 분석을 통해 앱인토스 게임의 성능 병목점을 정확히 진단하고 최적화하세요.