파트 2: ASP.NET Core MVC - 컨트롤러 추가하기

등록일시: 2016-06-02 08:00,  수정일시: 2016-08-16 07:33
조회수: 8,301
이 문서는 ASP.NET Core MVC 기술을 널리 알리고자 하는 개인적인 취지로 제공되는 번역문서입니다. 이 문서에 대한 모든 저작권은 마이크로소프트에 있으며 요청이 있을 경우 언제라도 게시가 중단될 수 있습니다. 번역 내용에 오역이 존재할 수 있고 주석은 번역자 개인의 의견일 뿐이며 마이크로소프트는 이에 관한 어떠한 보장도 하지 않습니다. 번역이 완료된 이후에도 대상 제품 및 기술이 개선되거나 변경됨에 따라 원문의 내용도 변경되거나 보완되었을 수 있으므로 주의하시기 바랍니다.
이번 파트에서는 MVC 패턴의 기본적인 개념을 살펴보고 문자열을 반환하는 간단한 컨트롤러를 추가해봅니다.

모델-뷰-컨트롤러(MVC) 아키텍처 패턴은 응용 프로그램을 모델(Model), 뷰(View), 그리고 컨트롤러(Controller)의 세 가지 주요 구성 요소로 분리합니다. MVC 패턴을 적용하면 기존의 일체형 응용 프로그램보다 관리와 유지보수가 쉽고 테스트가 용이한 응용 프로그램을 구축할 수 있습니다. MVC 기반의 응용 프로그램은 다음과 같은 구성 요소들로 구성됩니다:

  • 모델(Models): 응용 프로그램의 데이터를 표현하는 클래스로, 데이터에 업무 규칙을 적용하기 위한 유효성 검사 로직이 수행되는 곳이기도 합니다. 일반적으로 모델 개체는 데이터베이스를 이용해서 모델의 상태를 조회하거나 저장합니다. 예를 들어, 본 자습서에서는 Movie 모델을 이용해서 데이터베이스로부터 영화 데이터를 조회하고, 이 모델을 뷰에 전달하거나 변경합니다. 그리고 변경된 데이터는 SQL Server 데이터베이스에 저장됩니다.
  • 뷰(Views): 뷰는 응용 프로그램의 사용자 인터페이스(UI)를 출력하는 구성 요소입니다. 일반적으로 UI는 모델 데이터를 출력합니다.
  • 컨트롤러(Controllers): 브라우저에서 전달된 요청을 처리하고, 모델 데이터를 조회한 다음, 브라우저로 응답을 반환할 뷰 템플릿을 지정하는 클래스입니다. MVC 응용 프로그램에서는 오직 뷰에서만 정보를 출력하며, 컨트롤러는 사용자의 입력을 처리하고 사용자와 상호작용을 합니다. 가령, 컨트롤러는 라우트 데이터와 쿼리 문자열 값들을 처리하고 그 값들을 모델에 전달합니다. 그러면 모델에서는 전달받은 값들을 이용해서 데이터베이스에 질의하게 됩니다.

MVC 패턴을 적용하면 응용 프로그램의 개별적인 측면들을 분리하면서도 (입력 로직, 업무 로직, 그리고 UI 로직), 해당 요소들이 느슨하게 결합된 응용 프로그램을 구축할 수 있습니다. MVC 패턴은 로직의 유형에 따라 응용 프로그램 내에서 로직이 존재해야 할 위치를 규정합니다. 가령, UI 로직은 뷰에 위치합니다. 입력 로직은 컨트롤러에 위치합니다. 그리고 업무 로직은 모델에 위치합니다. 이런 방식으로 로직을 분리하면 작업 시 다른 부분의 코드에 미치는 영향을 우려하지 않고도, 한 번에 한 측면의 구현에만 집중해서 작업을 할 수 있기 때문에, 응용 프로그램을 구축할 때 복잡성을 관리하는데 도움이 됩니다. 가령, 업무 로직의 코드에는 전혀 신경쓰지 않고 뷰 코드에 대한 작업을 수행할 수 있습니다.

