2024. 10. 14. 05:21ㆍDesign Pattern
DI (Dependency Injection) 및 IoC (Inversion of Control)
DI (Dependency Injection)와 IoC (Inversion of Control)는 객체 지향 프로그래밍에서 의존성 관리와 소프트웨어 설계를 개선하는 두 가지 중요한 개념입니다. 이 두 패턴을 사용하면 객체들 간의 결합도를 줄이고, 코드의 유지보수성, 확장성, 테스트 용이성을 높일 수 있습니다.
1. IoC (Inversion of Control)란?
IoC (Inversion of Control)은 객체의 생성 및 객체 간의 의존성 관리를 프레임워크나 외부에서 처리하게 하는 디자인 원칙입니다. 기존의 프로그래밍 방식에서는 객체가 스스로 의존성을 생성하고 관리하지만, IoC에서는 그 제어를 프레임워크나 외부 환경에 맡깁니다.
IoC의 동작 방식:
- 객체 간의 결합도를 낮춤: IoC는 객체가 자신이 필요로 하는 객체(의존성)를 직접 생성하지 않고 외부로부터 주입받기 때문에 결합도가 낮아집니다.
- 제어권의 전환: 기존의 방식에서는 객체가 스스로 자신의 로직을 수행했지만, IoC에서는 필요한 자원을 외부에서 받아 처리하는 방식으로 제어권이 전환됩니다.
IoC 구현 방식:
- Service Locator: 특정 객체를 필요할 때 조회하여 가져오는 방식.
- Dependency Injection (DI): 의존성 객체를 외부에서 주입받는 방식.
2. DI (Dependency Injection)란?
DI (Dependency Injection)는 IoC 원칙을 구현하는 한 가지 방법으로, 객체가 자신의 의존성을 외부로부터 주입받도록 설계된 패턴입니다. DI는 생성자 주입, 속성 주입, 메서드 주입의 세 가지 방식으로 의존성을 주입할 수 있습니다.
2.1. 생성자 주입 (Constructor Injection)
생성자를 통해 의존성을 주입하는 방식입니다. 가장 많이 사용되는 방식으로, 객체가 생성될 때 필요한 의존성이 함께 주입됩니다.
public class UserService
{
private readonly ILogger _logger;
// 생성자 주입
public UserService(ILogger logger)
{
_logger = logger;
}
public void Register(string username)
{
_logger.Log($"User {username} registered.");
}
}
2.2. 속성 주입 (Property Injection)
속성을 통해 의존성을 주입하는 방식으로, 주입받는 객체가 필수적이지 않고 선택적인 경우에 사용됩니다.
public class UserService
{
public ILogger Logger { get; set; }
public void Register(string username)
{
Logger?.Log($"User {username} registered.");
}
}
2.3. 메서드 주입 (Method Injection)
메서드 호출 시 의존성을 전달하는 방식입니다. 주로 특정 작업에 필요한 의존성을 그때그때 주입할 때 사용됩니다.
public class UserService
{
public void Register(string username, ILogger logger)
{
logger.Log($"User {username} registered.");
}
}
3. DI와 IoC의 장점
3.1. 코드의 결합도 감소
객체가 필요한 의존성을 외부로부터 주입받기 때문에, 객체 간의 결합도가 낮아집니다. 이는 객체를 더 쉽게 교체하거나 확장할 수 있게 해줍니다.
3.2. 유지보수성 및 확장성 향상
DI를 통해 객체 간의 의존성을 외부에서 관리하면, 코드 변경 시 영향 범위가 줄어듭니다. 따라서 새로운 기능 추가나 수정이 더 수월해집니다.
3.3. 테스트 용이성
DI를 활용하면 의존성을 외부에서 주입받으므로, 테스트 시 실제 의존성 대신 Mock 객체를 사용하여 유닛 테스트가 더 쉬워집니다.
4. DI 컨테이너
DI 컨테이너는 의존성 주입을 자동으로 관리해주는 프레임워크나 라이브러리입니다. 이 컨테이너는 객체의 생명 주기와 의존성을 관리하여, 필요할 때 객체를 자동으로 주입해줍니다. C#에서는 ASP.NET Core에서 DI 컨테이너를 기본 제공하며, 대표적인 DI 라이브러리로는 Unity, Autofac, Ninject 등이 있습니다.
DI 컨테이너 사용 예시 (ASP.NET Core)
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IUserService, UserService>();
services.AddSingleton<ILogger, ConsoleLogger>();
}
}
위 코드에서는 IUserService 인터페이스를 구현한 UserService 클래스와 ILogger 인터페이스를 구현한 ConsoleLogger 클래스가 DI 컨테이너에 등록됩니다.
이후 컨테이너는 의존성을 필요로 할 때 자동으로 객체를 주입합니다.
5. DI 및 IoC 패턴의 사용 사례
5.1. ASP.NET Core에서의 DI
ASP.NET Core는 DI 컨테이너를 기본으로 사용하여, 컨트롤러나 서비스에 의존성을 주입합니다. 예를 들어, 서비스 계층에 의존하는 컨트롤러는 생성자 주입을 통해 필요한 서비스를 전달받습니다.
public class UserController : Controller
{
private readonly IUserService _userService;
public UserController(IUserService userService)
{
_userService = userService;
}
public IActionResult Register(string username)
{
_userService.Register(username);
return Ok();
}
}
5.2. 게임 개발에서의 DI
게임 개발에서도 DI 패턴을 사용하여 게임 로직과 의존성을 관리할 수 있습니다.
Unity 같은 게임 엔진에서는 Zenject 같은 DI 라이브러리를 사용하여 게임 오브젝트 간의 의존성을 관리할 수 있습니다.
Dependency Injection (DI)과 Inversion of Control (IoC) 패턴은 소프트웨어의 의존성 관리와 구조적인 설계에 중요한 역할을 합니다.
DI를 통해 의존성을 주입받고 IoC를 통해 객체 간의 결합도를 줄이면, 유지보수와 확장성이 뛰어난 코드를 작성할 수 있습니다.
이 패턴들은 특히 테스트 주도 개발(TDD)에서 유용하게 사용되며, 코드의 유연성과 재사용성을 크게 높여줍니다.
'Design Pattern' 카테고리의 다른 글
컴포지트(Composite) 패턴 (1) | 2024.10.17 |
---|---|
프로토타입(Prototype) 패턴 (0) | 2024.10.14 |
MVVM (Model-View-ViewModel) 패턴 (2) | 2024.10.14 |
MVC (Model-View-Controller) 패턴 (1) | 2024.10.14 |
전략(Strategy) 패턴 (0) | 2024.10.14 |