ASP.NET Core MVC 살펴보기

등록일시: 2016-12-12 08:00,  수정일시: 2017-04-05 01:48
조회수: 8,310
이 문서는 기술을 널리 알리고자 하는 개인적인 취지로 제공되는 번역문서입니다. 이 문서에 대한 모든 저작권은 마이크로소프트에 있으며 요청이 있을 경우 언제라도 게시가 중단될 수 있습니다. 번역 내용에 오역이 존재할 수 있고 주석은 번역자 개인의 의견일 뿐이며 마이크로소프트는 이에 관한 어떠한 보장도 하지 않습니다. 번역이 완료된 이후에도 대상 제품 및 기술이 개선되거나 변경됨에 따라 원문의 내용도 변경되거나 보완되었을 수 있으므로 주의하시기 바랍니다.
본문에서는 ASP.NET Core MVC가 사용하는 MVC 패턴에 대한 이해를 돕기 위한 간단한 설명과, ASP.NET Core MVC가 제공하는 다양한 기능들 및 그에 대한 개요가 제공됩니다.

ASP.NET Core MVC는 모델-뷰-컨트롤러 디자인 패턴을 이용해서 웹 응용 프로그램 및 APIs를 구축하기 위한 풍부한 프레임워크입니다.

MVC 패턴이란?

모델-뷰-컨트롤러(MVC, Model-View-Controller) 아키텍처 패턴은 응용 프로그램을 모델, 뷰, 컨트롤러의 세 가지 주요 구성 요소들의 그룹으로 구분합니다. 이 패턴을 사용하면 관심사의 분리(Separation of Concerns)를 준수하는데 도움이 됩니다. MVC 패턴을 사용할 때, 사용자의 요청은 먼저 모델을 이용해서 사용자의 작업을 수행하거나 질의 결과를 조회하는 역할을 담당하는 컨트롤러로 라우트 됩니다. 그러면, 컨트롤러는 사용자에게 출력될 뷰를 선택한 다음, 필요한 모델 데이터와 함께 뷰를 제공합니다.

다음 다이어그램은 세 가지 주요 구성 요소들 및 구성 요소들 간의 참조 관계를 보여줍니다:

역할에 대한 이런 방식의 구분은 응용 프로그램을 확장할 때 복잡성 측면에서 큰 도움이 됩니다. 한 가지 작업에만 집중하는, 즉 단일 책임 원칙(Single Responsibility Principle)을 준수하는 구성 요소(모델, 뷰 또는 컨트롤러)를 코딩하고, 디버그하고, 테스트 하는 것이 상대적으로 더 쉽기 때문입니다. 이 세 가지 영역 중, 두 가지 영역 이상에 의존성이 존재하는 코드를 변경하거나 테스트, 또는 디버그하는 것은 훨씬 어렵습니다. 가령, 사용자 인터페이스 로직은 업무 로직보다 자주 변경이 일어나는 편입니다. 따라서, 프리젠테이션 코드와 업무 로직을 단일 개체에 결합시킨다면 사용자 인터페이스 변경이 발생할 때마다 업무 로직을 포함하고 있는 개체를 수정해야 합니다. 결과적으로 사소한 사용자 인터페이스 변경으로 인해서 오류가 발생할 가능성이 높아지고, 모든 업무 로직을 다시 테스트해야 합니다.

노트

뷰와 컨트롤러는 모두 모델에 의존적입니다. 반면, 모델은 뷰나 컨트롤러 중 어느 쪽에도 의존하지 않습니다. 이는 분리를 통해서 얻을 수 있는 주요 장점 중 하나입니다. 이렇게 분리함으로써 시각적 표현과는 독립적으로 모델을 구현하거나 테스트 할 수 있습니다.

모델의 역할

MVC 응용 프로그램에서 모델은 응용 프로그램의 상태와 해당 응용 프로그램에서 수행해야 하는 업무 로직 및 작업을 표현합니다. 업무 로직은 응용 프로그램의 상태를 영속화하기 위한 구현 로직과 함께 모델에 캡슐화되어야 합니다. 강력한 형식의 뷰는 일반적으로 뷰에 표시할 데이터를 담기 위해서 특별히 준비된 ViewModel 형식을 사용하며, 컨트롤러는 모델을 이용해서 이런 ViewModel의 인스턴스를 생성하고 채웁니다.

노트

MVC 아키텍처 패턴을 사용하는 응용 프로그램에서 모델을 구성하는 방법은 여러 가지가 존재합니다. 다양한 유형의 모델 형식에 대해 더 자세히 살펴보시기 바랍니다.

뷰의 역할

