내 프로젝트에서 VContainer를 사용하고 있지만, ResourceManager는 AddressableService를 생성자 주입받지 않고 내부에서 직접 생성하는 구조를 채택했다. 언뜻 보면 DI 컨테이너의 철학과 맞지 않는 선택처럼 보일 수 있다.
AddressableService는 low-level 에셋 접근을 담당하고, ResourceManager는 AddressableService를 통해 에셋 로딩을 관리한다. 이 구조에서 두 클래스의 생명주기는 반드시 일치해야 한다.
처음에는 두 가지 대안을 고려했다.
1. 첫 번째는 둘 다 씬에 종속되게 설정하는 것이었는데, ResourceManager가 씬 관리도 담당하기 때문에 이 방법은 부적합했다.
2. 두 번째는 둘 다 싱글톤으로 VContainer에 등록하는 방법이었다. 이 방법이 가장 편하긴 했지만, 에셋 관리라는 단일 도메인에 두 개의 진입점을 만드는 셈이 되어 마음에 들지 않았다. 게다가 다른 서비스에서 AddressableService로 직접 접근할 수 있게 되면서 캡슐화가 깨지는 문제도 있었다.
결국 ResourceManager의 생성자에서 AddressableService를 직접 생성하는 방식을 선택했다.
[Inject]
public ResourceManager()
{
_addressableService = new AddressableService();
BsLogger.LogInfo("[ResourceManager] Initialized and AddressableService injected.");
}
이 방식은 여러 장점이 있다.
물론 이 선택에는 트레이드오프가 있다. VContainer를 통한 완전한 의존성 관리를 포기했고, AddressableService를 mock으로 교체할 수 없어서 테스트 가능성도 일부 희생했다. 하지만 그 대가로 도메인의 응집도와 캡슐화를 얻었고, 생명주기가 명시적으로 결합되었으며, 불필요한 의존성 노출을 방지할 수 있었다.
DI 컨테이너를 사용한다고 해서 모든 객체를 컨테이너에 등록해야 하는 것은 아니다. 도메인의 경계와 캡슐화가 더 중요한 가치라면, 일부 객체는 직접 생성하는 것이 오히려 더 나은 설계가 될 수 있다.
프레임워크는 도구일 뿐, 설계 원칙이 우선이다.