public class CheckExeTime : IHttpModule
{
    public string ModuleName // Module Name Define
    {
        get { return "CheckTime"; }
    }

    private DateTime startTime;

    public void Init(HttpApplication application)  // IhttpModule 인터페이스 구현
    {
        application.BeginRequest += new EventHandler(application_BeginRequest);
        application.EndRequest += new EventHandler(application_EndRequest);
    }
    public void Dispose() // IhttpModule 인터페이스 구현
    {
    }
    public void application_BeginRequest(object source, EventArgs e)
    {
        startTime = DateTime.Now; // 요청 시간 Check
    }

    public void application_EndRequest(object source, EventArgs e)
    {
        //현재의 HttpApplication 개체를 얻어온다
        HttpApplication app = (HttpApplication)source;
        //현재 웹 어플리케이션의 컨텍스트를 얻어온다
        HttpContext context = app.Context;

        string pageID = context.Request.CurrentExecutionFilePath.ToString(); // 요청한 PageID (*.aspx)
        string requestIP = context.Request.UserHostAddress; // 요청한 Client IP

        TimeSpan span = DateTime.Now - startTime;

        // TO DO : 출력형식을 자유롭게 코딩하면 됨(텍스트파일로 쓰기 / DB에 저장)

        context.Response.Write("");
    }
}

위 소스를 작성한 후 아래 구문을 web.config에 추가하면 asp.net 페이지가 동작을 하면 실행이 된다.

[IIS 6.0 및 IIS 7.0 기본 모드에서 HTTP 모듈 등록]


    
        
    



[통합 모드의 IIS 7.0에서 HTTP 모듈 등록]

    
        
    


 

여러 언어를 지원해야 하는 다국어 웹 사이트를 구축하기 위한 방법을 알아보자.

 

제일 쉬운 접근 방법은 각 언어에 해당하는 웹 사이트를 별도로 만드는 것이다.

그러나 이것은 접근 방법이 쉽다는 것 뿐, 개발 양적인 측면이나 유지/보수성을 생각한다면 결코 좋은 접근 방법이 아닐 것이다.

동일한 컨셉과 구조, 컨텐츠를 제공하는 웹 사이트가 단지 언어만 다르다는 이유로 쌍둥이 사이트를 새로 개발해야 한다는 문제점과

그렇게 구축된 웹 사이트의 특정 컨텐츠 추가나 변경을 해야 할 때에는 모든 웹 사이트를 다 수정해야 하는 문제점이 있다.

 

예전부터 효율적인 다국어 지원을 위해서 다양한 방법들이 개발자들에 의해 구현되어 왔었는데..

가장 보편적으로는 다국어 처리를 위한 일종의 유틸리티 성 모듈을 거쳐 처리하는 방식이 대부분 이었다.

 

이번 글에서는 닷넷이 제공하는 표준 리소스 메커니즘을 이용해 다국어 지원을 효율적으로 하는 방법을 알아 본다.

 

ASP.NET 1.x 2.0 모두 리소스를 이용할 수 있다.

닷넷의 리소스에 대한 개념은 다음 글에서 확인해 주길 바란다.

리소스(Resource)

 

ASP.NET 1.1 에서는 리소스를 이용한 다국어처리를 위해 ResourceManager 를 이용했었다.

그러나 ASP.NET 2.0에서는 보다 관리용이하고 개발용이 하도록 개선된 부분이 있다.

 

 

전역(Global) 리소스 파일 / 지역(Local) 리소스 파일

ASP.NET 2.0 에서는 리소스 파일의 사용 범위에 따라 전역,지역 두 가지로 분리되었다.

App_Code App_Data 처럼 리소스 파일 역시 예약된 폴더를 가지고 있다.

 

전역 리소스 파일 저장 -> App_GlobalResources

지역 리소스 파일 저장 -> App_LocalResources

 

전역 리소스는 말 그대로 웹 사이트 모든 영역에서 사용할 수 있으나

지역 리소스는 특정 단일 페이지(aspx, ascx, master) 에서만 사용가능하다.

또한 당연하겠지만 App_GlobalResources 폴더는 웹 사이트 루트에 단 1 개만 허용되지만 App_LocalResources 폴더는

웹 사이트 내의 모든 폴더에 하나 씩 존재해도 된다.

 

 

Web.Config 에 언어정보 추가하기.

언어 설정에 따른 지역화를 테스트 하기 위해 웹 사이트의 설정 파일인 Web.Config 파일에 다음과 같이 언어 정보를 기록하자.

<globalization uiCulture="en" />

일단 테스트를 위해 현재 UICulture 를 영어(en) 으로 설정하였다.

 

 

* 현재 브라우저에 설정된 기본 언어(첫 번째 언어) 를 참고해서 UICulture 를 지정하려면 ‘auto’ 를 사용하면 된다

<globalization uiCulture="auto" />

또는

<%@ Page UICulture="es" UICulture="auto" %>

 

이렇게 설정 하면 브라우저에 설정된 첫 번째 언어로 설정되게 된다

 

 

 

전역(Global) 리소스 이용하기.

웹 프로젝트에 App_GlobalResources 폴더를 생성하고 다음과 같이 리소스 파일을 생성하자.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

언어 정보가 없는 Resource.resx 기본 리소스 파일이다.

이 리소스 파일은 해당 언어의 리소스 파일이 없을 경우 대체(fallback) 리소스 파일로 사용된다.

이 기본 리소스 파일을 한국어를 위한 리소스 파일로 사용할 것이며 영어를 지원하기 위해서 Resource.en.resx 을 사용한다.

 

리소스 파일명에 포함된 언어 정보는 정확히 기입해야 한다.

런타임에 ASP.NET  에서는 CurrentUICulture 속성과 가장 많이 일치 하는 리소스 파일을 자동으로 찾아서 사용하게 된다.

이때 참조되는 정보가 리소스 파일명이 된다.

 

각 국가별 언어 표현은 아래와 같이 확인할 수 있다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

이렇게 리소스 파일이 준비 되었으면 컨트롤의 지역화를 수행해 보자.

웹 페이지에 버턴과 Localize 컨트롤을 올리고 Text 속성에 명시적으로 지역화를 수행하도록 한다.