뷰는 사용자 인터페이스를 통해서 콘텐트를 표시하는 역할을 수행합니다. 뷰는 Razor 뷰 엔진을 이용해서 HTML 마크업에 .NET 코드를 포함시킵니다. 뷰에는 최소한의 로직만 존재해야 하며, 해당 로직은 프리젠테이션 내용과 관련된 로직이어야만 합니다. 뷰 파일 내부에서 복잡한 모델의 데이터를 표시하기 위해 다수의 로직을 수행해야만 한다면 뷰 구성 요소, ViewModel 또는 뷰 템플릿을 이용해서 뷰를 단순화하는 것이 좋습니다.

컨트롤러의 역할

컨트롤러는 사용자 상호 작용을 처리하고 모델 작업을 수행하며 궁극적으로는 렌더링 할 뷰를 선택하는 구성 요소입니다. MVC 응용 프로그램에서 뷰는 정보만 표시하며, 컨트롤러가 사용자 입력 및 상호 작용을 처리하고 응답합니다. MVC 패턴에서 컨트롤러는 최초 진입점이며, 작업할 모델 형식과 렌더링 할 뷰를 선택하는 역할을 담당합니다 (컨트롤러라는 이름이 갖고 있는 뜻처럼 전달된 요청에 응용 프로그램이 응답하는 방식을 제어합니다).

노트

너무 많은 역할을 수행함으로써 컨트롤러가 지나치게 복잡해져서는 안됩니다. 컨트롤러의 로직이 지나치게 복잡해지는 것을 막으려면, 단일 책임 원칙(Single Responsibility Principle)을 준수하여 업무 로직을 컨트롤러로부터 분리해서 도메인 모델로 이동시킵니다.

컨트롤러 액션이 동일한 유형의 작업을 반복적으로 수행한다면, 액션 내부에서 직접 처리를 수행하는 대신 필터로 구현해서 중복배제(DRY, Don't Repeat Yourself) 원칙을 준수하는 것이 좋습니다.

ASP.NET Core MVC

ASP.NET Core MVC 프레임워크는 ASP.NET Core 상에서 실행되도록 최적화된, 오픈 소스 기반의 테스트가 용이한 경량 프레젠테이션 프레임워크입니다.

ASP.NET Core MVC는 패턴 기반의 접근 방식을 통해서 동적 웹 사이트를 구축함으로써 깔끔하게 관심사의 분리(Separation of Concerns)를 준수할 수 있습니다. 완벽하게 마크업을 제어할 수 있으며 TDD 친화적인 개발을 지원하며 최신 웹 표준을 사용합니다.

기능

ASP.NET Core MVC는 다음과 같은 기능들을 제공합니다:

라우팅

ASP.NET Core MVC는 이해하기 쉽고 검색 가능한 URL을 사용하는 응용 프로그램을 구현할 수 있게 해주는 강력한 URL-매핑 구성 요소인 ASP.NET Core의 라우팅을 기반으로 구축됩니다. 따라서, 웹 서버의 파일 구성 방식과는 관계없이 검색 엔진 최적화(SEO) 및 링크 생성에 적합한 응용 프로그램의 URL 명명 패턴을 정의할 수 있습니다. 라우트 값 제약 조건, 기본 및 선택적 값을 지원하는 편리한 라우트 템플릿 구문을 사용하여 라우트를 정의할 수 있습니다.

규약 기반의 기본 라우팅을 사용하면 응용 프로그램에서 허용하는 URL 서식과 해당 서식이 각각의 컨트롤러에서 특정 액션 메서드와 매핑되는 방식을 전역적으로 정의할 수 있습니다. 들어오는 요청이 전달되면 라우팅 엔진은 URL을 파싱하고 정의된 URL 서식 중 하나와 일치하는지 여부를 확인한 다음, 관련된 컨트롤러의 액션 메서드를 호출합니다.

routes.MapRoute(name: "Default", template: "{controller=Home}/{action=Index}/{id?}");

반면, 어트리뷰트 라우팅을 사용하면 컨트롤러 및 액션에 응용 프로그램의 라우트를 정의하는 어트리뷰트를 설정하는 방식으로 라우팅 정보를 지정할 수 있습니다. 다시 말해서, 라우트 정의가 관련 컨트롤러 및 액션의 바로 근처에 배치되게 됩니다.

[Route("api/[controller]")]
public class ProductsController : Controller
{
    [HttpGet("{id}")]
    public IActionResult GetProduct(int id)
    {
        ...
    }
}

모델 바인딩

ASP.NET Core MVC의 모델 바인딩은 클라이언트의 요청 데이터(폼 값, 라우트 데이터, 쿼리 문자열 매개변수, HTTP 헤더)를 컨트롤러가 처리할 수 있는 개체로 변환시켜 줍니다. 결과적으로 컨트롤러 로직에서는 전달된 요청의 데이터를 파악하는 작업을 수행할 필요가 없으며, 단순히 데이터를 액션 메서드의 매개변수로 갖고 있게 됩니다.

public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null) { ... }

