[Better Code]Visualize Code Relationships

Agile Development 2009. 5. 2. 18:40 Posted by kkongchi

image

위 그림처럼 이번 Visual Studio Team System 2010에서는 Analyze(분석) 메뉴에 새로운 기능이 하나 더 추가되었습니다. 바로 Visualize Code Relationships 메뉴입니다. (아직 한글로는 어떻게 번역될 지 잘 모르겠네요)

이 기능은 제목 그대로 코드의 관계를 시각화해서 보여 줍니다. UML 다이어그램과도 조금 다르고, 모델링 용의 DSL 다이어그램과도 조금 다르긴 하지만, 꽤 괜찮은 그림을 보여줍니다.

그림을 한 번 보기 위해서, 아래와 같은 아주 간단한 Visual Studio 솔루션을 한 번 구성해봤습니다.

image

구조는 간단합니다. WebApplication1이 WebService1을 Web Reference를 해서 호출하고, 다시 WebService1은 ClassLibaray1을 내부적으로 Reference해서 호출하는, 아주 흔한 형태의 Application 구조입니다. 그리고 ClassLibrary1 내부에서 Class2는 Class1을 사용합니다.

이 구조가 어떻게 시각화 되는지 한 번 볼까요?

1. Visualize Call Dependency – By Assembly

image

오른쪽 옆의 버튼을 누르면, 내부로 펼쳐지면서, 내부에 들어있는 클래스도 보입니다.

image 

2. Visualize Call Dependency – By Namespace

image

3. Visualize Call Dependency – By Class

image

프로젝트가 성숙하고 발전되면, 코드도 따라서 많아지면서 구조도 복잡해지기 마련입니다. 복잡도가 높아지면 높아질 수록 유용한 툴이 되지 않을까 싶습니다.

하지만, 지금 그림을 보시면 아시겠지만, WebApplication1이 Web Service1를 Web 참조를 하고 있습니다만 그림에는 그 참조 관계는 표시가 제대로 되질 않네요. 결국 직접적인 참조 외에는 다이어그램에서 보기가 힘든 것 같습니다. 쉽지 않겠지만, 그런 부분들도 표시할 수 있었더라면 정말 환상적이었을 텐데 하는 생각이 듭니다.

두번째 10분입니다.

자막 제작에는 Jubler를 사용하였습니다. 추천드립니다. :)

이젠 툴도 어느 정도 익숙해져서, 실제 10분 자막 제작에 드는 시간을 측정해보았습니다. 2시간반이 걸리더군요. ㅠㅠ 그냥 혼자 대충 듣고 이해할 때와는 달리 글로 정확하게 표현을 하려다보니 쉽지 않군요. 제가 빠삭하게 알고 있는 내용도 아니어서 더 시간이 걸린듯.

그래도 PPL이 태스크 개념 중심이라는 것, PPL과 TBB의 연동 계획 등 새로운 것들을 저도 알게 되어 기뻤습니다. 


언제가될지 모르겠으나 다음 회도 기대해주세요.
안녕하세요, 정재원이라고 합니다.

C++ 관련 글을 올리고 계신 흥배님과 마찬가지로 게임 개발자입니다.

그간 여러분들이 올려주시는 좋은 글들을 읽고만 있다가...; 무언가 올리지 않으면 짤리겠다는 위기감에 소재거리를 뒤져보았습니다;;

흥배님과 가능한한 겹치지 않는 주제와 방식으로 무엇이 적당할까 생각하던 중... 본 동영상을 발견하고는, 그래 여기에 자막을 달아보자! 생각하게 되었습니다. 작년 PDC에서 발표된 Parallel Pattern Library에 대한 동영상입니다.

왠지 간단할듯 여겨졌으나... 처음 해보는 자막 작업 쉽지 않더군요 ㅠㅠ 도구를 찾고 익히는데 시간이 더 들었습니다.

한시간이 넘는 것을 한번에 번역해 올리려면, 날새겠다는 생각이 들더군요; 유투브 업로드가 10분 미만의 동영상으로 제한된다는 것에 안도감(?)을 느꼈습니다...

이를 변명삼아 일단 10분 분량을 올립니다. 포스트 수를 늘리기 위한 수작 아니냐 라는 비난의 소리가 들립니다만... 맞습니다 ㅠㅠ 툴에 좀 더 익숙해지면 자주라도 올리겠습니다.

부족한 번역과 분량에 대해 건설적 충고 부탁드리고요, 기타 제 포스트에 제안 사항 있으시면 댓글 달아주십시오.

 

지난 번 글에서 Visual Studio Team System 2010의 향상된 Unit Test 기능에 대해서 살펴 본 바가 있었습니다. 오늘 소개드릴 PEX는 Microsoft Research 그룹에서 개발한 Automated Whitebox Testing Framework 입니다. 꽤나 긴 정의입니다만, 간단히 말하자면 자동으로 코드를 분석해서 WhiteBox Unit Test를 만들어주는 Visual Studio Extension입니다.

Blackbox Test – Blackbox Test는 우리가 코드 내부에 대한 지식이 없다는 것을 전제로 합니다. Blackbox Test에서는 코드의 노출된 API를 소비하는 Consumer의 입장에서 Function을 테스트하게 됩니다. 비교적 초기에 작성되는 Unit Test들은 이 Blackbox Test가 됩니다. 아직 Code가 성숙되지 않은 상태에서 API의 Requirements, Specification 등만을 가지고 테스트를 작성하게 되기 때문입니다. 하지만 코드 개발이 점점 진행됨에 따라서 Unit Test는 Whitebox Test로 이행되게 됩니다.

Whitebox Test – Whitebox Test는 코드 내부를 알고 있다는 것을 전제로 하는 테스트입니다. 우리는 코드를 알기 때문에 모든 가능한 성공/실패 시나리오를 모두 알 수가 있고, 특정 데이터나 입력 조건이 어떤 Flow를 따르는지에 대해서 충분하게 알 수 있습니다. 이런 가능한 모든 시나리오에 대해서 Unit Test를 작성하고 수행하게 되면 그것을 Whitebox Test라고 말할 수가 있습니다.

* 이는 Blackbox Test, Whitebox Test에 대한 일반적인 정의가 아니라, Unit Test관점에서 바라본 정의입니다. 일반적인 정의에 대해서는 Wikipedia의
Whitebox Testing, Blackbox Testing 항목들을 참고하시기 바랍니다.

Unit Test를 수행할 때에 대개 Code Coverage Test를 같이 수행하는 이유가 바로 우리가 작성한 Unit Test가 어느 정도 수준의 Whitebox Testing에 도달했는지를 알 수가 있기 때문입니다. 당연히 우리가 작성한 모든 코드에 대해서 완벽한 Whitebox Testing을 Unit Test를 통해서 수행했다면 Code Coverage Test의 결과는 100%가 되어야 합니다. 하지만, 실제로 Unit Test건 Manual Test Case건 Code Coverage Test결과가 100%에 도달하기는 참으로 힘듭니다. 일반적으로 80%정도를 목표로 하지만, 그 이하가 되는 경우가 대부분입니다. 그것은 복잡성의 문제이기도 하고, 비용 대비 효과의 문제이기도 합니다. 코드의 모든 경로를 통과하는 Unit Test를 작성하려면 엄청난 시간이 소요될 테니까요.

이 점이 PEX라는 제품이 나오게 된 배경이라고 할 수 있습니다. PEX는 Visual Studio의 코드를 분석해서, Input과 output 조합을 찾아서 자동으로 테스트 코드를 만들어 줍니다. 자동으로 생성된 테스트 코드이지만, 높은 Coverage를 보여 줍니다. PEX를 사용하면, Unit Test 작성에 들이는 수고를 상당히 줄일 수 있을 것 같습니다.

 

PEX 홈페이지는 http://research.microsoft.com/en-us/projects/pex/default.aspx 이며, 현재 Visual Studio Team System 2008과 2010에서 사용할 수 있다고 되어 있습니다. 즉, 현재 VSTS 2008을 쓰고 계시는 개발자 분이라면 다운로드 받아서 써 보실 수 있다는 의미입니다. 현재 정식 버전은 아니고 Pre-release 버전입니다. 그리고 Visual Studio 2008 Professional 버전을 위한 비 상업용 Academic 버전도 다운로드 받을 수 있습니다. 다운로드 링크는 http://research.microsoft.com/en-us/projects/pex/downloads.aspx 입니다.

이번 글은 개요이니까, PEX Tutorial에 나오는 간단한 샘플만 잠시 보도록 하겠습니다.

   1:  [PexMethod]
   2:  public void ParameterizedTest(int i)
   3:  {
   4:       if (i == 123)
   5:           throw new ArgumentException("i");
   6:  }

Integer 타입의 매개 변수를 받는 간단한 메소드입니다. 매개 변수가 123일 때에는 ArgumentException을 일으키는 딱 두 줄의 구현만 있을 뿐입니다. PexMethod Attribute는 이 메소드가 PEX Test method라는 것을 의미합니다. 이 메소드에 대고 마우스 오른쪽 클릭을 하게 되면 컨텍스트 메뉴에 다음과 같이 PEX 관련 메뉴들이 보이게 됩니다.

여기서 Run Pex Explorations를 선택하게 되면, PEX가 자동으로 코드를 분석해서 가능한 모든 매개 변수를 대입해 본 다음 그 결과를 바탕으로 아래처럼 결과를 보여주게 됩니다.

이 부분 – Run Pex Exploration 실행 - 에서 저는 예상치 못했던 에러를 만났습니다.

[critical] unexpected failure during exploration
System.IO.FileLoadException: Could not load file or assembly 'Microsoft.Z3, Version=2.0.30325.1, Culture=neutral, PublicKeyToken=9c8d792caae602a2' or one of its dependencies.


위와 같은 에러였는데, 이 에러는 Visual C++을 설치하지 않았을 때에 발생하는 것으로 보입니다. 아직 PEX 설치 파일이 어떤 조건에서도 완벽하게 모든 필요한 파일들을 설치하지 못하는 것 같습니다. 이 에러는 Visual C++ 2008 SP1 Redistributable Package를 다운로드해서 설치하는 것으로 해결할 수 있습니다. 이 정보는 http://social.msdn.microsoft.com/Forums/en-US/pex/thread/5862d522-0c2e-481c-b537-864e7427a7e5 에서 얻었습니다. 혹시 Visual C++ 가 설치되지 않은 분들은 참고하시기 바랍니다.

예상대로 123이라는 매개변수가 입력되었을 때에 ArgumentException이 발생한 것을 볼 수가 있습니다. 그리고 여기서 또 다시 마우스 오른쪽 컨텍스트 메뉴를 통해서 Go To를 선택하게 되면 실제 자동으로 생성된 테스트 코드를 아래 그림처럼 볼 수가 있습니다.

   1:  [TestMethod]
   2:  [PexGeneratedBy(typeof(HelloWorldTest))]
   3:  public void ParameterizedTest01()
   4:  {
   5:      this.ParameterizedTest(0);
   6:  }
   7:   
   8:  [TestMethod]
   9:  [PexGeneratedBy(typeof(HelloWorldTest))]
  10:  [PexRaisedException(typeof(ArgumentException))]
  11:  public void ParameterizedTest02()
  12:  {
  13:       this.ParameterizedTest(123);
  14:  }

간단한 샘플이긴 하지만, PEX의 강력한 자동 테스트 코드 생성 기능을 볼 수 있으셨을 거라고 생각합니다. 다음 포스팅에서는 PEX에 대해서 더 깊이 들어가 보겠습니다. 아 물론, Code Analysis에 대한 글들도 계속 올릴 예정입니다. 읽어주셔서 감사합니다.

Windows 7을 위한 Windows XP 모드

Windows 7 2009. 4. 27. 19:29 Posted by 알 수 없는 사용자
Windows XP 모드는 Windows 7 기반 PC에서 Windows XP 어플리케이션을 사용할 수 있습니다.
Windows XP에서 Windows 7 환경으로 이행하려는 기업을 목적으로 개발했다고 하는데 일반 사용자에게도 많은 도움이 될 것 같습니다.


공개된 스크린샷을 보면 Windows 7 환경에서 XP 어플리케이션이 자연스럽게 구동되는것을 볼 수 있습니다. (에어로 테마가 아닌 XP의 루나 테마로 확인 할 수 있습니다.)

Windows Virtual PC의 Windows XP 환경에서 가상으로 동작하게 되며 호환성 문제의 고민을 해결합니다. 현재 아직 공개되지는 않았고 조만간 베타버전이 선보인다고 하는데 이후에 자세한 내용을 알아보도록 하겠습니다.

현재 공개된 정보는 아래 링크에서 자세히 확인 할 수 있습니다.
Windows XP Mode for Windows 7 Screens
Revealing Windows XP Mode for Windows 7

Welcome to F#(7) - 클리프 행어.

F# 2009. 4. 26. 18:22 Posted by 알 수 없는 사용자

-거인의 어깨에 위태롭게 매달려서 안간힘-_-.

여전히 함수형언어를, F#을 이해하기 위해서 몸부림 치고 있습니다. 제 짧은 생각인지 모르겠으나, F#을 처음배우는 프로그래밍언어로 생각하시는 분은 없을거라고 생각합니다. 호기심이든 다른 목적이 있어서든 기존에 주력으로 쓰는 명령형 언어가 있고 추가로 공부하려고 하시는 분이 거의 대부분이 아닐까 합니다.(게다가 F#은 C#이나 VB.NET을 하시던 분이 거의 대부분이 아닐까 싶네요) 저 역시 C#을 주력으로 쓰면서 F#을 공부하고 있는거니까요.

그래서 단순히 문법적 차이를 말씀드리기 보다는 함수형언어에 깔려있는 사상&접근법&관점의 차이에 대해서 말씀드리고자 하는데, 부족한 지식으로 글이 들쭉날쭉하고 앞내용과 뒷내용이 연결이 안되는, 한글이 생긴이래 진급을 못하고 있는 불상사도 자주 보게되는거 같습니다. 

오늘도 함수형언어를 이해하기 위한 노력의 일환으로 한 논문의 내용을 바탕으로 이야기 하도록 하겠습니다. Anders hejlsberg가 언어설계에 대한 이야기를 하면서 자신은 다른 거인의 어깨위에 서있다고 말을 했었는데, 저는 거인의 어깨에 선게 아니라 위태롭게 매달려 있다는 느낌이 드는군요.-_-;  아놔, 오늘도 서론이 길군요-_-;;

 

-무슨 논문이냐.

제가 한번 
"Why Functional Programming Matters?"라는 논문에 대해서 언급한적이 있었습니다. 오늘 포스트의 바탕이 될 이 논문에서 모듈화에 대한 이야기를 하면서 다음과 같은 이야기를 합니다. 

