<type,object> 인 static 딕셔너리 만들고 의존성 필요하면 그 딕셔너리에 등록하고 가져다쓰고 하는 패턴이다.
싱글톤 패턴보다 유연하긴 하지만, 여전히 의존성은 숨겨진다.
따라서 UI 매니저, 오디오 매니저 같이 전역적으로 공유해야 하는 것에는 서비스 로케이터를 써도 되지만,
플레이어, 몬스터 등 객체들이 서로 상호작용해야 할 땐 의존성 주입을 사용하는게 낫다.
(근데 작은 프로젝트면 그런거 신경 안 쓰고 편한거 써도 된다고 함)
public class ServiceLocator : MonoBehaviour
{
private static Dictionary<Type, object> _services = new Dictionary<Type, object>();
public static void Register<T>(T service)
{
_services[typeof(T)] = service;
}
public static T Get<T>()
{
return (T)_services[typeof(T)];
}
}
사용 방법
public class GameManager : MonoBehaviour
{
private void Awake()
{
ServiceLocator.Register(this); // 등록
}
}
public class Player : MonoBehaviour
{
private GameManager _gameManager;
private void Start()
{
_gameManager = ServiceLocator.Get<GameManager>(); // 가져오기
}
}
using UnityEngine;
using System;
using System.IO;
public class LogManager : MonoBehaviour
{
private string logFilePath;
void Awake()
{
// 로그 파일 경로 설정
logFilePath = Path.Combine(Application.persistentDataPath, "GameLog_" + DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss") + ".txt");
// 기존 로그 파일이 있으면 삭제
if (File.Exists(logFilePath))
{
File.Delete(logFilePath);
}
// Unity 로그 이벤트 핸들러 등록
Application.logMessageReceived += HandleLog;
}
void OnDestroy()
{
// 핸들러 해제 (메모리 누수 방지)
Application.logMessageReceived -= HandleLog;
}
private void HandleLog(string logString, string stackTrace, LogType type)
{
// 로그 메시지 생성
string logEntry = $"{DateTime.Now:HH:mm:ss} [{type}] {logString}\n";
if (type == LogType.Exception || type == LogType.Error)
{
logEntry += $"StackTrace:\n{stackTrace}\n";
}
// 로그 파일에 기록
File.AppendAllText(logFilePath, logEntry);
}
}
로그 파일 저장 경로:
Windows: C:\Users\사용자\AppData\LocalLow\회사명\게임명\GameLog_날짜.txt
Android: /storage/emulated/0/Android/data/패키지명/files/GameLog_날짜.txt
Mac: ~/Library/Application Support/회사명/게임명/GameLog_날짜.txt
현재는 GameManager.cs에서 거의 모든 로직이 돌아가고 있어 기능 추가할 때 힘들다.
보드 관련은 Board.cs로 빼고
입력 관련은 InputHandler.cs로 빼고 (정신력 이슈로 패스)
이동 검증은 MoveValidator.cs로 빼기
void Awake()
{
GameObject serviceLocator = GameObject.FindGameObjectWithTag("ServiceLocator");
board = serviceLocator.GetComponentInChildren<Board>();
moveCalculator = serviceLocator.GetComponentInChildren<MoveCalculator>();
}
코드 짜긴 귀찮아서 Service Locator 패턴을 응용하여 참조시켰다.

코드를 3개로 분리하는데 성공했다.
게임 매니저의 코드가 521 줄에서 235 줄로 줄어들었다.
리팩토링 하면서도 과연 정상 작동 할 수 있을지 의문이었는데,
다행히 디버깅을 좀 거치고 나니 멀쩡하게 돌아가주었다.
다시는 이런 미친짓을 하지 않도록 진작에 SRP 지키자.
input.Flush() 호출 이유 : StreamWriter가 명령어를 바로 안 보내고 버퍼에 쌓아뒀다가 보낼 수 있기 때문.string response = output.ReadLine();을 아무 방어 코드 없이 넣어버리면 입력 올 때까지 기다려서 게임이 멈춰버린다.