모델 유효성 검사

ASP.NET Core MVC에서는 모델 개체에 데이터 주석 유효성 검사 어트리뷰트를 지정하는 방식의 유효성 검사를 지원합니다. 이 유효성 검사 어트리뷰트는 값이 서버로 전송되기 전에 클라이언트 측에서 검사를 수행할 뿐만 아니라, 컨트롤러 액션이 호출되기 전에 서버에서도 검사를 수행합니다.

using System.ComponentModel.DataAnnotations;
public class LoginViewModel
{
    [Required]
    [EmailAddress]
    public string Email { get; set; }

    [Required]
    [DataType(DataType.Password)]
    public string Password { get; set; }

    [Display(Name = "Remember me?")]
    public bool RememberMe { get; set; }
}

컨트롤러 액션:

public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
{
    if (ModelState.IsValid)
    {
        // work with the model
    }
    // If we got this far, something failed, redisplay form
    return View(model);
}

프레임워크는 클라이언트와 서버 모두에서 요청 데이터의 유효성 검사를 수행합니다. 모델 형식에 지정된 유효성 검사 로직은 렌더된 뷰에 튀지 않는 주석으로 추가되며, 브라우저에서 jQuery 유효성 검사를 통해서 적용됩니다.

의존성 주입

ASP.NET Core는 내장 의존성 주입(DI, Dependency Injection) 기능을 포함하고 있습니다. ASP.NET Core MVC에서 컨트롤러는 자신의 생성자를 통해서 필요한 서비스를 요청함으로써 명시적 의존성 원칙(Explicit Dependencies Principle)을 준수합니다.

뿐만 아니라, 응용 프로그램에서 @inject 지시문을 이용해서 뷰 파일에서도 의존성을 주입할 수 있습니다:

@inject SomeService ServiceName
<!DOCTYPE html>
<html>
<head>
    <title>@ServiceName.GetTitle</title>
</head>
<body>
    <h1>@ServiceName.GetTitle</h1>
</body>
</html>

필터

필터는 개발자가 예외 처리나 권한부여 같은 횡단 관심사(Cross-Cutting Concerns)를 캡슐화하는 데 도움이 됩니다. 필터를 사용하면 특정 액션 메서드에 대한 사용자 지정 전처리 로직 및 후처리 로직을 실행할 수 있으며, 전달된 요청에 대해 실행 파이프라인 내의 특정 지점에서 실행되도록 구성할 수도 있습니다. 필터는 어트리뷰트를 이용해서 컨트롤러나 액션에 적용할 수 있습니다 (또는 전역으로 실행될 수도 있습니다). 몇 가지 필터는 프레임워크에 기본적으로 포함되어 있습니다 (Authorize 등).