However, there is a very important point that is often missed. When writing a modular program to solve a problem, one first divides the problem into subproblems, then solves the sub-problems and combines the solutions. The ways in which one can divide up the original problem depend directly on the ways in which one can glue solutions together. Therefore, to increase ones ability to modularise a problem conceptually, one must provide new kinds of glue in the programming language. Complicated scope rules and provision for separate compilation only help with clerical details; they offer no new conceptual tools for decomposing problems. 

하지만, 그런 이야기에서 중요한 점이 무시되곤 한다. 어떤 문제를 해결하기 위해서 모듈화된 프로그램을 작성할때, 프로그래머는 우선 문제를 작은 문제로 분리해내야 하고, 각각의 작은문제를 해결해서 하나의 솔루션으로 조합해야 한다. 문제를 어떻게 분리할지는 전적으로 해결된 문제들을 어떻게 하나의 솔루션으로 조합할지에 달려있다. 그러므로 프로그래머가 조금더 능숙하게 문제를 모듈화해서 해결하게 해주려면, 프로그래밍언어에서 문제를 붙이기 위한 새로운 접착제를 제공해줘야 한다.  복잡한 스코프룰이나 분리컴파일을 좀 더 편리하게 할 수 있게 도와주는 것들은 그저 사무적인일(문제를 해결하는일 자체보다는 코드작성을 도와주는 부차적인일)정도를 도와줄 뿐이다. 즉, 문제를 잘개 쪼개기 위해선 아무 도움도 안되는 것이다.


그러면서 저자는 함수형언어에서 문제를 붙이기 위해 쓰이는 두가지 접착제를 소개합니다. 그 중 하나가 지난포스트에서도 언급했었던 higher-order function입니다. 기억나시나요? 함수를 인자로 받고 함수를 리턴가능한 함수가 higher-order function이었죠? 이 논문은 Miranda라는 언어를 사용하면서 Haskell을 쓰지 못해서 죄송하다고 언급하고 있습니다. 하지만, 조금 된 논문이라 F#으로 못해서 죄송하다는 말은 없군요. 크하하하-_-;;

그럼 저자가 higher-order function의 예로 언급한 reduce를 F#으로 구현해보면서 설명드리도록 하겠습니다. reduce는 MapReduce때문에 유명해지기도 했고, fold라는 이름으로도 불리는데요, 아마도 fold나 reduce나 둘다 뭔가를 하나씩 줄여나가는 이미지를 연상하면서 붙인 이름이 아닌가 싶습니다.


우선, 원문을 보실분들을 위해서 Miranda에 대해서 아주초큼 설명을 드리자면, Miranda는 ML에서 ML은 Lisp에서 출발한 언어라고 합니다. Lisp에서는 리스트를 Pair의 조합으로 표현하는데요, 여기서도 그런거 같습니다. Pair의 조합이란 cons(construct의 준말이라는 군요)라는 함수를 통해서 만들어지는데요(Lisp이나 Scheme하신분들은 많이 보셨을듯~) 인자를 두개 받아서 그 두 인자를 하나의 쌍으로 묶어서 리턴하는 함수입니다. 즉 Lisp에서 (1 2 3 4)라는 리스트는,

 

 (cons 1

(cons 2

(cons 3

(cons 4 nil)))) 


nil은 Lisp에서 빈값을 뜻합니다, (1 2)는 (cons 1 2), (1 2 3)은 (cons 1 (cons 2 3))의 결과 인거죠. F#이 OCaml에서, OCaml은 ML에서 출발했으니, F#에서도 리스트는 비슷하게 구현되지 않았을까 싶습니다.(List.hd, List.tl같은 함수를 보면 그런거 같습니다.) 그리고 두개의 값이 하나의 쌍으로 이루어 져있기 때문에, 값에서 head부분과 tail부분으로 나눠서 생각할 수 있습니다. (1 2 3)의 head는 1이고 tail은 (2 3)입니다. (4 5 6 7)의 head는 4이고 tail은 (5 6 7)인거죠

 

-그래 그래 얼른 reduce가 뭔지나 좀 보자.

일단, 숫자의 리스트를 모두더하는 sum을 생각해보도록 하죠. 빈값에 대한 sum은 0이겠죠? 그래서 아래와 같이 정의합니다. 

sum [] = 0 

그리고 리스트의 합은 head의 값과 나머지 tail의 합으로 정의할 수 있고, 그 tail의 합은 다시 head의 값과 나머지인 tail의 합으로 정의할 수 있습니다. 즉, 

sum list = List.hd(list) + sum List.tl(list) 

이렇게 정의할 수 있습니다. List.hd는 head의 약자로 List에서 head를 리턴하고, tl은 tail의 약자로 List에서 head를 제외한 나머지 부분을 리턴합니다. 이런 과정을 어떤 함수가 들어와도 가능하게 일반화 하려면, 위에서 빨간색(?)으로 음영표시한 두부분이 중요해 집니다. 즉, '리스트의 마지막에 도달했을때 어떤 값을 리턴할 것인가'와 '리스트의 head와 tail에 대해 어떤 함수를 수행할 것인가'하는 부분입니다. 예를 들면, 리스트에 대해서 곱셈을 수행하는데, 빈리스트에 대해서 0을 리턴하게 한다면 리스트의 곱셈값은 0이 되겠죠-_-;; 그래서 이럴땐 빈리스트의 값을 곱셈에 영향을 안주는 1로 설정해야 합니다. 물론, 위의 경우에서는 합에 영향을 주지 않는 0으로 선택한 것이구요. 

그럼, 각 리스트의 요소에 대해 수행할 함수를 아래와 같이 정의합니다. 

let add x y = x + y 

그리고 다음과 같이 reduce함수를 작성해줍니다. 

 let rec reduce f x list =
    if list = [] then x

    else f (List.hd list) ((reduce f x) (List.tl list)) 

즉, reduce는 자기자신의 정의속에 자신을 호출하는 부분이 있는 제귀함수이고, 인자로는 f, x, list를 받으며 list가 빈리스트라면 연산에 영향을 주지않는 값으로 x를 리턴하고, 빈 리스트가 아니라면 head와 나머지리스트에 함수를 f를 적용한 값을 가지고 f를 수행합니다. 위의 메서드를 좀 더 읽기 쉽게하기 위해서 타입을 명시해주면 아래과 같습니다.

let rec reduce (f : 'a -> 'b) (x : 'c) (list : 'd list) =
    if list = [] then x

    else f (List.hd list) ((reduce f x) (List.tl list))

즉, f는 함수이고, x는 제네릭한 값하나, list는 제네릭한 리스트인거죠. 일단, 수행결과를 보고 무슨 소리인지 더 풀어보도록 하죠. 위 두 함수의 정의를 평가해주고나서, reduce add 0 [1;2;3;4] 이렇게 실행을 하면 아래와 같은 결과가 나옵니다. 

val add : int -> int -> int

>

