Visual Studio 2008의 발표와 함께 ASP.NET 3.5도 정식 발표가 되었습니다.
ASP.NET 3.5로 버전이 변경되면서 2.0과의 차이점은 많이 있지는 않습니다만
그래도 아주 유용한 컨트롤이 추가가 되었습니다.
ListView라는 컨트롤 인데요. 제가 보기엔 GridView와 DataList의 장점을 빼내어
만들어진 컨트롤 같습니다. 그동안 참으로 아쉬웠던 부분이였는데...
이번에 이렇게 추가가 되어서 참으로 기쁩니다.
그럼 ListView의 설명과 사용법에 대해서 알아보도록 하겠습니다.

ListView 설명

아래 설명은 MSDN의 ListView 클래스의 설명을 사용하였습니다.
그동안 GridView를 사용하셨던 분들이라면 더욱 쉽게 접근을 하실수 있을 것입니다.
(GridView  예제 참고 : 게시판 목록을 보여주는 GridView 예제)

템플릿 형식 설명
LayoutTemplate LayoutTemplate 속성을 사용하여 ListView 컨트롤의 루트 컨테이너에 대한 사용자 지정 UI(사용자 인터페이스)를 정의할 수 있습니다. LayoutTemplate 템플릿은 ListView 컨트롤의 필수 요소입니다.

LayoutTemplate 내용에는 ItemTemplate 템플릿에 정의된 항목이나 GroupTemplate 템플릿에 정의된 그룹에 대한 테이블 행(tr) 요소 같은 자리 표시자 컨트롤이 들어 있어야 합니다. 이 자리 표시자 컨트롤의 runat 특성은 "server"로 설정되어 있어야 하고 ID 특성은 ListView 컨트롤이 그룹을 사용하고 있는지 여부에 따라 ItemPlaceholderID 또는 GroupPlaceholderID 속성의 값으로 설정되어 있어야 합니다.
ItemTemplate ItemTemplate 속성을 사용하여 데이터 항목을 표시하기 위한 사용자 지정 UI(사용자 인터페이스)를 정의할 수 있습니다. ItemTemplate 템플릿은 ListView 컨트롤의 필수 요소입니다. 이 템플릿에는 일반적으로 레코드의 필드 값을 표시하기 위한 컨트롤이 들어 있습니다. 사용자가 데이터를 수정할 수 있도록 하기 위해 일반적으로 레코드를 선택하거나, 편집 모드로 전환하거나, 레코드를 삭제할 수 있는 단추도 ItemTemplate 템플릿에 추가합니다.

컨트롤에 바인딩된 데이터 소스의 필드 값을 표시하려면 데이터 바인딩 식을 사용합니다. 자세한 내용은 데이터 바인딩 식 구문을 참조하십시오.

기본 제공 선택, 삭제 및 편집 작업을 수행하는 단추를 만들려면 단추 컨트롤을 템플릿에 추가합니다. 컨트롤의 CommandName 속성을 다음 표에 있는 값 중 하나로 설정합니다.
단추 종류 CommandName 값
삭제 "Delete"
편집 "Edit"
선택 "Select"
ItemSeparatorTemplate ItemSeparatorTemplate 속성을 사용하여 ListView 컨트롤의 단일 항목 사이에 있는 구분 기호의 내용을 정의할 수 있습니다. ItemSeparatorTemplate은 마지막 항목을 제외하고 모든 항목 뒤에 표시됩니다.
GroupTemplate GroupTemplate 속성을 사용하여 ListView에 바둑판식 레이아웃을 만들 수 있습니다. 바둑판식 테이블 레이아웃에서는 항목이 한 행에서 가로 방향으로 반복됩니다. 항목의 반복 횟수는 GroupItemCount 속성에 의해 지정됩니다.

GroupTemplate 속성에는 테이블 셀(td), div 또는 span 요소 같은 데이터 항목에 대한 자리 표시자가 들어 있어야 합니다. 이 자리 표시자의 runat 특성은 "server"로 설정되어 있어야 하고 ID 특성은 ItemPlaceholderID 속성의 값으로 설정되어 있어야 합니다. 런타임에 ListView 컨트롤은 자리 표시자를 ItemTemplate 및 AlternatingItemTemplate 템플릿의 각 항목에 대해 정의된 내용으로 바꿉니다.
GroupSeparatorTemplate 구분 기호를 사용하여 각 그룹 사이에 사용자 지정 내용이 들어 있는 요소를 넣을 수 있습니다. 그런 다음 ListView 컨트롤에서 GroupTemplate 내용과 GroupSeparatorTemplate 내용을 교대로 렌더링합니다. GroupTemplate 내용은 항상 마지막에 렌더링됩니다.

ListView 컨트롤은 LayoutTemplate 템플릿 내에서 GroupSeparatorTemplate 내용을 렌더링하므로 GroupSeparatorTemplate 템플릿의 전체 행에 대한 내용을 정의해야 합니다. 예를 들어 ListView 컨트롤에서 테이블 행(tr) 요소를 사용하여 그룹을 만들 수 있습니다. GroupItemCount 속성이 3으로 설정된 경우 GroupSeparatorTemplate 템플릿의 colspan 특성을 3으로 설정해야 합니다.
EmptyItemTemplate 현재 페이지의 마지막 그룹에 표시할 데이터 항목이 더 이상 없으면 ListView 컨트롤에 빈 항목이 표시됩니다. GroupItemCount가 1보다 큰 값으로 설정된 경우에만 이러한 상황이 발생할 수 있습니다. 예를 들어, ListView 컨트롤에서 GroupItemCount 속성이 5로 설정되어 있고 데이터 소스에서 반환된 전체 항목 수가 8개인 경우 마지막 데이터 행에는 ItemTemplate 템플릿에 정의된 3개의 항목과 EmptyItemTemplate 템플릿에 정의된 2개의 항목이 포함됩니다.

EmptyItemTemplate 속성을 사용하여 빈 항목에 대한 사용자 지정 UI(사용자 인터페이스)를 정의할 수 있습니다.
EmptyDataTemplate 컨트롤에 바인딩된 데이터 소스에 레코드가 들어 있지 않고 InsertItemPosition 속성이 InsertItemPosition..::.None으로 설정된 경우 빈 템플릿이 ListView 컨트롤에 표시됩니다. 이 템플릿은 LayoutTemplate 템플릿 대신 렌더링됩니다. InsertItemPosition 속성을 InsertItemPosition..::.None 이외의 값으로 설정하면 EmptyDataTemplate 템플릿이 렌더링되지 않습니다.

EmptyDataTemplate 속성을 사용하여 빈 템플릿에 대한 사용자 지정 UI(사용자 인터페이스)를 정의할 수 있습니다.
SelectedItemTemplate 선택한 항목을 다른 항목과 구별하기 위해 선택한 데이터 항목에 대해 렌더링할 내용을 정의합니다.
AlternatingItemTemplate 연속된 항목을 쉽게 구별하기 위해 대체 항목에 대해 렌더링할 내용을 정의합니다.
EditItemTemplate 항목을 편집할 때 렌더링할 내용을 정의합니다. EditItemTemplate 템플릿이 ItemTemplate 템플릿 대신 편집 중인 데이터 항목에 대해 렌더링됩니다.
InsertItemTemplate 항목을 삽입하기 위해 렌더링할 내용을 정의합니다. InsertItemTemplate 템플릿이 ItemTemplate 템플릿 대신 ListView 컨트롤에 표시된 항목의 처음이나 끝에서 렌더링됩니다. ListView 컨트롤의 InsertItemPosition 속성을 사용하여 InsertItemTemplate 템플릿의 렌더링 위치를 지정할 수 있습니다.

위 템플릿 설명의 이해가 잘 가지 않으시면 아래 소스를 이용한 사용 예제를 보시면 이해가 되실겁니다. 더욱 자세한 사항은 MSDN의 ListView 컨트롤 설명을 참고하시기 바랍니다.


ListView의 사용 예제

사용예제는 간단한 이미지 게시판 목록을 만들어 보도록 하겠습니다.
예제는 총 2가지로 동시에 진행되도록 하겠습니다.

첫번째 한줄에 하나의 데이터만 나오는 예제
두번째 한줄에 둘 이상의 데이터가 나오는 예제

설명은 소스를 가지고 간단히만 하도록 하겠습니다. 소스만 봐도 충분히 이해가 될것입니다.

그리고 예제에서는 DB까지 설명은 하지 않도록 하겠습니다. 단, 쿼리문은 아래와 같습니다.

Select [GoodsSeq],[Title],[Content],[ImageUrl] From TB_Goods

1. 게시판 목록에서 사용할 스타일을 지정하도록 하겠습니다.

