뷰: 뷰에 서비스 주입하기
- 본 번역문서의 원문은 Injecting Services Into Views docs.asp.net 입니다.
- 본 번역문서는 MVC : View - View에 서비스 주입하기 www.taeyo.net 에서도 함께 제공됩니다.
ASP.NET Core MVC는 뷰에 대한 의존성 주입(Dependency Injection)을 지원합니다. 이 기능은 지역화(Localization)나 뷰의 요소를 구성하기 위해서 전용 데이터가 필요한 경우 같은 뷰 전용 서비스에 유용하게 활용할 수 있습니다. 그러나 여전히 컨트롤러와 뷰 간에 관심사의 분리(Separation of Concerns)를 유지하기 위해서 최대한 노력해야 하므로, 뷰에서 출력하는 주요 업무 데이터는 그대로 컨트롤러에서 전달하는 것이 바람직합니다.
간단한 예제
뷰에 서비스를 주입하려면 @inject
지시문을 사용하면 됩니다.
@inject
지시문으로 뷰에 속성을 추가한 다음, DI를 이용해서 그 속성을 채운다고 생각하면 이해가 빠를 것입니다.
@inject
지시문의 구문은 다음과 같습니다:@inject <type> <name>
다음은 실제 @inject
지시문의 사용 예입니다:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
@using System.Threading.Tasks
@using ViewInjectSample.Model
@using ViewInjectSample.Model.Services
@model IEnumerable<ToDoItem>
@inject StatisticsService StatsService
<!DOCTYPE html>
<html>
<head>
<title>To Do Items</title>
</head>
<body>
<div>
<h1>To Do Items</h1>
<ul>
<li>Total Items: @StatsService.GetCount()</li>
<li>Completed: @StatsService.GetCompletedCount()</li>
<li>Avg. Priority: @StatsService.GetAveragePriority()</li>
</ul>
<table>
<tr>
<th>Name</th>
<th>Priority</th>
<th>Is Done?</th>
</tr>
@foreach (var item in Model)
{
<tr>
<td>@item.Name</td>
<td>@item.Priority</td>
<td>@item.IsDone</td>
</tr>
}
</table>
</div>
</body>
</html>
|
이 뷰에는 전반적인 통계를 보여주는 요약 정보와 ToDoItem
인스턴스의 목록이 함께 출력됩니다.
이때, 필요한 요약 정보를 뷰에 주입한 StatisticsService
에서 가져오는데, 이 서비스는 Startup.cs 파일의 ConfigureServices
메서드에서 의존성 주입에 등록됩니다:
1 2 3 4 5 6 7 8 |
// For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddTransient<IToDoItemRepository, ToDoItemRepository>();
services.AddTransient<StatisticsService>();
services.AddTransient<ProfileOptionsService>();
|
그리고 StatisticsService
클래스는 리파지터리를 통해서 접근해서 가져온 ToDoItem
인스턴스 집합을 대상으로 몇 가지 계산을 수행합니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
using System.Linq;
using ViewInjectSample.Interfaces;
namespace ViewInjectSample.Model.Services
{
public class StatisticsService
{
private readonly IToDoItemRepository _toDoItemRepository;
public StatisticsService(IToDoItemRepository toDoItemRepository)
{
_toDoItemRepository = toDoItemRepository;
}
public int GetCount()
{
return _toDoItemRepository.List().Count();
}
public int GetCompletedCount()
{
return _toDoItemRepository.List().Count(x => x.IsDone);
}
public double GetAveragePriority()
{
if (_toDoItemRepository.List().Count() == 0)
{
return 0.0;
}
return _toDoItemRepository.List().Average(x => x.Priority);
}
}
}
|
본문의 예제 리파지터리는 메모리에 저장된 컬렉션을 사용하고 있습니다. 그러나 이 예제에 사용된 구현 방식은 (모든 데이터를 메모리에 올려 놓고 작업하는 방식은) 원격으로 접근하는 대량의 데이터 집합을 대상으로는 권장되지 않습니다.
이번 예제는 뷰에 바인딩 된 모델의 데이터와 뷰에 삽입된 서비스의 데이터를 함께 출력합니다:
조회 데이터 채우기
뷰 주입은 드롭다운 리스트 같은 UI 요소의 옵션을 채워야 할 때 유용합니다.
가령, 성별과 상태 및 기타 설정을 지정할 수 있는 옵션들을 제공하는 사용자 프로필 폼을 가정해보시기 바랍니다.
일반적인 MVC 접근방식으로 이런 폼을 렌더하기 위해서는 컨트롤러에서 이런 옵션 집합들 각각에 대한 데이터 접근 서비스를 요청한 다음, 바운드 될 각각의 옵션 집합을 모델이나 ViewBag
에 채워넣어야 합니다.
다른 접근방식은 뷰에 직접 서비스를 주입해서 옵션들을 가져오는 것입니다. 이 방법은 컨트롤러 구현에 필요한 코드의 양을 최소화시켜주고, 뷰 요소 구성을 위한 로직을 뷰 자체로 이동시켜줍니다. 결과적으로 사용자 프로필 편집 폼을 출력하는 컨트롤러 액션에서는 사용자 프로필의 인스턴스만 뷰에 전달하면 됩니다:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
using Microsoft.AspNetCore.Mvc;
using ViewInjectSample.Model;
namespace ViewInjectSample.Controllers
{
public class ProfileController : Controller
{
[Route("Profile")]
public IActionResult Index()
{
// TODO: look up profile based on logged-in user
var profile = new Profile()
{
Name = "Steve",
FavColor = "Blue",
Gender = "Male",
State = new State("Ohio","OH")
};
return View(profile);
}
}
}
|
HTML 폼에서는 이 세 가지 속성들에 대한 드롭다운 리스트 등을 이용해서 프로필 설정을 수정합니다:
그리고 드롭다운 리스트들의 목록은 뷰에 주입된 서비스를 이용해서 채워집니다:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
@using System.Threading.Tasks
@using ViewInjectSample.Model.Services
@model ViewInjectSample.Model.Profile
@inject ProfileOptionsService Options
<!DOCTYPE html>
<html>
<head>
<title>Update Profile</title>
</head>
<body>
<div>
<h1>Update Profile</h1>
Name: @Html.TextBoxFor(m => m.Name)
<br/>
Gender: @Html.DropDownList("Gender",
Options.ListGenders().Select(g =>
new SelectListItem() { Text = g, Value = g }))
<br/>
State: @Html.DropDownListFor(m => m.State.Code,
Options.ListStates().Select(s =>
new SelectListItem() { Text = s.Name, Value = s.Code}))
<br />
Fav. Color: @Html.DropDownList("FavColor",
Options.ListColors().Select(c =>
new SelectListItem() { Text = c, Value = c }))
</div>
</body>
</html>
|
이 ProfileOptionsService
클래스는 이 폼에 필요한 데이터만을 제공하기 위해서 전용으로 설계된 UI 수준의 서비스입니다:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
using System.Collections.Generic;
namespace ViewInjectSample.Model.Services
{
public class ProfileOptionsService
{
public List<string> ListGenders()
{
// keeping this simple
return new List<string>() {"Female", "Male"};
}
public List<State> ListStates()
{
// a few states from USA
return new List<State>()
{
new State("Alabama", "AL"),
new State("Alaska", "AK"),
new State("Ohio", "OH")
};
}
public List<string> ListColors()
{
return new List<string>() { "Blue","Green","Red","Yellow" };
}
}
}
|
팁
먼저 사용할 형식을 Startup.cs 파일의 ConfigureServices
메서드에서 의존성 주입을 통해서 등록해야 한다는 점을 잊지 마십시오.
서비스 재정의하기
이 기법을 활용하면 새로운 서비스를 주입하는 작업 외에 기존에 페이지에 주입된 서비스를 대체할 수도 있습니다. 다음 그림은 첫 번째 예제에 사용된 페이지에서 사용 가능한 모든 필드들을 보여줍니다:
직접 확인할 수 있는 것처럼 기본 필드로 Html
, Component
, 그리고 Url
이 포함되어 있습니다
(주입된 StatsService
뿐만 아니라).
만약 기본 HTML 헬퍼의 인스턴스를 직접 구현한 버전으로 대체하고 싶다면, 다음과 같이 @inject
지시문을 이용해서 손쉽게 처리할 수 있습니다:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
@using System.Threading.Tasks
@using ViewInjectSample.Helpers
@inject MyHtmlHelper Html
<!DOCTYPE html>
<html>
<head>
<title>My Helper</title>
</head>
<body>
<div>
Test: @Html.Value
</div>
</body>
</html>
|
기존 서비스를 확장하고 싶은 경우, 기존 구현을 상속받거나 래핑해야 할 때 간단히 이 기법을 활용하면 됩니다.
참고 정보
- Simon Timms 블로그: Getting Lookup Data Into Your View
- 뷰: 뷰 개요 2016-08-26 08:00
- 뷰: Razor 구문 참조 2016-08-31 08:00
- 뷰: 레이아웃 2016-09-05 08:00
- 뷰: 뷰에 서비스 주입하기 2016-09-09 08:00
- 뷰: 태그 헬퍼 소개 2016-09-20 08:00
- 뷰: 태그 헬퍼 구현하기 2016-09-26 08:00
- 뷰: 태그 헬퍼로 폼 관련 작업하기 2016-10-03 08:00
- 뷰: 부분 뷰 2016-10-10 08:00
- 뷰: 뷰 구성 요소 2016-10-17 08:00