val reduce : ('a -> 'b -> 'b) -> 'b -> 'a list -> 'b 

> reduce add 0 [1;2;3;4];;
val it : int = 10 

리스트의 값을 모두 더해서 10이라는 값이 나왔군요. 이 값이 어떻게 나왔는지 reduce가 정의를 확장하고 줄여나가는 모습을 자세히 적어보면 아래와 같습니다.

 

즉,
add x y = x + y이고
(reduce add 0) [1;2;3;4] 라면..
= add 1 ((reduce add 0) [2;3;4])
= add 1 (add 2 ((reduce add 0) [3;4]))
= add 1 (add 2 (add 3 ((reduce add 0) [4])))
= add 1 (add 2 (add 3 (add 4 ((reduce add 0) []))))
= add 1 (add 2 (add 3 (add 4 0)))
= add 1 (add 2 (add 3 4))
= add 1 (add 2 7)
= add 1 9
= 10

음영으로 강조한 부분이 reduce가 확장되는걸 보기 쉽게 만든 부분입니다. 자신의 정의를 이용해서 빈리스트가 나올때까지 값을 확장하고, 하나씩 줄여가면서 값을 게산해 가는거죠. 사실 add는 이미 F#에 구현되어 있습니다. 아래와 같이 쓸 수도 있습니다. 

reduce (+) 0 [1;2;3;4] 

그리고 람다를 이용해서 아래와 같이 써도 똑같은 결과를 낼 수 있습니다. 

reduce (fun x y -> x + y) 0 [1;2;3;4] 

그리고 reduce를 이용해서 아래와 같은 연산들도 가능합니다. 

reduce (&&) true [true; true; true; false; true] //전부다 true인지 검사하는 함수
let append a b = reduce (fun x y -> List.Cons(x, y)) b a //두 리스트를 붙이는 함수 

그리고 아래는 append의 실행결과입니다.

> append [1;2] [3;4];;

val it : int list = [1; 2; 3; 4] 

List.Cons는 위에서 말씀드렸던 cons의 F#의 List버전입니다. 위 메서드를 확장하고 줄여가나는 모습을 자세히 그려보면 아래와 같습니다.

List.Cons(1, reduce (fun x y -> List.Cons(x, y)) [3;4] [2])

= List.Cons(1, (List.Cons (2, reduce (fun x y -> List.Cons(x, y)) [3;4] [])))

= List.Cons(1, (List.Cons (2, [3;4])))

= List.Cons(1, [2;3;4])

= [1;2;3;4]

 

즉, 리스트에 대해서 특정함수의 수행을 누적시켜서 결과를 얻는(Aggregate) 과정을 쪼개고 그 쪼갠 부분을 붙이는 방법으로 higher-order function을 사용해서 만든 reduce로 이렇게 다양한 연산이 가능한 것입니다. 물론 논문의 저자는 여기서 더 나아가고 있습니다만, 제 실력부족으로 더 이상 나가지 못하는 점 양해바랍니다.-_-;;; 아, 그리고 추가로 말씀드리자면 reduce는 F#에 이미 구현되어 있습니다. reduce가 fold라고 불리기도 한다고 위에서 말씀드렸었죠? 아래와 같이 하면 reduce add 0 [1;2;3;4]와 같은 결과를 낼 수 있습니다. 

List.fold_right (+) [1;2;3;4] 0

List.fold_left (+) 0 [1;2;3;4] 

둘의 차이점은 아래와 같습니다. 

(출처 : http://en.wikipedia.org/wiki/Fold_(higher-order_function))


아래그림은 fold_left이고, 윗그림이 fold_right입니다. 그리고
각각 그림의 왼쪽은 리스트이고, 오른쪽은 어떻게 펼치느냐에 대한 도식입니다. 즉, 왼쪽으로 펼쳐서 접느냐, 오른쪽으로 펼쳐서 접느냐에 따라 틀린거지요. 위에서 구현된 reduce는 fold_right라고 보시면 됩니다.

 

- 마치면서

사실, 진정한 함수형언어의 강점과 F#의 강점은 아직 맛도 못봤습니다. 이거 빨리 제가 맛을 보고 잘 전해드려야 할텐데;; 개인적인 사정이 점점 안좋아지는군요-_-. Anders hejlsberg의 인터뷰에서 발췌한 글을 끝으로 남기면서 글을 마치겠습니다.(왜 Anders hejlsberg이야기가 많이 나오냐면, 전  Anders hejlsberg빠돌이거든요-_-;) 이 글을 읽다보니, 왜 함수형언어의 힘을 빌린 LINQ를 추가해서 데이터를 다루는 부분을 추상화 했는지 확 다가오는 군요. 그리고 앤더스가 선언형 언어와 함수형언어의 장점을 점점 더 중요하게 생각하고 있다는 생각이 듭니다.(물론, 원문을 읽어보시면 더 자세한 내용을 보실수 있겠져~.) 그리고 언제나 그렇듯이 제 맘대로 번역-_-.

 

I think one of the things that are true of most programming languages today is that they force you to overspecify the solution to your problem. You're down there writing nested for loops and if statements and whatever, and really all you wanted to do was a join between two pieces of data. But there's nothing that allows you to say that. You have to get down and dirty and do hash tables and dictionaries, blah, blah, blah.

난 요즘 대부분의 프로그래밍언어가 프로그래머에게 문제를 해결하기 위한 솔루션을 너무 필요이상으로 서술하게 만든다고 생각합니다. 물론 사실이구요. 프로그래머는 그저 두 데이터집합을 조인하고 싶을 뿐인데, for루프랑 if문을 여러개 중첩시키고 어쩌고 저쩌고 해야만 하는거죠. 즉, 그냥 조인하게 해달라고만 말할 수 있는게 없다는 겁니다. 진흙탕으로 내려가서 해시테이블이랑 딕셔너리랑 기타등등, 뭐 그런것들이랑 열심히 뒹굴어야 하는거죠.



-참고자료

1. Expert F#, Don Syme, Adam Granicz, Antonio Cisternino, APRESS
2. Programming Language Pragmatics 2nd Edition, Michael L. Scott, Morgan Kaufmann Publishers
3. Structure and Interpretation of Computer Programs, 2nd Edition, Harold Abelson, Gerald Jay Sussman, Julie Sussman, The MIT Press
4. 구글을 지탱하는 기술, 니시다 케이스케, 멘토르
5. http://en.wikipedia.org/wiki/Fold_(higher-order_function)
6. http://broadcast.oreilly.com/2009/04/an-interview-with-anders-hejls-1.html
7. http://en.wikipedia.org/wiki/Miranda_(programming_language
8. http://www.math.chalmers.se/~rjmh/Papers/whyfp.pdf

Welcome to F#(6) - 비교본능.

F# 2009. 4. 23. 09:27 Posted by 알 수 없는 사용자
-지난시간에 이어서.
잘들 지내셨는지요. 어느새 수요일이 다 지나가는 군요. 오늘은 어제에 비해서 일이 잘 되셨나요? 그렇습니다. 사람은 자연스럽게 비교를 하려는 본능비스무리 한게 있는거 같습니다. 여친과 다른 사람들을 비교하고, 자기 자식이랑 친구아들을 비교하고, 심지어는 별 찌질한것까지도 비교하면서 그 우위에서 오는 만족감을 느끼려는게 사람이 아닐까요.

하지만, 비교는 좋은 이해의 틀이기도 합니다. 뭔가가 틀리다는 건, 다른 것들과 부딪히면서 비로소 틀린점들이 눈에 들어오기 시작하고 형체를 갖추기 시작합니다. 이번 포스트에서는 지난 포스트에서 봤었던 F#예제와 같은 일을 하는 C#예제를 통해서 차이점을 좀 찾아보고자 합니다. ........늘 느끼는 거지만, 참 서두를 번지르르하게 써놓고 시작을 하네요.-_-



- C#코드부터 보자!

넵. 보여드리겠습니다. 보여드려야죠~. 다만, 아래의 코드는 제가 짠 코드일뿐 모범적인 코드라고는 볼 수 없다는 걸 생각하고 봐주시기 바랍니다.


class Demo
    {
        private string destDir;
        private List<STRING> finalFiles;

        public Demo(string destDir)
        {
            this.destDir = destDir;
            finalFiles = new List<STRING>();
        }

        private List<STRING> GetSthAll(Func<STRING, string[]> GetSth, string path)
        {
            return GetSth(path).ToList();
        }

        public void PrintAllFilesCount(string ext)
        {
            GetAllFiles(destDir);
            List<STRING[]> filesWithExt = finalFiles.Select(file => file.Split('.')).ToList();
            int countOfExt = filesWithExt.Where(file => (file.Length == 2) && (file[1] == ext)).Count();

            Console.WriteLine(countOfExt);
        }

        private void GetAllFiles(string destDir)
        {
            finalFiles.AddRange(GetSthAll(Directory.GetFiles, destDir));
            foreach (string currentDir in GetSthAll(Directory.GetDirectories, destDir))
            {
                GetAllFiles(currentDir);
            }
        }
    }

그리고 위 코드는 아래처럼 실행할 수 있습니다.


class Program
    {
        static void Main(string[] args)
        {
            Demo demo = new Demo(@"c:\ATI");
            demo.PrintAllFilesCount("txt");
        }
    }

위의 코드는 이전 포스트의 코드와 같은 결과를 내는 코드입니다. F#에서는 중간결과를 따로 저장하지 않고 |>를 통해서 바로바로 다음함수로 연결했었기 때문에 중간값의 상태저장에 대한 고려가 필요없었습니다. 그 때문에 제귀호출 과정의 데이터를 어디에 저장해야 할지 역시 고려하지 않아도 되었습니다. 즉, 데이터가 어디에서 어디로 가는지에 대한 데이터의 흐름이 추상화 되어 있었던 것이죠. 위의 코드를 보시면, 명령형언어에서 오는 한계점이 분명히 존재하지만, 특별히 복잡해보인다거나 코드가 길어졌다거나 하는 모습은 볼 수 없습니다. 왜그럴까요?

바로 LINQ때문입니다. 함수형언어의 장점을 녹인 LINQ덕분에 데이터추출과정이 추상화되어서 코드가 큰 차이없이 작성될 수 있었습니다. 최근 추세는 명령형언어와 함수형언어가 조금씩 융화되어 가는 것 같습니다. C#에도 점점 함수형언어의 장점과 동적언어의 장점이 녹아들어가고 있습니다. 물론 C#의 설계자인
Anders Hejlsberg는 static한 언어가 가지는 강점을 여전히 높게 평가하고 있습니다만, 필요하다면 점점 융화되어 가는거겠죠. 그럼, 어디한번 계급장떼고(?)...아니아니 LINQ떼고 한번 해보죠!


- 계급장떼고 붙여봐!
넵, 계급장테고 붙여보겠습니다. LINQ없이 C#2.0의 익명메서드 기능을 이용해서 동일한 코드를 작성해 보겠습니다.


class Demo2
    {
        private string destDir;
        private List<STRING> finalFiles;

        public Demo2(string destDir)
        {
            this.destDir = destDir;
            finalFiles = new List<STRING>();
        }

        private List<STRING> GetSthAll(Func<STRING, string[]> GetSth, string path)
        {
            return GetSth(path).ToList();
        }

        private List<STRING[]> GetSplitedList(List<STRING> data, Func<STRING,STRING[]> splitMethod)
        {
            List<STRING[]> splitedList = new List<STRING[]>();
            foreach (string str in data)
            {
                splitedList.Add(splitMethod(str));
            }
            return splitedList;
        }

        private int GetFilesCountWithExt(List<STRING[]> data, Func<STRING[], bool> filterMethod)
        {
            List<STRING[]> filteredList = new List<STRING[]>();
            foreach (string[] fileStr in data)
            {
                if (filterMethod(fileStr))
                {
                    filteredList.Add(fileStr);
                }
            }
            return filteredList.Count;
        }
        public void PrintAllFilesCount(string ext)
        {
            GetAllFiles(destDir);
            List<STRING[]> filesWithExt = GetSplitedList(finalFiles, delegate(string str) { return str.Split('.'); });
            int countOfExt = GetFilesCountWithExt(filesWithExt, delegate(string[] fileStr) { return (fileStr.Length == 2) && (fileStr[1] == ext); });
            Console.WriteLine(countOfExt);
        }

        private void GetAllFiles(string destDir)
        {
            finalFiles.AddRange(GetSthAll(Directory.GetFiles, destDir));
            foreach (string currentDir in GetSthAll(Directory.GetDirectories, destDir))
            {
                GetAllFiles(currentDir);
            }
        }
    }

어떻습니까? 이젠 차이가 좀 드러나기 시작합니다. 익명메서드를 이용해서 람다를 흉내냈지만, 데이터추출과정을 추상화하진못해서 그 추출과정의 중간값저장이나 추출루프를 직접 제어해줘야 합니다. 이렇게 되면, 문제가 조금씩 복잡해질수록 코딩도 어려워 지겠지요.(Func<>는 .NET 3.5에서 추가된거지만 봐주셈요-_-)

즉, 단순히 LINQ라는 기술을 쓰고 안쓰고정도의 문제가 아니라, 추상화 단계가 낮아지는 것이기 때문에 생각의 복잡도는 높아질 수 밖에 없는 것입니다. 데이터의 흐름을 추상화해주는 함수형언어의 특징이 이런부분에서 굉장한 강점으로 작용하게 됩니다.



- 할말다했냐?
네 별로 대단치않은 예제이지만, 최선을 다해서 한번 비교를 해봤습니다. 물론, 예제가 완전히 적절한건 아니고 크기나 복잡도 역시 높은 수준이 아니라 명확한 비교라고 볼 수는 없지만, 하나의 예제가 될 수 있다고 생각합니다. 이번 포스트에서 함수형언어인 F#의 장점을 못 느끼셨다면 모두 제 책임이나 따뜻한 질타를 주시기 바랍니다. 끝으로 Expert F#의 저자인 Don Syme의 블로그에 올라온 사례하나를 소개하고 마치겠습니다.

-포스트 원문-

The first application was parsing 110GB of log data spread over 11,000 text files in over 300 directories and importing it into a SQL database. The whole application is 90 lines long (including comments!) and finished the task of parsing the source files and importing the data in under 18 hours; that works out to a staggering 10,000 log lines processed per second! Note that I have not optimized the code at all but written the application in the most obvious way. I was truly astonished as I had planned at least a week of work for both coding and running the application.

Ralf Herbrich, Microsoft Research
(http://blogs.msdn.com/dsyme/archive/2006/04/01/566301.aspx)


-제 맘대로 해석-_-;;;-

첫번째 프로그램은 300개가 넘는 디렉토리에 있는 11,000개의 텍스트파일에 저장되어있는 100GB나 되는 로그 데이터를 가져와서 분석해서 SQL데이터베이스에 저장하는 거였습니다. 어플리케이션은 전체 길이가 주석을 포함해서 딱 90줄이었고, 로그파일들을 읽어들여 분석하고 저장하는데 18시간이 채 안걸렸습니다. 1초에 1만줄의 로그를 처리한 셈이죠! 더 중요한건 코드를 최적하려고 하지도 않았고 그냥 제일 명확하게 보이도록 짰습니다. 제가 진짜 놀랐던건, 솔직히 전 그 프로그램 짜고 돌리는데 일주일은 걸릴 줄 알았었거든요.



- 참고자료

1. Expert F#, Don Syme, Adam Granicz, Antonio Cisternino, APRESS

2. http://blogs.msdn.com/dsyme/archive/2006/04/01/566301.aspx


 

WPF Features Preview (3) - Styling the DataGrid

WPF 2009. 4. 23. 03:55 Posted by 알 수 없는 사용자
WPF Features Preview (1) - DataGrid
WPF Features Preview (2) - DatePicker 

맨 처음 초기 어플리케이션을 보면 스타일이 적용되어 멋지게 보입니다. DataGrid를 적용하여 수정된 버전은 좀 더 기능적이긴 하지만 밋밋하고 보기엔 좀...
이번에는 DataGrid에 스타일을 주어서 컬러풀하고 비주얼적으로 멋지게 만들어 보도록 하겠습니다.

프로젝트를 열고 MainWindow.xaml 파일을 열어서 DockPanel 부분을 보면 뭔가 스타일이 적용된것을 볼 수 있습니다. 이 중에서 몇가지 brushGridViewColumnHeaderListViewItem 스타일을 DataGrid 스타일에 적용해 보도록 하겠습니다.

DataGrid의 스타일 프로퍼티
1. CellStyle - 각각의 cell에 사용되는 스타일 (DataGridCell)
2. RowStyle - row에 사용되는 스타일 (DataGridRow)
3. ColumnHeaderStyle - header bar에 사용되는 스타일 (DataGridColumnHeader)

사용자가 grid와 상호작용하는 것에 대한 프로퍼티
1. SelectionMode - 단일선택과 확장된 선택에 대한 모드
2. SelectionUnit - 무엇이 선택되는가에 대한 설정 (FullRow vs Cell vs CellOrRowHeader)
3. GridLinesVisibility - cell 주변의 라인에 대한 속성
4. VerticalGridLinesBrush - 수직 라인에 대한 색상
5. HorizontalGridLinesBrush - 수평 라인에 대한 색상

먼저 TargetType으로 스타일이 적용될 타입에 대해 설정해줍니다.
TargetType 값에 GridViewColumnHeaderdg:DataGridColumnHeader로 바꿔줍니다.
그리고 ListViewItemdg:DataGridRow로 바꿔줍니다.
변경된 xaml 코드는 아래와 같습니다.

   <Style x:Key="dgHeaderStyle" TargetType="dg:DataGridColumnHeader">

      <Setter Property="Background" Value="{StaticResource dgHeaderBrush}" />

      <Setter Property="Foreground" Value="White" />

      <Setter Property="BorderBrush" Value="{StaticResource dgHeaderBorderBrush}" />

   </Style>

   <Style x:Key="dgRowStyle" TargetType="dg:DataGridRow">

      <Setter Property="SnapsToDevicePixels" Value="True" />

      <Setter Property="Background" Value="White" />

      <Style.Triggers>

         <Trigger Property="ItemsControl.AlternationIndex" Value="1">

            <Setter Property="Background" Value="#FFD0D0E0" />

         </Trigger>

         <Trigger Property="IsSelected" Value="True">

            <Setter Property="Background" Value="LightGoldenrodYellow" />

         </Trigger>

      </Style.Triggers>

   </Style>

DataGrid가 정의된 곳에는 ColumnHeaderStyleRowStyle 속성을 설정해줍니다.

        <dg:DataGrid x:Name="dg" ItemsSource="{Binding}" Margin="10"

                  AutoGenerateColumns="False"

                  Background="#80909090" AlternationCount="2"

                  ColumnHeaderStyle="{StaticResource dgHeaderStyle}"

                  RowStyle="{StaticResource dgRowStyle}">

이제 어플리케이션을 실행해보면 스타일이 적용되어 이전과 다른 모습을 볼 수 있습니다.

이제 몇가지 grid에 대한 속성을 주도록 하겠습니다.
1. Extended 선택 모드 적용
2. SelecionUnitFullRow로 설정
3. GridLinesVisibilityAll로 설정
4. VerticalGridLinesBrushDarkGray로 설정

        <dg:DataGrid x:Name="dg" ItemsSource="{Binding}" Margin="10"

                  AutoGenerateColumns="False"

                  Background="#80909090" AlternationCount="2"

                  ColumnHeaderStyle="{StaticResource dgHeaderStyle}"

                  RowStyle="{StaticResource dgRowStyle}"

                  SelectionMode="Extended"

                  SelectionUnit="FullRow"

                  GridLinesVisibility="All"

                  VerticalGridLinesBrush="DarkGray">

또 몇가지 설정을 해서 header 스타일을 주도록 해보겠습니다.
1. BorderTickness1로 주어서 border를 볼 수 있도록
2. pixel snapping을 활성화 (SnapsToDevicePixels="True")
3. 컨텐트 horizontal 정렬 (HorizontalContentAlignment="Center")
4. MinWidth="0" MinHeight="30" 으로 설정
5. 기본 커서 모양을 Hand로 설정

  <Style x:Key="dgHeaderStyle" TargetType="dg:DataGridColumnHeader">

     <Setter Property="Background" Value="{StaticResource dgHeaderBrush}" />

     <Setter Property="Foreground" Value="White" />

     <Setter Property="BorderBrush" Value="{StaticResource dgHeaderBorderBrush}" />

     <Setter Property="BorderThickness" Value="1" />

     <Setter Property="SnapsToDevicePixels" Value="True" />

     <Setter Property="HorizontalContentAlignment" Value="Center" />

     <Setter Property="MinWidth" Value="0" />

     <Setter Property="MinHeight" Value="30" />

     <Setter Property="Cursor" Value="Hand" />

  </Style>

다시 컴파일해서 실행해보면 좀 더 직관적으로 보기 쉽게 스타일 적용이 된 것을 볼 수 있습니다.


다음으로는 Cell 자체에 대한 스타일 적용을 해보겠습니다. Cell의 컨텐트가 가운데로 오고 필요할 때 포커스가 적용되면 좀 더 편리할 것 같은데 이것을 해보도록 하겠습니다.



새로운 스타일 속성을 DockPanel.Resources에 추가하도록 합니다.
1. TargetTypedg:DataGridCell로 설정
2. 중요한 속성은 기본 DataGridCell을 따르기 위해 BasedOn 프로퍼티 설정
   "{StaticResource{x:Type dg:DataGridCell}}"

3. SnapsToDevicePixels"True"로 설정
4. VerticalAlignment"Center"로 설정
5. Trigger 컬렉션 추가 - 셀이 선택되었을 때 background/foreground 색상 변경
6. <Style.Triggers> 부분에 IsSelected 프로퍼티를 True로 설정하고
    a. BackgroundTransparent
    b. BorderBrushTransparent
    c. ForegroundBlack으로
7. 두번째 trigger를 추가하고 IsKeyboardFocusWithin="True" 프로퍼티에
    a. Background"{StaticResource whiteBackBrush}" 리소스
    b. BorderBrush"{DynamicResource{x:Static dg:DataGrid.FocusBorderBrushKey}}" 리소스
    c. ForegroundBlack

    <Style x:Key="dgCellStyle" TargetType="dg:DataGridCell"

           BasedOn="{StaticResource {x:Type dg:DataGridCell}}">

       <Setter Property="SnapsToDevicePixels" Value="True" />

       <Setter Property="VerticalAlignment" Value="Center" />

       <Style.Triggers>

          <Trigger Property="IsSelected" Value="True">

             <Setter Property="Background" Value="Transparent" />

             <Setter Property="BorderBrush" Value="Transparent" />

             <Setter Property="Foreground" Value="Black" />

          </Trigger>

          <Trigger Property="IsKeyboardFocusWithin" Value="True">

             <Setter Property="Background" Value="{StaticResource whiteBackBrush}" />

             <Setter Property="BorderBrush"

                  Value="{DynamicResource {x:Static dg:DataGrid.FocusBorderBrushKey}}" />

             <Setter Property="Foreground" Value="Black" />

          </Trigger>

       </Style.Triggers>

    </Style>

그리고 DataGrid 속성에 CellStyle 프로퍼티값을 줍니다.

        <dg:DataGrid x:Name="dg" ItemsSource="{Binding}" Margin="10"

                  AutoGenerateColumns="False"

                  Background="#80909090" AlternationCount="2"

                  ColumnHeaderStyle="{StaticResource dgHeaderStyle}"

                  RowStyle="{StaticResource dgRowStyle}"

                  CellStyle="{StaticResource dgCellStyle}"

                  SelectionMode="Extended"

                  SelectionUnit="FullRow"

                  GridLinesVisibility="All"

                  VerticalGridLinesBrush="DarkGray">

이제 Cell을 클릭해보면 색이 적용되어 하이라이트 되는것을 볼 수 있습니다.

마지막으로 RowDetail 속성을 설정하도록 하겠는데 위에서 했던 방법과 비슷하니 자세한 설명은 생략하겠습니다. xaml 코드를 보면 어렵지 않게 이해 할 수 있을 것입니다.

    <dg:DataGrid x:Name="dg" ItemsSource="{Binding}" Margin="10" ...

        VerticalGridLinesBrush="DarkGray"

        RowDetailsVisibilityMode="VisibleWhenSelected">

 

            <dg:DataGrid.RowDetailsTemplate>

                <DataTemplate>

                    <StackPanel Orientation="Horizontal" Margin="20,0,0,0">

                        <TextBlock />

                        <TextBox />

                    </StackPanel>

                </DataTemplate>

            </dg:DataGrid.RowDetailsTemplate>

TextBlock 속성

       <StackPanel Orientation="Horizontal" Margin="20,0,0,0">

          <TextBlock Text="Category:" VerticalAlignment="Center" FontWeight="Bold" />

          <TextBox />

TextBox 생성

      <dg:DataGrid.RowDetailsTemplate>

         <DataTemplate>

            <StackPanel Orientation="Horizontal" Margin="20,0,0,0">

               <TextBlock Text="Category:" VerticalAlignment="Center" FontWeight="Bold" />

               <TextBox Text="{Binding Category}" Margin="10,5" MinWidth="100">

                  <TextBox.Style>

                     <Style TargetType="TextBox">

                        <Setter Property="BorderBrush" Value="{x:Null}" />

                        <Setter Property="Background" Value="{x:Null}" />

                        <Style.Triggers>

                           <Trigger Property="IsFocused" Value="True">

                              <Setter Property="BorderBrush"

                                      Value="{x:Static SystemColors.WindowFrameBrush}" />

                              <Setter Property="Background"

                                      Value="{x:Static SystemColors.WindowBrush}" />

                           </Trigger>

                           <Trigger Property="IsMouseOver" Value="True">

                              <Setter Property="BorderBrush"

                                      Value="{x:Static SystemColors.WindowFrameBrush}" />

                              <Setter Property="Background"

                                      Value="{x:Static SystemColors.WindowBrush}" />

                           </Trigger>

                        </Style.Triggers>

                     </Style>

                  </TextBox.Style>

               </TextBox>

            </StackPanel>

         </DataTemplate>

            </dg:DataGrid.RowDetailsTemplate>

이제 모든 스타일 적용이 끝났습니다. 실행을 해보면 위에서 적용한 스타일이 적용되어 처음의 DataGrid보다 훨씬 보기도 좋고 스타일이 좋아진것을 알 수 있습니다.



지금까지 DataGrid에 대해 알아보았는데 다음에는 역시 새롭게 추가된 Ribbon 컨트롤에 대해 알아보겠습니다.
WPF에서도 기존의 메뉴와 툴바를 벗어나서 새로운 Ribbon 스타일을 적용할 수 있는데 이것 역시 어렵지 않게 할 수 있으니 기대해주세요.

'WPF' 카테고리의 다른 글

WPF 리본 컨트롤 RTW 출시  (4) 2010.08.05
WPF Features Preview (2) – DatePicker  (1) 2009.04.22
WPF Features Preview (1) – DataGrid  (1) 2009.04.17
WPF 4의 향상된 기능과 Windows 7 지원  (0) 2009.04.09

WPF Features Preview (2) – DatePicker

WPF 2009. 4. 22. 16:50 Posted by 알 수 없는 사용자

WPF Features Preview (1) - DataGrid

저번 시간에는 WPFToolkit을 이용해서 ListView를 DataGrid로 바꿔보았습니다. 훨씬 간편하고 쉽게 데이터를 보여줄 수 있다는것을 알았는데요 이번에는 WPFToolkit에 포함된
(WPF 4.0에는 기본으로 포함) 다른 컨트롤들을 이용해서 좀 더 DataGrid를 강화해 보겠습니다.

XAML 파일을 열고 DataGrid 아래에 보면 DataGridTextColumn 부분에 날짜가 표현되는 것을 볼 수 있습니다. 이 부분을 헤더는 그대로 두고 MinWidth 속성에 100으로 고정값을 주도록 하겠습니다.
그리고 template column에서 CellEditingTemplate을 생성합니다.

이제부터 중요한 작업을 하겠는데 기존에 그냥 날짜가 표시되는것을 DatePicker 컨트롤을 이용해서 표현해보겠습니다.
DataTemplate을 생성 한 후 WPFToolkitDatePicker 컨트롤을 추가해줍니다. DataGrid와 같은 네임스페이스에 있으니 따로 추가적인 작업은 필요하지 않습니다.
SelectedDate 속성에서 Date 프로퍼티를 바인딩해주고 SelectedDateFormat 프로퍼티에서 Short 값을 주어 포맷을 정합니다.
CellTemplate 프로퍼티에서는 TextBlock을 이용해서 Date 프로퍼티를 바인딩해주고 StringFormat에 d 값을 주어서 DateTime 포맷으로 해줍니다.
이렇게 해서 수정된 XAML은 아래와 같습니다.

  <!--<dg:DataGridTextColumn Header="Date" Binding="{Binding Date, StringFormat=d}" />-->

  <dg:DataGridTemplateColumn Header="Date" MinWidth="100">

     <dg:DataGridTemplateColumn.CellEditingTemplate>

        <DataTemplate>

           <dg:DatePicker SelectedDate="{Binding Date}" SelectedDateFormat="Short" />

        </DataTemplate>

     </dg:DataGridTemplateColumn.CellEditingTemplate>

     <dg:DataGridTemplateColumn.CellTemplate>

        <DataTemplate>

           <TextBlock Text="{Binding Date, StringFormat=d}" />

        </DataTemplate>

     </dg:DataGridTemplateColumn.CellTemplate>

  </dg:DataGridTemplateColumn>

이제 어플리케이션을 실행해보면 새로운 DatePicker 컨트롤이 나오는것을 볼 수 있고 달력을 클릭해서 값을 수정할 수 있는 모드가 나옵니다.

간단하게 새로운 컨트롤을 이용해서 좀 더 직관적인 데이터 표현을 할 수 있는것을 보았습니다. 컨트롤 사용법과 데이터 바인딩 방법만 익히면 다른 부분에도 쉽게 적용할 수 있을것입니다.
다음에는 DataGridStyle 속성을 이용하여 기본적인 DataGrid를 좀 더 보기 좋게 만드는 방법을 알아보도록 하겠습니다.


Welcome to F#(5) - 아주 조금씩 심화되는 탐색전.

F# 2009. 4. 20. 08:50 Posted by 알 수 없는 사용자

- 벌써 다섯번째네?


네, 맞습니다;; 벌써 다섯번째 포스트입니다. 그런데 영~ 포스트는 나아질 기미를 보이진 않는군요. 모든게 제 잘못입니다;; 계속 노력중이니 좋게 봐주시길 부탁드립니다.^^ 오늘은 지난번에 했던 거 보다 조금 더 복잡한 코드를 가지고 이야기를 해보려고 합니다. 그리고 그 코드를 바탕으로 C#과의 비교를 통해서 좀 더 의미있는 것들을 이야기 해보고 싶습니다. 읽어보시고 의미가 없다면 따뜻한 피드백으로 질타를...;;

 

- 알았으니까 예제부터 보자!

open System.IO

let rec getAllFiles dir =
    List.append 
      (dir |> Directory.GetFiles |> Seq.to_list) 
      (dir |> Directory.GetDirectories |> Seq.map getAllFiles |> Seq.concat |> Seq.to_list)
 
//여기서는 그렇게 가져온 파일리스트를 '.'을 기준으로 짤라낸다.
let splitedAllFiles = getAllFiles @"C:\ATI" |> List.map (String.split ['.'])
 
//여기서는 각 파일을 돌면서, 확장자가 사용자가 원하는 확장자인거만 걸러내고, 개수를 리턴한다.
let Count ext = splitedAllFiles |> List.filter (fun file -> if file.Length = 2 then file.[1] = ext else false) |> List.length

 

그리고 위의 예제를 F#인터렉티브에서 확인해본 결과입니다.(PowerPack.dll을 로딩하는 부분빼곤 전부 Alt+Enter를 이용했습니다.)


 > #r "FSharp.PowerPack.dll";;

--> Referenced 'C:\Program Files (x86)\FSharp-1.9.6.2\bin\FSharp.PowerPack.dll'

>

 

val getAllFiles : string -> string list
val splitedAllFiles : string list list
val Count : string -> int


> Count "txt";;
val it : int = 129
>

 

위 코드는 명시된 디렉토리를 모두 훑어서 거기에 있는 파일들의 경로를 가져오고, 그 파일들 중에 사용자가 명시한 확장자를 가진 파일이 몇개나 되는지를 출력하는 코드로 Expert F#에 나온코드를 조금 수정하고 덧붙인 것입니다. 일단 이 예제를 가지고 C#과의 비교를 통해서, 함수형언어로서의 F#이 가지는 장점에 대해 조금알아보고자 합니다. 그럼 우선 위의 코드에 대해서 알아보겠습니다.

 

- 첫번째는?

우선, 위의 코드에서는 두가지의 자료구조가 사용되었는데요, Seq과 List가 그것입니다. Seq는 C#에서의 IEnumerable<>타입을 의미하구요, List는 List<>을 의미합니다. 특성도 비슷합니다. 조금 더 익숙하실 C#을 통해 먼저 알아보죠. LINQ에서 보면, 쿼리를 날렸을때,  IEnumerable<>타입이 리턴되는데요, 이 IEnumerable<>로 리턴된 시퀀스는  아직 데이터를 가지고 있는게 아닙니다. 실제로 IEnumerable<>의 각요소에 접근해야 할때가 되서야 LINQ의 쿼리가 실행됩니다. 예제코드를 먼저 보시져~!

 

class Program
    {
        static void Main(string[] args)
        {
            int[] list1 = new int[] { 98, 34, 5, 134, 64, 57, 99, 320, 21, 55, 62, 39 };
            IEnumerable<int> queriedList1 = list1.Where(num => num <= 100);

            foreach (int num in queriedList1)
            {
                Console.WriteLine(num);
            }

            list1[0] = 198; //98 -> 198
            list1[1] = 134; //34 -> 134

            Console.WriteLine("---------------second call-------------");

            foreach (int num in queriedList1)
            {
                Console.WriteLine(num);
            }
        }
    }

 

위코드를 보시면, int형 배열에 대해서 100보다 작은 수만 골라서 쿼리를 날려서 IEnumerable<int>형으로 리턴을 받습니다. 그리고 처음에 모든 수를 돌면서 출력을 하고, 그 다음에 쿼리의 대상이었던 배열의 값중에 처음 두개를 100보다 크게 바꾼뒤에 다시 시퀀스돌면서 숫자를 출력하고 있습니다. 여기서 주목하실건, 쿼리를 두번 날린게 아니라 그저  IEnumerable<int>시퀀스 안의 요소를 한번더 돌면서 출력한 것 뿐이라는 겁니다. 결과를 보시죠.

 

 

위의 결과를 보시면, 두번째 foreach에서는 바꿔준 값들이 나오지 않는 걸 알 수 있습니다. 즉, 쿼리는 만들어지긴 하되 그 실행시기는 직접 요청될때로 연기(Deferred)된 것입니다. 이런 IEnumerable의 특성으로 인해, 변경된 내용을 가져오는데 쿼리를 두번날릴 필요가 없이 그저 전체요소를 한번더 돌기만 하면 자연스럽게 변경된 내용을 읽어오는게 되는 것입니다. 이와는 반대로 List는 즉시 모든 값을 읽어옵니다. 그래서 위의 코드에서,

 

IEnumerable<int> queriedList1 = list1.Where(num => num <= 100);

위코드를 아래와 같이 수정하고,

List<int> queriedList1 = list1.Where(num => num <= 100).ToList();


다시 예제를 돌려보면, 아래와 같은 결과가 나오는 걸 확인할 수 있습니다.

 

 

즉, 이미 수행시점에서 모든 요소를 미리(Immediately)가져 왔기 때문에, 원본값이 수정된 뒤에서 쿼리로 가져온 값에는 변화가 없는 것이죠. 이런 특성은 F#에서도 여전히 유효합니다.

 

> seq { 1 .. 100000000 };;
val it : seq<int> = seq [1; 2; 3; 4; ...] 
 

F# 인터렉티브에서는 위와같은 결과가 나옵니다. 즉, 1부터 1억까지의 숫자의 시퀀스를 선언하면, 모든 숫자가 다 즉시 만들어지는게 아니라는 거죠. 결과에서 보듯이 1부터 4까지만 나온걸 보실 수 있습니다. 즉, F#인터렉티브가 네번째 요소까지만 미리 읽어오도록 하고 있는거지요. 그래서 주의 하셔야 할점도 있습니다. List는 미리다 읽어오므로 메모리에 다 올라갈 수 있을지를 미리 생각해봐야 하고, Seq는 미리 다 읽어오는게 아니므로 읽어오기 전에 데이터변경이 되면 다른 결과가 나올 수 있습니다. 이 부분을 유념해야 합니다.

 

- 두번째는?


그럼 맨 처음에 파일들을 읽어오는 코드를 계속 보기로 하죠. 위에서 "|>"라는 기호가 보이는데 이 건 뭘까요? 뭐 리눅스같은데서 shell을 좀 다뤄보셨거나 하면 pipe겠구나 하는건 금방 아실 수 있겠죠.^^;; 이건 정방향 파이프 연산자(forward pipe operator)라고 하는데요, |> 앞쪽의 리턴값을 |> 뒤쪽의 메서드에 넘겨주는 역할을 합니다. 즉 위의 예제를 보자면, Directory.GetFiles의 인자로 dir를 넘겨주고, 그 결과를 Seq.to_list 의 인자로 넘겨주는 거죠. 굉장히 심플하게 중간 결과를 어디에 담아둘지 고민하지 않고 함수들을 연결할 수 있게 도와주는 연산자입니다.

아, 그리고 여기서 주목하실 부분이 있다면, 닷넷프레임워크의 메서드인 Directory.GetFiles나 Directory.GetDirectories를 F#에서도 First-class function으로 사용할 수 있습니다. 이런 부분이 F#의 진정한 강점이 아닌가 싶네요. 그리고 기존의 C#이나 VB.NET을 이용하시던 분들이 좀더 자연스럽게 F#이용해서 효율적인 함수형 프로그램을 작성할 수 있게 해주는 원동력이기도 하구요.


- First-class function?


Fisrt-class function이란 함수형언어의 특징중 하나로서, 함수가 다른 함수의 파라미터로 들어갈 수 있고, 연산결과로 리턴될 수 있으며 명령형언어에서는 추가적으로 변수에 대입가능한(자바스크립트를 떠올리시면 됩니다.) 함수라는 걸 의미합니다. Higher-order function은 함수를 인자로 받거나 함수를 결과로 리턴하는 함수를 의미하구요.

 

그 다음줄도 역시 Directory.GetDiretories에 dir을 넘겨줘서 현재 디렉토리의 하위 디렉토리들의 시퀄스를 가져오고 그걸 Seq.map에 넘겨줘서 각 디렉토리별로 getAllFiles를 실행해서 모든 하위 디렉토리의 파일경로를 가져오도록 합니다. 그리고 그 결과를 Seq.concat을 통해서 하나의 시퀀스로 합치구요, 그 다음에 그 시퀀스를 list로 변환합니다. 그리고 모든 결과를 하나의 list로 합쳐서 리턴합니다.

 

그리고 splitedAllFiles에서는 그렇게 만든 리스트의 모든 요소를 대상으로 '.'을 기준으로 파일경로와 확장자로 분리합니다. 그리고 마지막 Count함수에서는 그 결과를 List.filter함수에게 넘겨줘서 확장자가 사용자가 명시한 확장자와 일치하는 것만 추려서 하나의 리스트로 만든뒤, 그 리스트의 길이를 리턴합니다. 이렇게 해서 해당 디렉토리와 그 하위 디렉토리에서 특정 확장자를 가지는 파일의 개수를 읽어오는 프로그램이 완성되는 것입니다.

 

 

- C#과 비교한대매?


이번 포스트에서는 F#의 예제코드를 통해서 F#의 Seq, List의 특성을 알아봤고, |>연산자를 통해서 함수들을 쉽게 연결하는 것을 보았습니다. 다음 포스트에서는 이 F#의 코드와 동일한 C#코드를 통해서 비교를 조금 해보고자 합니다. 글의 내용이 여전히 만족스럽진 않지만, 저도 부족한 머리 박아가면서 쓰는 글이니 좀 봐주시구요;; 따뜻한 피드백 부탁드립니다.

 

- 참고자료

1. Expert F#, Don Syme, Adam Granicz, Antonio Cisternino, APRESS

2. Pro LINQ: Language Integrated Query in C# 2008, Joseph C. Rattz, Jr. , APRESS.

3. Programming Language Pragmatics 2nd Edition, Michael L. Scott, Morgan Kaufmann Publishers


[MEF] 9. Recomposition

Managed Extensibility Framework 2009. 4. 19. 22:47 Posted by POWERUMC

Recomposition

 

이전 포스트의 MEF 의 특징 중에 MEF 의 플러그인 모델(Plugin Model) 은 교체가 용이하다고 하였습니다. Composable Part 는 구성 요소로써 고유의 기능을 구현합니다. 그리고 MEF 는 각각의 Composable Part 를 조립하여 다양한 컴포넌트 또는 애플리케이션을 완성합니다.

 

어떠한 경우에는 특정한 구성 구성요소를 사용하다가 그것이 필요 없어질 경우 구성 요소를 언로드(Unload) 하거나 다른 구성요소로 교체할 필요가 있습니다.

 

MEF 의 이러한 유연함의 예를 들어보죠.

 

예를 들어, 어플리케이션의 로그(Log) 기능을 생각해볼 수 있습니다. 만약 어플리케이션이 동작하는 환경이 인터넷에 연결되지 않는 환경이라면 사용자의 컴퓨터 로컬에 로그를 기록하다가, 인터넷에 연결될 경우 외부 데이터베이스로 로그를 기록하는 시나리오를 가정할 수 있습니다. 그리고 이러한 기능 또는 요구 사항이 언제 변경될지도 모르는 일입니다.

 

일단 위의 시나리오를 구현하기 여러 가지 고려해야 할 사항이 있고, 기능 또는 요구 사항이 변경될 경우도 고려해야 합니다. 결국, MEF 는 이러한 변화에 굉장히 유연하게 대처할 수 있습니다.

 

MEF 에서 ImportAttribute AllowRecomposition 프로퍼티를 통해 구성 요소를 런타임(Runtime) 시 동적(Dynamic) 으로 교체할 수 있도록 합니다.

 

아래는 위의 예로 든 시나리오를 구현하기 위해 작성한 간단한 소스 코드 입니다.

 

ILogger 인터페이스

public interface ILogger

{

        void Write();

}

 

ILogger 인터페이스는 Write 메서드를 통해 로그를 기록할 수 있는 예입니다.

 

 

TextLogger 클래스

[Export(typeof(ILogger))]

public class TextLogger : ILogger

{

        public void Write()

        {

               Console.WriteLine("Logged TextLogger");

        }

}

 

 

DatabaseLogger 클래스

[Export(typeof(ILogger))]

public class DatabaseLogger : ILogger

{

        public void Write()

        {

               Console.WriteLine("Logged DatabaseLogger");

        }

}

 

 

LoggerContext 클래스

[Export]

public class LoggerContext

{

        [Import(AllowRecomposition=true)]

        public ILogger Context { get; set; }

 

        [ImportingConstructor]

        public LoggerContext(ILogger sender)

        {

        }

}

 

여기에서 ImportAttribute AllowRecomposition 프로퍼티의 값을 true 로 지정해 주었습니다. AllowRecomposition 프로퍼티가 true 일 경우 Imported 된 구성 요소를 동적(Dynamic)하게 교체할 수 있도록 합니다.

 

 

Main 어플리케이션

class Program

{

        static void Main(string[] args)

        {

               Program p = new Program();

               p.Run();

        }

 

        void Run()

        {

               var catalog    = new AggregateCatalog(new TypeCatalog(typeof(LoggerContext)));

 

               var container = new CompositionContainer(catalog);

                var batch = new CompositionBatch();

               var defaultPart = batch.AddPart(new TextLogger());

               container.Compose(batch);

 

               var obj = container.GetExportedObject<LoggerContext>();

 

               obj.Context.Write();

              

               batch = new CompositionBatch();

               batch.RemovePart(defaultPart);

               batch.AddPart(new DatabaseLogger());

               container.Compose(batch);

 

               obj.Context.Write();

        }

}

 

이 코드는 처음에 TextLogger 를 사용하다가, 필요 없어진 TextLogger 를 제거하고 DatabaseLogger 로 교체하는 코드입니다.

 

아래는 위의 소스 코드를 실행한 결과입니다.

 

[그림1] 소스 코드 실행 결과

 

 

Wow! Recomposition

 

사실 이러한 것이 기존에는 기능의 정의 또는 요구 사항에 따라 직접 구현할 수 있었지만, MEF 의 특징인 플러그인 모델(Plugin Model) 은 이러한 고민에 대해 좋은 방법을 제공해 줍니다. 개발자는 자신이 구현해야 할 기능에 더 충실하고, 비즈니스 로직에 대한 고민만을 하면 됩니다. 그리고 정책에 따라 그것을 집행하는 결정권을 가진 자는 구현된 구성 요소를 조립하고 교체하여 기존의 정적인(Static) 어플리케이션에게 동적인(Dynamic) 유연함을 제공합니다.

 

기존에 정적인 어플리케이션은 변화에 따라 유지 보수를 위해 지속적인 많은 리소스가 필요하였지만, 플러그인 모델(Plugin Model) 의 동적인 어플리케이션은 그러한 변화에 능동적으로 대처할 수 있는 큰 기쁨을 줄 수 있을 것입니다.

'Managed Extensibility Framework' 카테고리의 다른 글

MEF Preview 6 공개  (0) 2009.07.20
[MEF] 10. Querying the CompositionContainer  (0) 2009.05.18
[MEF] 8. Strongly Typed Metadata  (0) 2009.04.16
[MEF] 7. Exports and Metadata  (0) 2009.04.16
[MEF] 6. Lazy Exports  (0) 2009.04.13

지난 번 포스팅에서 말씀 드렸던 것처럼 Visual Studio Team System 2010에서 이루어진 Code Analysis 기능 개선에 대해서 더 깊게 다뤄 보겠습니다. 그 첫 번째로 새롭게 추가된 Rule Sets 개념에 대해서 알아보도록 하겠습니다.


Visual Studio 2005, 2008에서의 코드 분석 설정

Visual Studio의 코드 분석 시스템에는 약 200개 정도의 규칙이 있습니다. 하지만, 사실 이 규칙들을 모두 준수를 해야 하는 것은 아닙니다. 모든 규칙이 나름의 이유와 정당성을 갖고 있다고 해도, 프로젝트의 성격에 따라, 모듈의 성격에 따라서 꼭 지켜야 할 규칙, 그렇지 않은 규칙, 심지어는 어쩔 수 없이 위반해야 하는 규칙도 있을 수 있습니다.

그래서 코드 리뷰를 할 때, 대부분의 경우 모든 규칙을 다 검사하지는 않습니다. 각각 케이스 바이 케이스로 규칙을 적절하게 조절하게 됩니다.

Visual Studio Team System 2005와 2008 버전에서는, 이런 설정을 Visual Studio Project Configuration에서 각 프로젝트 별로 할 수가 있습니다.

이 규칙 설정은 프로젝트 파일(.vbproj 혹은 .csproj)에 CodeAnalysisRules라는 Property 이름으로 아래와 같이 저장되게 됩니다. 내용을 보면 아시겠지만, “-“ 기호와 규칙 ID를 붙여 놓은 모양으로 되어있습니다. 즉, 거기에 열거된 규칙 ID를 Analysis Rules에서 빼는 형태로 저장이 되는 것입니다. 아래 샘플은 그래서 이 프로젝트는 코드 리뷰를 할 때에 Design Rule의 CA1000번 규칙을 검사하지 않는다라는 의미가 됩니다.

<CodeAnalysisRules>-Microsoft.Design#CA1000</CodeAnalysisRules>

이런 형태로 Visual Studio 프로젝트마다 다른 규칙을 적용할 수 있다는 것은 대단히 유연한 디자인이긴 하지만, 많은 수의 Visual Studio Project가 존재한다면, 관리가 참으로 어렵습니다. 그래서 예전에 Visual Studio Team System 2005 를 사용했던 프로젝트에서 전체 Visual Studio 프로젝트의 분석 규칙들을 통일하기 위해서, 개별 개발자들에게 위임하지 않고 제가 직접 모든 프로젝트를 텍스트 에디터로 열어서 저 부분만 Copy & Paste로 넣어서 Check-In하는 엄청난 단순 반복 작업을 한 적도 있습니다.

 

Visual Studio Team System 2010의 Rule Sets Feature

그래서 새롭게 출시될 Visual Studio Team System 2010에서는 Rule Sets라는 개념이 도입되었습니다. 이 Rule Sets 기능은 이름에서도 알 수 있듯이 Rule들을 미리 정해진 Set으로 관리할 수 있는 기능입니다. 이제 Visual Studio 프로젝트 별로 Rule을 하나 하나 빼는 수고를 할 필요가 없이 Visual Studio 프로젝트에 Rule Set을 지정하는 것으로 Code analysis를 커스터마이징할 수 있게 된 것입니다. 아래 그림에서 볼 수 있듯이, 하나 이상의 Rule Set을 선택하는 것도 가능합니다. (가장 아래의 Choose Multiple Rule sets를 선택하면 됩니다.)


Visual Studio Solution 전체에 Rule Set을 적용하는 것도 가능합니다. Solution Property에서 아래와 같이 설정할 수 있습니다.

 

위 그림에서 보시는 것처럼 마이크로소프트는 기본적으로 7개의 기본 Rule Set을 제공합니다. 이 기본 Rule Set의 목록은 다음과 같습니다.

  • Microsoft All Rules – 전체 Rule이 모두 포함된 Rule Set입니다.
  • Microsoft Basic Correctness Rules – 로직 에러와 자주 저지르는 실수를 예방하는 규칙들로 이루어진 Rule Set입니다.
  • Microsoft Basic Design Guideline Rules – Framework API 작성 상의 Best Practice에 집중된 Rule Set입니다.
  • Microsoft Extended Correctness Rules – Basic Correctness Rules를 확장하여, COM Interop이나 Mobile Application에도 사용 가능하도록 만들어진 Rule Set입니다.
  • Microsoft Extended Design Guideline Rules – Basic Design Guideline Rules를 확장해서, 사용성이나 유지 보수성도 체크할 수 있도록 만들어진 Rule Set입니다.
  • Microsoft Globalization Rules – Application의 Globalization에 있어서 문제가 있는지 검증하는데 집중된 Rule Set입니다.
  • Microsoft Minimum Recommended Rules - MS가 제안하는 최소한의 Rule Set입니다. 50개 정도의 규칙으로 이루어져 있습니다. 다른 대부분의 Rule Set들이 이 Rule Set을 포함하고 있습니다.
  • Microsoft Security Rules – 이름 그대로 보안에 집중된 Rule Set으로, 모든 Security 관련 규칙들이 모두 포함되어 있습니다.

 

물론 이 외에도 사용자가 직접 Rule Set을 편집할 수 있습니다. Rule Set은 .ruleset이라는 확장자를 가지는 XML 파일 포맷으로 되어 있습니다. 기본적으로 Visual Studio 2010에서 아래와 같은 UI를 통해서 편집할 수 있도록 되어 있습니다.


 

아래는 Rule Set 파일을 직접 Notepad로 열어본 것입니다.

<?xml version="1.0" encoding="utf-8"?>
<RuleSet Name="Microsoft Basic Correctness Rules" Description="These rules focus on logic errors and common mistakes made in the usage of framework APIs. Include this rule set to expand on the list of warnings reported by the minimum recommended rules." ToolsVersion="10.0">
  <Localization ResourceAssembly="Microsoft.VisualStudio.CodeAnalysis.RuleSets.Strings.dll" ResourceBaseName="Microsoft.VisualStudio.CodeAnalysis.RuleSets.Strings.Localized">
    <Name Resource="BasicCorrectnessRules_Name" />
    <Description Resource="BasicCorrectnessRules_Description" />
  </Localization>
  <Include Path="minimumrecommendedrules.ruleset" Action="Default" />
  <Rules AnalyzerId="Microsoft.Analyzers.ManagedCodeAnalysis" RuleNamespace="Microsoft.Rules.Managed">
    <Rule Id="CA1008" Action="Warn" />
    <Rule Id="CA1013" Action="Warn" />
    <Rule Id="CA1303" Action="Warn" />
    <Rule Id="CA1308" Action="Warn" />
    <Rule Id="CA1806" Action="Warn" />
    <Rule Id="CA1816" Action="Warn" />
    <Rule Id="CA1819" Action="Warn" />
    <Rule Id="CA1820" Action="Warn" />
    <Rule Id="CA1903" Action="Warn" />
    <Rule Id="CA2004" Action="Warn" />
    <Rule Id="CA2006" Action="Warn" />
  </Rules>
</RuleSet>

 

Team Foundation Server Check-In Policy

이 Rule Set 기능은 Team Foundation Server의 Check-In Policy에도 쓰이게 됩니다.

 

맺으며..

이번 Rule Set Feature를 통해서 확실히 이전 버전보다는 Code Analysis를 사용하는 것이 쉬워지고 편리해진 것 같습니다. 하지만, Code Analysis에서 중요한 것은 편의성보다는, Rule의 문제라고 생각됩니다. 그런 면에서 아직 FxCop이 처음 나왔을 때의 200개 정도의 Rule에서 많은 추가가 없다는 것은 굉장히 아쉽습니다. 특히 직접적인 경쟁 제품이라고 볼 수 있는 Compuware의 DevPartner Code Review가 처음 버전부터 600개 이상의 Rule을 가지고 있다는 점에 비교해 본다면 더욱 그렇습니다.

이어지는 포스팅에서는 새롭게 Code Analysis에 추가된 Phoenix 엔진과 그에 따라 다시 복귀한 Data Flow 관련 규칙들에 대해서 한 번 알아보겠습니다.

Welcome to F#(4) - 과거와 배경을 좀 더 알고싶어.

F# 2009. 4. 18. 21:48 Posted by 알 수 없는 사용자

- I remember back in the day

우리는 어떤 사람이 마음에 들면, 그 사람에 대해서 작은 거라고 할지라도 더 알고 싶어서 발버둥 칩니다. 멀리 갈것도 없습니다. 연예인에 삘꽂혀서 그 연예인의 나이, 취미, 혈액형 그리고 별 정확하지도 않은 키랑 몸무게 찾아다니느라 애쓰던 시절을 떠올려 보시지요. 그리고 노트북을 살때, 노트북에 꽂혀서 사소한 것 까지도 다 찾아내고 물어보고 알아내서 자신에 마음에 조금이라도 더 드는 상품을 사지요.
 
아이팟도 그 좋은 예인데요, 아이팟을 사고나서 온갖 해킹이며, 어플다 깔다가 하루가 지나갔던 그런 경험을 하신분도 있을 겁니다. 서론이 너무 기네요. 이번 포스트에서는 함수형언어를 이해하려고 무진장 애를 쓰다가 머릿속에 떠오른 게 있어서 적어보려고 합니다. 엄밀하지 못한 글일 수도 있으나, 따뜻한 피드백 많이 부탁드립니다. 차가운 피드백은... 아시져? ^^
 

- 기초학문?

개인적인 생각으로 우리가 흔히 기초학문이라 부르는 것들은 공통적인 특징이 있는 것 같습니다. 그리고 기초학문은 아마도 존재하지 않던 새로운 것들을 만들어 내는 쪽보다는 기존에 있던 것들에 새로운 의미를 부여하고 새로운 발견을 해나가는 쪽이 더 맞지 않나 합니다. 그래서 그 새로운것이 어떤건지 성질을 밝혀내고 알아내서 어떻게 응용이 될 수 있을지에 대한 단서를 제공하는게 아닌가 합니다. 

- 그래서 뭐? 수학?

그런 기초학문중에 대표적인 하나를 꼽자면 수학을 꼽을 수 있을 것 같습니다. 수학이라는 학문도 사람들이 살면서 자연스럽게 뭔가를 세기 위해서 군만두만 먹으면서 벽에 작대기를 그으면서 표시하던게 점차 발전하여 지금의 우리가 쓰는 숫자도 아랍쪽 어디선가 발견이 되고  퍼지면서 사용되는거고, 그 도구에 '수'라는 이름을 붙이면서 시작된게 아닌가 합니다. 뭔가 분명히 존재하지만 명확하게 정의할 수 없을때, 거기에 이름을 붙이게 되면 우리는 그에 대한 연구를 시작할 수 있게 됩니다. 이름을 붙인다는 건 우리에게 상당한 지배력을 제공해 주기 때문입니다. 무진장 가깝지만 사귀자고 누구도 명확하게 이야기 하지않은 상태라면, 서로 뻘쭘하고 넘을 수 있는 선의 범위도 모호하지만 서로의 관계를 공식적으로 '연인'이라고 이름붙이게 되면 더 자연스럽게 애정행각(!)을 벌일 수 있는 법이지요. ^^;; 

- 그래서 하고 싶은 이야기가 머꼬?

자, 다들 중~고등학교때 수학공부를 하고 문제를 풀던 때를 떠올려 보시지요. 수학은 정의와 공식으로 시작해서 정의와 공식으로 끝납니다. 정의나 공식은 이미 존재하는 무언가를, 그리고 그 무언가에 나타나는 패턴을 명확하게 나타내 보일 수 있는 수학적 도구입니다. 그리고 그런 정의와 공식에서 시작해서 또다른 정의와 공식이 그위에 쌓이게 되고 그런 것들이 계속 되면서 각각의 수학분야가 하나로 모이게 되는 대통합수학도 이루어 지는 거고 그렇게 하다보니 페르마의 마지막 정리 같은 난제도 해결이 될 수 있었습니다. 

즉, 수학문제를 풀때 그 문제를 이루고 있는 각 요소가 뭔지 공식으로 정의를 해내고, 그 정의를 모아서 문제에 대한 공식을 도출해 냅니다. 그리고 그 공식을 통해서 문제를 해결하고, 동일한 패턴을 보이는 문제들을 풀어나갑니다. 가만히 생각해보면, 중간중간에 도출되는 값을 x,y같은 부호에 묶어주고, 도출된 공식을 부호로 표시하거나, 공식에 있는 부호를 공식으로 풀어내서 문제를 해결하곤 했습니다. 수학문제를 해결한다는 건 무엇을 어떻게 정의하느냐에 따라서 금새 풀리기도 아무리 싸매고 고생해도 안풀리곤 했던 거지요. 우리가 풀던 수학에선 x에는 하나의 값이나 공식만 정의할 수 있었고, 상태를 저장하는 거 역시 없었습니다. 

- 그게 함수형 언어랑 뭔 상관?

최대공약수를 정의할 때 명령형 프로그래머는,

a와 b의 최대공약수를 계산 해내려면, 일단 a랑 b가 같은지 보는거야. 만약에 그렇다면, 둘중에 하나를 출력하고 끝나면 되는거지. 안 그렇다면, 둘중에 큰수를 두수의 차(빼기)로 바꾸고 다시 하면 돼

그리고 함수형 프로그래머는,

a와 b의 최대공약수는 a와 b가 같다면 a이고, a와 b가 같지 않다면 c와 d에 대한 최대공약수를 구하게 되는데 여기서 c는 a와 b중에 작은수가 되고, d는 그 두수의 차가 된다. 주어진 쌍의 숫자의 최대공약수를 구하려면, 이 공식을 세부적으로 계속 확장해 나간다음에 식이 끝날때까지 하나씩 수행하면서 줄여나가면 된다.

사실 이 글을 쓰게 된데에는 "Programming Language Pragmatics"에서 위의 문장을 보고서 든 생각을 바탕으로(제 맘대로 해석했습니다-_-) 기존에 수학에 대해서 생각해오던 것들을 추려서 쓴 것입니다. 위의 문장이 많은 것을 말해주는 것 같다는 생각이 듭니다. 명령형 프로그래머는 "어떻게"에 집중하는 반면에 함수형 프로그래머는 최대공약수 "무엇"인지를 정의하는데 중점을 둔다는 것입니다. 물론 F#은 Haskell과 같은 순수한 함수형언어는 아닙니다. 물론, 순수한 함수형 언어로도 사용할 수 있지만, 그건 F#의 역할이 아니라는게 개인적인 생각입니다. 

- 아 거 말 되게 많네.

깊이도 없는 내용을 쓸데없이 길게말하느라 쓰는 저도 수고했고, 읽어주신 여러분도 수고하셨습니다.-_-;; 사실 함수형언어는 "왜 함수형 언어인가?"에 대한 질문에 답을 할 수 있어야 함수형언어와 친하게 지낼 수 있지 않을까 하는 생각이 듭니다. 명령형나라의 프로그래머와 함수형나라의 프로그래머는 서로 문제를 바라보는 관점이 다르기 때문이죠. "Why Functional Programming Matters?"와 같은 좋은 논문도 있어서 공부중이지만, 부족한 제머리와 부족한 시간은 깝깝하기만 하군요;; 아무튼, 제가 느끼는 것들은 이 공간을 통해서 계속 머리박아가면서 쓰겠습니다. 좋은 피드백으로 격려해주세요. 앞으론 차가운 피드백 이야긴 안하겠습니다.^^ 


-참고자료

1. Expert F#, Don Syme, Adam Granicz, Antonio Cisternino, APRESS.
2. Programming Language Pragmatics, 2nd Edition, Michael L. Scott, Morgan Kaufmann Publishers
3. 거짓의 사람들, 스캇 펙, 비전과 리더십
4. 페르마의 마지막 정리, 사이먼 싱, 영림카디널
5. 사고력을 키우는 수학책, 오카베 츠네하루, 을지외국어



ps. 왜 연재 계획도 없고, 이야기 했던 계획이랑도 틀린 포스트가 계속 올라오냐?

아직 아무도 지적하신 분은 없지만... 네 지당하신 지적입니다. 제가 연재라는 개념이 희박하고 분야에 대한 지식이 얉은 탓에 그렇게 되는 점 혹시 안좋게 보신 분들이 있다면 사과드립니다;;; 앞으로도 계속 그럴지도 모르기 때문에 지금쯤에는 사과를 해 드려야 나중에 한꺼번에 욕을 먹는 사태를 방지할 수 있을거 같아서 말이죠-_-;;;


Visual Studio Team System 2005 에서 코드 분석(Static Code Analysis) 기능이 처음 소개된 바가 있습니다. 이 코드 분석은 Microsoft에서 제안하는 닷넷 프레임워크에서의 디자인 가이드라인과 성능이나 보안, 신뢰성 등의 요소에 대한 Best Practice 등으로 이루어진 200개 이상의 Rule을 기반으로 Code를 검사하고 결함을 발견해 줍니다.

 

원래 이 Code Analysis는 FxCop이란 이름의 독립된 툴로써 세상에 먼저 선을 보인 바 있습니다.

이 FxCop이 Visual Studio 2005 Team System에서 통합되면서 현재의 코드 분석 기능이 나오게 된 것입니다. 실제로 비주얼 스튜디오에서 코드 분석 기능을 담당하는 실행 파일의 이름은 아직도 FxCopCmd.exe입니다.

앞으로 나오게 될 Visual Studio Team System 2010에서는 현재까지 알려진 바로는 다음 세 가지 부분에서 코드 분석의 기능 향상이 이루어 졌습니다. (출처: http://blogs.msdn.com/fxcop/archive/2008/10/30/new-code-analysis-features-in-visual-studio-2010-september-08-ctp.aspx)

  1. Rule Sets – 드디어, 규칙을 프로젝트 별로 하나 하나 빼던 수고스러움을 덜 수 있게 된 것 같습니다. DevPartner Code Review와 같은 상용 툴에서는 진작에 제공되던 기능이었지만, 이제 드디어 Visual Studio 2010에서는 Rule들을 Set으로 만들고 그 Rule Set 기반으로 코드 리뷰를 할 수 있게 되었습니다.
  2. Check-In Policy – 당연한 말이겠지만, 바로 위에서 소개한 Rule Sets 기능이 Team Foundation Server의 Check-In 정책에도 적용할 수 있게 됩니다.
  3. 8개의 새로운 Data-Flow 규칙MSDN Code Gallery의 VS2005와 VS2008 코드 분석 비교 문서를 보시면 아시겠지만, VS2008에서 7개의 규칙이 빠진 바 있습니다. Data-Flow Analysis Engine의 각종 버그와 낮은 성능으로 말미암은 결과라고 하는데, 이번 Visual Studio Team System 2010에서는 Phoenix라는 새로운 분석 엔진의 탑재와 함께 다시 7개의 규칙들이 복귀했고, 새롭게 추가된 하나를 합쳐서 총 8개의 새로운 규칙이 추가되었습니다. 그리고 이번에 추가된 Phoenix 분석 엔진은 당연히 기존 엔진이 가지고 있었던 버그나 성능 문제를 해결한 새롭고 강력한 엔진입니다.

그러면 다음 포스팅을 통해서 이 세 가지 개선점들에 대해서 더 자세히 다뤄보도록 하겠습니다.

WPF Features Preview (1) – DataGrid

WPF 2009. 4. 17. 20:32 Posted by 알 수 없는 사용자

WPF 4에서는 새로운 컨트롤들이 포함되게 되는데 대표적으로 DataGrid, Ribbon, VIsual State Manager 컨트롤이 있습니다.
WPF Features Privew를 통해서 새로운 컨트롤들을 미리 보는 시간을 가져보겠습니다.

Exercise 1 - Using the new WPF DataGrid

먼저 기존에 있는 Checkbook 어플리케이션을 살펴보면 ListView를 이용해서 DataGrid 형태로 사용하는 것을 볼 수 있습니다.
이번 실습에서는 몇가지 단계를 따르게 되는데 다음과 같습니다.

1. DataGrid 어셈블리 추가 (WPF Toolkit)
2. ListView와 GridView를 DataGrid로 교체
3. 스타일과 템플릿 변형

먼저 WPF 어플리케이션을 실행해보면 몇가지 데이터가 바인딩된 어플리케이션 형태를 볼 수 있습니다.

Task 1 - Examine the existing application

CheckbookManager 어플리케이션을 Visual Studio를 이용해서 열어서 살펴보면 어떤 형태로 동작하는지 쉽게 알 수 있습니다.
이제 소스코드를 살펴보면 메인 윈도우인 MainWindow.xaml에 보면 ListView가 정의된것을 볼 수 있습니다. 스타일과 컬럼 효과를 이용해서 형태를 정의하고 있는데 column definition은 템플릿을 필요로 하게 됩니다. 왜냐하면 기본적인 ListView 컬럼은 읽기 전용 속성이기 때문입니다.
이것을 TextBox를 이용해서 DataGrid처럼 표현하고 있는것을 볼 수 있습니다.
Data 폴더에 보면 자료구조가 정의되어 있는데 여기서 살펴볼것은 아니니 그냥 넘어가도록 하겠습니다.

Task 2 - Using the DataGrid

이번 단계에서는 ListView를 WPF DataGrid로 교체해보도록 하겠습니다.
먼저 DataGrid를 포함하고 있는 WPFToolkit 어셈블리를 참조합니다.


참조 추가를 선택하고 WPFToolkit.dll 파일을 선택해주면 됩니다.

그리고 나서 MainWindow.xaml 상단에 보면 네임스페이스가 정의된 부분이 있는데 여기에 WPF Toolkit을 추가해줍니다.


접두어를 붙여주고 Microsoft.Windows.Controls 네임스페이스를 선택해주면 WPFToolkit이 등록됩니다.

스크롤을 좀 더 내려서 XAML을 살펴보면 ListView가 있는데 ListView를 dg:DataGrid로 바꿔서 DataGrid 컨트롤을 사용할 수 있게 해줍니다.
물론 바꾸고 나면 많은 에러가 나게 되는데 이것은 차차 고쳐 나갈것입니다.
그리고 ItemContainerStyle 속성을 지워줍니다. 이것은 나중에 다시 해줄텐데 우선 지우도록 하겠습니다.

그리고 TextBox 스타일 리소스 역시 지워줍니다. 우리는 새로운 데이타 컬럼 타입을 사용하게 되니 더 이상필요하지 않습니다.
ListView의 ContextMenu 속성 역시 dg:DataGrid로 바꿔줍니다.
이렇게 해주면 다음과 같은 XAML 형태를 가지게 됩니다.

<!-- DataGrid fills remainder of space -->

<dg:DataGrid x:Name="dg" ItemsSource="{Binding}" Margin="10"

             Background="#80909090" AlternationCount="2">

   <dg:DataGrid.ContextMenu >

      <ContextMenu >

         <MenuItem Header="Copy Selected Transactions"

                   Command="{x:Static ApplicationCommands.Copy}" />

      </ContextMenu>

   </dg:DataGrid.ContextMenu>

   <!-- <ListView.View> ...

        </ListView.View> -->

</dg:DataGrid>

이제 어플리케이션을 컴파일하고 다시 실행시켜 보면 DataGrid가 자동적으로 컬럼을 생성해서 데이터를 보여주는것을 볼 수 있습니다.

다시 소스코드로 돌아와서 컬럼 타입을 정의해주도록 하겠습니다.
DataGrid 안에 <dg:DataGrid.Columns>를 추가 해 줍니다.
그리고 <dg:DataGridTextColumn> 요소를 위의 컬럼 컬렉션 안에 추가 해 줍니다.

Header 프로퍼티는 Header 텍스트값의 속성이고 Width는 컬럼의 폭을 정할 수 있는데 고정값을 줄 수도 있고 컬럼의 헤더나 셀 사이즈에 맞게 해줄 수도 있습니다.
마지막으로 컬럼에 데이터 바인딩을 해줘야 하는데 Binding 속성을 이용해서 ListView에서 해줬던것 처럼 같은 방식으로 해주면 됩니다.

<dg:DataGrid x:Name="dg" ItemsSource="{Binding}" Margin="10"

                  Background="#80909090" AlternationCount="2">

 

            <dg:DataGrid.Columns>

                <dg:DataGridTextColumn Header="No." Width="SizeToCells"  

                                       Binding="{Binding CheckNumber}" />

            </dg:DataGrid.Columns>

여기까지 하고 실행을 해보면 새로 정의해준 컬럼이 추가되긴 했지만 기존 컬럼이 그대로 남아있는것을 볼 수 있습니다.
DataGrid 속성중에 AutoGenerateColumns라는 속성이 기본적으로 True값을 가지게 되어 이렇게 되는데 이 속성을 False로 바꿔주면 정의된 컬럼만 나타나게 됩니다.

<dg:DataGrid x:Name="dg" ItemsSource="{Binding}" Margin="10"

                  AutoGenerateColumns="False"

                  Background="#80909090" AlternationCount="2">

그리고 나머지 컬럼들에 대해서도 속성을 정의해주면 됩니다.

DataGridCheckBoxColumn
DataGridComboBoxColumn
DataGridHyperlinkColumn
DataGridTextColumn
DataGridTemplateColumn

이름을 보면 알 수 있듯이 DataGrid 안에서 일반 컨트롤과 같은 역할을 하게 됩니다.
이러한 컬럼 스타일을 이용해서 원하는 데이터에 맞게 설정을 해주면 초기의 ListVIew에서 보여줬던 형태를 DataGrid로 만들 수 있습니다.

여기까지 마치고 실행해보면 정의해준 컬럼 스타일에 맞게 데이터가 바인딩되어 보이게 됩니다.
지금까지 WPF Toolkit을 이용해서 DataGrid를 추가하고 컬럼 속성 스타일을 주어서 데이타 바인딩하는 방법까지 살펴보았습니다.
다음에는 여기에 추가해서 Calendar와 DatePicker 컨트롤을 이용해서 데이터 필드를 구성하는것에 대해 보도록 하겠습니다.

참고자료
WPF Toolkit : http://www.codeplex.com/wpf

Expression Blend3 preview - 2. Photoshop import

RIA 2009. 4. 17. 20:29 Posted by 알 수 없는 사용자

 
Expression Blend3 preview - 1.인터페이스 에 이어서 Photoshop import - 테스트를 해봤습니다.

이슈는 Photoshop ans Illustrator import였는데,
illustrator랑은 친하지도 않고 라이센스도 없으며 있어도 거의 사용하지 않기 때문에~
(쓸 일 있으면, 요즘은 왠지 간단한 Expression Design으로 해결!)

저와 친한 Photoshop만 ~ (사이좋게 지내자 - ㅠ_ㅠ)



Photoshop은 *.psd, Illustrator는 *.ai 파일이 import가능하군요.

1. PSD파일을 하나 import 해봤습니다.
먼저, 어디에서든 import해도 문제가 많이 생길것만 같은 레이어 많은 psd를 선택해 보겠습니다.



원본은 이런모습의 ! (CodeSafe 홈페이지 시안입니다! _ 어어;; 이거 맘대로 썼다고 혼나면....? ;;)
layer가 100개이상 들어있고 group도 10개 이상, Mask, Layer style, path까지 들어있는 10MB가 넘는 덩치 큰 시안 입니다.
얼마나 어떻게 Import될지 두근두근합니다-



File 메뉴에서 Import Adobe Photoshop File 을 클릭하고 import할 파일을 선택하면,



오우- 노!!!!
이제, import를 어떤 경우에 쓸 수 있을지... 잘 살펴보겠습니다.



CodeSafe 시안이 엉망이 되어버렸군요!!
먼저 , 예상했듯..

1. 메인네비게이션 - clipping mask 가 사라졌군요.
2. 블랜딩이미지의 - 화살표는 opacity와 fill로 투명도를 조절해뒀는데 opacity는 잘 따라오는데, fill의 값은 import 못 했군요.
3. Hosting, ALM, TFS 색들이 짠- 나타난 것을 보니 Layer blanding 옵션도 안되는 군요.

4. 위의 세개는 예상했지만, Text들의 글자간격, 줄간격 값이 변했고,
5. 전체 포지션들도 어긋나는것을 볼 수 있습니다!

object패널을 보면-



group들도 적용이 되고 - 마스크등등으로 얽혀있던 레이어들을 깔!끔 - 하게 모조리 이미지로-
그리고 text들은 살려서 text block으로-
(이런 모습은 훌륭하군요!!)



그리고 생성된 이미지들은 (전부다 png ! - 다른 포맷으로 설정하는것은 안보이네요.)
import한 'psd파일이름_Images' 에 자동 저장되네요.

생성된 이미지 폴더의 속성을 보면 156개의 이미지가 생성되었고, 총 용량은 2.56MB 정도 되네요 - .

이번엔, 좀 간단한 psd파일을 import해보겠습니다.



얼마전 이미지 탭메뉴에서 사용했던 고양이들의 psd 입니다.
깔끔하게 불러왔습니다.
레이어도 그룹별로 나누어져 있습니다.
이런 기능이 전에도 있었으면 고양이들을 하나하나 따로 잘라내는 작업은 줄일 수 있었겠군요.

+ TIP :

Import 창에서 Compatibility Layer에 체크를 하시면 ~
레이어와 상관없이 -
PSD원본 그대로 View의 PNG 하나만 생성됩니다. 말그대로 합체!
(포토샵에서 디자인의 사본 이미지를 png로 저장해서 blend에 불러온것과 같은 느낌!)

결론 >>
프로젝트에 사용해야할 이미지들이 한가득이고, 라이브러리를 포토샵으로 이미지 작업 했을 경우 - 한번에 레이어 별로 프로젝트에 추가할 때는 유용하게 쓸 수 있겠군요.

image set를 psd로 만들어서 정리만 한 뒤, 프로젝트에 import한다던지...

아무튼 좋은 수단이 되어 주었으면 좋겠습니다. (뭐든 유용하게 쓰면 좋은 수단!)

Expression Blend3 preview - 1.인터페이스

RIA 2009. 4. 17. 20:27 Posted by 알 수 없는 사용자

Expression Blend3 preview 가 공개가 되었군요!
궁금한 마음에 후딱 설치를 해보고 살짝 무엇이 변하였나 인터페이스만 둘러 보았습니다.

Expression Blend3 preview 다운로드 - http://www.microsoft.com/expression/try-it/blendpreview.aspx



당연히, Blend2 와는 별도로 실행 할 수 있습니다.



New project를 클릭해서 새 프로젝트를 하나 생성해 보았습니다.



프로젝트 생성창이 바뀌었군요!!
silverlight 3 개발툴을 깔아 봐도 알 수 있겠지만, Visual Studio에서도 Silverlight Project에
Silverlight Navigation Application(누구냐 넌!)이라는게 추가 되었더군요.


위 이미지는 - 비쥬얼스튜디어 새프로젝트 생성- 창입니다.

뭔가, 새로운 것들을 또 해볼 수 있게 되겠군요.
더불어 공부할것도 많아지고? (나안해나안해)

새프로젝트로 Silverlight 3 Application + Website 를 생성해보았습니다.



미묘하게- 인터페이스가 깔끔해진 모습입니다.
사실, 이전 버전까지는 왠지 professional해 보이지 않는 느낌의 인터페이스라고 생각했었는데 ( 저만 그런거?)
미묘한 차이이긴 하지만, 저는 마음드는 군요!
역시, 개발자분은 인터페이스는 전이랑 똑같네!! 라고 말해 주었습니다. || 분명히 틀려!

인터페이스는 프로젝트패널이 왼쪽으로 가서 붙었습니다.
위치만 옮긴게 아니라 TFS (Team Foudation Server) 도 사용 할 수 있다니, Blend로 수정하고 Visual Studio를 열어 체크인하는 귀찮은 짓을 하지 않아도 되겠군요.
Visual Studio를 열지않고 TFS 기능을 사용할 수 있는 방법이 있다고 하나,

난... 몰랐을 뿐이고! XAML 한줄 고치고, 또 Visual Studio 열고 있고!
벌써 프로그램 종료했는데 또, 수정 할게 보이고!!
그래서, Blend에서 TFS사용하는것과 함께 다음기회에 ~

툴바를 살펴보니,
여기도 왠지 미묘하게 바뀌었습니다.

   


이 상단에 땡땡땡이 들은 움직일 수 있다는 거군요!!!
다짜고짜 패널을 더블클릭 해보니 분리되어 툭떨어져 나오고, 다시 더블클릭하면 원래 자리로 돌아갑니다.



다른 자리에도 찰싹찰싹 잘 갖다 붙네요.
사소하지만, 나름 유용한 업데이트 같습니다.

제가 제일 관심있었던 부분은 컨트롤입니다.
그래서 Asset Library를 살펴보았습니다.



Blend2의 Silverlight 의 asset library에서는 볼 수 없었던, 아이들을 표시 해 보았습니다.
반가운 컨트롤들이 보이는군요! WPF 프로젝트에서만 보이던 컨트롤들도 있고-
주만간 주물주물 해줘야 할 것들 -
아무쪼록 친하게 지내보자꾸나! ㅠ_ㅠ

하지만, 그 중 제일 반가운것은!!!



바로 이것, Intellisense!!
Blend 2 에서도 Add in을 설치하여 쓰는 방법도 있지만 ,
이 것은 친절하게 설명도 나옵니다!
자자- Intellisense가 얼마나 작업에 효율적이며, 얼마나 우리를 바보로 만들 수 있는지 직접적으로 쉽게 체험해 볼 시간이 왔네요. (ㅎㅎ) 코드 따위! 시작하는 알파벳만 외워주는겁니다!!! ( ㅠ_ㅠ )

인터페이스 구경은 이걸로 대충 끝-

조금 아쉬워 - File 메뉴를 살짝 열어보니


이런 것들이...
Import Adobe Photosop File?
아니 이게 도대체 뭐란 말인가!!
PSD파일을 이미지, 텍스트 채로 불러오는 굉장한(!;;) 기능 같아 보입니다.
블렌드랑 포토샵이랑 친하게 지내게 만들기?
이 것도 다음기회에...
궁금하면 설치하여 한번 해보는겁니다!!

자세한것과 Silverlight 3.. 등등은 다음에 또 해봐야겠습니다. @_@

Windows SDK 설치 후 XAML 인텔리센스 문제

Windows 7 2009. 4. 16. 22:50 Posted by 알 수 없는 사용자

Windows SDK 설치 후 XAML을 사용하는 프로젝트에서 인텔리센스가 동작하지 않는 문제가 있습니다.
Windows SDK가 설치 된 후 Visual Studio의 레지스트리 값이 변경되어 관련된 dll 파일  연결이 깨져버린 현상입니다. 

regedit 명령어를 이용해서 레지스트리 편집기를 연 후 다음 키를 살펴봅니다. 

x86
 HKEY_CLASSES_ROOT\CLSID\{73B7DC00-F498-4ABD-AB79-D07AFD52F395}\InProcServer32 

x64
 HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{73B7DC00-F498-4ABD-AB79-D07AFD52F395}\InProcServer32 

이 항목이 없다면 인텔리센스 문제가 생길 것입니다. 

두 가지 해결 방법이 있는데 제어판의 프로그램 추가/삭제에서 Visual Studio 2008을 복구 하거나
수동으로 dll 파일을 등록해주면 됩니다. 

수동으로 dll 파일 연결 방법은 다음과 같으니 시스템에 맞게 해주시면 됩니다. 

x86
 regsvr32 "%CommonProgramFiles%\Microsoft Shared\MSEnv\TextMgrP.dll" 

x64
 regsvr32 "%CommonProgramFiles(X86)%\Microsoft Shared\MSEnv\TextMgrP.dll" 

이렇게 해주고 다시 확인해보면 인텔리센스가 정상적으로 작동하는 것을 확인 할 수 있습니다.


Strongly Typed Metadata
 
지난 포스트의 [MEF] 7. Exports and Metadata를 통해 Export 의 Contract 에 Metadata 를 제공하는 방법을 알아보았습니다.
 
MetadataAttribute 을 선언하여 Export 의 Metadata 를 제공하는 방법입니다.
 
[Export(typeof(IMessageSender))]
[ExportMetadata("SenderType", "Email")]
[ExportMetadata("Logging", true)]
public class EmailMessageSender : IMessageSender
{
        public void Say()
        {
               Console.WriteLine("Import EmailMessageSender");
        }
}
 
Export 에 Metadata 를 제공해 주어서 무척 고맙지만, 위의 방법처럼 MetadataAttribute 를 선언하는 방법을 사용하기에 그다지 내키지 않는 구석이 있습니다.
 
아무래도 아래와 같은 이유 때문이겠죠?
 
l 메타데이터의 타입 불안정
l 빌드 시 오류를 해결이 어려움
l 사용상 모든 키값을 외우기 어렵고, 오타 발생 위험
 
위의 이유 때문에 Metadata 를 사용하기 위해 강력한 타입을 원하게 될 것입니다. 그리고 강력한 타입의 Metadata 를 사용하길 권장 드립니다.
 
 
Declaring Strongly Typed Metadata
 
Attribute 을 상속받아 Export 의 Metadata 를 Strongly Typed 으로 확장시키는 방법입니다.
 
아래의 Stringly Typed Metadata 를 위해 Attribute 클래스를 만드는 소스 코드 입니다.
 
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple=false)]
public class MessageSenderTypeAttribute : Attribute
{
        public MessageSenderTransport Transport { get; set; }
        public bool IsSecure { get; set; }
 
        public MessageSenderTypeAttribute() { }
 
        public MessageSenderTypeAttribute(MessageSenderTransport transport)
               : this(transport, false)
        {
        }
 
        public MessageSenderTypeAttribute(MessageSenderTransport transport, bool isSecure)
        {
               this.Transport = transport;
               this.IsSecure = IsSecure;
        }
}
 
public enum MessageSenderTransport
{
        Email,
        Phone,
        Sms
}
 
Strongly Typed Metadata 를 위한 Attribute 클래스를 완성하였으면, 이것을 그대로 ExportAttribute 과 함께 추가적으로 선언하면 됩니다.
 
그리고 ExportAttribute 을 선언한 코드에 위의 Attribute 특성을 부여합니다. 아래는 그 소스 코드의 일부입니다.
 
[Export(typeof(IMessageSender))]
[MessageSenderType(MessageSenderTransport.Email)]
public class EmailMessageSender : IMessageSender
{
        public void Say()
        {
               Console.WriteLine("Import EmailMessageSender");
        }
}
 
[Export(typeof(IMessageSender))]
[MessageSenderType(MessageSenderTransport.Phone, true)]
public class PhoneMessageSneder : IMessageSender
{
        public void Say()
        {
               Console.WriteLine("Import PhoneMessageSneder");
        }
}
 
[Export(typeof(IMessageSender))]
[MessageSenderType(Transport=MessageSenderTransport.Sms, IsSecure=true)]
public class SmsMessageSender : IMessageSender
{
        public void Say()
        {
               Console.WriteLine("Import SmsMessageSender");
        }
}
 
그렇다면 아래와 같이 Metadata 를 Strongly Typed 으로 질의(Query) 할 수 있게 됩니다.
 
아래의 소스 코드는 지난 포스트의 소스 전체 소스 코드를 참고하십시오.
 
foreach (var export in program.Sender)
{
        if ((MessageSenderTransport)export.Metadata["Transport"] == MessageSenderTransport.Sms)
        {
               export.GetExportedObject().Say();
 
               if ((bool)export.Metadata["IsSecure"] == true)
               {
                       Console.WriteLine("Security message");
               }
        }
}
 
위의 소스 코드 실행 결과는 원하던 결과대로 다음과 같습니다.
 

[그림1] Strongly Typed 질의 결과
 
 
하지만 아직도 문제는 남아 있는 것 같아 보이네요. Strongly Typed 으로 Export Metadata 를 선언하였지만 여전히 질의(Query) 과정은 똑같은 문제점을 가지고 있습니다.
 
l 메타데이터의 질의(Query) 과정의 타입 불안정
l 빌드 시 오류를 해결이 어려움
l 사용상 모든 키값을 외우기 어렵고, 오타 발생 위험
 
 
More Strongly Typed Metadata Query
 
Metadata 를 질의(Query) 하기 위해 MEF 는 보다 강력하게 Import 하는 방법을 제공해 줍니다. 확장된 MetadataAttribute 에 인터페이스(Interface) 를 구현하도록 하여 Import 시에 인터페이스(Interface) 를 통한 Metadata 를 질의(Query) 하는 방법입니다.
 
우선 Attribute 에 사용되는 프로퍼티를 인터페이스로 선언합니다.
 
public interface IMessageSenderTypeAttribute
{
        bool IsSecure { get; }
        MessageSenderTransport Transport { get; }
}
 
단, 여기에서 반드시 Getter 만 선언하셔야 합니다. Setter 를 선언하시면 MEF 는 Setter 프로퍼티로 인해 유효하지 않는 Attribute 으로 인식하여 예외를 발생하게 됩니다.
 
아래의 소스 코드는 Metadata Attribute 클래스에 위의 인터페이스를 구현한 코드 입니다.
 
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple=false)]
public class MessageSenderTypeAttribute : Attribute, IMessageSenderTypeAttribute
{
        public MessageSenderTransport Transport { get; set; }
        public bool IsSecure { get; set; }
 
