변환기는 왜?

올해 여름 엔팩스를 무척 많이 쓰던 시절에 만든 어플이 하나 있습니다. ( http://www.hind.pe.kr/1151 )
보통 팩스기계를 보유하고 있는 곳이면 팩스 송/수신에 큰 어려움이 없지만, 작은 회사나, 외부 업무 중 Fax 전달하려고 할 때 애매한 상황에 빠지기 쉽습니다. 이에 대한 대안점으로 사용하는 방법이 인터넷 팩스 입니다.

사실 인터넷 팩스 업체는 의외로 사이징을 가지고 발전하고 있습니다. 그래서 처음에는 엔팩스 라는 업체만 하나 있었는데, 어느새 어지간한 인터넷 서비스 제공하는 업체에서는 상품으로 제공하거나, 기타 여러 회사들이 건립되어 서비스를 운영하고 있더군요. ( 다 찾아보지는 않았습니다만.. )

저는 엔팩스에 충전한 금액이 있다보니, 그것을 활용하는 차원에서 엔팩스를 사용하는데요, 문제는 이 엔팩스를 통해 문서를 전달하려고 할 때 입니다. 인터넷 팩스다 보니, 모든 UI는 웹 브라우저고, 그 웹브라우저의 한계를 조금 벗어나 보려고, Active X를 사용하긴 합니다. 하지만, 파일 전송 사이즈가 10M 제한이고, 그 이미지를 변환하는데 생각보다 많은 시간이 소요됩니다.

예를 들어, 스캐너를 이용해 300dpi의 이미지를 10장 정도를 업로드 하려면, 이거 생각보다 까탈스러운 작업을 해야 합니다. 10장 이미지를 하나씩 하나씩 등록해주고, 미리 보기 버튼을 눌러 Active X로 된 변환 도구 띄워놓고, 잘 올라가는지 체크합니다. 만일 이미지 사이즈가 크거나 이러면 꽝입니다.

이점이 너무 불편해 이미지 변환기를 만들어 아예 이미지 사이즈를 통일하고, 정리해서 팩스 전용 TIFF 파일을 만들도록 했습니다. FAX용 TIFF 파일의 경우 흑백에 해상도도 그만 그만하기 때문에, 10메가를 훌쩍 넘는 이미지들이 넘쳐도, FAX용 TIFF으로 만들면 끽 해야 1M 도 안되는 사이즈가 됩니다. 게다가 하나의 파일 안에 여러개의 이미지를 담을 수 있기 때문에, 여러 장의 문서의 경우 하나의 파일로 묶을 수 있는 것이죠.

그러면 엔팩스와 같은 인터넷 팩스 업체에 업로드 할 때 빠르고 간편하게 업로드를 할 수 있게 되는 것입니다.

 

업데이트 로그

개인적으로 만들어서 나름 쓰면서, 몇 가지 오류 사항들을 추가적으로 수정하여 정리했습니다.

  • (FIX) Thumbnail 처리 및 Rotate 처리를 할 때, Thread로 처리하여, Not Response 상태에 빠지지 않도록 수정.
  • (FIX) Rotation 을 할 때, Thumbnail 이미지가 변경되지 않는 문제 해결.
  • (FIX) 대용량 이미지 로딩시, Out of Memory 현상 발생되는 문제 해결.
  • (FIX) FAX용 해상도인 204/196이 아닌 294/106으로 설정되어 이미지가 깨지는 문제 해결.
  • (UPDATE) 이미지 Thumnail 기능 지원
  • (UPDATE) 이미지 회전을 지원
  • (UPDATE) 파일 처리시 Thread 형태로 실행될 수 있도록 수정.
  • (UPDATE) 파일 선택시 하나만 선택되는 형태를 여러개가 선택될 수 있도록 수정.
  • (UPDATE) UI 배치를 일부 변경
  • (UPDATE)  Naver 개발자 센터 오픈프로젝트 등록.
  • (CREATE) 최초 프로젝트 구성.

대충의 History는 위와 같습니다.

모든 소스의 내용은 SVN을 이용해서 다운 받을 수 있습니다. ( 다운 받는 설명은 http://dev.naver.com/projects/imgconvertforfa/code?viewfunc=browse 에서 참고하시기 바랍니다. )

 

전체 UI 설명

먼저 전체 UI는 다음과 같습니다.

먼저 좌측 상단의 목록은 팩스 전달용 이미지 목록입니다. 스캔한 문서를 기반으로 한다고 가정해서 만든 것이기 때문에 이미지 파일만 지원합니다. 저 목록 순서가 팩스로 날라가는 순서가 됩니다. 등록된 파일 목록 중 하나를 선택하면 미리보기 창에 해당하는 이미지를 표시 합니다.

그리고 오른편에 있는 L과 R 버튼 그리고 아래의 이미지가 바로 화면 회전과 미리보기 창입니다.

이미지 스캔을 위 아래가 바뀌어 되기도 하기 때문에, 문서 정렬 작업을 여기서 해주는 것이죠. L은 왼쪽 회전 R을 오른쪽 회전으로 회전 시켜보면서 아래의 Thumnail 부분을 확인하시면 됩니다. 회전을 시키면 원본 이미지도 회전된 형태 그대로 저장됩니다.

맨 오른편에 위치한 버튼이 여기서 나오는 모든 기능을 나타냅니다.

"Add" 이미지를 추가합니다. 여러 개의 이미지를 동시에 선택할 수 있습니다. 선택한 순서대로 차례대로 추가됩니다.

"Del" 왼편의 이미지 목록 중 선택된 이미지를 목록에서 삭제합니다. 원본 파일은 그대로 존재 합니다.

"Up" 왼편의 이미지 목록 중 선택된 이미지를 위쪽으로 이동시킵니다. 여러 페이지의 팩스용 이미지가 생성될 때, 그 순서를 결정하게 됩니다.

"Down" 왼편의 이미지 목록 중 선택된 이미지를 아래쪽으로 이동시킵니다. 여러 페이지의 팩스용 이미지가 생성될 때, 그 순서를 결정하게 됩니다.

"Convert" 이미지 목록에 설정된 모든 이미지를 가지고 팩스용 이미지를 생성합니다. 생성할 때, 파일이름을 결정하기 위한 저장용 다이얼로그가 뜨는데, 적당한 위치를 선택하고, 적당한 이름을 넣어주시면 해당 하는 이름으로 저장합니다.

마지막으로 모든 처리를 위한 로그를 표시하기 위한 창을 맨 아래에 위치한 TextBox를 통해 표시합니다.

 

사용방법

  1. 먼저 사용할 이미지를 선택합니다.
    Add 버튼을 눌러 나오는 다이얼로그 창을 이용해서 원하는 이미지를 선택합니다. 선택하신 후 "확인" 혹은 "Ok"를 선택합니다. 여러 개를 선택할 때, 마우스로 드래그를 하거나,  "Ctrl"버튼을 누른 상태로 선택하시면 됩니다.
  2. 선택된 이미지 정리
    만일 뒤집어져 있는 이미지가 있거나, 기울어 졌으면 "L" 혹은 "R" 버튼을 눌러 팩스로 전달될 방향을 기준으로 설정합니다. 왼쪽 혹은 오른쪽으로 회전시키면 됩니다.
    그리고 페이지 순서가 잘못되었으면 잘못된 페이지를 선택하고, "Up" 혹은 "Down"버튼을 눌러 적당한 위치를 선택하여 줍니다.
  3. 변환하기.
    파일 목록이 모두 정리가 잘 되었다면 이제 "Conver" 버튼을 클릭하시면 됩니다. 그러면 저장될 파일이름을 결정하기 위한 파일 저장 다이얼로그가 뜨는데, 여기서 적당한 위치와 이름을 결정해주시면 됩니다.
    여러 개의 이미지 파일이라도, 1개의 TIFF 파일로 만들어 지므로, 파일 이름 한 개만 정해주면 됩니다.
    해당 위치에 결정한 파일이름의 TIF 파일이 저장될 것입니다.

정리

사실 단순한 변환 프로그램이기 때문에, 크게 사용방법이 어렵지는 않습니다.

게다가, 설치용 프로그램도 아니고, 실행파일 만 있으면 동작하는데 아무런 문제가 없는 단순한 프로그램입니다.
(배포용 Release.Zip 파일 내에 있는 파일은 딱 세 개 입니다. 하나는 실행파일인 exe 그리고 디버그용 pdb 파일, 마지막으로 config 파일. exe를 제외한 나머지 파일은 없어도 무방합니다.)

.NET 기반 이여서 약간은 부담될 수 있지만, Windows 업데이트를 어느 정도 수행했다면, 대부분 Windows 기반 PC에는 .NET Framework가 설치되어 있으므로, 큰 장벽은 아니라고 생각됩니다.

인터넷 팩스를 자주 사용한다면, 게다가, 여러 장의 팩스를 자주 보낸다면, 나름 편리할 거라는 생각이 듭니다. ㅋ


관련 URL

프로그램 소스 위치 : http://dev.naver.com/projects/imgconvertforfa/src ( SVN 도구와 Visual Studio 2010 이 있어야 됩니다. )

프로그램 실행 파일 위치 : http://dev.naver.com/projects/imgconvertforfa/download ( 가장 최신 파일을 받으세요 )

728x90

서두

System.Data.DataRow

데이터베이스 기반의 CS나 Web 관련 개발을 하면 DataTable을 많이 활용하는데, 이 때 데이터들은 DataRow를 통해서 전달 받게 된다. 그런데, 이 DataRow에 담긴 값들은 모두 Object 형식으로 담겨 있는데, 이 때 데이터를 끄집어 내는데, 그냥 ASP 시절의 웹 프로그래밍 방식을 따르게 되면 오류 발생율이 높다. 물론 의도적으로 try ~ catch 문에서 끄집어 내기 위해서 만들기도 하지만, 단순히 데이터를 끄집어 오는데, try ~ catch로 잡아야 될 정도면 개인적으로는 아니다 싶을 때가 많다.

 

본문

예를 들면, Person 이라는 테이블이 있는데, Name - varchar(50), Age – int, Address – varchar(100) 의 형태의 테이블이 있다고 하자.

Name Age Address
Goshe Mack 12 Gangnam
Park Sung-ook 23 [NULL]
Sonia Aka 22 Mapo

데이터테이블이 dtResult 라는 변수에 담겨 있다고 한다면,

   
string sName = dtResult.Rows[0]["Name"].ToSting();

하면 그 해당하는 값인 “Goshe Mack” 이라는 데이터가 나온다.

여기서 발생할 수 있는 오류의 형태는 2가지.

첫번째로는 데이터가 없을 때다. 즉 dtResult 안에 DataRow가 하나도 없으면 저기서 에러가 난다. [0] 부분은 0번째 Row를 가져오겠다는 말인데, 그 안에 앖이 없으면 Null 이고 Null에 대한 [Name]은 없으므로 에러다.

두번째로는 Name 부분이 DbNull 인 경우다. 이 경우 [0][Name] 결과가 null 이 되므로, null의 ToString 에서 오류가 발생하게 된다.

그렇다면 이제 Null을 피해보도록 하자.

첫번째의 경우.

foreach(DataRow row in dtResult.Rows)
{
       string sName = row["Name"].ToString();
}

라고 하면 일단 Data가 없는 경우에 발생되는 문제는 피할 수 있다. 왜냐면 foreach를 할 때, dtResult 안에 Row가 없으면 더 이상 안의 내용을 처리하지 않기 때문이다.

두번째의 경우는 어떻게 할까?

위의 경우 처럼 직관적으로 표현하자면 이렇게 할 수 있을 것이다.

foreach(DataRow row in dtResult.Rows)
{
    if(row["Name"] != null || row["Name"] != DBNull.Value)
    {
        string sName = row["Name"].ToString();
    }
}

조금 더 다듬어 보자.

row["Name"]
이 너무 자주 나온다!  이것을 조금 더 개선 시키는 방향은 아예 값을 밖으로 빼서 하나의 변수에 넣고, 그 값을 돌려 쓰는 방식을 취한다. 이러면 딱 한번만 row[“Name”] 을 부를 뿐 실제 값은 변수에서 직접 가져와 쓰게 된다.
foreach(DataRow row in dtResult.Rows)
{
    object value = row["Name"];
    if(value != null || value != DBNull.Value)
    {
        string sName = value.ToString();
    }
}

만일 Null 일 때 Default 값을 설정하고 싶다면? 더 간단하다. 이번에는 sName 부분을 위로 끄집어 올린다.

foreach(DataRow row in dtResult.Rows)
{
    string sName = "DEFAULT";
    object value = row["Name"];
    if(value != null || value != DBNull.Value)
    {
        sName = value.ToString();
    }
}

여기까지는 좋다고 생각된다. 그런데, 만일, 여러 개의 필드일 때는 어떻게 할까? 더욱이 String도 아닌 Int 같은 필드 인 경우 ToString 으로 해결도 안된다. 한번 위의 방식대로 코드를 짜보자

foreach(DataRow row in dtResult.Rows)
{
    string sName = "DEFAULT";
    int nAge = 0;
    string sAddress = "DEFAULT";

    object value = row["Name"];
    if(value != null || value != DBNull.Value)
    {
        sName = value.ToString();
    }

    object value = row["Address"];
    if(value != null || value != DBNull.Value)
    {
        sAddress = value.ToString();
    }

    object value = row["Age"];
    if(value != null || value != DBNull.Value)
    {
        string sTemp = value.ToString();
        try
        {
            nAge = int.Parse(sTemp);
        }
        catch(Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }

}

갑자기 길어지는 코드!!!!

지금은 값이 세 개니까, 저렇지 만일 필드수가 10개 이상된다고 하면, 값 한번 조회하는 코드만 100라인 넘게 된다. 까잇거 Copy&Paste 신공이 있는데! 라고 외치며 무식하게 짜시는 분이 계신다면, 나중에 그 코드를 유지보수해야 할 때 참여하는 자신 혹은 남을 위해 자제해주셨으면 한다. 제발 ㅋ

그래서 아예 값을 별도로 처리하기 위한 Util 형태의 Static 클래스를 만들어 사용하는 방법을 추천한다.

/// 
/// System.Data.DataRow의 데이터를 쉽게 접근하기 위해서 사용되는 함수 모음
/// 
static public class DBUtils
{

    /// 
    /// DataRow 상에서 특정 Coloumn 이름에 해당하는 문자열을 읽어온다.
    /// 별도 기본값을 제공하여 읽어오는데 실패한 경우 입력받은 기본값으로 돌려준다.
    /// 
    /// 읽어올 데이터가 담긴 DataRow
    /// 읽어올 데이터의 Column 이름
    /// 읽어오는 것을 실패한 경우 돌려줄 기본값
    /// 읽어온 문자열 값
    public static string ReadString(DataRow row, string sColumnName, string sDefaultValue)
    {
        string sResult = sDefaultValue;
        if (row.Table.Columns.Contains(sColumnName))
        {
            object value = row[sColumnName];
            if ((value is DBNull) == false && value != null)
            {
                sResult = value.ToString();
                    
            }
        }
        sResult = sResult.Trim();
        return sResult;
    }

    /// 
    /// DataRow 상에서 특정 Coloumn 이름에 해당하는 문자열을 읽어온다.
    /// 
    /// 읽어올 데이터가 담긴 DataRow
    /// 읽어올 데이터의 Column 이름    
    /// 읽어온 문자열 값
    public static string ReadString(DataRow row, string sColumnName)
    {
        return ReadString(row, sColumnName, string.Empty);
    }


    /// 
    /// DataRow 상에서 특정 Coloumn 이름에 해당하는 숫자값을 읽어온다.
    /// 별도 기본값을 제공하여 읽어오는데 실패한 경우 입력받은 기본값으로 돌려준다.
    /// 
    /// 읽어올 데이터가 담긴 DataRow
    /// 읽어올 데이터의 Column 이름
    /// 읽어오는 것을 실패한 경우 돌려줄 기본값
    /// 읽어온 숫자 값
    public static int ReadInteger(DataRow row, string sColumnName, int nDefaultValue)
    {
        int nResult = nDefaultValue;
        if (row.Table.Columns.Contains(sColumnName))
        {
            object value = row[sColumnName];
            if ((value is DBNull) == false && value != null)
            {
                string sValue = value.ToString();
                if (int.TryParse(sValue, out nResult) == false)
                    nResult = nDefaultValue;
            }
        }
        return nResult;
    }

    /// 
    /// DataRow 상에서 특정 Coloumn 이름에 해당하는 숫자값을 읽어온다.
    /// 
    /// 읽어올 데이터가 담긴 DataRow
    /// 읽어올 데이터의 Column 이름
    /// 읽어온 숫자 값
    public static int ReadInteger(DataRow row, string sColumnName)
    {
        return ReadInteger(row, sColumnName, 0);
    }

    /// 
    /// DataRow 상에서 특정 Coloumn 이름에 해당하는 DateTime값을 읽어온다.
    /// 별도 기본값을 제공하여 읽어오는데 실패한 경우 입력받은 기본값으로 돌려준다.
    /// 
    /// 읽어올 데이터가 담긴 DataRow
    /// 읽어올 데이터의 Column 이름
    /// 읽어오는 것을 실패한 경우 돌려줄 기본값
    /// 읽어온 DateTime 값
    public static DateTime ReadDateTime(DataRow row, string sColumnName, DateTime defaultTime)
    {
        DateTime result = defaultTime;
            
        if (row.Table.Columns.Contains(sColumnName))
        {
            object value = row[sColumnName];
            if ((value is DBNull) == false && value != null)
            {
                try
                {
                    result = (DateTime)value;
                }
                catch (Exception ex)
                {
                    Core.LogWriter.Current.Write(ex);
                }
                //try
                //{
                //    result = DateTime.Parse(sValue);
                //}
                //catch
                //{
                //}
            }
        }
        return result;
    }


    /// 
    /// DataRow 상에서 특정 Coloumn 이름에 해당하는 DateTime값을 읽어온다.
    /// 
    /// 읽어올 데이터가 담긴 DataRow
    /// 읽어올 데이터의 Column 이름
    /// 읽어온 DateTime 값
    public static DateTime ReadDateTime(DataRow row, string sColumnName)
    {
        return ReadDateTime(row, sColumnName, DateTime.MinValue);
    }

    public static System.Data.SqlTypes.SqlBinary ReadSQLBinary(DataRow row, string sColumnName)
    {
        System.Data.SqlTypes.SqlBinary result = System.Data.SqlTypes.SqlBinary.Null;

        if (row.Table.Columns.Contains(sColumnName))
        {
            object value = row[sColumnName];
            if ((value is DBNull) == false && value != null)
            {
                try
                {
                    result = new System.Data.SqlTypes.SqlBinary((byte[])value);                        
                }
                catch (Exception ex)
                {
                    Core.LogWriter.Current.Write(ex);
                }
                //try
                //{
                //    result = DateTime.Parse(sValue);
                //}
                //catch
                //{
                //}
            }
        }
        return result;

    }
}

저런 클래스를 만들면 위에서 제시한 예제 코드는 다음 형태로 변하게 된다.

foreach(DataRow row in dtResult.Rows)
{
    string sName = DBUtils.ReadString(row,"Name","DEFAULT");
    int nAge = DBUtils.ReadInteger(row,"Age",0);
    string sAddress = DBUtils.ReadString(row,"Address","DEFAULT");
}

 

정리

반복적인 작업을 보다 효율적으로 빠르게 처리하라고 있는 컴퓨터에서 노가다하고 있는 우리네 현실을 보면 조금 답답한 것 같다. 사실 C#에는 프로그래머에게 보다 더 편리하고 간단하게 짜라는 장치가 많다. 하지만, 100% 현실에 맞는 것은 아닌 것 같다.

하나씩 자신만의 Util을 만들어 자신의 코드 내에 있는 노가다의 요소들을 정리해보는 것은 어떨까?

728x90

원본 : http://msdn.microsoft.com/ko-kr/library/ms180915(VS.80).aspx

(즐겨찾기에 있는 정보로 링크 변경이나, 사이트 변경에 따라 정보가 유실될 것 같아 홈페이지에 등록합니다. 아래의 문건의 권리는 모두 Microsoft 에 귀속됩니다.)


이 항목에는 사용자 암호 관리에 대한 정보 및 코드 예가 들어 있습니다.

다음 C# 코드 예에서는 IADsUser::SetPasswordadsi.iadsuser_setpassword 메서드를 호출하여 사용자 암호를 설정하는 방법을 보여 줍니다. IADsUser::SetPassword에 대한 자세한 내용은 MSDN Library(http://msdn.microsoft.com/library)의 "IADsUser::SetPassword"를 참조하십시오.

 
usr.Invoke("SetPassword", SecurelyStoredPassword);

다음 C# 코드 예에서는 IADsUser::ChangePasswordadsi.iadsuser_changepassword 메서드를 호출하여 사용자 암호를 변경하는 방법을 보여 줍니다. IADsUser::ChangePassword에 대한 자세한 내용은 MSDN Library(http://msdn.microsoft.com/library)의 "IADsUser::ChangePassword"를 참조하십시오.

 
usr.Invoke("ChangePassword", OldSecurelyStoredPassword, NewSecurelyStoredPassword);

다음 C# 코드 예에서는 다음 로그온 시 사용자 암호를 변경할 수 있도록 설정하는 방법을 보여 줍니다. 이 경우 pwdLastSetadschema.a_pwdlastset 속성을 off(-1)로 설정합니다. adschema pwdLastSet 특성에 대한 자세한 내용은 MSDN Library(http://msdn.microsoft.com/library)의 "pwdLastSet" 또는 "Pwd-Last-Set attribute"를 참조하십시오.

 
usr.Properties["pwdLastSet"].Value = -1; // To turn on, set this value to 0.
usr.CommitChanges();

 

다음 C# 코드 예에서는 암호를 변경하는 사용자 권한을 거부하도록 ACE를 설정하는 기능을 보여 줍니다. 이 경우 ntSecurityDescriptor 속성을 가져올 수 있도록 IADsSecurityDescriptor에 액세스하는 데 ADSI 액세스에 COM 상호 운용성 사용를 사용합니다. 그런 다음 IADsAccessControlList를 사용하여 보안 설명자의 DACL을 가져오고 IADsAccessControlEntry를 사용하여 AceType, AceFlags, Trustee, Flags, ObjectType 및 AccessMask 속성을 가져옵니다. AceType 플래그는 ADS_ACETYPE_ENUM에 정의되어 있습니다. AceFlags는 ADS_FLAGTYPE_ENUM에 정의되어 있습니다. AccessMask 플래그는 ADS_RIGHTS_ENUM에 정의되어 있습니다.

 
using System;
using System.DirectoryServices;
using ActiveDs;

static void DenyChangePassword(DirectoryEntry User)
{

	const string PASSWORD_GUID = "{ab721a53-1e2f-11d0-9819-00aa0040529b}";
	const int ADS_UF_PASSWORD_EXPIRED=0x800000;
	const int ADS_UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION=0x1000000;

	string[] trustees = new string[]{@"NT AUTHORITY\SELF","EVERYONE"};
	

	ActiveDs.IADsSecurityDescriptor sd = (ActiveDs.IADsSecurityDescriptor)

	User.Properties["ntSecurityDescriptor"].Value;

	ActiveDs.IADsAccessControlList acl = (ActiveDs.IADsAccessControlList) sd.DiscretionaryAcl;

	ActiveDs.IADsAccessControlEntry ace = new ActiveDs.AccessControlEntry();

	foreach(string trustee in trustees)
	{
		ace.Trustee = trustee;
		ace.AceFlags = 0;
		ace.AceType = (int)ActiveDs.ADS_ACETYPE_ENUM.ADS_ACETYPE_ACCESS_DENIED_OBJECT;
		ace.Flags = (int)ActiveDs.ADS_FLAGTYPE_ENUM.ADS_FLAG_OBJECT_TYPE_PRESENT;
		ace.ObjectType = PASSWORD_GUID;
		ace.AccessMask = (int)ActiveDs.ADS_RIGHTS_ENUM.ADS_RIGHT_DS_CONTROL_ACCESS;
		acl.AddAce(ace);
	}

	sd.DiscretionaryAcl = acl;
	User.Properties["ntSecurityDescriptor"].Value = sd;
	User.CommitChanges();
}

다음 C# 예에서는 암호를 변경하는 사용자 권한을 거부하도록 ACE를 설정하는 기능을 보여 줍니다. 이 예에서는 .NET Framework 2.0에서 사용할 수 있는 관리되는 ACL 기능을 사용합니다.

using System;
using System.DirectoryServices;
using System.Security.Principal;
using System.Security.AccessControl;

static void DenyChangePassword(DirectoryEntry user)
{
    // Create a Guid that identifies the Change Password right.
    Guid changePasswordGuid = 
        new Guid("{AB721A53-1E2F-11D0-9819-00AA0040529B}");

    // Get the ActiveDirectorySecurity for the user.
    ActiveDirectorySecurity userSecurity = user.ObjectSecurity;

    // Create a SecurityIdentifier object for "everyone".
    SecurityIdentifier everyoneSid = 
        new SecurityIdentifier(WellKnownSidType.WorldSid, null);

    // Create a SecurityIdentifier object for "self".
    SecurityIdentifier selfSid = 
        new SecurityIdentifier(WellKnownSidType.SelfSid, null);

    // Create an access rule to allow everyone the change password 
    // right. 
    // This is used to remove any existing access rules.
    ActiveDirectoryAccessRule allowEveryone = 
        new ActiveDirectoryAccessRule(
            everyoneSid,
            ActiveDirectoryRights.ExtendedRight, 
            AccessControlType.Allow, 
            changePasswordGuid);

    // Create an access rule to deny everyone the change password right.
    ActiveDirectoryAccessRule denyEveryone =
        new ActiveDirectoryAccessRule(
            everyoneSid,
            ActiveDirectoryRights.ExtendedRight,
            AccessControlType.Deny,
            changePasswordGuid);

    // Create an access rule to allow self the change password right.
    // This is used to remove any existing access rules.
    ActiveDirectoryAccessRule allowSelf =
        new ActiveDirectoryAccessRule(
            selfSid,
            ActiveDirectoryRights.ExtendedRight,
            AccessControlType.Allow,
            changePasswordGuid);

    // Create an access rule to deny self the change password right.
    ActiveDirectoryAccessRule denySelf =
        new ActiveDirectoryAccessRule(
            selfSid,
            ActiveDirectoryRights.ExtendedRight,
            AccessControlType.Deny,
            changePasswordGuid);

    // Remove any existing rule that gives "everyone" the change 
    // password right.
    userSecurity.RemoveAccessRuleSpecific(allowEveryone);

    // Add a new access rule to deny "everyone" the change password 
    // right.
    userSecurity.AddAccessRule(denyEveryone);

    // Remove any existing rule that gives "self" the change password 
    // right.
    userSecurity.RemoveAccessRuleSpecific(allowSelf);

    // Add a new access rule to deny "self" the change password right.
    userSecurity.AddAccessRule(denySelf);

    // Commit the changes.
    user.CommitChanges();
}

다음 코드 예에서는 암호가 만료되지 않도록 설정하는 방법을 보여 줍니다. 이 경우 ADS_USER_FLAG_ENUM에 정의되어 있는 ADS_UF_DONT_EXPIRE_PASSWD 플래그를 설정할 수 있도록 userAccountControl 속성에 액세스하기 위해 Properties 메서드를 사용합니다.

using System;
using System.DirectoryServices;
using ActiveDs;

static void DontExpirePassword(DirectoryEntry User)
{
    int val;
    const int ADS_UF_DONT_EXPIRE_PASSWD =0x10000;
    val = (int) User.Properties["userAccountControl"].Value;
    User.Properties["userAccountControl"].Value = val | 
        ADS_UF_DONT_EXPIRE_PASSWD;
    User.CommitChanges();
}


728x90

집에는 팩스가 없고, 사무실에 있는 팩스 머신에는 전화선이 없어서 개인적으로 팩스를 보낼 때는 보통 인터넷 팩스를 자주 이용한다.

그런데, 이번에 아들녀석에 대한 보험금 처리 때문에, 많은 양의 팩스를 보내게 되었는데, 문제는 웹 기반의 인터넷 팩스에서는 여러장을 입력하기가 무척 불편한 구조로 되어 있다. 일단 한장씩 등록하게 되어 있는데, 그것도 장당 최대 용량은 5M 이하여야 되고, 한번에 모두 올릴 수 있는 양이 10M 안쪽이다.

문제는 스캔한 이미지를 가지고 올리게 되는데, 아무생각없이 스캔한 이미지는 저 사이즈는 훌쩍 뛰어 넘는다.

어쨌던 귀찮던 힘들던 간에, 하나씩 올리기는 했는데, 이번에는 팩스 전송이 실패하는 바람에 귀찮고 힘들게 올린 문서는 공중 분해. 다시 하나씩 올려야 되는 아픔이 있었다.

그래서 스캔한 이미지를 팩스로 올리기 편하도록 팩스용 Tiff 파일을 만드는 프로그램을 만들었다.

어떤 형태로 스캔을 한 이미지라도, 팩스로 보내기 위해서는 어느 정도 이미지 정리를 해야 한다.

  1. 첫번째로는 흑백 이여야 한다.
  2. 두번째는 해상도가 가로는 294 dpi, 세로는 190dpi 여야 한다. ( 최고급 팩스라도 저 이상의 크기는 지원하지도 않고, 잘 되지도 않는다. )
  3. 세번째는 압축 규칙을 CCITT 규약에 맞게 압축한 이미지 여야 한다.

이 세가지 조건만 만족하면 간단하게 팩스 보내는 이미지가 완성된다.

더욱이 TIFF 파일은 파일 내에 여러장의 페이지를 넣을 수 있는 구조이기 때문에, 여러 장으로 된 하나의 문서일 경우 여러장을 담은 하나의 파일로 만들 수 있다.

위의 조건을 충족하는 TIFF 파일을 만드는 도구를 급하게 만들어 봤다.

사용법은 간단.

Add 버튼을 눌러 이미지를 추가한다. (추가 가능한 이미지는 JPG, PNG, BMP 등이다. )

그리고 불필요한 이미지를 추가했으면 선택한 뒤에 Del 버튼을 누른다.

페이지의 순서를 변경하려면, UP/Down 버튼을 눌러 위치를 변경한다.

최종적으로 배치가 끝나면 Convert 버튼을 눌러 Tiff 파일로 저장한다.

등록한 이미지를 모두 없애고 처음부터 다시 하려면 Clear 버튼을 클릭하면 된다.

 

기타 사용상 편의 사항 따윈 일체 없다. ( 사용하다가 불편하면 첨부로 올린 소스를 직접 편집해서 필요한 대로 수정해서 쓰시면 된다. ) 단지, 필자가 편리하게 써보려고 만들어 보았다.

 

이 프로그램은 .NET FrameFramework 2.0 기반으로 구성되어 있으며, 개발은 Visual Studio 2010 에서 했다. (Visual Studio 2010 Express에서도 컴파일 가능 )

PS. 바로 전에 다니던 회사가 이미지 관련된 어플리케이션 개발하던 곳인데, 그 회사에서 개발했던 경험이 이렇게 유용하게 나올 줄은 몰랐다. – 물론 그 회사에서 사용한 기술은 사용하지 않고, 순수 .NET Framework 만들어 개발했다. – 다만, TIFF 개념이나, FAX 개념을 이해하는데 많은 도움이 된 것은 사실이다.

** UPDATE : 2012/08/08
프로그램 내 버그가 발생할 때마다 소스와 릴리즈를 압축하여 여기에 업로드하는 불편함 때문에,
네이버 개발자 센터를 이용하여 업로드를 할 예정이다. 접속하기 위한 네이버 개발자 센터 URL은 다음과 같다.

http://dev.naver.com/projects/imgconvertforfa/

빌드된 최신 버전 파일은 아래의 링크에서 다운 받을 수 있다.

http://dev.naver.com/projects/imgconvertforfa/download

코드는 SVN을 통해 다운로드가 가능하다. 필요하면 해당 프로젝트에 참여자로 등록도 가능하다.
(비밀댓글로 자신의 네이버 아이디를 알려주시면 됩니다. )

728x90

과연 어떤 코드가 좋을까? 이 문제는 프로그래머들의 스타일과 성격에 따라 무척 다르다.

일단 내 기준으로 보았을 때 다음과 같은 코드가 있으면 일단 뜯고 싶어진다.


        
char[] array4 = new char[]            
{             
         '<'
};
text17 = text17.TrimEnd(array4);
string[] array5 = text17.Split(array4, StringSplitOptions.RemoveEmptyEntries);
text17 = "";
for (int num24 = 0; num24 < array5.Length; num24++) 
{
       if (text17 == "")
       { 
               text17 = array5[num24];
       }
       else
      { 
              text17 = text17 + "." + array5[num24];  
       }
}

변수 명은 일단 넘어가자. Visual Studio를 쓰고 있다면 간단하게 이름을 바꿔줄 수 있으니까..

일단 저 프로그램의 목적은 ‘>’ 가 들어간 문자열 내에 문자들을 나누어서 최종적으로 “.” 로 연결된 문장을 만들고 싶은 것이다.

예를 들면 아래와 같다.

ABC1>2222>>222334>5>66666666>>>>>>>>>  

ABC1.2222.222334.5.66666666

로 바꿔주는 프로그램인 것이다.

 

위의 코드가  어떤 부분이 내 기준에 부합되지 않는지를 설명하려고 한다.

1. String “+” 연산자.

C# 중급 정도 하다보면, 문자열의 “+” 연산자의 낭비를 잘 알 수 있다. 즉 문자열을 단순히 한 두번 연결하는 레벨이라면 “+” 를 써서 연결은 하겠지만, 반복문 안에서 문자열 더하기는 확실히 좋지 않다.

만일 위의 예제로 본다면, 아래와 같은 문자열들이 나오게 된다.

ABC1  /  2222  /  222334  /  5  / 66666666

5개 정도의 문자열이 나오는데, 순서대로 더하게 되면 어떻게 될까?

메모리 상에서 더하는 과정을 순서대로 나열해 보겠다.

  1. ABC1, 2222, ABC12222   ( ABC1, 2222 은 언젠가 지워진다.. 언젠가..)
  2. ABC1(?), 2222(?), ABC12222 , 222334, ABC12222222334 ( 마찬가지로 ABC1 부터 222334까지의 문자열은 언제가 지워진다 –_-; )
  3. ABC1(?), 2222(?), ABC12222(?) , 222334(?), ABC12222222334, 5 ,ABC122222223345 ( ...... )
  4. ABC1(?), 2222(?), ABC12222(?) , 222334(?), ABC12222222334(?), 5(?) ,ABC122222223345,  66666666 , ABC12222222334566666666( ...... )

총 네번의 단계 동안 언젠가 지워져 줘야할 문자열의 갯수는 이미 상상을 초월한다.

“+” 연산자를 할 때, 특정 공간에 쭉 채워가듯 더하는 것이 아니라, 항상 문자열 공간을 새로 만들어 더하기를 하는 것이다. 저건 5 단계 정도니 저 정도 지만, 문자열의 길이가 길어지거나, 반복 횟수가 많아지기 시작하면, 언젠가 지워져야 할 문자열이 지워지기를 기다려야 될지도 모른다.

해결책은 여러가지가 있겠지만, C# 유저라면, StringBuilder를 추천한다. 사용방법은 간단하다.

StringBudiler sbReuslt = new StringBuilder();

sbResult.Append(“ABC1”);

sbResult.Append(“2222”);

sbResult.Append(“222334”);

sbResult.Append(“5”);

sbResult.Append(“66666666”);

sbResult.ToString();

매번 더해진 결과는 한 공간에 넣어서 계속 관리한다. 그 방법을 StringBuilder 가 해결해준다.

 

2. else 문과 string 비교.

저 프로그램이 정말 하고 싶어하는 것이 무엇이길래 저런 방식으로 짠걸까?
한번 생각해보았다. 그러니까, 다음 세 가지의 조건이 나온다.

1. 문자열을 더할 때 그 사이에 “.” 를 넣는다.

2. 맨 마지막 문자열을 더할 때 “.”  가 붙지 않는다.

3. 최초 문자열을 더할 때, 맨 앞에 “.” 가 붙지 않는다.

2번과 3번은 로직을 짤 때 2번을 택해도 되고, 3번을 택해도 된다. 그 이유는 문자열을 더한 뒤, 더 이상 붙여야 될 문자열이 없을 때 “.” 를 안붙이는 로직을 만들거나, 최초 문자열을 더할 때 앞쪽에 “.”를 붙이지 않고, 나머지 경우에 “.” 를 붙이는 방식이다.  처리 방법에 따라, 2번 조건 3번 조건을 쓸 수 있다.

자 저 위의 로직을 보자. 위의 조건 중 1번과 2+3 번의 형태를 만들었다.

즉 반복문 최초에는 그냥 문자열을 대입한 뒤, 다음 반복 부터는

이전 문자열 + “.” + 붙일 문자열

이런 식으로 짰다. 앞서 이야기 한대로 String 의 “+”는 안쓰는게 백번 낫다고 했다.
그렇다면 StringBuilder로 재 구성한다고 할 때, 최소한 저 방식대로 한다면, 매번 뒤에서는 Append를 두 번해야 한다.

sbResult.Append(“.”);

sbResult.Append(array[num24]);

코드가.. 아주 그냥...

더욱이 if 문을 보면, 문자열 비교를 한다. text17 == “”

개인적으로 숫자 비교는 용서하지만 문자열 비교는 가급적 피하고 싶다라는 개인적인 생각이다. 컴퓨터가 숫자에 대한 비교는 빠르지만, 문자열에 대한 비교는 문자열 갯수에 비례하도록 차이가 크게 발생된다. 그래서 꼭 필요한 경우가 아니라면 문자열 비교는 피하는게 좋다. ( 뭐 요즘 컴퓨터 사양은 무지하게 좋기 때문에, 무시되기도 하지만...)

또, for 문의 경우 index라는 개념이 있어서, 코드를 뭔가 알흠답지 않게 만든다. ( 100% 개인 취향 )

 

자 이것을 어떻게 수정할까?

먼저 최초 문자열일 때는 어떻게 판단하고, 어떻게 행동할까?

최종 무엇을 더하도록 할까?

 

결론부터 내자면 개인적으로는 위의 코드가 아래와 같이 만들어지길 원한다.

 

결론

StringBuilder 만 빼면, 나머진 개인 취향이다.
"어디를 향하든 서울만 가면 된다는” 말이 있듯이 사실 결과만 잘 나오면 된다. 어떻게 짜든 개인 취향이라는 것.
하지만, 최소한 나중에 유지보수를 해야 한다고 생각한다면, 간결하고, 단순하게 짜는게 더 낫지 않을까?

처음부터 저런 코드가 툭툭 떨어지진 않는다. 그래서 저렇게 코드가 안 나온다고 고민할 필요까진 없다. 다만, 시간이 될 때, 자신이 짠 코드를 뒤돌아 보면서 Refactoring을 하면서 정리해 보았으면 좋겠다.

728x90

C#에서는 Listener 패턴을 구현하기 쉽다. 특히 event라는 예약어를 제공하고 있어서, Callback 형태의 구현이 어렵지 않다.


class Test 
{ 
        int m_nCurrentIndex = 0; 
        public delegate void IndexChangedEventHandler(); 
        public event IndexChangedEventHandler ChangeCurrentIndex; 
        public void ChangePage(int nIndex) 
        { 
               m_nCurrentIndex = nIndex; 
               ChangeCurrentIndex(); 
               m_nCurrentIndex = m_nCurrentIndex + 1; 
        } 
}

위의 코드는 바로 그 event 형태로 어떻게 구현하는지를 나타낸다.

이를 사용하는 로직은 아래와 같다.

class Program 
{ 
        [STAThread] 
        static void Main() 
        { 
             Test test = new Test();

             test.ChangeCurrentIndex 
                 +=  new IndexChangedEventHandler(test_ChangeCurrentIndex); 
             test.ChangePage(1);

             Console.WriteLine(“Complete!”); 
        }

        private void test_ChangeCurrentIndex() 
        { 
               Console.WriteLine(“event!”); 
        } 
}

 

“test.ChangeCurrentIndex” 이렇게 쓰고 “+=” 만 추가하면, 자동으로 코드가 생성되면서 아래에 함수가 하나 생긴다.
이것이 바로 event 구현 작업이다. 즉 test 내부에서 “ChangeCurrentIndex()” 이 함수가 불리는 순간, 이벤트 구독한 함수로 호출을 하게 된다.

즉 역으로 함수를 부르는 일종의 Callback  함수로 생각하면 된다.

위의 작업을 한 줄씩 실행되는 순서를 보면 아래와 같은 순서로 진행된다.

  1. Test test = new Test(); –> Test 클래스의 인스턴스를 만든다.
  2. test.ChangeCurrentIndex +=  new IndexChangedEventHandler(test_ChangeCurrentIndex); –> 이벤트를 구독한다.
  3. test.ChangePage(1); –> Test 클래스의 ChangePage 함수를 호출한다.
  4. m_nCurrentIndex = nIndex;   -> TestClass 내부 : m_nCurrentIndex에 nIndex 값을 대입한다.
  5. ChangeCurrentIndex();   -> Event를 발생시킨다.
  6. Console.WriteLine(“event!”)  -> Main 쪽의 이벤트 구독할 때 등록한 함수(test_ChangeCurrentIndex)로 들어가서 “event!” 라는 문자열을 찍는다.
  7. m_nCurrentIndex = m_nCurrentIndex + 1; –> +1 를 한다. 그리고 ChangePage 함수를 종료한다.
  8. Console.WriteLine(“Complete!”);   -> Main 으로 돌아와서 Complete를 찍고 종료한다.


이것을 실행하는 순서를 다이어그램으로 나타내면 아래와 같다.

 

Test라는 클래스와 Program이라는 클래스가 서로 통신을 한다고 했을 때, 위와 같은 Listen 구조가 안된다면, 결국 Program 이라는 클래스에서 호출하는 작업이 없다면 Test가 Program으로 데이터를 보낼 방법이 없다. 만일 저 위와 같은 형태가 안되는 구조라면, Test 클래스 안에는 Program 개체의 레퍼런스를 들고 있어야 한다.

class Test
{

        Program m_parent = null;
        int m_nCurrentIndex = 0;

        ~~~~~~~~~
}

그리고 Program 클래스 안에는 Test를 통해 처리해야 할 함수 부분에서 private를 public 으로 바꿔야된다.

        public void test_ChangeCurrentIndex()
        {
               Console.WriteLine(“event!”);
        }

호출할 때는 기존에 함수형태로 된 것을 Program을 통해서 부르도록 해야 된다.

        public void ChangePage(int nIndex)
        {
               ~~~~~~~~~~~
               m_parent .test_ChangeCurrentIndex();
               ~~~~~~~~~~~
        }

서로 인스턴스를 주고 받아야 저런 통신이 가능하게 되는 것이다.

하지만 위와 같이 하게 되는 경우 같은 DLL 이나 EXE 의 경우면 어차피 같은 프로젝트 안에 있는 내용이니 큰 문제가 없지만, 만일 서로 다른 프로젝트로 되어 구성된 경우라면, 서로 인스턴스를 주고 받으려면 상당히 난감해 질 수 밖에 없다.

이렇게 훌륭한 event 구성에도 심각한 문제가 있다.

그건 바로 실행되는 순서에 있다. 실행되는 순서 중 5~7 사이를 보도록 하자.
만일 5->7->6 의 순서로 실행하고 싶다면 어떻게 해야 할까? 즉, ChangePage 함수가 종료되면 자동으로 test_ChangeCurrentIndex 가 실행되고 싶을 때라는 것이다. 간단한 구조의 프로그램이나, 구성의 경우에는 이런 부분을 신경 쓰지 않지만, Multi Thread 나 Windows UI 응용 프로그램인 경우 문제가 발생한다.

왜 문제가 발생할까?

만일 저 Event 구조가 연달아 실행되는 형태라면? 이라는 가정으로 시작해보자.

만일 저런 형태라면, E에서 부터 시작해서 A까지 모든 작업이 끝나야 실제 동작이 끝나게 된다. 이 경우 함수를 연달아 부른 구조와 별 다를게 없다. 그래서 간혹 중간에 시간이 오래 걸리는 작업이 있는 경우, 메인 프로그램의 UI가 마치 다운된 것과 같다.

UI가 다운되지 않은 것처럼 하기 위해서는 E 가 완전히 끝난 뒤, D가 실행되고, 다음에는 D가 완전히 다 실행한 뒤, C가 실행되는 형태가 되어야 한다.

즉 맨 위의 예제 코드를 기준으로 보면, 1->2->3->4->5->6->7->8 이 아니라, 1->2->3->4->5->7->6->8 또는 1->2->3->4->5->7->8->6 순으로 실행되면 되는 것이다. 어찌되었던 간에, ChangePage 메소드가 종료 된 뒤에, 실행을 요청한 코드가 실행되어야 한다는 것이다.

이렇게 하려면 어떻게 해야 할까?

1가지 방법은 Windows Message를 사용하는 것이다. SendMessage 나, PostMessage 같은 것으로 호출하는 방법이다. 최소한 SendMessage나 PostMessage는 Windows Message Queue에다 던지는 형식이기 때문에, Windows Message Queue에서 해당 메시지가 나올 때 까지 나머지 부분을 계속 실행하게 된다. 즉 5 번 단계의 실행에서 바로 7단계로 넘어 간 뒤에, Windows Message Queue에서 Message 가 언제 나오는지에 따라, 6 번은 그에 맞게 실행되는 것이다.

이 작업은 .NET에서 Windows Message를 다루는 방법이므로 일단 이 방법은 넘어가도록 하자.

순수 .NET 코드에서는 어떻게 해야 할까?

바로 Invoke 라는 함수를 쓰는 것이다.

Invoke의 정확한 목적은 해당 개체에 있는 함수를 강제적으로 동작하게 하는 것인데, 이것은 상대 클래스내의 함수를 무조건 실행하는 것이다. public 이든, protected 든, 심지어 private 일지라도 강제적으로 실행하는 방법이다.

이 때, 중요한 것은 Invoke 명령어는 System.Windows.Form 네임스페이스에 있는 Control 기반의 Windows 개체만 동작한다는 것이다.  위의 예제 코드는 다음과 같이 다시 작성을 해야 한다. 먼저 Windows Form 기반 응용 프로그램이여야 할 것이다.

public partial class Form1 : Form 
{ 
        Test m_test = null; 
        public Form1() 
        { 
            InitializeComponent(); 
            m_test = new Test(this); 
            m_test.ChangeCurrentIndex 
              +=  new IndexChangedEventHandler(m_test_ChangeCurrentIndex); 
            test.ChangePage(1); 
            Console.WriteLine(“Complete!”); 
        }

        private void m_test_ChangeCurrentIndex() 
        { 
               Console.WriteLine(“event!”); 
        } 
} 

class Test 
{ 
        Form m_parent = null; 
        int m_nCurrentIndex = 0; 
        public delegate void IndexChangedEventHandler(); 
        public event IndexChangedEventHandler ChangeCurrentIndex; 
        public Test(Form parent) 
        { 
             m_parent = parent; 
        } 
        public void ChangePage(int nIndex) 
        { 
               m_nCurrentIndex = nIndex; 
               ChangeCurrentIndex(); 
               m_nCurrentIndex = m_nCurrentIndex + 1; 
        } 
}

앞서 event 기반의 Listen 패턴 관련해서 상호간 인스턴스가 필요없다고 했는데, 이번에는 필요하다. 왜냐면, event를 호출하는 로직을 할 때, Invoke를 해야 하는데, 그 Invoke를 당하는 대상의 인스턴스가 필요하기 때문이다.

다음 로직을 보자. 이제 ChangePage 라는 함수를 수정해야 하기 때문이다.

public void ChangePage(int nIndex) 
{ 
      m_nCurrentIndex = nIndex; 
      m_parent.Invoke(ChangeCurrentIndex); 
      m_nCurrentIndex = m_nCurrentIndex + 1; 
 } 

앞에서는 ChageCurrentIndex 라는 이벤트를 직접 함수처럼 실행했지만, 이번에는 Invoke 라는 것을 사용해서 실행하기 때문이다. 즉 Windows개체.Invoke(delegation함수) 형태로 실행하는 것이다.

m_parent.Invoke(ChangeCurrentIndex)라는 문구가 올 때, ChageCurrentIndex 에 연결된 함수가 실행되지 않고, 일단 m_parent 한테 실행하라고 지시만 한 상태가 된다. 일단 저 안의 함수 내용이 완료될 때까지는 저 Invoke가 실행되지 않는다. 그러므로 ChangeCurrentIndex에 구독한 부분은 나중에 실행되게 되고, 바로 그 아래에 있는 코드가 실행되게 된다. 그리고 그 함수가 완전히 종료되면, 그제서야 Invoke 한 함수를 실행하게 되는 것이다.

이 모든 작업은 COM Component 기반의 동작이 가능한 System.Windows.Form 계열의 Windows Form, Control 등만이 가능한 기능인 것이다.

728x90

이 설치는 SharePoint Foundation을 간략하게 설치하거나, 개발 환경 정도로 구성할 때 사용한다. 실제 운영하거나, SharePoint Server 2010 Standard 이상 버전의 서버 군 설치와는 다르다.

이 작업을 위해 필요한 환경은 아래와 같다.

Hardware

  • x64를 지원하는 PC 혹은 가상 머신
  • RAM 4G 이상.
  • HDD 80G 이상.
  • 인터넷이 연결 가능한 네트워크

Software

  • MS Windows Server 2008 R2 Standard 혹은 Enterprise Edition.
  • SharePoint Foundation 2010 ( http://download.microsoft.com 에서 다운 가능 )
  • MS SQL 2008 R2 Express with Tools ( http://download.microsoft.com 에서 다운 가능 )
    - 그냥 Express 버전은 Management Tool 이 없어서, DB 설정이나, 구성을 할 때 사용하는 도구가 없다.

Overview

전체적인 설치 순서는 다음과 같다. 이 순서대로 캡쳐 화면과 함께 소개할 예정이다.

  1. MS Windows Server 2008 R2 설치.
  2. Windows 관련 설정.
  3. SharePoint Foundation 설치.
  4. SQL 2008 R2 로 업그레이드 및 DB 관리도구 설치.
  5. SharePoint Foundation 구성.
  6. 기타..

이제 위의 순서대로 설치하는 방법을 나열할 것이다.
캡쳐된 화면은 1024 * 768의 화면을 캡쳐했는데, 블로그의 표시 해상도 상 사이즈를 많이 줄였다.
만일 제대로 확인이 불가능하면, 해당 그림을 클릭해서 확대해서 보면 된다. 

 

MS Windows Server 2008 R2 설치

Windows Server 2008 R2 미디어를 준비한다. 그리고 필요하면 CD-Key 정도는 확보해야 한다. 정품인증을 하지 않아도  대략 30일 정도는 사용할 수 있지만, 그 이후에는 여러 가지 문제가 발생할 수 있다. 또한 Windows Update 부분에서도 일부 불이익을 받을 수 있다.  만일 단순 테스트 용도라면 굳이 정품 인증이 필요 없다.

먼저 일반적인 Windows 설치와 마찬가지로, 미디어를 넣고 시작한다. Windows Server 2008 R2는 Windows 7 기반과 동일한 비슷한 구조로 되어 있어 시작화면도 Windows 7과 비슷하다.

본격적인 서버 설치 화면은 다음과 같다.

로딩이 완료되면 제품을 선택하는 화면이 나온다. 이 중, Standard 혹은 Enterprise를 선택한다. 또한 주의할 점이 (Server Core Installation) 이라는 항목들이 있는데, 이것은 피하도록 하자. 반드시 (Full Installation)을 선택한다. (Server Core Installation)은 진짜 Server의 핵심적인 모듈만 설치하기 때문에, UI가 전혀 없고, 도스 창 하나만 달랑 나오기 때문에, 진짜 운영용 서버를 만드는게 아니면 피하는게 상책이다.

만일 HDD Disk가 여러 개인 경우나, 파티션이 나눠져 있거나, 혹은 기존에 설치된 운영체제가 있는 경우에는 디스크 관련 도구가 뜬다. 그 부분은 상황에 맞춰서 적절하게 설치위치를 잡아준다.

설치가 본격적으로 진행되면 다음과 같은 화면이 나타나면서 자동으로 설치가 진행된다.

설치가 완료되면 Administrator의 암호가 설정되지 않았다고 나오면서 암호를 넣어달라고 한다. 이 암호를 만든 후에, 그 암호로 로그인을 하도록 한다. 그러면 Windows Server 설치는 완료된다.

Windows Server 설정

반드시 필요하다고 하면 필요하고 필요 없다고 하면 필요 없지만, 일단 간단한 설정을 해주도록 한다.

먼저 Internet Explorer의 보안설정을 끄도록 한다. 여기서는 약자로 IE ESC 라고 하는데, 이 항목들을 모두 Off로 해줘야 한다. 안 그러면 보안 설정으로 인해 웹사이트 접속이 어렵기도 하고, SharePoint 내의 검색 엔진이 정상적으로 동작하지 않게 된다.

서버 관리자를 띄우면 바로 볼 수 있다. 아래의 그림에서 왼편에 있는 Configure IE ESC링크를 누르면 창이 뜨는데, 그 안의 내용에 모든 Off 부분에 설정하면 된다.

다음 작업들은 반드시 필요한 것은 아니지만, 관리 및 작업의 편의상 해주면 좋은 정도이다.

Windows Update 비활성화.

MS의 Windows Update 설정은 대부분 완전 자동으로 하는 것을 추천한다. 하지만, 서버 제품 군은 가급적 이 설정을 끄는 것이 좋다. 특히 예상치 못한 시점에 갑자기 꺼지는 경우도 있기 때문이다.

설정 방법은 간단하다.먼저 제어판에 들어간다.

다음은 System and Security에 들어간다.

Windows Update 항목에 들어간 뒤,

왼편에 조그만하게 있는 Chnage Settings에 들어간다.

선택상자에서 Never check for updates를 선택하고 OK를 클릭한다.

 

User Account Control 비활성화

UAC(User Account Control) 이란, Windows에서 동작하게 되는 중요한 자원(시스템 파일 혹은 레지스트리 정보, 설치된 프로그램 파일 등등)이 자동적으로 프로그램에서 손대게 될 때, 사용자에게 확인 작업을 하는 보안 기능이다. 특히 트로이의 목마 같이 사용자 몰래 동작해서 Windows의 주요 파일들을 멋대로 변조하거나, 시스템 감시를 하는 짓과 같은 것을 원천 차단하게 된다. 이 기능은 Windows의 최 중심부인 Kernel 레벨에서 원천 차단하기 때문에, 화면이 완전히 멈추고, 사용자 확인을 요청하는 확인, 취소 버튼만 보이게 된다. 그래서 보안 설정 상 이 부분을 그래도 두는것을 권장한다.

하지만, 매번 작업할 때마다, 화면이 잠기게 되면 매번 확인을 눌러주는 것도 일이 되는데다가, 원격에서 작업하는데 미묘하게 화면이 잠겨서 더이상애로사항이 꽃피게 된다. 가급적 이 옵션을 설정하는게 편하다. 물론 화면이 잠겨도 아무런 문제가 없다면 켜 놓는게 좋다.

먼저 제어판에 들어간다.

제어판 항목 중, User Account 항목을 선택한다.

User Accounts 안의 User Account를 선택한다.

User Account 안에 맨 아래에 위치한 Change User Account Control Settings 를 클릭한다.

그러면 UAC(User Account Control) 설정화면이 나오는데, 여기서 맨 아래 단계로 맞추고 OK를 클릭한다

 

SharePoint Foundation 설치.

이제 본격적인 SharePoint 설치가 되겠다. SharePoint 에서 설치 작업은 크게 파일 자체 설치와 DB 구성 설치로 나눌 수 있다. 그에 맞추어 설명한다.

먼저 SharePoint Foundation 설치 파일을 실행한다. 대개 http://download.microsoft.com 에서 SharePoint Foundation 이라고 치면 설치파일을 쉽게 찾을 수 있다. 이 파일을 받아 설치를 하면 된다. 더블 클릭하면, 자동으로 압축을 풀고 설치진행이 시작된다.

이 SharePoint Foundation도 나름 서버 제품이기 때문에, 서버 제품 특유의 Autorun 화면이 나온다.
그 중 “Install software prerequisites” 라는 항목을 클릭해서 실행한다. (Install 바로 다음에 있는 항목)

예전에는 설치를 진행할 때, 설치에 필요한 필수 항목들을 설치해 달라고 오류가 나거나, 별도 메시지 창을 제공했는데, 이번에는 별도 도구를 제공해서 한번에 필요한 요소들을 모조리 설정해주는 편리한 도구가 생겼는데, 바로 그것이 “Install software prerequisites”  이다.

실행하면 다음과 같은 항목들을 알아서 구성해준다.

  • Application Server Role, Web Server (IIS) Role
  • Microsoft SQL Server 2008 Native Client
  • Hotfix for Microsoft Windows (KB976462)
  • Windows Identity Foundation (KB974405)
  • Microsoft Sync Framework Runtime v1.0 (x64)
  • Microsoft Chart Controls for Microsoft .NET Framework 3.5
  • Microsoft Filter Pack 2.0
  • Microsoft SQL Server 2008 Analysis Services ADOMD.NET
  • Microsoft Server Speech Platform Runtime (x64)
  • Microsoft Server Speech Recognition Language - TELE(en-US)
  • SQL 2008 R2 Reporting Services SharePoint 2010 Add-in

먼저 설치되어 있으면 건너뛰고, 없으면 설치하도록 되어 있다.
이 모든 진행은 마법사 화면으로 되어 있으며, Next만 누르면 거의 완료된다.

설치 중간에 재 시작이 될 수 있는데, 로그인만 하면 자동으로 실행되므로, 반드시 설치했던 계정으로 계속 로그인 하도록 한다. (대개 특별한 계정을 만들지 않으면 Administrator 계정일 것이다. )

모든 설치가 완료되면 다음과 같은 요약화면이 뜬다.

완료가 되었으면 이제 SharePoint 자체를 설치한다. 
아래와 같은 초기 화면이 닫혔다면 다시 설치파일을 실행해서 화면에 나오게 한다.

이제 앞서 클릭했던 “Install software prerequisites” 다음 항목인 “Install SharePoint Foundation”을 선택하도록 한다.

자동으로 새로운 설치 진행 준비가 완료되면 다음과 같이 설치 종류를 물어본다. 하나는 Standalone 설치이고 다른 하나는 Server Farm 설치이다. SQL DB를 별도로 구성하려면 Server Farm을 해야 되지만, 이를 위해서는 Active Directory 까지 준비가 되어 있어야 한다. 그 정도 되면 여기서 설명하는 범위가 벗어나므로 그냥 Standalone으로 구성한다. 자동으로 SQL DB와 SharePoint 모듈이 모두 설치되며, 심지어는 자동으로 팀 사이트까지 만들어 놓게 된다.

여튼 Standalone을 선택한다.

모든 설치는 자동으로 진행된다, 끝날 때까지 기다리면 된다.

완료가 되면, 체크 박스와 함께 Finish 버튼이 나오는데, 체크 박스를 끄고 Finish를 한다. 다음 단계는 SharePoint 구성인데, 이 체크 박스의 체크의 의미가 그 SharePoint 구성 도구를 자동으로 띄울 것인지를 묻는 것이다.

우리는 SharePoint 구성 전에 , SQL Server 2008 R2에 대한 업데이트를 먼저 진행할 것이다.

 

SQL 2008 R2로 업그레이드 및 DB 관리도구 설치

SharePoint 가 2010이 되면서 SQL Server 2008 R2를 지원하게 되었는데, 이를 통해 업데이트 된 사항이 바로, FILESTREAM이라는 기능이다. 이 기능은 BLOB 즉 대용량 바이너리 파일을 DB에 직접 넣지 않고, 공유 폴더 같은 곳에 넣어주는 기능이다. SharePoint의 최대의 단점이 바로 이 바이너리 파일을 직접 DB에 넣는 문제라고 볼 수 있는데, 이 부분을 상당 부분 개선할 수 있다.

그렇지만, 이 FILESTREAM 기능을 사용하려면, SQL DB 자체의 설정 변경이 반드시 필요하다. 그 작업을 위해서는 당연한 것이지만, SQL Server가 2008 R2 이여야 하며, 또한 SQL Management Studio 가 필요하다.

이제부터 그 작업이라고 보면 된다.

먼저 MS SQL Server 2008 R2 Express with Tools 라는 것을 받는다. 반드시 With Tools 버전을 받는다. 다른 Express 버전과는 다르게, 이 버전은 SQL Managment Studio가 탑재되어 있다. 물론 별도로 받아 설치할 수 있지만, 한번에 받는 것이 더 편하다.

먼저 SQL Server 2008 R2로 업그레이드부터 한다.

SQL Server 2008 R2로 업그레이드

이 작업도 많이 애매한 편인데, 요즘 등록된 SharePoint Foundation 2010 에는 2008 R2 버전이 탑재되어 있다. 예전에 받아놓았던 SharePoint Foundation 설치 파일에는 그냥 2008만 담겨 있다.여튼 R2든 그냥 2008이든, 업그레이드 과정을 먼저 시도한다.

설치용 실행파일을 실행하면 Server 설치도구가 뜬다.

좀 메뉴가 복잡하게 되어 있는데, 왼편 Installation 항목을 클릭해서 화면을 전환한 뒤, 3가지 항목 중에 가운데에 위치한, Upgrade from SQL Server 2000, SQL Server 2005 or SQL Server 2008 을 클릭하도록 한다.


그러면 EUL 승인 화면이 뜨는데, 적당히 하고 Next를 하면, 대부분의 화면에서는 자동으로 넘어간다.

완료되면 정리된 요약 화면이 뜬다. 해당 서버 설정에 따라 Reboot를 요청하기도 하는데, Reboot를 해주도록 한다.

다시 서버 설치용 도우미 화면으로 돌아가도록 한다. 그리고 난 뒤, 이번에는 “New Insatllation or add features to an existing installation” 항목을 클릭한다.

무언가 자동으로 설정을 체크하고 자동으로 휙휙 넘어간다. EUL 화면이 뜨면 반드시 승인하고 넘어가도록 한다.

그러면 아래와 같은 선택화면이 나오는데, 모두 선택하도록 하자. 굳이 분리해서 구성할 정도로 옵션이 많지도 않기 때문에, 체크 박스 모두 선택한 뒤 Next를 클릭한다.

다음은 SQL의 인스턴스를 선택하는 부분인데, 여기서는 기존에 있는 인스턴스에 덧붙이는 형태로 한다. 그래서 선택 버튼에서도 Named Instance로 선택되어 있다. 특별히 변경하지 않고 그대로 진행한다.

다음은 서비스 동작 계정인데, 이 부분 역시 기본값으로 둔다.

이부분은 SQL 인증 방식인데, 만일 원격에서(다른 PC에서)해당 SQL DB를 열여볼 일이 있다면,반드시 아래 화면과 같이 설정한다. Windows authentication mode는 AD 계정이 아닌 이상 내부에서 밖에는 열 수가 없다. 그러므로 반드시 Mixed Mode를 선택하도록 한다. 그리고 암호를 설정한다.

같은 화면의 탭에서 FILESTREAM을 선택하는데, 여기 안의 모든 항목을 체크하도록 한다.

Next를 클릭하면, 역시 오류 사항 자동 보고 인데 역시 그냥 두고 Next

자동으로 구성되면서 무엇인가 열심히 설치된다.

완료되면 완료 보고 페이지가 뜬다.

 

SharePoint Foundation 구성

이제 본격적인 SharePoint 구성이다. 원래 이 부분은 Server Farm 인 경우 복잡다단하게 되지만, Standalone으로 하는 경우에는 대부분의 설정이 자동으로 구성되기 때문에, 지켜 바라만 봐도 된다.

실행 방법은 Start –> All Programs –> Microsoft SharePoint 2010 Product 에 있는 SharePoint 2010 Products Configuration Wizard 를 실행하면 된다.

간단한 형태의 마법사가 뜬다.

Next를 누르면 경고 메시지가 뜨는데, 그냥 Yes를 클릭한다. 이 작업에서 자동으로 설정되고, 자동으로 껏다가 켜지는 항목들을 나열하는 부분인데, 운영 중이 아닌 경우에는 무시해도 되는 항목들이다.

이제 설치를 가만히 지켜본다.

완료되면 Finish를 클릭한다. 그러면 자동으로 만들어진 웹사이트가 뜬다. 웹사이트 주소는 http://컴퓨터이름 이다.

현재 로그인한 계정의 ID와 암호로 로그인하면 된다.

 

간단하게 구성한 SharePoint는 여기까지. 설치 작업은 예전 WSS 3.0 보다 훨씬 간단해지고, 단순해진 것 같다.
더욱이 자동적으로 설치 환경을 꾸며주는 기능은 칭찬을 주고 싶다.

이 외에도 실제로 활용하려면, 몇몇 부분을 손봐야 겠지만, 당장 사용하는데에는 큰 문제가 없을 것이다.

728x90

예전 2007 시절에는 asmx 파일을 적당히 올려서 웹서비스를 구축했다. 특히 javascript를 이용해서 데이터를 전달할 때는 aspx 파일로 적당히 만들어 JSON으로 Return 하는 식으로 만들곤 했다. 그런데, 이번에 SharePoint 2010으로 넘어오면서 기존에 Copy&Paste 식의 배포가 아닌 SharePoint 솔루션 방식으로 하다 보니, 위의 방식으로 하기에는 왠지 폼이 나지 않았다. 게다가 WCF 라는 나름 유연한 통신 방식을 활용하고 싶다는 생각도 들었다.

그래서 WCF 인터페이스를 어떻게 WSP로 싸서 업로드 하고 그 내용을 Client를 통해 제어를 하는지 살펴보려고 한다.

이 모든 작업은 SharePoint 2010 을 기준으로 하며, 개발 도구는 Visual Studio 2010 으로 한다.

 

1. SharePoint Project 만들기.

새 SharePoint Project를 만든다. 템플릿 트리에서는 Visual C# –> SharePoint –> 2010 을 선택하고, 목록에서는 Empty SharePoint Project 를 선택한다. 적당한 위치에 프로젝트 이름과 위치를 결정하고 생성한다.

그리고 난 뒤, 배포 위치를 설정한다.
URL이 있는데, 이 URL은 디버그를 하기 위한 URL이다. 가급적 로컬로 잡는 것이 작업하는데 편하다. 굳이 이 부분의 URL에 연연하지 않아도 된다. 그리고 그 밑에 솔루션 유형을 선택하는 부분이 있는데, 그 중 “Deploy as a farm solution”을 선택한다.

 

WCF를 설치하는 작업은 _vti_bin 과 같은 시스템 쪽 위치의 가상 디렉터리를 활용할 예정이기 때문에, 제한된 형태로 구성되는 Sandboxed solution 으로는 무리가 있다. 그러므로 farm solution 으로 선택한다.

다 구성되면 다음과 같은 솔루션 구성을 보여준다.

 

2. WCF 파일 구성.

먼저 WCF 파일을 담을 위치를 구성한다. 구성하는 방법은 배포용 폴더 위치를 추가해주면 된다. 웹 서비스나, WCF와 같은 외부 노출된 실행 모듈은 대부분 /_vti_bin 에 위치해 있다. 이 폴더는 SharePoint 설정 및 도구가 있는 14 폴더 중 ISAPI 이다. ( 예 C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\ISAPI )

배포 도구에서 해당 위치에 파일을 하기 위해서는 해당 배포 위치를 구성해줘야 하는데, 배포 폴더를 하나 추가해줘야 한다. 추가하려면, 프로젝트 위에서 오른쪽 클릭을 해서 Context Menu를 띄운다. 그리고, Add –> SharePoint Mapped Folder... 를 선택한다.

SharePoint Mapped Folder를 클릭하면, Add SharePoint Mapped Folder 창이 뜨면, 그 중에 ISAPI를 선택한다.

이제 WCF용 파일들을 만들어야 된다. 이 파일들을 직접 cs 파일들과 svc 파일을 만들어도 되지만, WCF 프로젝트를 잠시 추가해서 템플릿을 받아 사용하는 것이 좋다.

솔루션에서 새로운 프로젝트를 추가하고, 프로젝트 중에서 WCF Service Library를 선택한다.
 

템플릿을 통해 만들어진 다음 2개의 파일을 SharePoint의 프로젝트에 복사한다.

  • Service1.cs
  • IService.cs

적당한 폴더를 만들어 구성할 수도 있고, 아니면 프로젝트 루트에 복사할 수도 있다.

이 파일은 WCF를 구성하기 위한 구성요소들로 IService.cs는 Contract를 담당하는 Interface 파일과 다른 하나는 그 Contract를 구현하는 구현 코드가 담기는 파일이다. 그 내용은 다음과 같다.

IService1.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;

namespace WcfServiceLibrary1
{
    // NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "IService1" in both code and config file together.
    [ServiceContract]
    public interface IService1
    {
        [OperationContract]
        string GetData(int value);

        [OperationContract]
        CompositeType GetDataUsingDataContract(CompositeType composite);

         // TODO: Add your service operations here
    }

    // Use a data contract as illustrated in the sample below to add composite types to service operations
    [DataContract]
    public class CompositeType
    {
        bool boolValue = true;
        string stringValue = "Hello ";

        [DataMember]
        public bool BoolValue
        {
             get { return boolValue; }
             set { boolValue = value; }
        }

        [DataMember]
        public string StringValue
        {
              get { return stringValue; }
              set { stringValue = value; }
        }
    }
}

Service1.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;

namespace WcfServiceLibrary1
{
    // NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "Service1" in both code and config file together.
    public class Service1 : IService1
    {
        public string GetData(int value)
        {
            return string.Format("You entered: {0}", value);
        }

        public CompositeType GetDataUsingDataContract(CompositeType composite)
        {
            if (composite == null)
            {
                throw new ArgumentNullException("composite");
            }
            if (composite.BoolValue)
            {
                composite.StringValue += "Suffix";
            }
            return composite;
        }
    }
}

적당한 위치에 추가하면 다음과 같은 솔루션 구성을 볼 수 있다.

지금까지 구성한 내용은 WCF의 알맹이 부분이라면, 이제 외부에 노출되는 페이지를 만들어야 한다. 즉 ASMX나 ASPX 파일을 만들어야 되는 것이다. WCF에서는 이런 역할을 하는 파일이 SVC 파일이다. 그러나 현재 SharePoint 템플릿에서는 SVC 파일을 독자적으로 추가할 수 없으므로, 텍스트 파일로 추가하여 확장자를 SVC로 변경하는 것이다.

이제 새롭게 열린 파일 안에 다음과 같은 항목을 추가한다.

<%@ ServiceHost Language="C#" Debug="true"
Factory="Microsoft.SharePoint.Client.Services.MultipleBaseAddressBasicHttpBindingServiceHostFactory
,Microsoft.SharePoint.Client.ServerRuntime, Version=14.0.0.0, Culture=neutral
, PublicKeyToken=71e9bce111e9429c"
Service="WcfServiceLibrary1.Service1, $SharePoint.Project.AssemblyFullName$" %>

위의 내용을 추가할 때, Service 항목 내에 위에서 추가한 WCF 메인 모듈의 네임스페이스와 클래스이름을 연결해서 구성한다. (필자는 특별히 바꾼게 없으므로 위와 같이 적었다.)

 

3. 참조(References) 구성

지금까지 파일들을 추가했으면, 이제 WCF를 구성하기 위해 필요한 DLL 참조를 구성해야 한다.

  1. System.ServiceModel
  2. System.ServiceModel.Web
  3. System.Runtime.Serialization
  4. Micosoft.SharePoint.Client.ServerRuntime

1, 2, 3번은 Add Reference를 한 뒤, .NET 탭에서 찾으면 금방 찾을 수 있다. 그런데, 문제는 4번 Assembly다. 저 Assembly는 .NET이나, COM에서 절대 찾을 수 없다. 직접 Folder를 따라가는 수 밖에 없다. 이를 위해서는 Browse 탭으로 넘어간다.

그리고 난 뒤, C:\ –> Windows –> Assembly –> GAC_MSIL –> Micosoft.SharePoint.Client.ServerRuntime –> 14.0.0.0__71e9bce111e9429c –> Microsoft.SharePoint.Client.ServerRuntime.dll 로 들어가서 Microsoft.SharePoint.Client.ServerRuntime.dll을 선택한다.

 

4. Attribute 설정하기.

원래 WCF에서 Address 설정부터, Binding, 인증 처리 등을 설정하기 위해서는 web.config를 통해서 설정하게 된다. 그런데, 현재 구성하려는 SharePoint 패키지 프로젝트에서는 특정 web.config 부분을 일일히 수정할 수 없다. 그것을 .NET Attribute 기능을 이용해서 적용할 수 있다.

이 부분은 구현하는 CS 파일( 위의 예제로 보면, Service1.cs 파일 )에서 적용한다.
여기서 사용하는 Attribute는 BasicHttpBindingServiceMetadataExchangeEndpointAttribute 와 AspNetCompatibilityRequirements 를 사용한다.

적용하는 코드 부분만을 보면 아래와 같다.

Service1.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using Microsoft.SharePoint.Client.Services;
using System.ServiceModel.Activation;
using Microsoft.SharePoint.Security;

namespace WcfServiceLibrary1
{

   [BasicHttpBindingServiceMetadataExchangeEndpointAttribute]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]   
    public class Service1 : IService1
    {
         public string GetData(int value)
         {
              return string.Format("You entered: {0}", value);
         }

         public CompositeType GetDataUsingDataContract(CompositeType composite)
         {
              if (composite == null)
              {
                       throw new ArgumentNullException("composite");
               }
               if (composite.BoolValue)
               {
                       composite.StringValue += "Suffix";
               }
               return composite;
          }
     }
}

using으로 세가지를 추가하고, class 위에 attribute로 두 개를 추가해주면 된다.

 

5. 프로젝트 파일 설정 변경.

프로젝트 자체가 SharePoint 구성요소에 대한 설정만 자동으로 추가된다. 그래서 WCF의 svc 파일 처리가 불가능하다. 이를 위해서는 수동적인 방법을 통해서 처리해야 한다.

먼저 SharePoint 프로젝트를 Text 파일로 열어 그 안의 XML 파일을 연다. csproj 파일을 메모장 같은 도구로 열면 된다. 그리고 Project –>PropertyGroup 엘리멘트 안에 <TokenReplacementFileExtensions>svc</TokenReplacementFileExtensions> 라는 엘리멘트를 추가하면 된다.

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <SchemaVersion>2.0</SchemaVersion>
    <ProjectGuid>{6EDE3191-D412-4B0A-917A-09BA91DAD509}</ProjectGuid>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>Nocson.EzSearch</RootNamespace>
    <AssemblyName>Nocson.EzSearch</AssemblyName>
    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
    <ProjectTypeGuids>{BB1F664B-9266-4fd6-B973-E1E44974B511};{14822709-….</ProjectTypeGuids>
    <SandboxedSolution>False</SandboxedSolution>
    <TokenReplacementFileExtensions>svc</TokenReplacementFileExtensions>
    <SccProjectName>SAK</SccProjectName>
    <SccLocalPath>SAK</SccLocalPath>
    <SccAuxPath>SAK</SccAuxPath>
    <SccProvider>SAK</SccProvider>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <De…

저장한 뒤에 해당 프로젝트를 다시 로드 한다.

 

6. 배포 하고 테스트.

SharePoint 프로젝트의 특징은 역시 자동으로 .WSP라는 SharePoint 솔루션으로 만들어 주고 그것을 Debug로 설정한 사이트로 자동으로 업로드 해준다.

배포가 성공적으로 되면 웹사이트에서 http://localhost/_vti_bin/WCFTest.svc/MEX 를 입력한다.(꼭 맨 뒤에 /MEX가 있어야 정상적으로 접속이 가능하다.)

정상적으로 읽어오면 아래와 같은 화면이 뜬다.

 

7. 정리

이 연결하는 작업을 전부 수동으로 만들면 그리 문제 없이 설정하고 구성할 수 있다. 하지만, SharePoint의 솔루션으로 포함시켜서 만들려면 몇 가지 씩 손봐야 된다. 그런데도 굳이 SharePoint의 솔루션으로 작업하는 이유는 나중에 Uninstall이나 업그레이드를 보다 간단하게 수행할 수 있기 때문이다.

위의 글들은 아래의 문서들을 참고로 직접 적용 후 정리했다.

728x90

+ Recent posts

728x90