<style type="text/css">
    body { font-family:Verdana, 굴림; font-size:9pt; }
   
    .list_title  { background-color:#5d7b9d; color:#ffffff; font-weight:bold; text-align:center;
                    height:25px; border-bottom:solid 2px #000000; border-top:solid 2px #000000;
                    padding-top:4px; }
    .list_item   { background-color:#ffffff; color:#284775; }
    .list_item2  { background-color:#eeeeee; color:#333333; }
    .list_sep    { background-color:#000000; height:1px; }
    .list_nodata { background-color:#ffffff; height:50px; text-align:center; }
</style>

2-1. 한줄에 하나의 데이터만 나오도록 하는 예제
   - 기존의 GridView 처럼 생각하시면 될거 같습니다.

<asp:ListView ID="ListView1" runat="server" ItemPlaceholderID="phItemList"
    OnItemDataBound="ListView_ItemDataBound">
    <LayoutTemplate>
        <table border="0" cellpadding="0" cellspacing="0">
            <tr>
                <td class="list_title" style="width:60px;">이미지</td>
                <td class="list_title" style="width:460px;">타이틀 / 설명</td>
            </tr>
            <asp:PlaceHolder ID="phItemList" runat="server" />
            <tr><td colspan="4" class="list_sep"></td></tr>
        </table>
    </LayoutTemplate>
    <ItemTemplate>

        <tr>
            <td class="list_item">
                <asp:Image ID="imgGoods" runat="server" Width="50px" Height="50px" Style="margin:5px;" />
            </td>
            <td class="list_item">
                <asp:HyperLink ID="hlTitle" runat="server" />
                <asp:Label ID="lblContent" runat="server" />
            </td>
        </tr>
    </ItemTemplate>
    <AlternatingItemTemplate>

            <td class="list_item2">
                <asp:Image ID="imgGoods" runat="server" Width="50px" Height="50px" Style="margin:5px;" />
            </td>
            <td class="list_item2">
                <asp:HyperLink ID="hlTitle" runat="server" />
                <asp:Label ID="lblContent" runat="server" />
            </td>
    </AlternatingItemTemplate>
    <ItemSeparatorTemplate>
        <tr><td colspan="2" class="list_sep"></td></tr>
    </ItemSeparatorTemplate>
    <EmptyDataTemplate>

        <table border="0" cellpadding="0" cellspacing="0">
            <tr>
                <td class="list_title" style="width:60px;">이미지</td>
                <td class="list_title" style="width:460px;">타이틀 / 설명</td>
            </tr>
            <tr>
                <td colspan="2" class="list_nodata">데이터가 없습니다.</td>
            </tr>
            <tr><td colspan="2" class="list_sep"></td></tr>
        </table>
    </EmptyDataTemplate>
</asp:ListView>
- 여러 템플릿 중에서 LayoutTemplate, ItemTemplate은 필수 사항입니다.
- 속성중에서 빨간색으로 된 부분 ItemPlaceholderID는 필수사항입니다.

2-1. 한줄에 두개 이상의 데이터가 나오도록 하는 예제
   - 기존의 DataList 처럼 생각하시면 될거 같습니다.

<asp:ListView ID="ListView2" runat="server" GroupPlaceholderID="phGroupList" ItemPlaceholderID="phItemList" GroupItemCount="2"
    OnItemDataBound="ListView_ItemDataBound">
    <LayoutTemplate>
        <table border="0" cellpadding="0" cellspacing="0" style="table-layout:fixed;">
            <tr>
                <td class="list_title" style="width:60px;">이미지</td>
                <td class="list_title" style="width:200px;">타이틀 / 설명</td>
                <td class="list_title" style="width:60px;">이미지</td>
                <td class="list_title" style="width:200px;">타이틀 / 설명</td>
            </tr>
            <asp:PlaceHolder ID="phGroupList" runat="server" />
            <tr><td colspan="4" class="list_sep"></td></tr>
        </table>
    </LayoutTemplate>
    <GroupTemplate>
        <tr>
            <asp:PlaceHolder ID="phItemList" runat="server" />
        </tr>
    </GroupTemplate>
    <ItemTemplate>

        <td class="list_item">
            <asp:Image ID="imgGoods" runat="server" Width="50px" Height="50px" Style="margin:5px;" />
        </td>
        <td class="list_item">
            <asp:HyperLink ID="hlTitle" runat="server" />
            <asp:Label ID="lblContent" runat="server" />
        </td>
    </ItemTemplate>
    <GroupSeparatorTemplate>

        <tr><td colspan="4" class="list_sep"></td></tr>
    </GroupSeparatorTemplate>
    <EmptyItemTemplate>

        <td colspan="2">&nbsp;</td>
    </EmptyItemTemplate>
    <EmptyDataTemplate>
        <table border="0" cellpadding="0" cellspacing="0">
            <tr>
                <td class="list_title" style="width:60px;">이미지</td>
                <td class="list_title" style="width:200px;">타이틀 / 설명</td>
                <td class="list_title" style="width:60px;">이미지</td>
                <td class="list_title" style="width:200px;">타이틀 / 설명</td>
            </tr>
            <tr>
                <td colspan="4" class="list_nodata">데이터가 없습니다.</td>
            </tr>
            <tr><td colspan="4" class="list_sep"></td></tr>
        </table>
    </EmptyDataTemplate>
</asp:ListView>
- 여러 템플릿 중에서 LayoutTemplate, GroupTemplate, ItemTemplate은 필수 사항입니다.
- 속성중에서 빨간색으로 된 부분 GroupPlaceholderID, GroupItemCount, ItemPlaceholderID는 필수사항입니다.

참고
위 2번에서 각 데이터가 들어갈 컨트롤에 직접 데이터 바인딩이 가능합니다.

<asp:Label ID="lblContent" runat="server" Text='<%#Eval("Content")%>' />

이와같이 지정을 하면 직접 데이터 바인딩이 됩니다. 참고로 Text 속성의 데이터를 지정할때 " 를 사용하지 말고 ' 를 사용해야 합니다.

그리고 이 예제에서는 직접 데이터 바인딩을 하지않고 OnItemDataBound 을 사용하여 데이터를 지정하도록 하겠습니다.

3. OnItemDataBound 사용
    - 2번의 디자인 소스에 보면 ListView 속성에 OnItemDataBound="ListView_ItemDataBound" 라는 이벤트가 지정되어 있습니다. cs 파일의 비하인드 코드에서 코드를 작성해 보도록 하겠습니다.

protected void ListView_ItemDataBound(object sender, ListViewItemEventArgs e)
{
    ListViewDataItem item = (ListViewDataItem)e.Item;
    // 아이템의 타입이 DataItem일 경우
    if (item.ItemType == ListViewItemType.DataItem)
    {
        // 상품의 이미지를 설정한다.
        Image imgGoods = item.FindControl("imgGoods") as Image;
        imgGoods.ImageUrl = string.Format("~/files/images/{0}", DataBinder.Eval(item.DataItem, "ImageUrl"));

        // 상품명과 상품 상세보기 링크를 설정한다.
        HyperLink hlTitle= item.FindControl("hlTitle") as HyperLink;
        hlTitle.Text = DataBinder.Eval(item.DataItem, "Title").ToString();
        hlTitle.NavigateUrl = string.Format("~/View.aspx?Seq={0}", DataBinder.Eval(item.DataItem, "GoodsSeq"));

        // 상품의 설명을 설정한다.
        Label lblContent = item.FindControl("lblContent") as Label;
        lblContent.Text = DataBinder.Eval(item.DataItem, "Content").ToString();
    }
}

이제 브라우저로 보기를 실행해보시기 바랍니다.
아래와 같은 화면이 나왔을 것입니다. (원하시는 화면이 나왔나요??)



이것으로 ASP.NET 3.5의 새로운 컨트롤 ListView에 대한 설명을 마치도록 하겠습니다.
이 내용이 많은 도움이 되길 바랍니다!!

ASP.NET로 개발하는 많은 개발자 분들이 구성 정보 파일인 web.config 파일에 어느 정도 익숙해 있을 것이라 생각합니다. 다들 아시다시피, 이 파일은 웹 사이트의 다양한 구성 정보들을 저장해놓는 역할을 하죠. 데이터베이스 연결 문자열(주로)부터 웹 사이트의 다양한 설정들을 이 파일에 저장해 놓곤 하는데요. 이번 강좌에서는 이 구성 파일에 우리만의 사용자 정의 구성 섹션을 별도로 제작하여 구성정보들을 직관적으로 다루는 방법에 대해 설명하고자 합니다.

그러니깐, 예를 들면 말입니다. 예전에는 다양한 설정 정보들을 주로 <appSettings> 영역에 주욱 나열하는 형태를 많이 써왔는데요. 이것이 뭐 그런대로 괜찮기는 했습니다만, 설정 정보가 수십 개 이상에 이르는 경우에는 관리가 용이하지 않다는 단점이 있었습니다(저만 그렇게 느낀 것은 아니었겠죠?). 그룹으로 묶임없이 일렬로 주루룩 나열되는 형태이다 보니 보기에도, 관리하기에도 그다지 직관적이지 않았다는 것이죠. 다음처럼 말입니다.

<configuration>
  
<appSettings>
    
<add key="ConnectionStr" value="server=(local);database=A;uid=siteUser;pwd=xx" />
    <
add key="MailTemplate" value="/Users/MailTemplate.htm" />
    <
add key="MailServer" value="100.100.100.100" />
    <
add key="MailServerPort" value="25" />
    <
add key="FileDownloadVPath" value="/Download/Files" />
    … 기타 등등의 설정이 수십 개~~ …
</appSettings>

물론, 이렇게 사용하는 것이 뭔가 크게 안 좋은 것은 아닙니다만, 가독성 측면과 관리 측면에서 그다지 좋지 않다는(이렇게 말하면 유수석님이 딴지를 걸 수도 있겠지만 ㅎㅎㅎ)…

해서, .NET 1.x 시절에도 이러한 구성정보 섹션을 다음과 같이 직관적으로 분류해서 작성하는 경우가 많았는데요. 이는 확실히 직관적인 구성을 가질 수 있다는 장점이 있습니다만, 이를 위해서 추가적으로 Configuration 관련 클래스를 개발해야 한다는 단점도 있습니다.

<configuration>
  
<configSections>
    
<sectionGroup name="TaeyoSite">
      
<section name="Mail" type="System.Configuration.DictionarySectionHandler, 
        System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
 />
    </
sectionGroup>
  
</configSections>

  
<!-- 태오 사이트 전용 구성 섹션 -->
  
<TaeyoSite>
    
<Mail>
      
<add key="MailTemplate" value="/Users/MailTemplate.htm" />
      <
add key="MailServerPort" value="25" />
      <
add key="MailServer" value="192.168.0.2" />
    </
Mail>
  
</TaeyoSite>

추가적인 Configuration 관련 클래스라고 말하니 뭔가 좀 말이 이상하네요. 정확히 말하자면, System.Configuration.IConfigurationSectionHandler 인터페이스를 구현하는 사용자 정의(custom) 구성 섹션 처리기를 제작해야 합니다 Web.config 파일에 우리 멋대로 추가해놓은 XML 구조를 이해 및 분석할 수 있는 처리기를 말이죠(아~ 말이 너무 길고 어렵네요 ㅜㅜ).. 근데, 그 클래스를 작성하는 것이 약간(?) 피곤 내지 복잡하기에 개발자의 귀차니즘이 발동하여, "별도 섹션은 무슨~ 그냥 <appSetting>를 이용하자"라고 하게 만들었었죠. ㅎㅎ (뻘쭘하면 걍 웃습니다?)

아아! 물론, 기존에도 System.Configuration.DictionarySectionHandler와 같이 기본으로 제공되는 처리기를 사용해서 구성을 할 수도 있긴 했습니다만(사실, 위의 예가 바로 그를 사용한 예입니다), 이는 XML 포맷이 딱 정해져 있기에 우리가 원하는 형태로 XML을 사용할 수 없다는 단점이 있죠. 위에서 보이다시피, 구성의 시작(<TaeyoSite>)은 그럴싸하지만, 실제 값 설정은 여전히 <add>를 이용해서 구성하는 것을 볼 수 있습니다. 좀 더 나은 구조라면 다음과 같은 것이 아닐까요? 물론, 이 견해는 지극히 주관적일 수 있습니다만 말입니다.

<configuration>
  
<site title="Taeyo.NET">
    
<mail server="192.168.0.2" template="/Users/MailTemplate.htm" port="25" />
  </
site>
  ...

그런데! 이렇게 구성하게 되면, DictionarySectionHandler와 같이 기본으로 제공되는 처리기로는 불가능하고, 위의 XML 구조를 분석하기 위한 별도의 구성 처리기를 제작해야 하는 상황에 이르게 됩니다. 슬슬 귀찮아지죠? 심지어는 강좌를 읽는 것도 슬슬 귀찮아지지 않나요? 하하!! 딱 걸렸어~~

그래서, .NET 2.0에서는 이러한 처리기를 쉽게 개발할 수 있도록 돕는 클래스들이 추가되었습니다. 해서, 자체 처리기를 개발하는 것이 상당히 간단하게 되죠. 이번 강좌에서는 그와 관련된 클래스를 소개하고, 이를 이용해 매우 쉽게(?) 우리만의 구성 섹션을 작성하여 web.config 의 가독성을 높이는 방법을 설명 드리고자 합니다.

ConfigurationSection 클래스

.NET 2.0에서 새로이 추가된 ConfigurationSection 클래스. 이 친구가 바로 사용자 지정 섹션 형식을 구현할 수 있게 하는 클래스입니다. 만일, 여러분이 다음과 같은 XML 구조를 web.config에서 사용하고 싶다면,

<configuration>
  
<configSections>
    
<section name="mySite" type="SiteSection"/>
  </
configSections>

  
<mySite title="Taeyo.NET" MailServerIP="61.100.xx.xx" MailServerPort="25"
         MailTemplate="/temps/mail.tmp" />
  </
mySite>

다음과 같은 식으로 처리기 클래스를 작성하시면 됩니다. (작성한 처리기는 위에서 보이는 것과 같이 <configSections>의 하위로 등록해 주어야 합니다)

public class SiteSection : ConfigurationSecqtion
{
    
public string Title { ... }
    
public string MailServerIP { ... }
    
public string MailServerPort { ... }
    
public string MailTemplate { ... }
    ...
}

개발 규칙은 매우 간단합니다. 처리기 클래스는 반드시 ConfigurationSection를 상속하여 구현되어야 하며, XML의 각 어트리뷰트들은 클래스의 속성으로서 정의하면 됩니다. 다만, 추가적으로 각 속성들이 [ConfigurationProperty] 어트리뷰트를 가져야 한다는 것을 기억하세요. 다음은 실제로 상기 XML을 위한 처리기 클래스의 코드입니다.

public class SiteSection : ConfigurationSection
{
    [ConfigurationProperty(
"title", DefaultValue="Taeyo.NET")]
    
public string Title
    {
        
get return (string)base["title"]}
        
set base["title"= value; }
    }

    [ConfigurationProperty(
"mailServerIP")]
    
public string MailServerIP {
        
get return (string)base["mailServerIP"]}
        
set base["mailServerIP"= value; }
    }

    [ConfigurationProperty(
"mailServerPort")]
    
public string MailServerPort {
        
get return (string)base["mailServerPort"]}
        
set base["mailServerPort"= value; }
    }

    [ConfigurationProperty(
"mailTemplate")]
    
public string MailTemplate {
        
get return (string)base["mailTemplate"]}
        
set base["mailTemplate"= value; }
    }
}

ConfigurationProperty 어트리뷰트에 사용되는 이름은 실제 XML에서의 각 어트리뷰트 명과 동일해야 한다는 점을 주의하시면 되구요. 실제 속성 안에서의 get, set 용 값은 base["xml 어트리뷰트 명"];과 같이 접근하시면 됩니다. 매우 간단하죠?

다만, 이전의 XML 구조를 살펴보면 Mail과 관련된 정보들이 3개 정도가 있는데요. 이들을 하위 노드로서 분리하면 더욱 구조가 이쁠 것 같지 않나요? 다음처럼 말입니다.

<mySite title="Taeyo.NET">
  
<mail server="192.168.0.2" template="/Users/MailTemplate.htm" port="25" />
</
mySite>

그렇죠? 이렇게 XML을 구성하는 것이 보다 직관적일 것입니다. 그렇다면, 이런 구조를 위해서라면 처리기를 어떻게 구성해야 할까요? 이 또한 어려울 것이 없습니다. 하위 노드를 위해서는 ConfigurationElement 클래스를 상속하는 하위 클래스를 정의하면 됩니다. 다음은 그렇게 정의한 클래스의 프로토 타입입니다.

public class SiteSection : ConfigurationSection
{
    
public string Title { ... }
    
public MailServerElement Mail { ... }

    
public class MailServerElement : ConfigurationElement
    {
        
public string Server { ... }
        
public string Port { ... } 
        
public string Template { ... }
    }
}

자식 노드인 mail을 위해서 하위 클래스를 구성해야 하는데요. 이를 위해서는 ConfigurationElement를 상속하는 클래스를 작성하면 됩니다. 그리고, mail의 각 xml 어트리뷰트에 대해서는 클래스의 속성으로 작성하시면 되는 것이죠. 규칙은 간단합니다. "자식 노드를 위해서는 ConfigurationElement를 상속하는 하위 클래스를 작성하면 된다!" 이게 전부입니다. ^^ (클래스의 명칭은 그다지 중요하지 않습니다. 이는 내부적인 형식일 뿐이니까요)

