뷰: 태그 헬퍼로 폼 관련 작업하기

등록일시: 2016-10-03 08:00,  수정일시: 2016-10-12 12:11
조회수: 10,643
이 문서는 ASP.NET Core MVC 기술을 널리 알리고자 하는 개인적인 취지로 제공되는 번역문서입니다. 이 문서에 대한 모든 저작권은 마이크로소프트에 있으며 요청이 있을 경우 언제라도 게시가 중단될 수 있습니다. 번역 내용에 오역이 존재할 수 있고 주석은 번역자 개인의 의견일 뿐이며 마이크로소프트는 이에 관한 어떠한 보장도 하지 않습니다. 번역이 완료된 이후에도 대상 제품 및 기술이 개선되거나 변경됨에 따라 원문의 내용도 변경되거나 보완되었을 수 있으므로 주의하시기 바랍니다.
본문에서는 태그 헬퍼를 이용해서 Form 요소와 Form 요소에서 보편적으로 사용되는 HTML 요소들을 다루는 기본적인 방법을 알아봅니다.

주의

이 페이지의 문서는 버전 1.0.0-rc1을 기준으로 작성되었으며, 아직 버전 1.0.0으로 갱신되지 않았습니다.

본문에서는 Form 요소와 Form 요소에서 보편적으로 사용되는 HTML 요소들을 다루는 방법을 살펴봅니다. HTML Form 요소는 웹 응용 프로그램에서 서버에 데이터를 게시하기 위해서 사용되는 핵심 메커니즘을 제공해줍니다. 본문의 내용 대부분은 태그 헬퍼(Tag Helpers) 자체와 태그 헬퍼를 이용해서 생산적으로 견고한 HTML Form을 작성하는 방법을 다루고 있습니다. 본문을 읽기 전에 먼저 태그 헬퍼 소개 문서부터 읽어보시기를 것을 권해드립니다.

많은 경우, 특정 태그 헬퍼에 대한 대안으로 HTML 헬퍼(HTML Helpers)를 사용하는 접근 방식을 선택할 수 있습니다. 중요한 점은 태그 헬퍼가 HTML 헬퍼를 대체하기 위한 것도 아니고, 모든 HTML 헬퍼에 그와 대응하는 태그 헬퍼가 지원되는 것도 아니라는 사실을 이해하는 것입니다. HTML 헬퍼를 이용한 대안이 존재하는 경우에는 그에 관해서도 같이 언급하도록 하겠습니다.

Form 태그 헬퍼

Form 태그 헬퍼는:

  • MVC 컨트롤러 액션이나 명명된 라우트에 대한 HTML <FORM> action 어트리뷰트 값을 생성합니다.
  • 크로스 사이트 요청 위조 공격을 방지하기 위한 숨겨진 요청 검증 토큰(Request Verification Token)을 생성합니다. (HTTP Post 액션 메서드에 지정되는 [ValidateAntiForgeryToken] 어트리뷰트와 함께 사용됩니다.)
  • asp-route-<Parameter Name> 형태의 어트리뷰트를 제공해주며, 이런 형태의 어트리뷰트에 지정된 값은 <Parameter Name>을 키로 라우트 값에 추가됩니다. 이 어트리뷰트는 Html.BeginForm HTML 헬퍼나 Html.BeginRouteForm HTML 헬퍼에 전달되는 routeValues 매개변수와 비슷한 기능을 제공해줍니다.
  • 대신 Html.BeginForm HTML 헬퍼 및 Html.BeginRouteForm HTML 헬퍼를 사용할 수 있습니다.

예제:

<form asp-controller="Demo" asp-action="Register" method="post">
    <!-- Input and Submit elements -->
</form>

이 Form 태그 헬퍼는 다음과 같은 HTML을 생성합니다:

<form method="post" action="/Demo/Register">
    <!-- Input and Submit elements -->
    <input name="__RequestVerificationToken" type="hidden" value="<removed for brevity>" />
</form>

MVC 런타임은 Form 태그 헬퍼의 asp-controller 어트리뷰트의 값과 asp-action 어트리뷰트의 값을 이용해서 HTML action 어트리뷰트 값을 생성합니다. 그리고 크로스 사이트 요청 위조 공격을 방지하기 위한 숨겨진 요청 검증 토큰(Request Verification Token)을 함께 생성합니다 (HTTP Post 액션 메서드에 지정되는 [ValidateAntiForgeryToken] 어트리뷰트와 함께 사용됩니다.) 크로스 사이트 요청 위조 공격을 순수한 HTML Form 만으로 방어하기는 매우 어렵지만, Form 태그 헬퍼를 사용하면 손쉽게 방어가 가능합니다.

