닷넷 프로그래밍 커뮤니티에서 요즈음 제일 큰 이슈는 바로 .NET Framework 4와 Visual Studio 2010일 것입니다. 어느새 버전 4에 이르렀고, 정말로 많은 변화와 개선이 있었고, 그 현주소가 바로 지금 시점일 것입니다. 오늘은 .NET Framework 4에 대한, 그 중에서도 핵심 API 및 기술들에 대한 변화를 살펴보는 블로그 아티클을 올려봅니다.
진단 기능 및 성능 측정
.NET Framework 4에 들어서면서부터, CPU 사용량과 메모리 사용량을 프로세스 단위가 아닌, 응용프로그램 도메인 단위에서 측정하는 일이 가능해졌습니다. 응용프로그램 도메인은, 닷넷 프레임워크에서 사용하는 실제 실행 단위이며, 무엇보다도 중요한 것은 응용프로그램 도메인은 프로세스와 달리 닷넷 프레임워크의 관리 아래에서 통제할 수 있는 자원이라는 점입니다. (프로세스는 운영 체제의 커널에 의하여 귀속되고 관리되는 자원입니다. 따라서, 비 관리 영역에 속하는, 기본적으로는 권외 영역인 셈입니다.)
.NET Framework 4에 들어서면서부터 백그라운드 가비지 수집 기능이 제공되는데, 이는 이전 버전의 동시 가비지 수집을 대체하고, 좀 더 향상된 가비지 수집 처리 서비스를 제공합니다. 자세한 내용은 http://msdn.microsoft.com/ko-kr/library/ee787088.aspx 페이지의 Background Garbage Collection 섹션을 참고하시기 바랍니다.
병렬 컴퓨팅 패러다임의 지원
이제 .NET Framework 4에서도 병렬 컴퓨팅의 패러다임을 손쉽게 가져다 사용할 수 있게 되었습니다. 이제까지 .NET Framework에서 사용하던 비동기 프로그래밍은 Thread 클래스를 직접 사용하거나, Begin/End의 Pair로 구성된 비동기 버전의 연산, 나중에 이벤트로 결과를 통지 받는 패턴, ThreadPool 클래스의 활용과 같이 다중 Threading 작업의 세밀한 부분과 상호 작용을 고려해야 하는 패턴들이었습니다.
http://msdn.microsoft.com/ko-kr/library/dd460693.aspx 에서 소개하는 것 처럼 .NET Framework 4는 병렬 프로그래밍에 대한 새로운 개념들을 제공합니다. .NET Framework 4 환경에서 병렬 프로그래밍은 Task Parallel Library (TPL)에 의한 Action과 PLINQ (Parallel LINQ)에 의한 Action으로 구분됩니다.
Task Parallel Library의 경우, 데이터를 중심으로 비동기/병렬 연산을 수행하기 위한 시나리오, 작업 자체에 집중하여 작업의 실행 순서를 제어하고 관리하는 시나리오, 기존의 전통적인 비동기 패턴과 TPL을 통합하는 시나리오 등 다양한 시나리오를 MSDN 내에서 제공하고 있습니다. TPL을 어떻게 활용할 지를 고민하고 효율적으로 선택해야 하는 어려움이 있지만, 여러분의 응용프로그램이 Cloud Ready 응용프로그램이 될 수 있도록 도와주는 지름길로도 활용할 수 있을 것입니다.
.NET Framework 4에서는 HttpWebRequest, HttpListener, SmtpClient, SslStream, NegotiateStream 등 여러 클래스에서 Windows 인증의 보안이 강화되었습니다. Windows 7 및 Windows Server 2008 R2에서 응용 프로그램에 대한 확장된 보호 기능을 사용할 수 있습니다. 그리고, IPv6 및 Teredo를 사용한 NAT(Network Address Translation) 통과를 지원합니다. 자세한 내용은 IPv6 및 Teredo를 사용한 NAT 통과를 참조하십시오.
HttpWebRequest 클래스에서 AddRange 메서드에 대한 새 오버로드를 통해 큰 바이트 범위 헤더(64비트 범위)를 사용할 수 있도록 지원합니다. HttpWebRequest 클래스의 새 속성을 통해 응용 프로그램에서 여러 개의 HTTP 헤더를 설정할 수 있습니다. Host 속성을 사용하여 요청 URI에 종속되지 않은 HTTP 요청에 호스트 헤더 값을 설정할 수 있습니다. 그 외에, SmtpClient 및 관련 클래스에 대한 SSL(Secure Sockets Layer) 지원 기능이 추가되었으며, MailMessage 클래스의 메일 헤더 지원 기능이 향상되었습니다.
암호화에 null 암호화를 사용할 수 있도록 지원합니다. ServicePointManager 클래스와 EncryptionPolicy 속성을 사용하여 암호화 정책을 지정할 수 있습니다. SslStream 클래스의 생성자에서 EncryptionPolicy 클래스를 매개 변수로 사용합니다.
ASP.NET 4
ASP.NET Caching을 확장할 수 있게 하는 새 API, Session State의 압축 지원 및 Application Preload 기능이 추가되어 더욱 개선된 Performance를 제공하게 되었습니다. Web Forms의 경우, 보다 통합된 ASP.NET 라우팅 지원, 향상된 웹 표준 지원, 업데이트된 브라우저 지원, 데이터 컨트롤을 위한 새 기능 및 새로운 뷰 상태 관리 기능 등이 포함됩니다. 그리고, 새로운 차트 컨트롤 등이 포함됩니다.
ASP.NET MVC 2에서는 뷰를 위한 새 도우미 메서드, 분할된 MVC 응용 프로그램에 대한 지원 및 비동기 컨트롤러 등이 포함됩니다. ASP.NET AJAX 라이브러리의 클라이언트 기반 AJAX 응용 프로그램에 대한 추가 지원 등이 포함됩니다.
그 외 변경 사항들
WPF 4는 이제 Silverlight 4와 동등한 컨트롤들을 제공합니다. 특히 데이터 그리드, 달력, 날짜 선택 컨트롤이 제공되고 실버라이트 응용프로그램을 거의 그대로 있는 그대로의 상태 (as-is)로 마이그레이션하는 것을 고려해 볼 수 있습니다. 자세한 내용은 http://msdn.microsoft.com/ko-kr/library/bb613588.aspx 를 참고하여 주십시오.
.NET Framework 4.0의 기능을 지금 사용해보기 원하시나요? http://www.microsoft.com/express 에 방문하셔서 최신 버전의 Visual Studio 2010 Express Edition을 설치하고 지금 시작해보세요. 그리고, 2010년 6월 1일에 있을 REMIX'10 행사는 Visual Studio 2010과 함께 최신 웹 기술을 집중적으로 조명합니다. 관심있으신 개발자 여러분들의 적극적인 참여를 기대합니다. :-)
지난번에 올린 Dynamic Programming에서, COM에 대한 Dynamic Binding을 통하여 웹 브라우저를 마치 자바스크립트로 제어하는것과 같은 편리성을 확인할 수 있었습니다. 그리고 여기에 더불어서, Dynamic Programming에 대한 재미있는 내용을 하나 더 다루어보고자 합니다.
System.Dynamic 네임스페이스는 닷넷 프레임워크 4.0에서 새로 소개된 네임스페이스로, Dynamic Language Runtime을 활용할 수 있도록 기본 기능들에 대한 사항들을 포함하고 있습니다. 여기서 우리가 알아두면 유용한 리소스 두 가지를 살펴보기로 하겠습니다.
1. DynamicObject의 재정의
System.Dynamic.DynamicObject 클래스는, System.Dynamic.IDynamicMetaObjectProvider 인터페이스를 구현하는 보통의 클래스입니다. 다만, 생성자가 protected로 보호되어있고, 10종류가 넘는 가상 메서드를 제공하고 있습니다.
DynamicObject는 Dynamic Programming에 있어서 필요한 기능들을 거의 모두 내장하고 있습니다. 가령, 컴파일 타임에서 정의되지 않은 멤버를 두고서 단항/이항 연산자를 재정의하거나, 멤버를 가져오거나, 멤버를 설정하거나, 멤버를 삭제하거나, 멤버를 호출하는 등 보편적인 객체 지향 프로그래밍 환경에서 취할 수 있는 액션들을 모두 재정의할 수 있도록 추상화하고 있습니다.
여기서 주목할 점은, DynamicObject의 기능을 확장하기 위하여 Expression Parser를 같이 이용한다는 점입니다. 앞 예제에서 보여드린, 마치 JavaScript처럼 코드를 자유자재로 C# 코드를 확장한 예시를 예로 들어본다면, Dynamic 컨텍스트 내에 서술한 C# 코드 조각은 Expression Parser에 의하여 처리되어 Expression 클래스의 인스턴스로 변환됩니다. 이렇게 변환된 객체는 DynamicObject에 미리 정의된 메서드들에 전달되는 것입니다.
다음은 Expression 객체를 사용하는 예시 코드입니다. (MSDN에서 발췌함)
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq.Expressions;
// The class derived from DynamicObject.
public class DynamicNumber : DynamicObject
{
// The inner dictionary to store field names and values.
Dictionary dictionary
= new Dictionary();
// Get the property value.
public override bool TryGetMember(
GetMemberBinder binder, out object result)
{
return dictionary.TryGetValue(binder.Name, out result);
}
// Set the property value.
public override bool TrySetMember(
SetMemberBinder binder, object value)
{
dictionary[binder.Name] = value;
return true;
}
// Perform the binary operation.
public override bool TryBinaryOperation(
BinaryOperationBinder binder, object arg, out object result)
{
// The Textual property contains the textual representaion
// of two numbers, in addition to the name
// of the binary operation.
string resultTextual =
dictionary["Textual"].ToString() + " "
+ binder.Operation + " " +
((DynamicNumber)arg).dictionary["Textual"].ToString();
int resultNumeric;
// Checking what type of operation is being performed.
switch (binder.Operation)
{
// Proccessing mathematical addition (a + b).
case ExpressionType.Add:
resultNumeric =
(int)dictionary["Numeric"] +
(int)((DynamicNumber)arg).dictionary["Numeric"];
break;
// Processing mathematical substraction (a - b).
case ExpressionType.Subtract:
resultNumeric =
(int)dictionary["Numeric"] -
(int)((DynamicNumber)arg).dictionary["Numeric"];
break;
// In case of any other binary operation,
// print out the type of operation and return false,
// which means that the language should determine
// what to do.
// (Usually the language just throws an exception.)
default:
Console.WriteLine(
binder.Operation +
": This binary operation is not implemented");
result = null;
return false;
}
dynamic finalResult = new DynamicNumber();
finalResult.Textual = resultTextual;
finalResult.Numeric = resultNumeric;
result = finalResult;
return true;
}
}
class Program
{
static void Main(string[] args)
{
// Creating the first dynamic number.
dynamic firstNumber = new DynamicNumber();
// Creating properties and setting their values
// for the first dynamic number.
// The TrySetMember method is called.
firstNumber.Textual = "One";
firstNumber.Numeric = 1;
// Printing out properties. The TryGetMember method is called.
Console.WriteLine(
firstNumber.Textual + " " + firstNumber.Numeric);
// Creating the second dynamic number.
dynamic secondNumber = new DynamicNumber();
secondNumber.Textual = "Two";
secondNumber.Numeric = 2;
Console.WriteLine(
secondNumber.Textual + " " + secondNumber.Numeric);
dynamic resultNumber = new DynamicNumber();
// Adding two numbers. The TryBinaryOperation is called.
resultNumber = firstNumber + secondNumber;
Console.WriteLine(
resultNumber.Textual + " " + resultNumber.Numeric);
// Subtracting two numbers. TryBinaryOperation is called.
resultNumber = firstNumber - secondNumber;
Console.WriteLine(
resultNumber.Textual + " " + resultNumber.Numeric);
// The following statement produces a run-time exception
// because the multiplication operation is not implemented.
// resultNumber = firstNumber * secondNumber;
}
}
// This code example produces the following output:
// One 1
// Two 2
// One Add Two 3
// One Subtract Two -1
Expression 객체를 받아서, 이항 연산을 수행하는 부분을 살펴보면, 언어의 종류에 관계없이 Expression Parser를 이용하여 처리된 구문 분석 결과를 사용한다는 것을 알 수 있습니다. 즉, C#이나 Visual Basic .NET 같은 기본 런타임 언어 뿐만 아니라, IronPython과 같은 언어에도 대응이 가능함을 뜻합니다. 이러한 기능을 활용하여, 메서드의 추가 없이 언어의 특성 만으로 원하는 기능까지 이끌어내는 것이 좀 더 손쉽습니다. 예를 들어, 별도의 메서드 호출 없이 문자열 간 이항 연산 중 더하기 연산에서 HTML에 대응할 수 있도록 <BR /> 태그를 자동으로 덧붙이는 기능을 생각해 볼 수 있을 것입니다.
2. Anonymous Type과 ExpandoObject
C# 3.0에서는 익명 형식이 도입되었습니다. 이것은 전적으로 LINQ와 같이 동적으로 데이터 형식을 확장해야 하는 시나리오에 최적화된 기술로, LINQ를 위한 새로운 클래스를 작성하는 수고를 덜어주게 됩니다. (Where 절이나 Select절에서 여러 조건을 지정하는 시나리오를 생각하시면 쉽습니다.)
그러나 지금 소개하는 ExpandoObject는 어떤 시점에서 임시로 필요한 형식을 개설하는 일과는 별도로, 전형적인 스크립트 언어의 동적 형식을 구현한 것으로, 익명 형식이 컴파일러에 의하여 동적으로 추가되지 않고, 런타임에서 키와 값의 쌍으로 이루어진 Dictionary Type의 객체를 만드는 것이 조금 다릅니다. 이러한 기능 자체는 이미 C# 초기 버전부터 제공되어오던 Hashtable이나 C# 2.0의 Generic 버전의 Dictionary<TKey, TValue> 형식을 이용하는 것과 개념상 유사하지만, 문법적인 관점에서 좀 더 업그레이드 된 형태로 이해할 수 있습니다.
아래는 ExpandoObject를 Dynamic Context 아래에서 활용하는 예시입니다.
class Program
{
static void Main(string[] args)
{
dynamic employee, manager;
employee = new ExpandoObject();
employee.Name = "John Smith";
employee.Age = 33;
manager = new ExpandoObject();
manager.Name = "Allison Brown";
manager.Age = 42;
manager.TeamSize = 10;
WritePerson(manager);
WritePerson(employee);
}
private static void WritePerson(dynamic person)
{
Console.WriteLine("{0} is {1} years old.",
person.Name, person.Age);
// The following statement causes an exception
// if you pass the employee object.
// Console.WriteLine("Manages {0} people", person.TeamSize);
}
}
// This code example produces the following output:
// John Smith is 33 years old.
// Allison Brown is 42 years old.
그리고 아래의 코드는 Dictionary의 경우와 마찬가지로, ExpandoObject의 모든 멤버들을 Reflection 없이 나열하는 것입니다. (내부적으로 Dictionary와 같은 개체를 사용하여 멤버들을 관리하고 있으므로 Reflection을 이용하지 않는것으로도 볼 수 있습니다.)
dynamic employee = new ExpandoObject();
employee.Name = "John Smith";
employee.Age = 33;
foreach (var property in (IDictionary)employee)
{
Console.WriteLine(property.Key + ": " + property.Value);
}
// This code example produces the following output:
// Name: John Smith
// Age: 33
ExpandoObject는 문법적으로 Dictionary 컬렉션을 확장한 것으로 이해할 수 있기 때문에, 확정적이지 않은, 변동 가능성이 있는 다수의 매개 변수를 복잡하지 않게, 문법적으로 융합된 형태로 코드를 작성하기 수월하게 합니다. Dictionary 컬렉션 개체를 생성하고 넘기는 동작을 미화한 것으로 볼 수 있을 것입니다.
Visual Studio 2010 베타 2가 이 글을 쓰는 현 시점에 Express Edition과 함께 새롭게 공개되었습니다. .NET Framework 4.0과 더불어서 C# 4.0, Visual Basic .NET 10.0 등 언어들의 기능도 새롭게 향상되었으며, 특히 이목을 끄는 것은 Dynamic Language Runtime과 연동되는 Dynamic Programming에 관한 것입니다.
Dynamic Programming에 대해서, 철수네 소프트웨어 세상 블로그 (http://blogs.msdn.com/bkchung/)에 소개된 내용을 오래 전에 처음 포스팅을 했었지만 최근까지도 dynamic 키워드의 실용적인 사례를 찾지 못하고 있었습니다만, 최근 dynamic 키워드의 새로운 활용 가능성을 새롭게 발견하였습니다. 여러 시나리오가 있을 수 있겠지만 제가 오늘 이 포스트를 통해서 조명해보고자 하는 것은 Windows Forms가 제공하는 Web Browser와의 연동에 관한 것입니다.
Windows Forms가 버전 2.0에 들어서면서부터 매우 실용적인 컴포넌트를 새롭게 추가하였는데 바로 웹 브라우저 컨트롤이 그것이었습니다. 웹 브라우저 컨트롤은 단순히 브라우저로서도 활용할 수 있지만 잘 활용한다면 사용자 인터페이스를 웹 기반으로 업그레이드하는 것 또한 가능합니다.
하지만 COM Interop 프로그래밍에 기댈 수 밖에 없는 부분은 여전히 많았고, 당시의 C# 언어 사양만으로 COM Interop을 다루는데에는 비효율적이고 생산성이 떨어지는 부분이 많았습니다. 그런 이유로, 예전의 브라우저 컨트롤보다는 "조금 뛰어난" 수준으로 인지되는데에 그쳤습니다. 하지만 이번에 새로 소개되는 Dynamic Programming과 결합하면 매우 매력적인 프레임워크로 다시 거듭나게 됩니다.
본디 이 스크립트가 하는 일은 Internet Explorer가 고유하게 제공하는 웨이브 필터를 활용해서 텍스트를 화려하게 보일 수 있도록 치장하는 것입니다. 그러나 이 기능을, 여러 브라우저가 보는 페이지로서가 아닌, 온전히 Windows Forms 응용프로그램만을 위한 고유한 기능으로 바꾸어보도록 하겠습니다.
폼에 웹 브라우저 컨트롤을 새로 추가합니다.
웹 브라우저 컨트롤이 처음 보일 페이지를 about:blank 로 설정합니다. 빈 문서를 준비시키도록 하는데에 도움이 됩니다.
Timer timer = new Timer(); timer.Tick += (a, b) => DynWave(); timer.Interval = 50; timer.Enabled = true; }
위의 코드에서 우리가 관심있게 봐두어야 할 부분은 dynamic으로 선언한 변수 뒤에 나타나는 표현식들입니다. 컴파일러 기반의 언어들은 늘 그렇듯 문법의 정확성을 언제나 따지게 됩니다. C#도 분명히 그런 언어였습니다. 하지만 Dynamic Programming을 통하여 위와 같이 컴파일 시점에는 존재하지 않는 내용을 런타임 때에 유추해낼 수 있게 되었습니다.
여기서 의문점이 드는 것이 있는데, 그렇다면 이를 유추해내기 위해서 어떤 식으로 구현을 제공하는가에 대한 부분입니다. 이 부분에 대한 답은 바로 .NET Framework가 런타임 과정에서 정확한 객체 정보를 확인하기 위해서 사용하는 ObjectBinder에 있습니다. regasm 유틸리티로 등록해두었던 COM Interop Assembly나 Primary Interop Assembly에 대한 내용을 런타임 차원에서 관리하고, 이를 자동으로 연결하고 있는 것입니다. 직접 프로젝트에서 참조를 하지 않았다고 할지라도 그러합니다.
그리고 이러한 기능 위에 DLR과 더불어서 이미 LINQ에서 소개된 적이 있는 Expression Tree 해석 엔진에 의하여 C# 기준으로 작성된 코드를 Object Binder가 가지고 있는 정보와 일치시켜 정확한 기능을 하는 실제 코드를 컴파일 과정에서 작성하게 되는 것입니다. 이것은 형식 정보를 읽어서 매번 같은 컬렉션을 여러번 반복하면서 조회하는 Reflection 기반의 프로그래밍보다 훨씬 빠르게 동작하고, 훨씬 정확도가 높습니다.
위의 코드를 실행한 결과는 아래와 같습니다. 중간에 어떤 메시지를 표시할 것인지를 묻는 브라우저의 프롬프트 창에 원하는 메시지를 넣어보는 것도 괜찮을 것입니다.
C#을 제 포트폴리오상의 주력 프로그래밍 언어로 삼은지 벌써 수 년이 지났고 어느덧 C#의 네 번째 버전이 베일을 벗고 있습니다. C#을 처음 접할 때만 하더라도 Cava 라는 웃지 못할 별명 (C + Java)이 꼬리표처럼 따라 다녔지만 이제는 C#은 Java와 다르다는 것을 증명해 나가고 있고 C# 3.0 이후로는 더 이상 Cava라는 이름이 따라다니는 불행이 없다는 것이 참 좋았습니다. :-)
이번 C# 4.0에서는, 제가 이전에 C# 3.0에서 우려했던 var 키워드의 모습을 실제로 구현하는 dynamic 키워드가 등장합니다. var 키워드가 컴파일 타임에서의 유추에 의존하는 것이면 dynamic 키워드는 예전의 Variant 키워드를 연상하게 하는 내용입니다. 하지만 예전에 스크립트 프로그래밍을 구현하던 때와는 중요한 차이점이 있는데, 바로 Reflection의 유무입니다.
Reflection이 없었을 때의 Duck Typing은 사치의 절정이었습니다. 메모리 소모량도 크고 불안정하기까지 했으니까요. 하지만 .NET Framework가 제시하는 Duck Typing은 개념이 많이 다른데, 그 기반에는 Reflection과 DLR (Dynamic Language Runtime)이 저변에 있습니다.
DLR은 이미 Iron 시리즈 (IronPython, IronRuby 등)에서 핵심 엔진으로 채택하여 사용 중인 Microsoft의 스크립트 언어용 런타임입니다. C# 4.0은 DLR에 대한 호환성을 높일 목적으로 dynamic 키워드를 이들 스크립트 언어에서 사용하는 것과 같은 의도로 사용할 수 있게 해줍니다. 다음의 예를 보기로 하겠습니다.
dynamic calc = GetCalculator(); int sum = calc.Add(10, 20);
단정을 하고 본다면 위의 코드에서 calc는 object 형식과 다르지 않으며, 컴파일러 수준에서 Reflection으로 Add 메서드를 찾아내고 동적 파라미터로 10과 20을 배열로 구성하여 호출한 결과를 다시 가져온 것입니다. 하지만 언어의 확장과 DLR이 미리 제공하는 인터페이스인 IDynamicObject 인터페이스를 구현하여 목적에 맞고 좀 더 빠르게 동작하도록 확장할 수 있습니다.
object calc = GetCalculator(); Type calcType = calc.GetType(); object res = calcType.InvokeMember("Add", BindingFlags.InvokeMethod, null, new object[] { 10, 20 }); int sum = Convert.ToInt32(res);
특히 주목해야 할 것은 IDynamicObject 인터페이스를 이용하여 기존의 COM Interop 프로그래밍은 형 변환 작업 없이, 오버헤드 없이 손쉽게 프로그래밍을 할 수 있게 됩니다. C# 이야기는 아니지만 이러한 수혜를 바탕으로 Visual Basic .NET의 차기 버전도 더욱 향상된 Lazy-Binding 프로그래밍 기술을 가지게 될 것으로 봅니다.
요약하면, 이번 C# 4.0은 동적 프로그래밍 언어로서의 면모를 갖추는 본격적인 단계로 정의내릴 수 있습니다. 다음 Article에서는 C# 4.0이 가지는 또 다른 파격적인 변화 하나를 소개하겠습니다.