        public MessageSenderTypeAttribute() { }
 
        public MessageSenderTypeAttribute(MessageSenderTransport transport)
               : this(transport, false)
        {
        }
 
        public MessageSenderTypeAttribute(MessageSenderTransport transport, bool isSecure)
        {
               this.Transport = transport;
               this.IsSecure = IsSecure;
        }
}
 
 
이제 Export 의 MetadataView 로 질의할 수 있도록 MetadataView 의 인터페이스를 알려주도록 해야 합니다.
 
[Import(typeof(IMessageSender))]
ExportCollection<IMessageSender, IMessageSenderTypeAttribute> Sender { get; set; }
 
아래의 소스 코드는 MetadataView 를 통해 Strongly Typed 으로 Export Metadata 를 질의(Query) 하는 소스 코드 입니다.
 
[Import(typeof(IMessageSender))]
ExportCollection<IMessageSender, IMessageSenderTypeAttribute> Sender { get; set; }
                                             
static void Main(string[] args)
{
        Program program = new Program();
        program.Run();
 
        foreach (var export in program.Sender)
        {
               if (export.MetadataView.Transport == MessageSenderTransport.Email)
               {
                       export.GetExportedObject().Say();
               }
        }
}
 


'Managed Extensibility Framework' 카테고리의 다른 글