명명된 라우트 사용하기

asp-route 태그 헬퍼 어트리뷰트를 이용해서 HTML action 어트리뷰트 마크업을 생성할 수도 있습니다. 가령, register라고 명명된 라우트가 존재하는 응용 프로그램에서는 등록 페이지에서 다음과 같은 마크업을 사용할 수 있습니다:

<form asp-route="register" method="post">
    <!-- Input and Submit elements -->
</form>

내장 템플릿을 이용해서 개별 사용자 계정(Individual User Accounts) 옵션으로 새로운 웹 응용 프로그램을 생성하는 경우에 만들어지는, Views/Account 폴더에 존재하는 뷰들 중 상당수에서 asp-route-returnurl 어트리뷰트가 사용됩니다:

<form asp-controller="Account" asp-action="Login"
    asp-route-returnurl="@ViewData["ReturnUrl"]"
    method="post" class="form-horizontal" role="form">

노트

내장 템플릿에서는 인증 받지 않거나 권한이 부여되지 않은 상태로 권한을 필요로 하는 리소스에 접근할 경우에만 자동으로 returnUrl이 설정됩니다. 인증되지 않은 접근을 시도하면 보안 미들웨어가 returnUrl을 설정한 상태로 로그인 페이지로 재지정해줍니다.

Input 태그 헬퍼

Input 태그 헬퍼는 Razor 뷰의 HTML <input> 요소를 모델 식(Model Expression)과 바인딩합니다.

구문:

<input asp-for="<Expression Name>" />

Input 태그 헬퍼는:

  • asp-for 어트리뷰트에 지정된 식 이름(Expression Name)에 대해 idname HTML 어트리뷰트를 생성합니다. asp-for="Property1.Property2"m => m.Property1.Property2와 같으며, 이 어트리뷰트의 값은 말 그대로 식의 일부입니다. 다시 말해서, 식의 이름이 asp-for 어트리뷰트 값으로 사용되는 것입니다.
  • 모델 속성의 형식과 모델 속성에 적용된 데이터 주석(Data Annotation) 어트리뷰트를 기반으로 HTML type 어트리뷰트 값을 설정합니다.
  • 이미 HTML type 어트리뷰트에 값이 지정되어 있으면 덮어쓰지 않습니다.
  • 모델 속성에 적용된 데이터 주석 어트리뷰트를 기반으로 HTML5 유효성 검사 어트리뷰트를 생성합니다.
  • Html.TextBoxFor HTML 헬퍼 및 Html.EditorFor HTML 헬퍼와 중복되는 기능을 갖고 있습니다. 보다 자세한 내용은 이후의 Input 태그 헬퍼 대신 사용할 수 있는 HTML 헬퍼 절을 참고하시기 바랍니다.
  • 강력한 형식의 사용 방법을 제공합니다. 만약 모델의 속성 이름을 변경하고 이를 태그 헬퍼에 반영하지 않으면 다음과 비슷한 오류가 발생합니다:
An error occurred during the compilation of a resource required to process this request. 
Please review the following specific error details and modify your source code appropriately.

Type expected
 'RegisterViewModel' does not contain a definition for 'Email' and no
 extension method 'Email' accepting a first argument of type 'RegisterViewModel'
 could be found (are you missing a using directive or an assembly reference?)

Input 태그 헬퍼는 .NET 형식을 기반으로 HTML type 어트리뷰트를 설정합니다. 다음 표는 몇 가지 일반적인 .NET 형식과, 그에 대응해서 생성되는 HTML type 어트리뷰트를 보여줍니다 (모든 .NET 형식을 제공하고 있지는 않습니다).

.NET 형식 Input 형식
Bool type="checkbox"
String type="text"
DateTime type="datetime"
Byte type="number"
Int type="number"
Single, Double type="number"

다음 표는 Input 태그 헬퍼가 특정 Input 형식으로 매핑해주는 몇 가지 일반적인 데이터 주석 어트리뷰트를 보여줍니다 (모든 유효성 검사 어트리뷰트를 제공하고 있지는 않습니다):

어트리뷰트 Input 형식
[EmailAddress] type="email"
[Url] type="url"
[HiddenInput] type="hidden"
[Phone] type="tel"
[DataType(DataType.Password)] type="password"
[DataType(DataType.Date)] type="date"
[DataType(DataType.Time)] type="time"

예제:

using System.ComponentModel.DataAnnotations;

