티스토리 뷰
요즘은 시간이 많아서 그런지 자주 강좌를 올리네요 ㅋㅋ
이번에는 웹사이트의 핵심인 Core(코어) 모듈을 구성해 보는 시간을 가져봅니다.
잘 구성된 모듈은 안정된 시스템 그리고 코딩의 작업 속도와 바로 직결됩니다.
모듈의 구성 방법은 개발자마다, 그리고 시스템 마다 각이 각색이겠지만,
오늘 구성해 볼 모듈은 대부분의 웹사이트에서 적용할 수 있는 코어 구성 첫 번째
시간을 갖도록 합니다.
본 강의는 객체지향적인 문법을 공부하거나 소화한 분을 대상으로 합니다.
우리가 구성할 코어는 XML 로 구성된 사이트 맵이 필요합니다.
<?xmlversion="1.0"encoding="utf-8" ?>
<AdminID="ADMIN1" Path="WebAdmin">
<Title>블로그 관리자</Title>
<ModuleGroupID="MG1" Path="01Blog">
<Title>기본 설정 관리</Title>
<ModuleID="UBI1"ViewControl="/WebAdmin/01Blog/BlogBaseInfo.ascx">
<Title>블로그 기본 정보</Title>
<InitParams>
<ParamName="umc">Umc</Param>
<ParamName="umc1">Umc1</Param>
</InitParams>
</Module>
<ModuleID="UBI2"ViewControl="/WebAdmin/01Blog/ProfileInfo.ascx">
<Title>프로필 기본정보 관리</Title>
</Module>
</ModuleGroup>
<ModuleGroupID="MG2"Path="02Privacy">
<Title>프라이버시 관리</Title>
<ModuleID="UP1"ViewControl="/WebAdmin/02Privacy/Privacy.ascx">
<Title>프라이버시 관리</Title>
</Module>
</ModuleGroup>
<ModuleGroupID="MG3"Path="02Bbs">
<Title>게시판 관리자</Title>
<ModuleID="Bbs1"ViewControl="/WebAdmin/02Privacy/Privacy.ascx">
<Title>자유게시판</Title>
<InitParams>
<ParamName="BoardNo">1</Param>
<ParamName="Template.Bbs.List">/WebAdmin/02Bbs/ListTemplate.ascx</Param>
<ParamName="Template.Bbs.Write">/WebAdmin/02Bbs/WriteTemplate.ascx</Param>
<ParamName="Template.Bbs.View">/WebAdmin/02Bbs/ViewTemplate.ascx</Param>
</InitParams>
</Module>
</ModuleGroup>
</Admin> |
ModuleGruop 섹션은 여러 개의 Module 로 구성되어 있습니다. 이 사이트맵을 기준으로
메뉴 컨트롤을 만들때나 이 Module의 부모가 누구인지가 필요할 때 유용합니다.
Module 섹션은 ViewControl 이라는 속성이 있습니다. 사용자 정의 콘트롤로 만들어진
이 컨트롤은 PageTemplate.ascx 이라는 마스터페이지와 같은 역할을 하는 PageTemplate의
PlaceHolder 컨트롤에 들어가게 될것입니다.
Module 섹션안에는 InitParams 라는 섹션이 또 존재하게 되는데,예를 들어 QueryString으로
넘겨야할 BoardNo 등의 파라메터들을 여기에 모두다 집어 넣을 수 있습니다.
이제 이 XML 을 사이트에 이용할 수 있도록 Parsing 해야 합니다.
로직은 “C# 디자인 패턴” 책의 Composite 패턴을 확장해 보았습니다. Composite 패턴에
대한 선수 학습이 필요합니다.
먼저 ISitemap 이라는 Interface를 만들어 보겠습니다.
namespace Umc.Core.WebAdmin.Sitemap
{
public interface ISitemap
{
string ID { get; set; }
string Path { get; set; }
string Title { get; set; }
string ViewControl { get; set; }
int Count { get; }
IEnumerator GetEnumerator();
ISitemap Parent { get; }
void Add(ISitemap sitemap);
bool HasChild { get; }
void ReadXml(XmlReader reader);
}
} |
Sitemap 에 필요한 모든 클래스는 위 Interface를 상속 받게 되고 가장 핵심이 되는
ReadXml 메서드가 바로 XML 을 해독하게 될 것입니다.
이 Interface는 SitemapModule가 상속받습니다.
namespace Umc.Core.WebAdmin.Sitemap
{
public class SItemapModule : ISitemap
{
protected ArrayList sitemap;
protected ISitemap parent;
protected string _id;
protected string _path;
protected string _title;
// InitParams 섹션
protected StringDictionary _initParam = new StringDictionary();
public SItemapModule(ISitemap _parent)
{
this.parent = _parent;
this.sitemap = new ArrayList();
}
#region ISitemap 멤버
public string ID
{
get { return _id; }
set { _id = value; }
}
public string Path
{
get { return _path; }
set { _path = value; }
}
public string Title
{
get { return _title; }
set { _title = value; }
}
public virtual string ViewControl
{
get { return null; }
set { throw new Exception("The method or operation is not implemented."); }
}
public int Count
{
get { return sitemap.Count; }
}
public System.Collections.IEnumerator GetEnumerator()
{
return sitemap.GetEnumerator();
}
public ISitemap Parent
{
get { return this.parent; }
}
public virtual void Add(ISitemap sitemap)
{
this.sitemap.Add( sitemap );
}
public bool HasChild
{
get { return sitemap.Count>0; }
}
public virtual void ReadXml(XmlReader reader)
{
throw new Exception("The method or operation is not implemented.");
}
#endregion
public void ReadInitParams(XmlReader reader)
{
while (reader.Read())
{
if(reader.IsEmptyElement) return;
if (reader.NodeType == XmlNodeType.Element)
{
if (reader.Name == Sitemap.NODE_PARAM)
{
string _key = reader["Name"];
string _value = reader.ReadElementString();
_initParam.Add( _key, _value );
}
}
if(reader.Name==Sitemap.NODE_INIT_PARAMS &&
reader.NodeType==XmlNodeType.EndElement)
return;
}
}
public StringDictionary GetInitParam
{
get { return _initParam; }
}
}
} |
기본이 되는 각각 메서드와 프로퍼티의 역할을 정의해 놓았습니다. 각각 쓰임새에 따라
특히 ReadXml 은 virtual 로 선언된 것을 볼 수 있습니다.
이 클래스는 각각 섹션을 처리할 클래스들의 뼈다가 되는 클래스로, 다음과 같은
다이어그램을 볼 수 있습니다.
차례대로 Sitemap 클래스부터 살펴보겠습니다.
public class Sitemap : SItemapModule
{
public const string NODE_ADMIN = "Admin";
public const string NODE_MODULE_GROUP = "ModuleGroup";
public const string NODE_MODULE = "Module";
public const string NODE_INIT_PARAMS = "InitParams";
public const string NODE_PARAM = "Param";
public const string NODE_TITLE = "Title";
public Sitemap(ISitemap _parent)
: base(_parent)
{
}
public Sitemap() : base(null)
{
}
public override void Add(ISitemap _sitemap)
{
sitemap.Add( _sitemap );
}
public override void ReadXml(XmlReader reader)
{
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element)
{
if (reader.Name == NODE_ADMIN)
{
this.ID = reader["ID"];
this.Path = reader["Path"];
}
if (reader.Name == NODE_TITLE)
{
this.Title = reader.ReadElementString();
}
if (reader.Name == NODE_MODULE_GROUP)
{
ModuleGroup moduleGroup = new ModuleGroup(this);
moduleGroup.ID = reader["ID"];
moduleGroup.Path = reader["Path"];
moduleGroup.ReadXml(reader);
this.Add(moduleGroup);
}
}
if(reader.Name==NODE_ADMIN &&
reader.NodeType==XmlNodeType.EndElement)
break;
}
}
public static ISitemap ReadSitemap(string path)
{
XmlTextReader reader = null;
try
{
reader = new XmlTextReader(path);
ISitemap root = new Sitemap();
root.ReadXml( reader );
return root;
}
catch(Exception ex)
{
throw ex;
}
finally
{
reader.Close();
}
}
} |
ReadSitemap 이라는 static 클래스가 시발점으로 각각의 노드를 탐색 할 수 있습니다.
클래스의 상단에는 XML 의 섹션들을 상수로 정의 해 놓았습니다.
ISitemap root = new Sitemap() 과 같이 최상단의 부모는 Null 이라는걸 알 수 있습니다.
ModuleGroup moduleGroup = new ModuleGroup(this); 구문이 의아해 하실겁니다.
XML의 ModuleGroup 섹션을 만나면 ModuleGroup 내의 요소들을 처리할 또 다른 처리기가
필요합니다. 여기의 this 인자는 ModuleGroup 내에서는 이 this 가 부모 클래스가 되는
것입니다.
public class ModuleGroup : SItemapModule
{
public ModuleGroup(ISitemap _parent)
: base(_parent)
{
}
public override void ReadXml(System.Xml.XmlReader reader)
{
int depth = reader.Depth;
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element)
{
if (reader.Name == Sitemap.NODE_TITLE)
{
this.Title = reader.ReadElementString();
}
if (reader.Name == Sitemap.NODE_MODULE)
{
ModuleInfo moduleInfo = new ModuleInfo(this);
moduleInfo.ID = reader["ID"];
moduleInfo.ViewControl = reader["ViewControl"];
moduleInfo.ReadXml( reader );
this.Add(moduleInfo);
}
}
if(reader.Depth <= depth ) return;
}
}
} |
여기도 별반 다를게 없네요~
ModuleGroup 을 탐색하다 Module 섹션을 만나면 this 클래스를 인자로 ModuleInfo
클래스에게 탐색을 넘깁니다.
public class ModuleInfo : SItemapModule
{
private string _viewControl = null;
public ModuleInfo(ISitemap _parent)
: base(_parent)
{
}
public override string ViewControl
{
get { return _viewControl; }
set { _viewControl = value; }
}
public override void ReadXml(XmlReader reader)
{
int depth = reader.Depth;
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element)
{
if (reader.Name == Sitemap.NODE_TITLE)
{
this.Title = reader.ReadElementString();
}
if (reader.Name == Sitemap.NODE_INIT_PARAMS)
{
ReadInitParams( reader );
}
}
if(reader.Name==Sitemap.NODE_MODULE &&
reader.NodeType==XmlNodeType.EndElement)
return;
}
}
} |
ViewControl 은 Module 섹션이 갖는 Attribute 이므로 여기에서 override 되었습니다.
그리고 ReadInitParams() 는 이미 부모 클래스에서 정의 해 놓았습니다.
이제 이 클래스를 통제시키는 SitemapManager 를 살펴봅니다.
public class SitemapManager
{
private static SitemapManager _instance = null;
private ISitemap _sitemap;
public ISitemap SiteInfo
{
get { return _sitemap; }
}
private SitemapManager()
{
}
public static SitemapManager GetInstance()
{
lock (typeof(SitemapManager))
{
if (_instance == null)
{
_instance = new SitemapManager();
_instance.Init();
}
}
return _instance;
}
public void Init()
{
ReConfig();
string path = Utility.GetAbsolutePath( Umc.Core.UmcConfiguration.Core["AdminXmlPath"]);
_sitemap = Sitemap.ReadSitemap(path);
}
public void ReConfig()
{
_sitemap = null;
}
public string GetNavigateString(ISitemap _map)
{
ISitemap _tempSitemap = _map;
StringBuilder naviString = new StringBuilder();
naviString.Insert(0, _tempSitemap.Title);
while (_tempSitemap.Parent != null)
{
naviString.Insert(0, _tempSitemap.Parent.Title + " > " );
_tempSitemap = _tempSitemap.Parent;
}
return naviString.ToString();
} |
여기에서 Singleton 패턴이 사용되었습니다.
생성자가 private 로 선언되어있습니다. 이 객체를 접근 하기 위해서 GetInstance() 라는
단 한 개의 통로만 열려있습니다.
이로 인한 이점은 객체의 정보를 소멸 시키지 않고 언제든 참조할 수 있습니다.
때문에 언제 어디서든 SiteInfo 프로퍼티를 호출하면 사이트맵의 정보를 다시 읽지 않고
고스란이 참조 할 수 있게 되는것입니다.
또한, GetNavigateString(ISitemap _map) 으로 부모의 Sitemap 까지 찾아 올라가
네비게이트 문자열을 뽑아 낼 수 있습니다.
여기의 Init는 언제 호출 될 것이냐가 의문이네요… 간단합니다..
Global.asax 에 첫 어플케이션이 시작될 때 초기화 되면 됩니다.
private SitemapManager _sitemapManager = null;
void Application_Start(object sender, EventArgs e)
{
_sitemapManager = SitemapManager.GetInstance();
} |
이제 이 Sitemap 이 TreeView 컨트롤에 바인딩 될 수 있도록 Sitemap_Node 클래스를
만들어봅니다.
public class Sitemap_Node : System.Web.UI.WebControls.TreeNode
{
ISitemap sitemap;
public Sitemap_Node(ISitemap _sitemap)
: base(_sitemap.Title)
{
this.sitemap = _sitemap;
base.Value = _sitemap.ID;
}
public ISitemap Sitemap
{
get { return sitemap; }
}
} |
이와 같이 TreeView 에 Sitemap의 Title 프로퍼티가 바인딩 되고, TreeView의 Value 에는
Sitemap.ID 가 바인딩 됩니다.
그럼 우리가 구성한 Sitemap 은 TreeView 컨트롤과 거의 흡사한 형식으로 메모리에
저장됩니다. Sitemap 은 지금 내가 누구인지, 내 부모가 누구인지, 그리고 Root 가
누구인지 까지 알아낼 수 있습니다.
마지막으로 재귀호출을 이용하여 TreeView 컨트롤에 Sitemap 노드들을 바인딩 해봅니다.
public partial class WebAdmin_Default : Umc.Core.WebAdmin.WebAdminTemplate
{
private ISitemap sitemap;
protected void Page_Load(object sender, EventArgs e)
{
// Postback 또는 UpdatePanel 안의 클릭후 Sitemap_Node 로 캐스팅이 안되므로
// 매번 다시 바인딩
BuildTree();
}
private void BuildTree()
{
treeMenu.Nodes.Clear();
sitemap = SitemapManager.GetInstance().SiteInfo;
Sitemap_Node node = new Sitemap_Node(sitemap);
treeMenu.Nodes.Add(node);
AddNodes(sitemap, node);
treeMenu.ExpandAll();
}
private void AddNodes(ISitemap sitemap, Sitemap_Node node)
{
IEnumerator currentSitemap = sitemap.GetEnumerator();
while (currentSitemap.MoveNext())
{
ISitemap newSitemap = (ISitemap)currentSitemap.Current;
Sitemap_Node newNode = new Sitemap_Node(newSitemap);
node.ChildNodes.Add( newNode );
AddNodes( newSitemap, newNode );
}
}
protected void treeMenu_SelectedNodeChanged(object sender, EventArgs e)
{
ISitemap _sitemap = (ISitemap)((Sitemap_Node)treeMenu.SelectedNode).Sitemap;
lblNavigate.Text = SitemapManager.GetInstance().GetNavigateString(_sitemap);
CurrentSitemapInfo = _sitemap;
if (_sitemap.ViewControl != null)
{
Control template = LoadControl(_sitemap.ViewControl);
MainPlace.Controls.Clear();
MainPlace.Controls.Add(template);
}
}
} |
실행결과 화면은 다음과 같습니다.
이런 방식의 Sitemap 을 구성함으로써 다음과 같은 장점이 있습니다.
1. MasterPage 가 필요없음
2. SiteMap 컨트롤 / SiteMapPath 컨트롤이 필요없음.
3. 불필요한 QueryString 이 필요없음
4. Menu 컨트롤 등이 필요없음
5. Sitemap 을 Dom 과 같은 형태로 탐색이 가능
6. Atlas/Ajax 등을 활용하여 한 페이지로 동적인 사이트가 완성 가능
(사실 뭐좀 준비하고 있어요^^ㅋ)
이외에도 많은 장점을 가지고 있지만, 결정적인 단점은 직접 만들어야 한다는거~~ oTL
하지만 오늘 구성한 사이트맵이 가장 핵심이 되는 부분이므로, 핵심이 완성된다면 다른
부분은 쉽게 구현 가능 할 것입니다.
훔… 내가 쓰고 봐도 재미가 없네요 ㅋ
그럼 다가오는 크리스마스는 행복하게 보내시기 바랍니다^^//
※ UmcBlog 소스 ( http://umc.pe.kr/article_107.aspx ) 관리자 부분을 보시면 위의 패턴으로 구현이 되어있으니, 참고 하시기 바랍니다.
'.NET > ASP.NET' 카테고리의 다른 글
delegate 를 이용한 게시판 구성 (1) | 2007.07.24 |
---|---|
너 ASPX 웹폼 확장자를 바꿔봤니? 난 해봤~어! (0) | 2007.04.09 |
이미지를 합성해보자~ [ 이미지 도용방지 ] (0) | 2007.04.06 |
제네릭 처리기를 이용하여 자동가입방지 폼 구현 (Captcha) (0) | 2007.04.05 |
파일을 압축하여 내려받자 (0) | 2007.04.05 |
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- 2,833,030
- Today
- 0
- Yesterday
- 115
링크
- ***** MY SOCIAL *****
- [SOCIAL] 페이스북
- [SOCIAL] 팀 블로그 트위터
- .
- ***** MY OPEN SOURCE *****
- [GITHUB] POWERUMC
- .
- ***** MY PUBLISH *****
- [MSDN] e-Book 백서
- .
- ***** MY TOOLS *****
- [VSX] VSGesture for VS2005,200…
- [VSX] VSGesture for VS2010,201…
- [VSX] Comment Helper for VS200…
- [VSX] VSExplorer for VS2005,20…
- [VSX] VSCmd for VS2005,2008
- .
- ***** MY FAVORITES *****
- MSDN 포럼
- MSDN 라이브러리
- Mono Project
- STEN
- 일본 ATMARKIT
- C++ 빌더 포럼
- .
TAG
- .NET Framework 4.0
- TFS
- monodevelop
- .NET
- Team Foundation Server 2010
- 땡초
- TFS 2010
- Visual Studio 11
- Visual Studio
- LINQ
- 비주얼 스튜디오 2010
- 엄준일
- Visual Studio 2010
- testing
- github
- Silverlight
- Managed Extensibility Framework
- c#
- Visual Studio 2008
- MEF
- 팀 파운데이션 서버
- test
- POWERUMC
- 비주얼 스튜디오
- Windows 8
- ALM
- umc
- ASP.NET
- mono
- Team Foundation Server
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 |
글 보관함
- 2020/05 (1)
- 2019/10 (3)
- 2018/11 (1)
- 2018/08 (2)
- 2017/04 (1)
- 2017/01 (2)
- 2016/11 (2)
- 2016/08 (1)
- 2016/05 (1)
- 2016/04 (2)
- 2016/02 (2)
- 2016/01 (1)
- 2015/05 (1)
- 2015/04 (2)
- 2015/03 (1)
- 2015/02 (1)
- 2015/01 (1)
- 2014/11 (1)
- 2014/09 (2)
- 2014/08 (2)
- 2014/05 (2)
- 2014/04 (3)
- 2014/03 (2)
- 2014/02 (2)
- 2014/01 (4)
- 2013/12 (2)
- 2013/11 (1)
- 2013/10 (2)
- 2013/09 (6)
- 2013/08 (3)