[MEF] 10. Querying the CompositionContainer  (0) 2009.05.18
[MEF] 9. Recomposition  (1) 2009.04.19
[MEF] 7. Exports and Metadata  (0) 2009.04.16
[MEF] 6. Lazy Exports  (0) 2009.04.13
[MEF] 5. Catalog 사용  (0) 2009.04.09

[MEF] 7. Exports and Metadata

Managed Extensibility Framework 2009. 4. 16. 01:11 Posted by POWERUMC
Exports and Metadata
 
Export 에 Metadata 를 등록하고 제어하는 방법입니다. 지난 포스트에서 알 수 있듯이 Export 는 구성 요소간에 Contact 를 제공하여 이들을 구성(Composition) 할 수 있는 플러그인 모델(Plugin Model) 을 제공해 줍니다.
 
하지만 Contract 가 제공되어 구성 요소를 확장하고 구성하는 것은 매우 용이하다는 것을 알게 되었으나 Contract 로 인해 파생된 다양한 구성 요소를 어떻게 제어하느냐의 고민을 하게 됩니다. 즉, 다양한 구성 요소 가운데 내가 필요로 하는 구성 요소를 골라낼 수 있도록 Metadata 를 제공하여 구성 요소를 질의(Query)할 수 있도록 하는 것입니다.
 