namespace FormsTagHelper.ViewModels
{
    public class RegisterViewModel
    {
        [Required]
        [EmailAddress]
        [Display(Name = "Email Address")]
        public string Email { get; set; }

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

<form asp-controller="Demo" asp-action="RegisterInput" method="post">
    Email: <input asp-for="Email" /><br />
    Password: <input asp-for="Password" /><br />
    <button type="submit">Register</button>
</form>

이 코드는 다음과 같은 HTML을 생성합니다:

<form method="post" action="/Demo/RegisterInput">
    Email:
    <input type="email" data-val="true"
           data-val-email="The Email Address field is not a valid e-mail address."
           data-val-required="The Email Address field is required."
           id="Email" name="Email" value="" /> <br>
    Password:
    <input type="password" data-val="true"
           data-val-required="The Password field is required."
           id="Password" name="Password" /><br>
    <button type="submit">Register</button>
    <input name="__RequestVerificationToken" type="hidden" value="<removed for brevity>" />
</form>

모델의 Email 속성과 Password 속성에 적용된 데이터 주석은 모델에 대한 메타데이터를 만들어냅니다. Input 태그 헬퍼는 이 모델 메타데이터를 이용해서 HTML5 data-val-* 어트리뷰트들을 생성합니다 (자세한 내용은 모델 유효성 검사 문서를 참고하시기 바랍니다). 이 어트리뷰트들은 Input 필드에 연결되는 유효성 검사기를 기술하며, 이를 이용해서 튀지 않는 HTML5 및 jQuery 유효성 검사가 제공됩니다. 튀지 않는 유효성 검사 어트리뷰트는 data-val-rule="Error Message"의 형태를 갖고 있는데, 여기서 rule은 유효성 검사 규칙의 이름입니다 (예를 들어, data-val-required, data-val-email, data-val-maxlength 등). 또한 모델의 어트리뷰트에 오류 메시지가 지정되면 data-val-rule 어트리뷰트의 값으로 렌더되어 출력됩니다. data-val-ruleName-argumentName="argumentValue" 형태의 어트리뷰트도 존재하며, 가령 data-val-maxlength-max="1024" 같은 어트리뷰트들은 규칙에 대한 보다 상세한 정보를 제공해줍니다.

Input 태그 헬퍼 대신 사용할 수 있는 HTML 헬퍼

Html.TextBox, Html.TextBoxFor, Html.Editor 그리고 Html.EditorFor HTML 헬퍼는 Input 태그 헬퍼와 중복되는 기능들을 갖고 있습니다. Input 태그 헬퍼는 자동으로 type 어트리뷰트를 설정해주지만 Html.TextBox HTML 헬퍼 및 Html.TextBoxFor HTML 헬퍼는 그렇지 않습니다. Html.Editor HTML 헬퍼 및 Html.EditorFor HTML 헬퍼는 컬렉션, 복합 형식 개체 및 템플릿을 처리할 수 있지만 Input 태그 헬퍼는 불가능합니다. Input 태그 헬퍼, Html.EditorFor HTML 헬퍼, 그리고 Html.TextBoxFor HTML 헬퍼는 강력한 형식이지만 (람다 식을 사용합니다), Html.TextBox HTML 헬퍼 및 Html.Editor HTML 헬퍼는 아닙니다 (식 이름을 사용합니다).

식 이름

asp-for 어트리뷰트의 값은 ModelExpression 형식으로, 람다 식의 우변에 해당합니다. 따라서, 자동으로 생성되는 뷰 코드에서 asp-for="Property1"m => m.Property1으로 변환되며, 바로 이 점 때문에 식 앞 부분에 Model을 추가할 필요가 없는 것입니다. 또한 "@" 문자를 사용해서 인라인 식을 시작하고 m. 부분을 제외할 수 있습니다. 가령 다음의 코드는:

@{
    var joe = "Joe";
}
<input asp-for="@joe" />

다음과 같은 HTML을 생성합니다:

<input type="text" id="joe" name="joe" value="Joe" />

식 이름과 컬렉션

역주

이미 언급된 것처럼 Input 태그 헬퍼로는 컬렉션, 복합 형식 개체 및 템플릿을 처리할 수 없습니다. 그래서 이번 절의 예제들은 Html.Editor HTML 헬퍼와 템플릿을 이용해서, 대상 형식에 대한 템플릿의 내부에서 Input 태그 헬퍼를 사용하는 방식으로 우회하고 있습니다.

먼저 Colors라는 컬렉션을 속성으로 갖고 있는 모델을 가정해보겠습니다:

public class Person
{
    public List<string> Colors { get; set; }

