반응형
반응형

이 글은 월간 마이크로소프트웨어(일명 마소) 2008년 01월호 실전 강의실에 기고한 글입니다. 요즘 도통 포스팅을 할 수 없는 관계로 인해 대체합니다. ㅡ_ㅡ;; (하는 거 없이 바빠요~~~)


ASP.NET AJAX 웹 사이트 생성 및 웹 폼 구성

RSS 정보를 관리하는 데이터베이스가 만들어졌으므로, 이제 ASP.NET AJAX 웹 사이트를 만들어보도록 하겠다. Visual Studio 2005를 실행시킨 후, 새 웹사이트 메뉴를 선택한다. 새 웹 사이트를 만드는 창이 나타나면, <화면 5>와 같이 ASP.NET AJAX-Enabled Web Site 템플릿으로 선택한 후, 확인 버튼을 클릭하여, 새로운 웹 사이트를 생성한다.


<화면 5> 새 웹사이트의 생성

새롭게 웹 사이트가 생성이 되었으면, 새 항목 추가 메뉴를 이용하여 새로운 웹 폼을 추가한 뒤 웹 폼의 이름을 “RssView.aspx"라고 한다. 그리고, <head> 태그 사이에 웹 폼에서 사용할 스타일을 <리스트 6>과 같이 정의한다.

