텍스트박스(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 에서 해당 이름에 해당하는 값을 조회하는 역할을 합니다. 이 메서드의 상세 구현은 추후 다룰 예정입니다.
다음 두 가지 설계 선택에 주목할 필요가 있습니다:
value파라미터 타입:object타입으로 정의되었습니다. 이는 사용자가 편리하게 데이터를 전달할 수 있도록 하며, 기존 ASP.NET MVC Helpers 와 일관된 행동을 보장합니다.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 클래스의 주요 기능과 그 설명은 아래 표와 같습니다.
| 메서드 이름 | 설명 |
|---|---|
| AddCssClass | CSS 클래스 이름을 추가합니다. 이미 존재하는 클래스라면 기존 값과 병합됩니다. |
| MergeAttribute | 태그의 속성을 추가하거나 업데이트합니다. 중복 키 처리를 위한 replaceExisting 인자를 가진 오버로딩이 제공됩니다. |
| MergeAttributes | 하나의 호출로 여러 속성을 추가하거나 갱신합니다. |
| SetInnerText | 태그 내부에 표시될 텍스트 콘텐츠를 설정합니다. |
| ToString | HTML 문자열을 생성합니다. 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 확장 메서드를 어떻게 생성할지에 대해 논의할 차례입니다.