많은 프로젝트에서 싱글톤을 자주 쓰고 있다. 나 역시도 많이 써왔다.
그 이유는 간단하다. 쓰기가 매우 편하다.
언제 어디서든 쉽게 호출할 수 있고, 데이터 캐싱 역할까지 맡기면서 쓰기 좋다.

하지만 이렇게 편리하게 쓰면서도, 그 안에는 여러 가지 문제들이 숨어 있다.
오늘 포스팅에서는 이 문제를 구조적으로 어떻게 해결할 수 있는지, 간단한 로그인 샘플 프로젝트를 만들어서 풀어볼 생각이다.

 

로그인 프로젝트를 예제로 선택한 이유

회사에서 프로젝트를 진행할 때, 가장 초반에 마주하는 게 바로 로그인이다.
특히 빌드 환경(Dev, QA, Live)에 따라 실행 과정, 로그인 플로우, 접속 주소 등이 달라진다.
그만큼 분기 처리가 많아지고, 구조적으로 깔끔하게 정리하지 않으면 관리가 힘들어진다.

게임을 시작하는 첫 번째 관문이 로그인이라 생각했고, 그래서 이번 샘플도 로그인 프로젝트를 기반으로 진행해보기로 했다.

 


그래서 싱글톤에 문제가 뭔데?

아래는 싱글톤으로 로그인 서비스를 구현 내용이다.

// LoginPanel.cs
public class LoginPanel : MonoBehaviour
{
    public void OnClickLogin()
    {
        AuthService.Instance.Login("testId", "1234");
    }
}

// AuthService.cs
public class AuthService
{
    private static AuthService _instance;
    public static AuthService Instance => _instance ??= new AuthService();

    public bool IsLoggedIn { get; private set; }

    public void Login(string id, string pw)
    {
        // Dev, QA, Live 분기를 여기서 직접 처리
#if DEV
        string url = "https://dev-login.server.com";
#elif QA
        string url = "https://qa-login.server.com";
#else
        string url = "https://live-login.server.com";
#endif
        // 실제 로그인 로직 (간단화)
        Debug.Log($"Login Request → {url} : {id}/{pw}");
        IsLoggedIn = true;
    }
}

 

문제점으로 크게 3가지다.

1. AuthService가 싱글톤인 만큼 어디서나 호출 할 수 있다. (뭐 예를 들어 LoginManager.cs이런 매니저류 클래스)

2. 환경 분기 지옥이다. (Dev, QA, Live)가 내부에 하드코딩 되어있다. (지금은 url만 있지만, 환경에 따라 QA는 자동로그인,  DEV는 테스트 로그인만 가능, LIVE는 보안로직 강화 등 분기 지옥이 시작된다)

3. 테스트 환경 변경 어려움 (이건 앞으로 추가적인 예시로 설명 보강하겠다)

 

실무에서 느낀 문제

실제 회사 프로젝트에서 로그인 과정을 싱글톤으로 관리하다 보면, 환경에 따라 다른 주소/설정/로직을 처리하는 코드가 한 군데에 몰린다. 결과적으로:

  • 조건문이 계속 늘어나고
  • 유지보수가 점점 힘들어지며
  • 테스트 환경에서 특정 동작만 분리하기도 어렵다

즉, 편하다고 쓰던 싱글톤이 장기적으로는 프로젝트 속도를 늦추는 원인이 될 수 있다.

 

그~래서 이부분을 해결 할 수 있는 방법이 DI(Dependency Injection)이다.

 


DI(Dependency Injection)란?

DI(의존성 주입)은 말 그대로 객체가 필요한 의존성을 직접 만들지 않고, 외부에서 넣어주는 방식

쉽게 말해서

  • 싱글톤: “내가 필요하면 직접 가져다 쓴다.”
  • DI: “필요한 건 외부에서 준비해주고, 나는 받기만 한다.”

왜 좋은가?

  1. 테스트성
  2. 명시적 의존성
  3. 확장성

DI(의존성 주입)로 해결할 수 있는 포인트

DI는 필요한 객체를 스스로 만들지 않고, 외부에서 주입받는 방식이다.
이를 통해 다음과 같은 개선이 가능하다:

  • 테스트성: 실제 서비스 대신 Mock/Stub 객체를 주입해서 테스트 가능
  • 명시적 의존성: 어떤 클래스가 무엇을 필요로 하는지 코드에 드러남
  • 확장성: Dev, QA, Live 환경별로 다른 객체를 주입하면 깔끔하게 분리 가능

마무리 & 다음 편 예고

이번 글에서는:

  • 싱글톤을 왜 많이 쓰는지
  • 그로 인해 어떤 문제가 발생하는지
  • 로그인 샘플 프로젝트를 예제로 선택한 이유

까지 다뤄보았다.

다음 글에서는 실제로 VContainer를 사용해 DI 환경을 구성하고,
간단한 로그인 플로우를 어떻게 개선할 수 있는지 코드와 함께 살펴볼 예정이다.

 

 

깃허브:

https://github.com/hahahohohun/LoginSample.git

+ Recent posts