본 자습서 시리즈에서는 이와 같은 개념들에 대해서 모두 알아보고, 이를 활용하여 간단한 영화 정보 응용 프로그램을 구축하는 방법을 살펴봅니다. 다음 그림은 MVC 프로젝트의 Models, Views 그리고 Controllers 폴더들을 보여주고 있습니다.

  • 먼저 솔루션 탐색기(Solution Explorer)에서 Controllers 폴더를 마우스 오른쪽 버튼으로 클릭한 다음, 추가(Add) > 새 항목(New Item)을 선택합니다. (역주: 원문의 이 부분에 작은 실수가 존재합니다. 새 항목 대신 스캐폴드 항목 새로 만들기를 선택해야만 본문의 이후 내용과 동일한 화면을 보실 수 있습니다.)
  • 그리고 스캐폴드 추가(Add Scaffold) 대화 상자가 나타나면
    • MVC Controller - Empty 항목을 선택합니다.
    • 추가(Add) 버튼을 누릅니다.
  • 컨트롤러의 이름을 HelloWorldController로 지정합니다.
  • 다시 추가(Add) 버튼을 누릅니다.

그리고 다음 코드로 Controllers/HelloWorldController.cs 파일의 내용을 대체합니다:

using Microsoft.AspNetCore.Mvc;
using System.Text.Encodings.Web;

namespace MvcMovie.Controllers
{
    public class HelloWorldController : Controller
    {
        // 
        // GET: /HelloWorld/

        public string Index()
        {
            return "This is my default action...";
        }

        // 
        // GET: /HelloWorld/Welcome/ 

        public string Welcome()
        {
            return "This is the Welcome action method...";
        }
    }
}

이 컨트롤러에 존재하는 public 메서드들은 모두 HTTP 끝점으로 호출이 가능합니다. 그리고 이 코드에서 메서드들은 모두 문자열을 반환하고 있습니다. 각 메서드 앞에 작성된 주석들을 유의해서 살펴보시기 바랍니다:

public class HelloWorldController : Controller
{
    // 
    // GET: /HelloWorld/

    public string Index()
    {
        return "This is my default action...";
    }

    // 
    // GET: /HelloWorld/Welcome/ 

    public string Welcome()
    {
        return "This is the Welcome action method...";
    }
}

첫 번째 주석은 이 메서드가 URL에 "/HelloWorld/"를 추가해서 호출할 수 있는 HTTP GET 메서드임을 말해줍니다. 그리고 두 번째 주석은 이 메서드가 URL에 "/HelloWorld/Welcome/"을 추가해서 호출할 수 있는 HTTP GET 메서드임을 말해줍니다. 나중에 본 자습서에서는 스캐폴딩 엔진을 이용해서 HTTP POST 메서드도 생성해 볼 것입니다.

