모기와의 사투를 버린 끝에 이제야 컴퓨터 앞에 앉아 글을 쓸 수 있게 되네요(새벽 1시네요ㅡ.ㅡ) 아흑.
비록 눈이 따갑고 눕고 싶지만, 이제는 정말 제 자신과의 약속을 지키기 위해 한자 한자 적어나가렵니다.^^

지난 시간에 유효성 검사에 대해 살펴봤는데요. 이제 본론으로 넘어와서 적용해봐야겠죠?

유효성 검사 적용하기

저희가 USER 모델을 생성할때 엔터티 프레임워크(엔티티가 입에 붙었는데 한글판에 엔터티라고 명시되어있네요;;)를 통해 생성한 것 다들 기억하시죠? 엔터티 프레임워크의 경우 자동으로 모델 클래스를 생성해 주는 것도 다들 아실겁니다. 또한, 엔터티 프레임워크로 생성된 모델클래스를 직접적으로 컨트롤 할수 없다는 것도..
그렇다면 유효성 검사 부분은 도대체 어디다 둬야 한단 말이냐?

파샬 & 메타데이타 클래스 생성하기

메타 데이타 클래스를 만들어야 합니다. 또한 USER 모델에 해당하는 파샬 클래스도 생성해야합니다.
파샬 클래스의 경우 여러 파일, 여러 부분에 멤버나 메쏘드 등의 정의를 각각 두면 컴파일시에 이들 모두를 결합하게 되죠. 다들 아시는 내용!
여기서 잠깐, 엔터티 프레임워크로 생성된 모델 클래스의 소스를 잠깐 살펴보면,


모델 클래스가 파샬 클래스로 정의 되어 있는 것을 확인할 수 있습니다. 아~ 이러면 자동 생성된 이 모델 클래스는 건들 필요 없이 파샬 클래스를 하나 더 추가해서 그곳에다가 우리가 필요한 정의를 내려주면 되겠구나~ 라는 생각이 팍팍 드시죠?

그래서 추가해봤습니다. 동일한 이름의 모델 클래스를 하나 만들어 보죠. 그리고, 메타 데이타 클래스도 같이 만들겠습니다.

using System.ComponentModel.DataAnnotations;
using System.ComponentModel;

namespace MvcSite.Models

    [MetadataType(typeof(USERMetaData))]
    public partial class USER
    {      
    }

    public class USERMetaData
    {
        [Required(ErrorMessage="아이디 입력하셔야죠!")]
        [StringLength(10)]
        public object ID { get; set; }

        [Required(ErrorMessage="이름 입력하셔야죠!")]
        public object NAME { get; set; }
               
        [Required(ErrorMessage="패스워드 입력하셔야죠!")]       
        public object PWD { get; set; }
              
        [Required(ErrorMessage="이메일 입력하셔야죠!")]
        [RegularExpression(@"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$",
                                                             ErrorMessage = "올바른 이메일 형식이 아닙니다.")]
        public object EMAIL { get; set; }
    }
}

메타 데이터의 경우 테이블의 필드값을 대신합니다. 즉 모델과 같아야 합니다.
메타 데이터를 만든 후 파샬로 된 모델(USER) 클래스에 MetadataTypeAttribute를 통해 USERMetaData을 정의합니다. 이렇게하면 1차작업이 완료됩니다. 실행해 보시면 잘 돌아갑니다. 확인페이지는 따로 보여드리지 않겠습니다^^ 글이 너무 길어지면 지루해지겠죠?

모델에 없는 필드 확인하기

우리는 패스워드 확인 필드를 갖고 있습니다. 필수값이고 비교도 해야하지만 테이블에는 없는 필드죠. DataAnnotation을 통해 나머지 필드들은 각각 비교는 했는데, 패스워드 확인 필드는 어떻게~ 어떻게~ 어떡하면 되냐고~ 띠리링~ 그냥 만들어!

헉. 뭐 만들면 되죠;;;
일단, ValidationAttribute를 상속 받는 PropertiesMatchAttribute라는 이름의 두 값을 비교할 커스텀한 DataAnnotation 클래스를 만듭니다. 중요한건 검사를 담당하게될 IsValid 메쏘드를 오버라이드해야합니다.


