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 어트리뷰터 정보(메일서버, 포트, 템플릿명 등)를 쉽게 얻어올 수 있는 것을 보실 수 있을 것입니다.

다음은 결과 화면입니다.

+ Recent posts