[Authorize]
public class AccountController : Controller
{

영역

영역은 대규모 ASP.NET Core MVC 웹 응용 프로그램을 보다 작은 기능 그룹으로 분할할 수 있는 방법을 제공해줍니다. 영역은 응용 프로그램 내부의 MVC 구조에 효과적입니다. MVC 프로젝트에서 모델, 컨트롤러, 뷰 같은 논리적 구성 요소들은 각기 다른 폴더에 위치하며, MVC는 명명 규칙을 통해서 이런 구성 요소들 간의 관계를 수립합니다. 규모가 큰 응용 프로그램에서는 응용 프로그램을 서로 분리된 고수준의 기능적 영역으로 분할하는 편이 유리합니다. 한 가지 예로, 지불, 결제, 검색 등과 같은 여러가지 업무 단위로 구상된 전자상거래 응용 프로그램을 들 수 있습니다. 이런 각각의 업무 단위들은 자체적인 논리적 구성 요소들, 즉 뷰, 컨트롤러, 모델을 갖습니다.

Web APIs

ASP.NET Core MVC는 웹 사이트 구축을 위한 최적의 플랫폼일 뿐만 아니라, Web APIs 구축을 위한 여러 가지 훌륭한 기능들을 지원해줍니다. 브라우저 및 모바일 장치를 비롯한 다채로운 클라이언트들이 접근할 수 있는 서비스를 구축할 수 있습니다.

프레임워크에는 데이터를 JSON이나 XML로 서식화하는 내장 기능을 이용한 HTTP 콘텐트-협상 기능을 포함되어 있습니다. 그리고 자체적으로 필요한 서식에 대한 지원을 추가하려면 사용자 지정 포멧터를 구현하면 됩니다.

하이퍼미디어에 대한 지원을 활성화시키기 위해서는 링크 생성 기능을 사용하면 됩니다. Web APIs를 여러 웹 응용 프로그램에서 공유할 수 있도록 손쉽게 교차-원본 자원 공유(CORS, Cross-Origin Resource Sharing) 기능을 활성화시킬 수도 있습니다.

테스트 용이성

인터페이스와 의존성 주입을 활용하는 프레임워크의 특징은 단위 테스트와 잘 어울리며, 통합 테스트를 빠르고 쉽게 수행할 수 있는 기능들도 (TestHost 및 Entity Framework를 위한 InMemory 공급자 같은) 프레임워크에 포함되어 있습니다. 컨트롤러 로직의 테스트에 관해서 더 자세히 살펴보시기 바랍니다.

Razor 뷰 엔진

ASP.NET Core MVC의 뷰는 뷰를 렌더하기 위해서 Razor 뷰 엔진을 사용합니다. Razor는 뷰 내부에 작성된 C# 코드를 이용해서 뷰를 정의하기 위한 작고, 표현력이 뛰어나며 유연한 템플릿 마크업 언어입니다. Razor는 서버에서 웹 컨텐트를 동적으로 생성하기 위해서 사용됩니다. 서버 코드를 클라이언트 측 콘텐트 및 코드와 깔끔하게 섞어서 작성할 수 있습니다.

<ul>
    @for (int i = 0; i < 5; i++) {
        <li>List item @i</li>
    }
</ul>

Razor 뷰 엔진을 사용하면 레이아웃이나 부분 뷰, 그리고 교체 가능한 섹션을 정의할 수 있습니다.

강력한 형식의 뷰

Razor 뷰를 모델 형식에 기반한 강력한 형식을 갖도록 만들 수도 있습니다. 컨트롤러는 강력하게 형식화 된 모델을 뷰에 전달해서, 뷰에서 형식 검사와 인텔리센스 지원을 활성화시킬 수 있습니다.

가령, 다음 뷰는 IEnumerable<Product> 형식의 모델을 선언하고 있습니다:

@model IEnumerable<Product>
<ul>
    @foreach (Product p in Model)
    {
        <li>@p.Name</li>
    }
</ul>

태그 헬퍼

태그 헬퍼를 사용하면 서버 측 코드를 이용해서 Razor 파일에서 수행되는 HTML 요소의 생성과 렌더링에 관여할 수 있습니다. 태그 헬퍼를 이용해서 사용자 지정 태그를 정의하거나(예, <environment>, 기존 태그의 동작을 변경할 수 있습니다(예, <label>). 태그 헬퍼는 요소의 이름과 해당 요소의 어트리뷰트를 기준으로 특정 요소와 바인딩 됩니다. HTML 편집 경험은 계속해서 유지하면서도 서버 측 렌더링의 장점을 얻을 수 있습니다.

폼 생성, 링크, 자산 로딩 등 보편적인 작업들을 위한 다양한 내장 태그 헬퍼가 기본으로 제공되며, 공개 GitHub 리파지터리를 통해서, 그리고 NuGet 패키지로 더 많은 태그 헬퍼들을 사용할 수 있습니다. 태그 헬퍼는 C#으로 작성되었으며, 요소의 이름이나 어트리뷰트 이름, 또는 부모 태그를 기반으로 HTML 요소에 적용됩니다. 예를 들어서, 다음과 같이 내장 LinkTagHelper를 사용해서 AccountsControllerLogin 액션을 가리키는 링크를 생성할 수 있습니다:

<p>
    Thank you for confirming your email.
    Please <a asp-controller="Account" asp-action="Login">Click here to Log in</a>.
</p>

또 다른 예로, EnvironmentTagHelper를 사용해서 뷰에서 다음과 같이 Development, Staging, 또는 Production 같은 런타임 환경에 적합한 각기 다른 스크립트들을 (원본 스크립트 또는 최소화된 스크립트들을) 구성할 수도 있습니다:

<environment names="Development">
    <script src="~/lib/jquery/dist/jquery.js"></script>
</environment>
<environment names="Staging,Production">
    <script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.1.4.min.js"
            asp-fallback-src="~/lib/jquery/dist/jquery.min.js"
            asp-fallback-test="window.jQuery">
    </script>
</environment>

태그 헬퍼는 HTML 및 Razor 마크업 생성을 위한 HTML 친화적인 개발 경험과 풍부한 인텔리센스 환경을 제공해줍니다. 대부분의 내장 태그 헬퍼들은 기존 HTML 요소들을 대상으로 하며, 해당 요소에 대한 서버 측 어트리뷰트들을 제공해줍니다.

뷰 구성 요소

뷰 구성 요소를 사용하면 렌더링 로직을 패키지화해서 이를 응용 프로그램 내의 곳곳에서 재사용 할 수 있습니다. 부분 뷰와 비슷하지만 관련 로직이 존재한다는 차이점이 있습니다.