예를 들어 아래와 같은 Export 구성 요소 중 어떻게 EmailMessageSender 로 Say() 를 호출할 것인가가 문제인 것이죠.

[Export(typeof(IMessageSender))]
public class EmailMessageSender : IMessageSender
{
        public void Say()
        {
               Console.WriteLine("Import EmailMessageSender");
        }
}
 
[Export(typeof(IMessageSender))]
public class PhoneMessageSneder : IMessageSender
{
        public void Say()
        {
               Console.WriteLine("Import PhoneMessageSneder");
        }
}
 
[Export(typeof(IMessageSender))]
public class SmsMessageSender : IMessageSender
{
        public void Say()
        {
               Console.WriteLine("Import SmsMessageSender");
        }
}
 
이런 경우, 원초적인 방법으로 리플랙션(Reflection) 을 이용하여 Type 검사를 통해 EmailMessageSender 를 골라내서 사용하면 되지만, 직접적으로 Type 을 검사하기 위해서는 Tightly Coupling 이 발생하여 결국 유연한 플러그인 모델을 구현하기 위해 아무런 도움이 되지 않습니다.
 
이러한 방법을 해소하기 위해 또 다른 우회 방법은 리플랙션을 통해 Modules Name 으로 비교하는 방법이지만, 이것 또한 플러그인 모델에서 구성 요소가 교체 되었을 경우를 생각하면 전혀 대응할 수 없는 방법입니다.
 