이렇게 만든 후에, 생성한 USER 클래스를 수정하도록 하겠습니다.

    [PropertiesMatchAttribute("PWD", "CPWD",
                                         ErrorMessage = "패스워드 확인 안하실거에요?!")]

    [MetadataType(typeof(USERMetaData))]
    public partial class USER
    {
        [Required(ErrorMessage = "패스워드 확인 입력하셔야죠!")] 
       public string CPWD { get; set; }
    }

    public class USERMetaData
    {
        [Required(ErrorMessage="아이디 입력하셔야죠!")]
        [StringLength(10)]
        public object ID { get; set; }

        [Required(ErrorMessage="이름 입력하셔야죠!")]
        public object NAME { get; set; }
               
        [Required(ErrorMessage="패스워드 입력하셔야죠!")]       
        public object PWD { get; set; }
              
        [Required(ErrorMessage="이메일 입력하셔야죠!")]
        [RegularExpression(@"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$",
                                                            ErrorMessage = "올바른 이메일 형식이 아닙니다.")]
        public object EMAIL { get; set; }
    }

USER 클래스에 커스텀한 DataAnnotation 정의를 추가했고요, 패스워드 확인 필드를 필수값으로 정의하였습니다.
여기까지 잘 오셨죠? 실행해 보도록 하겠습니다.


위 결과물은 모든 필드에 입력을 안하고 submit을 했을 경우고, 아래 결과물은 패스워드를 다르게 입력 했을 경우입니다.


일단 원하는대로 출력되는 것을 확인했습니다.

역시 급정리요

이번시간 역시 유효성 검사 부분을 다뤘고요, 메타 데이터와 파샬 클래스를 이용한 유효성 검사를 살펴봤습니다.
정말 간단한 내용인데 쓰다보면 길어지네요;; 더 간단하게 필요한 메시지만 전달하도록 노력하겠습니다.

참조 : http://byatool.com/mvc/custom-data-annotations-with-mvc-how-to-check-multiple-properties-at-one-time

M, V 그리고 C의 각방생활(4) - 유효성 검사

ASP.NET MVC 2010. 5. 31. 09:00 Posted by 네버덜레스
안녕하세요. 지난 포스팅에 이어서(넘흐 오랜만이죠^^;) 시작하겠습니다. 아마 다들 잊으셨을 겁니다. 여기까지 했었죠?


_db.SaveChanges() 를 하려 했더니, 에러가 발생했습니다. 자세히 들여다 보니


ID 에 NULL 값을 넣을 수가 없다네요. 이래서 에러가 발생했죠.
아~ 이래서 사용자가 빈 값을 넣으려 하면 막아야하겠구나~ 라는 생각이 번뜩 드셨을겁니다.

유효성 검사!

유효성검사라 하면 필수입력값에는 꼭 데이터를 입력해야하고, 데이터의 타입이나 길이에 맞게 들어오게 체크하는 것을 말하겠죠?

ASP.NET MVC 프레임워크에서는 모델 스테이트(Model State)를 제공합니다. 정확히 말하면 model state dictionary 라고 해서 유효성 에러들을 표시하기 위해 사용됩니다. 유효성 검사중에 해당 프로퍼티에서 fail 이 발생하면 모델 스테이트에 이를 추가합니다. 모델 스테이트에 에러가 있으면 ModelState.IsVaild 는 false를 반환합니다.
여기까지 설명을 드리고, 예제와 함께 보시겠습니다.

예제 만들기

아주 간단한 전화번호를 담는 TelDir 클래스를 만들겠습니다.


DirectoryController 도 추가하겠습니다. 이 컨트롤러에 두개의 Create 액션메쏘드를 만들겠습니다. 하나는 /Directory/Create url 요청시(GET) 호출되는 메쏘드이고, 다른 하나는 POST로 호출되는 메쏘드 입니다. 아시죠?^^
ASP.NET MVC 프레임워크에서는 자동적으로 폼 필드에 값을 해당 모델 속성들과 매핑을 시킵니다. 모델 바인더가 이런 일을 하게되죠. 예제에서 처럼 HTML 폼 필드의 값을 TelDir 객체에 매핑을 시키는데, 에러가 없이 바인딩이 되면 즉, ModelState.IsValid가 true 이면 데이터베이스에 저장을 하는 것이고, 그렇지 않다면, 다시 폼을 그리며 에러를 표시하게됩니다.


뷰도 같이 만들겠습니다. 액션메쏘드에서 오른쪽버튼을 클릭하여 Add View 를 선택하고, 강하게 생성하겠습니다.


