의존성 및 문제점에 대해서 간단히 다루고 이를 해결하기 위한 '의존성 주입'에 대해 다룹니다.


· 의존성?

의존성에 대해서는 많이 알려져있기때문에 간단하게 설명하면, 'A'가 변경될경우 'A'와 관련된 다른것들이 바뀌어야 한다는 것을 의미합니다.

 

· 예시

요구사항은 다음과 같습니다.
"식품 서비스 FoodService를 FastFoodService로 바꿔서 서비스해주세요."
  • FoodService는 한식을 다루고, FastFoodService는 패스트푸드를 다룹니다. 

 

public class Food
{
    public string Name { get; set; }
    public int Price { get; set; }
}

public class FoodService
{
    public List<Food> GetFoods()
    {
        List<Food> foods = new List<Food>()
        {
            new Food() { Name = "Bibimbap", Price = 8000 },
            new Food() { Name = "Kimbap", Price = 3000 },
            new Food() { Name = "Bossam", Price = 30000 }
        };

        return foods;
    }
}

public class FastFoodService
{
    public List<Food> GetFoods()
    {
        List<Food> foods = new List<Food>()
        {
            new Food() { Name = "Burger", Price = 8000 },
            new Food() { Name = "Fries", Price = 3000 },
        };

        return foods;
    }
}
  • 다른곳에서 FoodService를 사용하고있다면, FoodService를 모두 FastFoodService로 바꿔야합니다.

 

@page "/"
@using BlazorApp.Data;

<div>
	@foreach (var food in _foodService.GetFoods())
	{
		<div>@food.Name</div>
		<div>@food.Price</div>
	}
</div>


@code {

	//FoodService _foodService = new FoodService();

	//이 코드로 대체
	FastFoodService _foodService = new FastFoodService();
}
  • 주석처리된 FoodService를 사용하는 클래스는 FastFoodService로 대체되어야합니다.
  • 이 예시에서는 단순히 클래스만 변경하기에 크게 와닿지 않지만, 그 크기를 예측할 순 없습니다.
  • 이런 사항을 해결하기 위해서 인터페이스를 사용할 수 있습니다.

 

· Interface

  • Blazor은 C#문법을 사용하기때문에 Interface라는 이름으로 사용합니다.

 

public class Food
{
    public string Name { get; set; }
    public int Price { get; set; }
}

public interface IFoodService
{
    IEnumerable<Food> GetFoods();
}

public class FoodService : IFoodService
{
    public IEnumerable<Food> GetFoods()
    {
        List<Food> foods = new List<Food>()
        {
            new Food() { Name = "Bibimbap", Price = 8000 },
            new Food() { Name = "Kimbap", Price = 3000 },
            new Food() { Name = "Bossam", Price = 30000 }
        };

        return foods;
    }
}

public class FastFoodService : IFoodService
{
    public IEnumerable<Food> GetFoods()
    {
        List<Food> foods = new List<Food>()
        {
            new Food() { Name = "Burger", Price = 8000 },
            new Food() { Name = "Fries", Price = 3000 },
        };

        return foods;
    }
}
  • IFoodService라는 인터페이스에 GetFoods()라는 함수가 있습니다.
  • IFoodService에서 GetFoods를 할 때, 현재 서비스중인 서비스가 어떤것인지 명시해주면 추후에 다른 서비스로 바뀌어야할지라도 명시해준것만 바꿔주면 됩니다.

 

· 의존성 주입

  • 인터페이스 타입으로 구현했다면, 웹에서 이 서비스를 사용할때 어떤 클래스(서비스타입)을 사용할지 알려줘야합니다. 이때 사용되는것이 의존성 주입입니다.

 

//Program.cs

...

// 서비스를 시작한다.
// IFoodService라는 인터페이스의 서비스는 FastFoodService로 할것이다.
builder.Services.AddSingleton<IFoodService, FastFoodService>();

...
  • AddSingleton<>에서 IFoodService라는 인터페이스에 다음 매개변수로 FastFoodService가 들어갑니다.
  • 이 말은 IFoodService에서 사용할것은 FastFoodService라는 뜻 입니다.

 

@page "/"

@using BlazorApp.Data;
@inject IFoodService foodService;

<div>
	@foreach (var food in foodService.GetFoods())
	{
		<div>@food.Name</div>
		<div>@food.Price</div>
	}
</div>

@code {

}
  • 서비스를 사용한다면, 해당 서비스를 다른 페이지에서 불러와야합니다.
  • @inject IFoodService foodService;로 IFoodService를 foodService라는 이름으로 불러옵니다.
  • foodService는 IFoodService클래스로서 사용방법은 다른 클래스들처럼 동일합니다.

 

· 서비스 변경

  • 요구사항대로 서비스를 변경합니다.
  • Program.cs에서 단순히 타입명만 바꿔주면 의존성 없이 유동적으로 변경할 수 있습니다.

 

· FoodService

// 서비스를 시작한다.
// IFoodService라는 인터페이스의 서비스는 FoodService 할것이다.
builder.Services.AddSingleton<IFoodService, FoodService>();

 

· FastFoodService

// 서비스를 시작한다.
// IFoodService라는 인터페이스의 서비스는 FastFoodService로 할것이다.
builder.Services.AddSingleton<IFoodService, FastFoodService>();

 

· 의존성 전이

  • 만약 다른 인터페이스를 변수로 가지는 클래스가 있다면, 어떠한 인터페이스를 사용중이라고 알려줘야합니다.
  • Blazor에서는 생성자에 의존성 인터페이스가 매개변수로 있다면, 이것을 자동으로 설정해줍니다.

 

//Program.cs

...

// 서비스를 시작한다.
// IFoodService라는 인터페이스의 서비스는 FoodService 할것이다.
builder.Services.AddSingleton<IFoodService, FastFoodService>();

// PaymentService를 서비스한다.
builder.Services.AddSingleton<PaymentService>();

...
  • FastFoodService를 사용할때 PaymentService도 함께 사용한다고 설정한 상태입니다.
  • 생성자에 인터페이스 변수를 초기화하도록 하였지만, 이 코드에서는 특별히 생성자에 대한 명시적 표현이 없습니다.

 

public class PaymentService
{
    IFoodService _service;

    // Dependency Injection에 의해 생성자에 필요한 매개변수가 자동으로 들어감!
    public PaymentService(IFoodService service)
    {
        _service = service;
    }

    // TODO HERE

}
  • PaymentService에 FoodService 인터페이스가 있고 생성자가 있습니다.
  • 이 생성자를 어디에서도 호출하지 않았지만 IFoodService에는 FastFoodService타입의 인터페이스로 설정되게 됩니다.

'server > web server' 카테고리의 다른 글

[Web] 다이얼로그박스 및 유효성 검사  (0) 2023.02.24
[Web] 서비스 타입  (0) 2023.02.24
[Web] 템플릿 컴포넌트  (0) 2023.02.24
[Web] Cascading 파라미터  (0) 2023.02.24
[Web] 컴포넌트 재사용  (0) 2023.02.24
bonnate