MEF 에서는 이런 문제를 해소하기 위해 Contract 에 Metadata 를 제공하며 정적/동적인 방법을 제공해 줍니다.
 
 
Attaching Metadata to an Export
 
Export 에 Metadata 를 제공해 주기 위해서 MEF 에서는 ExportMetadataAttribute 특성을 제공해 줍니다.
 
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Method | AttributeTargets.Field,
                    AllowMultiple = true, Inherited = false)]
public ExportMetadataAttribute(string name, object value)
 
왜 ExportMetadata 클래스는 sealed 로 선언이 되었나요?
 
일반적으로 sealed 로 클래스를 봉인할 경우 리플랙션의 성능이 향상됩니다.
 
 
ExportMetadata 는 키와 값(Key/Value) 을 지정하여 Contract 에 Metadata 를 제공해 줄 수 있습니다. 그리고 하나의 Export 에 여러 개의 Metadata 를 제공할 수 있도록 AllowMultiple 을 지원합니다.
 
Metadata 는 여러 가지의 정보를 포함할 수 있습니다. Export 가 제공하는 기능의 특성을 기술할 수 있으며, 예를 들어, 권한, 로깅, 구성 요소 분류 방법 등이 될 수 있을 것입니다.
 
아래의 소스 코드는 Metadata 를 지정하는 예를 보여줍니다.
 
