What is Cryptography?

암호화란 데이터를 보호 하는데 사용되며 그 외에도 유용하게 많은 곳에서 사용된다.
이는 데이터가 보여지고, 수정되어지는 것을 방지하고, 생성자의 데이터에 대한 무결성을 보장하기 위해 사용되어진다.
암호화는 데이터를 암호화 하여 암호화 된 상태에서 네트워크 상에 전송하며 그리고 수신측에서 다시 데이터를 암호 해독함으로써, 인터넷과 같은 안전하지 않은 네트워크 상에서 보안 통신을 제공하는 메커니즘을 제공한다.
Encryption은 데이터베이스에 저장된 암호와 같은 정보를 사람이 읽을수 있고 이해할 수 있게 하는 것을 방지하는 추가적인 보안 메커니즘에 이용되어 질수 있다.

Encryption Component
인크립션은 데이터를 암호화하고 해독하는 키와 조합된 암호화 알고리즘의 사용을 포함한다.
모든 암호화 알고리즘의 목적은 적절한 키 없이는 데이터를 암호해독 하는 것을 가급적 어렵게 만드는데 있다.
Microsoft .NET 프레임워크 클래스(System.Security.Cryptography)는 암호화에 대한 상세정보를 관리해 줄것이다.
이 클래스들은 동일한 인터페이스로 구현된다. ; 그래서 이 클래스들을 가지고 작업하는 것은 Cryptography 네임스페이스를 통해 하는 것과 동일하다.
프레임워크내의 어떤 클래스들은 단지 Microsoft CryptoAPI에 존재하는 알고리즘에 대한 래퍼이다. 다른 클래스들은 그 주요한 알고리즘에 대한 구현을 관리한다.

Public-Key Encryption
비대칭 암호화를 알려진 공개키 암호화는 공개키와 비밀키 쌍을 이용하여 데이터를 암호화하고 암호해독한다.
공개키는 누구나 사용할 수 있도록 되어있으며, 비밀키 소유자에게 보내질 데이터를 암호화 하는데 사용된다.
비밀키는 그 이름이 의미하듯 기밀을 유지한다. 비밀키는 데이터를 암호해독하는데 사용되며 오직 정확한 공개키가 데이터 암호화에 사용된 경우에만 작동하게 된다.
비밀키는 공개키로 암호화된 데이터를 암호해독 하는데 사용될수 있는 유일한 키이다.
키들은 여러 번 사용될수 있게 저장될수 있으며 또는 한번만 사용되기 위해 생성 될수도 있다.

비대칭 암호화 알고리즘은 대게 단지 적은 양의 데이터를 암호화하는데 효율적이다.
아래의 공개키 알고리즘은 .NET Framework 상에서 사용될 수 있다.

- Digital Signature Algorithm(DSA)
- RSA

Private-Key Encryption
대칭 암호화로 알려진 비밀키 암호화는 정보를 암호화하고 암호해독하는데 하나의 키를 이용한다. 키는 인증되지 않은 자에 의해 데이터가 암호해독되어 데이터가 손상되지 않도록 기밀을 유지해야 하며. 비밀키 알고리즘은 상대적으로 빠르며 거대한 데이터 스트림을 암호화하고 암호해독하는데 사용될수 있다.
비밀키 알고리즘은 block ciphers로 알려져 있다. 왜냐하면, 이 알고리즘은 데이터를 한번에 한 블록씩 암호화하기 때문이다.
Block ciphers는 알고리즘과 키에 기반하여 동일한 입력블럭을 동일한 출력블럭으로 암호화한다. 만일 데이터의 구조를 누군가 알고 있다면, 패턴이 인식되어 키가 역공학되어 질수 있는 가능성이 있다. 이를 해결하기 위해, .NET Framework 상의 클래스들은 Chaining이라 알려진 처리를 이용한다. 이는 이전 블록의 정보가 현재 블록을 암호화하는데 사용되도록 한다. 이는 키가 발견되지 않도록 방지한다. 이는 첫 블록의 데이터를 암호화 하는데 초기화 백터(Initialization vector : IV)를 요구한다.

아래의 비밀키 알고리즘은 .NET Framework 상에서 이용가능한 것이다. 각각의 상세내용에는 각 알고리즘에 대한 기본적인 정보를 담고 있으며 장, 단점을 포함하고 있다.

- Data Encryption Standard(DES) 알고리즘은 데이터를 64 bit 블록으로 암호화하고 해독하는데 64bit 키를 이용한다. 키가 64bit라 할지라도 효율적인 키 강도는 단지 56 bit이다. 적당한 시간안에 모든 DES 키를 검색할수 있는 하드웨어 장비가 존재한다. 이것은 DES 알고리즘이 깨어질수 있게 하였고, 그래서 이 알고리즘은 쓸모없는 것으로 취급되었다.
- RC2는 가변 키 길이 block cipher이다. 키 길이는 8bit에서 64bit로 가변적일 수 있다. 이는 특히 DES보다 안전한 대안으로서 설계되었다. 처리시간은 DES에 비해 2, 3배 빨라졌다. 하지만 .NET Framework 에서 사용가능한 RC2CryptoServiceProvider는 8 문자 또는 64bit 키로 제한되었다. 8문자 제한은 이 알고리즘이 DES가 가졌던 공격위험성을 그대로 가지게 하였다.
- TripleDES 알고리즘은 DES알고리즘의 세가지 계승적인 반복을 사용한다.
- 이 알고리즘은 2개 키, 3개 키를 사용한다. DES알고리즘과 같이 키 길이는 64bit 에 유효 키강도는 56bit이다. TripleDES 알고리즘은 DES 알고리즘의 결점을 정정하기 위해 설계되었다. 그러나 세 반복(Three Iteration)은 처리시간을 DES보다 세배 정보 느리게 하는 결과를 낳았다.
- Advanced Encryption Standard(AES) 알고리즘의 하나인 Rijndeal 알고리즘은 DES 알고리즘의 대안으로서 설계되었다. 키 강도는 DES보다 강하고 DES보다 나은 성능을 위해 설계되었다. 키의 길이는 128, 192, 256 bit로 가변적일수 있다. 이 알고리즘은 가장 신뢰할수 있으며 이번 칼럼 내용의 예제에서 사용하겠다.