<asp:Button ID="Button1" runat="server"

        Text="<%$ Resources:Resource, Message %>" /><br />

              

<asp:Localize ID="Localize1" runat="server"

        Text="<%$ Resources:Resource, Message %>"></asp:Localize>

 

전역 리소스 파일을 사용할 경우 다음과 같은 표현식으로 리소스 내용을 참조하게 된다.

이를 명시적 지역화라 한다.

<%$ Resource: Class , Key %>

Class 의 경우 확장자와 언어정보를 제외한 리소스 파일명이 된다. 이 예에서는 Resource 가 된다.

Key 는 리소스 파일에 기록한 Key 정보 이다. Key 에 해당하는 Value 를 가져오는 것이 되겠다.

 

Localize 컨트롤은 닷넷 2.0에 새로 추가된 컨트롤로써 Literal 컨트롤을 상속받은 컨트롤이다.

이는 웹 페이지에 고정되고 정적인 내용을 표현하기 위한 가벼운 컨트롤 즈음 될 것이다.

(사실 Literal , Label 컨트롤로도 동일한 지역화를 구현할 수 있다)

 

이렇게 다 구현되었다면 Web.Config <globalization uiCulture="en" /> 내용을 변경하면서 테스트 해 보자.

아래는 en, ko 두 가지를 테스트 해본 결과 이다.

en으로 설정하면 Resource.en.resx 가 사용될 것이며

ko 로 했을 경우에는 Resource.ko.resx 가 없으므로 기본 리소스 파일인 Resource.resx 가 대체되어 사용되었다.

 

또한 다음과 같이 전역 리소스의 내용을 출력할 수 있다.

Response.Write(this.GetGlobalResourceObject("Resource", "Message"));

 

 

지역(Local) 리소스 이용하기.

웹 프로젝트의 원하는 폴더에(웹 루트도 상관없다) App_LocalResources 폴더를 생성하고 다음과 같이 리소스 파일을 생성하자.

이전과 동일하게 기본리소스와 영어지원을 위한 리소스 두 개를 생성하고 내용을 Key-Value 형태로 기록하였다.

 

주의 해야 할 것은 지역(Local) 리소스의 경우 특정 한 페이지만을 위한 리소스 이므로 페이지명과 연결 시켜서 리소스 파일명이 부여되어야 한다.

현재 이 예제에서는 Local.aspx.resx 리소스라는 말은 Local.aspx 라는 페이지를 위한 리소스 파일이 되는 것이다.

또한 닷넷 컨트롤의 속성과 리소스 파일의 Key 이름과도 연관성이 있는데, 이 예제에서는

ButtonResource.Text 라 했으므로 컨트롤의 Text 속성에 이 Key 가 자동으로 사용된다 라고 해석하면 되겠다.

 

일단 지역 리소스파일을 이용해 컨트롤의 지역화를 수행해 보자

<asp:Button ID="Button1" runat="server"

        Text="<%$ Resources: ButtonResource.Text %>" />

또는       

<asp:Button ID="Button2" runat="server" Text="DefaultText2"

        meta:resourcekey="ButtonResource" />

 

지역 리소스를 사용할 경우 전역 리소스처럼 명시적 지역화를 수행할 수도 있지만

Meta 태그를 이용한 암시적 지역화로도 사용가능하다.

암시적 지역화는 앞서 말했듯이 리소스 키 값만 연결해 주면 컨트롤의 특정 속성과 리소스 파일에 속성이 매칭되어 사용되는 것을 말한다.

 

또한 명시적 지역화를 사용할 경우에도 Class 명을 생략할 수 있다. 단일 페이지에 연결된 리소스 파일이기 때문에 굳이 리소스 파일명을

기입할 필요가 없는 것이다.

역시 Web.config 의 언어 정보를 변경하면서 수행하면 다음과 같이 결과가 나온다.

 

 

 

물론 지역(Local) 리소스 파일의 경우에도 다음과 같이 일반 출력이 가능하다

Response.Write(this.GetLocalResourceObject("ButtonResource.Text"));

 

 

전역(Global) 리소스 와 지역(Local) 리소스의 적용 상황

닷넷 프레임워크에서는 지역화를 위한 단일한 접근이 아니라 범위에 따른 두 개의 영역으로 리소스 파일을 관리하도록 하였다.

이 둘 중 어느것을 사용할 지에 대한 기준은 프로젝트의 성격에 따라 다르겠지만

일반적으로 다음과 같은 가이드 라인을 제공할 수 가 있겠다.

1)       공용 메시지나 단순 출력 메시지일 경우 전역 리소스 사용

2)       웹 사이트에 전역적으로 사용되는 메시지일 경우 전역 리소스 사용

3)       전역 리소스 파일이 너무 커져 협업의 어려움이나 관리의 어려움이 있을 경우 지역 리소스 사용
-
웹 사이트의 모든 페이지의 리소스를 하나의 전역 리소스에 다 표현한다면 파일을 관리하기도 어려울 것이며,
 
협업 시 발생하는 파일 동시 변경과 같은 문제점도 발생할 수 있다.
 
로컬 리소스를 사용하면 각 폴더마다 리소스 폴더를 별도로 둘 수 있으며 각 페이지에 해당하는 리소스 파일을 각각 생성함으로써
 
협업 시 서로 신경 쓰지 않고 작업할 수 있으며 페이지 별로 리소스를 관리하기 때문에 파일에 대한 관리도 용이해 질 수 있다.

4)       각 페이지의 컨트롤의 텍스트 표현(Text 속성)을 위해서는 지역 리소스 사용
- 3)
의 이유와도 동일함.
 
모든 페이지의 컨트롤 Text 속성을 단 하나의 전역 리소스에 포함해야 한다면.. 끔찍할 것이다.

 

이상의 가이드 라인을 기준으로 현재 프로젝트에 적합한 리소스 파일을 사용하기 바란다.

 

 

리소스를 이용한 웹 페이지 지역화에 대해 보다 상세한 내용은 다음의 MSDN 을 참고하기 바란다.

http://msdn2.microsoft.com/ko-kr/library/ms227427(VS.80).aspx

 

 

 

 