이제 응용 프로그램을 비-디버그 모드에서 실행하고 (Ctrl+F5 키를 누릅니다) 주소 창의 경로에 "HelloWorld"를 추가합니다. (아래 그림에는 http://localhost:1234/HelloWorld 라는 주소가 사용되고 있지만, 실제로 여러분은 이 주소의 1234 부분을 로컬 응용 프로그램의 포트 번호로 변경해야 합니다.) 그러면 Index method 메서드가 문자열을 반환합니다. 시스템에게 HTML을 반환하도록 요청했으며 그에 따라 시스템이 응답을 한 것입니다!

MVC는 전달 받은 URL에 근거하여 컨트롤러 클래스를 (그리고 그 내부의 액션 메서드를) 호출합니다. MVC에 의해서 사용되는 기본 URL 라우팅 로직은 다음과 같은 세그먼트 형식을 사용해서 호출할 코드를 결정합니다:

/[Controller]/[ActionName]/[Parameters]

라우팅에 대한 이 형식은 Startup.cs 파일에서 설정됩니다.

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

만약 응용 프로그램을 실행하고 아무런 URL 세그먼트도 지정하지 않으면, 기본적으로 위의 코드에 강조된 템플릿 라인에 정의된 대로 "Home" 컨트롤러의 "Index" 메서드가 지정됩니다.

첫 번째 URL 세그먼트는 실행할 컨트롤러 클래스를 결정합니다. 따라서 localhost:xxxx/HelloWorldHelloWorldController 클래스에 매핑됩니다. 그리고 URL 세그먼트의 두 번째 부분은 클래스의 액션 메서드를 결정합니다. 그 결과 localhost:xxxx/HelloWorld/IndexHelloWorldController 클래스의 Index 메서드를 실행하게 됩니다. 그런데 방금 본문에서 단지 localhost:xxxx/HelloWorld에 접근했음에도 불구하고 Index 메서드가 기본적으로 호출되었다는 점에 주목하시기 바랍니다. 그 이유는 명시적으로 메서드 이름이 지정되지 않았을 경우 컨트롤러에서 호출되는 기본 메서드로 Index 메서드가 지정되어 있기 때문입니다. URL 세그먼트의 마지막 세 번째 부분(Parameters)은 라우트 데이터입니다. 라우트 데이터에 관해서는 잠시 뒤에 다시 살펴보겠습니다.

다시 http://localhost:xxxx/HelloWorld/Welcome으로 이동해봅니다. 그러면 Welcome 메서드가 실행되어 "This is the Welcome action method..."라는 문자열이 반환됩니다. 기본 MVC 라우팅은 /[Controller]/[ActionName]/[Parameters]입니다. 따라서 이번 URL의 컨트롤러는 HelloWorld이고 액션 메서드는 Welcome입니다. URL의 [Parameters] 부분은 이번에도 사용되지 않았습니다.

이번에는 URL을 통해서 임의의 매개변수 정보를 컨트롤러에 전달할 수 있도록 (/HelloWorld/Welcome?name=Scott&numtimes=4) 예제 코드를 조금 변경해보겠습니다. 다음과 같이 두 개의 매개변수를 전달받도록 Welcome 메서드를 변경합니다. 이 예제 코드에서 numTimes 매개변수에 아무런 값도 전달되지 않았을 경우 기본값으로 1이 사용되도록 이 매개변수에 C#의 선택적 매개변수(Optional-Parameter) 기능이 사용된 부분을 주의해서 살펴보시기 바랍니다.

public string Welcome(string name, int numTimes = 1)
{
    return HtmlEncoder.Default.Encode(
        "Hello " + name + ", NumTimes is: " + numTimes);
}

노트

이 코드에서는 악의적인 입력으로부터 응용 프로그램을 보호하기 위해서 HtmlEncoder.Default.Encode를 사용하고 있습니다.

노트

Visual Studio 2015에서 디버그하지 않고 응용 프로그램을 실행하면 (Ctl+F5) 코드를 변경한 뒤에 응용 프로그램을 다시 빌드할 필요가 없습니다. 그냥 파일을 저장한 다음, 브라우저를 새로 고치기만 하면 변경사항을 확인할 수 있습니다.

응용 프로그램을 실행하고 다음 URL로 이동해봅니다. (여기서 xxxx 부분은 로컬 응용 프로그램의 포트 번호로 변경합니다):

http://localhost:xxxx/HelloWorld/Welcome?name=Rick&numtimes=4

이 URL에서 namenumtimes에 다른 값을 입력해도 무방합니다. 그러면 MVC 모델 바인딩 시스템이 자동으로 주소 창의 쿼리 문자열에서 명명된 매개변수들을 가져와서 메서드의 매개변수에 매핑시켜 줍니다. 보다 자세한 정보는 모델 바인딩(Model Binding) 문서를 참고하시기 바랍니다.

이번 예제에서 URL 세그먼트는 (Parameters) 사용되지 않았으며, name 매개변수와 numTimes 매개변수는 쿼리 문자열로 전달되었습니다. 이 URL에서 ? (물음표)는 구분자로, 이후에 나타나는 문자열들은 모두 쿼리 문자열들입니다. 그리고 & 문자는 쿼리 문자열들을 구분하는 구분자입니다.

다시 Welcome 메서드를 다음의 코드로 대체합니다:

public string Welcome(string name, int ID = 1)
{
    return HtmlEncoder.Default.Encode(
        "Hello " + name + ", ID: " + ID);
}

응용 프로그램을 실행하고 다음 URL을 입력합니다: http://localhost:xxx/HelloWorld/Welcome/3?name=Rick

이번에는 세 번째 URL 세그먼트와 라우트 매개변수 id가 일치합니다. 그리고 Welcome 메서드에는 MapRoute 메서드에 지정된 URL 템플릿과 일치하는 id 매개변수가 존재합니다. 템플릿에서 id 매개변수 뒤에 지정된 ?는 (id?) 이 매개변수가 선택적 매개변수임을 의미합니다.

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

지금까지 본문에서 살펴본 예제들에서는 컨트롤러가 MVC의 "VC" 부분, 즉 뷰와 컨트롤러의 역할을 모두 담당하고 있습니다. 다시 말해서 컨트롤러가 HTML을 직접 반환하고 있는 것입니다. 일반적으로 컨트롤러가 직접 HTML을 반환하는 것은 바람직하지 않은데, 코드 작성과 유지보수가 대단히 번거로워지기 때문입니다. 대신 HTML 응답을 생성하기 위한 별도의 Razor 뷰 템플릿 파일을 사용하는 것이 더 보편적인 방법입니다. 본 자습서의 다음 파트에서는 바로 이 부분을 살펴보도록 하겠습니다.