자. 그럼 실제로 한번 예제를 완성해 보도록 하겠습니다. 새로운 웹 사이트를 하나 만드시고(혹은, 기존의 여러분의 웹 사이트에다가) 새로운 클래스를 하나 추가하도록 하겠습니다. 클래스는 당연히 App_Code 폴더에 놓여져야 하겠죠? 저는 다음과 같이 SiteConfig 라는 이름의 C# 클래스를 추가했습니다.

그리고, 우리는 다음과 같은 구성 형식을 가질 예정이므로 이에 맞도록 handler(처리기) 클래스 코드를 다음과 같이 작성하도록 합니다.

구성 섹션

<mySite title="Taeyo.NET">
    
<mail server="192.168.0.2" template="/Users/MailTemplate.htm"
      port
="25" />
</
mySite>

클래스 코드

public class SiteConfig : ConfigurationSection
{
    [ConfigurationProperty(
"title", DefaultValue="Taeyo.NET")]
    
public string Title
    {
        
get return (string)base["title"]}
        
set base["title"= value; }
    }

    [ConfigurationProperty(
"mail", IsRequired=true)]
    
public MailServerElement Mail
    {
        
get return (MailServerElement)base["mail"]}
    }

    
public class MailServerElement : ConfigurationElement
    {
        [ConfigurationProperty(
"server", DefaultValue "")]
        
public string Server
        {
            
get return (string)base["server"]}
            
set base["server"= value; }
        }

        [ConfigurationProperty(
"port", DefaultValue "")]
        
public string Port
        {
            
get return (string)base["port"]}
            
set base["port"= value; }
        }

        [ConfigurationProperty(
"template", DefaultValue "")]
        
public string Template
        {
            
get return (string)base["template"]}
            
set base["template"= value; }
        }
    }
}

클래스가 완성되었으면, 해당 클래스를 web.config의 <configSections> 섹션에 등록 합니다. 이 처리기 클래스를 <configSections>에 등록해야만 올바르게 우리가 정의한 Xml 구조를 이해할 수 있기 때문입니다.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  
<configSections>
    
<section name="mySite" type="SiteConfig"/>
  </
configSections>

  
<mySite title="Taeyo.NET">
    
<mail server="192.168.0.2" template="/Users/MailTemplate.htm"
          port
="25" />
  </
mySite>
...

더불어, 우리만의 구성 정보(XML)도 위와 같이 web.config 파일에 기록합니다.

이것으로 완성이 되었습니다. ^^

여러분은 이제 다음과 같은 코드를 이용해서 원할 경우, 어디에서나 우리만의 구성 정보를 쉽게 읽어올 수 있을 것입니다.

SiteConfig Settings =
        
(SiteConfig)WebConfigurationManager.GetSection("mySite");

그러나, 좀 더 효율적으로 이를 사용하기 위해서는 사이트 전역 속성으로 이를 제공하는 방법도 생각해 볼 수 있습니다.

예를 들면, 많은 분들이 웹 사이트에 Globals 라는 정적 클래스를 만들고, 그 클래스를 통해서 사이트 전역적인 공통 정보들을 제공하고 있을텐데요(아직 그렇게 구성하지 않으신 분들은 이번 기회에 한번 해 보세요 ^^). 그 클래스에 이 설정 정보도 속성으로서 노출하면 좀 더 편하게 웹 페이지들에서 구성 정보에 접근이 가능할 것입니다. 말이 좀 복잡하게 느껴진다면, 한번 같이 해볼까요?

다음과 같이 App_Code 폴더에 Globals 라는 클래스를 하나 추가해 보도록 하세요. 그리고, 다음과 같이 코드를 작성하도록 합니다.

using System;
using 
System.Configuration;
using 
System.Web.Configuration;

/// <summary>
/// GlobalsAC Globals의 요약 설명입니다.
/// </summary>
public static class Globals
{
    
public readonly static SiteConfig Settings =
        
(SiteConfig)WebConfigurationManager.GetSection("mySite");
}

아주 간단하죠? 이것이 전부입니다. 다만, WebConfigurationManager 클래스를 이용하기 위해서는 System.Web.Configuration 네임스페이스를 추가해 줘야 합니다. 그것을 잊지 말도록 하세요 ^^

자. 이제 얼마나 간편하게 우리만의 사이트 구성 정보를 이용할 수 있는지 한번 테스트를 해보겠습니다. Default.aspx.cs 파일에 다음과 같은 코드를 작성해 보세요 ^^

using System.Configuration;
using 
System.Web;
using 
System.Web.Security;
using 
System.Web.UI;
using 
System.Web.UI.WebControls;
using 
System.Web.UI.WebControls.WebParts;
using 
System.Web.UI.HtmlControls;
using 
System.Web.Configuration;

public 
partial class _Default : System.Web.UI.Page 
{
    
protected void Page_Load(object sender, EventArgs e)
    {
        Response.Write(
"<br>title : " + Globals.Settings.Title);
        
Response.Write("<br>mail(server) : " + Globals.Settings.Mail.Server);
        
Response.Write("<br>mail(port) : " + Globals.Settings.Mail.Port);
        
Response.Write("<br>mail(template) : " + Globals.Settings.Mail.Template);
    
}
}

Globals.Settings와 같이 우리의 구성 정보 클래스에 접근이 가능한 것을 볼 수 있고, Settings 의 속성을 통해서 각각의 XML 어트리뷰터 정보(메일서버, 포트, 템플릿명 등)를 쉽게 얻어올 수 있는 것을 보실 수 있을 것입니다.

다음은 결과 화면입니다.

ASP.NET 에서 우리가 사용한 서버 컨트롤 영역을 클릭 후 ENTER KEY를 누르면

해당 페이지가 다시 로드되는 현상을 많이 봤을 것이다.

 

이 부분은 개발하는 나도 사용하는 사용자도 상당히 신경쓰이고 짜증 나는 일이 아닐 수 없다.

ENTER키를 안먹게 하는 방법이 없을까?

 

그 해결 방법으로 아래와 같은 Javascript 함수로 ENTER 키를 모두 막아 버렸다.

 

function CheckEnter() {

try {

if (!e) var e = window.event;

if (document.all) var key = e.keyCode;

else var key = e.which;

 

if( key == 13 ) {

var tag = e.srcElement ? e.srcElement.tagName : e.target.nodeName ;

var tagId = e.srcElement ? e.srcElement.id : "" ;

 

/*** 1. 원하는 컨트롤 아이디만 등록 ***/

if( tagId == "InputLogin" ) {

CheckLogin();

return false;

}

/*** 2. 원하는 컨트롤만 엔터키 반응 (여기서는 TEXTAREA만) ***/

if( tag == "TEXTAREA") {

return true;

}

return false;

}

}

catch(e) { return true; }

}

document.onkeydown = CheckEnter;

 

여기서 중요한 것은 document.onkeydown = CheckEnter;

함수 내부에 있어서는 안되며 함수 밖에 등록되어 전체 HTML 문서에서의 KEYDOWN 이벤트를 체크 해야 한다.

 

주석 부분의

1. 원하는 컨트롤 아이디만 등록 

웹사이트에서 로그인 부분에서만 ENTER KEY를 사용할 수 있게 하여 로그인체크 함수를 실행 할 수 있게 했다.

 

2. 원하는 컨트롤만 엔터키 반응

이건 사이트 전체의 TEXTAREA에서만 ENTER KEY를 사용할 수 있게 했다.

 

만약 검색 INPUT 에서 ENTER KEY를 사용하려면 검색 INPUT ID를 위의 함수에 추가로 등록하여 사용할 수 있다.

예)

검색 INPUT 박스 <input type="text" id="InputSearch" name="InputSearch" size="20" /> 라면

 

if( tagId == "InputSearch" ) {

CheckSearch();

return false;

}

 

다르게 체크 할 수 있는 방법이 더 있겠지만 일단 -0-;;

Html에 특수문자를 표기하고 싶을 경우 정말이지 가끔 까먹어서 찾기 귀찮을 때가 상당히 많다;;

 

특수문자 표기는 아래의 표에서 왼쪽 회색 문자 앞뒤로 & ;를 붙이면 우측 문자가 된다.

 

quot

"

sect

§

amp

&

uml

¨

apos

'

copy

©

lt

< 

ordf

ª

nbsp

 

laquo

«

iexcl

¡

not

¬

cent

¢

shy

­

pound

£

reg

®

yen

¥

macr

¯

brvbar

¦

deg

°

plusmn

±

raquo

»

sup2

²

frac14

¼

sup3

³

frac12

½

acute

´

frac34

¾

micro

µ

iquest

¿

para

Agrave

À

middot

·

Aacute

Á

cedil

¸

Acirc

Â

sup1

¹

Atilde

Ã

ordm

º

Auml

Ä

Aring

Å

Iuml

Ï

AElig

Æ

ETH

Ð

Ccedil

Ç

Ntilde

Ñ

Egrave

È

Ograve

Ò

Eacute

É

Oacute

Ó

Ecirc

Ê

Ocirc

Ô

Euml

Ë

Otilde

Õ

Igrave

Ì

Ouml

Ö

Iacute

Í

times

×

Icirc

Î

Oslash

Ø

Ugrave

Ù

atilde

ã

Uacute

Ú

auml

ä

Ucirc

Û

aring

å

Uuml

Ü

aelig

æ

Yacute

Ý

ccedil

ç

THORN

Þ

egrave

è

szlig

ß

eacute

é

agrave

à

ecirc

ê

aacute

á

euml

ë

acirc

â

igrave

ì

iacute

í

divide

÷

icirc

î

oslash

ø

iuml

ï

ugrave

ù

eth

ð

uacute

ú

ntilde

ñ

ucirc

û

ograve

ò

uuml

ü

oacute

ó

yacute

ý

ocirc

ô

thorn

þ

otilde

õ

yuml

ÿ

ouml

ö

>

> 

 


Web Service를 이용한 파일 업로드 ( Web Site )

 

먼저 D:\SaveImage 아까 웹 서비스를 만들 때 저장 폴더를 설정했는데 그 경로로 upload 라는 가상 디렉토리를 설정한다. 이 설정은 http://local.image.co.kr 에 설정해야 한다.

 

이제 웹 사이트를 만들어보자

 

 

새 프로젝트에서 ASP.NET 웹 응용프로그램을 선택 후 UploadTestWeb이름을 생성하자.

 

FileUpload.aspx 생성하기

이 파일은 업로드를 테스트 하는 .aspx 파일이다.

 

FileUpload.aspx

<br /><br /><br />

   

   

    새이름으로 : <asp:FileUpload ID="files" runat="server" /> <asp:Button ID="Upload" runat="server" Text="업로드" OnClick="Upload_OnClick" />

    <br /><br />

   

    업로드이름으로 : <asp:FileUpload ID="files2" runat="server" /> <asp:Button ID="Upload2" runat="server" Text="업로드" OnClick="Upload2_OnClick" />

   

    <br /><br /><br />

 

 

    <img src="http://local.images.co.kr/upload/hi2/01.png" border=1 /> 01.png - <asp:Button ID="btnDel" runat="server" Text="삭제" OnClick="btnDel_OnClick" />

       

    <br /><br /><br />

   

    <img src="http://local.images.co.kr/upload/hi2/02.png" border=1 /> 02.png - <asp:Button ID="btnDel2" runat="server" Text="삭제" OnClick="btnDel2_OnClick" />

 

    <br /><br /><br />

   

    <img src="http://local.images.co.kr/upload/hi2/03.png" border=1 /> 03.png - <asp:Button ID="btnDel3" runat="server" Text="삭제" OnClick="btnDel3_OnClick" />

 

   

    <br /><br /><br />

    <br /><br /><br />

    <br /><br /><br />

 

    다중 업로드(새이름)<br />

    <asp:FileUpload ID="file1" runat="server" /><br />

    <asp:FileUpload ID="file2" runat="server" /><br />

    <asp:FileUpload ID="file3" runat="server" /> <asp:Button ID="Upload3" runat="server" Text="업로드" OnClick="Upload3_OnClick" />

 

 

    <br /><br /><br />

 

    다중 업로드(기존이름)<br />

    <asp:FileUpload ID="file4" runat="server" /><br />

    <asp:FileUpload ID="file5" runat="server" /><br />

    <asp:FileUpload ID="file6" runat="server" /> <asp:Button ID="Upload4" runat="server" Text="업로드" OnClick="Upload4_OnClick" />

 

 

    <br /><br /><br />

소스 중간의 이미지 출력 부분은 이미지가 잘 업로드가 되었는지를 보기 위해서 이다.

 

새 이름으로 업로드 하기

#region 다중 새이름으로 업로드

/// <summary>

/// 다중 새이름으로 업로드

/// </summary>

/// <param name="sender"></param>

/// <param name="e"></param>

protected void Upload3_OnClick(object sender, EventArgs e)