[Export(typeof(IMessageSender))]
[ExportMetadata("SenderType", "Email")]
[ExportMetadata("Logging", true)]
public class EmailMessageSender : IMessageSender
{
        public void Say()
        {
               Console.WriteLine("Import EmailMessageSender");
        }
}
 
[Export(typeof(IMessageSender))]
[ExportMetadata("SenderType", "Phone")]
public class PhoneMessageSneder : IMessageSender
{
        public void Say()
        {
               Console.WriteLine("Import PhoneMessageSneder");
        }
}
 
[Export(typeof(IMessageSender))]
[ExportMetadata("SenderType", "Sms")]
public class SmsMessageSender : IMessageSender
{
        public void Say()
        {
               Console.WriteLine("Import SmsMessageSender");
        }
}
 
 
 
Constraining Imports statically
 
MEF Preview 5 에서는 ImportRequiredMetadataAttribute 클래스가 제거되었습니다.
 
MEF Preview 4 에서는 선언적인 방법으로 ImportRequiredMetadataAttribute 를 통해 Metadata 를 질의할 수 있었으나, MEF Preview 5 에서는 ImportRequiredMetadataAttribute 클래스가 제거되었습니다.
 
아마도 추측으로는 ImportRequiredMetadataAttribute 를 선언 시에 여러 개의 구성 요소가 검색될 경우 Exception 이 발생하는데, Exception 을 최소화 하고자 제거가 된 것 같습니다.
 
혹시 Statically 한 방법으로 ImportRequiredMetadataAttribute 에 대응되는 클래스를 아시면 저에게 알려주세요.
 
 
Constraining Imports dynamically
 
이 방법은 Export 의 ExportMetadata 를 런타임 시에 질의(Query) 하는 방법입니다.
 
Import 시 ExportCollection<T> 을 사용하여 Export 를 수동적으로 질의(Query) 하는 방법입니다. 이 방법은 지난 포스트의 Lazy Load 를 이용한 방법으로 단지 Metadata 만 질의(Query) 뿐이고, 객체의 생성에 대한 판단은 필요 시에만 GetExportedobject() 메서드를 이용하여 생성할 수 있습니다.
 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Reflection;
 
namespace MetadataSample
{
        class Program
        {
               [Import(typeof(IMessageSender))]
               ExportCollection<IMessageSender> Sender { get; set; }
 
               static void Main(string[] args)
               {
                       Program program = new Program();
                       program.Run();
 
                       foreach (var export in program.Sender)
                       {
                              if ((string)export.Metadata["SenderType"] == "Email")
                                      export.GetExportedObject().Say();
 
                              if (export.Metadata.ContainsKey("Logging") &&
                                      (bool)export.Metadata["Logging"] == true)
                                      Console.WriteLine("Logged success");
                       }
               }
 
               void Run()
               {
                       var catalog = new AggregateCatalog(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
 
                       var container = new CompositionContainer(catalog);
                       var batch = new CompositionBatch();
                       batch.AddPart(this);
                       container.Compose(batch);
               }
 
               public interface IMessageSender
               {
                       void Say();
               }
 
               [Export(typeof(IMessageSender))]
               [ExportMetadata("SenderType", "Email")]
               [ExportMetadata("Logging", true)]
               public class EmailMessageSender : IMessageSender
               {
                       public void Say()
                       {
                              Console.WriteLine("Import EmailMessageSender");
                       }
               }
 
               [Export(typeof(IMessageSender))]
               [ExportMetadata("SenderType", "Phone")]
               public class PhoneMessageSneder : IMessageSender
               {
                       public void Say()
                       {
                              Console.WriteLine("Import PhoneMessageSneder");
                       }
               }
 
               [Export(typeof(IMessageSender))]
               [ExportMetadata("SenderType", "Sms")]
               public class SmsMessageSender : IMessageSender
               {
                       public void Say()
                       {
                              Console.WriteLine("Import SmsMessageSender");
                       }
               }
        }
}
 
 
실행 결과는 예상할 수 있듯이 아래와 같이 런타임 시에 결과를 보여줍니다.


'Managed Extensibility Framework' 카테고리의 다른 글

[MEF] 9. Recomposition  (1) 2009.04.19
[MEF] 8. Strongly Typed Metadata  (0) 2009.04.16
[MEF] 6. Lazy Exports  (0) 2009.04.13
[MEF] 5. Catalog 사용  (0) 2009.04.09
[MEF] 4. Import 선언  (0) 2009.04.07