    public int Age { get; set; }
}

이 모델을 뷰로 전달하는 액션 메서드의 코드는 다음과 같습니다:

public IActionResult Edit(int id, int colorIndex)
{
    ViewData["Index"] = colorIndex;
    return View(GetPerson(id));
}

다음 Razor 뷰는 특정 Color 요소에 접근하는 방법을 보여줍니다:

@model Person
@{
    var index = (int)ViewData["index"];
}

<form asp-controller="ToDo" asp-action="Edit" method="post">
    @Html.EditorFor(m => m.Colors[index])
    <label asp-for="Age"></label>
    <input asp-for="Age" /><br />
    <button type="submit">Post</button>
</form>

다음은 위의 뷰에서 사용되는 Views/Shared/EditorTemplates/String.cshtml 템플릿입니다:

@model string

<label asp-for="@Model"></label>
<input asp-for="@Model" /> <br />

이번에는 List<T> 형식을 사용한 예제를 살펴보겠습니다:

public class ToDoItem
{
    public string Name { get; set; }

    public bool IsDone { get; set; }
}

다음 Razor 뷰는 컬렉션을 루프로 순회하는 방법을 보여줍니다:

@model List<ToDoItem>

<form asp-controller="ToDo" asp-action="Edit" method="post">
    <table>
        <tr> <th>Name</th> <th>Is Done</th> </tr>

        @for (int i = 0; i < Model.Count; i++)
        {
            <tr>
                @Html.EditorFor(model => model[i])
            </tr>
        }

    </table>
    <button type="submit">Save</button>
</form>

다음은 위의 뷰에서 사용되는 Views/Shared/EditorTemplates/ToDoItem.cshtml 템플릿입니다:

@model ToDoItem

<td>
    <label asp-for="@Model.Name"></label>
    @Html.DisplayFor(model => model.Name)
</td>
<td>
    <input asp-for="@Model.IsDone" />
</td>

@*
    This template replaces the following Razor which evaluates the indexer three times.
    <td>
         <label asp-for="@Model[i].Name"></label>
         @Html.DisplayFor(model => model[i].Name)
     </td>
     <td>
         <input asp-for="@Model[i].IsDone" />
     </td>
*@

노트

목록을 순회할 때는 가급적 for를 사용하는 것이 좋습니다 (foreach 대신). LINQ 표현식에서 인덱서를 평가하는 작업은 비용이 많이 들 수 있으므로 최소화시켜야 합니다.

노트

위의 예제 코드는 람다 식 대신 @ 연산자를 사용해서 목록에 담긴 각각의 ToDoItem 요소에 접근하는 방법을 보여줍니다.

Textarea 태그 헬퍼

Textarea 태그 헬퍼도 Input 태그 헬퍼와 비슷한 작업을 수행합니다.

  • idname HTML 어트리뷰트를 생성하고, 모델로부터 <textarea> 요소에 대한 데이터 유효성 검사 어트리뷰트를 생성합니다.
  • 강력한 형식의 사용 방법을 제공합니다.
  • 대신 Html.TextAreaFor HTML 헬퍼를 사용할 수 있습니다.

예제:

using System.ComponentModel.DataAnnotations;

namespace FormsTagHelper.ViewModels
{
    public class DescriptionViewModel
    {
        [MinLength(5)]
        [MaxLength(1024)]
        public string Description { get; set; }
    }
}
@model DescriptionViewModel

<form asp-controller="Demo" asp-action="RegisterTextArea" method="post">
    <textarea asp-for="Description"></textarea>
    <button type="submit">Test</button>
</form>

이 코드는 다음과 같은 HTML을 생성합니다:

<form method="post" action="/Demo/RegisterTextArea">
    <textarea data-val="true"
        data-val-maxlength="The field Description must be a string or array type with a maximum length of &#x27;1024&#x27;."
        data-val-maxlength-max="1024"
        data-val-minlength="The field Description must be a string or array type with a minimum length of &#x27;5&#x27;."
        data-val-minlength-min="5"
        id="Description" name="Description">
    </textarea>
    <button type="submit">Test</button>
    <input name="__RequestVerificationToken" type="hidden" value="<removed for brevity>" />
</form>

Label 태그 헬퍼

  • 식 이름에 대한 Label 캡션을 생성하고 <label> 요소에 HTML for 어트리뷰트를 생성합니다.
  • 대신 Html.LabelFor HTML 헬퍼를 사용할 수 있습니다.

Label 태그 헬퍼는 순수한 HTML Label 요소에 비해 다음과 같은 이점을 제공해줍니다:

  • 모델 속성에 적용된 Display 어트리뷰트를 기반으로 서술적인 라벨 값이 자동으로 만들어집니다. 그리고 이렇게 만들어진 이름은 시간이 흐름에 따라 변경될 수도 있는데, 이때 Display 어트리뷰트를 변경하면 Display 어트리뷰트와 Label 태그 헬퍼가 함께 사용된 모든 곳에 반영됩니다.
  • 보다 적은 마크업으로 소스 코드를 작성할 수 있습니다.
  • 모델 속성을 이용한 강력한 형식의 사용 방법을 제공합니다.

예제:

using System.ComponentModel.DataAnnotations;

namespace FormsTagHelper.ViewModels
{
    public class SimpleViewModel
    {
        [Required]
        [EmailAddress]
        [Display(Name = "Email Address")]
        public string Email { get; set; }
    }
}
@model SimpleViewModel

<form asp-controller="Demo" asp-action="RegisterLabel" method="post">
    <label asp-for="Email"></label>
    <input asp-for="Email" /><br />
</form>

위의 <label> 요소에 대해 다음과 같은 HTML이 만들어집니다:

<label for="Email">Email Address</label>

Label 태그 헬퍼는 for 어트리뷰트의 값을 <input> 요소에 할당된 ID인 "Email"로 설정합니다. 태그 헬퍼는 id 어트리뷰트 값과 for 어트리뷰트 값을 동일한 방식으로 만들어내므로 올바른 연결이 가능합니다. 이번 예제에서 캡션은 Display 어트리뷰트를 기반으로 만들어집니다. 만약 모델에 Display 어트리뷰트가 지정되지 않으면, 식의 속성 이름을 기반으로 캡션이 만들어집니다.

유효성 검사 태그 헬퍼

유효성 검사 태그 헬퍼는 두 가지 종류가 존재합니다. 유효성 검사 메시지 태그 헬퍼와 (모델의 단일 속성에 대한 유효성 검사 메시지를 출력합니다), 유효성 검사 요약 태그 헬퍼가 (유효성 검사 요약을 출력합니다) 바로 그것입니다. Input 태그 헬퍼는 모델 클래스에 지정된 데이터 주석 어트리뷰트를 기반으로 Input 요소에 클라이언트 측 유효성 검사 HTML5 어트리뷰트를 추가합니다. 그리고 서버 측에서도 유효성 검사가 수행됩니다. 유효성 검사 태그 헬퍼는 이런 유효성 검사에서 오류가 발생하는 경우, 오류 메시지들을 출력합니다.

유효성 검사 메시지 태그 헬퍼

  • 지정된 모델 속성에 대한 Input 필드의 유효성 검사 오류 메시지를 연결해주는 HTML5 data-valmsg-for="property" 어트리뷰트를 span 요소에 추가합니다. 클라이언트 측 유효성 검사 오류가 발생할 경우, jQuery가 해당 오류 메시지를 <span> 요소에 출력해줍니다.
  • 유효성 검사는 서버 측에서도 수행됩니다. 클라이언트가 JavaScript를 비활성화시켰을 수도 있고, 일부 유효성 검사는 서버에서만 수행할 수 있는 경우도 있습니다.
  • 대신 Html.ValidationMessageFor HTML 헬퍼를 사용할 수 있습니다.

유효성 검사 메시지 태그 헬퍼는 HTML span 요소에 asp-validation-for 어트리뷰트를 지정해서 사용합니다.

<span asp-validation-for="Email"></span>

이 유효성 검사 메시지 태그 헬퍼는 다음과 같은 HTML을 생성합니다:

<span class="field-validation-valid"
    data-valmsg-for="Email"
    data-valmsg-replace="true"></span>

일반적으로 유효성 검사 메시지 태그 헬퍼는 동일한 속성에 대한 Input 태그 헬퍼 뒤에 위치하는 경우가 많습니다. 그러면 오류가 발생한 Input 요소 부근에 유효성 검사 오류 메시지를 출력할 수 있습니다.

노트

클라이언트 측 유효성 검사를 수행하기 위해서는 뷰에 올바른 JavaScript 및 jQuery 스크립트 참조가 구성되어 있어야만 합니다. 더 자세한 정보는 모델 유효성 검사를 참고하시기 바랍니다.

MVC는 서버 측 유효성 검사 오류가 발생하면 (사용자 지정 서버 측 유효성 검사를 사용하고 있거나 클라이언트 측 유효성 검사가 비활성화 된 경우), 오류 메시지를 <span> 요소의 본문으로 출력합니다.

<span class="field-validation-error" data-valmsg-for="Email" data-valmsg-replace="true">
    The Email Address field is required.
</span>

유효성 검사 요약 태그 헬퍼