{

    List<System.Web.UI.WebControls.FileUpload> listFile = new List<System.Web.UI.WebControls.FileUpload>();

    listFile.Add(file1);

    listFile.Add(file2);

    listFile.Add(file3);

 

    SiteUpload sUpload = new SiteUpload();

 

    string statusMessage = sUpload.SatausMessage(sUpload.MultiNewFileUpload(listFile, "HI2")[2][0]);

 

    Response.Write(statusMessage);

 

}

#endregion

위에서 설정한 아이디와 키 값이 맞지 않으면 에러가 발생한다. 따로 try catch문을 설정하지 않아 에러로 보이니 설정 값을 서로 잘 맞춰야 한다.

 

기존 이름으로 덮어 씌우기

#region 기존 이름 덮어씌우기

/// <summary>

/// 기존 이름 덮어씌우기

/// </summary>

/// <param name="sender"></param>

/// <param name="e"></param>

protected void Upload2_OnClick(object sender, EventArgs e)

{

    SiteUpload sUpload = new SiteUpload();

 

    Response.Write("Len : " + files2.PostedFile.ContentLength + "<br />");

 

    /*

    상태 변수

    READY : 준비

    NOEXT : 지원되지 않는 확장자

    NOFNM : 잘못된 파일명

    FAILD : 서버에서 실패

    SUCES : 성공

    NOSEL : 파일선택안함

     */

    string statusMessage = sUpload.SatausMessage(sUpload.SameFileUpload(files2, "HI2")[0]);

 

 

    Response.Write(statusMessage);

}

#endregion

 

다중 새 이름으로 업로드

#region 다중 새이름으로 업로드

/// <summary>

/// 다중 새이름으로 업로드

/// </summary>

/// <param name="sender"></param>

/// <param name="e"></param>

protected void Upload3_OnClick(object sender, EventArgs e)

{

    List<System.Web.UI.WebControls.FileUpload> listFile = new List<System.Web.UI.WebControls.FileUpload>();

    listFile.Add(file1);

    listFile.Add(file2);

    listFile.Add(file3);

 

    SiteUpload sUpload = new SiteUpload();

 

    string statusMessage = sUpload.SatausMessage(sUpload.MultiNewFileUpload(listFile, "HI2")[2][0]);

 

    Response.Write(statusMessage);

 

}

#endregion

 

다중 기존 이름으로 덮어씌우기

#region 다중 기존이름으로 덮어씌우기

/// <summary>

/// 다중 기존이름으로 덮어씌우기

/// </summary>

/// <param name="sender"></param>

/// <param name="e"></param>

protected void Upload4_OnClick(object sender, EventArgs e)

{

    List<System.Web.UI.WebControls.FileUpload> listFile = new List<System.Web.UI.WebControls.FileUpload>();

    listFile.Add(file4);

    listFile.Add(file5);

    listFile.Add(file6);

 

    SiteUpload sUpload = new SiteUpload();

 

    string statusMessage = sUpload.SatausMessage(sUpload.MultiSameFileUpload(listFile, "HI2")[2][0]);

 

    Response.Write(statusMessage);

}

#endregion

 

파일 삭제

#region 파일 삭제

/// <summary>

/// 파일 삭제

/// </summary>

/// <param name="sender"></param>

/// <param name="e"></param>

protected void btnDel_OnClick(object sender, EventArgs e)

{

    /*

    상태 변수

    FAILD : 서버에서 실패

    SUCES : 성공

     */

    SiteUpload sUpload = new SiteUpload();

 

    string statusMessage = sUpload.SatausMessage(sUpload.DeleteFile("HI2", "01.png"));

 

    Response.Write(statusMessage);

 

}

 

 

/// <summary>

/// 파일 삭제

/// </summary>

/// <param name="sender"></param>

/// <param name="e"></param>

protected void btnDel2_OnClick(object sender, EventArgs e)

{

    /*

    상태 변수

    FAILD : 서버에서 실패

    SUCES : 성공

     */

    SiteUpload sUpload = new SiteUpload();

 

    string statusMessage = sUpload.SatausMessage(sUpload.DeleteFile("HI2", "02.png"));

 

    Response.Write(statusMessage);

 

}

 

/// <summary>

/// 파일 삭제

/// </summary>

/// <param name="sender"></param>

/// <param name="e"></param>

protected void btnDel3_OnClick(object sender, EventArgs e)

{

    /*

    상태 변수

    FAILD : 서버에서 실패

    SUCES : 성공

     */

    SiteUpload sUpload = new SiteUpload();

 

    string statusMessage = sUpload.SatausMessage(sUpload.DeleteFile("HI2", "03.png"));

 

    Response.Write(statusMessage);

 

}

#endregion

 

이렇게 해서 웹 서비스를 통해 업로드 하는 과정을 모두 완료했다.

Web Service를 이용한 파일 업로드 ( DLL )

 

DLL 만들기

웹사이트에서만 사용하지 않고 Console App, Win App, ASP.NET에서 모두 이용하기 위해

클래스 라이브러리 프로젝트로 DLL을 만들 것이다.

 

 

새 프로젝트에 클래스 라이브러리를 선택하자

 

이제 프로젝트에서 웹 서비스의 자원을 사용해야 하는데 어떻게 사용해야 할까?

웹 서비스의 자원을 사용하는 것을 다음과 같다.

 

 

DLL 이나 EXE 프로젝트에서 웹서비스 사용하기

 

우리가 DLL이나 Exe에서 사용할 시 WDSL 규약에 맞게 코딩을 해주어야 한다.

이 부분은 Visual Studio에서 제공을 해준다.

 

Visual Studio 명령 프롬프트에서

wsdl /l:CS <webservice URL>을 하면 명령 프롬프트 폴더데 .cs파일 자동적으로 생성이 된다.

이것을 프로젝트에 추가하면 된다.

 

참조에는 System.Web.Service, System.Web를 추가

using System.IO;를 추가

 

wsdl 명령어에 대해 자세히 알고 싶다면 wsdl /? 하면 자세한 도움말이 나온다.

 

아까 Web Service를 만든 경로를 입력하자.

wsdl /l:CS http://local.images.co.kr/SvcFileUpload.asmx

  

 

생성된 SvcFileUpload.cs 파일을 프로젝트에 추가하면 이제부터 웹 서비스의 자원을 사용할 수 있다.

 

이제 변수와 메소드를 만들어보자

변수는 다음과 같다.

#region 변수

/// <summary>

/// 아이디 변수

/// </summary>

private string id = string.Empty;

/// <summary>

/// 아이디 세팅

/// </summary>

public string Id

{

    get { return id; }

    set { id = value; }

}

 

/// <summary>

/// 키 변수

/// </summary>

private string kno = string.Empty;

/// <summary>

/// 키 세팅

/// </summary>

public string Kno

{

    get { return kno; }

    set { kno = value; }

}

 

/// <summary>

/// 최대 업로드 용량

/// </summary>

private int maxLength = 102400 * 5; // 5MB

/// <summary>

/// 최대 업로드 용량

/// </summary>

public int MaxLength

{

    get { return maxLength; }

    set { maxLength = value; }

}

 

/// <summary>

/// 지원 확장자

/// </summary>

//private string[] ext = {"GIF", "PNG", "JPG", "JEPG", "BMP" };

public List<string> listExt = new List<string>();

 

/// <summary>

/// 상태 변수

/// READY : 준비

/// NOEXT : 지원되지 않는 확장자

/// NOFNM : 잘못된 파일명

/// FAILD : 서버에서 실패

/// SUCES : 성공

/// NOSEL : 파일선택안함

/// </summary>

private string RTN_STATUS = string.Empty;

 

#endregion

 

생성자에서는 역시나 초기화를 했다.

#region 생성자

/// <summary>

/// 생성자

/// </summary>

public SiteUpload()

{

    // 기본 세팅

    Id = " website1";

    Kno = " website1111";

 

    // 지원되는 파일 확장자

    listExt.Add("GIF");

    listExt.Add("PNG");

    listExt.Add("JPG");

    listExt.Add("JEPG");

    listExt.Add("BMP");

   

    // 초기 상태

    RTN_STATUS = "READY";

}

#endregion

 

DLL에서 사용할 메소드는 다음과 같다.

1.     디렉토리 생성

2.     새 파일로 저장

3.     기존 파일로 저장

4.     다중 새 파일로 저장

5.     다중 기존 파일로 저장

6.     파일 삭제

7.     상태 메시지

 

디렉토리 생성

#region 디렉토리 생성

/// <summary>

/// 디렉토리 생성

/// </summary>

/// <param name="dir">생성할 디렉토리</param>

/// <param name="id">사이트아이디</param>

/// <param name="kno">사이트키</param>

public void CreateDirectory(string dir, string id, string kno)

{

    // 웹서비스 자원 사용하기

    SvcFileUpload sfile = new SvcFileUpload();

    sfile.CreateDirectory(dir, id, kno);

}

#endregion

 

새 파일로 저장

#region 새 파일로 저장

/// <summary>

/// 새 파일 저장 - 같은 파일일 경우 새 이름으로 저장한다.

/// [0] 상태값

/// [1] 파일명

/// </summary>

/// <param name="files">FileUpload컨트롤</param>

/// <param name="dir">저장경로</param>

/// <returns></returns>

public List<string> NewFileUpload(System.Web.UI.WebControls.FileUpload files, string dir)

{

    string filename = string.Empty;

    string ext = string.Empty;

 

    int fileLength = files.PostedFile.ContentLength;

    int fnStart = -1;

    int fnLen = 0;

   

    // 리턴변수

    List<string> Rtn = new List<string>();

    Rtn.Add(string.Empty); //[0] : 상태값

    Rtn.Add(string.Empty); //[1] : 파일명

 

   

    filename = files.PostedFile.FileName;

    fnLen = filename.Length;

    fnStart = filename.LastIndexOf('\\') + 1;

   

    // 파일 유무 체크

    if (fnLen > 0)

    {

        if (fnStart > 0 && (fnLen - fnStart) > 0)

        {

            Stream strm;

 

            // 파일명만 추출하기

            int extStart = -1;

            int extLen = 0;

            bool chkExt = false;

 

            filename = filename.Substring(fnStart, fnLen - fnStart); // 파일명

 

            // 파일명에서 확장자 추출하기

            extLen = filename.Length;

            extStart = filename.LastIndexOf('.') + 1;

            if (extStart > 0 && (extLen - extStart) > 0)

                ext = filename.Substring(extStart, extLen - extStart); // 확장자

 

            // 확장자 체크

            foreach (string s in listExt)

                if (ext.ToUpper() == s)

                    chkExt = true;

 

            if (chkExt)

            {

                byte[] send = new byte[fileLength];

 

                strm = files.PostedFile.InputStream;

                strm.Read(send, 0, fileLength);

 

                // 웹서비스 자원 사용하기

                SvcFileUpload sfile = new SvcFileUpload();

                // 파일명만 넘겨주세요.

                filename = sfile.NewFileSaveFile(send, dir, filename, Id, Kno);

 

                strm.Close();

 

                if (!filename.Equals(string.Empty))

                    RTN_STATUS = "SUCES";

                else

                    RTN_STATUS = "FAILD";

            }

            else

                RTN_STATUS = "NOEXT";

        }

        else

            RTN_STATUS = "NOFNM";

    }

    else

        RTN_STATUS = "NOSEL";

 

    Rtn[0] = RTN_STATUS;

    Rtn[1] = filename;

 

    return Rtn;

}

#endregion

엄청난 if ;; 급하게 만드느라 생각을 많이 못하고 붙이다 보니 이렇게 많은 if문이 생겼네요.

 

기존파일명으로 파일 저장

#region 기존파일명으로 파일 저장

/// <summary>

/// 기존파일명으로 파일 저장 : 같은 이름일 경우 덮어씌운다.

/// [0] 상태값

/// [1] 파일명

/// </summary>

/// <param name="files">FileUpload컨트롤</param>

/// <param name="dir">저장경로</param>

/// <returns></returns>

public List<string> SameFileUpload(System.Web.UI.WebControls.FileUpload files, string dir)