추가하기 전에 빌드하는 것 잊지 않으셨죠? 모델 생성 후 빌드를 하지 않으면 View data class 항목에 표시가 되지 않습니다. Add 해서 완료를 하시면 /Views/Directory/Create.aspx 가 생성되었습니다.

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MvcTest.Models.TelDir>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
 Create
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <h2>Create</h2>
    <% using (Html.BeginForm()) {%>
        <%: Html.ValidationSummary(true) %>
        <fieldset>
            <legend>Fields</legend>
           
            <div class="editor-label">
                <%: Html.LabelFor(model => model.Name) %>
            </div>
            <div class="editor-field">
                <%: Html.TextBoxFor(model => model.Name) %>
                <%: Html.ValidationMessageFor(model => model.Name) %>
            </div>
           
            <div class="editor-label">
                <%: Html.LabelFor(model => model.Phone) %>
            </div>
            <div class="editor-field">
                <%: Html.TextBoxFor(model => model.Phone) %>
                <%: Html.ValidationMessageFor(model => model.Phone) %>
            </div>
           
            <div class="editor-label">
                <%: Html.LabelFor(model => model.SpeedDial) %>
            </div>
            <div class="editor-field">
                <%: Html.TextBoxFor(model => model.SpeedDial) %>
                <%: Html.ValidationMessageFor(model => model.SpeedDial) %>
            </div>
           
            <div class="editor-label">
                <%: Html.LabelFor(model => model.Email) %>
            </div>
            <div class="editor-field">
                <%: Html.TextBoxFor(model => model.Email) %>
                <%: Html.ValidationMessageFor(model => model.Email) %>
            </div>
           
            <p>
                <input type="submit" value="Create" />
            </p>
        </fieldset>
    <% } %>
</asp:Content>

휴. 여기까지 했으니 이제 유효성검사를 해보실까요?
다음과 같이 컨트롤러에서 유효성 검사를 할수 있습니다.


물론, 클라이언트단인 aspx 에서도 할 수 있겠죠. 제가 프로젝트에서 경험해본 유효성검사는 클라이언트단에서 먼저 검사를 하고 혹시나 몰라서, 클라이언트에서의 유효성검사를 신뢰할수 없어서 서버단에서도 한번 더 유효성검사를 했었습니다. 코드가 중복되고 또한 비슷한 UI 에서도 같은 검사를 해야했었죠.
ASP.NET MVC 에서는 이러한 부분을 모두 없애고 모델클래스에서 이를 담당하게 합니다. 심플해지고 개발속도도 향상되죠.

DataAnnotation을 이용한 유효성 검사


위와같이 컨트롤러와 뷰가아닌 모델에 유효성 검사로직을 두게되면, 다른 UI(Edit와 같은) 에서도 따로 유효성 검사를 하지 않고도 동일한 유효성 검사를 할 수 있습니다. 이렇게 함으로써 중복되는 코드를 피할 수 있게되는 거죠. DRY관점에서도 올바른 방향으로 나가는 거겠죠?ㅡ.ㅡ

위 소스를 보시면 유효성 검사를 위한 몇개의 속성들이 눈에 띄실겁니다.  using 문에 System.ComponentModel.DataAnnotations를 추가하면 유효성 검사 속성들을 사용할 수가 있습니다. 
각 필드에 속성들을 추가할 수 있는데요. [Required], [Ragng], [ReqularExpression], [StringLength] 등이 있고 커스텀한 속성도 만들 수가 있습니다.
만약 Name 에 길이제한을 5자로 하고 싶다면 [StringLength(5, ErrorMessage="5자까지만!")] 을 추가만 하시면 됩니다. 모델의 유효성 검사를 추가함으로(컨트롤러와 뷰 수정없이), 이 모델을 사용하는 부분에는 모두 적용이 되는거죠. 참 쉽죠잉?

일단 에러를 내볼까요?


위 에러메시지가 표시되는 것은 Create.aspx 소스를 보시면

<div class="editor-field">
    <%: Html.TextBoxFor(model => model.Name) %>
    <%: Html.ValidationMessageFor(model => model.Name) %>
</div>

<%: Html.ValidationMessageFor() %> 를 보실 수 있습니다. 바로 이것이 ModelState.IsValid 가 false 여서 뷰를 다시 그릴때, 각각의 해당 필드 옆에 붙어서 에러메시지를 보여주는 유효성 검사 헬퍼 메쏘드 입니다.

여기서 마무리

바로 지난번에 이어 계속 진행하고 싶지만, 유효성 검사에 대해 설명하다보니 이것만으로 너무 길어져서 오늘도 여기서 마무리 하겠습니다(__). 곧 찾아뵙도록 하겠습니다. ^^

참고 : http://weblogs.asp.net/scottgu/archive/2010/01/15/asp-net-mvc-2-model-validation.aspx