  • asp-validation-summary 어트리뷰트를 지정해서 <div> 요소에 적용됩니다.
  • 대신 Html.ValidationSummary HTML 헬퍼를 사용할 수 있습니다.

유효성 검사 요약 태그 헬퍼는 유효성 검사 메시지의 요약 정보를 출력하기 위해서 사용됩니다. asp-validation-summary 어트리뷰트에는 다음 중 한 가지 값을 지정할 수 있습니다:

asp-validation-summary 출력할 유효성 검사 메시지
ValidationSummary.All 속성 및 모델 수준
ValidationSummary.ModelOnly 모델
ValidationSummary.None 출력 안 함

예제

다음 예제의 데이터 모델에는 DataAnnotation 네임스페이스의 어트리뷰트들이 적용되어 있으며, 이로 인해서 <input> 요소에 유효성 검사 오류 메시지 어트리뷰트들이 생성됩니다. 유효성 검사 오류가 발생하면 유효성 검사 태그 헬퍼가 오류 메시지를 출력합니다:

using System.ComponentModel.DataAnnotations;

namespace FormsTagHelper.ViewModels
{
    public class RegisterViewModel
    {
        [Required]
        [EmailAddress]
        [Display(Name = "Email Address")]
        public string Email { get; set; }

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

<form asp-controller="Demo" asp-action="RegisterValidation" method="post">
    <div asp-validation-summary="ValidationSummary.ModelOnly"></div>
    Email: <input asp-for="Email" /><br />
    <span asp-validation-for="Email"></span><br />
    Password: <input asp-for="Password" /><br />
    <span asp-validation-for="Password"></span><br />
    <button type="submit">Register</button>
</form>

다음은 생성되는 HTML입니다 (모델이 유효한 경우):

<form action="/DemoReg/Register" method="post">
    <div class="validation-summary-valid" data-valmsg-summary="true">
    <ul><li style="display:none"></li></ul></div>
    Email: <input name="Email" id="Email" type="email" value=""
        data-val-required="The Email field is required."
        data-val-email="The Email field is not a valid e-mail address."
        data-val="true"><br>
    <span class="field-validation-valid" data-valmsg-replace="true"
        data-valmsg-for="Email"></span><br>
    Password: <input name="Password" id="Password" type="password"
        data-val-required="The Password field is required." data-val="true"><br>
    <span class="field-validation-valid" data-valmsg-replace="true"
        data-valmsg-for="Password"></span><br>
    <button type="submit">Register</button>
    <input name="__RequestVerificationToken" type="hidden" value="<removed for brevity>" />
</form>

Select 태그 헬퍼

  • 모델의 속성에 대한 select 요소 및 연관된 option 요소들을 생성합니다.
  • 대신 Html.DropDownListFor HTML 헬퍼와 Html.ListBoxFor HTML 헬퍼를 사용할 수 있습니다.

다음 마크업과 같이 Select 태그 헬퍼asp-for 어트리뷰트에는 select 요소에 대한 모델 속성 이름을 지정하고, asp-items 어트리뷰트에는 option 요소들에 대한 모델 속성 이름을 지정합니다:

 <select asp-for="Country" asp-items="Model.Countries"></select>

예제:

using Microsoft.AspNetCore.Mvc.Rendering;
using System.Collections.Generic;

namespace FormsTagHelper.ViewModels
{
    public class CountryViewModel
    {
        public string Country { get; set; }