{

    string filename = string.Empty;

    string ext = string.Empty;

 

    int fileLength = files.PostedFile.ContentLength;

    int fnStart = -1;

    int fnLen = 0;

 

    // 리턴변수

    List<string> Rtn = new List<string>();

    Rtn.Add(string.Empty); //[0] : 상태값

    Rtn.Add(string.Empty); //[1] : 파일명

 

    filename = files.PostedFile.FileName;

    fnLen = filename.Length;

    fnStart = filename.LastIndexOf('\\') + 1;

 

    // 파일 유무 체크

    if (fnLen > 0)

    {

        if (fnStart > 0 && (fnLen - fnStart) > 0)

        {

           

 

            // 파일명만 추출하기

            int extStart = -1;

            int extLen = 0;

            bool chkExt = false;

 

            filename = filename.Substring(fnStart, fnLen - fnStart); // 파일명

 

            // 파일명에서 확장자 추출하기

            extLen = filename.Length;

            extStart = filename.LastIndexOf('.') + 1;

            if (extStart > 0 && (extLen - extStart) > 0)

                ext = filename.Substring(extStart, extLen - extStart); // 확장자

 

            // 확장자 체크

            foreach (string s in listExt)

                if (ext.ToUpper() == s)

                    chkExt = true;

 

            if (chkExt)

            {

                Stream strm;

                byte[] send = new byte[fileLength];

 

                strm = files.PostedFile.InputStream;

                strm.Read(send, 0, fileLength);

 

                // 웹서비스 자원 사용하기

                SvcFileUpload sfile = new SvcFileUpload();

                // 파일명만 넘겨주세요.

                filename = sfile.SameFileSaveFile(send, dir, filename, Id, Kno);

 

                strm.Close();

 

                if (!filename.Equals(string.Empty))

                    RTN_STATUS = "SUCES";

                else

                    RTN_STATUS = "FAILD";

            }

            else

                RTN_STATUS = "NOEXT";

        }

        else

            RTN_STATUS = "NOFNM";

    }

    else

        RTN_STATUS = "NOSEL";

 

    Rtn[0] = RTN_STATUS;

    Rtn[1] = filename;

 

    return Rtn;

}

#endregion

마찬가리로 많은 if문을 볼 수 있습니다. ^^;;

 

다중 새 파일로 저장

#region 다중 새 파일로 저장

/// <summary>

/// 다중 새 파일 저장 - 같은 파일일 경우 새 이름으로 저장한다.

/// [2][0] 전체 상태값

/// </summary>

/// <param name="files"></param>

/// <param name="dir"></param>

/// <returns></returns>

public List<List<string>> MultiNewFileUpload(List<System.Web.UI.WebControls.FileUpload> files, string dir)

{

    string CHK_STATUS = "FAILD";

 

    // 리턴변수

    List<List<string>> Rtn = new List<List<string>>();

 

    List<string> RtnFile = new List<string>();

    List<string> RtnStatus = new List<string>();

    List<string> RtnFullStatus = new List<string>();

    RtnFullStatus.Add(string.Empty);

 

    List<string> svcRtn = new List<string>();

 

    foreach (System.Web.UI.WebControls.FileUpload f in files)

    {

        svcRtn = NewFileUpload(f, dir);

 

        RtnStatus.Add(svcRtn[0]); // 상태값

        RtnFile.Add(svcRtn[1]); // 파일명

    }

 

    foreach (string s in RtnStatus)

    {

        if (s == "SUCES" || s == "NOSEL")

            CHK_STATUS = "SUCES";

    }

    RtnFullStatus[0] = CHK_STATUS;

 

    Rtn.Add(RtnStatus); // 각 업로드 상태

    Rtn.Add(RtnFile); // 각 업로드 파일명

    Rtn.Add(RtnFullStatus); // 전체 상태

 

    return Rtn;

}

#endregion

하나씩 파일 업로드만 있길래 메소드는 나중에 추가하여 다중으로도 업로드를 지원했습니다.

 

다중 기존 파일로 저장

#region 다중 기존 파일로 저장

/// <summary>

/// 다중 기존 파일로 저장 - 같은 이름일 경우 덮어씌운다.

/// [2][0] 전체 상태값

/// </summary>

/// <param name="files"></param>

/// <param name="dir"></param>

/// <returns></returns>

public List<List<string>> MultiSameFileUpload(List<System.Web.UI.WebControls.FileUpload> files, string dir)

{

    string CHK_STATUS = "FAILD";

 

    // 리턴변수

    List<List<string>> Rtn = new List<List<string>>();

 

    List<string> RtnFile = new List<string>();

    List<string> RtnStatus = new List<string>();

    List<string> RtnFullStatus = new List<string>();

    RtnFullStatus.Add(string.Empty);

 

    List<string> svcRtn = new List<string>();

 

    foreach (System.Web.UI.WebControls.FileUpload f in files)

    {

        svcRtn = SameFileUpload(f, dir);

 

        RtnStatus.Add(svcRtn[0]); // 상태값

        RtnFile.Add(svcRtn[1]); // 파일명

    }

 

    foreach (string s in RtnStatus)

    {

        if (s == "SUCES" || s == "NOSEL")

            CHK_STATUS = "SUCES";

    }

    RtnFullStatus[0] = CHK_STATUS;

 

 

    Rtn.Add(RtnStatus); // 각 업로드 상태

    Rtn.Add(RtnFile); // 각 업로드 파일명

    Rtn.Add(RtnFullStatus); // 전체 상태

 

    return Rtn;

}

#endregion

마찬가지의 이유로 추가했습니다.

 

파일 삭제

#region 파일 삭제

/// <summary>

/// 파일 삭제

/// </summary>

/// <param name="dir">디렉토리</param>

/// <param name="filename">파일명</param>

public string DeleteFile(string dir, string filename)

{

    bool delCheck = false;

    string RTN_STATUS = string.Empty;

 

    // 웹서비스 자원 사용하기

    SvcFileUpload sfile = new SvcFileUpload();

    delCheck = sfile.DeleteFile(dir, filename, Id, Kno);

 

    if (delCheck)

        RTN_STATUS = "SUCES";

    else

        RTN_STATUS = "FAILD";

 

    return RTN_STATUS;

}

#endregion

 

상태 메시지

#region 상태 메세지

/// <summary>

/// 상태 메세지

/// </summary>

/// <param name="status">상태코드 5자리</param>

/// <returns></returns>

public string SatausMessage(string status)

{

    /*

    상태 변수

   

    READY : 준비

    NOEXT : 지원되지 않는 확장자

    NOFNM : 잘못된 파일명

    FAILD : 서버에서 실패

   

    SUCES : 성공

    NOSEL : 파일선택안함

     */

    string rtnMessage = string.Empty;

 

    switch (status)

    {

        case "READY":

            rtnMessage = "업로드 대기중입니다.";

            break;

        case "NOEXT":

            rtnMessage = "지원되지 않는 확장자입니다.";

            break;

        case "NOFNM":

            rtnMessage = "파일명이 잘못되었습니다.";

            break;

        case "FAILD":

            rtnMessage = "업로드가 실패했습니다.";

            break;

        case "SUCES":

            rtnMessage = "성공적으로 업로드 되었습니다.";

            break;

        case "NOSEL":

            rtnMessage = "";

            break;

    }

    return rtnMessage;

}

#endregion

업로드의 각 상태 메시지를 리턴해 주는 메소드 입니다. 급하게 만들었다고 생각해주세요;;

 

자 이렇게 해서 DLL에서 필요한 변수 및 메소드를 모두 만들었다.

빌드를 하여 dll을 만들자.

 

 

다음 장에서는 마지막으로 웹 사이트를 통해 파일 업로드 테스트 하는 페이지에 대해 알아 보겠다.

 

Web Service를 이용한 파일 업로드 ( Web Service )

 

준비하기