---------------------------------------------------------------------------------------------------
<style type="text/css">
td { font-size : 9pt font-family : "맑은고딕“ }
a img { border : 0 }
a:link { color : #636563 text-decoration : none font-weight : bold }
a:visited { color : #636563 text-decoration : none font-weight : bold }
a:active { color : #FFA600 text-decoration : none font-weight : bold }
a:hover { left : 1px color : #FFA600 position : relative top : 1px text-decoration : none }
.modalBackground { background-color : Gray filter : alpha(opacity=70) opacity : 0.7 }
#content { width : 500px padding : 10px margin-top : 50px margin-bottom : 20px margin-right : auto; margin-left : auto background : #ccc border : 3px solid #666 ; text-align:left }
.panPopUp { width : 450px height : 400px padding : 10px margin-top : 10px margin-bottom : 10px margin-right : auto; margin-left : auto background : #EEEEEE border : 3px solid #666 text-align : left vertical-align : middle overflow : auto }
</style>
---------------------------------------------------------------------------------------------------
<리스트 6> 웹 폼에서 사용하는 스타일 정의

또한, RSS 주소를 추가하기 위해 추가 버튼을 클릭할 경우, 추가할 RSS 주소를 입력했는지, 그리고 RSS 주소가 정확하게 “http://"로 시작하는지를 확인하기 위한 자바스크립트 함수를 <리스트 7>과 같이 <head> 태그 사이에 추가한다.

---------------------------------------------------------------------------------------------------
<script language="javascript" type="text/javascript">
function fnInputValid() {
var txtRssAddress = document.getElementById( "txtRssAddress" );

if ( txtRssAddress.value == "" ) {
alert( "URL을 입력하세요 );
txtRssAddress.focus();
return false;
} else {
if ( txtRssAddress.value.indexOf( "http://" ) < 0 ) {
alert( "http://로 시작하는 URL을 입력하세요" );
txtRssAddress.focus();
return false;
}
}

return true;
}
</script>
---------------------------------------------------------------------------------------------------
<리스트 7> 웹 폼에서 사용하는 자바스크립트 정의

스타일과 자바스크립트 코드를 웹 폼에 입력하였다면, 이제 실질적인 웹 폼 코드를 입력해보도록 하겠다. 웹 폼의 구조는 content라는 최상위의 div 태그 아래에 divAddRss, divRssList, divPostList라는 3개의 div 태그가 속한 구조로 되어 있다. <리스트 8>은 웹 폼의 전체 소스 코드를 보여준다.

---------------------------------------------------------------------------------------------------
<form id="RSSForm" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server" />
<div id="content">
<div id="divAddRss" style="text-align:center;padding-bottom:20px;">
<h4>총<asp:Label ID="lblCount" runat="server" Text="0"></asp:Label>개의 주소가 등록되어 있습니다</h4>
추가할 주소를 입력하세요. :
<asp:TextBox ID="txtRssAddress" runat="Server" Width="200"></asp:TextBox>
<br /><br />
<asp:Label ID="lblInValid" runat="server" ForeColor="red" Visible="false"></asp:Label>
<asp:ImageButton ID="btnAddRssAddress" runat="server" ImageAlign="AbsMiddle" ImageUrl="~/Images/btn_add.png"
OnClientClick="return fnInputValid()" OnClick="btnAddRssAddress_Click" />
</div>
<hr />
<div id="divRssList" style="text-align:center;padding-bottom:20px;">
<h4>등록된 주소 목록</h4>
<asp:GridView ID="gvRssList" runat="server" BackColor="White" BorderColor="#999999" BorderStyle="Solid"
BorderWidth="1px" CellPadding="3" ForeColor="Black" GridLines="Vertical" ShowHeader="false"
AutoGenerateColumns="false" Width="100%" OnRowCommand="gvRssList_RowCommand">
<FooterStyle BackColor="#CCCCCC" />
<SelectedRowStyle BackColor="#000099" Font-Bold="True" ForeColor="White" />
<PagerStyle BackColor="#999999" ForeColor="Black" HorizontalAlign="Center" />
<HeaderStyle BackColor="Black" Font-Bold="True" ForeColor="White" />
<AlternatingRowStyle BackColor="#CCCCCC" />
<Columns>
<asp:TemplateField>
<ItemStyle HorizontalAlign="left" />
<ItemTemplate>
<asp:LinkButton ID="lbtnTitle" runat="server" Text='<%#Eval("title")%>'
CommandName='<%#Eval("link")%>'></asp:LinkButton>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
</div>
<hr />
<div id="divPostList" style="text-align:center;padding-bottom:20px;">
<h4>포스트목록</h4>
<asp:GridView ID="gvPostList" runat="server" BackColor="White" BorderColor="#999999" BorderStyle="Solid"
BorderWidth="1px" CellPadding="3" ForeColor="Black" GridLines="Vertical" ShowHeader="false"
AutoGenerateColumns="false" Width="100%">
<FooterStyle BackColor="#CCCCCC" />
<SelectedRowStyle BackColor="#000099" Font-Bold="True" ForeColor="White" />
<PagerStyle BackColor="#999999" ForeColor="Black" HorizontalAlign="Center" />
<HeaderStyle BackColor="Black" Font-Bold="True" ForeColor="White" />
<AlternatingRowStyle BackColor="#CCCCCC" />
<Columns>
<asp:TemplateField>
<ItemStyle HorizontalAlign="left" />
<ItemTemplate>
<asp:Label ID="lblItemTitle" runat="server"><span style="cursor:hand"><%#Eval("title")%></span></asp:Label>
<asp:Panel ID="panPopUp" runat="server" Style="display:none">
<asp:Panel ID="panDesc" runat="server" CssClass="panPopUp">
<h4><a href="<%#Eval("link")%>" target="_blank"><%#Eval("title")%></a></h4>
<%#Eval("pubdate")%><br /><hr />
<%#Eval("description")%><br /><hr />
<center><asp:ImageButton ID="btnCancel" runat="server" ImageAlign="AbsMiddle"
ImageUrl="~/Images/btn_cancel.png" /></center>
</asp:Panel>
</asp:Panel>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
</div>
</div>
</form>
---------------------------------------------------------------------------------------------------
<리스트 8> 웹 폼의 전체 소스 코드

웹 폼에 관련된 코드 비하인드에서의 코드는 이후에, 순차적으로 소개를 하도록 하겠다. 우선 현재의 소스로 빌드를 해보면, 오류가 발생하게 될 것이다. 이러한 오류를 피하기 위해서, 웹 폼에 정의된 필요한 이벤트 핸들러들의 빈 코드를 다음과 같이 추가한다.

---------------------------------------------------------------------------------------------------
protected void btnAddRssAddress_Click(object sender, EventArgs e) { }
protected void gvRssList_RowCommand(object sender, GridViewCommandEventArgs e) { }
---------------------------------------------------------------------------------------------------

이제, 디버그 메뉴의 “디버깅하지 않고 시작” 또는 Ctrl+F5를 눌러, 웹 폼을 브라우저에서 보도록 하자. 다음과 같은 화면이 보이게 될 것이다.


<화면 6> RSS Viewer 화면

CommonUtility 클래스의 구성

위에서 생성한 웹 폼에서 데이터베이스의 데이터를 읽어오고, 저장하는 기능들과, 기타 다른 기능들을 제공하는 메소드들을 포함하는 CommonUtility 클래스를 만들어보도록 하겠다. 새 항목 추가 메뉴를 선택하여, 클래스 템플릿을 선택하고 클래스명을 <화면 7>과 같이 "CommonUtility.cs"라고 한다.


<화면 7> CommonUtility.cs 클래스의 생성

CommonUtility 클래스가 생성되었으면, using 선언부에 다음과 같은 네임스페이스를 추가한다.

---------------------------------------------------------------------------------------------------
using System.Data.SqlClient;
using System.Net;
using System.Xml;
---------------------------------------------------------------------------------------------------

다음으로, 데이터베이스에 연결하기 위한 연결 문자열에 대한 정보를 가지는 변수를 선언한다. (물론, 연결 문자열을 Web.Config의 <connectionStrings> 섹션에 정의하여도 상관은 없다.)

---------------------------------------------------------------------------------------------------
private static string strConnection="Server=localhost;DataBase=MASODataBase;UID=neostyx;PWD=;"
---------------------------------------------------------------------------------------------------

CommonUtility 클래스에는 다음과 같은 7개의 메소드가 추가되게 되며, 각각의 메소드의 설명은 다음과 같다.

- GetRssData() 메소드 : 입력받은 RSS 주소를 통해 반환되는 XML 정보를 XmlDocument로 반환한다.
- ConvertToFitDateTime() 메소드 : <Item>요소의 하위 요소인 <pubDate>의 날짜 정보를 변경한다.
- GetRssAddressCount() 메소드 : 등록된 RSS 주소의 카운트를 반환한다.
- GetRssAddressList() 메소드 : 등록된 RSS에 대한 기본 정보를 반환한다.
- GetRssItemList() 메소드 : 선택한 RSS에 대한 컨텐츠(Item) 정보를 반환한다.
- InsertRssAddress() 메소드 : 새로운 RSS 정보를 데이터베이스에 추가한다.
- InsertRssItems() 메소드 : 새로운 컨텐츠(Item) 정보를 반환한다.

CommonUtility 클래스의 전체 소스는 <리스트 9>와 같다.

---------------------------------------------------------------------------------------------------
using System;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
using System.Net;
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.Xml;

public class CommonUtility
{
private static string strConnection
= "Server=localhost;DataBase=MASODataBase;UID=neostyx;PWD=;"

public CommonUtility()
{

}

public static XmlDocument GetRssData(string strURL)
{
HttpWebRequest httpReq = (HttpWebRequest)WebRequest.Create(strURL);
httpReq.UserAgent
= HttpContext.Current.Request.ServerVariables["HTTP_USER_AGENT"].ToString();
HttpWebResponse httpRes = (HttpWebResponse)httpReq.GetResponse();

XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(httpRes.GetResponseStream());

return xmlDoc;
}

public static XmlNode ConvertToFitDateTime(XmlNode xChannel)
{
XmlNodeList xItems = xChannel.SelectNodes("item");

foreach (XmlNode xNode in xItems)
{
xNode.SelectSingleNode("pubDate").InnerText
= Convert.ToDateTime(xNode.SelectSingleNode("pubDate").InnerText).ToString("yyyy-MM-dd HH:mm:ss");
}

return xChannel;
}

public static string GetRssAddressCount()
{
string strReturn = string.Empty;

SqlConnection sqlCon = new SqlConnection(strConnection);
SqlCommand sqlCom = new SqlCommand();
sqlCom.Connection = sqlCon;
sqlCom.CommandText = "dbo.usp_GetRssAddressCount"
sqlCom.CommandType = CommandType.StoredProcedure;

sqlCon.Open();
strReturn = sqlCom.ExecuteScalar().ToString();
sqlCon.Close();

return strReturn;
}

public static DataTable GetRssAddressList()
{
DataTable dtReturn = new DataTable();

SqlConnection sqlCon = new SqlConnection(strConnection);
SqlCommand sqlCom = new SqlCommand();
sqlCom.Connection = sqlCon;
sqlCom.CommandText = "dbo.usp_GetRssAddressList"
sqlCom.CommandType = CommandType.StoredProcedure;

SqlDataAdapter sqlDa = new SqlDataAdapter(sqlCom);
sqlDa.Fill(dtReturn);

return dtReturn;
}

public static DataTable GetRssItemList(string strLink)
{
DataTable dtReturn = new DataTable();

SqlConnection sqlCon = new SqlConnection(strConnection);
SqlCommand sqlCom = new SqlCommand();
sqlCom.Connection = sqlCon;
sqlCom.CommandText = "dbo.usp_GetRssItemList"
sqlCom.CommandType = CommandType.StoredProcedure;

sqlCom.Parameters.Add("@link", SqlDbType.VarChar, 200);
sqlCom.Parameters["@link"].Value = strLink;

SqlDataAdapter sqlDa = new SqlDataAdapter(sqlCom);
sqlDa.Fill(dtReturn);

return dtReturn;
}

public static string InsertRssAddress(string strTitle, string strLink, string strDesc)
{
string strReturn = string.Empty;

SqlConnection sqlCon = new SqlConnection(strConnection);
SqlCommand sqlCom = new SqlCommand();
sqlCom.Connection = sqlCon;
sqlCom.CommandText = "dbo.usp_InsertRssChannel"
sqlCom.CommandType = CommandType.StoredProcedure;

sqlCom.Parameters.Add("@title", SqlDbType.NVarChar, 200);
sqlCom.Parameters["@title"].Value = strTitle;

sqlCom.Parameters.Add("@link", SqlDbType.VarChar, 200);
sqlCom.Parameters["@link"].Value = strLink;

sqlCom.Parameters.Add("@description", SqlDbType.NVarChar, 2000);
sqlCom.Parameters["@description"].Value = strDesc;

sqlCom.Parameters.Add("@result", SqlDbType.NVarChar, 5);
sqlCom.Parameters["@result"].Direction = ParameterDirection.Output;

sqlCon.Open();
sqlCom.ExecuteNonQuery();
sqlCon.Close();

strReturn = sqlCom.Parameters["@result"].Value.ToString();

return strReturn;
}

public static string InsertRssItems(string strLink, XmlNode xChannel)
{
string strReturn = string.Empty;

SqlConnection sqlCon = new SqlConnection(strConnection);
SqlCommand sqlCom = new SqlCommand();
sqlCom.Connection = sqlCon;
sqlCom.CommandText = "dbo.usp_InsertRssItem"
sqlCom.CommandType = CommandType.StoredProcedure;

sqlCom.Parameters.Add("@link", SqlDbType.VarChar, 200);
sqlCom.Parameters["@link"].Value = strLink;

sqlCom.Parameters.Add("@iteminfo", SqlDbType.NText);
sqlCom.Parameters["@iteminfo"].Value = xChannel.OuterXml;

sqlCom.Parameters.Add("@result", SqlDbType.NVarChar, 5);
sqlCom.Parameters["@result"].Direction = ParameterDirection.Output;

sqlCon.Open();
sqlCom.ExecuteNonQuery();
sqlCon.Close();

strReturn = sqlCom.Parameters["@result"].Value.ToString();

return strReturn;
}
}
---------------------------------------------------------------------------------------------------
<리스트 9> CommonUtility 클래스의 전체 소스 코드


Creative Commons License
저작물크리에이티브 커먼즈 코리아 저작자표시-비영리-동일조건변경허락 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
반응형
반응형

이 글은 월간 마이크로소프트웨어(일명 마소) 2008년 01월호 실전 강의실에 기고한 글입니다. 요즘 도통 포스팅을 할 수 없는 관계로 인해 대체합니다. ㅡ_ㅡ;; (하는 거 없이 바빠요~~~)



ASP.NET을 이용하여, 사용자에게 친숙한 인터페이스 프로그래밍을 제공할 수 있는 확장 도구인 ASP.NET AJAX와 개발의 편의성을 제공되는 ASP.NET AJAX 컨트롤 툴킷이 나온지도 거의 1년이 넘었다. 이러한 장점을 바탕으로 한 ASP.NET AJAX에서 제공하는 확장 컨트롤과 ASP.NET AJAX 컨트롤 툴킷에서 제공하는 컨트롤을 이용하여 간단하게나마 RSS Viewer를 만들어보도록 하겠다.

필자가 ASP.NET을 접한 지 4년이라는 시간이 흘렀다. 이 기간 동안 ASP.NET을 가지고 프로젝트를 진행하는 사이트가 상당히 증가되었다는 점은 ASP.NET이 그만큼 사용자들에게 효율적이며, 개발자들에게 개발에 필요한 용이성을 제공해준다는 것을 인정받은 셈이다. 더더욱, 요즘 추세에 맞추어서 ASP.NET AJAX라는 ASP.NET과 궁합이 잘 들어맞는 프레임워크를 사용함으로써, ASP.NET의 단점이라 할 수 있는 불필요한 화면의 깜빡임을 없애고, 사용자에게 동적이고 파워풀한 기능을 보여줄 수 있게 되었다. 이번 호에서는 이 ASP.NET과 ASP.NET AJAX를 사용하여, 간단하게나마 사용자 자신이 등록한 RSS의 정보를 볼 수 있는 프로그램을 만들어보도록 하겠다.

RSS는 무엇인가?

웹 2.0의 시대에 들어서면서, 대다수의 사용자는 자신만의 인터넷 공간인 블로그를 가지고 있을 것이다. 사용자 자신의 자유로운 생각과 다른 인터넷 사용자들에게 필요한 정보를 포스트의 형태로 블로그에 등록하게 된다. 그래서, 인터넷 사용자가 자신에게 필요한 정보가 등록되어 있는 블로그를 찾게 되었을 때, 이를 즐겨찾기에 등록하거나 자신의 블로그로 해당 포스트를 등록하게 된다, 하지만 이러한 목록이 많아지게 되면, 정작 필요할 때 즐겨찾기에 등록한 블로그 목록을 선택해가며, 찾고자 하는 정보가 나와 있는 블로그를 다시 찾아야 하는 번거로움이 생길 수 있다. 이러한 불편함을 해소하기 위해 나온 것이 RSS이며, 이러한 RSS를 관리해 줄 수 있는 사이트 또는 프로그램을 이용하여 간편하게 정보를 찾을 수 있겠다. 필자의 경우는 한RSS를 통하여 이러한 정보를 제공하는 블로그 또는 사이트들을 관리하고 있다.


<화면 1> 한RSS 사이트

RSS의 구조는 이미 많은 사이트에서 내용을 알려주고 있고, 이 글을 읽고 있는 독자들도 어느 정도의 지식이 있다고 생각하므로, 간단하게RSS에 대한 설명을 하도록 하겠다.
RSS를 제공하고 있는 ““ASP.NET News””(http://www.asp.net/news/rss.ashx) 의 RSS 소스를 보면 다음과 같다.


<화면 2> Xml 형태로 구성되어 있는 RSS 정보

RSS는 Xml의 형태로 제공되고 있으며, 구조는 크게 요소와 요소의 하위에 속하는 으로 구분질 수 있다. <표 1>은, 요소에필요한 요소의 목록을 보여준다.


기타, 요소에 들어갈 수 있는 요소들이 더 있으나, 여기에서는 이 요소들에 대한 설명은 하지 않도록 하겠다. <표 2>는, 요소의 하위 항목들의 정의에 쓰이는 요소에 사용되는 요소의 목록을 보여준다.


대부분의 웹 사이트 또는 블로그에서 RSS에 대한 정보를 제공해줌으로, 사이트나 블로그를 사용하는 사용자들은 크게 신경을 쓰지 않아도 된다. 다만, 이러한 요소들로 RSS에 대한 정보가 제공된다는 것을 아는 것도 나쁘지는 않을 듯 하다.

데이터베이스의 생성 및 구성

먼저, ASP.NET 웹 사이트를 만들기 전에 RSS 주소를 등록, 삭제하고 RSS 주소에 들어있는 정보를 저장하기 위한 데이터베이스 및 테이블을 만들도록 하겠다. 먼저, SQL Server 2005를 실행시킨 후, 새 데이터베이스 생성 메뉴를 통해, 새로운 데이터베이스를 만들도록 한다. 데이터베이스의 명은 MASODataBase라고 한다.


<화면 3> MASO 데이터베이스의 생성

확인 버튼을 눌러 MASODataBase를 생성한다. 이제RSS 정보를 저장할 테이블을 만들도록 하겠다. 정보를 저장할RSS_CHANNEL 테이블과 정보를 저장할 RSS_ITEMS라는 2개의 테이블을 만든다. 각 테이블의 필드들은 다음과 같다.



이들 테이블의 관계는 <화면 4>와 같다.


<화면 4> RSS_CHANNEL 테이블과 RSS_ITEM 테이블의 관계

저장 프로시저의 생성 및 구성

지금 만들려고 하는 RSS Viewer 프로그램은, RSS 정보를 저장하고 읽어오는 과정을 저장 프로시저를 사용하여 처리할 것이다. 그러므로 웹 사이트를 만들기 전에 사용하는 저장 프로시저를 미리 만들어놓도록 한다. RSS Viewer 프로그램에 사용되는 저장 프로시저는 총 5개이며, 각 저장프로시저의 이름은 다음과 같다.

- dbo.usp_GetRssAddressCount
- dbo.usp_GetRssAddressList
- dbo.usp_GetRssItemList
- dbo.usp_InsertRssChannel
- dbo.usp_InsertRssItem

이제부터, 위에 나열한 저장 프로시저에 대한 설명을 하도록 하겠다. 우선, 첫 번째 저장 프로시저인 dbo.usp_GetRssAddressCount는 RSS_CHANNEL 테이블에 등록되어 있는 RSS 주소의 카운트를 가지고 오는 저장 프로시저로 <리스트 1>과 같이 구성되어 있다.

---------------------------------------------------------------------------------------------------
CREATE PROCEDURE [dbo].[usp_GetRssAddressCount]
AS
BEGIN
SELECT ISNULL(COUNT(channelid), 0) AS CNT
FROM dbo.RSS_CHANNEL WITH(NOLOCK)
END
---------------------------------------------------------------------------------------------------
<리스트 1> dbo.usp_GetRssAddressCount 저장 프로시저

두 번째 저장 프로시저인 dbo.usp_GetRssAddressList는 RSS_CHANNEL 테이블에 등록되어 있는 정보들을 반환하는 역할을 하며, <리스트 2>와 같이 구성되어 있다.

---------------------------------------------------------------------------------------------------
CREATE PROCEDURE [dbo].[usp_GetRssAddressList]
AS
BEGIN
SELECT channelid, title, link
FROM dbo.RSS_CHANNEL WITH(NOLOCK)
END
---------------------------------------------------------------------------------------------------
<리스트 2> dbo.usp_GetRssAddressList 저장 프로시저

세 번째 저장 프로시저인 dbo.usp_GetRssItemList는 @link 파라미터를 통해, RSS_CHANNEL 테이블에 등록되어 있는 link 필드에서 @link 파라미터와 동일한 channelid를 찾은 후, RSS_ITEM 테이블의 channelid 필드의 값과 동일한 channelid 값을 가지는 행들의 정보를 반환하는 역할을 하며, <리스트 3>과 같이 구성되어 있다.

---------------------------------------------------------------------------------------------------
CREATE PROCEDURE [dbo].[usp_GetRssItemList]
(
@link VARCHAR(200)
)
AS
BEGIN
DECLARE @channelid INT

IF @link = ''
BEGIN
SELECT @channelid = (SELECT TOP 1 ISNULL(channelid, 0)
FROM dbo.RSS_CHANNEL WITH(NOLOCK) ORDER BY channelid ASC)
END
ELSE
BEGIN
SELECT @channelid = (SELECT ISNULL(channelid, 0)
FROM dbo.RSS_CHANNEL WITH(NOLOCK) WHERE link = @link)
END

IF @channelid <> 0
BEGIN
SELECT title, link, description, pubdate
FROM dbo.RSS_ITEM WITH(NOLOCK)
WHERE channelid = @channelid
ORDER BY itemid ASC
END
END
---------------------------------------------------------------------------------------------------
<리스트 3> dbo.usp_GetRssItemList 저장 프로시저

네 번째 저장 프로시저인 dbo.usp_InsertRssChannel 저장 프로시저는 새로운 RSS 주소를 추가할 때의 RSS 주소의 기본 정보를 저장하는 저장 프로시저로, <리스트 4>와 같이 구성되어 있다.

---------------------------------------------------------------------------------------------------
CREATE PROCEDURE [dbo].[usp_InsertRssChannel]
(
@title NVARCHAR(200)
, @link VARCHAR(200)
, @description NVARCHAR(2000)
, @result VARCHAR(5) OUTPUT
)
AS
BEGIN
IF EXISTS ( SELECT link FROM dbo.RSS_CHANNEL WITH(NOLOCK) WHERE link = @link )
BEGIN
SET @result = 'EXIST'
END
ELSE
BEGIN
INSERT INTO dbo.RSS_CHANNEL WITH(ROWLOCK) (title, link, description)
VALUES (@title, @link, @description)

SET @result = 'OK'
END
END
---------------------------------------------------------------------------------------------------
<리스트 4> dbo.usp_InsertRssChannel 저장 프로시저

마지막 저장 프로시저인 dbo.usp_InsertRssItem는 dbo.usp_InsertRssChannel 저장 프로시저와 마찬가지로, 새롭게 추가된 RSS 주소에 속한 Item의 기본 정보를 저장하는 저장 프로시저로, <리스트 5>와 같이 구성되어 있다.

---------------------------------------------------------------------------------------------------
CREATE PROCEDURE [dbo].[usp_InsertRssItem]
(
@link VARCHAR(200)
, @iteminfo NTEXT
, @result VARCHAR(5) OUTPUT
)
AS
BEGIN
DECLARE @channelid INT
DECLARE @pDoc INT

SELECT @channelid = (SELECT channelid FROM dbo.RSS_CHANNEL WITH(NOLOCK)
WHERE link = @link)

EXEC sp_xml_preparedocument @pDoc OUTPUT, @iteminfo

INSERT INTO dbo.RSS_ITEM WITH(ROWLOCK)
(channelid, title, link, description, pubdate)
SELECT @channelid, title, link, description, pubdate
FROM OPENXML (@pDoc, '//channel/item', 2)
WITH (
title NVARCHAR(200) 'title'
, link VARCHAR(200) 'link'
, description NTEXT 'description'
, pubdate DATETIME 'pubDate' )

EXEC sp_xml_removedocument @pDoc

SET @result = 'OK'
END
---------------------------------------------------------------------------------------------------
<리스트 5> dbo.usp_InsertRssItem 저장 프로시저

저장 프로시저를 만들었다면, 이제 RSS Viewer를 만들기 위한 ASP.NET AJAX 웹 사이트를 만들어보도록 하자.


Creative Commons License
저작물크리에이티브 커먼즈 코리아 저작자표시-비영리-동일조건변경허락 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
반응형
반응형

웹 사이트를 개발하다 보면 모든 페이지에 공통적으로 포함되는 부분이 있기 마련이다.
일단 간단히 생각해 보면 아래와 같은 공통 요소가 떠오른다.
1. 페이지 상단 : 회사 로고나 대표 이미지 및 주 메뉴
2. 페이지 하단 : Copyright 정보나 바로가기 등
 
일반적인 웹 사이트의 모든 페이지는 위의 2가지 요소를 공통적으로 포함하게 된다.
 
아래 그림을 보자.

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
이 처럼 모든 페이지에 공통적으로 포함되는 부분을 위해 예로부터(?) 다양한 방법들이 동원되었었다.
 
그간 사용되어진 방법들을 몇 가지만 살펴 보자.
 
1. Include 방식
 - ASP 시절부터 많이 사용되어진 방법이다. (물론 아직까지 많은 사이트가 Include 방식을 이용하고 있다)
   공통부분을 별도의 파일로 생성한 후 이 파일을 모든 페이지에 인클루딩 시키는 방법이다.
   다음 샘플코드는 top.inc , copyright.inc 라는 Include 용 파일을 생성한 후 일반 페이지에서 인클루딩 하는 코드이다.
  

= Include 파일 =

top.inc

<table width=100% ID="Table1" height="100%">

        <tr>

               <td>회사로고</td>

        </tr>

</table>

copyright.inc

<table width=100% ID="Table1" height="100%">

        <tr>

               <td>Copyright</td>

        </tr>

</table>

 

= 일반 페이지 =

<table border=1 width = 80% height=90%>

             <tr>

                          <td>

                                        <!-- #include file = "./Include/top.inc" -->

                          </td>

             </tr>

             <tr height=80%>

                          <td>페이지별 내용(Inlude 페이지)</td>

             </tr>

             <tr>

                          <td>

                                        <!-- #include file = "./Include/copyright.inc" -->

                          </td>

             </tr>

</table>

 

 
2. 웹 UserControl 방식
 - .NET 은 웹사이트의 이러한 요구사항을 위한 해결책으로 UserControl 이라는 걸 지원하게 되었다.
    큰 흐름은 Include 방식과 유사하나 .NET 의 특성을 그대로 계승한 형태로 진화 되었다.
    공통부분을 별도의 UserControl로 생성한 후 이 컨트롤을 모든 페이지에 Register(등록) 시키는 방법이다
    다음 샘플코드는 top.ascx, copyright.ascx 라는 UserControl파일을 생성한 후 일반 페이지에서 등록 하는 코드이다.
  

= UserControl 파일 =

top.ascx

<%@ Control Language="c#" AutoEventWireup="false" Codebehind="top.ascx.cs" Inherits="WebApplication1.UserControl.top" TargetSchema="http://schemas.microsoft.com/intellisense/ie5"%>

<table width=100% ID="Table1" height="100%">

        <tr>

               <td>회사로고</td>

        </tr>

</table>

copyright.inc

<%@ Control Language="c#" AutoEventWireup="false" Codebehind="copyright.ascx.cs" Inherits="WebApplication1.UserControl.copyright" TargetSchema="http://schemas.microsoft.com/intellisense/ie5"%>

<table width=100% ID="Table1" height="100%">

             <tr>

                           <td>Copyright</td>

             </tr>

</table>

 

 

= 일반 페이지 =

<%@ Register TagPrefix="uc1" TagName="top" Src="UserControl/top.ascx" %>

<%@ Register TagPrefix="uc1" TagName="copyright" Src="UserControl/copyright.ascx" %>

<table border="1" width="80%" height="90%">

             <tr>

                          <td>

                                        <uc1:top id="Top1" runat="server"></uc1:top>

                          </td>

             </tr>

             <tr height="80%">

                          <td>페이지별 내용(UserControl 페이지)</td>

             </tr>

             <tr>

                          <td>

                                        <uc1:copyright id="Copyright1" runat="server"></uc1:copyright>

                          </td>

             </tr>

</table>

 
  
3. HttpModule 방식
 - 조금은 생소할 수도 있으나 닷넷의 요청과 응답사이에 ISAPI 필터처럼 동작하는 HttpModule 로도 이와 같은 공통처리를 할 수도 있다.
   물론 HttpModule 가 UI 의 공통요소를 처리하기 위한 메터니즘과는 어울리지 않는다(일종의 필터라고 생각하면 된다)
   그러나 이번 글에서는 이러한 방식도 있다는 차원에서 언급하도록 한다.
   동작방식을 간략히 설명하자면,
   요청의 처음과 끝에 개입하여 특정 로직을 수행하도록 하는데 이 부분에 공통 UI 를 기술하게 된다.
   다음 샘플코드는 IHttpModule 를 구현하는 CustomHttpModule 클래스를 만들고 이 모듈이 모든 페이지 요청에 개입하도록 하는 코드이다.
   
 

= CustomerHttpModule 클래스 =

public class CustomHttpModule : IHttpModule

{

        public CustomHttpModule(){}

        public void Init(HttpApplication context)

        {

               context.BeginRequest += (new EventHandler(this.Application_BeginRequest));

               context.EndRequest += (new EventHandler(this.Application_EndRequest));   

        }

        public void Application_BeginRequest(object sender,EventArgs e)

        {

               HttpApplication application = (HttpApplication)sender;

               HttpContext context = application.Context;

               context.Response.Write("<table width=80% border=1><tr><td>회사로고</td></tr></table>");               

        }

        public void Application_EndRequest(object sender,EventArgs e)

        {

               HttpApplication application = (HttpApplication)sender;

                       HttpContext context = application.Context;

                       context.Response.Write("<table width=80% border=1><tr><td>Copyright</td></tr></table>");                    

        }              

}

 

 

= Web.Config 설정=

<httpModules>

        <add name="CustomHttpModule"  type="WebApplication1.CustomHttpModule, WebApplication1" />    

</httpModules>

 

이렇게 Web.Config 에 설정하면 모든 요청은 CustomHttpModule 모듈에 의해 필터링 된다.

 

 
4. MasterPage 방식(닷넷 2.0 에 새로 추가된 기능)
 - 드디어 주인공이 등장한다. 이전까지의 액스터라들은 MasterPage 를 위한 들러리(?)일 뿐이었다 --;
   닷넷 2.0 에서는 보다 근본적인 해결책과 다른 접근 방법으로 이와 같은 요구사항을 충족시키는 매커니즘을 새로 내놓았다.
   이것이 바로 Master Page 라는 것이다.
   Master Page 는 이전 방법들과는 다른 접근 방법을 채택하고 있따.
   이전 방식에서는 공통되는 부분을 별도로 분리하여 모든 페이지에 명시적으로 포함하도록 코딩했었다.
   그러나 Master Page 에서는 주/객이 바뀌었다고 보면 된다.
   Master Page 가 전체 골격을 유지하며 공통요소를 포함하게 되며 일반페이지들은 이 Master Page 를 Content 페이지로써 동작하도록 한다.
   다음 샘플코드는 MasterPage.master 이라는 마스터 페이지를 정의하고 Content 로 생성되는 일반페이지에 대한 코드 이다.

 

= Master 페이지 =

<%@ Master Language="C#" AutoEventWireup="true" CodeFile="MasterPage.master.cs" Inherits="MasterPage" %>

<table border="1" width="80%" height="90%">

        <tr>

                <td>

                       <table width="100%" height="100%">

                               <tr>

                                      <td>회사로고</td>

                               </tr>

                       </table>                                            

                </td>

        </tr>

        <tr height="300">

                <td >

                    <asp:contentplaceholder id="ContentPlaceHolder1" runat="server">

                     </asp:contentplaceholder>

                </td>

        </tr>

        <tr>

                <td>

                       <table width="100%" height="100%">

                               <tr>

                                      <td>Copyright</td>

                               </tr>

                       </table>

                </td>

        </tr>

</table>

 

 

= 일반 페이지 =

<%@ Page Language="C#" MasterPageFile="~/MasterPage.master" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" Title="Untitled Page" %>

<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">

페이지별 내용(일반 페이지)

</asp:Content>

 

마스터 페이지를 이용하는 Content 페이지는 VS IDE의 디자인모드에서 다음처럼 표시된다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

MasterPage 에서 정의한 상,하단의 내용(회사로고 및 Copyright) 는 비활성화 된 모습이다.

그리고 실제 페이지의 Content 를 위한 영역에 각 페이지별 내용이 기술되도록 한다.

 

 

이로써 페이지 공통요소를 효율적으로 처리하는 몇가지 방법들에 대해서 살펴 보았다.

물론 이 글에서 소개하지 않은 또 다른 방식도 존재하리라 본다.

개인적인 생각으로는 닷넷 2.0에서 제공하는 Master Page 가 다른 모든 방법들 보다 장점을 많이 지닌 듯 하다

(예를 들면 일반페이지들은 공통부분에 대한 어떠한 처리에도 신경쓰지 않아도 된다는 점 등...)

 

Master Page  에 대해서는 기회가 되면 다시 정리하도록 하겠다.

 

 

첨부파일에 이 글에서 사용된 샘플코드가 포함되어 있다.

반응형
반응형

Cookie 제대로 알고 사용하자 』

 

Cookie 는 아주 보편적이고 유명(?)하기 까지 하다.

웹 개발자는 물론이고 일반 웹 유저들 마저 쿠키에 대해 어느 정도는 알고 있다.

비 연결 지향인 웹 환경에서 사용자(일반적으로 웹 브라우저가 되겠다)와 웹 서버간의 특정 데이터의 상태 정보 저장을 위해 사용하는 하나의 기법이다.

 

이번 아티클은 이렇게 보편적인 쿠키를 개발단에서 사용할 때 참고할 만한 내용들을 정리해 본다.

 

1. 쿠키는 어디에 저장되는가?

   일반적으로 쿠키는 아래의 경로에 TXT 파일 형태로 저장이 된다.

 C:\Documents and Settings\현재로그인한 사용자\Cookies

 그러나 서버에서 쿠키의 만료 일자를 지정하지 않았다면 파일로 저장되지 않고 사용자 세션정보의 일부로써 유지된다.

 즉 브라우저 기반 쿠키로써 브라우저를 닫으면 쿠키 역시 소멸되는 것이다

 쿠키를 파일로 저장하지 않는 것은 보안상 이슈로 더 좋은 선택이 될 수 있다.

 결론적으로, 쿠키를 설정할 때 만료일자의 지정 유/무에 따라 파일기반 쿠키 또는 세션(브라우저)기반 쿠키로 저장되는 것이다

 

 참고> 만일 파일기반 쿠키의 경우 동일한 이름의 쿠키를 저장할 경우 이전 쿠키파일을 덮어 쓰게 된다

 

2. 쿠키의 제한 사항

   웹 서버가 클라이언트에게 쿠키를 저장 할 경우 대부분 아래와 같은 제약 사항을 가진다.

   1) 하나의 사이트에서 클라이언트에게 저장할 수 있는 최대 쿠키는 20까지 이다.       

   2) 최대 4096 byte 쿠키 지원

 

    쿠키의 수적 제한과 최대 크기의 제한 사항은 하위키 형태의 쿠키로 해결 할 수 있다.

, 만일 한 사이트에서 20개 이상의 쿠키가 필요하다면 하위 키를 포함한 쿠키를 사용하면 더 많은 쿠키를 저장하는 효과를 볼 수 있다.

    Cookie[“Key”][“하위키1”]; Cookie[“Key”][“하위키2”];

    이에 대한 MSDN 내용은 다음과 같다.

   

하위 키를 포함하는 쿠키를 사용하면 쿠키 파일의 크기를 제한하는 데에도 도움이 됩니다.

일반적으로 쿠키는 4096바이트로 제한되고 각 사이트마다 최대 20개의 쿠키를 저장할 수 있습니다.

하위 키를 포함하는 단일 쿠키를 사용하면 사이트에 할당된 20개의 쿠키보다 적은 수의 쿠키를 사용하게 됩니다.

또한 단일 쿠키에는 오버헤드(만료 정보 등)에 대한 50개 정도의 문자와 쿠키에 저장되는 값의 길이가 포함됩니다.

이 값은 모두 4096바이트 제한에 거의 근접한 값입니다. 5개의 쿠키를 개별적으로 저장하는 대신 하위 키 5개를 저장하면 개별 쿠키로 인한 오버헤드를 줄여 약 200바이트를 절약할 수 있습니다.

 

 

3. 쿠키 샘플

   이제 ASP.NET 에서 쿠키를 저장하고 불러오는 샘플 코드를 보자.

1)       쿠키 저장하기
아래 샘플 코드는 하위키를 가진 쿠키를 저장하는 두 가지 방법을 나타낸다

Response.Cookies["MyCookie"]["Cookie1"] = "1";

Response.Cookies["MyCookie "][" Cookie2"] = “2”;

Response.Cookies["MyCookie "].Expires = DateTime.Now.AddDays(1);

 

HttpCookie aCookie = new HttpCookie("MyCookie ");

aCookie.Values["Cookie1"] = " 1";

aCookie.Values["Cookie2"] = “2”

aCookie.Expires = DateTime.Now.AddDays(1);

Response.Cookies.Add(aCookie);

 

2)       쿠키 불러오기

아래 샘플 코드는 하위키를 가진 쿠키를 불러오는 방법을 나타낸다

샘플 처럼 해당 쿠키가 존재하는지 여부를 체크 하는 것이 좋다.(그렇지 않으면 ‘NullReferenceException’ 이 발생할 수 있다)

if(Request.Cookies["MyCookie "] != null)

{

    Label1.Text =

        Server.HtmlEncode(Request.Cookies["MyCookie "][" Cookie1"]);

 

    Label2.Text =

        Server.HtmlEncode(Request.Cookies["MyCookie "][" Cookie2"]);

}

 

    MSDN : 쿠키 값을 표시하기 전에 HtmlEncode 메서드를 호출하여 쿠키 내용을 인코딩했습니다. 이렇게 하면 악의적인 사용자가 실행 스크립트를 쿠키에 추가할 수 없습니다.

 

3)       모든 쿠키 읽기

현재 나의 사이트에서 저장한 모든 쿠키를 알아 볼 수 있는 샘플 코드이다. (MSDN)

for(int i=0; i<Request.Cookies.Count; i++)

{

    aCookie = Request.Cookies[i];

    output.Append("Name = " + aCookie.Name + "<br />");

    if(aCookie.HasKeys)

    {

        for(int j=0; j<aCookie.Values.Count; j++)

        {

            subkeyName = Server.HtmlEncode(aCookie.Values.AllKeys[j]);

            subkeyValue = Server.HtmlEncode(aCookie.Values[j]);

            output.Append("Subkey name = " + subkeyName + "<br />");

            output.Append("Subkey value = " + subkeyValue +

                "<br /><br />");

        }

    }

    else

    {

        output.Append("Value = " + Server.HtmlEncode(aCookie.Value) +

            "<br /><br />");

    }

}

Label1.Text = output.ToString();

 

 

4. 쿠키 (유효) 범위 제어

   쿠키를 저장 하고 난 후 사용할 때 (불러올 때) 에 유요한 범위를 아래와 같이 두 가지 방법으로 제어 할 수 있다.

 

1)       디렉터리 단위 제어

웹 서버의 특정 디렉터리(폴더)를 기준으로 쿠키의 범위를 제어 할 수 있다.

ASP.NET HttpCookie 에는 Path 속성이 이와 관련된 속성이다.

만일 Path = “/Directory1” 로 설정된 쿠키라면 웹 서버의 ‘/Directory1’ 의 하위 폴더 및 가상 디렉터리에서만 쿠키 값을 읽어 들일 수 있게 되는 것이다.

이외의 폴더 및 루트에 속한 페이지에서는 쿠키를 사용할 수 없다.

       Path 속성이 설정되지 않으면 기본적으로 웹 루트인  ‘/’ 가 설정되므로 해당 웹 사이트에서는 모두 사용 가능하게 되는 것이다.

       참고> 일부 브라우저 에서는 경로의 대/소문자를 구분한다. URL Path 속성이 정확히 대/소문자가 일치해야 쿠키를 사용할 수 있다.

 

2)       도메인 단위 제어

도메인 단위로도 범위 설정이 가능하다.

만일 특정 하위 도메인 에서만 사용가능한 쿠키를 만들고 싶으면 Domain 속성을 지정해 주면 된다.

Mkexp.pe.kr 사이트의 하위 도메인인 sub.mkex.pe.kr 에서만 쿠키를 사용하고 싶으면 Domain = “sub.mkex.pe.kr” 로 설정하면 된다.

또한 mkex.pe.kr 의 모든 하위도메인에서 사용가능 한 쿠키를 만들고 싶으면 Domain = “mkex.pe.kr” 로 설정하면 된다

   

 

 

5. 쿠키 허용 유/

   클라이언트는 서버에서 제공하는 쿠키를 허용할 지 결정 할 수있다.

   아래 그림은 IE 6.0 에서 쿠키를 차단하는 모습니다.

  

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

이렇게 되면 웹 서버에서 쿠키를 사용하고 싶어도 할 수 없다.

, 웹 서버에 쿠키 저장 시 오류는 발생하지 않는다. 다만 클라이언트는 쿠키를 저장하지 않으며 서버 요청 시 쿠키 값도 전달하지 않는 것이다.

 

따라서 쿠키 의존도가 상당히 강한 사이트의 경우 클라이언트의 쿠키 허용 유/무를 판단할 필요가 있다.

그러나 클라이언트의 브라우저가 쿠키를 허용 했는지 안 했는지 한방에 체크 할 수는 없다.

 

Msdn : Cookies 속성은 쿠키 사용 여부를 나타내지 않습니다. 대신 현재 브라우저에서 기본적으로 쿠키를 지원하는지 여부만 나타냅니다.

 

따라서 msdn 에서 이를 체크 하기 위해 조금은 무식한(?) 방법을 사용한다.

시나리오는 이렇다.

쿠키를 클라이언트에 저장해 한 후 다시 읽어 들여서 읽히면 쿠키가 허용된 것이고 아니면 쿠키가 불허용 된 것이라 판단하는 것이다.

아래는 msdn 의 샘플 코드이다.

//첫 번째 페이지(쿠키를 작성하고 두 번째 페이지로 리다이렉트 한다.

protected void Page_Load(object sender, EventArgs e)

{

    if (!Page.IsPostBack)

    {

        if (Request.QueryString["AcceptsCookies"] == null)

        {

            Response.Cookies["TestCookie"].Value = "ok";

            Response.Cookies["TestCookie"].Expires =

                DateTime.Now.AddMinutes(1);

            Response.Redirect("TestForCookies.aspx?redirect=" +

                Server.UrlEncode(Request.Url.ToString()));

        }

        else

        {

            Label1.Text = "Accept cookies = " +

                Server.UrlEncode(

                Request.QueryString["AcceptsCookies"]);

        }

    }

}

 

//두 번째 페이지 (쿠키를 읽어보고 허용 유/무를 판단하여 다시 첫번째 페이지로 이동한다)

protected void Page_Load(object sender, EventArgs e)

{

    string redirect = Request.QueryString["redirect"];

    string acceptsCookies;

    if(Request.Cookies["TestCookie"] ==null)

        acceptsCookies = "no";

    else

    {

        acceptsCookies = "yes";

        // Delete test cookie.

        Response.Cookies["TestCookie"].Expires =

            DateTime.Now.AddDays(-1);

    }

    Response.Redirect(redirect + "?AcceptsCookies=" + acceptsCookies,

    true);

}

 

Msdn 이 이러한 두 페이지에 걸친 처리보다는 클라이언트 스크립트로 처리하는 것이 더 좋을 때도 있다.

다음처럼

<script language="JavaScript">

<!--

function ReadCookie(cookieName) {

var theCookie=""+document.cookie;

var ind=theCookie.indexOf(cookieName);

if (ind==-1 || cookieName=="") return "";

var ind1=theCookie.indexOf(';',ind);

if (ind1==-1) ind1=theCookie.length;

return unescape(theCookie.substring(ind+cookieName.length+1,ind1));

}

 

function SetCookie(cookieName,cookieValue,nDays) {

var today = new Date();

var expire = new Date();

if (nDays==null || nDays==0) nDays=1;

expire.setTime(today.getTime() + 3600000*24*nDays);

document.cookie = cookieName+"="+escape(cookieValue)

                 + ";expires="+expire.toGMTString();

}

//-->

</script>

 

<script language="JavaScript">

<!--

testValue=Math.floor(1000*Math.random());

SetCookie('AreCookiesEnabled',testValue);

if (testValue==ReadCookie('AreCookiesEnabled'))

     document.write('<b>현재고객님의 브라우져에는 쿠키가허용되어있습니다.</b>')

else document.write('<b>현재 고객님의 브루우져에 쿠키허용이 금지되어있습니다. </b>')

//-->

</script>

 

이 스크립트 코드는 asp.net 에서의 체크 시나리오와 동일하다. 단지 클라이언트에서 실행되는 것이라서 불 필요한 라운드 트립 이 발생하지 않는다는 장점이 있다.

샘플 코드의 해석은 중요치 않다. 단지 이러한 방법으로 확인하는 시나리오가 있을 수 있다는 것이 중요하다.

 

 

6. 기타 참고 사항

 쿠키는 http 프로토콜의 헤더에 포함되어 진다.

 

  아래 그림은 쿠키를 저장할 때의 http 헤더 정보이다.

 

 

 

 

 

 

 

 

 

 

 

그리고 다음은 클라이언트가 쿠키 값을 서버로 전송하는 http 헤더 정보이다.

 

 

 

 

 

 

 

 

 

 

 

자세한 http 헤더 정보는 다음 링크에서 확인 하자.

è      HTTP 프로토콜 - http 헤더

 

 

또한 하위 키가 있는 쿠키의 정보 변경 시 주의할 사항에 대해서는 다음 링크에서 확인 하자.

è      ASP.NET에서 다중쿠키 수정 주의점

 

 

7. 쿠키.. 보안에 유의 하라.

   쿠키는 정보 유지를 위해 클라이언트에 의존 하는 편하고도 좋은 방법이다.

   그러나 모든 것은 얻는 것이 있으면 잃는 것 또한 있는 법이다.

   기본적으로 쿠키는 클라이언트 pc txt 파일 형태로 저장되기 때문에 보안상 좋지 않다.

   만일 중요한 정보를 평문 형태로 저장 한다면 이건 거의 자살행위나 마찬 가지다.

   또한 적절한 암호화를 수행했다고 하여도 안정성은 보장 할 수 없다.

   나아가 Key=Value 형태를 그대로 저장하는 것에도 문제가 있다.

  

   가장 기본은 중요한 정보는 쿠키에 의존하면 안 된다 이다.

   그러나 만일 불가피하게 약간은 중요한 정보를 쿠키에 저장해야만 할 경우에는 적절한 암호화를 수행하도록 한다.

또한 key=value 패턴을 사용하지 않도록 한다. 그리고 악의의 사용자가 특정 범위를 쉽게 알아내어 복호화 하는 것을 미연에 방지하도록 한다.

 

쿠키 변조와 관련된 내용은 아래 링크에서 확인 하자.

 

è      [ 보안 시리즈] 6. 데이터 변조 쿠키 변조

반응형
반응형

PRB: ASP.NET 응용 프로그램이 이벤트 로그에 새로운 EventSource를 쓰려고 하면 "요청한 레지스트리에 액세스할 수 없습니다" 오류 메시지 발생
 
 
현상
ASP.NET을 사용하여 이벤트 로그에 새로운 이벤트 원본을 만들려고 하면 다음 오류 메시지가 나타날 수 있습니다.
System.Security.SecurityException: 요청한 레지스트리에 액세스할 수 없습니다.
 
 
원인
기본적으로 ASP.NET 작업자 프로세스의 사용자 토큰은 ASPNET(또는 인터넷 정보 서비스[IIS] 6.0에서 실행되는 응용 프로그램의 경우 NetworkService)입니다. "현상" 절의 문제는 사용자 계정이 이벤트 원본을 만들기 위한 올바른 사용자 권한을 갖고 있지 않기 때문에 발생합니다.
 
 
해결 방법
경고: 레지스트리 편집기를 잘못 사용하면 심각한 문제가 발생할 수 있으며 문제를 해결하기 위해 운영 체제를 다시 설치해야 할 수도 있습니다. Microsoft는 레지스트리 편집기를 잘못 사용함으로써 발생하는 문제에 대해 해결을 보증하지 않습니다. 레지스트리 편집기의 사용에 따른 모든 책임은 사용자에게 있습니다. 이 문제를 해결하려면 관리자 권한이 있는 사용자가 ASP.NET 웹 응용 프로그램을 실행하기 전에 이벤트 원본을 만들어야 합니다. 이벤트 원본을 만들려면 다음 방법 중 하나를 사용하십시오.

첫 번째 방법
레지스트리 편집기의 Application 이벤트 로그 아래에 이벤트 원본을 만듭니다. 이렇게 하려면 다음과 같이 수행하십시오. 1. 시작을 누르고 실행을 누릅니다.
2. 열기 입력란에 regedit를 입력합니다.
3. 다음 레지스트리 하위 키를 찾습니다.
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\Application
4. Application 하위 키를 마우스 오른쪽 단추로 누른 다음 새로 만들기를 가리킨 다음 키를 누릅니다.
5. 키 이름으로 TEST를 입력합니다.
6. 레지스트리 편집기를 닫습니다.
 
두 번째 방법
System.Diagnostics 네임스페이스의 EventLogInstaller 클래스를 사용하면 응용 프로그램이 실행되는 동안 읽거나 쓰는 이벤트 로그를 설치하고 구성할 수 있습니다. EventLogInstaller를 사용하여 이벤트 원본을 만들 수 있습니다. 이렇게 하려면 다음과 같이 수행하십시오. 1. Microsoft Visual Basic .NET 또는 Microsoft Visual C# .NET을 사용하여 EventLogSourceInstaller라는 새로운 클래스 라이브러리를 만듭니다. 기본적으로 Class1.vb 파일이나 Class1.cs 파일이 만들어집니다.
2. 솔루션 탐색기에서 EventLogSourceInstaller를 마우스 오른쪽 단추로 누른 다음 참조 추가를 누릅니다.
3. 참조 추가 대화 상자에서 System.Configuration.Install.dll을 두 번 누른 다음 확인을 누릅니다.
4. Class1.vb\Class1.cs를 MyEventLogInstaller.vb\MyEventLogInstaller.cs로 이름을 바꿉니다.
5. MyEventLogInstaller.vb 또는 MyEventLogInstaller.cs의 기존 코드를 다음 예제 코드로 바꿉니다.
Visual Basic .NET 예제Imports System.Diagnostics
Imports System.Configuration.Install
Imports System.ComponentModel
<RunInstaller(True)> _
Public Class MyEventLogInstaller
    Inherits Installer
    Private myEventLogInstaller As EventLogInstaller
    Public Sub New()
        ' Create an instance of 'EventLogInstaller'.
        myEventLogInstaller = New EventLogInstaller()
        ' Set the 'Source' of the event log, to be created.
        myEventLogInstaller.Source = "TEST"
        ' Set the 'Log' that the source is created in.
        myEventLogInstaller.Log = "Application"
        ' Add myEventLogInstaller to 'InstallerCollection'.
        Installers.Add(myEventLogInstaller)
    End Sub
End Class
Visual C# .NET 예제using System;
using System.Diagnostics;
using System.ComponentModel;
using System.Configuration.Install;

namespace EventLogSourceInstaller
{
 [RunInstaller(true)]
 public class MyEventLogInstaller : Installer
 {
  private EventLogInstaller myEventLogInstaller;
  public MyEventLogInstaller()
  {
   //Create Instance of EventLogInstaller
   myEventLogInstaller = new EventLogInstaller();
   // Set the Source of Event Log, to be created.
   myEventLogInstaller.Source = "TEST";
   // Set the Log that source is created in
   myEventLogInstaller.Log = "Application";
      // Add myEventLogInstaller to the Installers Collection.
   Installers.Add(myEventLogInstaller);
  }
 }
}

6. 빌드 메뉴에서 솔루션 빌드를 눌러 EventLogSourceInstaller.dll을 만듭니다.
7. Visual Studio .NET 명령 프롬프트를 엽니다.
8. 명령 프롬프트에서 EventLogSourceInstaller.dll이 있는 폴더로 변경합니다.
9. 다음 명령을 실행하여 아래 EventSource를 만듭니다.
InstallUtil EventLogSourceInstaller.dll
 

참조
자세한 내용을 보려면 다음 Microsoft 웹 사이트를 방문하십시오.
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vbcon/html/vbwlkWalkthroughCreatingEventLogInstallers.asp (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vbcon/html/vbwlkWalkthroughCreatingEventLogInstallers.asp)
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemdiagnosticseventlogclasstopic.asp (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemdiagnosticseventlogclasstopic.asp)
반응형
반응형

1. 서론
 
대부분의 브라우저에서는 한 도메인당 사용할 수 있는 쿠키명의 개수가 20개로 제한되어 있기 때문에
부득이하게 많은 개수의 쿠키명을 사용할 수 밖에 없는 경우라면 다중 쿠키를 사용하는 것도 한 방법이 될 수 있다.
 
다중 쿠키를 사용하는 경우, ASP와 ASP.NET에서 다중 쿠키를 다루는 방법이 다소 차이가 있어
이에 대해 정리하고자 한다.
 
일반적으로 ASP에서의 다중 쿠키 사용은 다음과 같다
 
[쿠키 입력]
Response.Cookies("쿠키명").Domain = "mkex.pe.kr"
Response.Cookies("쿠키명")("하위키1") = "값1"
Response.Cookies("쿠키명")("하위키2") = "값2"
 
[쿠키 읽기]
Response.Write(Request.Cookies("쿠키명")("하위키1"))
Response.Write(Request.Cookies("쿠키명")("하위키2"))
 
다중 쿠키로 쿠키 값을 입력하는 경우 헤더에는 다음과 같이 쿠키 정보가 기록된다.
 
Cookie Header : 쿠키명=하위키1=값1&하위키2=값2;...
 
ASP.NET에서도 이와 같은 방식으로 쿠키를 다룰 수 있다.
 
[쿠키 입력]
Response.Cookies["쿠키명"].Domain = "mkex.pe.kr";
Response.Cookies["쿠키명"]["하위키1"] = "값1";
Response.Cookies["쿠키명"]["하위키2"] = "값2";
 
[쿠키 읽기]
Response.Write(Request.Cookies["쿠키명"]["하위키1"]);
Response.Write(Request.Cookies["쿠키명"]["하위키2"]);
 
 
 
2. 문제상황
 
하지만, 다중 쿠키에서 하나의 하위키에 대한 값을 수정하려고 한다면 ASP와 ASP.NET에서 차이점이 발생하게 된다.
 
ASP에서는 다음과 같은 방식으로 간단하게 하위키에 해당하는 값을 수정할 수 있다.
 
Response.Cookies("쿠키명")("하위키1") = "변경된값"
Cookie Header : 쿠키명=하위키1=변경된값&하위키2=값2;...
 
ASP.NET에서 위와 같은 방식으로 수정하고자 하면 다음과 같은 결과가 나타난다.
 
Response.Cookies["쿠키명"]["하위키1"] = "변경된값";
Cookie Header : 쿠키명=하위키1=변경된값;...     //하위키2와 관련된 쿠키 정보 사라짐
 
 
 
기존의 쿠키명에 해당하는 다른 하위키에 대한 쿠키 값이 모두 없어지게 된다.
ASP.NET에서는 쿠키를 NameValueCollection으로 관리하기 때문에 ASP와 같은 형식으로 쿠키를 수정하려고 하면
해당 Collection이 초기화 되어 재설정되기 때문에 기존 설정된 쿠키가 사라지게 되는 것이다.
 
 
3. 해결 방안
 
 
ASP.NET에서는 다음과 같은 방식으로 하위키의 값을 변경하여야 한다.
 
HttpCookie cookie = Request.Cookies["쿠키명"];  //가져올 쿠키명에 해당하는 쿠키 객체를 가져온다.
cookie.Domain = "mkex.pe.kr";
cookie.Values.Set("하위키1","변경된값");               //쿠키 객체의 Collection의 Set 메서드를 이용하여 하위키 값을 변경한다.
Response.Cookies.Set(cookie);                           //변경된 쿠키 객체를 다시 Response Cookie Collection에 설정한다.
 
Cookie Header : 쿠키명=하위키1=변경된값&하위키2=값2;... 
  
 
반응형

+ Recent posts

반응형