* 참고

아래 이미지를 보자

 

이와 같이 텍스트를 포함하는 이미지일 경우에도 다국어 처리를 해 주어야 한다.

여러 방법이 있을 수 있으나,

통상적으로 각 언어별로 이미지를 새로 만들어 폴더를 구분하는 방법이 있다.

이 경우에도 리소스 파일과 연계하여 사용하면 그나마 유연한 환경이 될 것이라 본다.

아래처럼 각 언어별 리소스 파일에 이미지의 경로를 기록하는 것이다.

 

 

 

 

 

 

그리고 아래와 같이 현재 UICulture 에 맞는 리소스의 경로를 참조하도록 한다.

<asp:Image ID="Image1" runat="server" ImageUrl="<%$ Resources:Resource, ImageUrl %>" />

또는       

<img src="<%$ Resources:Resource, ImageUrl %>" runat=server />

 

물론 웹 사이트의 모든 이미지를 서버 컨트롤로 만드는 것이 결코 좋은 선택은 아니다.

닷넷의 서버 컨트롤은 필요하다면 최소화 하는 것이 수행 속도 면에서는 좋을 수 있다.

 

여기서 말하는 이미지 다국어 처리 방법은 리소스와 연계한 힌트 성 글이니 이 글을 절대적으로 수용할 필요도 없으며

적합하지 않을 가능성이 크다.  그래서 참고인 것이다 ^^;

 

 

기본적으로는 서버와 클라이언트 코드는 서로를 직접적으로 호출 할 수 없다. 서로 실행되는 시간과 위치가 다르기 때문이다.

서버코드 : Requeset를 받았을때 서버에서 실행

클라이언트코드 : Response를 받은 클라이언트 브라우저에서 실행

 

하지만, 이를 가능하게 하는 몇가지 방법이 있고, 매번 기억하기 어려워서 여기에 정리해본다.

뭐 고급유저라면 관련 Tip을 상당히 여러가지 알고 있겠지만, 그래도 모든걸 항상 기억하긴 여러우니까...

<작성: http://blog.naver.com/myfancy>

 

1. ASP.NET AJAX - Web Service 호출

스크립트에서 Web Service 컨테이너에서 노출하고 있는 함수를 AJAX로 호출하는 방법이다. 포스트백이나 페이지 리프레시가 발생하지 않는다. 하지만, 비교적 많은 이해가 필요하고, .asmx로 웹서비스를 별도로 만들어야만 된다.

참고) Client-Side Web Service Call with AJAX Extentions

         http://msdn.microsoft.com/en-us/magazine/cc163499.aspx

        Calling Web Servivce from Client Script

         http://msdn.microsoft.com/en-us/library/bb398995.aspx

         http://msdn.microsoft.com/ko-kr/library/bb398998.aspx

 

2. ASP.NET Ajax Extentions - PageMethod 호출

PageMethod는 기본적으로 Page의 Behind코드에 public static으로 노출하고, JavaScript에서 호출하는 방식이다. PageMethod는 [WebMethod]라고 함수에 꾸며주기만 하면되고, Response에 Inline JavaScript Proxy 코드가 생성된다.

 

[System.Web.Services.WebMethod]
public static string GetContactName(string custid){}
함수는 반드시 public static이여야 한다.
 

<asp:ScriptManager ... EnablePageMethods=”true”/>

이 기법을 사용하기 위해서는 ScriptManager에 EnablePageMethods속성을 true로 설정하면 된다.

 

function CallGetContactName(custid,cbParam)
{    
 // call server side method
 PageMethods.GetContactName(custid,OnSuccess,OnFailed,cbParam);
}
// set the destination textbox value with the ContactName
function OnSuccess(res, cbParam)
{    
 //결과값 : res, 콜백파라미터:cbParam
}
// alert message on some failure
function OnFailed(res, cbParam)
{
 //결과값 : res, 콜백파라미터:cbParam
 alert(res.get_message());
}
스크립트를 별도의 파일로 추가할 경우 <head/>섹션이 아닌 <body>태그 아래 추가해야 한다.

3. jQuery를 이용한 WebService 호출

http://encosia.com/2008/03/27/using-jquery-to-consume-aspnet-json-web-services/

http://encosia.com/2008/05/29/using-jquery-to-directly-call-aspnet-ajax-page-methods/

http://encosia.com/2008/06/05/3-mistakes-to-avoid-when-using-jquery-with-aspnet-ajax/

 

4. Get, Post를 이용한 방법 (Non Ajax)

ASP.NET의 Page, HttpHandler, HttpModule등을 이용하여 Request를 받아 Response를 주는 인스턴스를 만들어 놓고, 순수 Ajax나, Post등의 기법을 이용하여 처리 할 수 있다.

Introduction

Text files are easy way to store data in either comma separated values or separated by new line. While working with many application usually we come across situations where we need to read and write data from / into text files. This article describes different approach of reading and writing data into the text files.

Essentials

In order to work with reading and writing data from / into text file, you will have to use System.IO namespace. You need to specify the path of the file to read from and write data into as Physical path on your hard driver. If you know the physical path of your file you want to read or write, you can specify as follows

string fileNameWithPath = @"E:\Csharp\temp\withsinglelinecode.txt";

If you do not know or not sure the path of the file from the drive or you want to create the text file in your applications sub folders, you can get the physical file path by using Server.MapPath method and passing the location of the file from your root directory of the application.

string fileNameWithPath = Server.MapPath("temp/withsinglelinecode1.txt");

In order to create a new line into the text file, you can use System.Environment.NewLine or "\r\n", to place a tab space you can use "\t". Reading data from text file and showing on the web page, will not automatically convert \r\n to new line, you will have to replace these characters with the <br /> as shown below.

litText.Text = contents.Replace("\r\n", "<br />");

Here litText is the Literal control I have placed in my .aspx page to show the contents of the file.

Reading and Writing with Single line of code


To simply write and read all contents of any any text file, you can use following one line code.

C# Code

 string data = "This is the first line . \r\n" 
    + "This is the second line. next two line space \r\n\r\n"
    + "\t This comes after a tab space";