1.     웹사이트 만들기 ( : http://local.images.co.kr )

2.     호스트 파일에 등록 : 127.0.0.1      local.images.co.kr

3.     Visual Studio -> 파일 -> 새로 만들기 -> 새 웹사이트

4.     HTTP 선택 후 http://local.images.co.kr 을 입력 후 언어는 Visual C# 선택 후 확인

5.     웹 사이트로 프로젝트 생성 완료

 

이제 웹서비스를 생성할 준비가 완료 되었다.

 

웹 서비스 만들기

1.     프로젝트에서 새 항목 추가를 클릭

2.     템플릿에서 웹 서비스 선택 후 이름은 SvcFileUpload.asmx로 생성

 

이제 웹 서비스가 완성되었다.

기본 적으로 HelloWorld() 메소드가 추가되고

이 메소드는 http://local.images.co.kr/SvcFileUpload.asmx 에서 확인 할 수 있다.

 

 

파일 업로드를 위한 웹 서비스 계획

현재 만들어진 웹 서비스가 있는 사이트를 업로드용 파일 서버로 사용할 경우로 가정한다.

1.     간단하게 아이디와 키 값을 부여

2.     아이디와 키 값이 일치 할 경우 디렉토리 생성 및 파일 업로드

3.     아이디와 키 값이 일치 할 경우 디렉토리 삭제 및 파일 삭제

 

웹 서비스에 필요한 변수

1.     아이디와 키 값 저장 변수

2.     아이디와 키 값 비교 변수

3.     저장 할 경로의 디렉토리 변수

 

웹 서비스에 필요한 메소드

1.     아이디와 키 값 비교 메소드

2.     디렉토리 생성 메소드

3.     파일 여부 체크 메소드

4.     파일 저장 메소드 새 이름으로 저장

5.     파일 저장 메소드 기존 파일 덮어 씌우기

6.     파일 삭제 메소드

 

구조는 웹사이트에서 DLL을 참조해서 웹서비스의 메소드를 호출하여 처리하는 것이다.

 

그럼 이제부터 SvcFileUpload.asmx 을 만들어 보겠다.

 

/// <summary>

/// SvcFileUpload의 요약 설명입니다.

/// </summary>

[WebService(Namespace = "http://local.images.co.kr", Name = "SvcFileUpload", Description = "파일 업로드를 처리")]

[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]

public class SvcFileUpload : System.Web.Services.WebService

{

XML Web Service를 위한 기본 네임스페이스를 설정하고 XML Web Service의 명을 정하는 것이다.

 

여러 사이트에서 하나의 업로드 서버를 사용할 수 있으므로 사이트 마다 아이디와 키를 부여하고업로드 서버 저장 경로 변수 설정

/// <summary>

/// 사이트키

/// </summary>

private Dictionary<string, string> site = new Dictionary<string, string>();

/// <summary>

/// 저장할 경로 변수

/// </summary>

private string saveDir = string.Empty;

 

/// <summary>

/// 아이디, 키값 비교 변수

/// </summary>

private bool equValue = false;

 

생성자에서 위의 변수 초기화

/// <summary>

/// 생성자

/// </summary>

public SvcFileUpload () {

 

    //디자인된 구성 요소를 사용하는 경우 다음 줄의 주석 처리를 제거합니다.

    //InitializeComponent();

 

    // 저장할 기본 경로 ( 루트 )

    saveDir = "D:\\SaveImage";

    // Key : 사이트 아이디, Value : 사이트 키 번호

    site.Add("website1", "website1111");

}

 

SvcFileUpload 웹서비스는 다음과 같이 8가지의 메소드로 구성이 된다.

1.     아이디와 키 번호 체크

2.     저장 경로 반환

3.     디렉토리 생성

4.     파일 여부 체크

5.     새로운 파일명을 생성 ( 파일명 중복 방지 )

6.     파일명이 같을 경우 같은 이름으로 저장

7.     파일명이 같을 경우 새 파일명으로 저장

8.     파일 삭제

 

아이디와 키 번호 체크 메소드

/// <summary>

/// 아이디와 키번호 체크

/// </summary>

/// <param name="id"></param>

/// <param name="kno"></param>

/// <returns></returns>

[WebMethod(Description = "아이디와 키번호 체크")]

private bool EqualsIdKno(string id, string kno)

{

    string sno = site[id];

 

    if (sno == kno)

        equValue = true;

    else

        equValue = false;

 

    return equValue;

}

 

저장 경로 반환

/// <summary>dkdle

/// 저장 경로 반환

/// </summary>

/// <returns></returns>

[WebMethod(Description = "저장 경로를 반환")]

public string ReturnDirectory()

{

    return saveDir;

}

 

디렉토리 생성

/// <summary>

/// 디렉토리를 생성

/// </summary>

/// <param name="dir"></param>

[WebMethod(Description = "디렉토리를 생성")]

public bool CreateDirectory(string dir, string id, string kno)

{

    bool createDir = false;

 

    if (EqualsIdKno(id, kno))

    {

        string strDir = string.Empty;

 

        strDir = ReturnDirectory() + "\\" + dir;

 

        //디렉토리 생성

        if (!Directory.Exists(strDir))

        {

            Directory.CreateDirectory(strDir);

        }

        createDir = true;

    }

    else

    {

        createDir = false;

    }

 

    return createDir;

}

 

파일 여부 체크

/// <summary>

/// 파일 여부 체크

/// </summary>

/// <param name="fname">저장폴더와 함께 파일명</param>

/// <returns></returns>

[WebMethod(Description = "파일 여부 체크")]

public bool EqualsFile(string fname)

{

    string strFile = ReturnDirectory() + "\\" + fname;

   

    return File.Exists(strFile);

}

 

새로운 파일명 생성

/// <summary>

/// 새로운 파일명 생성

/// </summary>

/// <param name="folderPath"></param>

/// <param name="filename"></param>

/// <returns></returns>

private string GetAvailablePathname(string folderPath, string filename)

{

    int invalidChar = 0;

    do

    {

        // 파일명에서 사용할 수 없는 문자가 들어 있는 배열을 가져온다.

        invalidChar = filename.IndexOfAny(Path.GetInvalidFileNameChars());

 

        // 사용할 수 없는 문자 제거

        if (invalidChar != -1)

            filename = filename.Remove(invalidChar, 1);

    }

    while (invalidChar != -1);

 

 

    string fullPath = Path.Combine(folderPath, filename);

    string filenameWithoutExtention = Path.GetFileNameWithoutExtension(filename);

    string extension = Path.GetExtension(filename);

 

 

 

    while (File.Exists(fullPath))

    {

        Regex rg = new Regex(@".*\((?<Num>\d*)\)");

        Match mt = rg.Match(fullPath);

 

        if (mt.Success)

        {

            string numberOfCopy = mt.Groups["Num"].Value;

            int nextNumberOfCopy = int.Parse(numberOfCopy) + 1;

            int posStart = fullPath.LastIndexOf("(" + numberOfCopy + ")");

 

            fullPath = string.Format("{0}({1}){2}", fullPath.Substring(0, posStart), nextNumberOfCopy, extension);

        }

        else

        {

            fullPath = folderPath + filenameWithoutExtention + " (2)" + extension;

        }

    }

    return fullPath;

}

웹 사이트 검색을 통해 이 부분을 다른 분이 작성한 코드를 사용했다.

이 코드의 내용은 다음의 주소에 자세히 나와 있다.

http://chaoskcuf.com/entry/C-파일명이-중복일--자동으로-이름을-생성하는-코드

 

파일명이 같을 경우 같은 이름으로 저장

/// <summary>

/// 파일을 저장 - 파일이 있을 경우 같은 이름으로 저장

/// </summary>

/// <param name="httpFile"></param>

/// <param name="dir"></param>

/// <param name="id"></param>

/// <param name="kno"></param>

/// <returns></returns>

[WebMethod(Description = "파일을 저장 - 파일이 있을 경우 같은 이름으로 저장")]

public string SameFileSaveFile(byte[] fileInfo, string dir, string fileName, string id, string kno)

{

    string checkSave = string.Empty;

    string filePath = string.Empty;

 

    // 아이디와 키 값 비교

    if (EqualsIdKno(id, kno))

    {

        // 저장할 경로

        string dirpath = ReturnDirectory() + "\\" + dir + "\\";

        filePath = dirpath + fileName;

           

        if (CreateDirectory(dir, id, kno))

        {

            try

            {

                //파일처리

                using (MemoryStream ms = new MemoryStream(fileInfo))

                {

                    using (FileStream fs = new FileStream(filePath, FileMode.Create))

                    {

                        ms.WriteTo(fs);

 

                        ms.Close();

                        fs.Close();

                    }

                }

                checkSave = fileName; // 파일명

            }

            catch

            {

                checkSave = "";

            }

        }

        else

        {

            checkSave = "";

        }

    }

    return checkSave;

}

 

파일명이 같을 경우 새 파일명으로 저장

/// <summary>

/// 파일을 저장 - 파일이 있을 경우 새이름으로

/// </summary>

/// <param name="httpFile"></param>

/// <param name="dir"></param>

/// <param name="id"></param>

/// <param name="kno"></param>

/// <returns></returns>

[WebMethod(Description = "파일을 저장 - 파일이 있을 경우 새 이름으로")]

public string NewFileSaveFile(byte[] fileInfo, string dir, string fileName, string id, string kno)

{

    string checkSave = string.Empty;

    string filePath = string.Empty;

 

    // 아이디와 키 값 비교

    if (EqualsIdKno(id, kno))

    {

        // 저장할 경로

        string dirpath = ReturnDirectory() + "\\" + dir + "\\";

 

        if (CreateDirectory(dir, id, kno))

        {

            try

            {

                // 중복되지 않는 파일명 생성

                filePath = GetAvailablePathname(dirpath, fileName);

 

                //파일처리

                using (MemoryStream ms = new MemoryStream(fileInfo))

                {

                    using (FileStream fs = new FileStream(filePath, FileMode.Create))

                    {

                        ms.WriteTo(fs);

 

                        ms.Close();

                        fs.Close();

                    }

                }

                int fnLen = filePath.Length;

                int fnStart = filePath.LastIndexOf('\\') + 1;

 

                checkSave = filePath.Substring(fnStart, fnLen - fnStart); // 파일명

            }

            catch

            {

                checkSave = "";

            }

        }

        else

        {

            checkSave = "";

        }

    }

 

    return checkSave;

}

 

파일 삭제

/// <summary>

/// 파일을 삭제한다.

/// </summary>

/// <param name="DirPath">디렉토리 절대경로</param>

/// <param name="FileName">파일명</param>

/// <returns></returns>

[WebMethod(Description = "파일을 삭제한다.")]

public bool DeleteFile(string dir, string filename, string id, string kno)

{

    bool checkDel = false;

   

    // 아이디와 키 값 비교

    if (EqualsIdKno(id, kno))

    {

        try

        {

            string strDir = string.Empty;

 

            strDir = ReturnDirectory() + "\\" + dir + "\\";

 

            //파일삭제

            if (File.Exists(strDir + filename))

            {

                File.Delete(strDir + filename);

            }

            checkDel = true;

        }

        catch

        {

            checkDel = false;

        }

    }

    else

        checkDel = false;

 

    return checkDel;

}

 

위의 소스에서 아이디와 키값이 일치 하지 않으면 디렉토리 생성이나 파일 업로드 그리도 삭제 모두 되지 않는 것을 볼 수 있다.

 

이와 같이 파일 업로드에 필요한 대부분의 메소드를 생성했다.

 

다음 장에서는 DLL 만들어 웹 서비스 자원을 어떻게 사용하는지를 알아보겠다.

웹사이트가 로드되면 Web.config의 appSetting정보가 메모리 상으로 올라가게 되고 그 값을 읽고 올라간 정보를 변경 할 수도 있다.

1. appSetting값 읽기

string value = System.Web.Configuration.WebConfigurationManager.AppSettings.Get("appValue");

또는

string value = System.Web.Configuration.WebConfigurationManager.AppSettings["appValue"];

2. appSetting값 변경

System.Web.Configuration.WebConfigurationManager.AppSettings.Set("appValue", "test");

위와 같이 하면 XML파일의 값은 변경되지 않지만

현재 appValue로 읽어오는 appSetting값은 웹사이트가 다시 시작하지 않는 이상 계속 test라는 값을 읽어온다.

3. 간단하게 appSetting.xml파일 변경하기

 

string key = "appValue";

string value = "test2"; 

DataSet ds = new DataSet();

ds.ReadXml("D:\\AppSettings.xml");

if (dsRs.Tables.Count > 0) {

DataTable dt = ds.Tables[0];

if (dt.Rows.Count > 0) {

for (int r = 0; r < dt.Rows.Count; w++) {

//그냥 찍어보는 for문 ㅋㅋ

for (int c = 0; c < dt.Columns.Count; i++) {

Response.Write(r + "/" + i + " : " + dt.Rows[r][c].ToString() + "<br />");

}

if (dt.Rows[r][0].ToString() == key) {

dt.Rows[r][c] = value;

System.Web.Configuration.WebConfigurationManager.AppSettings.Set(dt.Rows[r][0].ToString(), value);

}

}

}

ds.WriteXml("D:\\AppSettings.xml"); 

}

위의 예제는 간단하게 메모리상의 appSetting값을 변경하고 XML파일로 저장한다.

지금은 AppSettings.xml에 값이 있는 경우에만 수정이 가능한데

위의 예제로 쉽게 응용하여 새로운 값을 추가, 삭제도  쉽게 할 수 있을 것이다.

만약 appSettings가 자주 변경이 일어 나게 된거나 connectStrings 정보를 Web.config파일에 입력하지 않길 원한다면

아래와 같이 이 두 속성에 대해 따로 xml파일로 관리를 할 수 있다.

 

<appSettings configSource="App_Data\파일명.xml"/>
<connectionStrings configSource="App_Data\파일명.xml"/>

 

<appSettings file="경로\파일명.xml"/>

 

appSettings 와 같은 경우 configSource 또는 file 특성을 사용할 수 있다.

configSource 는 ASP.NET 프로젝트의 APP_Data 폴더에 파일을 두고 사용할 경우에 해당 특성을 사용하며

file과 같은 경우는 로컬 컴퓨터의 어디 위치에 상관없이 사용이 가능하다.

 

 

 위와 같이 파일로 설정을 뺀 이유는 다음과 같다.

  1. 어트리뷰트를 이용하여 특정 Configuration Section의 Subset을 다른 XML 파일로부터 가져오도록 처리 할 수 있다.
  2. 해당 XML 파일의 내용이 바뀌더라도 동적 컴파일이 발생하지 않는 유연성을 확보할 수 있다.

  

 

ASP.NET이라는 언어를 공부를 안한 상태로 처음 프로젝트를 진행했을 때의 일이다.

 

어느날인가 DB에서 불러온 값을 수정 한 후 다시 저장을 하는데 계속 같은 값으로 나오는 것이다.

수차례 클릭을 하면서 저장하는 이벤트 로직과 페이지를 봤지만 특별이 문제가 없었다

그때는 당연히 저장되어야 한다고 생각했다;;

 

대부분은 Copy&Paste로 작업을 해서 그런 문제가 생겼다고 생각을 해

수정이 잘되는 페이지를 열어 본 후 다른점을 발견했다

 

그것이 바로 IspostBack 이라는 것이 Page_Load부분에 들어가 있는 것이였다.

변경후 수정이 잘되는 페이지에는

if(!IsPostBack) {

//데이터 불러온 값을 처리하는 함수

}

와 같은 구조로 되어 있었다.

 

ASP.NET은 기존 다른 WEB PROGRAMMING과는 다른 구조로 되어 있다.

이벤트라는 것을 통하여 자신의 페이지를 POSTBACK 하는 방식으로 데이터를 처리 한다.

 

문제는 수정 버튼을 클릭했을 때 POSTBACK이 일어난다. 그 순간 다시 자신의 페이지를 읽으면서 Page_Load 이벤트를 타게된다.

원래 수정하고자 했던 텍스트 박스에는 다시 DB에서 불러온 값을 다시 넣을 것이고 결국 그 값으로 저장이 된 것이다.

그래서 내가 수정하고자 했던 정보는 계속 같은 값으로 나왔던 것이다.

 

ASP.NET에서는 기본적으로 페이지 수명 주기 이벤트를 이해해야 한다.

  

 페이지 이벤트  일반적인 용도 

 PreInit

이 이벤트의 용도는 다음과 같습니다.

  • IsPostBack 속성을 검사하여 페이지가 처음으로 처리되는 것인지 여부를 확인합니다.
  • 동적 컨트롤을 만들거나 다시 만듭니다.
  • 마스터 페이지를 동적으로 설정합니다.
  • Theme 속성을 동적으로 설정합니다.
  • 프로필 속성 값을 읽거나 설정합니다.

 참고
 요청이 포스트백인 경우에는 컨트롤의 값이 뷰 상태에서 아직 복원되지 않았습니다. 이 단계에서 컨트롤 속성을 설정하면 다음 이벤트에서 속성 값이 덮어쓰여질 수 있습니다.

 

 Init 

 모든 컨트롤을 초기화하고 모든 스킨 설정을 적용한 후에 발생합니다. 이 이벤트를 사용하여 컨트롤 속성을 읽거나 초기화할 수 있습니다.

 InitComplete  Page 개체를 통해 발생합니다. 이 이벤트를 사용하여 모든 초기화를 완료해야 할 작업을 처리할 수 있습니다.
 PreLoad

 이 이벤트는 Load 이벤트에 앞서 페이지나 컨트롤을 처리해야 하는 경우에 사용합니다.

 

Page에서 이 이벤트가 발생한 후에 해당 페이지와 모든 컨트롤에 대한 뷰 상태를 로드하고 Request 인스턴스에 포함된 모든 포스트백 데이터를 처리합니다.

 Load

 Page에서 Page에 대해 OnLoad 이벤트 메서드를 호출한 다음 자식 컨트롤 각각에 대해 동일한 과정을 되풀이합니다. 이 과정이 페이지와 모든 컨트롤을 로드할 때까지 해당 자식 컨트롤 각각에 대해 동일하게 진행됩니다.

 

OnLoad 이벤트 메서드를 사용하면 컨트롤의 속성을 설정하고 데이터베이스 연결을 설정할 수 있습니다.

 컨트롤 이벤트

 이러한 이벤트를 사용하면 Button 컨트롤의 Click 이벤트나 TextBox 컨트롤의 TextChanged 이벤트 같은 특정 컨트롤 이벤트를 처리할 수 있습니다.

 

 참고
 포스트백 요청에서 페이지에 유효성 검사기 컨트롤이 포함되어 있으면 처리를 수행하기 전에 Page 및 각 유효성 검사기 컨트롤의 IsValid 속성을 확인합니다.

 

 LoadComplete  이 이벤트는 페이지의 다른 모든 컨트롤을 로드해야 하는 작업에 대해 사용합니다.
 PreRender

 이 이벤트가 발생하기 전에 다음 과정이 진행됩니다.

  • Page 개체에서 각 컨트롤과 페이지에 대해 EnsureChildControls를 호출합니다.
  • 해당 DataSourceID 속성이 설정되어 있는 데이터 바인딩된 컨트롤 각각에서 해당 DataBind 메서드를 호출합니다. 자세한 내용은 이 항목의 뒷부분에 나오는 데이터 바인딩된 컨트롤에 대한 데이터 바인딩 이벤트를 참조하십시오.

페이지의 각 컨트롤에 대해 PreRender 이벤트가 발생합니다. 이 이벤트를 사용하여 페이지나 해당 컨트롤의 내용을 최종적으로 변경할 수 있습니다.

 SaveStateComplete

 이 이벤트가 발생하기 전에 페이지와 모든 컨트롤에 대해 ViewState가 저장됩니다. 이 시점에서 페이지나 컨트롤에 대한 변경 내용은 모두 무시됩니다.

 

이 이벤트를 사용하여 뷰 상태 저장이 필요한 작업을 수행할 수 있지만 컨트롤을 변경할 수는 없습니다.

 Render

 이는 이벤트가 아닙니다. 대신, 이 처리 단계에서 Page 개체가 각 컨트롤에 대해 이 메서드를 호출합니다. 모든 ASP.NET 웹 서버 컨트롤에는 브라우저에 보내는 컨트롤의 태그를 작성하는 Render 메서드가 있습니다.

 

사용자 지정 컨트롤을 만드는 경우 일반적으로 이 메서드를 재정의하여 컨트롤의 태그를 출력합니다. 그러나 사용자 지정 컨트롤에 표준 ASP.NET 웹 서버 컨트롤만 포함되어 있고 사용자 지정 태그는 포함되지 않은 경우 Render 메서드를 재정의할 필요가 없습니다. 자세한 내용은 사용자 지정 ASP.NET 서버 컨트롤 개발을 참조하십시오.

 

사용자 정의 컨트롤(.ascx 파일)에는 렌더링이 자동으로 포함되므로 코드에서 컨트롤을 명시적으로 렌더링할 필요가 없습니다.

 Unload

 이 이벤트는 각 컨트롤에 대해 발생한 다음 페이지에 대해 발생합니다. 컨트롤에서 이 이벤트를 사용하면 컨트롤별 데이터베이스 연결을 닫는 등 특정 컨트롤에 대한 최종 정리 작업을 수행할 수 있습니다.

페이지 자체에 대해 이 이벤트를 사용하면 열려 있는 파일 및 데이터베이스 연결을 닫거나 로깅 또는 기타 요청에 따른 작업을 마무리하는 등의 최종 정리 작업을 수행할 수 있습니다.

 

 참고
언로드 단계를 수행하는 동안 페이지와 페이지 컨트롤이 렌더링되었으므로 응답 스트림을 추가로 변경할 수 없습니다. Response.Write 등의 메서드를 호출하려고 하면 페이지에서 예외가 throw됩니다.

 

 출처: http://msdn.microsoft.com/ko-kr/library/ms178472.aspx ( MSDN )

 

 추가적인 정보 또한 위의 페이지에 친절하게 나와 있다.

 

최대 요청 길이를 초과했습니다.

 

System.Web.Services.Protocols.SoapException: Config 파일에 지정된 확장을 실행하는 동안 예외가 발생했습니다.

System.Web.HttpException: 최대 요청 길이를 초과했습니다.

 

Web.config 파일의

<httpRuntime executionTimeout="90" maxRequestLength="10240" useFullyQualifiedRedirectUrl="false" minFreeThreads="8" minLocalRequestFreeThreads="4" appRequestQueueLimit="100" enableVersionHeader="true" />

 

maxRequestLength 를 늘려주세요.

단위는 kb 입니다.

 

액션 필더 이해하기

  

이 장에서 목표는 액션 필더를 설명하는 것이다. 액션 필터는 여러분이 컨트롤러 액션을 응용할 수 있는 속성이다.

ASP.NET MVC 프레임웍은 여러 액션 필터를 포함한다.

l   OutputCache – 이 액션 필터는 특정한 시간을 위한 컨트롤러 액션의 결과를 저장한다.

l   HandleError – 이 액션 필터는 컨트롤러 액션이 실행될 때 발생하는 에러를 잡아낸다.

l   Authorize – 이 액션 필터는 개개인의 사용자 또는 룰을 통해 접근을 제한할 수 있다.

 

여기서는 OutputCache 액션 필터에 대해서만 알아보겠다.

액션 필터는 attribute이다. 각각의 컨트롤러 또는 전체 컨트롤러에서 모두 액션 필터를 적용할 수 있다.

 

다음 예제는  Index() 액션의 결과를 10초동안 저장하는 예제이다.

 

//[OutputCache(Duration = 10)] 사이트소스ㅡㅡ)+ 에러발생아놔~

[OutputCache(Duration = 10, VaryByParam="none")]

public string Index()

{

    return DateTime.Now.ToString("T");

}

 

주소창에 URL /Data/Index입력하고 리플래쉬를 하면 10초동안 동일한 시간을 보게 될 것이다.

 OutputCache 액션 필터를 10초동안 Index() 페이지에 적용시켰다.

 

 

다른 타입의 필터

 

ASP.NET MVC 프레임웍은 4개의 다른 타입의 필터를 제공한다.

1.      Authorization Filters – IauthorizationFilter  속성을 구현

2.      Action Filter - IActionFilter 속성을 구현

3.      Result Filter – IresultFilter 속성을 구현

4.      Exception Filters – IexceptionFilter 속성 구현

 

필터는 위에 나열된 순서대로 실행이 된다.

 

 

Base ActionFilterAttribute Class

 

이 장에서는 여러분이 쉽게 커스텀 액션 필터, ASP.NET framework에 포함된 기본 ActionFilterAttribute 클래스로 구현을 쉽게 만들 것이다. 

이 클래스는 필터 클래스로부터 상속 받는 IActionFilter IResultFilter 인터페이스 둘다 구현을 한다.

 

base ActionFilterAttribute 클래스는 다음과 같은 메서드를 override 할 수 있다.

l   OnActionExecuting – 이 메소드는 컨트롤 액션 전에 실행이 된다.

l   OnActionExecuted – 이 메소드는 컨트롤 액션 후에 실행이 된다.

l   OnResultExecuting – 이 메소드는 컨트롤 액션 결과 전에 실행된다.

l   OnResultExecuted – 이 메소드는 컨트롤 액션 결과 후에 실행된다.

 

 

Creating a Log Action Filter

 

이 장에서는 커스텀 액션 필터를 어떻게 만들지에 대해 설명한다.

우리는 Visual Studio Output 윈도우에 컨트롤 액션의 상태 단계 로그의 커스텀 액션 필터를 만들 것이다.

LogActionFilter Listing2에 포함되어 있다.

 

using System;

using System.Diagnostics;

using System.Web.Mvc;

using System.Web.Routing;

 

namespace MvcApplication1.ActionFilters

{

    public class LogActionFilter : ActionFilterAttribute

    {

        public override void OnActionExecuting(ActionExecutingContext filterContext)

        {

            Log("OnActionExecuting", filterContext.RouteData);

        }

 

        public override void OnActionExecuted(ActionExecutedContext filterContext)

        {

            Log("OnActionExecuted", filterContext.RouteData);

        }

 

        public override void OnResultExecuting(ResultExecutingContext filterContext)

        {

            Log("OnResultExecuting", filterContext.RouteData);

        }

 

        public override void OnResultExecuted(ResultExecutedContext filterContext)

        {

            Log("OnResultExecuted", filterContext.RouteData);

        }

 

        private void Log(string methodName, RouteData routeData)

        {

            var controllerName = routeData.Values["controller"];

            var actionName = routeData.Values["action"];

            var message = String.Format("{0} controller:{1} action:{2}", methodName,

                                controllerName, actionName);

            Debug.WriteLine(message, "Action Filter Log");

        }

 

    }

}

 

Listing2에서 OnActionExcuting(), OnActionExcuted(), OnResultExecuting(), OnResultExecuted() 메소드를 Log()메소드에서 모두 호출한다.

메소드명과 정확한 라우트 데이터는 Log() 메소드를 통과한다.

Log() 메소드는 Visual Studio Output 윈도우에 메시지를 출력한다. ( 디버그 모드로 )

 

 

 

Home컨트롤러의 Index() 액션이든 아니든 Home컨트롤러에의 모든 액션에 대해서 동작을 한다.

그 동작된 내용을 Output창에 출력이 된다.

 

namespace ExamActionFilter.Controllers

{

    //[HandleError]

    [LogActionFilter]

    public class HomeController : Controller

    {

        public ActionResult Index()

        {

            return View();

        }

 

        public ActionResult About()

        {

            return View();

        }

    }

}

 

여기까지 간단한 액션 필터를 구현하는 것을 배웠다.

 

마스터 페이지

 

이 단원에서는 공통 페이지 레이아웃을 만드는 방법에 대해 배울 것이다.

마스터 페이지의 강점을 배우고 사이트에 로고나 메뉴 링크 그리고 배너 광고를 마스터 페이지 내에 배치하는 것을 배울 것이다.

 

마스터 페이지 만들기

 

 

하나의 어플리케이션 안에 하나 이상의 마스터 페이지를 만들 수 있으며 각각의 마스터 페이지는 각각 다른 레이아웃을 정의 할 수 있다.

 

컨텐츠 페이지 만들기

마스터 페이지를 만든 이후 마스터 페이지를 기본으로 하나 이상의 컨텐츠 페이지를 만들 수 있다.

 

 

  

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">

    브라우저 타이틀 변경

</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

 

    <h2>Contents Page</h2>

 

 

</asp:Content>

 

 

추가한 index 페이지에서 타이틀을 변경한 내용이다. 마스터 페이지의 타이틀 부분에 컨텐츠를 만들어 놓으므로써

각 컨텐츠 페이지의 타이틀을 넣을 수 있다.

 

 

마스터 페이지로 데이터 전달

 

이 단원의 목표는 마스터 페이지에 데이터 전달할 수 있는가를 설명하고 있다.

영화 데이터베이스를 구축하고 영화 카테고리 테이블과 영화 목록 테이블 2개를 만들 것이다.

카테고리를 뿌려주고 그 뿌려준 카테고리에 속한 영화 리스트를 보여줄 것이다.

 

 

데이터베이스 Movies를 만들자. 만들어진 데이터베이스를 더블 클릭하면 서버 탐색기로 이동하여 해당 데이터베이스의 정보를 볼 수 있다.

 

 

 

모델 폴더에 해당 데이터베이스 관련된 LINQ to SQL을 생성한다

 

 컨트롤러 페이지

using System;

using System.Collections.Generic;

using System.Linq;

using System.Web;

using System.Web.Mvc;

using MvcApplication1.Models;

 

namespace MvcApplication1.Controllers

{

    [HandleError]

    public class HomeController : Controller

    {

        private MoviesDataContext _dataContext = new MoviesDataContext();

 

        public ActionResult Index()

        {

            ViewData["categories"] = from c in _dataContext.MovieCategory

                                     select c;

            ViewData["movies"] = from m in _dataContext.Movie

                                 select m;

            return View();

        }

 

        public ActionResult Details(int id)

        {

            ViewData["categories"] = from c in _dataContext.MovieCategory

                                     select c;

            ViewData["movies"] = from m in _dataContext.Movie

                                 where m.CategoryId == id

                                 select m;

            return View();

        }

    }

}

 

 디테일 페이지

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>

<%@ Import! Namespace="MvcApplication1.Models" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">

        Details

</asp:Content>

 

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

    <h2>Details</h2>

    <% foreach (var m in (IEnumerable<Movie>)ViewData["movies"])

         { %>

         <li><%= m.title %></li>

 

    <% } %>

</asp:Content>

 

 마스터 페이지

<%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage" %>

<%@ Import! Namespace="MvcApplication1.Models" %>

 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

 

<html xmlns="http://www.w3.org/1999/xhtml" >

<head runat="server">

    <title><asp:ContentPlaceHolder ID="TitleContent" runat="server" /></title>

</head>

<body>

    <div>

        <h1>My Web Site</h1>

       

          <% foreach (var c in (IEnumerable<MovieCategory>)ViewData["categories"])

             {%>

               <%= Html.ActionLink(c.name, "Details", new {id=c.id} ) %>

 

          <% } %>

   

        <asp:ContentPlaceHolder ID="MainContent" runat="server">

       

        </asp:ContentPlaceHolder>

    </div>

</body>

</html>

 

위의 소스는 마스터 페이지에서 영화 카테고리를 클릭하면 해당 카테고리에 속한 영화 타이틀이 출력되는 예제이다.

 

 위의 소스는 마스터 페이지에서 영화 카테고리를 클릭하면 해당 카테고리에 속한 영화 타이틀이 출력되는 예제이다.

 

 

Good Solution

위의 예제는 심플하게 데이터를 뿌려주고 있다. 하지만 컨트롤러 소스를 보면 영화 카테고리 정보를 중복되게 사용하는 것을 보게 될 것이다.

왜 중복되게 보내는 것일까? 그 이유는 마스터에서 사용하는 카테고리 ViewData 때문이다.

이 단원에서 배울 것은 위의 예제를 좀 더 개선하여 좋을 프로그램으로 만드는 것이 목적이다.

 

우리는 컨트롤러에 추상화로 구현한 클래스를 하나 만들 것이다.

이 클래스의 역할은 Movie데이터를 사용할 수 있게 속성(프로퍼티)을 하나 제공하며 생성자에서 카테고리 정보를 ViewData로 만들어 주는 것을 한다.

그리고 잊으면 안되는 것이 이 클래스는 컨트롤러를 상속 받는 것이다.

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Web;

using System.Web.Mvc;

using System.Web.Mvc.Ajax;

using MvcApplication1.Models;

 

namespace MvcApplication1.Controllers

{

    public abstract class ApplicationController : Controller

    {

        private MoviesDataContext _dataContext = new MoviesDataContext();

 

        public MoviesDataContext DataContext

        {

            get { return _dataContext; }

        }

 

        public ApplicationController()

        {

            ViewData["categories"] = from c in DataContext.MovieCategory

                                     select c;

        }

    }

}

 

해당 추상 클래스를 사용할 Movies컨트롤을 만들겠다.

using System;

using System.Collections.Generic;

using System.Linq;

using System.Web;

using System.Web.Mvc;

using System.Web.Mvc.Ajax;

using MvcApplication1.Models;

 

namespace MvcApplication1.Controllers

{

    public class MoviesController : ApplicationController

    {

        /// <summary>

        /// Show list of all movies

        /// </summary>

        public ActionResult Index()

        {

            ViewData["movies"] = from m in DataContext.Movie

                                 select m;

            return View();

        }

 

        /// <summary>

        /// Show list of movies in a category

        /// </summary>

        public ActionResult Details(int id)

        {

            ViewData["movies"] = from m in DataContext.Movie

                                 where m.CategoryId == id

                                 select m;

            return View();

        }

    }

}

 

소스를 보면 알듯이 ApplicationController를 상속 받아 Movie데이터를 사용하는 것을 볼 수 있다.

그리고 달라진 것이 마스터에 보내야 하는 정보를 이미 추상 클래스에서 해결을 했으므로 Movies컨트롤러에서는 할 필요가 없어진 것이다.

 

Controller를 상속 받은 클래스로 인해 추상 클래스를 상속 받은 모든 클래스는 어느 액션이든 호출될 때 마다 자동으로 영화 카테고리 정보를 ViewData에 포함하게 된다.

'Web Platform' 카테고리의 다른 글

에러) System.Web.HttpException: 최대 요청 길이를 초과했습니다.  (0) 2009.06.29
ASP.NET MVC 액션 필터  (0) 2009.06.29
ASP.NET MVC TagBuilder  (0) 2009.06.29
ASP.NET MVC View  (0) 2009.06.29
ASP.NET MVC Routing 기술  (0) 2009.06.29

HTML Helper 만들고 TagBuilder 사용하기

 

ASP.NET MVC 프레임웍은 HTML Helper를 만들때 TagBuilder라는 utility 클래스를 사용한다.

TagBulider 클래스는 손쉽게 HTML 태그를 만드는 것을 가능하게 하고 생각하네 하는 클래스이다.

 

TagBulider클래스는 System.Web.Mvc 네임스페이스에서 제공되고 5개의 Method를 가지고 있다.

 

AddCssClass()

새로운 Class=”” 속성을 추가

GenerateId()

Id 속성 추가

MergeAttribute()

속성을 추가. 다수의 overload 제공

SetInnerText()

Inner text 추가.

ToString()

기본적인 tag, “<”, “>”, “< />”

 

 

Image HTML Helper 만들기

tag명을 넣어 TagBuilder 생성자를 통해 TagBuilder Class의 인스턴스를 만든다.

다음으로 태그의 속성을 수정하기 위해 AddCssClass MergeAttribute() 메소드를 호출한다.

마지막으로 ToString() 메소를 호출하여 태그를 제공한다.

 

/Helpers/ImageHelper.cs

 

using System.Web.Mvc;

using System.Web.Routing;

 

namespace MvcMyApplication4.Helpers

{

    public static class ImageHelper

    {

        public static string Image(this HtmlHelper helper, string id, string url, string alternateText)

        {

            return Image(helper, id, url, alternateText, null);

        }

 

        public static string Image(this HtmlHelper helper, string id, string url, string alternateText, object htmlAttributes)

        {

            // Create tag builder

            var builder = new TagBuilder("img");

 

            // Create valid id

            builder.GenerateId(id);

 

            // Add attributes

            builder.MergeAttribute("src", url);

            builder.MergeAttribute("alt", alternateText);

            builder.MergeAttributes(new RouteVal!ueDictionary(htmlAttributes));

 

            // Render tag

            return builder.ToString(TagRenderMode.SelfClosing);

        }

    }

}

 

/Home/Index.aspx

 

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>

<%@ Import! Namespace="MvcMyApplication4.Helpers" %>

 

<asp:Content ID="indexTitle" ContentPlaceHolderID="TitleContent" runat="server">

    Home Page

</asp:Content>

 

<asp:Content ID="indexContent" ContentPlaceHolderID="MainContent" runat="server">

 

    <!-- Calling helper without HTML attributes -->

    <%= Html.Image("img1", ResolveUrl("~/Content/XBox.png"), "XBox Console") %>

 

    <!-- Calling helper with HTML attributes -->

    <%= Html.Image("img1", ResolveUrl("~/Content/XBox.png"), "XBox Console", new {border="4px"})%>

 

</asp:Content>

 

 

 

 

<%@ Import! Namespace="MvcMyApplication4.Helpers" %> index.aspx view의 상단에 반드시 import! 해야 한다.

'Web Platform' 카테고리의 다른 글

ASP.NET MVC 액션 필터  (0) 2009.06.29
ASP.NET MVC 마스터 페이지  (0) 2009.06.29
ASP.NET MVC View  (0) 2009.06.29
ASP.NET MVC Routing 기술  (0) 2009.06.29
ASP.NET MVC 개발 환경 만들기  (0) 2009.06.29

MVC
에서는 Routing Controller Method를 통해 View page를 호출을 한다

using System.Web.Mvc;

 

namespace MvcMyApplication2.Controllers

{

    [HandleError]

    public class HomeController : Controller

    {

        public ActionResult Index()

        {

            ViewData["Message"] = "Welcome to ASP.NET MVC!";

            return View();

        }

 

        public ActionResult About()

        {

            return View();

        }

 

        public ActionResult Detail()

        {

            return RedirectToAction("Index");

        }

     }

}

HomeController 에는 3개의 Index(), About(), Detail() action Method가 있다.

 

action은 다음과 같이 주소표시줄에 표기된다.

Index()

/Home/Index

About()

/Home/About

Detail()

/Home/Detail

 

Index() action view를 리턴한다. 어떤 action은 다른 type action 결과를 리턴할 수 있다.

예를 들어 Detail() action처럼 Index() action을 요청하는 RedirectToActionResult를 리턴할 수 있다.

 

Index() action View();

반드시 웹서버에 \Views\Home\Index.aspx가 있어야 한다.

 

만약 View(“Fred”); 를 리턴하면

\Views\Home\Fred.aspx 실행된다.

 

View에 내용추가

View는 다양한 script내용을 포함할 수 있는 html문서이다.

예를 들어 날짜를 출력하는 view를 보겠다.

  

Add.aspx

<%@ Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage" %>

 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

 

<html xmlns="http://www.w3.org/1999/xhtml" >

<head id="Head1" runat="server">

    <title>Add</title>

</head>

<body>

    <div>

       

    The current date and time is<br />

    <% Response.Write(DateTime.Now); %>

 

    </div>

</body>

</html>

이 구분기호 <% %> C#에서 쓰여진 script이다.

Response.Write() Method는 브라우저 내용에 현재 날짜를 보여준다.

이 구분기호 <% %>은 하나 또는 더 많은 세크먼트를 실행하는데 사용된다.

 

Result

The current date and time is
2009-04-18
오후 10:45:43

Add2.aspx

<%@ Page Title=”” Language=”C#” Inherits=”System.Web.Mvc.ViewPage” %>

 

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>

 

<html xmlns=”http://www.w3.org/1999/xhtml” >

<head id=”Head1” runat=”server”>

    <title>Add</title>

</head>

<body>

    <div>

 

    The current date and time is<br />

    <%=DateTime.Now %>

 

    </div>

</body>

</html>

<%=%> 구분기호는 Response.Write() 를 간략하게 쓰는 기호이다.

Result

The current date and time is
2009-04-18
오후 10:45:43

 

 

View에서 HTML Helper 사용하기

HTML HelperTextbox, Link, Dropdown List, List Box등의 표준 HTML 요소를 사용할 수 있게 한다.

 

Login.aspx

<%@ Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage" %>

 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

 

<html xmlns="http://www.w3.org/1999/xhtml" >

<head id="Head1" runat="server">

    <title>Login Form</title>

</head>

<body>

    <div>

   

    <% using (Html.BeginForm())

       { %>

        

        <label for="UserName">User Name:</label>

        <br />

        <%= Html.TextBox("UserName") %>

       

        <br /><br />

           

        <label for="Password">Password:</label>

        <br />

        <%= Html.Password("Password") %>

       

        <br /><br />

 

        <input type="submit" value="Log in" />       

   

    <% } %>

   

    </div>

</body>

</html>

HTML Source

<form action="/Home/Login" method="post">

 

        <label for="UserName">User Name:</label>

        <br />

        <input id="UserName" name="UserName" type="text" value="" />

       

        <br /><br />

           

        <label for="Password">Password:</label>

        <br />

        <input id="Password" name="Password" type="password" />

       

        <br /><br />

 

        <input type="submit" value="Log in" />       

   

    </form>

 

위에서 보듯이 HTML Helper는 각 HTML 요소로 변경된다. 기존의 서버컨트롤과는 다른 string만 변경하여 리턴을 해준다. ( 랜더링 작업이 없다. )

 

 

View View Data 사용하기

Controller 에서 ViewData["Message"] = "Welcome to ASP.NET MVC!"; 설정하면

ViewData["Message"]View Page에서 사용할 수 있다.

<%= Html.Encode(ViewData["Message"]) %> 하면 View Page Welcome to ASP.NET MVC! 라고 출력이 된다.

 

 

Custom HTML Helper 만들기

ASP.NET MVC HTML Helper의 종류는 다음과 같다.

  • Html.ActionLink()
  • Html.BeginForm()
  • Html.CheckBox()
  • Html.DropDownList()
  • Html.EndForm()
  • Html.Hidden()
  • Html.ListBox()
  • Html.Password()
  • Html.RadioButton()
  • Html.TextArea()
  • Html.TextBox()

<label>관련된 static Method를 만들려고 한다.

우리가 가끔 string 관련 함수를 만들어 return 하는 방식과 같다.

Helpers/LabelHelper.cs

using System;

 

namespace MvcMyApplication2.Helpers

{

    public class LabelHelper

    {

        public static string Label(string target, string text)

        {

            return String.Format("<label for='{0}'>{1}</label>", target, text);

        }

    }

}

HTML 요소의 특성에 맞게 파라메터를 받고 스트링 문자열을 만들어 return 한다.

이렇게 만든 클래스를 사용해보자

 

<%@ Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage" %>

<%@ Import! Namespace="MvcMyApplication2.Helpers " %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >

<head id="Head1" runat="server">

     <title>Index2</title>

</head>

<body>

     <div>

          <% using (Html.BeginForm())

          { %>

               <%= LabelHelper.Label("firstName", "First Name:") %>

               <br />

               <%= Html.TextBox("firstName")%>

               <br /><br />

               <%= LabelHelper.Label("lastName", "Last Name:") %>

               <br />

               <%= Html.TextBox("lastName")%>

               <br /><br />

               <input type="submit" value="Register" />

          <% } %>

     </div>

</body>

</html>

 

Result

 

 

HTML Helpers Extension Method 만들기

HtmlHelper 클래스에 Label() 확장 메소드를 추가하기

1.     클래스는 Static class

2.     Static classExtension Method를 정의 해야한다.

 

Helpers/LabelExtensions.cs

using System;

using System.Web.Mvc;

 

namespace MvcMyApplication2.Helpers

{

    public static class LabelExtensions

    {

        public static string Label(this HtmlHelper helper, string target, string text)

        {

            return String.Format("<label for='{0}'>{1}</label>", target, text);

        }

    }

}

 

작성 후 View Page Import! 하면 아래 그림과 같이 출력이 된다.

 

 

 

'Web Platform' 카테고리의 다른 글

ASP.NET MVC 마스터 페이지  (0) 2009.06.29
ASP.NET MVC TagBuilder  (0) 2009.06.29
ASP.NET MVC Routing 기술  (0) 2009.06.29
ASP.NET MVC 개발 환경 만들기  (0) 2009.06.29
ASP.NET MVC(Model, View, Controller)란?  (0) 2009.06.29

+ Recent posts