Hashing Algorithm
Hashing은 특정 길이의 데이터를 고정 길이 바이트 시퀀스로 맵핑하는 것을 의미한다.
입력이 타이핑 테스트 이거나 법전의 내용이든 상관없이 결과는 동일 크기로 나타날 것이다.
해싱은 독특한 결과를 생산한다. 똑 같은 두개의 눈송이가 존재하지 않듯이 두개의 입력 조합이 동일한 해시 결과를 생산할 수는 없다. 입력이 하나의 문자가 다를지라도 다른 결과를 생산한다. .NET Framework는 아래의 해시 알고리즘에 대한 자원을 제공한다.

- HMACSHAI
- MACTripleDES
- MD5CryptoServiceProvider
- SHA1Managed
- SHA256Managed
- SHA384Managed
- SHA512Managed

How to Generate a Key and IV for Private-key Encryption
각 알고리즘은 사용되는 특정 키 사이즈를 가지고 있다. 각 키는 미리 정의된 크기, 전형적으로 1 문자(8bit) 에서 32문자(256bit)의 범위를 가지는, 에 맞추어야 한다.
어떤 알고리즘은 다양한 키 사이즈를 지원한다. 그러나 특정 알고리즘의 키 사이즈의 유효한 범위 내에 있어야만 한다. 우리는 128, 192, 256 bit key를 지원하는 RiJndeal를 사용할 것이다. 128 bit 키를 만드는 방법은 해싱 알고리즘의 하나를 이용해서 만든다.
아래의 code outline에서 클래스는 입력구를 받아서 키를 생성하고 IV를 생성하는 메서드를 포함한다.


// Initialize internal values
this._Key = new byte[24];
this._IV = new byte[16];

// Perform a hash operation using the phrase. This will generate
// a unique 48 character value to be used as the key and IV.
byte[] bytePhrase = Encoding.ASCII.GetBytes(Phrase);
SHA384Managed sha384 = new SHA384Managed();
sha384.ComputeHash(bytePhrase);
byte[] result = sha384.Hash;

// Transfer the first 24 characters of the hashed value to the key
// and 16 more characters for the initialization vector.
for( int loop=0; loop<24; loop++ ) this._Key[loop] = result[loop];
for( int loop=24; loop<40; loop++ ) this._IV[loop-24] = result[loop];


How to Use Private-Key Encryption
아래의 클래스는 데이터를 암호하 하는데 사용되는 비밀키를 생성하기 위해 해싱을 하는 방법을 데모한다. 암호해독 메서드는 암호화 함수에서 사용된 것의 역 함수를 사용함으로써 간단히 이용될수 있다.


using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;

internal class MyEncryptor
{
// Internal value of the phrase used to generate the secret key
private string _Phrase = "";
/// Set the phrase used to generate the secret key.
public override string Phrase
{
set
{
this._Phrase = value;
this.GenerateKey();
}
}

// Internal initialization vector value to
// encrypt/decrypt the first block
private byte[] _IV;

// Internal secret key value
private byte[] _Key;

///
/// Constructor
///

/// Secret phrase to
generate key
public MyEncryptor(string SecretPhrase)
{
this.Phrase = SecretPhrase;
}

///
/// Encrypt the given value with the Rijndael algorithm.
///

/// Value to encrypt
/// Encrypted value.
public string Encrypt(string EncryptValue)
{
CryptoStream encryptStream = null; // Stream used to encrypt
RijndaelManaged rijndael = null; // Rijndael provider
ICryptoTransform rijndaelEncrypt = null; // Encrypting object
MemoryStream memStream = new MemoryStream(); // Stream to contain
// data

try
{
if( EncryptValue.Length > 0 )
{
// Create the crypto objects
rijndael = new RijndaelManaged();
rijndael.Key = this._Key;
rijndael.IV = this._IV;
rijndaelEncrypt = rijndael.CreateEncryptor();
encryptStream = new CryptoStream(
memStream, rijndaelEncrypt, CryptoStreamMode.Write);

// Write the encrypted value into memory
byte[] input = Encoding.UTF8.GetBytes(EncryptValue);
encryptStream.Write(input, 0, input.Length);
encryptStream.FlushFinalBlock();

// Retrieve the encrypted value and return it
return( Convert.ToBase64String(memStream.ToArray()) );
}
else
{
return "";
}
}
finally
{
if( rijndael != null ) rijndael.Clear();
if( rijndaelEncrypt != null ) rijndaelEncrypt.Dispose();
if( memStream != null ) memStream.Close();
}
}

/*****************************************************************
* Generate an encryption key based on the given phrase. The
* phrase is hashed to create a unique 32 character (256-bit)
* value, of which 24 characters (192 bit) are used for the
* key and the remaining 8 are used for the initialization
* vector (IV).
*
* Parameters: SecretPhrase - phrase to generate the key and
* IV from.
*
* Return Val: None
***************************************************************/
private void GenerateKey(string SecretPhrase)
{
// Initialize internal values
this._Key = new byte[24];
this._IV = new byte[16];

// Perform a hash operation using the phrase. This will
// generate a unique 32 character value to be used as the key.
byte[] bytePhrase = Encoding.ASCII.GetBytes(SecretPhrase);
SHA384Managed sha384 = new SHA384Managed();
sha384.ComputeHash(bytePhrase);
byte[] result = sha384.Hash;

// Transfer the first 24 characters of the hashed value to the key
// and the remaining 8 characters to the intialization vector.
for( int loop=0; loop<24; loop++ ) this._Key[loop] = result[loop];
for( int loop=24; loop<40; loop++ ) this._IV[loop-24] = result[loop];
}
}



