안녕하세요. 늦바람이 무섭다고 하는데요. jQuery를 향한 늦바람이 불어주길 바라는 1인입니다. ㅎㅎ
이렇게 간단해도 되는겨?
이번 포스팅을 준비하면서 정말 jQuery의 놀라운 힘에 다시 한번 놀랐습니다. 이렇게 간단히 탭메뉴를 넣는게 가능했던건가요?
준비물 준비
먼저, jQueryUI 사이트에서 jquery-ui-1.8.2.custom.zip 파일을 다운받습니다. 압축을 푸시면 jquery-ui-1.8.2.custom.min.js 와 jquery-ui-1.8.2.custom.css 파일이 있습니다.(각각 js폴더와 css폴더에 있습니다.) 이 두 파일을 프로젝트의 Content와 Scripts 폴더에 추가시킵니다. 이제 준비는 됐고요.
준비끝! 예제로!
Index.aspx 페이지 소스입니다.
<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
홈 페이지
</asp:Content>
$("#tabs").tabs() 이게 바로 그 놀라운 능력을 가진 탭메뉴를 가능케하는 힘입니다. (자세한 것은 다음으로 미루고~ 언제가 될지는 몰라요. 그냥 잘 쓰면 되는거죠^^;;)
아. 추가시켰던 두 파일은 마스터페이지에 끌어다놨습니다.
이제 Html.ActionLink() 에 걸린 액션들만 만들어 주면 됩니다.
요청 컨트롤러입니다. 첫 Index() 액션 메쏘드를 보시면 dynamic 이라는 타입이 보이는데요. 처음에는 Contact()와 마찬가지로 PartialView()만 리턴을 하였는데, 파샬뷰를 생성해 놓고 보니.
저렇게 dynamic 이 눈에 딱 띄는바람에 어쩔수(?) 없이 dynamic데이터를 전달하게 되었습니다. 간단히 설명드리면 dynamic은 대인배의 마음 씀씀이를 갖고 있어서 어떤 타입이던지 모두 수용합니다.(컴파일타임에는 터치를 안합니다. 귀찮아서 런타임한테 넘기는거죠;;) dynamic으로 선언된 변수에 멤버, 메쏘드, string, int 가리지 말고 막 넣어주세요. 다 받아줍니다. 하.. 저도 대인배로 살아가야 할텐데 참.. 아쉽습니다 :)
다음 기회에 dynamic에 대해 좀더 자세히 알아보면 좋겠네요.
다시 본론으로 넘어와서, /Product/Index.ascx 를 보시면
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<dynamic>" %>
<h3><%: Model.Message %></h3>
<p>
ASP.NET MVC에 대한 자세한 내용을 보려면 <a href="http://asp.net/mvc" title="ASP.NET MVC 웹 사이트">http://asp.net/mvc</a>를 방문하십시오.
</p>
Model객체의 Message의 접근하면(Model.Message) Index 메쏘드에서 넘겨준 다이나믹한 메시지를 받을 수 있습니다.
너무 간단해서 할말을 잃게 만들죠. foreach문을 통해 루프를 돌면서 데이터를 출력합니다.
나머지 Contact.ascx는 안보셔도 됩니다.^^;
자, 완료가 되었으니 확인을 해보죠.
페이지 로드 없이 깔끔하게 탭기능이 완성되었습니다.
마무리요
일반적인 페이지(aspx)도 탭메뉴로 가능합니다. 파샬뷰를 사용한 것은 한 페이지 전체보다 불필요한 부분을 제거한(head, html, body가 보시다시피 파샬뷰에는 존재하지 않습니다.) 간결함때문이랄까요? ㅎㅎ
다음은 더 재미있는 것으로 찾아뵙겠습니다. (지금은 재밌다는겨? 뭐여? ㅡ.ㅡ 이렇게 생각하시는 분은 제발 없으시길 바래요^^)
jQuery is a new kind of JavaScript Library.
jQuery is a fast and concise JavaScript Library that simplifies HTML document traversing, event handling, animating, and Ajax interactions for rapid web development. jQuery is designed to change the way that you write JavaScript.
쭉 보면, jQuery는 자바스크립트 라이브러리의 한 종류입니다. jQuery는 신속한 웹 개발을 위한 HTML 문서 탐색, 이벤트 처리, 애니메이션, Ajax와의 상호작용을 간단하게하는 빠르고 간결한 자바스크립트 라이브러리입니다. jQuery는 자바스크립트 작성 방식의 전환을 위해 설계되었습니다.
다른건 몰라도, 암튼 웹 개발을 빠르게 해준다니까 오케이입니다. 귀찮은 작업도 간결하게 해주는 것 같고요.
Visual Studio 에 jQuery가 탑재되어있는 것도 마이크로소프트가 이를 지원한다는 얘기? 그래서 오케이. 스캇 구쓰리의 블로그를 보시면 계속 jQuery 플러그인 얘기가 올라오고 있는 것으로 봐서 활발하게 개발중인 것 같습니다.
간단 예제
정말 간단한 예제를 한번 살펴보도록 하겠습니다. 실망하시면 안~되요.
지금 하려는 것은 'ASP.NET MVC에 대한 자세한 내용을 보려면...' 이 있는 p 태그의 스타일을 변경해 볼겁니다. 먼저 MVC 프로젝트를 새로 생성하겠습니다. 그 다음, Index.aspx 페이지에 Contents 폴더에 있는 jquery-1.4.1.js 파일을 끌어다 놓습니다. '$(' 입력해보시면 놀랍게도 정말 놀~랍게도 인텔리센스를 지원해주는 것을 확인하실 수 있습니다. 멋지죠? :-)
아. 이거할때가 아닌데 좋아하고 있었네요. 먼저 필요한 스타일을 추가하겠습니다. 간단합니다.
지난 아티클에서 Visual Studio 2008 과 Visual Studio 2010 을 동일한 소스 코드와 프로젝트로 개발하기 위한 환경을 구성하는 방법을 알아보았습니다.
문제 원인
하지만 지난 시간에 언급한 듯이 테스트 프로젝트가 포함된 경우는 VS2008 과 VS2010 을 동시에 사용할 수 없는 문제가 발생합니다. 그 이유는 Microsoft.VisualStudio.Quality 프레임워크가 개선이 되고, Microsoft.VisualStudio.TestTools 프레임워크가 도입되면서 기존의 테스트 프레임워크와 비호환적인 부분이 존재하게 됩니다.
아래와 같이 VS2008 에서 작업한 테스트 프로젝트가 있을 경우,
지난 아티클의 방법으로 프로젝트를 변환하게 되면 아래의 오류가 발생합니다.
기존 프로젝트는 기존의 .NET Framework 버전을 그대로 사용할 수 있지만, 테스트 프로젝트는 반드시 .NET Framework 4.0 으로만 사용할 수 있습니다.
만약 하위 프레임워크 버전인 .NET Framework 3.5 로 변경하고자 할 경우 아래와 같은 오류 메시지가 나타나고, 다시 .NET Framework 4.0 버전으로 변경이 됩니다.
일부 이미 완성된 테스트 프로젝트인 경우 이것보다 더 다양한 오류 메시지를 볼 수 있습니다.^^;
어쨌든, Microsoft.VisualStudio.QualiltyTools 프레임워크가 .NET Framework 4.0 버전으로 고정되어 자칫 테스트 프로젝트가 굉장히 큰 우범을 저지를 수 있는 문제가 될 수 있습니다.
반대로 Visual Studio 2010 에서 만든 테스트 프로젝트는 .NET Framework 4.0 이 기본이고 Microsoft.VisualStudio.QualityTools 프레임워크와 Microsoft.VisualStudio.TestTools 프레임워크가 기본 참조(RTM 에서는 제외 됨)이며, 이 프레임워크의 버전도 4.0.xxxxx 버전이라 하위 버전과 호환되지 않습니다.
VS2010 이든 VS2008 이든 테스트 프로젝트가 한번 VS2010 으로 업그레이드 되었다면, VS2008 에서 테스트 프로젝트를 사용하기 위해서는 다운그레이드는 그리 쉽지 만을 않습니다. 왜냐하면 VS2008 에서 테스트 프로젝트를 로드하면 프로젝트의 참조가 깨져있습니다.
그 이유는 .csproj 파일을 열어보면 답이 나오는데요. 아예 Microsoft.VisualStudio.QualityTools 프레임워크의 버전 번호를 명시하여 해당 VS2008 에서는 어셈블리 리디렉션(Assembly Redirection) 을 시키기가 좀 애매해 집니다.
문제 해결 기본 지식
이 문제를 해결하기 위해서는 테스트 프로젝트간의 비 호환적인 테스트 프레임워크로 인한 문제이므로, 문제를 해결하기 위한 접근 측면에 제한을 둘 수 밖에 없습니다. Visual Studio 2010 에서는 Coded UI, Test Impact 등 새로운 기능이 추가되었고, 기존 테스트 또한 비주얼한 부분이 개선이 되면서 강제적으로 테스트 프레임워크의 버전을 .NET Framework 4 로 고정을 시키는 것 같습니다.
이 문제는 MSBuild 를 통해 해결하기 위한 기본적인 지식을 알려드립니다. 여러분이 알다시피 MSBuild 는 Microsoft 의 통합 빌드 솔루션입니다. 예전에는 "빌드"라는 말 대신 "컴파일"이라는 단어를 사용했었죠. 컴파일이란 소스 코드를 목적 파일 또는 실행 파일로 변환하는 과정을 "컴파일"이라고 합니다.
컴파일과 빌드를 비교하는 아주 간단한 그림 입니다.
컴파일은 목적은 소스 코드를 목적 파일로 변환하여 실행 파일 또는 라이브러리로 만들기 위한 목적입니다.
빌드는 컴파일의 일련의 과정을 플로우(Flow) 로 처리하여 컴파일 중에 더 많은 작업을 하기 위한 목적입니다.
가장 대표적인 빌드 솔루션이 MSBuild 이며, 이 외에 Ant 또는 NAnt 등이 바로 이러한 솔루션입니다. 그리고 Team Foundation Server 의 팀 빌드도 바로 MSBuild 에 기반하고 있다는 것입니다.
필자 또한 MSBuild 를 접하면서 나의 지식의 끝을 무한하게 확장해 주었던 것이 MSBuild 입니다. MSBuild 는 정적인 컴파일 방식에서 동적인 방식의 빌드로 거듭나면서 굉장히 많은 가능성을 보여주는 부분이기도 합니다. Microsoft 의 MSBuild 의 대략적인 구조는 아래와 같습니다.
기본적으로 MSBuild 는 Task 의 집합이라고 해도 과언이 아닙니다. 그리고 이 Task 중에 빌드와 연관된 Task 도 있습니다. 이 Task 를 .NET Framework 버전에 따라 Project References(프로젝트 참조)를 변형시키는 방법입니다.
해결 방법
이 테스트 프로젝트를 VS2008, VS2010 양 쪽에서 사용하도록 하기 위해서는 이 어셈블리 참조를 동적으로 변화시킬 필요가 있습니다. 이 방법도 MSBuild 의 Choose 라는 조건문으로 제어를 분기할 수 있는 방법입니다.
1. 먼저 솔루션 탐색기에서 열려 있는 프로젝트를 언로드 한 후, 편집을 클릭합니다.
2. 그럼 아래와 같이 참조와 관련되어 있는 부분이 ItemGroup 요소에 있는 것을 확인할 수 있습니다.
3. 이 ItemGroup 에서 VS2008, VS2010 에서 공통적인 참조 어셈블리를 별도의 ItemGroup 으로 분리합니다. 그럼 아래와 같은 형태가 되겠지요?
4. 테스트와 관련된 ItemGroup 에 Choose 조건 분기 요소를 사용하여 조금 변형해 봅시다. .NET Framework 의 버전 별로 말이죠.
위의 $(MSBuildBinPath) 는 실제로 빌드가 수행할 때의 MSBuild 의 경로를 나타냅니다. 하지만 여기에는 한 가지 함정이 있습니다. Visual Studio 2008 에서는 <Message Text="$(MSBuildBinPath)" /> 가 아래와 같이 C:\Windows\Microsoft.NET\Framework\v2.0.50727 로 나타납니다. 하지만 내부적으로 이 MSBuild 는 v3.5 경로의 MSBuild.exe 를 실행하게 됩니다. 자세한 이유와 내막은 Microsoft.Common.targets 파일을 뒤져보시면 아실거라고 생각합니다.
그리고 Choose 조건 분기 요소는 if ~ else 와 같은 구문입니다. ItemGroup 요소는 하나의 항목을 담는 필드라고 보시면 되고, PropertyGroup 은 한 Property 에 여러 항목을 담는 속성이라고 보시면 됩니다. 이 부분은 MSBuild 를 공부해 보시면 어렵지 않는 기본적인 부분이니 자세한 설명은 여기에서 하지 않겠습니다.
5. 모두 완료 되었습니다. 각각의 VS2008, VS2010 에서 테스트 프로젝트를 모두 사용할 수 있게 되었습니다.
만약 Coded UI 와 같은 VS2010 의 새로운 기능을 사용할 경우 아래와 같이 추가적인 어셈블리를 참조하게 됩니다.
이 경우도 위의 4번과 같이 ItemGroup 의 VS2010 용 어셈블리를 아래와 같이 넣어버리면 됩니다.
그럼 VS2008 인지 VS2010 인지에 따라서 참조 어셈블리가 완벽하게 분리가 됩니다.
하지만 VS2008 에서 빌드를 할 경우 아래와 같이 오류가 발생하게 됩니다. 당연히 VS2008 에서는 Coded UI 등에서 필요한 Microsoft.VisualStudio.TestTools 프레임워크가 존재하지 않고, 이 프레임워크를 재사용하기 힘들기 때문입니다.
하지만 이 문제로 해결해 볼까요? 위에서 Property 를 재정의한 구문이 생각나실 겁니다. 전처리 지시문의 상수 값으로 사용되는 <DefineConstants> 에 NET4.0 빌드인지, NET3.5 빌드인지 알 수 있도록 상수 값을 선언하였습니다.
이 상수 값을 이용하여 CodedUI 등 VS2010 에서 새로 추가된 부분에, #if ~ #endif 지시문을 사용하여 감싸 주시면 됩니다.
이제 Visual Studio 2008 이든 Visual Studio 2010 이든 테스트 프로젝트를 양 쪽 어떤 도구를 사용하든 테스트가 가능하도록 구성하는 방법을 완료하였습니다.
WM7을 제대로 공부할려고 하다보니, 제가 WM7을 제법 잘못 알고 있다는 것을 알게 되었습니다. 그래서 저 같은 분들을 위해서, 실제로 WM7 개발을 시작하기 전에 간단하게 정리를 한번 할려고 합니다.
먼저 설치 환경입니다.
OS
Windows Vista 이상
Tools
Visual Studio 2010 express for windows phone CTP
- 기존에 visual Studio 2010 다른 버전을 설치하지 마시길 권장합니다.
Language
1. Silverlight
2. XNA Framework
Native는 불가능하고 위의 두 가지를 이용할 수 있습니다. 일반 어플은 Silverlight를 게임은 XNA 플랫폼을 이용할 수 있습니다.(전, Silverlight 닮은 꼴인줄 알았는데 그냥 Silverlight 입니다. 즉, C,C++ Native 개발자보다, Silverlight를 아는 사람이 더 개발에 유리합니다.)
Hardware Spec
1. 800x480 WVGA
2. 4-point multi-touch screen
3. Direct-X 9 Support
4. A-GPS, accelerometer, compass, light, proximity
, phone’s location Service, Digital Camera
5. Start, Search, Back Button
6. 3G and Wi-fi
7. over 256MB RAM, over 8GB flash Storage
원래 Windows Moblie 의 특징은 (정확히는 CE지만) 크게 하드웨어의 제약을 주지 않았지만, WM7 부터는 WM7로 인증을 받기 위해서는 하드웨어의 제약이 생겼습니다. 그리고 프로그램 설치도, MS 마켓을 통해서도 개발이 가능합니다.
이런 부분들이 WM7의 장점이 될지, 단점이 될지는 아직 알 수 없지만, WM7의 기본 UI,UX 나, 개발 환경은 역시 MS 다 라는 생각이 들게 해주는 것 같습니다.
지금까지는 닷넷 4.0에 추가된 TPL과 PLINQ를 통해서 멀티 스레드 프로그래밍을 하는 방법을 살펴봤습니다. 그러면, 잠깐 추억을 되살릴겸, 뭐가 어떻게 달라졌는지도 한번 비교해 볼겸 해서, 닷넷 3.5까지의 멀티 스레드 프로그래밍 방법을 잠깐 살펴보도록 하겠습니다. 호호호호
- Thread와 다이다이로 작업하던 시절.
TPL은 System.Threading.Tasks를 사용해서, ThreadPool을 내부적으로 사용한다고 말씀을 드렸었습니다. 하지만, 그것 닷넷 4.0이나, 닷넷 3.5에서는 Reactive Extension(Rx)을 통해서 추가적으로 지원하는 기능이구요. 그 이전에는 직접적으로 Thread나 ThreadPool을 이용해서 프로그래밍 해야 했습니다. 그럼 Thread를 직접 사용하던 코드를 예제로 한번 보시죠.
using System; using System.Threading;
namespace Exam18 { class Program { static readonly int max = 10000;
//현재 작업중인 스레드에서도 반복문 시작 for (int count = 0; count < max; count++) { Console.Write("-"); } Console.WriteLine("메인 스레드 끝");
//혹시 현재 스레드가 빨리 끝나더라도, //추가 스레드가 끝날 때 까지 기다리기. thread.Join(); } } }
<코드1> Thread와 다이다이로.
<코드1>을 보면, 맨 처음에 Task를 소개해드리면서 사용했던 예제를 Thread를 사용하도록 바꾼 코드입니다. 차이점이 있다면, ThreadStart타입의 델리게이트를 사용해야 한다는 것과, Wait()메서드가 아니라 Join()메서드를 사용한다는 것이죠. 결과를 보시면, Task를 사용했던 것과 동일합니다.
---------|||||||-|||||||||||--------------|||||||||-------------|||||------|||||||||||--------- -||||||||--------|||||||||||||-----메인 스레드 끝|||||||||||||||||||||||||||||||||||||||||||||| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| |||||||||||||||||||||||||||||||||||||||||||||||||||추가 스레드 끝 계속하려면 아무 키나 누르십시오 . . .
<결과1> Thread를 사용한 결과.
그리고 Thread를 보면, Task와 마찬가지로 실행을 제어할 수 있도록 몇가지 속성을 제공하는데요, 그 목록을 보면 아래와 같습니다.
속성
설명
Join()
추가 스레드가 완료되기 전에 메인 스레드가 완료되면, 추가 스레드가 하던 작업은 다 날아간다. 그래서 추가 스레드의 작업이 완료될 때까지 메인 스레드가 기다리도록 한다.
IsBackground
이 속성은 기본적으로 false이다. 즉, 스레드는 기본적으로 foreground작업인데, 그 때문에 스레드가 완료되기 전까지는 프로세스를 종료시킬 수 없다. 이 속성을 true로 주면, 스레드의 작업이 완료되기 전에도 프로세스를 종료시킬 수 있다.
Priority
Join메서드를 사용한 경우에, 이 속성을 통해서 스레드의 우선순위를 바꿀 수 있다.
ThreadState
이 속성을 통해서 스레드의 상태를 파악할 수 있는데, Aborted, AbortRequested, Background, Runnging, Stopped, StopRequested, Suspended, SuspendRequested, Unstarted, WaitSleepJoin등의 상태 값을 얻을 수 있다.
Thread.Sleep()
현재 실행 중인 스레드의 실행을 명시한 시간만큼 일시정시 시키는 메서드이다.
Abort()
이름 그대로, 스레드를 중지시키는 메서드. ThreadAbortException이 발생된다.
<표1> Thread의 속성.
위의 Thread멤버 중에서, Task에도 있는 건, Join()과 ThreadState뿐입니다. 왜 그럴까요? 일반적으로 권장되지 않는 것들이기 때문이죠. 그래서 닷넷 프레임워크 4.0으로 프로그래밍 할 때는, 위에서 언급한 것들 중에서 Task에 없는 속성들을 될 수 있으면 사용하지 말아야 합니다.
- ThreadPool을 사용해보자.
ThreadPool을 사용하면, 새로운 스레드를 계속 해서 생성하기 보다 기존에 있는 스레드를 재활용해서 추가적인 스레드 생성을 막을 수 있습니다. 참고로, TPL이 내부적으로 ThreadPool을 사용한다고 말씀드렸었죠? 그럼 ThreadPool을 사용하는 예제도 한번 보시죠.
using System; using System.Threading;
namespace Exam19 { class Program { static readonly int max = 10000;
//혹시 현재 스레드가 빨리 끝나더라도, //추가 스레드가 끝날 때 까지 기다리기. Thread.Sleep(1000); } } }
<코드2> ThreadPool을 사용한 코드.
<코드2>를 보시면, ThreadPool을 사용하고 있는데요. QueueUserWorkItem메서드를 통해서 작업을 추가하고 있습니다. 그러면, 자동으로 스레드를 활용해서 작업을 시작하게 되구요. 결과는 앞선 예제와 동일합니다. 그런데, ThreadPool을 사용할 때 장점만이 있는 건 아닌데요. 작성한 코드외에도 다른 라이브러리등에서 내부적으로 시간이 많이 걸리는 I/O작업 등에 ThreadPool을 사용한다면, 그 작업이 끝날 때까지 기다려야 하거나, 심한 경우에는 데드락이 발생하기도 합니다. 그리고 Thread나 Task를 사용할 때와는 다르게 ThreadPool은 실행 중인 작업에 접근할 수 있는 방법이 없습니다. 그래서 실행 중인 작업을 조종한다거나, 상태를 확인할 수 가 없죠. 그래서 <코드2>를 보시면, Join()이나 Wait()대신에, Thread.Sleep()메서드를 통해서 추가 스레드가 끝날 때까지 메인 스레드를 기다리게 합니다.
- 마치면서
오늘은 닷넷 3.5 까지의 멀티 스레드 프로그래밍 방법에 대해서 알아봤는데요. 크게 다른 모습은 없습니다. 다만, 좀 더 안전하고 간단한 방법을 제공하는 것이죠. 대한민국도 16강에 진출했는데 오늘은 여기까지 하시죠!...응??
- 참고자료
1. Essential C# 4.0, Mark Michaelis, Addison Wesley
네. 아직입니다. ^^; 원래는 마무리를 지으려고 했었는데요. 갑자기 jQuery 가 급땡기는 바람에 슬슬 관련글을 적어보렵니다.
클라이언트단에서 유효성검사하기
지난번 포스팅을 보시면, 서버단의 모델 클래스에 DataAnnotaion을 사용하여 유효성검사를 했습니다. 물론, 클라이언트단에서도 자바스크립트를 사용하여 유효성검사를 할 수 있지만, 이는 동일한 유효성 검사를 두번(서버와 클라이언트) 하게됩니다. DRY(Don't Repeat Yourself) 규칙에 위반되는 작업인 거죠.
근데 왜?
저 아시는 분 없죠? 듣보잡인거죠. 그래서 이렇게 앞뒤가 없습니다. 이번 포스팅을 먼저 했으면 하는 마음도 있지만, 뭐 이렇게 된 것 그냥 적어내려갑니다.^^
DRY에 반하는 작업을 한다고 너무 차가운 피드백은 달지 말아주세요; '이런 방법도 있는 거였군'이라는 생각만 가져주셨으면 좋겠습니다.
먼저, 지난번 유효성 검사를 했던 소스에 jQuery를 이용한 유효성 검사 스크립트를 추가하겠습니다.
프로젝트내의 Scripts폴더에 있는 jquery-1.4.1.js와 jquery.validate.js파일을 추가합니다.(프로젝트에 이런 스크립트 파일들이 자동으로 적용되어있는 것으로 봐서는 맘껏 사용하라는 거겠죠?^^; 아. 그리고 미니버전을 사용해도 되는 것은 다들 아시죠? *min.js)
소스를 보시면(빨간색) jQuery 스크립트와 유효성 검사를 위한 스크립트를 추가하였습니다. 또한 유효성 검사를 담당하는 jQuery 스크립트 구문도 추가하였습니다.
자, $("form").validate() 를 통해 유효성 검사를 합니다. 보시는대로, rules와 messages를 통해 에러를 표시하게되죠. Email을 제외한 각 필드를 필수값으로 세팅을 했고( required: true), 이름은 5자 이내(maxlength :5), 단축다이얼은 1~99까지의 숫자를 받도록(range[1,99]), 이메일은 이메일 형식을 체크(email: true)하도록 하였습니다. 이밖의 옵션들은 여기서 확인하실 수 있습니다.
빈값으로 폼을 전송하려고하면 클라이언트단에서 이에 제재를 가하게 됩니다.
이메일(필수값 아님)을 제외한 나머지는 에러가 났습니다. 올바른 값을 하나하나 입력하면 바로바로 에러메시지가 사라지는 것을 확인할 수 있습니다.
이메일을 잘못입력하면 에러메시지가 뜨는 것도 확인할 수 있습니다.
여기까지 잘 따라오셨으면 보다 싶게 클라이언트단에서의 유효성 검사를 진행해보죠. (윗부분은 이제 잊어도 좋습니다. 딱히 잊으라는게 아닌 아래 소스에서는 필요가 없어서.. 이렇게 말씀드리는건데...음.. '아 이런방법도 있구나'만 기억하시면 됩니다.^^;;)
DRY 잊지말자
지난번 포스팅에서는 서버단에서 유효성 검사를 하였기때문에 유효성 에러 메시지를 보려면 서버단까지 다녀와야할 필요가 있었습니다. 이를 가만히둘 마이크로소프트가 아닙니다. 정말 심플한 방법으로 손쉽게 클라이언트단과 서버단 두군데 모두 유효성검사를 할 수 있도록 하는 단 세줄의 코드가 있습니다.
위 세줄을 추가함으로 지난번 포스팅에서 DataAnnotation을 이용한 유효성 검사 로직을 클라이언트단에서도 사용할수 있게 되었습니다. 실행을 시킨 후, Create 버튼을 클릭하면 리로드없이 즉각적으로 에러메시지를 확인할 수 있습니다.
또한, 유효한 값을 입력하면 즉시 에러메시지가 사라집니다.
저희는 지금 클라이언트단에 유효성 검사 로직을 추가하지 않았습니다. 유효성 검사로직은 모델클래스에만 존재하고 있습니다. 하하하.(승리자의 웃음인거죠^^) 룰은 한 곳에다가 두고, 두군데(클라이언트와 서버)에서 모두 검사를 하도록 하였습니다. 이로써 DRY를 잊지 않은체 작업이 완료되었습니다.
마무리요
이렇게 손쉽게 클라이언트단에서도 검사가 가능한 방법이 있었습니다. ㅎㅎ 기분좋네요.
마이크로소프트는 현재 jQuery 프로젝트에 참여하여 계속 플러그인을 개발중에 있습니다. (이 얘기는 왜하는 걸까요? 음..) 이 부분에 대해서도 포스팅을 하도록 노력해보겠습니다.
Asynchronous Agents Library
– message 전달 함수. 1 ( 전송 )
작성자: 임준환( mumbi at daum dot net )
Message 메커니즘
Asynchronous Agents Library( 이하, AAL ) 에서 message 메커니즘이란 agent 들 간의 데이터 교환이나 동기화 등 상호 작용을 위해 사용되는 기능입니다.
Message 메커니즘은 크게 message 를 보내고( send ) 받는( receive ) 전달 함수( passing function )와 message 들을 관리하거나 message 에 특별한 기능을 부여하는 message block 으로 구성되어 있습니다.
실질적으로 message block 이 다루는 message 란 빌트인 자료 형( built-in type )이나 클래스와 같은 사용자 정의 자료 형( user define type ) 의 데이터입니다.
이 글에서는 먼저 message 를 주고 받는 전달 함수 중 전송 함수에 대해서 알아보겠습니다.
Message 전송
Message 전달 함수 중 보내는 기능을 하는 함수에는 동기 전송 함수 send() 와 비 동기 전송 함수 asend(), 이렇게 두 가지가 있습니다.
동기와 비 동기라는 용어가 혼란스러울 수 있기 때문에 잠깐 언급하고 넘어가겠습니다.
여기서 쓰이는 동기( synchronous )라는 용어는 병렬 처리에서 쓰이는 동기화( synchronization )라는 용어와는 약간은 다른 개념입니다.
동기화는 다른 두 시간을 하나로 일치시킨다는 뜻으로 행위를 말합니다. 반면 동기는 이미 동기화되었다는 뜻으로 상태를 뜻합니다. 마찬가지로 비 동기는 동기화되지 않았다는 뜻입니다.
즉, 보통 프로그래밍에서 동기 함수란 그 함수가 호출되고, 그 함수가 반환될 때까지 해당 컨텍스트가 진행되지 않고 기다리다가 반환되고 나서야 컨텍스트가 진행되는 함수를 말합니다. 이것은 사실 컨텍스트가 기다리는 것이 아니라, 해당 컨텍스트가 함수의 내용을 직접 처리하기 때문에 함수를 호출한 입장에서 보면 기다리는 것처럼 보이는 것입니다.
마찬가지로 비 동기 함수는 함수를 호출한 컨텍스트가 직접 함수의 내용을 처리하지 않고 새로운 작업 스레드를 생성하고 생성된 스레드의 컨텍스트가 진행되기 때문에 함수를 호출한 컨텍스트는 함수를 호출하자마자 함수의 반환을 받고, 계속해서 진행되는 것입니다. 함수를 호출한 컨텍스트는 이러한 비 동기 함수가 언제 실제로 종료될지 모르기 때문에 함수의 반환이 아닌 다른 기법이 필요합니다. 보통 폴링( polling )이나 메시지 또는 콜백 함수와 같은 기법을 사용하여 함수의 종료를 알 수 있습니다.
그럼 이제 본격적으로 두 message 전달 함수에 대해서 알아 보겠습니다.
동기 전송 함수 send()
앞에서 설명한 것처럼 send() 는 동기 함수이기 때문에 message 가 전송에 대한 결과가 확실해 졌을 때 반환됩니다. 즉, 전송된 결과가 확실할 때까지 기다린다는 뜻입니다.
#include <agents.h>
using namespace Concurrency;
int main()
{
// message block
unbounded_buffer< int > message_block;
asend( message_block, 1 );
Concurrency::wait( 10 );
}
[ 코드4. asend() 예제 ]
asend() 가 반환되었을 때에는 아직 message block 에 message 가 전송되지 않았습니다. 이것으로 asend() 가 비 동기 함수임을 확인할 수 있습니다.
약간의 시간( 10 milli second ) 이 지난 후에는 message block 에 message 가 전송된 것을 확인할 수 있습니다.
[ 그림2. asend() 예제 디버깅 화면 - 호출 직 후 ]
[ 그림3. asend() 예제 디버깅 화면 - 약간의 시간이 지난 후 ]
Message 필터
Message 전송 함수인 send() 의 반환 값이 전송 결과라고 하였고, 실패할 경우 false 를 반환한다고 하였습니다. 사실, 실패할 경우란 message 를 받는 message block 이 전송을 거부할 경우, 즉 필터링되었을 경우입니다.
결론적으로 send() 와 asend() 의 반환 값은 모두 message block 의 수락 또는 거절 여부입니다.
여기서 집고 넘어가야 할 부분이 언제 전송이 거부되는 것인가 하는 것입니다.
message block 은 두 가지 경우에 message 전송을 거부합니다.
첫째는 message block 이 파괴되어 소멸자가 처리되고 있을 때입니다. 당연한 상황입니다.
둘째는 message block 의 필터에 의해 message 가 거부당했을 때입니다. 모든 message block 의 생성자 중에는 filter_method 형의 매개변수를 갖는 생성자가 있습니다. filter_method 형은 사실 std::tr1::function<bool(_Type const&)> 입니다. message block 을 생성하는 클라이언트는 임의의 message 필터를 적용할 수 있습니다. 이 필터 함수가 false 를 반환할 경우, message 전송은 거부됩니다.
이 예제에는 message block 의 필터로 Visual studio 2010 에서 지원하는 C++0x 의 람다를 사용하였습니다. 람다는 이 글의 논제에서 벗어나기 때문에 설명하지 않도록 하겠습니다. Visual studio 팀 블로그에서 람다에 대한 정보를 얻을 수 있습니다.
간단히 람다에 대해서 설명하고 넘어가자면 익명의 함수 객체라고 보셔도 될 것입니다.
예제에 사용된 message block 의 필터는 짝수만 수락하는 필터입니다. 그래서 실행 결과로 send() 와 asend() 모두 홀수는 거부되었고, 짝수는 수락되는 것을 볼 수 있습니다.
[ 그림4. 전송 거부 예제 실행 결과 ]
마치는 글
이 글에서는 message 전달 함수 중 전송 함수들에 대해서 알아보았습니다.
이 함수들 중 어떤 것을 사용하는 것이 적절한지를 판단하기 위해서는 반드시 동기와 비 동기에 대한 개념의 이해가 필요합니다.
상황에 따라 적절한 함수를 사용하시면 원하는 결과를 얻을 수 있을 것입니다.
전송 함수들에 대해서 알아보았지만 아직 수신 함수들에 대해 알아보지 않았습니다. 다음 글에서는 message block 으로부터 message 를 수신하는 수신 함수들에 대해 알아보겠습니다.
필자를 포함해서 여러명의 개발자들은 WCF 서비스를 이용할때 당췌 알수 없는 이유의 오류때문에 무한 야근을 한 경험이 있을거라고
확신한다. 이번엔 그중에 하나이면서 , 지금까지 명확하게 설명되지 않았던 오류 하나를 살펴보고자 한다.
<exception>
This collection already contains an address with scheme http. There can
be at most one address per scheme in this collection.
Parameter name: item
한글로는?
이 컬렉션에는 스키마 http을(를) 가진 주소가 이미 있습니다. 이 컬렉션에서 스키마당 주소는 하나만 존재할 수 있습니다.
매개 변수 이름: item
설명: 현재 웹 요청을 실행하는 동안 처리되지 않은 예외가 발생했습니다. 스택 추적을 검토하여 발생한 오류 및 코드에서 오류가
발생한 위치에 대한 자세한 정보를 확인하십시오.
예외 정보: System.ArgumentException: 이 컬렉션에는 스키마 http을(를) 가진 주소가 이미
있습니다. 이 컬렉션에서 스키마당 주소는 하나만 존재할 수 있습니다.
매개 변수 이름: item
분명
로컬 호스트에서는 넘흐넘흐 잘 돌아갔던 우리 프로그램. 서버에 올려봤더니 요지부동. 절대 동작할 생각을 안하는 우리 WCF
서비스. 당신은 과연 이 메시지를 본적이 있는가?
이 문제는 하나의 WCF 서비스는 오로지 1개의 호스트로만 서비스되야 하는 규칙이 존재하기 때문이다.
Microsoft 는 이 문제에 대해 처음 해결책을 제시하였다.(이글은 IE에서 클릭했을때만 보입니다.)
http://blogs.msdn.com/rampo/archive/2008/02/11/how-can-wcf-support-multiple-iis-binding-specified-per-site.aspx
그러나 이글은 www.loveciel.com 이란 바인딩으로 접근했을때는 A 라는 메서드를 호출하게 할수 있는 시나리오로 ,
사용자가 원하던 방식인 멀티 도메인으로 호출했을때 동일한 호출을 기대하는것은 아니었다.
그리고 그에 따른 방식을 사용자가 제안하였을때 Microsoft 의 대답은 다음과 같았다.
Lets say ServiceA is bound to Services.xxx.com and ServiceB is bound to
xxx.com.
I am trying to understand why ServiceB cannot call directly call
services.xxx.com. The baseAddressPrefixfilter workaround should further
help to host these two services under the same site.
If you did want two base address today the only workaround is to
have two copies of the service.
뭐 간단히 말하면 , 여러개의 호스트로 WCF서비스를 호출하는건 안되고 , 정 그렇게 하고 싶으면 두개의 카피를 만들라는
이야기였다. 그리고 이 Microsoft 측은 이 이슈를 종료시켜 버렸다.
그리고 프로그래머는 분노했다 -_-;;
WTF? "By design"? This is a blocking issue for us, and we are now
turning back to ASMX handlers, thanks WCF Team! You did a great job by
putting in fundamental "design" flaws in WCF.
But oh well, MS connect is useless anyway. They don't take this
feedback seriously... THE workaround is to not use WCF and go back to
ASMX handlers. Period.
WTF 는 What the fuck? 의 줄임말이다. 처음
이 댓글을 봤을때 얼마나 웃었는지 ㅎㅎ
결과적으로 이 이슈는 3.5에서는 해결될수 없는 이슈이다. 본인의 경우는 ssl을 통하는 사이트가 있었는데 , 이번에 아이폰 인증
모듈이 들어가면서 이 ssl증서를 사용해야할 일이 생겼는데 , 기존사이트는 이 ssl 을 미러링으로 사용하였기 때문에 상당히
많은 host 주소가 하나의 IIS에 등록이 되어 있었다. 그러한 이유로 , baseaddress를 지정하는 방법으로 해당 이슈를
해결할수 있었다. 그러나 crossdomain 에 민감한 ajax 나 flash 등에서는 여러개의 도메인을 하나의 머신에서
쓰는게 상당히 중요한 이슈이기 때문에 ajax 와 실버라이트에서 가장 유용하게 쓰이는 WCF 는 이 이슈를 해결해야 할 필요가
있었다.
결론적으로? 이 이슈는 WCF 4.0 에서 해결이 되었다 ^^
WCF 4.0 에서는 해당 Attribute 를 지정하면 간단하게 멀티 도메인을 허용할수 있게 된다.
웹기반의 WCF 서비스에서만 동작한다
이 일련의 에피소드는 Microsoft 라는 조직내에서 개발자의 니즈를 처리하는 방식을 보여준다. 물론 그 과정에서 다소간의
오류가 의견충돌이 일어날수도 있지만 , 사용자에 입장에서 얼마나 생각하는지에 따라 사용자가 그 기업을 바라보는 시각이 달라짐을
보여준다고 할수 있다. 비록 WCF 3.5에서 해당 오류를 해결하진 못했지만 , 긴 시간동안 사용자의 불만에 대해 고민하고, 결국
수용하는 Microsoft 의 모습을 나는 칭찬해주고 싶다.
이미 이전 포스트에서 얘기 했듯이, 똑같은 "단위 테스트"라는 단어를 가지고 개발자, 테스터, 고객은 각자 그 의미를 전혀 다르게 생각하고 있습니다. 이런 단어의 해석 조차 각자 틀린데, 애자일(Agility)하게 어떻게 소프트웨어를 만들 수 있을까요. 이미 "단위 테스트" 라는 작은 주제를 가지고 벌써부터 고객과 개발 조직간의 불화음이 발생합니다.
아니, 이미 개발 팀 내부에서부터 어디서 부터 시작해야 할지 어디둥절 할 수 있습니다. 그렇다면 과연 "단위 테스트" 가 결함의 발생을 줄이는 약이 될지, 팀 간의 커뮤니케이션 장애를 발생시키는 독이 될지, 그것은 아마 이 글을 읽는 독자 분들의 실천에서부터 시작될 것입니다.
이렇게 말도 많고 탈도 많은 단위 테스트를 왜 꼭 해야 하는지부터 짚고 넘어가 봅니다. 단위 테스트는 기능 또는 단위 별로 결함을 조기에 발견하기 위한 테스트 방법입니다. 하지만 "단위 테스트" 라는 단어만으로는 그 이해는 너무나도 상이하게 틀리다는 것입니다.
개발자 - 단위 테스트 코드를 만드는 것
테스터 - 개발중인 웹 어플리케이션 또는 클라이언트 어플리케이션 등을 만져보면서 기능 결함을 발견 하는 것
고객 - 문서!! 기능에 대한 산출물 또는 보고서
일단 "단위 테스트" 에 대한 이해가 달라도 현재까지는 상관이 없습니다. 왜냐하면 단위 테스트가 가지는 의미를 제대로 이해하고, 공감대를 형성하는 것이지 어떻게 수행할 지는 적어도 지금은 중요하지 않습니다.
단위 테스트에 대해, 국내에는 번역본이 대부분이라 사실 우리 나라 실정에 정말 맞는지에 대해 많은 고민을 하였고, 적어도 필자는 이런 고민을 방관하고 싶지는 않습니다. "NO" 를 외치고 싶을 때는 외쳐야 하지 않겠습니까?
왜 버그가 발생하는가?
일반적으로 버그나 소프트웨어의 결함은 어떻게든 발생할 수 밖에 없습니다. 아무리 기계적이고 단순한 코드를 개발한다고 하더라도, 코드 간의 상호 연동, 클래스 간의 연동, 컴포넌트(Components) 간의 연동, 레이어(Layer) 간의 연동, 더 나아가 시스템 간의 연동.. 즉, 연동 또는 상호 종속적인 관계가 발생하는 시점부터 버그는 이미 예견될 수 밖에 없습니다. 쉽게 얘기하면, A 란 코드와 B 란 코드가 있습니다. 이 두 개의 코드는 분명히 다른 목적에 의해 개발이 되었지만, 목적 자체가 틀린 코드가 상호 연동 또는 종속적인 관계가 발생하게 된다면, 과연 어떨까요? 이것은 코드 자체에서 발생되는 결함이라기 보다 상호 연관 관계에 놓이면서 발생하는 결함이라고 볼 수 있습니다. 그리고 대부분의 중요한 또는 큰 버그는 이러한 얽히고 설키게 되는 연동/종속 이란 문제로 발생됩니다.
만약 버그가 발생되지 않는 상황이라도 A와 B 코드는 언제든지 업그레이드가 될 수 있습니다. 기능이 변경될 수 도 있고, 기능이 추가될 수 도 있습니다. 아무것도 모르는 최종 사용자(End user) 는 잘 되던 기능이 갑자기 안된다면 좀 어이없어 할 것입니다. 일부 이러한 코드가 최종 사용자의 요구에 의해, 그리고 최종 사용자를 위해 변경되지만, 최종 사용자는 결함을 발생시킨 원인을 알고 싶지도 않고, 단지 개발 팀을 신뢰할 수 없을 뿐입니다.
즉, 이러한 버그가 조기에 발견되지 않는다면 버그는 지속적으로 증식을 하게 됩니다. 가장 대표적인 예라면, 월별, 년별 정산해 주는 기능이겠죠. 이곳에서 만약 버그가 발생한다면 몇 일, 아니 몇 주를 이 버그를 해결하기 위해 많은 시간을 소비해야 할 것입니다. 실제로도 필자는 주변에서 이와 유사한 버그로 인해 고생하는 동료 개발자들을 많이 보아왔습니다. 결과적으로 모든 코드에는 버그가 발생할 가능성이 있고, 그것은 바로 연동/종속적인 관계가 시작되면서 집중적으로 발생하게 됩니다. 그리고 잠재적인 모든 코드가 버그의 대상이 되고, 잠재적인 버그가 가장 위험하다는 것입니다. 잠재적인 버그는 지금의 나도, 너도, 우리 모두가 모르는, 언제 발생할 지 모르는 버그이기 때문입니다.
이러한 주제로 필자는 이미 Techday 2009 에서 온라인 세미나를 한 적이 있습니다. 아래의 링크를 통해 간략한 단위 테스트 기술에 대해 미리 익히는 것도 좋습니다.
단위 테스트는 많은 곳에서 장점을 이야기 합니다. 예를 들어, "결함을 줄이고, 잠재적인 버그를 줄이고, 코드를 리팩토링 하게 하며…." 등등… 단위 테스트가 실제로 이러한 많은 장점이 있는 것은 필자 또한 강력하게 권장하고 싶습니다. 하지만, 아시다시피 "단위 테스트" 에 대해 개발자, 테스터, 고객과의 공감대가 형성되지 않은 이 마당에, 저런 소리를 하면 정말 비즈니스적인 가치가 있을까요? 아마도 고객은 단위 테스트를 한답시고 비용과 시간이 늘어난다는 것을 절대 용납하지 않을 것입니다.
그렇다면 단위 테스트가 우리에게 주는 의미는 무엇일까요? 단위 테스트를 형용할 수 있는 모든 단어를 떠올려 보십시오. 참 많습니다. 하지만 고객과 개발 조직, 그리고 비즈니스 측면에서 어떤 단어가 가장 잘 어울릴까요.
바로 단위 테스트는 "신뢰" 입니다. 개발 조직에서 개발자와 개발자간의 코드에 대한 신뢰! 개발 조직과 테스트 팀간의 신뢰! 그리고 단위 테스트의 결과는 매우 명확해서 고객과의 신뢰로 이어질 수 있습니다. 이러한 단위 테스트로 인해 장기적인 비용이나 리소스 절감 효과 등은 잠재적인 비즈니스적인 신뢰로 이어질 수도 있습니다. 만약, 개발 팀 내의 단위 테스트는 다른 개발자가 만든 코드와 연동해야 할 때 대한 최소한의 신뢰를 가지는 매우 신사적인 행위입니다.
즉, 개발자, 테스터, 고객과의 단위 테스트에 대한 이해가 틀리다고 하더라도, 최소한 단위 테스트라는 것을 했고, 그 결과가 명확했을 때 그 관계에서 "신뢰" 가 형성될 수 있습니다. 젊은 개발자가 아닌 고객은 자신의 과거의 경험에 빗대로 단위 테스트라는 것이 어떤 것인지 잘 모르더라도, 매번 명확하게 결과를 보여준다면 비록 버그가 발생하였더라도, 버그의 발생 시점이 명확하고 이 버그의 해결 결과 또한 명확하다면 "버그" 라는 단어로 절대 날뛰는 일은 없을 테니까요.
애자일(Agile) 한 팀 모델! 무엇이 문제인가...
자! 이제 단위 테스트에 대해 어느 정도 확신을 갖게 되었다면, 이것을 수행하기 위한 팀 모델이 필요합니다. 하지만 이러한 팀 모델을 구축하기 위한 시도는 매우 말이 많고 신중해야 할 부분입니다. 애자일을 도입하여 실패했다는 많은 히스토리와 사례들이 범람하면서 과연 애자일이 좋은 것일까라는 고민을 해 보아야 할 시기인 것입니다.
애자일이라고 하면 일반적으로 통용되는 XP(eXtreme Programming) 의 팀 모델과 스크럼(Scrum) 의 팀 모델은 현저하게 차이가 납니다. 그리고 글로벌 소프트웨어 개발 업체 중 단연 1위인 Microsoft 의 정보 기술 솔루션인 MSF(Microsoft Solution Framework) 의 팀 모델은 모두 다 다르다는 것입니다.
일단 현재 우리나라의 팀 모델의 특색은 매우 다양하고 변칙적이며, 상하 수직적인 관계입니다. 예를 들어, 개발자를 동일 선상의 개발자가 아닌 "대리급", "과장급", 일부 "부장급" 도 코딩을 하기도 합니다. 그리고 단순히 "개발 조직"이라는 단어 조차 어색하게 사수, 부사수 달랑 두 명이 개발을 하기도 합니다. 편의상 아래와 같은 형태가 되는군요. (이 부분은 조직마다 매우 다른 형태를 띌 수 있습니다)
어떤 모델에서는 개발자, 아키텍처, 테스터로 구분하지만 필자는 이러한 팀 모델이 전혀 한국적이지 않다고 생각합니다. 애자일과 MSF(Microsoft Solution Framework) 에서도 언급하지만 개발 팀에서는 우두머리, 즉 대장을 두지 말라고 조언합니다. 위의 그림에서 PL(Project Leader) 는 바로 개발 팀의 우두머리이며, 가장 테크니컬 하거나 경력, 또는 업무 도메인 지식이 뛰어나야 합니다. 그리고 한국형 조직이나 문화 특성상, 윗사람 또는 상사에게 코드나 아키텍처적인 문제를 언급하는 것은 매우 조직력이 부족한 사람으로 비추어지기도 합니다.
예를 들어, 한국의 개발자는 코드에 굉장히 민감하고, 곧 코드가 자신의 자존심이라고 생각하는 경향이 짙습니다. 적어도 필자 또한 마찬가지 입니다. 개발자는 자신의 사수에게 코드적인 문제를 언급한다면 충분히 문제의 소지가 발생할 수 있습니다. 데이터베이스 개발자일 경우 자신의 사수에서 SQL 쿼리에 대한 성능적인 문제를 지적한다면 자신의 부사수가 잘난 체 한다거나, "경력도 얼마 안되는 놈이 좀 안다고 까부네" 라고 생각하기도 합니다. 상사와의 관계가 아닌, 동등한 개발자 간에서도 다른 사람이 자신의 코드에 대해 지적하는 것은 '거의 인간 관계를 포기한 것'과 다름이 없다고 봐도 무리는 아닐 것입니다. (물론 어떻게 얘기를 잘 하느냐는 문제도 있겠지만요)
결론적으로 애자일 또는 MSF 가 언급한 팀 모델은 굉장히 이상적이지만, 전혀 우리나라의 특성을 적용하기가 힘이 들 수 있습니다. 위에서 예를 든, 단편적인 예만 봐도 애자일 또는 MSF 의 자유분방한 팀 모델은 우리나라 현실에 도입되기는 매우 힘이 듭니다. 작은 조직이라면 모를까, 거대한 엔터프라이즈 프로젝트의 경우 많은 점점 더 큰 팀과 또는 다른 업체와 함께 일을 하는 경우가 발생하기 때문에, 팀 내부가 아닌 팀 외부로 까지 애자일한 행위는 절대 금물입니다.
한국적인 애자일(Agile) 한 팀 모델
많은 사람들이 오해하는 것 중에 하나가 애자일의 XP 와 스크럼을 도입하기 위해 그것이 요구 또는 원칙, 권장하는 방법들을 반드시 따라야만 한다고 생각합니다. 이미 지난 포스트에서도 언급했듯이 고객은 명확한 일정을 요구하는 폭포수 모델(Waterfall Model) 을 요구하는데, 팀 내외부적으로 애자일(Agile) 을 외치고 있다가는 고객이 원하는 어떠한 일정과 조건에도 맞출 수가 없습니다. 우리는 이 시점에서, 애자일이라는 단어가 주는 의미를 다시 한번 상기해 볼 필요가 있습니다. 어떠한 애자일, MSF 에서도 그것을 반드시 이행하라고 명시하지 않습니다. 다만, '이렇게 이렇게 해보니 좋으니 너희들도 이런 방법을 써봐라' 라는 권장의 의미이지 강요가 아니라는 것입니다.
어쨌든, 개발자라고 하면 아래와 같이 두 가지 형태의 개발자가 있을 것입니다. 유지보수 인력인 SM(System Management) 이 있겠고, 개발 인력이 있습니다. 두 형태는 같은 개발은 하는 것이 맞지만, 깊은 내막은 전혀 다른 개발을 하고 있습니다. 일반적으로 개발 인력은 개발이 마치면 유지 보수 팀 또는 인력에게 인수가 됩니다. 그리고 인도적인 차원에서 각종 산출물을 뽑아내어 유지 보수 인력에게 모든 추후 버그나 추가적인 기능 개발이 떠넘겨지게 됩니다. 즉, 유지 보수 인력은 1년 365일 전산 시스템이 잘 운영되고, 지속적인 국가 정책이나 기업 정책, 그리고 고객의 요구를 시스템에 반영하는 역할을 하게 됩니다. 이 과정에서 일부 개발 인력이 유지 보수 인력으로 자연스럽게 전환되기도 합니다.
하지만, 좀 더 큰 기업에서는 개발 팀만해도 매우 복잡한 형태를 띄게 됩니다. 일단 내부 개발자, 외부 개발자가 분리가 되며 기업의 내부적인 정보를 공유할 수 있는 권한이 있느냐 없느냐로 볼 수 있습니다. 뭐 이러한 경우 사적인 자리에 까지 이어져, 점심을 함께 먹는 동료가 정해지기도 합니다. ^^; 외부 개발자는 일반적으로 프리랜서나 개인 사업자를 등록한 사람들이 됩니다. 하지만 대부분 프리랜서나 개인 사업자는 기업의 하청 업체를 통해 계약이 되는 경우다 더 많죠.
필자 또한 이러한 팀 모델과 현실과의 많은 고민을 하면서, 과연 어떠한 것이 한국적인 팀 모델이 될 것인가에 대한 고민을 끝없이 하고 있습니다. 마찬가지로, 필자가 좀 더 성숙해 지면 아래에 언급하는 팀 모델에 대한 생각이 바뀔 수 도 있습니다. 어쨌든 필자의 경험상 아래와 같이 팀 모델을 권장합니다.
위의 선은 직접적으로 커뮤니케이션 할 수 있는 범위입니다. 자세한 롤 모델(Role Model) 까지는 언급하지 않겠지만, 대충 커뮤니케이션의 통로와 작은 하위 팀을 보면 이해가 갈 것입니다. 특히 핵심 개발자 팀은 그 관리자나 일반 개발자와만 커뮤니케이션이 이루어지며, 이 핵심 개발자 팀은 외부 또는 타 업체가 될 수 도 있습니다. 일반 개발자에서 파생되는 여러 종류의 개발자는 모두 일반 개발자로 들어갑니다. 그리고 고객은 오직 관리자와 직접적으로 커뮤니케이션이 이루어지며, 경우에 따라서 품질 유지 팀에 의해 보고를 받을 수 도 있습니다. 애자일의 자유분방한 커뮤니케이션의 트랜잭션(Transaction) 을 넓은 범위에서 최소한으로 우리나라의 실정에 맞도록 줄이는 것이고, 관리자가 권한을 일부 위임해 주는 형태가 되는 것입니다.
또 하나, 필자가 애자일(Agile) 에 대해 공부를 시작하면서, 가장 답답했던 것이 바로 "고객의 자발적인 참여"를 기대한다는 것입니다. 일반적으로 개발 팀 입장에서 느끼는 고객은 굉장히 권위적이며, 상하 수직적인 관계입니다. 과연 애자일하게 프로젝트를 하면서 점진적인 릴리즈(Release) 를 통해 1달에 한번의 반복(Iteration) 으로 총 1년 동안 12번의 반복을 통해 점진적으로 최종 릴리즈에 도달한다고 가정해 봅시다. 이 릴리즈마다 고객의 자발적인 참여와 의견을 교류하는 이상을 생각하며, 첫 번째 릴리즈를 보여줬을 때, 과연 고객은 소프트웨어 개선을 위해 적극적인 참여를 할까 아니면 "내가 원하는 소프트웨어는 이게 아닌데?" 라고 할까요.
과연 그렇다면 여러분은 애자일의 의미에 대해 고객에게 세미나를 할 것인가요, 아니면 어떻게든 설득을 할 것인가요? 이미 고객의 경향을 알고 있는데, 그러한 고객을 설득하고 이해시키는 계몽(啓蒙)을 할 수 있을지 의문입니다. 그렇다면 이미 답은 나왔습니다. 고객을 변화시키지 못할 바엔, 차라리 우리가 변하는 것입니다. 즉, 개발 조직 내에서 불필요한 소음을 줄이기 위해, 어디에서 문제가 발생하는지, 그리고 소통이 어떻게 이루어지는지에 대한 트랜잭션(Transaction) 에 대한 관찰이 필요하다는 것입니다.
그리고 위의 그림과 같이 필자는 최종적인 트랜잭션의 소통이 현재 한국적인 가장 이상적인 애자일이 아닐까 생각합니다.
각 역할 별로 간단히 정리하자면, 아래와 같은 마음 가짐과 자세가 필요하겠군요. 물론 모든 조건을 완벽하게 갖추기보다는 최소한의 자세와 지식이 갖는 것이 유리할 듯 합니다. 자신이 부족한 부분이 있다면, 팀 동료가 이것을 뒷받침 해 주어야 겠지요. (유지 보수 팀은 복잡성을 줄이기 위해 여기에서 제외합니다)
관리자
고객을 이해하고 개발 조직을 관리
소프트웨어 품질을 지속적으로 유지 및 팀 조율
핵심 개발 팀
(Core Developer)
기술적인 기반 지식
프레임워크
업무 도메인의 이해
개발자 보호(Care) 및 지원(Support)
품질 유지 팀
(Test Team)
업무 도메인의 이해
테스트 도구 사용
전반적인 테스트 시스템 인프라 와 운영체제(OS) 의 이해
일반 개발자
기본적인 개발 지식
할당된 개발 업무에 대한 책임감
품질 좋은 소프트웨어를 위한 단위 테스트
필자가 단위 테스트에 대한 필요성을 얘기하다가 뜬금없이 버그와 팀 모델에 대해서 이야기 하는 것이 의문일 수 있을 것입니다. 하지만 이 내용 또한 이전 포스트에서 이야기 했듯이, 소프트웨어 품질이 떨어지는 것에 대해 어느 누구에게 몰빵(?)할 수도 없는 문제이며, 단위 테스트에 대한 공감대가 없다면 절대 할 수 없는 것이기 때문입니다. 애자일이 우리에게 많은 자율성(Autonomy) 와 타이트함(Tightly) 를 주는 것은 매우 환영할 일이지만, 일부 정책적인 강제가 없다면 애자일은 우리나라에서 이미 실패한 방법론 또는 프로세스일 뿐입니다. 왜냐하면 우리나라의 고객은 아직까지는 변하지 않을 테니까요.
C/C++에서 포인터를 초기화 할 때 ‘NULL’을 사용합니다. 그러나 VC++ 10에는 C++0x에서는 포인터를 초기화 할 때 NULL 대신 새로 생긴 ‘nullptr’을 사용할 수 있게 되었습니다.
C++/CLI는 이전부터
nullptr이 있었습니다.
C++/CLI에서는 ref 클래스의
핸들을 초기화 할 때는 nullptr을 사용합니다.
C++/CLI, C++0x의
nullptr은 C/C++ 처럼 ‘0’이 아니라는
것을 잘 기억하시기 바랍니다.
interior_ptr
interior_ptr은 관리 힙(managed
heap. 즉 GC겠죠) 상의 value type나 기본형을 가리키는 포인터라고 할 수 있습니다.
interior_ptr는 value type나 기본형을 비관리 코드의 포인터처럼 사용하고
싶을 때 사용하면 좋습니다.
< 코드 1. >
ref class REFClass
{
public:
int nValue;
};
void SetValue( int* nValue )
{
*nValue = 100;
}
int main()
{
REFClass^ refClass = gcnew REFClass;
SetValue( &refClass->nValue ); // 에러
}
위 코드를 빌드 해 보면 SetValue(
&refClass->nValue ); 에서 빌드 에러가 발생합니다. 매니지드
힙에 있는 것은 그 위치가 변하므로 비 관리 코드의 포인터를 넘길 수가 없습니다. 그럼 <코드 1>를 정상적으로 빌드 하기 위해서 interior_ptr를 사용해 보겠습니다.
< 코드 2. >
ref class REFClass
{
public:
int nValue;
};
void SetValue( interior_ptr<int>
nValue )
{
*nValue = 100;
}
int main()
{
REFClass^ refClass = gcnew REFClass;
SetValue( &refClass->nValue );
}
<코드 2>의 SetValue의 파라미터로 비관리 코드의 참조나 포인터를 넘길 수도 있습니다.
< 코드 3. >
#include <iostream>
void SetValue( interior_ptr<int>
nValue )
{
*nValue = 100;
}
int main()
{
int
nValue = 50;
SetValue(
&nValue );
std::cout
<< nValue << std::endl;
getchar();
return
0;
}
그리고 interior_ptr에 대신 C++/CLI의 참조(‘%’)를 사용하는 방법도 있습니다.
pin_ptr
pin_ptr은 관리 힙 상의
value type나 기본형을 비관리 코드에서 포인터로 사용하고 싶을 때 사용하는 기능입니다. 가장
필요한 경우가 C++/CLI에서 기존의 비관리 코드로 만들어 놓은 라이브러리를 사용할 때입니다.
< 코드 4. >
ref class REFClass
{
public:
int nValue;
};
void SetValue( int* pValue )
{
*pValue = 100;
}
int main()
{
REFClass^ refClass = gcnew REFClass;
pin_ptr<int> pValue = &refClass->nValue;
SetValue( pValue );
pValue = nullptr;
}
pin_ptr에 메모리 주소를 할당하는 것을 ‘pin’이라고 부르고 사용이 끝난 후 nullptr로 초기화 하는
것을 ‘unpin’ 이라고 부릅니다. pin_ptr 사용이
끝난 후 가능한 빨리 unpin 해주는 것이 좋습니다.
interior_ptr과 pin_ptr의 차이점
interipor_ptr과
pin_ptr은 둘 다 관리 힙 상의 value type이나 기본형을 가리키는 포인터로
사용되지만 interior_ptr은 관리 힙 상에서 인스턴스가 이동하여도 올바르게 추적할 수 있는 포인터로
런타임의 지배하에 있습니다(즉 인스턴스가 관리 힙 상에서 이동하여도 괜찮습니다).
pin_ptr은 관리 힙 상의
value type을 비관리 코드에서 사용하고 싶을 때 사용합니다. 당연히 이 때는 관리
힙에 있는 인스턴스가 이동하면 안되므로 인스턴스의 이동을 금지합니다.
interipor_ptr과
pin_ptr의 같은 점 : 포인터처럼 사용할 수 있다.
interipor_ptr과
pin_ptr 다른 점 : interipor_ptr은 관리 코드 상에서 포인터로 사용하고, pin_ptr는 비관리 코드에 포인터로 넘길 때 사용합니다.
interipor_ptr과
pin_ptr을 공부했으니 다음에는 C++/CLI에서 비관리 C++과 혼합해서 사용할 때 어떻게 해야 하는지 설명하겠습니다.
DirectX11의 여러가지 기능이 있지만 한눈에 무엇이 좋아졌는지 왜 써야 하는지에 대해서 의문을 가지시던 분들이 계실 것 같습니다. Dirt2라는 게임에서 사용된 DX11의 기능을 보면 그런 의문이 조금은 사라질것 같습니다.
tessellation은 GPU파이프라인이 정점을 생성하는 것을 신속하고 능률적으로 계산하는 과정입니다. Xbox360의 Xeons칩부터 ATI의 GPU까지도 존재하는 기능이나 HD 5000 시리즈에서 최초로 DX의 버젼이 지원되었습니다. 높은 bandwidth를 필요로하는 하이폴리곤 모델을 쓰지 않게 해주고, 특히 신속하게 현장의 풀잎 전체를 빨리 처리해야하는 상황에서도 유용한 기술입니다.
Dirt2에서는 저수치가 교차 할 때, 경기를 보고 있는 관중 그리고 트랙 바깥쪽에서 휘날리는 깃발에서 총 3개의 상황에서 사용을 하였습니다.
위 그림에서 본 것 처럼 tessellation은 더 상세한 모양을 추가해 더 현실적인 표현을 할 수 있습니다.
그러나 하이폴리곤 모델에서는 사용해선 안됩니다. 오히려 tessellation을 쓰지 않으니만 못한 경우가 생길 수 있습니다.
AMD에 따르면 " CPU는차가그것을통과할때 height field texture를 generates한다. tessellator는카메라위치에근거를둔수백개의삼각형을생성한다.
6월 1일 REMIX10 행사를 기점으로 Visual Studio 2010 한글판이 대중에 공개가 되었습니다. Visual Studio 2010 의 영문 버전은 그 이전에 출시가 되었지만 한글판이 출시된 이후에 더 많은 관심을 받게 되었습니다.
Visual Studio 2010 으로 개발 환경을 업그레이드를 진행하는 곳이 특히 해외에서 많습니다. 제가 그걸 어떻게 다 아냐구요? 항상 트위터 검색을 통해 해외에서 Visual Studio 2010 를 어떻게 사용하고 있는지 매일 매일 관심 있게 보고 있답니다. ^^
어쨌든 Visual Studio 2008 을 쓰고 있지만, Microsoft MVP 이거나 회사에서 MSDN Subscription 라이선스가 있다면 Visual Studio 2008, Visual Studio 2010 개발 도구가 혼합해서 사용될 경우가 있습니다. 이런 경우 두 개발 도구에서 쌍방 개발 가능하게 구성을 할 수 있습니다.
이 방법은 제니퍼소프트의 정성태 과장님의 블로그에서 예전에 소개했던 VS2005, VS2008 혼합해서 사용하는 방법과 동일합니다.
1. 간단한 예제로 Console Application 을 Visual Studio 2008 에서 생성했습니다.
2. 기존의 솔루션 파일의 복사본을 하나 만듭니다.
3. 솔루션 파일을 노트패드로 ConsoleApplication - VS2010.sln 파일을 열어 다음의 항목을 수정합니다.
4. 프로젝트 파일을 열어 ToolsVersion 의 '3.5' 를 '4.0' 으로 수정합니다. 'ConsoleApplication1.csproj'
만약 다수의 프로젝트일 경우, 위의 3번에서 수정한 솔루션 파일을 열면 프로젝트를 변환하는 마법사로 진행하면 쉽게 변경이 됩니다.
5. 모두 완료 되었습니다. Visual Studio 2010 와 Visual Studio 2008 에서 각각의 솔루션 파일로 동시에 작업을 할 수 있습니다.
위의 방법을 이용하여 Visual Studio 2008 과 Visual Studio 2010 에도 모두 개발이 가능합니다. 하지만 만약 테스트 프로젝트가 포함이 되어 있다면 두 개발 도구에서 사용할 수 없습니다. 왜냐하면 .NET Framework 4.0 에서는 테스트와 관련된 Microsoft.VisualStudio.Quality 프레임워크가 개선되고, Microsoft.VisualStudio.TestTools 프레임워크가 추가되면서 이전 Visual Studio 2008 과 프로젝트가 호환이 되지 않습니다.
하지만 불가능할 것 같은 테스트 프로젝트도 Visual Studio 2008과 Visual Studio 2010에서 동시에 사용할 수 있는 방법이 있습니다. 이것은 다음에 알아보도록 하겠습니다.
DreamSpark는 Microsoft의 Spark 프로그램 시리즈 중 하나이며, 웹 비지니스 활성화를 위한 WebSiteSpark 프로그램, 초기 창업 지원 및 기업 활성화를 위한 BizSpark 프로그램이 있습니다. 그리고 DreamSpark는 학생들의 IT 기술력 향상을 위하여, 기존에 제공되어오던 MSDN Academic Alliance (http://msdn.microsoft.com/ko-kr/academic/default.aspx)와는 별도로 전역적으로 일반화하고 확대한 프로그램입니다.
DreamSpark를 통해서 2010년 6월 현재 제공받을 수 있는 소프트웨어 솔루션 및 혜택들은 다음과 같습니다. 다음은 한글판까지 같이 제공되는 소프트웨어 솔루션들의 목록입니다.
Visual Studio 2010 Professional Edition (* 한글판 제공됨)
Windows Server 2008 R2 Server Standard Edition (* 한글판 제공됨)
Expression Studio 3 (* 한글판 제공됨)
Visual Studio 2008 Professional Edition (* 한글판 제공됨)
Windows Server 2008 Server Standard Edition (* 한글판 제공됨)
SQL Server 2008 Developer Edition (* 한글판 제공됨)
SQL Server 2008 Express Edition (* 한글판 제공됨)
Visual Basic 2008 Express Edition (* 한글판 제공됨)
Visual C# 2008 Express Edition (* 한글판 제공됨)
Visual C++ 2008 Express Edition (* 한글판 제공됨)
Visual Web Developer 2008 Express Edition (* 한글판 제공됨)
Visual Studio 2005 Professional Edition (* 한글판 제공됨)
그 외에 추가적으로 제공되는 소프트웨어들은 다음과 같습니다.
Visual Studio 2010 Express Edition
Windows Mobile 6.5 SDK
Windows Phones Developer Tools CTP
XNA Game Studio 3.1
Robotics Developer Studio 2008 R3
Windows Embedded CE 6.0
Virtual PC 2007
Concurrency and Coordination Runtime and Decentralized Software Services Toolkit 2008 R2
Windows Server 2003 Standard Edition
DreamSpark를 통하여 기본적인 운영 체제와 개발 도구를 다운로드받아서, 여러분이 원하는 소프트웨어나 웹 사이트를 손쉽게 개발하고 디자인할 수 있습니다. Microsoft Imagine Cup 대회에 참가하기 위하여 필요한 소프트웨어를 모두 이곳에서 가져오실 수 있으며, 학생 벤처를 시작하고자 하시는 분들께도 충분한 기술적인 리소스들을 모두 이곳에서 가져오실 수 있습니다.
DreamSpark 등록하는 방법
DreamSpark에 등록하는 방법은 크게 세 가지가 있습니다. DreamSpark 무료 쿠폰을 받으신 경우 (1), 국제학생증 (International Student Identification Card - ISIC)을 소지하고 계신 경우 (2), DreamSpark에 관리자가 기관을 등록한 경우 (3)로 나눌 수 있습니다. 이 세 가지 중 하나로 해당이 되시는 경우, Windows Live ID를 통하여 로그인을 하신 후, Verification 과정을 거치면 됩니다. 각 다운로드 항목 별로 페이지에 Verify 버튼을 클릭하신 후, 국가를 Korea로 선택하시고, 학생 인증으로 접속하기 위하여 Verify as a Student 라디오 버튼 항목을 클릭합니다.
(1) DreamSpark 무료 쿠폰을 받으신 경우: 아래 그림에서처럼 "I have an Activation Code" 라디오 버튼 항목을 클릭하고, Activation Code 입력란에 쿠폰에 적힌 Activation Code를 입력하고 Verify 버튼을 클릭하면 인증이 완료됩니다. 단, 중복 사용이 제한되거나 유효 기간, 유효 횟수가 있는 쿠폰의 경우 Activation Code를 재 사용할 수 없을 수도 있습니다.
(2) 국제 학생증을 가지고 계신 경우: 국제 학생증을 취급하는 대학 교육 기관의 경우 이 옵션을 사용할 수 있습니다. 국제 학생증을 취급하지 않더라도 국내외 여러 국제 학생증 발급 기관 (예: 하나은행, 한국씨티은행 등)을 통하여 약간의 수수료를 지불하고 다양한 목적으로 국제 학생증을 발급받아 혜택을 누리실 수도 있습니다.
참고: ISIC 코드를 입력할 때에, 일부 ISIC 카드의 경우 제일 마지막 자릿수를 제외하고 입력해야 하는 규칙이 따를 수 있습니다. 저의 경우, 인하대학교 학생증과 겸용해서 발급되는 ISIC 카드의 경우 이러한 규칙이 적용됩니다.
(3) DreamSpark 서비스에 등록된 기관에서 재학중이신 경우: 이 경우 해당 기관 내의 재학생임을 증명할 수 있는 수단을 사용하여 접속할 수 있습니다. 해당 기관 내에서 자체적으로 SSO (Single Sign On) 서비스를 Windows Live ID 기반으로 구현하였을 경우 이를 이용하시면 됩니다. 만약 별도의 SSO 서비스가 없을 경우 해당 기관에서 제공하는 메일 서비스를 경유하여 인증을 완료하실 수 있습니다.
Continue 버튼을 클릭하여 계속 진행하면 다음과 같은 페이지가 나타납니다.
위의 화면에서 해당하는 기관을 선택하고, Continue 버튼을 클릭합니다. 아래의 링크를 클릭하시면 2010년 7월 현재 DreamSpark에 등재된 기관들의 목록을 보실 수 있고, Ctrl + F 키를 이용하여 해당 기관명을 검색하실 수 있습니다.
해당 기관 내의 구성원 - 또는 - 학생임을 인증하기 위하여, 해당 기관에서 운영 중인 전자 메일 서비스의 계정과 주소를 이곳에 기재해야 합니다. 예를 들어, 메일 서비스의 도메인이 rkttu.com 일 경우, E-mail Address에 anonymous@rkttu.com 과 같이 기재할 수 있으며, Confirm E-mail Address에 다시 한번 같은 메일 주소를 기재하면 됩니다. 기재가 끝나면 Verify 버튼을 클릭하여 추가 절차를 진행합니다.
DreamSpark는 대학생 여러분들을 위한 솔루션입니다.
Verification 과정을 완료하고 다운로드를 할 때에는 Internet Explorer 뿐만 아니라 Firefox, Chrome 등 여러분이 원하는 브라우저를 이용하여, 별도의 Download Manager를 이용하거나, 직접 웹 브라우저를 이용하여 다운로드하거나 자유롭게 이용하실 수 있습니다. 참고로, Internet Explorer를 이용하여 직접 브라우저로 다운로드하게 되는 경우, 단일 파일의 크기가 4GB 이상인 파일을 다운로드받을 수 없다는 점만 유의하시면 되겠습니다.
소프트웨어 개발이나, IT 기술을 개발하기 위하여 더 이상 불법 다운로드 서비스를 이용하지 마세요. DreamSpark를 통하여 최신 기술을 항상 편리하게 이용하고, 손쉽게 다운로드받으실 수 있습니다. 그리고, DreamSpark와 더불어서 비 정기적으로 진행되는 Windows 및 Office의 대학생 할인 프로모션 프로그램도 자주 있으니 최신 소프트웨어와 IT 기술의 혜택을 편리하게 가져가실 수 있으면 좋겠습니다.
애자일(Agile) 프로그래밍 기법 등이 대중화 되면서, 특히 XP(eXtreme Programming) 에서는 단위 테스트의 코드를 먼저 작성하라고 합니다. 그것이 바로 TDD(Test Driven Development) 입니다.! 그 이유는 다들 아시다시피 간단합니다. 바로 코드를 작성할 때 설계부터 하라는 것입니다. 좀 직설적으로 얘기하자면, 생각 좀 하고 만들라는 것이죠. 생각 없이 만들 코드를 나중에 리팩토링(Refectoring) 할 바에는 처음부터 리팩토링 비용을 줄이고, 좀 더 세련된 디자인으로 코드를 작성하라는 의미입니다.
단위 테스트(Unit Test) 라는 의미에서도 사실 개발자와 테스터, 고객과는 굉장히 괴리감이 있는 단어이기도 합니다. "단위 테스트" 라는 똑같은 단어를 사용하지만 그것을 받아들이는 사람의 직책이나 파트(Job) 이 어디냐에 따라 틀리기도 합니다. 아마도 애자일(Agile) 이라는 트랜드를 아느냐 모르느냐의 기준이 될 수 도 있습니다.
개발자 - "테스트 코드를 만들라는 의미군!"
테스터 - "나더러 테스트 코드를 만들라거야? 아니면 각 기능별로 테스트를 하라는거야?"
고객 - "기능별 테스트를 어떻게?"
단위 테스트에 대한 개발자의 입장
우선 개발자를 봅시다. 필자는 개발자에게 TDD 를 강요하기란 굉장히 어려운 문제라고 생각합니다. 테스트 코드를 작성하는 것은 만들어진 코드의 양에 비례하여 추가적인 테스트 코드를 작성해야 합니다. 테스트 코드를 만드는 것이 문제가 아니라, 시간이 아깝다는 것이죠. 테스트 코드를 만들 시간에 더 생산성 있는(폼-Form 빼는 작업) 이 훨씬 더 나을 것입니다. 사실 우리나라 SI 프로젝트의 문제가 일단 소프트웨어를 만들고 난 다음에, 유지 보수 계약으로 버그를 잡아 치웁니다.
이런 구조는 실제 개발했던 사람과 유지 보수 하는 사람이 교체되면서, 유지 보수 인력은 이미 떠난 개발 인력들에게 "뭐 이런 쓰레기 코드를..!" 이라는 말을 할 수 밖에 없죠. 다시 개발했던 당사자의 입장으로 돌아가 보면, 빨리 빨리 대충 돌아가는 기능을 만드느냐, 느릿 느릿하지만 완벽한 기능을 만드느냐라는 기로에 설 수 밖에 없습니다. 여기서 고객은 언제나 쉽게 맘이 변합니다. 기존의 기능이 추가되거나 변경되면서 이미 완성된 기능에 지속적으로 덧칠을 할 수 밖에 없습니다.
그럼 TDD 를 왜 해야 하는가에 대한 의문점을 가질 수 밖에 없습니다. 단위 테스트조차 여유로운 작업이 아님이 분명한데, 테스트를 먼저하라니!!! 만약 누군가가 그것을 저에게 강요한다면 충분히 해명하거나 변경의 근거가 다분할 것입니다. 아마도 필사적으로 반대 입장에서 논의를 할 지도 모릅니다. 잘 차려놓은 테스트 코드를 돌려보는 관리자의 입장에서는 완벽한 프로세스일지 몰라도 적어도 개발자의 입장에서는 테스트 코드, 아니 TDD 마저 그저 먼 산일 뿐입니다.
단위 테스트에 대한 테스터의 입장
일반적으로 엔터프라이즈 어플리케이션(Enterprise Application) 은 기반이 되는 어플리케이션 프레임워크(Application Framework) 를 구축합니다. 범용 프레임워크를 이용하여 개발자가 사용하거나 소프트웨어나 업무 구조에 맞도록 도메인 집약적인 프레임워크로 파생하거나 전환됩니다. 이 과정은 진정한 기반 프레임워크에서 파생될 수 도 있고, 단순한 라이브러리 형태로 파생될 수 도 있습니다.
일단 팀의 구성원은, 개발 팀, 테스트 팀으로 구분합니다. 개발 팀은 오직 기능을 구현하고, 테스트 팀은 구현된 기능을 수동 테스트(Manual Test) 를 하거나 단위 테스트 코드를 작성하여 테스트를 수행합니다. 즉, 역할이 완벽히 분리되어 개발 인력은 개발에만, 테스트 인력은 원칙에 따른 테스트만 수행하는 형태입니다. 테스터가 테스트를 진행하면서 개발자가 구현해 놓은 기능에 결함이 발견되면, 개발자에게 이것을 알리고 반복적으로 테스트만 전담합니다. 이 프로세스에서 팀 간의 커뮤니케이션은 문서가 될 수 도 있고, 메신저가 될 수 도 있고, 메일이 될 수도 있습니다. 그것은 테스터에게 그리 중요한 것이 아닙니다.
하지만, 점점 더 고객은 특정 프레임워크나 아키텍처에 종속되길 원하지 않습니다. 그 이유는 과거의 잘못된 방법론이나 Non OOP(Object Oriented Programming) 의 확장성에 매우 민감합니다. 즉 기존에 어떤 아키텍처를, 어떤 프레임워크를 쓰던지 간에 현재와 미래가 더 중요할 수 밖에 없습니다. 이러한 과도기에서 과연 테스터는 무엇을 할 수 있을까요?
OOP 기반 프로그래밍은 추상화입니다. 그리고 추상화를 하다 보면 다형성을 따라가게 됩니다. 개발자나 아키텍처에게는 박수를 쳐줘야 하겠지만, 테스터의 입장에서는 전혀 그렇지 않을 수 있습니다. 즉, 테스터는 테스트 코드를 작성하기 위해 전체적인 아키텍처까지 이해해야 하는 어처구니 없는 상황이 발생할 것입니다. 아마도 잘 나가는 개발자들은 Mockup 을 이야기 할 것입니다. 그런데 누가 Mockup 을 할 겁니까? 아무것도 모르는 테스터? 아니면 코드를 작성한 개발자?
얼마 전에 엔터프라이즈 시스템의 확장성과 안정성에 대해서 고객과 미팅을 한 적이 있습니다. 고객은 현재의 프레임워크는 특정 프레임워크에 종속되어 확장이 불가능하며, 다른 프레임워크로 교체마저 불가능하다고 합니다. 그리고 과거의 기술을 사용하기 때문에 특정 벤더의 지원이 부족하고 그것을 이해하는 내부 직원도 없을 뿐더러, 시스템이 간혈적으로 다운되기도 한다고 합니다. 시스템이 돈(Money) 과 직결되는 문제라면 어느 누구도 나서서 고치겠다고 할 사람 없지 않겠습니까?
차세대 시스템에 대한 목표를 언급하면서 SOA(Service Oriented Architecture) 의 구현 산출물인 ESB(Enterprise Services Bus) 에 대해서 열심히 설명하면서, 단위 테스트에 대해서 언급을 한적이 있습니다. 그리고 대부분 고객의 관리자가 젊은 개발자가 아니기에, 이러한 최신 트랜드의 아키텍처와 방법론(?) 을 설명하면 기대 반, 의심 반으로 기대 심리를 갖기도 합니다.
하지만, 고객에게 단위 테스트에 대한 이야기를 언급할 때는 전형적인 과거의 산출물을 상상합니다. 바로 테스트의 이력을 볼 수 있는 문서이죠. 하지만 현재는 이미 그런 단위 테스트의 자동화와 산출물은 자동화가 되었음에도 불구하고 오히려 고객은 과거의 자신의 경험이 비추어 이해합니다. 이미 현재 시스템도 단위 테스트를 수행했고, 철저히 감사했음에도 불구하고 현재 이지경에 이르렀다는 것이죠.
즉, "단위 테스트" 라는 똑같이 형용하는 문자임에도 불구하고, 서로의 이해가 너무나도 부족합니다. 아마도 단위 테스트의 개념조차 없었던 코볼(Cobol), 포트란(Fortran) 의 원시 언어로 돌아가는 기분이 들었습니다. (학문적으로 고급 언어에 속하지만 과연 현재도 고급 언어일까?…) 즉, 그들과 눈높이를 맞출 수 있는 방법은 목표대로 보여주던가, 아니면 우리를 믿지 못하고 과거의 경험에 빗대어 비교할 것인가…
그렇다면 누가 단위 테스트를 수행해야 하는가?
아마도 이 섹션은 특히 필자의 개인적인 생각이기도 합니다. 어쨌든 "단위 테스트"에 대해 이렇게 이해 관계가 얽히는 풀기 힘든 문제이기도 합니다.
그럼 결론적으로 과연 누가 테스트를 수행해야 하는가 입니다. 아마도 필자의 개인적이고 경험적인 부분이므로, 다른 의견이 있다면 조언 부탁 드립니다.
우선 개발 팀과 테스터 팀의 조직적인 분할이 필요합니다. 즉, 팀 별로 역할 범위를 정확하게 명시함으로써 프로세스적인 측면에서 해결해야 할 문제라고 생각합니다. 이러한 문제는 Microsoft Solution Framework(MSF) 에서 잘 설명을 하고 있습니다.
아래의 필자의 분류는 우리나라의 전형적인 프로젝트 진행 구조를 고려하였습니다.
개발 팀
코드 개발, 단위 테스트 코드 개발
테스트 팀
매뉴얼 테스트(Manual Test), 테스트 인프라 관리
관리자
코드 및 테스트 조율 및 소프트웨어 품질 조율
고객
소프트웨어 품질에 대한 논의 및 제안
즉, 필자가 이렇게 구분한 의도는 어느 누군가에게 책임을 소프트웨어의 품질에 대해 소위 몰빵(?) 할 수 없다는 것입니다. 결국 가장 유연해야 할 책임과 역할의 구분이 명확하지 않는다는 것은 스스로의 무덤을 파는 것과 다를 바가 없습니다.
단위 테스트에 대한 적절한 대안은 있는가?
필자는 위와 같은 이유로 팀과 역할을 구분하여 단위 테스트를 수행하는 것을 권장합니다. 물론 위의 팀별 역할은 정답은 아니고, 조직의 형태에 따라 유연하고 적절하게 배분이 되어야 할 것입니다.
필자는 "단위 테스트" 라는 주제로 개인적인 견해를 이야기했지만, 더 나아가 소프트웨어를 개발하기 위한 방법론(SDLC-Software Development LifeCycle) 과 고객이나 기업의 거버먼스와 비즈니즈 가치가 결합되지 않으면, 좋은 소프트웨어를 절대 나올 수 없다고 생각합니다. 즉, 절대적인 방법론이 있고, 방법론을 적용한 경험이 있다고 해서 좋은 소프트웨어가 나올 수 없습니다. 극단적으로, 전산화가 제대로 되지 않은 업무 부처가 있음에도 불구하고 SOA 를 적용한다고 해서 절대 통합과 공유라는 컨셉을 가질 수 없는 것과 마찬가지 입니다.
애자일(Agile)의 대가인 켄트 백(Kent Back)은 아래와 같은 이야기를 한 바 있습니다.
Individuals and interactions over processes and tools
프로세스와 도구보다는 개인과 상호작용을
대부분은 애자일 선언의 이러한 용어로 인해, 매우 혼란스러워하거나 기존의 프로세스를 갈아치우려고 합니다. 마치 애자일의 XP 에서 이야기하는 원칙에서 하나라도 빠트리면 애자일이 아닌 것처럼 말입니다. 애자일의 주요 키워드는 "변화" 이지, 절대 강요가 아닙니다. 만약, 고객은 철저한 기간 엄수를 요구하는 폭포수 모델(Waterfall Model) 을 강요하지만, 개발 팀은 애자일(Agile) 만 고집한다면 그것은 서로 파멸로 가는 길이며, 애자일의 진정한 의미를 이해하지 않았다고 볼 수 있습니다.
켄트 백(Kent Back) 은 아래의 링크를 통해 위의 원칙에 대한 오해를 풀어내고 있습니다.
Tools for Agility - A White paper by Kent Beck, Three Rivers Institute
저는 늘 의문을 품어왔습니다...는 훼이크고 이번 포스트를 준비하면서 의문을 가지게 되었습니다. 분명 병렬 프로그래밍의 정신은 남아도는 코어를 활용해서 협력을 해서 작업을 좀 더 빨리 끝내자고 하는 건데요, 그런면에서 '백지장도 맞들면 낫다'는 말은 병렬 프로그래밍의 정신을 잘 표현하는 선조들의 지혜라고 볼 수 있습니다. 그런데요.... 과연 백지장같이 갓난 아기도 혼자들 수 있는 걸 같이 드는게 과연 나은 일일까요? 오히려 혼자 할 때보다 못한 결과를 가져오지는 않을까요? 오늘은 그에 대한 이야기입니다.
- LINQ도 맞들면 낫다, 어헣.
LINQ는 데이터 쿼리에 가까운 표현을 사용하면서, 데이터 쿼리시에 직관적이고 선언적인 코드를 활용할 수 있도록 해주었는데요. 거기에 이전 포스트들에서 설명드렸던 Parallel.For나 Parallel.ForEach처럼 매우 간단하게 남아도는 코어를 활용할 수 있도록 하는 방법을 제공합니다.
using System; using System.Linq; using System.Threading.Tasks; using System.Threading;
namespace Exam15 { class Program { static void Main(string[] args) { int[] nums = Enumerable.Range(1, 10000).ToArray();
<코드1>을 보시면, 1부터 1000까지의 숫자를 가진 배열을 생성하고, 각 수를 제곱한 수를 구하는 코드입니다. 기존의 LINQ코드와 다른 점이 있다면, 제곱 연산을 수행하기 위한 데이터 소스인 nums에 대해서 AsParallel()을 호출했다는 것입니다. <코드1>에선 AsParallel()의 리턴타입이 ParallelQuery<int>인데요, LINQ에서는 Enumerable을 사용하지만, PLINQ에서는 ParallelEnumerable을 사용합니다.
<코드1>을 보면, 정말 간단하게 병렬 프로그래밍이 구현되는데요. 정말 저렇게 간단한 방법으로 병렬 쿼리가 실행되는지 확인하기 위해서 Task.CurrentId를 통해서 실행중인 스레드의 Id를 출력하도록 했습니다. 그리고 비교적 일관성 있는 결과를 얻기 위해서 Thread.Sleep를 통해서 실행을 조금 여유롭게 해줬죠. 결과를 보실까요?
3->1->4->2의 패턴이 반복되는 걸 확인하실 수 있습니다. 물론, 실행도중에 패턴은 바뀌기도 합니다만, 분명 AsParallel()메서드를 호출하는 것 만으로도 병렬 프로그래밍이 구현된 것이죠. 그런데, 출력되는 스레드의 아이디를 보면, 딱 4개만 생성된 걸 확인할 수 있는데요. 제 컴퓨터의 CPU가 쿼드코어라서 딱 4개만 생성된 것 같습니다. 그런데 왜 딱 4개만 생성된 걸까요? 이전에 TPL을 활용해서 작업할 때는 4개 이상의 스레드도 생성되어서 작업을 처리했는데 말이죠. 그건 PLINQ가 병렬 쿼리를 처리하는 방식에서 원인을 찾을 수 있습니다.
제가 술을 먹고 만취한 상태에서 글을 적어서 그럴까요? 아래 내용은 새빨간 거짓말 입니다!!! 낄낄낄-_-;;; 스레드가 4개만 생성된 건, PLINQ가 분할 알고리즘으로 구간 분할을 사용하기 때문에 그렇습니다. 그리고 정확한 설명은, PLINQ는 ParallelEnumerable타입 같이 병렬 쿼리를 돌려도 안전한 타입에 대해서는 주저없이 쿼리를 병렬화 해서 작업을 하지만, IEnumerable타입 같이 병렬로 쿼리를 돌릴 때, 안전하다고 보장할 수 없는 경우에는 순차적인 쿼리로(정확히 말하지만, 순차적인 쿼리가 아니라 Chunk 분할 알고리즘을 통해서 데이터 소스에 락을 걸고, 스레드가 한번에 작업할 덩어리를 떼어주는 형태로)작업을 하게 됩니다. 오해 없으시길 바랍니다! 어헣-_-;;;
PLINQ는 AsParallel()메서드로 데이터 소스에 대해서 병렬처리를 원했다고 하더라도 항상 병렬적으로 처리를 하지는 않습니다. 예를 들면, 작업이 너무나 간단해서, 병렬적으로 처리할 때 오히려 손해를 보는경우가 있습니다. 작업이 너무 간단하기 때문에 각 스레드가 처리하는 작업의 시간이 매우 짧고, 그래서 작업 처리에 걸리는 시간보다, 스레드 간의 작업전환에 더 많은 시간이 걸리는 것이죠. 그래서 PLINQ는 AsParallel()이 호출되면, 우선 쿼리를 분석합니다. 그리고 그 쿼리가 간단하다는 판단을 하면, 병렬적으로 처리하기 보다는 순차적으로 처리를 하게 되는 것이죠. <결과1>에서 스레드가 4개가 돌아간 것은, CPU의 코어가 4개 이기 때문에, 코어별로 스레드가 한 개씩 생성된 것입니다. 각 코어의 입장에서 보자면, 스레드가 한 개씩 있는 셈이므로 작업전환이 필요없겠죠. 참고로, 듀얼 코어인 제 노트북에서 실행한 결과는 아래와 같습니다.
패턴은 약간 불안정할 때도 있지만, 대략 1->2->3의 순서를 유지하고 있습니다. 그런데, 왜 이렇게 스레드의 개수를 정해 줄 수도 있게 했을까요? 바로 최적화 때문입니다. 기본적으로 PLINQ의 알고리즘은 많은 경우를 테스트해서 최적화 알고리즘을 만들어 놓았기 때문에, 대부분의 경우는 기본옵션으로 실행하는 것이 가장 좋은 결과를 냅니다. 하지만, 그렇지 못한 경우가 있을 수 있는데요. 그럴 때, 테스트를 통해서 적절한 스레드 개수를 지정할 수 있도록 옵션을 둔 것이죠.
위에서 쿼리 식이 단순하면, 순차적으로 실행한다고 말씀을 드렸는데요, 쿼리 식이 병렬로 실행하기에 안전하지 못한 경우에, 순차적으로 실행하다고 말씀을 드렸는데요, 그런 경우도 병렬적으로 실행을 강제할 수 있습니다. 쿼리 식에 '.WithExecutionMode(ParallelExecutionMode.ForceParallelism)'메서드를 추가하면, 기본 알고리즘과는 상관없이 무조건 병렬적으로 실행하도록 합니다. 실행시간을 테스트한다거나 할때 유용하게 사용할 수 있는 옵션이겠죠.
- LINQ 맞들기 취소는 어떠케?
이번에는 PLINQ 쿼리를 취소하는 방법에 대해서 알아보겠습니다. 지금까지 취소에는 CancellationTokenSource를 활용했었죠? 마찬가지 입니다. 똑같이 Token을 활용해서 취소에 사용하되, 사용하는 방법이 조금씩 다른 것 뿐이지요.
using System; using System.Linq; using System.Threading; using System.Threading.Tasks;
namespace Exam17 { class Program { public static int[] SimpleParallelTask(int[] source, CancellationToken token) { Func<int, int> square = (num) => { Console.WriteLine(Task.CurrentId); Thread.Sleep(10); return num * num; }; return source.AsParallel() .WithCancellation(token) .WithDegreeOfParallelism(3) .Select(square) .ToArray(); }
static void Main(string[] args) { CancellationTokenSource cts = new CancellationTokenSource();
<코드3>을 보면, AsParallel메서드의 결과로 리턴되는 ParallelQuery타입에 포함된 .WithCancellation메서드를 사용해서 PLINQ 쿼리에 CancellationToken을 넘겨준다는 것을 제외하고는 Parallel.For, Parallel.ForEach와 동일한 방법을 사용하고 있습니다. 결과도 예측할 수 있듯이 동일합니다.
(생략) 1 3 2 1 3 2 1 3 2 1 3 2 1 3 2 1 3
------------------------------------- 쿼리가 중간에 취소되었습니다. 계속하려면 아무 키나 누르십시오 . . .
<결과4> LINQ 맞들기를 취소한 결과.
- 마치면서
어떠셨나요? '백지장도 맞들면 낫다'는 속담이 PLINQ에서는 항상 참이 아니라는 게 말이죠. 이래서 병렬 프로그래밍이 어려운가 봅니다. 어허허허헣. 악플 사절(죽을때 까지 미워할거임)! 피드백 환영! 호호호호^^
- 참고자료
1. Essential C# 4.0, Mark Michaelis, Addison Wesley
모기와의 사투를 버린 끝에 이제야 컴퓨터 앞에 앉아 글을 쓸 수 있게 되네요(새벽 1시네요ㅡ.ㅡ) 아흑.
비록 눈이 따갑고 눕고 싶지만, 이제는 정말 제 자신과의 약속을 지키기 위해 한자 한자 적어나가렵니다.^^
지난 시간에 유효성 검사에 대해 살펴봤는데요. 이제 본론으로 넘어와서 적용해봐야겠죠?
유효성 검사 적용하기
저희가 USER 모델을 생성할때 엔터티 프레임워크(엔티티가 입에 붙었는데 한글판에 엔터티라고 명시되어있네요;;)를 통해 생성한 것 다들 기억하시죠? 엔터티 프레임워크의 경우 자동으로 모델 클래스를 생성해 주는 것도 다들 아실겁니다. 또한, 엔터티 프레임워크로 생성된 모델클래스를 직접적으로 컨트롤 할수 없다는 것도..
그렇다면 유효성 검사 부분은 도대체 어디다 둬야 한단 말이냐?
파샬 & 메타데이타 클래스 생성하기
메타 데이타 클래스를 만들어야 합니다. 또한 USER 모델에 해당하는 파샬 클래스도 생성해야합니다.
파샬 클래스의 경우 여러 파일, 여러 부분에 멤버나 메쏘드 등의 정의를 각각 두면 컴파일시에 이들 모두를 결합하게 되죠. 다들 아시는 내용!
여기서 잠깐, 엔터티 프레임워크로 생성된 모델 클래스의 소스를 잠깐 살펴보면,
모델 클래스가 파샬 클래스로 정의 되어 있는 것을 확인할 수 있습니다. 아~ 이러면 자동 생성된 이 모델 클래스는 건들 필요 없이 파샬 클래스를 하나 더 추가해서 그곳에다가 우리가 필요한 정의를 내려주면 되겠구나~ 라는 생각이 팍팍 드시죠?
그래서 추가해봤습니다. 동일한 이름의 모델 클래스를 하나 만들어 보죠. 그리고, 메타 데이타 클래스도 같이 만들겠습니다.
using System.ComponentModel.DataAnnotations;
using System.ComponentModel;
namespace MvcSite.Models
{
[MetadataType(typeof(USERMetaData))]
public partial class USER
{
}
public class USERMetaData
{
[Required(ErrorMessage="아이디 입력하셔야죠!")]
[StringLength(10)]
public object ID { get; set; }
[Required(ErrorMessage="이름 입력하셔야죠!")]
public object NAME { get; set; }
[Required(ErrorMessage="패스워드 입력하셔야죠!")]
public object PWD { get; set; }
[Required(ErrorMessage="이메일 입력하셔야죠!")]
[RegularExpression(@"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$",
ErrorMessage = "올바른 이메일 형식이 아닙니다.")]
public object EMAIL { get; set; }
}
}
메타 데이터의 경우 테이블의 필드값을 대신합니다. 즉 모델과 같아야 합니다.
메타 데이터를 만든 후 파샬로 된 모델(USER) 클래스에 MetadataTypeAttribute를 통해 USERMetaData을 정의합니다. 이렇게하면 1차작업이 완료됩니다. 실행해 보시면 잘 돌아갑니다. 확인페이지는 따로 보여드리지 않겠습니다^^ 글이 너무 길어지면 지루해지겠죠?
모델에 없는 필드 확인하기
우리는 패스워드 확인 필드를 갖고 있습니다. 필수값이고 비교도 해야하지만 테이블에는 없는 필드죠. DataAnnotation을 통해 나머지 필드들은 각각 비교는 했는데, 패스워드 확인 필드는 어떻게~ 어떻게~ 어떡하면 되냐고~ 띠리링~ 그냥 만들어!
헉. 뭐 만들면 되죠;;;
일단, ValidationAttribute를 상속 받는 PropertiesMatchAttribute라는 이름의 두 값을 비교할 커스텀한 DataAnnotation 클래스를 만듭니다. 중요한건 검사를 담당하게될 IsValid 메쏘드를 오버라이드해야합니다.
이렇게 만든 후에, 생성한 USER 클래스를 수정하도록 하겠습니다.
[PropertiesMatchAttribute("PWD", "CPWD",
ErrorMessage = "패스워드 확인 안하실거에요?!")]
[MetadataType(typeof(USERMetaData))]
public partial class USER
{ [Required(ErrorMessage = "패스워드 확인 입력하셔야죠!")]
public string CPWD { get; set; } }
public class USERMetaData
{
[Required(ErrorMessage="아이디 입력하셔야죠!")]
[StringLength(10)]
public object ID { get; set; }
[Required(ErrorMessage="이름 입력하셔야죠!")]
public object NAME { get; set; }
[Required(ErrorMessage="패스워드 입력하셔야죠!")]
public object PWD { get; set; }
[Required(ErrorMessage="이메일 입력하셔야죠!")]
[RegularExpression(@"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$",
ErrorMessage = "올바른 이메일 형식이 아닙니다.")]
public object EMAIL { get; set; }
}
USER 클래스에 커스텀한 DataAnnotation 정의를 추가했고요, 패스워드 확인 필드를 필수값으로 정의하였습니다.
여기까지 잘 오셨죠? 실행해 보도록 하겠습니다.
위 결과물은 모든 필드에 입력을 안하고 submit을 했을 경우고, 아래 결과물은 패스워드를 다르게 입력 했을 경우입니다.
일단 원하는대로 출력되는 것을 확인했습니다.
역시 급정리요
이번시간 역시 유효성 검사 부분을 다뤘고요, 메타 데이터와 파샬 클래스를 이용한 유효성 검사를 살펴봤습니다.
정말 간단한 내용인데 쓰다보면 길어지네요;; 더 간단하게 필요한 메시지만 전달하도록 노력하겠습니다.
프로그래밍 할 때 가장 자주 사용하는 자료구조가 바로 배열입니다. 배열을
사용하지 않고 프로그래밍 하기는 힘들죠^^.
그래서 이번에는 C++/CLI에서의 배열에 대해서 이야기하려고 합니다.
C++/CLI에서의 배열은 ‘array’
비관리 C++에서는 배열은 ‘[]’을
사용합니다.
int Nums[10];
char szName[20] = {0,};
그러나 C++/CLI에서의 배열은 ‘array’라는
클래스를 사용합니다.
int 형의 3개의 요소를
가지는 배열은 아래와 같이 정의합니다.
array< int >^ A1 = gcnew array<
int >(3);
array< int >^ A2 = gcnew array<
int >(4) { 1, 2, 3 };
array< int >^ A3 = gcnew array<
int >{ 1, 2, 3 };
다음은 간단한 사용 예입니다.
< 코드 1. >
int main()
{
array<
int >^ Numbers = gcnew array< int >(5);
for(
int i = 0; i < 5; ++i )
{
Numbers[
i ] = i;
System::Console::WriteLine(
Numbers[i] );
}
getchar();
return
0;
}
array에 유저 정의형 사용하기
array에는 기본형(int,
float 등)만이 아닌 유저 정의형도 사용할 수 있습니다. 다만 비관리 클래스는 안됩니다. 오직 관리 클래스(ref class)만 가능합니다. 또 그냥 ref 클래스를 그대로 넣을 수는 없는 클래스의 핸들을 사용해야 합니다(ref
클래스는 GC에 동적 할당을 하기 때문이겠죠).
ref class refTest
{
};
array< refTest >^ arrTest;// 에러
array< refTest^ >^ arrTest;// OK
for each 사용하기
앞서 <코드1>의
예제에서는 배열의 모든 요소를 순환하기 하기 위해 ‘for’문을 사용했습니다. 그러나 .NET에서는 for문
보다 ‘for each’문을 사용하는 것이 성능이나 안정성 등에서 더 좋습니다(다만 for each를 사용하면 내부에서 값을 변경할 수 없다는 문제는
있습니다).
< 코드 2. >
#include <iostream>
int main()
{
array<
int >^ Numbers = gcnew array< int > { 10, 11, 12, 13, 14 };
약속은 하는 것 보다, 취소하는 게 어렵습니다. 게다가 취소할 때는 적절한 타이밍을 놓치면, 안 좋은 기억만 남기게 되죠. 그래서 프로그램에서도 취소를 제대로 할 수 있도록 지원하는 게 중요합니다. 누구나 실수 할 수 있거든요.
- TPL과 함께 취소 좀 더 쉽게하기. 어헣.
TPL은 두가지 형태의 병렬성을 지원합니다. 첫 번째는 작업 병렬성(Task Parallel)이고, 두 번째는 데이터 병렬성(Data Parallelism)입니다. 작업 병렬성은 하나 이상의 작업이 동시에 진행되는 것을 말하구요, 데이터 병렬성은 연속적인 데이터에 대해서 동일한 작업이 동시적으로 수행되는 것을 말합니다. 기존까지 Task클래스와 관련해서 살펴봤던게 작업 병렬성을 위한 것이었다면, 이번에는 데이터 병렬성을 지원하는 부분을 살펴보겠습니다.
데이터 병렬성을 매우 손쉽게 지원하기 위해서 System.Threading.Tasks.Parallel클래스에 병렬성을 지원하는 for와 foreach를 추가했습니다. Parallel.For와 Parallel.ForEach가 바로 그 것인데요. 하나씩 살펴보겠습니다.
using System; using System.Linq; using System.Threading.Tasks;
for (int i = 0; i < 10; i++) { Console.WriteLine(nums[i].ToString()); } } } }
<코드1> 간단한 병렬 예제.
<코드1>을 보면, 1부터 1000까지의 정수 배열을 만든 뒤에, 각각의 수를 제곱하는 코드입니다. i번째의 숫자를 제곱해서 그 결과를 i번째 인덱스에 넣는 작업과, i+1번째의 숫자를 제곱해서 그 결과를 i+1번째 인덱스에 넣는 작업은 별개의 작업이며, 동시에 수행가능한 작업이죠. 저렇게 for와 거의 비슷한 모양으로 작성하고, for대신에 Parallel.For를 써주는 것 만으로도 남아도는 CPU의 코어를 활용할 수 있다니. 간편하죠?
Parallel.ForEach와 병렬 루프에서 예외를 처리하는 부분은 이미 다룬 부분이기 때문에 건너뛰구영. 바로, 병렬 루프를 취소하는 방법에 대해서 알아보겠습니다. 지난 포스트에서 작업을 취소하는 방법에 대해서 알아봤었는데요. 이번에도 크게 다르지 않습니다. 동일하게 CancellationTokenSource와 CancellationToken클래스를 활용합니다. 다만, 방법이 약간 다른데요, 예제를 보시죠.
using System; using System.Collections.Generic; using System.Linq; using System.IO; using System.Threading.Tasks; using System.Threading;
namespace Exam14 { class Program { static void Main(string[] args) { CancellationTokenSource cts = new CancellationTokenSource(); ParallelOptions parallelOptions = new ParallelOptions { CancellationToken = cts.Token }; cts.Token.Register( () => Console.WriteLine("Cancelling....") );
cts.Cancel(); task.Wait(); foreach (var file in fileList) { Console.WriteLine(file); } Console.WriteLine("총 파일 개수 : {0}",fileList.Count()); } } }
<코드2> 병렬 루프에서의 작업 취소.
<코드2>를 보면, Parallel.ForEach이용해서, 음악 파일 중에서 10메가가 넘는 파일만 찾아서 리스트에 담고 있습니다. 그리고 루프 취소와 모니터링을 위해서 CancellationTokenSource, CancellationToken클래스를 활용하고 있습니다. 다른점이 있다면, 병렬 루프에 옵션을 주기 위해서 ParallelOptions클래스를 사용하고 있다는 것이죠. 그리고 생성된 ParallelOptions타입의 객체에 Token을 주고, 그 객체를 Parallel.ForEach루프에 매개변수로 넘겨주고 있습니다. 결과를 보면, 늦게 취소를 한 경우에는 리스트가 모두 완성된 반면에, 빨리 취소를 한 경우에는 리스트가 만들어지다가 만 걸 확인할 수 있죠.
끝내려면 엔터키를 누르세용. 파일 개수 : 2746
Cancelling....
(중략...)
05 サンクチュアリ.mp3 06 空のように 海のように.mp3 07 月の虹.mp3 총 파일 개수 : 380 계속하려면 아무 키나 누르십시오 . . .
<결과1> 늦게 취소해서 다 완성된 리스트.
끝내려면 엔터키를 누르세용. 파일 개수 : 2746
Cancelling....
(중략...)
01.mp3 02.mp3 03.mp3 01.うるわしきひと.mp3 총 파일 개수 : 256 계속하려면 아무 키나 누르십시오 . . .
<결과2> 중간에 취소해서 만들어지다 만 리스트.
ParallelOptions를 통해서 CancellationToken을 받은 병렬 루프는 내부적으로, IsCancellationRequested속성을 계속해서 주시하고 있습니다. 그리고, 이 취소 요청이 들어와서 이 속성이 true가 되면, 그 이후로는 새로운 루프가 시작되는 걸 막아버리는 것이죠. 그리고 병렬 루프가 취소되었음을 외부에 알릴 수 있는 유일한 방법이 OperationCanceledException을 통해서 인데요. <코드2>를 보면, catch를 통해서 예외를 잡긴하지만, 무시해버렸습니다. 그래서 Register메서드를 통해서 등록된 "Cancelling...."이라는 메세지가 출력되고 프로그램이 종료된 것이죠.
- 마치면서
역시 병렬처리를 간단하게 만들어 주는 만큼, 병렬처리를 취소하는 방법도 최대한 간단하게 만들어 주네요. TPL만쉐이! 어헣.