        public List<SelectListItem> Countries { get; } = new List<SelectListItem>
        {
            new SelectListItem { Value = "MX", Text = "Mexico" },
            new SelectListItem { Value = "CA", Text = "Canada" },
            new SelectListItem { Value = "US", Text = "USA"  },
        };
    }
}

Index 메서드에서는 CountryViewModel 개체를 초기화하고 선택된 국가를 설정한 다음, 이를 Index 뷰에 전달합니다.

public IActionResult Index()
{
    var model = new CountryViewModel();
    model.Country = "CA";
    return View(model);
}

그리고 HTTP POST Index 메서드에서는 선택된 결과를 출력합니다:

[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Index(CountryViewModel model)
{
    if (ModelState.IsValid)
    {
        var msg = model.Country +  " selected";
        return RedirectToAction("IndexSuccess", new { message = msg});
    }

    // If we got this far, something failed; redisplay form.
    return View(model);
}

Index 뷰의 마크업은 다음과 같습니다:

@model CountryViewModel

<form asp-controller="Home" asp-action="Index" method="post">
    <select asp-for="Country" asp-items="Model.Countries"></select> 
    <br /><button type="submit">Register</button>
</form>

이 뷰는 다음과 같은 HTML을 생성합니다 ("CA"가 선택되어 있습니다):

<form method="post" action="/">
    <select id="Country" name="Country">
        <option value="MX">Mexico</option>
        <option selected="selected" value="CA">Canada</option>
        <option value="US">USA</option>
    </select>
    <br /><button type="submit">Register</button>
    <input name="__RequestVerificationToken" type="hidden" value="<removed for brevity>" />
</form>

노트

ViewBag 개체나 ViewData 개체를 Select 태그 헬퍼와 함께 사용하는 것은 권장되지 않습니다. 뷰 모델을 사용한 방식을 사용하면 MVC 메타데이터를 보다 확실하게 제공할 수 있으며 일반적으로 문제가 덜 발생합니다.

다른 일반적인 태그 헬퍼 어트리뷰트들과는 달리 asp-items 어트리뷰트에 Model 전치사가 사용된 점에 주의하시기 바랍니다.

 <select asp-for="Country" asp-items="Model.Countries"></select>

Enum 바인딩

종종 <select> 요소와 함께 enum 속성을 사용해서 enum 값들로부터 SelectListItem 요소들을 생성하면 편리한 경우가 많습니다.

예제:

public class CountryEnumViewModel
{
    public CountryEnum EnumCountry { get; set; }
}
using System.ComponentModel.DataAnnotations;

namespace FormsTagHelper.ViewModels
{
    public enum CountryEnum
    {
        Mexico,
        [Display(Name = "United States of America")]
        USA,
        Canada,
        France,
        Germany,
        Spain
    }
}

GetEnumSelectList 메서드를 사용하면 열거형에 대한 SelectList 개체를 생성할 수 있습니다.

@model CountryEnumViewModel

<form asp-controller="Home" asp-action="IndexEnum" method="post">
    <select asp-for="EnumCountry" 
            asp-items="Html.GetEnumSelectList<CountryEnum>()">
    </select> 
    <br /><button type="submit">Register</button>
</form>

열거형 목록에 Display 어트리뷰트를 적용해서 UI를 보다 풍부하게 구성할 수도 있습니다:

using System.ComponentModel.DataAnnotations;

namespace FormsTagHelper.ViewModels
{
    public enum CountryEnum
    {
        [Display(Name = "United Mexican States")]
        Mexico,
        [Display(Name = "United States of America")]
        USA,
        Canada,
        France,
        Germany,
        Spain
    }
}

다음과 같은 HTML이 만들어집니다:

<form method="post" action="/Home/IndexEnum">
    <select data-val="true" data-val-required="The EnumCountry field is required."
            id="EnumCountry" name="EnumCountry">
        <option value="0">United Mexican States</option>
        <option value="1">United States of America</option>
        <option value="2">Canada</option>
        <option value="3">France</option>
        <option value="4">Germany</option>
        <option selected="selected" value="5">Spain</option>
    </select>
    <br /><button type="submit">Register</button>
    <input name="__RequestVerificationToken" type="hidden" value="<removed for brevity>" />
</form>

Option 그룹

뷰 모델에 하나 이상의 SelectListGroup 개체가 포함되어 있는 경우에는 HTML <optgroup> 요소가 만들어집니다.

다음의 CountryViewModelGroup 모델 클래스는 SelectListItem 요소들을 "North America" 그룹과 "Europe" 그룹으로 분류하고 있습니다:

public class CountryViewModelGroup
{
    public CountryViewModelGroup()
    {
        var NorthAmericaGroup = new SelectListGroup { Name = "North America" };
        var EuropeGroup = new SelectListGroup { Name = "Europe" };

        Countries = new List<SelectListItem>
        {
            new SelectListItem
            {
                Value = "MEX",
                Text = "Mexico",
                Group = NorthAmericaGroup
            },
            new SelectListItem
            {
                Value = "CAN",
                Text = "Canada",
                Group = NorthAmericaGroup
            },
            new SelectListItem
            {
                Value = "US",
                Text = "USA",
                Group = NorthAmericaGroup
            },
            new SelectListItem
            {
                Value = "FR",
                Text = "France",
                Group = EuropeGroup
            },
            new SelectListItem
            {
                Value = "ES",
                Text = "Spain",
                Group = EuropeGroup
            },
            new SelectListItem
            {
                Value = "DE",
                Text = "Germany",
                Group = EuropeGroup
            }
        };
    }

    public string Country { get; set; }

    public List<SelectListItem> Countries { get; }
}

이 두 그룹은 다음과 같이 출력됩니다:

그리고 다음과 같은 HTML이 생성됩니다:

<form method="post" action="/Home/IndexGroup">
    <select id="Country" name="Country">
        <optgroup label="North America">
            <option value="MEX">Mexico</option>
            <option value="CAN">Canada</option>
            <option value="US">USA</option>
        </optgroup>
        <optgroup label="Europe">
            <option value="FR">France</option>
            <option value="ES">Spain</option>
            <option value="DE">Germany</option>
        </optgroup>
    </select>
    <br /><button type="submit">Register</button>
    <input name="__RequestVerificationToken" type="hidden" value="<removed for brevity>" />
</form>

다중 선택

Select 태그 헬퍼는 asp-for 어트리뷰트에 지정된 속성이 IEnumerable 형식일 경우 자동으로 multiple="multiple" 어트리뷰트를 생성합니다. 예를 들어, 다음과 같은 모델을:

using Microsoft.AspNetCore.Mvc.Rendering;
using System.Collections.Generic;

namespace FormsTagHelper.ViewModels
{
    public class CountryViewModelIEnumerable
    {
        public IEnumerable<string> CountryCodes { get; set; }

        public List<SelectListItem> Countries { get; } = new List<SelectListItem>
        {
            new SelectListItem { Value = "MX", Text = "Mexico" },
            new SelectListItem { Value = "CA", Text = "Canada" },
            new SelectListItem { Value = "US", Text = "USA"    },
            new SelectListItem { Value = "FR", Text = "France" },
            new SelectListItem { Value = "ES", Text = "Spain"  },
            new SelectListItem { Value = "DE", Text = "Germany"}
        };
    }
}

다음의 뷰를 이용해서 렌더하면:

@model CountryViewModelIEnumerable

<form asp-controller="Home" asp-action="IndexMultiSelect" method="post">
    <select asp-for="CountryCodes" asp-items="Model.Countries"></select> 
    <br /><button type="submit">Register</button>
</form>

다음과 같은 HTML이 만들어집니다:

<form method="post" action="/Home/IndexMultiSelect">
    <select id="CountryCodes"
            multiple="multiple"
            name="CountryCodes">
        <option value="MX">Mexico</option>
        <option value="CA">Canada</option>
        <option value="US">USA</option>
        <option value="FR">France</option>
        <option value="ES">Spain</option>
        <option value="DE">Germany</option>
    </select>
    <br /><button type="submit">Register</button>
    <input name="__RequestVerificationToken" type="hidden" value="<removed for brevity>" />
</form>

선택되지 않음

값을 선택하지 않는 상황을 지원하기 위해서는, 선택 목록에 "선택되지 않음"을 의미하는 option 요소를 추가해야 합니다. 만약 해당 속성이 값 형식인 경우에는, 이를 nullable로 만들어야 합니다.

@model CountryViewModel

<form asp-controller="Home" asp-action="IndexEmpty" method="post">
    <select asp-for="Country" asp-items="Model.Countries">
        <option value="">&lt;none&gt;</option>
    </select> 
    <br /><button type="submit">Register</button>
</form>

여러 페이지에서 "선택되지 않음" option 요소를 사용해야 한다면, 전용 템플릿을 작성해서 반복적인 HTML 작업을 줄일 수도 있습니다:

@model CountryViewModel

<form asp-controller="Home" asp-action="IndexEmpty" method="post">
    @Html.EditorForModel()
    <br /><button type="submit">Register</button>
</form>

다음은 Views/Shared/EditorTemplates/CountryViewModel.cshtml 템플릿을 보여줍니다:

@model CountryViewModel

<select asp-for="Country" asp-items="Model.Countries">
    <option value="">--none--</option>
</select>

값을 선택하지 않는 경우에만 HTML <option> 요소를 추가할 수 있는 것은 아닙니다. 예를 들어서, 다음의 뷰와 액션 메서드는 이전 코드와 비슷한 HTML을 만들어냅니다:

public IActionResult IndexOption(int id)
{
    var model = new CountryViewModel();
    model.Country = "CA";
    return View(model);
}
@model CountryViewModel

<form asp-controller="Home" asp-action="IndexEmpty" method="post">
    <select asp-for="Country">
        <option value="">&lt;none&gt;</option>
        <option value="MX">Mexico</option>
        <option value="CA">Canada</option>
        <option value="US">USA</option>
    </select> 
    <br /><button type="submit">Register</button>
</form>

결과를 확인해보면 현재의 Country 값에 따라서 올바른 <option> 요소가 선택되는 것을 (selected="selected" 어트리뷰트가 설정됩니다) 알 수 있습니다.

<form method="post" action="/Home/IndexEmpty">
    <select id="Country" name="Country">
        <option value="">&lt;none&gt;</option>
        <option value="MX">Mexico</option>
        <option value="CA" selected="selected">Canada</option>
        <option value="US">USA</option>
    </select>
    <br /><button type="submit">Register</button>
    <input name="__RequestVerificationToken" type="hidden" value="<removed for brevity>" />
</form>