// writing content into a text file with a single line of 
codeFile.WriteAllText(fileNameWithPath, data);

// reading content from a textfile with single line of codestring 
contents = File.ReadAllText(fileNameWithPath);

// Format the data with new line so that on the web page it appears in the new line
litText.Text = contents.Replace("\r\n", "< br />");

In the above code snippet, File.WriteAllText method accepts following parameters

  1. fileNameWithPath - physical location of the file with its name
  2. data - contents to write into the text file

File.ReadAllText method accepts following parameters

  1. fileNameWithPath - physical location of the file with its name

In the remaining lines, I have replaced the new line characters with the "<br />" so that it appears as next line in my web page.

Appending contents into a File and Reading data one by one

C# Code

 // Append data into the text file
using (StreamWriter writer = File.AppendText(fileNameWithPath))
{
    // writer.Write method will simply append the contents into the text // file without creating a new line
    writer.Write("NEW LINE ADDED without Creating a line line. ");
    writer.Write("This line will not appear into new line. ");
    
    // writer.WriteLine method append the contents into the text file 
    // and add a new line so that next appended contents appear in new line
    writer.WriteLine("Appended into above line but next appended contents will appear into a new line. ");
    writer.Write("This should appear in the next line. ");
}

// Read data from text file
using (StreamReader reader = File.OpenText(fileNameWithPath))
{
    // reads a single line of contents
    contents = reader.ReadLine();

    // reads complete contents of the the file
    contents += reader.ReadToEnd();
}

litText.Text = contents

In the above code snippets, I am using StreamWriter to append the contents into the file. File.AppendText method accepts physical location of the file with its name and returns StreamWriter, that can be used to append contents into the text file. This can be done using Write method or WriteLine method.

  1. writer.Write() - method accepts contents to append and simply append the contents at the end of the file.
  2. writer.WriteLine() - method accepts contents to append and add a new line after appending contents so that next appended contents appears in the new line.
Reading and Writing using FileStream

C# Code

// using File Stream
fileNameWithPath = Server.MapPath("temp/filestream.txt");

