ASP.NET MVC 커스텀 ViewHelper 구현: 텍스트박스 컴포넌트

텍스트박스(View) 구현 개요

이전 이론적 배경에 이어 실제 텍스트박스를 위한 ViewHelper 클래스를 설계해 보겠습니다. 우리가 목표로 하는 컴포넌트는 다음과 같은 핵심 기능을 갖추어야 합니다.

  • 레이블(Label) 태그 포함 여부 옵션
  • 모델 데이터나 기본값을 바인딩할 텍스트 박스 입력 필드
  • 유효성 검사 메시지(Validation Message) 표시 영역

이 세 가지 기본 요건은 지난 편에서 언급된 다섯 가지 설계 원칙 모두를 충족시킵니다. 여기에 더해 코드 재사용성을 높이기 위해 리스트 항목(li)으로 감싸는지 여부와 읽기 전용(readonly) 모드 여부를 속성으로 정의할 수 있으면 유용합니다. 이를 통해 뷰 페이지 내에서의 유연한 제어가 가능해집니다. 아래 코드는 HtmlText 객체가 관리해야 하는 모든 속성을 초기화하는 생성자 정의입니다.

private readonly string _labelText; 
private readonly bool _generateLabel; 
private readonly object _boundValue; 
private readonly string _validationMsg; 
private readonly bool _showValidationMsg; 
private readonly bool _wrapInListItem; 
private readonly bool _readOnlyMode; 

public HtmlText(
    ViewRequestContext context, string identifier, string labelText, 
    object value, string validationMessage, bool readOnlyMode, 
    bool wrapInListItem, object attributes
    ) : base(context, identifier)
{
    _labelText = labelText;
    _generateLabel = !string.IsNullOrWhiteSpace(labelText);
    _validationMsg = validationMessage;
    _showValidationMsg = !string.IsNullOrWhiteSpace(validationMessage);
    _wrapInListItem = wrapInListItem;
    _readOnlyMode = readOnlyMode;
    Attributes = attributes;

    object effectiveValue = value;
    
    // 모델 상태에서 값 찾기 시도
    if (effectiveValue == null) 
    {
        effectiveValue = RetrieveModelValue(identifier, typeof(string));
    }

    _boundValue = effectiveValue;
}

생성자 및 매개변수 처리

생성자 내부에서는 각 파라미터를 비공개 필드에 저장하고, 렌더링 시점인 StartView 메서드에서 사용할 플래그들을 초기화합니다. 특히 RetrieveModelValue 메서드가 사용되는데, 이는 현재 폼의 ModelState 에서 해당 이름에 해당하는 값을 조회하는 역할을 합니다. 이 메서드의 상세 구현은 추후 다룰 예정입니다.

다음 두 가지 설계 선택에 주목할 필요가 있습니다:

  1. value 파라미터 타입: object 타입으로 정의되었습니다. 이는 사용자가 편리하게 데이터를 전달할 수 있도록 하며, 기존 ASP.NET MVC Helpers 와 일관된 행동을 보장합니다.
  2. attributes 파라미터: 역시 object 타입이며 익명 객체를 사용하여 확장할 수 있습니다. 예를 들어, 텍스트 박스의 maxlength 속성을 5 로 제한하려면 new { maxlength = 5 }와 같이 전달하면 됩니다. 이렇게 전달된 익명 타입은 생성되는 HTML 의 특성이 되어 변환됩니다.

이러한 유연성은 모든 View helper 객체에 적용되어야 하므로 본 클래스에서도 동일하게 구현하였습니다.

렌더링 로직과 TagBuilder 활용

클래스가 상속받은 StartView와 EndView 메서드는 실제 HTML 을 출력하는 핵심 부분입니다. 문자열 직접拼接(Concatenation) 대신 System.Web.Mvc.TagBuilder를 사용하는 것을 강력히 권장합니다. ASP.NET MVC 환경에서 TagBuilder 는 HTML 구조를 안전하게 생성하고 관리하는 표준 도구입니다.

TagBuilder 클래스의 주요 기능과 그 설명은 아래 표와 같습니다.

메서드 이름설명
AddCssClassCSS 클래스 이름을 추가합니다. 이미 존재하는 클래스라면 기존 값과 병합됩니다.
MergeAttribute태그의 속성을 추가하거나 업데이트합니다. 중복 키 처리를 위한 replaceExisting 인자를 가진 오버로딩이 제공됩니다.
MergeAttributes하나의 호출로 여러 속성을 추가하거나 갱신합니다.
SetInnerText태그 내부에 표시될 텍스트 콘텐츠를 설정합니다.
ToStringHTML 문자열을 생성합니다. TagRenderMode 열거형 값을 통해 태그 형태를 제어합니다.

ToString 메서드에서 사용되는 TagRenderMode枚举值에 따라 생성되는 HTML 형태가 결정됩니다.

태그 렌더링 모드결과 예시
Normal<div name="Sample01">내용</div>
StartTag<div name="Sample01">
EndTag</div>
SelfClosing<div name="Sample01" />

필요한 HTML 구조에 따라 적절히 모드 (StartTag, Normal, EndTag 등) 를 선택해야 합니다. InnerHtml 속성에 값을 대입하더라도 자동으로 닫히는 태그가 생성되지 않으므로, 명확하게 시작과 종료 태그를 생성하여 명시적으로 내용을 작성해야 합니다.

public override void StartView() 
{ 
    HttpResponseBase response = RequestContext.HttpResponse;
    string outputName = this.Name;
    
    // LI 랩퍼 처리
    if (_wrapInListItem) 
    { 
        var liBuilder = new TagBuilder("li"); 
        response.Write(liBuilder.ToString(TagRenderMode.StartTag)); 
    } 
 
    // 레이블 렌더링
    if (_generateLabel) 
    { 
        var labelTag = new TagBuilder("label"); 
        labelTag.Attributes.Add("for", outputName); 
        labelTag.SetInnerText(_labelText); 
        response.Write(labelTag.ToString(TagRenderMode.Normal)); 
    } 
 
    string displayValue = string.Empty; 
    if (_boundValue != null) 
    { 
        displayValue = Convert.ToString(_boundValue, CultureInfo.CurrentCulture); 
    } 
 
    // 읽기 전용 모드 체크 및 분기
    if (_readOnlyMode) 
    { 
        var spanTag = new TagBuilder("span");         
        spanTag.AddCssClass("readonly-text"); 
        spanTag.SetInnerText(displayValue); 
        response.Write(spanTag.ToString(TagRenderMode.Normal)); 
    } 
    else 
    { 
        // 표준 MVC 헬퍼를 활용해 인풋 생성
        response.Write(RequestContext.HtmlHelper.TextBox(
            outputName, _boundValue, Attributes)); 
    } 
 
    // LI 래퍼 닫기
    if (_wrapInListItem) 
    { 
        var liEndBuilder = new TagBuilder("li"); 
        response.Write(liEndBuilder.ToString(TagRenderMode.EndTag)); 
    } 
}

public override void EndView() 
{ 
    // 이 요소에는 추가적인 종료 작업 불필요
}

위 StartView 구현에서 볼 수 있듯이, UI 구성 요소들의 순차적인 출력을 체계적으로 관리할 수 있습니다. 이제 본격적으로 HtmlHelper 확장 메서드를 어떻게 생성할지에 대해 논의할 차례입니다.

태그: ASP.NET MVC C# ViewHelper TagBuilder HTML Generation

10월 5일 11:36에 게시됨