-- ND_DT 를 테이블의 KEY 로 가정하고 만들었습니다.
-- 여러 ROW 를 한꺼번에 업데이트 할수도 있기 때문에 트리거 안에서 커서를 돌렸습니다.

 

CREATE TRIGGER tr_Save_Data_Temp 
ON dbo.Save_Data_Temp 


FOR UPDATE,INSERT
AS         SET 
NOCOUNT ON
DECLARE 
@ND_DT VARCHAR(20
BEGIN        DECLARE trigger_cur CURSOR        FOR SELECT ND_DT FROM inserted
       
OPEN trigger_cur                FETCH NEXT FROM trigger_cur INTO @ND_DT 

         
WHILE (@@FETCH_STATUS=0)
         
BEGIN 

               
-- Save_Data 에 데이터가 있을시 삭제
               
IF EXISTS     (    SELECT 'TRUE'   FROM  Save_Data WHERE ND_DT =@ND_DT     )
               
BEGIN
                       DELETE 
Save_Data WHERE ND_DT =@ND_DT
               
END                     

               
-- 데이터 입력
               
INSERT INTO Save_Data 
               
SELECT FROM Save_Data_Temp WHERE  ND_DT =@ND_DT

               -- 데이터 삭제
DELETE FROM Save_Data_Temp WHERE  ND_DT =@ND_DT                        FETCH NEXT FROM trigger_cur INTO @ND_DT 
         
END

   CLOSE 
trigger_cur
         
DEALLOCATE trigger_cur
END
 
 

MS-SQL 2005에서는 재귀적 관계를 가지는 데이터를 출력할 수 쿼리를 지원한다.
(MS-SQL2000 지원안함)

Oracle 에서의 Connect By와 같은 역할을 하는것으로
트리 구조의 메뉴를 출력할 때 사용한다.

예를 들어 어느 회사 조직도가 아래와 같다 하자.

부장 홍길동
       차장 김길동
              과장 고길동
                     대리 서길동
                     대리 안길동
              과장 이길동
                     대리 안길동
                     사원 최길동
       차장 채길동
.... 

부장 홍길동을 필두로 하여 트리 구조로 만들어져 있는것을 볼 수 있다.

위 트리 구조를 데이터로 간단하게 표현하면 아래와 같다.

사원번호       직함        성명       소속사번001              부장       홍길동      null
002              차장       김길동      001
003              과장       고길동      002
004              대리       서길동      003
005              대리       안길동      003
006              과장       이길동      002
007              대리       안길동      006
008              사원       최길동      006
009              차장       채길동      001

고길동의 직장 상사는 002 번인 차장 김길동 이다 라는 식으로 찾을 수 있겠다.
그럼 이 트리 구조를 쿼리로 작성할 때 차장 김길동을 포함한 부하직원을 모두 찾는다고 가정하자.
방법으로는 커서를 이용하여 찾는 방법도 있을 것이고, 몇개의 컬럼을 더 포함해서 찾을 수도 있을 것이다.

그러나 CTE(Common Table Expression) 기능중 WITH를 이용하면 쉽게 쿼리가 가능하다.
쿼리는 아래와 같다.

쿼리문
WITH EMP (사원번호, 직함, 성명, 소속사번) AS
(
     SELECT 사원번호, 직함, 성명, 소속사번
     FROM    사원
     WHERE  사원번호 = ''002'' -- 김길동
     UNION ALL
     SELECT  T1.사원번호, T1.직함, T1.성명, T1.소속사번
     FROM    사원 T1 INNER JOIN EMP T2 ON T2.사원번호 = T1.소속사번
)
SELECT *
FROM EMP;

실행 결과
002              차장       김길동      001
003              과장       고길동      002
004              대리       서길동      003
005              대리       안길동      003
006              과장       이길동      002
007              대리       안길동      006
008              사원       최길동      006

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

    다음은 결과 화면입니다.

        WCF 서비스를 이용한 Silverlight 구현 소스 참고

    ü  Sharepoint 서버에 구현하였던 WCF서비스 파일 배포

    1.     WCF 서비스 가상 디렉토리 생성하여 해당 디렉토리에 WCF 서비스 전담으로 만들었던 웹사이트 프로젝트의 파일을 아래 그림과 같이 배포합니다.


     -   Bin 폴더에 dll파일 배포 하지 않고, GAC에 등록하여 svc파일 상단에 아래에 음영 부분을 추가시켜 주시면 됩니다.

    Code

    Service.svc

    <%@ Assembly Name="WCFServiceLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f1a7a4b23352b442"%>

    <%@ ServiceHost Debug="true" Language="C#" Service="WCFServiceLibrary.ProductService" %>


    2.     생성한 가상디렉토리는 익명 액세스 가능하게 체크합니다.

     

    3.     Service.svc 파일을 선택한 후, 마우스 오른쪽 버튼을 클릭하여 웹 페이지로 보기항목을 선택하여 웹 페이지를 보면 Sharepoint 웹사이트에서는 VirtualPath에 관련된 아래와 같은 에러 화면을 볼 수 있습니다.

    4.     VirtualPath 해결 방안 (관련 링크)

    -     클래스 라이브러리 프로젝트를 만듭니다.

    -   자동으로 생성된 Class1.cs파일은 삭제하고 System.Web.dll을 참조시킵니다.

      아래 두 클래스를 추가하여 줍니다.

    Code

    WCFPatchupModule.cs

    public class WCFPatchupModule : IHttpModule

    {

        private static bool virtualPathProviderInitialized;

        private static readonly object virtualPathProviderInitializedSyncLock = new object();

     

        public void Dispose()

        {

        }

     

        public void Init(HttpApplication context)

        {

            if (!virtualPathProviderInitialized)

            {

                lock (virtualPathProviderInitializedSyncLock)

                {

                    if (!virtualPathProviderInitialized)

                    {

                        WCFVirtualPathProvider virtualPathProvider = new WCFVirtualPathProvider();

                        HostingEnvironment.RegisterVirtualPathProvider(virtualPathProvider);

                        virtualPathProviderInitialized = true;

                    }

                }

            }

        }

    }

     

    WCFVirtualPathProvider.cs

    public class WCFVirtualPathProvider : System.Web.Hosting.VirtualPathProvider

    {

        public override string CombineVirtualPaths(string basePath, string relativePath)

        {

            return base.Previous.CombineVirtualPaths(basePath, relativePath);

        }

     

        public override ObjRef CreateObjRef(Type requestedType)

        {

            return base.Previous.CreateObjRef(requestedType);

        }

     

        public override bool FileExists(string virtualPath)

        {

            string str = virtualPath;

            if (virtualPath.StartsWith("~", StringComparison.Ordinal) && virtualPath.EndsWith(".svc", StringComparison.InvariantCultureIgnoreCase))

            {

                str = virtualPath.Remove(0, 1);

            }

            return base.Previous.FileExists(str);

        }

     

        public override CacheDependency GetCacheDependency(string virtualPath, IEnumerable virtualPathDependencies, DateTime utcStart)

        {

            return base.Previous.GetCacheDependency(virtualPath, virtualPathDependencies, utcStart);

        }

     

        public override string GetCacheKey(string virtualPath)

        {

            return base.Previous.GetCacheKey(virtualPath);

        }

     

        public override VirtualDirectory GetDirectory(string virtualDir)

        {

            return base.Previous.GetDirectory(virtualDir);

        }

     

        public override VirtualFile GetFile(string virtualPath)

        {

            return base.Previous.GetFile(virtualPath);

        }

     

        public override string GetFileHash(string virtualPath, IEnumerable virtualPathDependencies)

        {

            return base.Previous.GetFileHash(virtualPath, virtualPathDependencies);

        }

    }

    -     어셈블리 서명을 추가해주고, 해당 어셈블리 파일을 GAC에 등록합니다.

    -     해당 웹사이트의 Web.Config 파일을 보면, <httpModules>세션에 음영된 부분을 추가합니다

    Code

    Web.Confing

    <httpModules>

      <clear />

      <add name="SPRequest" type="Microsoft.SharePoint.ApplicationRuntime.SPRequestModule, Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />

      <add name="OutputCache" type="System.Web.Caching.OutputCacheModule" />

      <add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule" />

      <add name="UrlAuthorization" type="System.Web.Security.UrlAuthorizationModule" />

      <add name="WindowsAuthentication" type="System.Web.Security.WindowsAuthenticationModule" />

      <add name="RoleManager" type="System.Web.Security.RoleManagerModule" />

      <!-- <add name="Session" type="System.Web.SessionState.SessionStateModule"/> -->

      <add name="PublishingHttpModule" type="Microsoft.SharePoint.Publishing.PublishingHttpModule, Microsoft.SharePoint.Publishing, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />

      <!-- Ajax & SIlverlight -->

      <add name="Session" type="System.Web.SessionState.SessionStateModule" />

      <add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />

      <add name="WCFVirtualPathProvider" type="WCFVirtualPathProvider.WCFPatchupModule, WCFVirtualPathProvider, Version=1.0.0.0, Culture=neutral, PublicKeyToken=2bea20c5375d001d"/>

    </httpModules>

    -   Service.svc 파일을 웹 브라우저로 열어 서비스가 정상 작동하지는 확인합니다.

    ü  Silverlight 응용 프로그램 프로젝트 수정하기

    1.     기존에 만든 Silverlight 프로젝트를 열어 참조된 서비스를 클릭하고 서비스 참조 구성이라는 항목을 선택합니다.

    2.    클라이언트 주소가 나오는데 배포한 WCF 서비스 URL로 변경합니다. 업데이트 진행 상태가 나타나며, 문제가 없으면 업데이트가 완료됩니다.

    ü  Sharepoint Silverlight WebPart 만들기

    1.  Sharepoint 웹파트 추가 하는 종류

    -   IIS Silverlight 전담 웹사이트를 만들고 배포한 후 Sharepoint의 기본 웹파트 중에 하나인 페이지 뷰어 웹파트를 이용하여 실버라이트를 보여주는 방법이 있습니다.

    -   System.Web.UI.SilverlightControls.Silverlight 컨트롤을 사용하여 웹파트 클래스를 만드는 방법이 있습니다.

    2.  우선 여기서는 Silverlight 컨트롤을 이용하여 웹파트를 추가하도록 하겠습니다. 새 프로젝트 추가합니다. (프로젝트 형식은 클래스 라이브러리을 선택합니다.)

    -   실버라이트 응용 프로그램 빌드 후, xap 파일을 _layouts의 특정 폴더 배포합니다. )다른 방법으로는 문서 라이브러리 등에 배포 하셔도 상관없습니다.)

    -  Silverlight 웹파트 클래스 만들기

    Code

    DemoSilverlight.cs

    using System;

    using System.Web.UI;

    using System.Web.UI.WebControls;

    using Microsoft.SharePoint;

     

    namespace SilverlightWebParts

    {

        public class DemoSilverlight : Microsoft.SharePoint.WebPartPages.WebPart

        {

            protected override void OnLoad(EventArgs e)

            {

                base.OnLoad(e);

     

                ScriptManager sm = ScriptManager.GetCurrent(this.Page);

                if (sm == null)

                {

                    sm = new ScriptManager();

                    Controls.AddAt(0, sm);

                }

            }

     

            protected override void CreateChildControls()

            {

                base.CreateChildControls();

     

                System.Web.UI.SilverlightControls.Silverlight ctrlSilverlight = new System.Web.UI.SilverlightControls.Silverlight();

                ctrlSilverlight.ID = "SilverlightControl";

                ctrlSilverlight.Source = SPContext.Current.Web.Url + "/_layouts/Silverlight/SilverlightApplication.xap";

                ctrlSilverlight.Width = new Unit(600);

                ctrlSilverlight.Height = new Unit(400);

     

                this.Controls.Add(ctrlSilverlight);

            }

        }

    }

    Description

    ð  로드 이벤트(OnLoad 이벤트)에 보시면 ScriptManager 라는 컨트롤이 추가되어 있는데, 반드시 추가하여야 합니다. 클래스 안에 추가하는 방법 말고, 마스터 페이지 상단에 <asp:ScriptManager ID=”ScriptManager1” ></asp:ScriptManager>라고 추가하셔도 됩니다.

    ð Silverlight 컨트롤에 많은 속성들이 있는데 그 중에 Source를 보시면 xap파일(Silverlight 패키지 파일)을 배포한 URL 주소가 들어 있습니다.

    3.    해당 dll 파일을 GAC에 등록하시고, Sharepoint 웹파트 갤러리에 추가한 후, 원하는 화면에 웹파트를 추가하시면 아래와 같은 Silverlight 웹파트가 완성됩니다.

    ü  Install .NET 3.5 Framework SP1 in Sharepoint Server

    ü  Windows Sharepoint Service 3.0 SP1 & MOSS 2007 SP1

    ü  GAC System.Web.Silverlight.dll 등록

    1.    저장 폴더 위치

    -       Microsoft Silverlight Tools for Visual Studio 2008 SP1 개발 환경이 구성되어 있거나 Silverlight 2 SDK 설치(링크)하면, C:\Program Files\Microsoft SDKs\Silverlight\v2.0\Libraries\Server 폴더 안에 System.Web.Silverlight.dll이 존재합니다.

    ü  IIS MIME Type 정의

    1.     인터넷 정보 서비스 관리 창을 뜨웁니다.

    -       시작 실행에서 inetmgr 명령어를 입력하여 실행하여 Silverlight 적용시킬 Sharepoint 웹사이트의 속성 창을 엽니다.

    -       HTTP 헤더 탭을 선택한 후, MIME 형식 버튼을 클릭합니다.

    -       MIME 형식 창이 나타나며, 새 형식 버튼을 클릭하여 확장명에 .xap, MIME 형식에 application/x-silverligth-app을 등록합니다.

    =>

    ü  Sharepoint 웹사이트 해당 포트에 대한 Web.Config 수정

    -       Web.config (C:\Inetpub\wwwroot\wss\VirtualDirectories\포트번호)에 아래와 같이 음영 처리된 부분을 추가합니다.

    1.     WCF 서비스를 구동시키기

    -       서비스를 담당할 전담 웹사이트 프로젝트를 만듭니다.


    -       저장 위치를 지정하고, WCF 서비스 항목을 선택합니다.


    -       App_Code폴더와 그 안에 IService.cs, Service.cs 파일 그리고, Service.svc 파일이 자동 생성된 것을 볼 수 있는데, 서비스 구현은 이미 했기 때문에 Service.svc파일을 제외한 두 개의 파일들을 과감하게 삭제 해버립니다.


    -       참조 추가 항목을 선택하고, WCF서비스 라이브러리 프로젝트 항목을 선택합니다. 그리고, Bin 폴더에 dll이 잘 추가된 것을 볼 수 있습니다.

     

    -       Service.svc 파일을 수정합니다.

    자동 생성되었던 Service.svc

    <%@ ServiceHost Language="C#" Debug="true" Service="Service" CodeBehind="~/App_Code/Service.cs" %>

     

    수정된 Service.svc

    <%@ ServiceHost Debug="true" Language="C#" Service="WCFServiceLibrary.ProductService" %>

    ð  기존 파일에는 Service태그에 클래스명을 지정되어 있고, CodeBehind에 해당 코드 주소를 입력하였으나, 별도의 컴포넌트로 구현하였기에, 컴포넌트 이름에 해당 서비스 클래스명을 지정해 주면 됩니다.

    -       Web.config 수정하여야 하는데, 전에 app.config 파일을 WCF 구성 편집 항목을 통해 수정해 본 적이 있습니다. 그 방식과 같이 WCF서비스 구성을 편집합니다. (9번 항목)

    -       Web.config 파일을 열어 보면 <system.serviceModel>부분을 제외하고 나머지는 과감하게 삭제해버립니다.

    Web.config

    <?xml version="1.0" encoding="utf-8"?>

    <configuration>

      <system.serviceModel>

        <behaviors>

          <serviceBehaviors>

            <behavior name="ServiceBehavior">

              <serviceMetadata httpGetEnabled="true" />

              <serviceDebug includeExceptionDetailInFaults="false" />

            </behavior>

          </serviceBehaviors>

        </behaviors>

        <services>

          <service behaviorConfiguration="ServiceBehavior" name="WCFServiceLibrary.ProductService">

            <endpoint address="" binding="basicHttpBinding" bindingConfiguration=""

              contract="WCFServiceLibrary.IProductService">

              <identity>

                <dns value="localhost" />

              </identity>

            </endpoint>

            <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />

          </service>

        </services>

      </system.serviceModel>

    </configuration>

    -       WCFServiceWebSite 웹사이트의 속성에 가상 경로가 “/”가 아닌 다른 경로가 지정된 경우가 있는데, “/”로 수정합니다. 조금 있다 알게 되겠지만, 정책 파일을 찾는 경로는 웹사이트 최상위 루트이기 때문에 바꿔주어야 합니다.

     

    -       Service.svc브라우저에서 보기 항목을 선택하여 잘 구동되는지를 확인합니다.


    -       WCF 테스트 브라우저 창을 통해 WCF 서비스 역할이 잘 작동하는지 확인한 적이 있습니다. 웹사이트에서도 잘 되는지 확인하겠습니다. Visual Studio 2008 명령 프롬프트를 선택하고, cmd창이 나타나는 것을 볼 수 있습니다. (cmd창에서 Visual Studio 2008 설치 경로\VC로 들어가시면 됩니다. 기본 설치시 C:\Program Files\Microsoft Visual Studio 9.0\VC 입니다.)


    -       좀 전에 svc 파일을 브라우저에서 봤었는데, cmd창에 명령어를 “wcfTestClient + 브라우저에서 봤던 url”를 입력하시면 WCF 테스트 브라우저 창이 나타나며, 전에 했던 방식으로 서비스 역할 테스트를 해보시면 됩니다. (부가 설명은 하지 않겠습니다.)


    2.     Silverlight 응용 프로그램 만들기 (위에서 설명한 바가 있어, 자세한 설명은 하지 않겠습니다)

    -       솔루션에 새 프로젝트 추가 한 후, 프로젝트명과 저장 경로를 지정하고, Silverlight 응용 프로그램을 선택합니다.

    -       Silverlight를 호스팅할 웹 사이트 지정하는 브라우저 창이 나타나며, WCFServiceWebSite WCF 서비스 전담으로 만든 프로젝트이므로, “솔루션에 Silverlight를 호스팅할 새 ASP.NET 웹 프로젝트 추가항목을 선택하여 웹 사이트를 추가합니다.

    -       Silverlight 영역에 DataGrid 컨트롤 및 각 이벤트의 버튼을 추가하여 UI을 구상합니다. Microsoft Blend2를 실행시켜 디자인 작업하면 손쉽게 진행할 수 있습니다.

    Code

    Page.xaml

    <UserControl xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"  x:Class="SilverlightApplication.Page"

        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >

        <Grid x:Name="LayoutRoot" Background="White">

            <Grid.RowDefinitions>

                <RowDefinition Height="60"></RowDefinition>

                <RowDefinition Height="*"></RowDefinition>

            </Grid.RowDefinitions>

           

    <StackPanel Orientation="Horizontal" Grid.Row="0">

                <Button x:Name="btnSelect" Width="60"  Height="35" Margin="5" Content="보기"></Button>

                <Button x:Name="btnInsert" Width="60"  Height="35" Margin="5" Content="추가"></Button>

                <Button x:Name="btnUpdate" Width="60"  Height="35" Margin="5" Content="수정"></Button>

                <Button x:Name="btnDelete" Width="60"  Height="35" Margin="5" Content="삭제"></Button>

            </StackPanel>

    <data:DataGrid Grid.Row="1"></data:DataGrid>

        </Grid>

    </UserControl>

    -       Silverlight 응용 프로젝트에 WCF 서비스를 참조합니다. 좀전에 WCF 서비스 전담 웹사이트에서의 svc 파일 URL을 주소에 입력하고 이동하면, 문제가 없다면 인터페이스 목록이 나타납니다. 그리고 서비스 참조 네임스페이스를 입력하고 추가합니다.


    -       UI 구성 및 서비스 참조가 완료한 후, 모든 버튼의 이벤트 함수를 구현하기에 앞 서, “보기 버튼에 대한 이벤트를 구현해보겠습니다.

    Code

    Page.xaml

    <!-- 생략 -->

    <StackPanel Orientation="Horizontal" Grid.Row="0">

        <Button x:Name="btnSelect" Width="60"  Height="35" Margin="5" Content="보기" Click="btnSelect_Click"></Button>

        <Button x:Name="btnInsert" Width="60"  Height="35" Margin="5" Content="추가" Click="btnInsert_Click"></Button>

        <Button x:Name="btnUpdate" Width="60"  Height="35" Margin="5" Content="수정" Click="btnUpdate_Click"></Button>

        <Button x:Name="btnDelete" Width="60"  Height="35" Margin="5" Content="삭제" Click="btnDelete_Click"></Button>

    </StackPanel>

    <data:DataGrid Grid.Row="1" x:Name="dlResult" Background="Beige"></data:DataGrid>

    <!-- 생략 -->

     

    Page.xaml.cs

    public partial class Page : UserControl

    {

        ProductDBService.ProductServiceClient serivce = new ProductDBService.ProductServiceClient();

     

        public Page()

        {

            InitializeComponent();

        }

     

        private void btnSelect_Click(object sender, RoutedEventArgs e)

        {

            serivce.SelectProductAsync();

            serivce.SelectProductCompleted += new EventHandler<SelectProductCompletedEventArgs>(serivce_SelectProductCompleted);

        }

     

        void serivce_SelectProductCompleted(object sender, SelectProductCompletedEventArgs e)

        {

            dlResult.ItemsSource = e.Result;

        }

     

        private void btnInsert_Click(object sender, RoutedEventArgs e) {  }

     

        private void btnUpdate_Click(object sender, RoutedEventArgs e) {  }

     

        private void btnDelete_Click(object sender, RoutedEventArgs e) {  }

    }

    Description

    ð  WCF 서비스에 노출된 서비스를 보면, Async 함수와 Completed 이벤트를 가지게 되는 것을 볼 수 있습니다. 간단히 설명드리면, Async 함수로 필요한 인자를 넣고 실행시키면, Completed 이벤트 함수에서 Result(WCF 서비스 호출한 데이터)를 보내주는 방식입니다.


    ð  DataList 컨트롤에 서비스 호출하여 Select한 데이터를 바인딩한 후, 잘 동작하는 지 실행(F5)합니다. 하지만, 아래와 같은 오류가 발생하는 것을 볼 수 있으며, IE 개발 툴인 HttpWatch, Fidder, WebDevHelper 등을 이용하여 조회하여 보면, 2개의 파일에서 404. 파일이 없다는 결과를 볼 수 있습니다.


    ð  Silverlight는 크로스 도메인에 대해서 제약이 되어 있다고 설명한 바가 있습니다. 크로스 도메인 접근을 위해서는 웹사이트의 최상위 루트 디렉토리에 보안 정책 파일인 clientaccesspolicy.xml 파일과 crossdomain.xml 파일을 배포하여야 합니다.

    (참고 : Silverlight 2.0 Tutorial)

    -       보안 정책 파일을 WCF 서비스 웹사이트의 최상위 루트에 배포한 후, 다시 실행하면, 잘 작동하는 것을 볼 수 있습니다.


    -       마지막으로 남은 버튼의 이벤트도 구현하겠습니다.

    Code

    public partial class Page : UserControl

    {

        ProductDBService.ProductServiceClient serivce = new ProductDBService.ProductServiceClient();

     

        public Page()

        {

            InitializeComponent();

        }

     

        private void btnSelect_Click(object sender, RoutedEventArgs e)

        {

            serivce.SelectProductAsync();

            serivce.SelectProductCompleted += new EventHandler<SelectProductCompletedEventArgs>(serivce_SelectProductCompleted);

        }

     

        void serivce_SelectProductCompleted(object sender, SelectProductCompletedEventArgs e)

        {

            dlResult.ItemsSource = e.Result;

        }

     

        private void btnInsert_Click(object sender, RoutedEventArgs e)

        {

            Products product = new Products();

            product.ProductName = "TEST";

     

            serivce.InsertProductAsync(product);

            serivce.InsertProductCompleted += new EventHandler<InsertProductCompletedEventArgs>(serivce_InsertProductCompleted);

        }

     

        void serivce_InsertProductCompleted(object sender, InsertProductCompletedEventArgs e)

        {

            Refresh(e.Result);

        }

     

        private void btnUpdate_Click(object sender, RoutedEventArgs e)

        {

            serivce.UpdateProductAsync(dlResult.SelectedItem as Products);

            serivce.UpdateProductCompleted += new EventHandler<UpdateProductCompletedEventArgs>(serivce_UpdateProductCompleted);

        }

     

        void serivce_UpdateProductCompleted(object sender, UpdateProductCompletedEventArgs e)

        {

            Refresh(e.Result);

        }

     

        private void btnDelete_Click(object sender, RoutedEventArgs e)

        {

            serivce.DeleteProductAsync((dlResult.SelectedItem as Products).ProductName);

            serivce.DeleteProductCompleted += new EventHandler<DeleteProductCompletedEventArgs>(serivce_DeleteProductCompleted);

        }

     

        void serivce_DeleteProductCompleted(object sender, DeleteProductCompletedEventArgs e)

        {

            Refresh(e.Result);

        }

     

        private void Refresh(bool result)

        {

            if (result)

            {

                MessageBox.Show("작업 완료 되었습니다.");

                serivce.SelectProductAsync();

            }

            else

            {

                MessageBox.Show("작업 실패하였습니다.");

            }

        }

    }

    Description

    ð  각 버튼의 이벤트를 구현하였으며, 추가, 수정, 삭제 시 리플래시하였습니다.

     

     

    l  참고 사이트

    -       MSDN Library (WCF)

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

     

    1.     WCF 서비스 구현하기

    -      이름을 넣고 WCF 서비스 항목을 선택하여 추가하면 App.config, ProductService.cs 그리고 인터페이스 IProductService.cs 파일이 자동 생성되어 있으며, 샘플 코드는 삭제합니다.

    -       CRUD 기능 구현을 위해 인터페이스에 메서드 재정의를 선언합니다.

    Code

    IProductService.cs

    [ServiceContract]
    public interface IProductService
    {
        [OperationContract]
        bool InsertProduct(Products product);

        [OperationContract]
        bool UpdateProduct(Products product);

        [OperationContract]
        bool DeleteProduct(string name);

        [OperationContract]
        List<Products> SelectProduct();
    }

    ð  인터페이스 IProductService.cs 상단 부분에 보면 [ServiceContract]이라는 속성이 명시되어 있습니다. WCF 응용 프로그램의 서비스 계약을 정의하는 것으로, 반드시 선언하여야 합니다.

    ð  메서드 재정의 부분에 [OperationContract]이라는 속성이 명시되어 있으며, WCF 응용 프로그램의 서비스 계약의 일부인 작업을 정의합니다.

    -       CRUD(Insert, Update, Delete, Select) 로직을 구현합니다.

    Code

    ProductService.cs

    namespace WCFServiceLibrary

    {

        [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]

        public class ProductService : IProductService

        {

            NorthwindDBDataContext dc = new NorthwindDBDataContext();

     

            public bool InsertProduct(Products product)

            {

                try

                {

                    dc.Products.InsertOnSubmit(product);

                    dc.SubmitChanges();

     

                    return true;

                }

                catch { return false; }

            }

     

            public bool UpdateProduct(Products product)

            {

                try

                {

                    var target = (from tmp in dc.Products

                                  where tmp.ProductID == product.ProductID

                                  select tmp).Single();

                   

                    target.ProductID = product.ProductID;

                    target.ProductName = product.ProductName;

                    target.SupplierID = product.SupplierID;

                    target.CategoryID = product.CategoryID;

                    target.QuantityPerUnit = product.QuantityPerUnit;

                    target.UnitPrice = product.UnitPrice;

                    target.UnitsInStock = product.UnitsInStock;

                    target.UnitsOnOrder = product.UnitsOnOrder;

                    target.ReorderLevel = product.ReorderLevel;

                    target.Discontinued = product.Discontinued;

                    dc.SubmitChanges();

     

                    return true;

                }

                catch { return false; }

            }

     

            public bool DeleteProduct(string name)

            {

                try

                {

                    var target = (from tmp in dc.Products

                                  where tmp.ProductName.Equals(name)

                                  select tmp).ToList();

     

                    dc.Products.DeleteOnSubmit(target[0]);

                    dc.SubmitChanges();

     

                    return true;

                }

                catch { return false; }

            }

     

            public List<Products> SelectProduct()

            {

                var target = (from tmp in dc.Products

                              select tmp).ToList();

     

                return target;

            }

        }

    }

    ð  클래스 상단 부분에 [ServiceBehavior]이라는 속성이 명시되어 있으며, 서비스 계약 구현의 내부 실행 동작을 지정합니다. InstanceContextMode는 들어오는 메시지에 포함된 호출을 처리할 수 있는 서비스 인스턴스 수를 지정합니다. WCF 서비스 구현에 중요한 요소이므로 자세히 알아보겠습니다.

    모드

    설명

    Single

    개체가 들어오는 모든 호출에 대해 사용되며 호출 후에는 재활용되지 않습니다. 서비스 개체가 없는 경우 새로 만들어집니다. (공유개념)

    PerCall

    매번 호출하기 전에 만들어지고 매번 호출한 후에 재활용됩니다. 채널에서 세션을 만들지 않으면 이 값은 PerCall인 것처럼 동작합니다. (비동기인 Silverlight에서는 의미없는 모드라고 볼 수 있습니다.)

    PerSession

    각 세션마다 개체가 만들어집니다. (클라이언트가 더 이상 필요로 하지 않을 때까지 인스턴스가 존재합니다.)

    ð  LINQ에 대한 MSDN Library 링크

    2.    빌드(F6), App.config 파일 수정 (해당 기능 꼭 알아두기)

    -       해당 파일을 열어 수정하는 방법도 있지만, WCF 구성 편집 항목을 이용하여 편집해보겠습니다.

    -      App.config 파일에 마우스 오른쪽 버튼을 클릭하면 WCF 구성 편집 항목을 선택합니다.

    -       WCF 구성 편집 창이 나타나며, WCF 서비스 라이브러리에 정의된 것과 같은지 확인합니다. 틀릴 경우 해당 항목을 선택하면 <!--[if !vml]--><!--[endif]-->이 나타나는데 클릭합니다. 그리고, bin폴더/Debug폴더 안에 WCFServiceLibrary.dll을 선택한 후, 알맞은 형식 이름을 선택하시면 자동으로 변경됩니다.

    -       위 파트에서 설명한 바가 있지만, Silverlight에서는 바인딩을 basicHttpBinding만 지원하므로 반드시 변경해주여야 합니다.

    -       그리고 Contact 속성이 맞게 지정되어 있는지 확인합니다. 틀릴 경우 위에서 했던 방식으로 알맞은 형식 이름을 선택합니다.

    3.     WCF 서비스 구성이 잘 되어 작동이 이상유무를 확인해 봅니다.

    -       실행(F5)를 해보면, 아래와 같이 WCF 테스트 클라이언트 창이 나타나며, 트라이아이콘에 WCF 서비스 호스트 아이콘이 나타나며 구성에는 이상없음을 나타냅니다.

    -       SelectProduct()을 더블클릭 한 후, 별다른 인자가 특별 없기 때문에 우측에 호출 버튼을 클릭합니다. Length에 아마 NorthWind 데이터베이스의 Products테이블의 아이템 개수와 동일하게 나나타나는 것을 볼 수 있습니다.

    -       이번에는 InsertProduct()을 더블클릭 한 후, 각 변수의 임의의 값을 넣어주고 호출을 하면 성공시, 응답 화면에 return 값에 true가 나타나며, DB 테이블에 아이템 개수가 한 개 늘어난 것을 볼 수 있습니다.

    ü  WCF 서비스 라이브러리를 사용하여 WCF Service 만들기

    1.     구현 설명

    -       Linq to Sql 클래스를 통해 DB 서비스 구현

    -       CRUD(Select, Insert, Update, Delete) 기능 구현

    -       Silverlight DataGrid 컨트롤 추가하여 데이터 바인딩 및 CRUD 이벤트 구현

    2.     WCF 서비스 라이러브리 프로젝트 추가하기

    -       웹사이트에 Silverlight 사용 WCF 서비스 항목을 선택하여 서비스 구현하여도 상관없지만, 확장성 및 유지 보수를 고려해 볼 때, WCF 서비스 라이브러리 프로젝트로 서비스 구현하는 것이 관리하기 좋습니다.

    -       WCF 서비스 라이브러리 항목을 선택 한 후, 새 프로젝트를 추가합니다.

    -       IService1.cs Service1.cs, App.config 세 개의 파일이 생성되어 있는 것을 볼 수 있으며, 모두 삭제 한 후 새로 구현해 보겠습니다.

    3.     “Northwind and pubs Sample Databases for SQL Server 2000” 를 다운 받아 샘플 데이터베이스 환경을 만듭니다.

    -       링크 페이지에서 SQL2000SampleDb.msi 파일을 다운 받아 설치 합니다. (링크)

    -       기본적으로 C:\SQL Server 2000 Sample Databases 아래 데이터 베이스 파일이 저장됩니다.

    -       Microsoft SQL Server Studio를 열어, Northwind DB를 연결 추가합니다.

    -      연결할 데이터 베이스 추가할 항목을 선택한 후, 추가합니다.

     =>

    4.    LINQ to SQL 클래스 추가한 후, DB 연결 하기

    -      WCF 서비스 라이브러리 프로젝트에 LINQ to SQL 클래스를 선택하고, 이름 작성한 후, 추가버튼을 클릭합니다.

    -      아래와 같은 화면이 나타나며 서버 탐색기를 열어(메뉴에서 보기à서버 탐색기 혹은 Ctrl + W, L) 샘플 데이터 베이스(Northwind) 스키마를 추가합니다.

    -      서버 탐색기에서 데이터 연결에 마우스 오른쪽 버튼을 클릭하면 연결 추가가 나오며, Microsoft SQL Server를 선택한 후, 계속 버튼을 클릭합니다.

     =>

    -       연결할 서버 이름 및 데이터베이스 이름을 선택하며, 연결 테스트 버튼을 클릭한 후, “테스트 연결에 성공하였습니다.” 라는 성공 메시지가 나타나면 확인을 누릅니다.

    5.     Northwind 데이터베이스 스키마가 추가된 것을 볼 수 있으며, 테이블 및 저장 프로시저를 볼 수 있습니다.

    -       SQL Server 2005 Management Studio 에서의 기본적인 기능은 제공한다고 합니다.

    (데이블 데이터 조회, 쿼리 디자이너, 저장 프로시저 작성)

    6.     작업을 진행할 테이블 및 저장 프로시저를 오른쪽 화면으로 드래그하면 아래 화면과 같이 추가됩니다. 여러 개의 테이블을 사용할 경우에는 여러 개를 드래그하여 스키마를 관리하면 됩니다.

    7.    속성 창을 열어 DataContext 정의 할 때, Serialization Mode None에서 Unidirectional으로 변경해 줍니다. (객체를 직렬화)

    + Recent posts