// writing contents using FileStream
using (FileStream stream = File.Open(fileNameWithPath, FileMode.OpenOrCreate))
{
     // writing data
     byte[] info = new UTF8Encoding(true).GetBytes(data);
     stream.Write(info, 0, info.Length);
}
// Reading contents using FileStream
using (FileStream stream = File.OpenRead(fileNameWithPath))
{
     // reading data
     StringBuilder strb = new StringBuilder();
     byte[] b = new byte[stream.Length];
     UTF8Encoding temp = new UTF8Encoding(true);

     while (stream.Read(b, 0, b.Length) > 0)
     {
           strb.Append(temp.GetString(b)); 
     }

     // write the contents of the file now
     Response.Write("Using FileStream 
" + strb.ToString() + "
"); }

FileStream object gives you extra control over how you want to read or write data from / into a file. This also let you specify the mode of the file to access like in Append mode, or Create mode or only Read mode. Also you can specify Type of access you want to have as third parameter of the File.Open() method.

FileStream buffers input and output for better performance. In order to use FileStream for reading and writing, you will need to use System.Text namespace as encoding related objects like UTF8Encoding contains in this namespace.

For detailed explanations FileStream, please visit MSDN.

'Framework' 카테고리의 다른 글

LINQ 자료  (2) 2009.10.09
C# Collection Classes (부제:List 와 ArrayList 의 차이 )  (0) 2009.10.06
[펌]Mircrosoft Office 2007 Groove 사용 방법  (0) 2009.09.02
XML 특수문자 처리  (0) 2009.08.11
.NET Naming Rule Guidelines  (0) 2009.07.02

※ VS 2008 다운로드 

http://www.asp.net/downloads/essential/




※ ASP.NET AJAX

  •  Watch the ASP.NET AJAX Support in VS2008 Video
  •  Watch the Adding AJAX Functionality to an Existing ASP.NET Page Video
  •  Watch the Creating and Using an AJAX-enabled Web Service in a Web Site Video
  • The ASP.NET AJAX Site
  • ASP.NET AJAX Overview 




    ※ New ListView and DataPager Controls
  •  Watch the ListView Control Video
  •  Watch the DataPage Control Video
  • Using the ASP.NET 3.5 ListView Control
  • ListView Web Server Control 




    ※ LINQ and other .NET Framework 3.5 Improvements
  •  Watch the LINQ How Do I Video Series
  • Part 1: Introduction to LINQ and SQL
  • Part 2: Defining our Data Model Classes
  • Part 3: Querying our Database
  • Part 4: Updating our Database
  • Part 5: Binding UI using the ASP:LinqDataSource Control
  • Part 6: Retrieving Data Using Stored Procedures
  • Part 7: Updating our Database using Stored Procedures
  • Part 8: Executing Custom SQL Expressions
  • Part 9: Using a Custom LINQ Expression with the <asp:LinqDataSource> control
  • LinqDataSource Technology Overview 




    ※ LINQ and other .NET Framework 3.5 Improvements

    Using ASP.NET Web Services Roadmap 




    ※ New Web Design Interface
  •  Watch the New Designer Support in VS2008 Video
  •  Watch the Quick Tour of the VS2008 IDE Video
  •  Watch the VS2008 and Nested Masterpages Video
  •  Watch the Creating and Modifying a CSS File Video
  • VS 2008 Web Designer and CSS Support
  • VS 2008 Nested Master Page Support
  • Working with CSS Overview





    ※ JavaScript Debugging and Intellisense

  •  Watch the JavaScript Intellisense Support in VS2008 Video
  •  Watch the JavaScript Debugging in VS2008 Video
  •  Watch the IntelliSense for JScript and ASP.NET AJAX Video
  • Visual Studio 2008 JavaScript Debugging
  • Visual Studio 2008 JavaScript Intellisense 




    ※ Multi-targeting Support
  •  Watch the Multi-Targeting Support in VS2008 Video
  • Visual Studio 2008 Multi-Targeting Support
  • .NET Framework Multi-targeting Overview




    ※ Other Resources

  • ASP.NET AJAX Forums
  • Visual Studio 2008 Forum
  • Visual Web Developer 2008 Express Forum


  • ASP.NET에서 MS-SQL을 사용할 경우, SQLClient 라는 전용 공급자(Provider)를 사용한다.
    Oracle에서는 OleDb방식을 사용하여야 하는데 .NET에서 오라클 전용 공급자(Provider)를 제공해주고 있다.

    MS-SQL 연결방법과 큰차이는 없지만 데이터 공급자가 다를 뿐이다.
    아래는 오라클 전용 공급자(Provider)를 사용하여 Oracle에 연결하는 간단한 소스이다.

    1. 참조추가에 System.Data.OracleClient 를 추가한다.
    2. Using System.Data.OracleClient 선언한다.
    3. OracleProvider 이용하여 코딩한다.
      string service_name = @"
                                (DESCRIPTION =
                                    (ADDRESS_LIST =
                                      (ADDRESS = (PROTOCOL = TCP)(HOST = localhost)(PORT = 1521))
                                    )
                                    (CONNECT_DATA =
                                      (SERVICE_NAME = database_name)
                                    )
                                  )";
           
    string sqlString = "SELECT EMP_NO, EMP_NM FROM TBM_EMP";

    DataSet ds = new DataSet();
    OracleConnection Oracleconn =  
                     New OracleConnection("Data Source=" + service_name + ";User=CALLSM;Password=CALLSM");

    Oracleconn.Open();
    OracleDataAdapter OrcleAd = new OracleDataAdapter(sqlString, Oracleconn);

    Oracleconn.Close();
    OrcleAd.Fill(ds);

    GridView1.DataSource = ds;
    GridView1.DataBind();

    ASP.NET

    ·         Extending the GridView to Include Sort Arrows: Scott Mitchell has a nice article that describes how to add a visual indicator to the GridView control to indicate the current sort order on columns.

    ·         Using ASP.NET 3.5's ListView and DataPager Controls: Sorting Data: Scott Mitchell continues his ListView control series with a good article on enabling sorting scenarios with the new ListView control.

    ·         Building a Grouping Grid with the ListView and LinqDataSource Controls: Matt Berseth has an awesome post that shows off using the new ListView control and LinqDataSource controls to build a hierarchical grouping grid.  A post to bookmark.

    ·         Using the ListView, DataPager and LinqDataSource Controls: Matt Berseth has a good tutorial post that shows off using these new controls to join data from two database tables using LINQ.

    ·         Some ASP.NET 3.5 ListView Control Examples: Mike Ormond has a nice post that provides a number of samples that show how to use the new ASP.NET ListView control.  For even more ListView articles, check out my last link-listing post which pointed to a bunch of them.

    ·         Large File Uploads in ASP.NET: Jon Galloway has a nice post that provides some good details on handing large file uploads using ASP.NET.

    ASP.NET AJAX

    ·         Four ASP.NET AJAX JavaScript UI Methods You Should Learn: Dave Ward has another great post in his series about ASP.NET AJAX's client-side JavaScript Helper Methods.

    ·         Five Tab Themes Created for the ASP.NET AJAX Control Toolkit: Matt Berseth posts some really cool themes created for the ASP.NET AJAX Control Toolkit's Tab control. Very slick!

    ·         CNN Style Scrolling Ticker with the Marquee Toolkit Control: Matt Berseth posts another great one that shows how to implement a scrolling marquee UI using the ASP.NET AJAX Control Toolkit.

    Visual Studio

    ·         Did You Know?: Lisa Feigenbaum from the VB team has posted a really cool series of blog posts that talk about some of the new VS 2008 editor and IDE features.  Read Part 1: Intellisense Everywhere, Part 2: IntelliSense is now Transparent, Part 3: Ctrl+Tab to Navigate Windows, Part 4: What You Can Do with Debugger DataTips, and Part 5: VB IntelliSense now filters as you type.

    ·         Web Server Settings for ASP.NET Web Application Projects can now be stored per user as well as per project: The VS Web Tools Team has a nice post that describes how you can now store web server settings per-user instead of per-project.  This is very useful for multi-developer scenarios (where you don't want to check-in these values into source control).

    ·         Using Ctrl-Break to Stop VS Building: Steven Harman points out a cool tip/trick, which is that you can use the Ctrl-Break key within Visual Studio to kill the current compilation build.  A useful tip if you've accidentally kicked off a long build or get tired waiting for it to finish.

    ·         Visual Studio 2008 Trouble Shooting Guide: If you run into any issues installing VS 2008, make sure to check out this blog post.  It details a bunch of common causes of failures, and how to fix them.

    .NET

    ·         Marshaling between Managed and Unmanaged Code: Yi Zhang and Xiaoying Guo from my team in Shanghai have written a great MSDN article that describes how to use the marshaling interop features of the CLR to call native code.  One of the tools they highlight is an awesome P/Invoke Interop Assistant application they built that makes it much, much easier to generate p/invoke interop signatures when calling native methods.  A must-have tool for anyone doing native/managed interop!

    ·         .NET Framework 3.5 Poster: Brad Abrams posts about the cool new .NET Framework 3.5 posters now available for download (now in multiple file formats).

    IIS

    ·         Microsoft Web Deployment Tool Technical Preview 1: Yesterday the IIS team posted the first preview of a new Microsoft Web Deployment tool.  This tool works with both IIS6 and IIS7 and enables automated deployment, synchronization, and migrating of applications on web servers.  If you are looking for a great way to automate the deployment of your ASP.NET applications then this tool is definitely one to check out.  To learn more, read the walkthroughs at the bottom of this page (in particular the "Introduction to MS Deploy" one).  This tool is awesome and should make automated deployment much easier.
    Hope this helps,
    Scott

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

    ListView 설명

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

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

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

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

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

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

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

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

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

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


    ListView의 사용 예제

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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



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

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

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

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

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

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

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

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

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

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

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

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

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

    ConfigurationSection 클래스

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    구성 섹션

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

    클래스 코드

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    다음은 결과 화면입니다.

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

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

     

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

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

     

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

     

    function CheckEnter() {

    try {

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

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

    else var key = e.which;

     

    if( key == 13 ) {

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

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

     

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

    if( tagId == "InputLogin" ) {

    CheckLogin();

    return false;

    }

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

    if( tag == "TEXTAREA") {

    return true;

    }

    return false;

    }

    }

    catch(e) { return true; }

    }

    document.onkeydown = CheckEnter;

     

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

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

     

    주석 부분의

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

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

     

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

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

     

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

    예)

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

     

    if( tagId == "InputSearch" ) {

    CheckSearch();

    return false;

    }

     

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

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

     

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

     

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

     

     

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

     

    FileUpload.aspx 생성하기

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

     

    FileUpload.aspx

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

       

       

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

        <br /><br />

       

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

       

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

     

     

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

           

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

       

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

     

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

       

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

     

       

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

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

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

     

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

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

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

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

     

     

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

     

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

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

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

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

     

     

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

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

     

    새 이름으로 업로드 하기

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

    /// <summary>

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

    /// </summary>

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

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

    protected void Upload3_OnClick(object sender, EventArgs e)

    {

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

        listFile.Add(file1);

        listFile.Add(file2);

        listFile.Add(file3);

     

        SiteUpload sUpload = new SiteUpload();

     

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

     

        Response.Write(statusMessage);

     

    }

    #endregion

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

     

    기존 이름으로 덮어 씌우기

    #region 기존 이름 덮어씌우기

    /// <summary>

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

    /// </summary>

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

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

    protected void Upload2_OnClick(object sender, EventArgs e)

    {

        SiteUpload sUpload = new SiteUpload();

     

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

     

        /*

        상태 변수

        READY : 준비

        NOEXT : 지원되지 않는 확장자

        NOFNM : 잘못된 파일명

        FAILD : 서버에서 실패

        SUCES : 성공

        NOSEL : 파일선택안함

         */

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

     

     

        Response.Write(statusMessage);

    }

    #endregion

     

    다중 새 이름으로 업로드

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

    /// <summary>

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

    /// </summary>

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

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

    protected void Upload3_OnClick(object sender, EventArgs e)

    {

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

        listFile.Add(file1);

        listFile.Add(file2);

        listFile.Add(file3);

     

        SiteUpload sUpload = new SiteUpload();

     

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

     

        Response.Write(statusMessage);

     

    }

    #endregion

     

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

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

    /// <summary>

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

    /// </summary>

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

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

    protected void Upload4_OnClick(object sender, EventArgs e)

    {

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

        listFile.Add(file4);

        listFile.Add(file5);

        listFile.Add(file6);

     

        SiteUpload sUpload = new SiteUpload();

     

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

     

        Response.Write(statusMessage);

    }

    #endregion

     

    파일 삭제

    #region 파일 삭제

    /// <summary>

    /// 파일 삭제

    /// </summary>

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

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

    protected void btnDel_OnClick(object sender, EventArgs e)

    {

        /*

        상태 변수

        FAILD : 서버에서 실패

        SUCES : 성공

         */

        SiteUpload sUpload = new SiteUpload();

     

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

     

        Response.Write(statusMessage);

     

    }

     

     

    /// <summary>

    /// 파일 삭제

    /// </summary>

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

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

    protected void btnDel2_OnClick(object sender, EventArgs e)

    {

        /*

        상태 변수

        FAILD : 서버에서 실패

        SUCES : 성공

         */

        SiteUpload sUpload = new SiteUpload();

     

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

     

        Response.Write(statusMessage);

     

    }

     

    /// <summary>

    /// 파일 삭제

    /// </summary>

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

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

    protected void btnDel3_OnClick(object sender, EventArgs e)

    {

        /*

        상태 변수

        FAILD : 서버에서 실패

        SUCES : 성공

         */

        SiteUpload sUpload = new SiteUpload();

     

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

     

        Response.Write(statusMessage);

     

    }

    #endregion

     

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

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

     

    DLL 만들기

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

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

     

     

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

     

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

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

     

     

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

     

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

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

     

    Visual Studio 명령 프롬프트에서

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

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

     

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

    using System.IO;를 추가

     

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

     

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

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

      

     

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

     

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

    변수는 다음과 같다.

    #region 변수

    /// <summary>

    /// 아이디 변수

    /// </summary>

    private string id = string.Empty;

    /// <summary>

    /// 아이디 세팅

    /// </summary>

    public string Id

    {

        get { return id; }

        set { id = value; }

    }

     

    /// <summary>

    /// 키 변수

    /// </summary>

    private string kno = string.Empty;

    /// <summary>

    /// 키 세팅

    /// </summary>

    public string Kno

    {

        get { return kno; }

        set { kno = value; }

    }

     

    /// <summary>

    /// 최대 업로드 용량

    /// </summary>

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

    /// <summary>

    /// 최대 업로드 용량

    /// </summary>

    public int MaxLength

    {

        get { return maxLength; }

        set { maxLength = value; }

    }

     

    /// <summary>

    /// 지원 확장자

    /// </summary>

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

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

     

    /// <summary>

    /// 상태 변수

    /// READY : 준비

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

    /// NOFNM : 잘못된 파일명

    /// FAILD : 서버에서 실패

    /// SUCES : 성공

    /// NOSEL : 파일선택안함

    /// </summary>

    private string RTN_STATUS = string.Empty;

     

    #endregion

     

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

    #region 생성자

    /// <summary>

    /// 생성자

    /// </summary>

    public SiteUpload()

    {

        // 기본 세팅

        Id = " website1";

        Kno = " website1111";

     

        // 지원되는 파일 확장자

        listExt.Add("GIF");

        listExt.Add("PNG");

        listExt.Add("JPG");

        listExt.Add("JEPG");

        listExt.Add("BMP");

       

        // 초기 상태

        RTN_STATUS = "READY";

    }

    #endregion

     

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

    1.     디렉토리 생성

    2.     새 파일로 저장

    3.     기존 파일로 저장

    4.     다중 새 파일로 저장

    5.     다중 기존 파일로 저장

    6.     파일 삭제

    7.     상태 메시지

     

    디렉토리 생성

    #region 디렉토리 생성

    /// <summary>

    /// 디렉토리 생성

    /// </summary>

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

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

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

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

    {

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

        SvcFileUpload sfile = new SvcFileUpload();

        sfile.CreateDirectory(dir, id, kno);

    }

    #endregion

     

    새 파일로 저장

    #region 새 파일로 저장

    /// <summary>

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

    /// [0] 상태값

    /// [1] 파일명

    /// </summary>

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

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

    /// <returns></returns>

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

    {

        string filename = string.Empty;

        string ext = string.Empty;

     

        int fileLength = files.PostedFile.ContentLength;

        int fnStart = -1;

        int fnLen = 0;

       

        // 리턴변수

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

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

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

     

       

        filename = files.PostedFile.FileName;

        fnLen = filename.Length;

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

       

        // 파일 유무 체크

        if (fnLen > 0)

        {

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

            {

                Stream strm;

     

                // 파일명만 추출하기

                int extStart = -1;

                int extLen = 0;

                bool chkExt = false;

     

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

     

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

                extLen = filename.Length;

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

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

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

     

                // 확장자 체크

                foreach (string s in listExt)

                    if (ext.ToUpper() == s)

                        chkExt = true;

     

                if (chkExt)

                {

                    byte[] send = new byte[fileLength];

     

                    strm = files.PostedFile.InputStream;

                    strm.Read(send, 0, fileLength);

     

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

                    SvcFileUpload sfile = new SvcFileUpload();

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

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

     

                    strm.Close();

     

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

                        RTN_STATUS = "SUCES";

                    else

                        RTN_STATUS = "FAILD";

                }

                else

                    RTN_STATUS = "NOEXT";

            }

            else

                RTN_STATUS = "NOFNM";

        }

        else

            RTN_STATUS = "NOSEL";

     

        Rtn[0] = RTN_STATUS;

        Rtn[1] = filename;

     

        return Rtn;

    }

    #endregion

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

     

    기존파일명으로 파일 저장

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

    /// <summary>

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

    /// [0] 상태값

    /// [1] 파일명

    /// </summary>

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

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

    /// <returns></returns>

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

    {

        string filename = string.Empty;

        string ext = string.Empty;

     

        int fileLength = files.PostedFile.ContentLength;

        int fnStart = -1;

        int fnLen = 0;

     

        // 리턴변수

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

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

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

     

        filename = files.PostedFile.FileName;

        fnLen = filename.Length;

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

     

        // 파일 유무 체크

        if (fnLen > 0)

        {

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

            {

               

     

                // 파일명만 추출하기

                int extStart = -1;

                int extLen = 0;

                bool chkExt = false;

     

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

     

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

                extLen = filename.Length;

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

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

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

     

                // 확장자 체크

                foreach (string s in listExt)

                    if (ext.ToUpper() == s)

                        chkExt = true;

     

                if (chkExt)

                {

                    Stream strm;

                    byte[] send = new byte[fileLength];

     

                    strm = files.PostedFile.InputStream;

                    strm.Read(send, 0, fileLength);

     

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

                    SvcFileUpload sfile = new SvcFileUpload();

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

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

     

                    strm.Close();

     

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

                        RTN_STATUS = "SUCES";

                    else

                        RTN_STATUS = "FAILD";

                }

                else

                    RTN_STATUS = "NOEXT";

            }

            else

                RTN_STATUS = "NOFNM";

        }

        else

            RTN_STATUS = "NOSEL";

     

        Rtn[0] = RTN_STATUS;

        Rtn[1] = filename;

     

        return Rtn;

    }

    #endregion

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

     

    다중 새 파일로 저장

    #region 다중 새 파일로 저장

    /// <summary>

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

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

    /// </summary>

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

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

    /// <returns></returns>

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

    {

        string CHK_STATUS = "FAILD";

     

        // 리턴변수

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

     

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

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

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

        RtnFullStatus.Add(string.Empty);

     

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

     

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

        {

            svcRtn = NewFileUpload(f, dir);

     

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

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

        }

     

        foreach (string s in RtnStatus)

        {

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

                CHK_STATUS = "SUCES";

        }

        RtnFullStatus[0] = CHK_STATUS;

     

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

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

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

     

        return Rtn;

    }

    #endregion

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

     

    다중 기존 파일로 저장

    #region 다중 기존 파일로 저장

    /// <summary>

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

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

    /// </summary>

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

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

    /// <returns></returns>

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

    {

        string CHK_STATUS = "FAILD";

     

        // 리턴변수

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

     

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

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

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

        RtnFullStatus.Add(string.Empty);

     

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

     

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

        {

            svcRtn = SameFileUpload(f, dir);

     

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

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

        }

     

        foreach (string s in RtnStatus)

        {

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

                CHK_STATUS = "SUCES";

        }

        RtnFullStatus[0] = CHK_STATUS;

     

     

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

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

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

     

        return Rtn;

    }

    #endregion

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

     

    파일 삭제

    #region 파일 삭제

    /// <summary>

    /// 파일 삭제

    /// </summary>

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

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

    public string DeleteFile(string dir, string filename)

    {

        bool delCheck = false;

        string RTN_STATUS = string.Empty;

     

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

        SvcFileUpload sfile = new SvcFileUpload();

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

     

        if (delCheck)

            RTN_STATUS = "SUCES";

        else

            RTN_STATUS = "FAILD";

     

        return RTN_STATUS;

    }

    #endregion

     

    상태 메시지

    #region 상태 메세지

    /// <summary>

    /// 상태 메세지

    /// </summary>

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

    /// <returns></returns>

    public string SatausMessage(string status)

    {

        /*

        상태 변수

       

        READY : 준비

        NOEXT : 지원되지 않는 확장자

        NOFNM : 잘못된 파일명

        FAILD : 서버에서 실패

       

        SUCES : 성공

        NOSEL : 파일선택안함

         */

        string rtnMessage = string.Empty;

     

        switch (status)

        {

            case "READY":

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

                break;

            case "NOEXT":

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

                break;

            case "NOFNM":

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

                break;

            case "FAILD":

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

                break;

            case "SUCES":

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

                break;

            case "NOSEL":

                rtnMessage = "";

                break;

        }

        return rtnMessage;

    }

    #endregion

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

     

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

    빌드를 하여 dll을 만들자.

     

     

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

     

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

     

    준비하기

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

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

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

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

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

     

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

     

    웹 서비스 만들기

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

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

     

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

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

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

     

     

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

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

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

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

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

     

    웹 서비스에 필요한 변수

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

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

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

     

    웹 서비스에 필요한 메소드

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

    2.     디렉토리 생성 메소드

    3.     파일 여부 체크 메소드

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

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

    6.     파일 삭제 메소드

     

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

     

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

     

    /// <summary>

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

    /// </summary>

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

    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]

    public class SvcFileUpload : System.Web.Services.WebService

    {

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

     

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

    /// <summary>

    /// 사이트키

    /// </summary>

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

    /// <summary>

    /// 저장할 경로 변수

    /// </summary>

    private string saveDir = string.Empty;

     

    /// <summary>

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

    /// </summary>

    private bool equValue = false;

     

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

    /// <summary>

    /// 생성자

    /// </summary>

    public SvcFileUpload () {

     

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

        //InitializeComponent();

     

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

        saveDir = "D:\\SaveImage";

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

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

    }

     

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

    1.     아이디와 키 번호 체크

    2.     저장 경로 반환

    3.     디렉토리 생성

    4.     파일 여부 체크

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

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

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

    8.     파일 삭제

     

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

    /// <summary>

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

    /// </summary>

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

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

    /// <returns></returns>

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

    private bool EqualsIdKno(string id, string kno)

    {

        string sno = site[id];

     

        if (sno == kno)

            equValue = true;

        else

            equValue = false;

     

        return equValue;

    }

     

    저장 경로 반환

    /// <summary>dkdle

    /// 저장 경로 반환

    /// </summary>

    /// <returns></returns>

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

    public string ReturnDirectory()

    {

        return saveDir;

    }

     

    디렉토리 생성

    /// <summary>

    /// 디렉토리를 생성

    /// </summary>

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

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

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

    {

        bool createDir = false;

     

        if (EqualsIdKno(id, kno))

        {

            string strDir = string.Empty;

     

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

     

            //디렉토리 생성

            if (!Directory.Exists(strDir))

            {

                Directory.CreateDirectory(strDir);

            }

            createDir = true;

        }

        else

        {

            createDir = false;

        }

     

        return createDir;

    }

     

    파일 여부 체크

    /// <summary>

    /// 파일 여부 체크

    /// </summary>

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

    /// <returns></returns>

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

    public bool EqualsFile(string fname)

    {

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

       

        return File.Exists(strFile);

    }

     

    새로운 파일명 생성

    /// <summary>

    /// 새로운 파일명 생성

    /// </summary>

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

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

    /// <returns></returns>

    private string GetAvailablePathname(string folderPath, string filename)

    {

        int invalidChar = 0;

        do

        {

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

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

     

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

            if (invalidChar != -1)

                filename = filename.Remove(invalidChar, 1);

        }

        while (invalidChar != -1);

     

     

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

        string filenameWithoutExtention = Path.GetFileNameWithoutExtension(filename);

        string extension = Path.GetExtension(filename);

     

     

     

        while (File.Exists(fullPath))

        {

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

            Match mt = rg.Match(fullPath);

     

            if (mt.Success)

            {

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

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

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

     

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

            }

            else

            {

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

            }

        }

        return fullPath;

    }

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

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

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

     

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

    /// <summary>

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

    /// </summary>

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

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

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

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

    /// <returns></returns>

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

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

    {

        string checkSave = string.Empty;

        string filePath = string.Empty;

     

        // 아이디와 키 값 비교

        if (EqualsIdKno(id, kno))

        {

            // 저장할 경로

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

            filePath = dirpath + fileName;

               

            if (CreateDirectory(dir, id, kno))

            {

                try

                {

                    //파일처리

                    using (MemoryStream ms = new MemoryStream(fileInfo))

                    {

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

                        {

                            ms.WriteTo(fs);

     

                            ms.Close();

                            fs.Close();

                        }

                    }

                    checkSave = fileName; // 파일명

                }

                catch

                {

                    checkSave = "";

                }

            }

            else

            {

                checkSave = "";

            }

        }

        return checkSave;

    }

     

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

    /// <summary>

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

    /// </summary>

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

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

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

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

    /// <returns></returns>

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

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

    {

        string checkSave = string.Empty;

        string filePath = string.Empty;

     

        // 아이디와 키 값 비교

        if (EqualsIdKno(id, kno))

        {

            // 저장할 경로

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

     

            if (CreateDirectory(dir, id, kno))

            {

                try

                {

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

                    filePath = GetAvailablePathname(dirpath, fileName);

     

                    //파일처리

                    using (MemoryStream ms = new MemoryStream(fileInfo))

                    {

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

                        {

                            ms.WriteTo(fs);

     

                            ms.Close();

                            fs.Close();

                        }

                    }

                    int fnLen = filePath.Length;

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

     

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

                }

                catch

                {

                    checkSave = "";

                }

            }

            else

            {

                checkSave = "";

            }

        }

     

        return checkSave;

    }

     

    파일 삭제

    /// <summary>

    /// 파일을 삭제한다.

    /// </summary>

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

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

    /// <returns></returns>

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

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

    {

        bool checkDel = false;

       

        // 아이디와 키 값 비교

        if (EqualsIdKno(id, kno))

        {

            try

            {

                string strDir = string.Empty;

     

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

     

                //파일삭제

                if (File.Exists(strDir + filename))

                {

                    File.Delete(strDir + filename);

                }

                checkDel = true;

            }

            catch

            {

                checkDel = false;

            }

        }

        else

            checkDel = false;

     

        return checkDel;

    }

     

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

     

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

     

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

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

    1. appSetting값 읽기

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

    또는

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

    2. appSetting값 변경

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

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

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

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

     

    string key = "appValue";

    string value = "test2"; 

    DataSet ds = new DataSet();

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

    if (dsRs.Tables.Count > 0) {

    DataTable dt = ds.Tables[0];

    if (dt.Rows.Count > 0) {

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

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

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

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

    }

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

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

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

    }

    }

    }

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

    }

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

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

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

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

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

     

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

     

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

     

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

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

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

     

     

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

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

      

     

    + Recent posts