288 Results for 'Programming/Silverlight'

  1. 2009.03.27 실버라이트 3의 새 기능 정리 (2)
  2. 2009.03.27 데이터 바인딩#1 맛보기
  3. 2009.03.26 실버라이트 3에 대한 생각 (2)
  4. 2009.03.19 [MIX09] 첫날 키노트 (1)
  5. 2009.02.25 [HOL#00] 핸즈 온 랩 시리즈 인덱스 (2)
  6. 2009.02.25 [HOL#06] RenderTransform 기초
  7. 2009.02.25 [HOL#05] Bubbling Events - 이벤트의 라우팅 (2)
  8. 2009.02.25 [HOL#03] VisualTreeHelper (2)
  9. 2009.02.25 실버라이트에서 사용 가능한 crossdomain.xml의 조건 (4)
  10. 2009.02.19 실버라이트 2.0.40115.0 업데이트[추가] (5)
  11. 2009.02.19 Prism 2.0 for WPF & Silverlight
  12. 2009.02.13 [TIP] 불필요한 이미지 리소스 제거하기 (2)
  13. 2009.02.12 Moonlight 1.0 정식 출시
  14. 2009.02.11 Silverlight Streaming에 동영상 올리기#2
  15. 2009.02.10 Silverlight Streaming에 동영상 올리기#1
  16. 2009.02.10 [HOL#09] MediaElement
  17. 2009.02.10 [HOL#04] Event Handling (2)
  18. 2009.02.10 [HOL#02] Built-in Container(Layout)
  19. 2009.02.10 [HOL#01] Hello Silverlight
  20. 2009.02.05 앗 이럴쑤가! Mixtify 10k 등록 되었어요 >_< (3)
  21. 2009.02.02 My Mixtify 10k entry - Ink 10k (10)
  22. 2009.02.01 [Tip] Blend에서 실행중인지 아닌지 구분하는 방법 (1)
  23. 2009.01.31 환상의 궁합! MVVM+Command Pattern+Dependency Injection #2 (5)
  24. 2009.01.31 환상의 궁합! MVVM+Command Pattern+Dependency Injection #1 (4)
  25. 2009.01.30 라이브러리 문서화의 중요성 (2)
  26. 2009.01.16 커스텀 컨트롤의 DefaultStyle을 여러 xaml로 나누기
  27. 2009.01.16 Blend 2 SP1에서 인텔리센스를! (6)
  28. 2009.01.06 SBS드라마 가문의 영광 홈페이지 오픈 (1)
  29. 2008.12.11 Why not Silverlight? (3)
  30. 2008.11.20 스토리보드 거꾸로 재생하기 (1)

What’s New in Microsoft Silverlight 3 : http://videos.visitmix.com/MIX09/T14F 참고 :D

Silverlight 3 툴 지원

  • Visual Studio 2008 Tools for Silverlight 3 beta/RTM
    • XAP 압축률 향상
      • 10~30% 감소된 XAP파일 크기
    • 인터랙티브 디자이너
      • Visual Studio 2010에서 지원 예정
  • Expression Blend 3 preview
    • 최종 버전은 올해 말

Media Format 지원

  • H.264/AAC/MP4 디코더 지원
  • 산업 표준 포맷으로 광범위하게 사용되고 있으므로 지원하기로 결정
  • H.264 파일로 할 수 없는 것
    • DRM 지원
    • Windows Media Server에서 스트리밍 호스팅

GPU 가속 지원

  • 옵트인 기능 즉, 명시적으로 사용할 것을 지정해야 함
    • Param으로 EnableGPUAcceleration, EnableCacheVisualization를 true로 설정
    • 최종 서피스 렌더링에 GPU를 사용
  • 엘리먼트 마다 옵트인으로 적용
    • CacheMode="BitmapCache"로 설정해야 함
    • 여러 개의 엘리먼트가 조합되어 렌더링 될 때 GPU를 사용
    • 엘리먼트를 늘릴 때(Stretch) GPU를 사용
  • 브라우저와 full-screen 모드에서 모두 동작
  • GPU 가속이 가능한 클라이언트에서 성능 향상
    • 다른 어떤 비주얼도 수정하지 않아도 됨

(데모)
미디어 관련 : T43F, T19F, T56F
GPU 관련 : T17F

Perspective 3D(Projection)

  • Perspective 3D
    • 2D 오브젝트를 3D 공간에 배치
    • X,Y,Z 회전 및 X,Y,Z 회전 축 지정
    • 지역/전역 X,Y,Z 옵셋 지원
  • Element.Projection
    • 현재는 PlaneProjection만 지원

애니메이션 가속도 지원

  • 기본 지원되는 애니메이션 가속도 함수
    • CircleEase, SineEase, BackEase
    • ExponetialEase, PowerEase
    • QuadraticEase, CubicEase
    • QuarticEase, QuinticEase
    • ElasticEase, BounceEase
  • 커스텀 가속도 함수도 작성 가능
    • 자신만의 가속도 함수를 사용 가능

비트맵 이펙트와 픽셀 셰이더

  • 이펙트
    • 비주얼의 동작에 영향을 줌
    • 기본 지원되는 이펙트
      • Blur, DropShadow
  • 커스텀 이펙트
    • 커스텀 이펙트는 Shader로 구현됨
    • HLSL이라는 표준 코드를 사용
      • DX SDK 유틸리티를 사용하여 byte code로 컴파일
      • 실버라이트 3는 컴파일 된 byte code를 사용
    • 셰이더는 UI 엘리먼트가 렌더링 되기 전에 각각의 픽셀을 변경할 수 있는 기회를 제공
    • 셰이더 = 픽셀 단위 함수 혹은 명령
    • (Green screen 등)

픽셀 API

  • 두 개의 파츠
    • 동적 비트맵 생성
      • WriterbleBitmap
      • 비트맵에 있는 픽셀을 읽고 쓰기
    • 비주얼 트리를 비트맵에 렌더링하기
  • 시나리오
    • 동적인 이미지 생성(실시간 그래프 등)
    • 이미지 편집 및 효과
    • 비주얼 복사
      • reflections, drag effect 등

Raw 오디오/비디오 API

  • 픽셀 API와 비슷하지만 오디오/비디오를 위한 기능
  • 시나리오
    • 동적으로 사운드 생성
    • 커스텀 오디오/비디오 디코더 작성(DIVX, XVID 등)

자세한 데모 : T17F

로컬 메시징 지원

  • 실버라이트 플러그인 간 데이터 통신
    • 같은 페이지에 있는 여러 플러그인
    • 다른 브라우저 탭에 있는 여러 플러그인
    • 다른 브라우저에 있는 여러 플러그인
  • 구현
    • 공유 메모리(Shared memory)로 구현
    • "named pipes"와 같이 노출됨
    • 문자열 기반의 메시지

UI Framework 향상

  • 리소스 병합 기능(Merged Resource Dictionaries)
  • BasedOn 스타일(스타일 구조화)
  • 런타임에 스타일 변경 가능
  • 다중 선택 리스트박스
  • "Handled"된 라우트 이벤트를 수신하는 것이 가능
  • VSM에 "Invalid" 상태 추가
    • 입력 상태가 유효하지 않을 때를 의미
    • TextBox, CheckBox, ComboBox, ListBox, RadioButton 등

상세 : T16F

기타 향상

  • SystemColors
    • 플랫폼에서 사용중인 색상 테마
  • FileSaveDialog 지원
    • 명시적인 UI를 통한 저장 위치 결정 다이얼로그를 통해 파일 위치 결정
    • 보안상의 이유로 유저가 선택한 파일을 열고 쓸 수만 있음
  • 텍스트 향상
    • 클리어 타입 텍스트(베타 이후에 지원 예정)
    • CaretBrush(텍스트 박스 안에 있는 커서)
    • 텍스트 애니메이션을 위한 최적화 옵션
      • 텍스트가 정확한 픽셀에 정렬하기 위해 걸리는 것을 선택 가능
    • Glyphs도 시스템 폰트를 지원
  • 이미지 정렬

새 SDK 컨트롤

  • DockPanel, Expander, Label, TreeView, ViewBox, WrapPanel, ChildWindow, DatePickerTextBox, TabPanel
  • +기타…

그 외 중요한 추가 사항

  • 비즈니스 관점의 지원
    • 내비게이션 프레임워크(Frame, Page 모델)
    • 데이터 건트롤
    • SEO(검색 엔진 최적화) 지원
    • N-Tier 데이터 모델 지원
    • T40F 참고
  • WebService 스택 향상
    • Binary XML, SOAP faults, Credentials 지원
    • T42F 참고
  • Silverlight Toolkit
    • T15F 참고

"Out of the Browser" 지원

  • 실버라이트를 브라우저 바깥에서 실행 가능
    • 그러나 일반 애플리케이션과는 다르게 철저하게 Sandbox 보안 모델 내에서 실행됨
    • 브라우저에서 실행하는 것과 정확하게 동일한 보안 모델 및 사용성을 가짐
  • 실버라이트 코어 런타임에 통합
    • 브라우저 바깥에서 실행 될 때 SilverlightLauncher가 실행되고 그 위에서 동작
  • 애플리케이션 단위로 사용 가능
    • Manifest에 애플리케이션을 설명
    • Deployment.ApplicationIdentity
  • 설치 경험
    • 우클릭하여 Install 선택
      • 데스크탑과 프로그램 그룹에 추가할지 여부 선택
    • 또는 설치를 위해 커스텀 버튼을 추가 가능
      • Application.Current.Detach
    • 삭제는 단순히 우클릭 후 Uninstall 한방으로 끝
  • 새 네트워크 API
    • 연결/연결 안됨 상태
    • 상태 변경됨 이벤트
  • Offline API
    • 브라우저에서 실행되었는지 브라우저 바깥에서 실행되었는지 상태
      • Application.Current.RunningOffline
    • 업데이트 API 등
      • 기본적으로 Online상태일 경우 새 버전의 XAP을 자동으로 설치
  • 비즈니스 관점의 지원
Posted by gongdo

음… 한 달 전에 월간 마이크로소프트에 실버라이트 데이터 바인딩에 대해 기고를 했는데요, 3월호에 실렸더군요. 전 모르고 있었는데…(이건 뭔가… ┐─) 이 글은 총 3회로 나누어 쓴 글로 현재 2회까지 기고를 했고 아마 4월호에 두 번째 내용이 나갈거에요. 글의 저작권은 제게 있지만 월간 마이크로소프트가 나오면 그 때 올리도록 할께요.

--------------------------

데이터의 표현과 로직의 분리

표현과 로직의 분리. 얼마나 아름다운 말인가! 그러나 우리는 십수년간 사용해왔던 HTML의 경험으로부터 결코 쉽지만은 않다는 걸 배웠을뿐이다. 그렇지 않은가? 여러분은 디자이너가 만든 HTML 레이아웃을 손대지 않고 얼마나 작업할 수 있는가? 여러분은 데이터의 표현을 얼마나 협업의 고통 없이 수정할 수 있는가?

실버라이트가 출시된지도 벌써 2년이 다 되어간다. 결코 긴 시간은 아니지만 경험을 쌓기엔 충분한 시간이다. 현장에서 접하는 실버라이트는 어떤 모습인가? 필자를 포함한 얼리어답터들이 주장하던 것 처럼 정말로 표현과 로직이 잘 분리되어 디자이너와 개발자가 원활한 협업을 하고 있는가?

필자의 답은 ‘아니오’이다. 우리는 여전히 HTML 시절에 겪었던 문제를 고스란히 안고 있고 여전히 디자이너의 결과물을 개발자가 망치고 있으며 데이터의 표현이 변경될 필요가 있을 때 디자이너와 소모적인 코드 수정을 하고 있다. 다만 HTML에 비해 레이아웃이나 표현을 하기가 쉬워졌고 엄격한 XML 문법으로 인하여 구조적인 문제가 줄었을 뿐이다.

그렇다면 도대체 무엇이 문제일까? 단지 디자이너가 툴에 익숙하지 않아서 협업이 이루어지지 않는다고 말한다면 그것은 ‘비겁한 변명’일 것이다. 필자는 실버라이트가 표현과 로직을 분리하기 위한 도구와 기법들을 제공함에도 불구하고 별반 나아진게 없는 문제의 원인을 패러다임의 변화에 적응하지 못하는 우리 개발자에게 돌리고 싶다. 그러나 처음부터 너무 비관적으로 될 필요는 없다. 문제를 발견했다면 그것을 해결할 뿐. 오히려 문제를 푸는 과정이 재밌다고 느껴지지 않는가? 기존의 경험으로부터 정리된 통찰을 이끌어내는 과정은 짜릿하지 않은가?

물론 모든 문제를 해결하는 마법 같은건 존재하지 않지만 적어도 몇몇 문제를 해결하는 좋은 방법 정도는 존재하는 법이다. 앞으로 연재될 이 글을 통해 데이터의 표현과 로직을 보다 효율적으로 분리하는 방법을 연구해보고 아주 조금이라도 디자이너와 개발자가 협업의 고통을 줄였으면 하는 바램이다.

처음 기고하다 보니 의욕이 넘쳐 서론이 길었다(^^a). 그럼 본격적으로 데이터와 표현과 로직을 분리하기 위해 반드시 익혀야 할 실버라이트의 데이터 바인딩에 대해 연구해보자.

전형적인 데이터 표현 과정

메신저나 커뮤니티 회원 명부 등에서 볼 수 있는 회원 카드 애플리케이션을 생각해보자. 단순하게 ID, 사진, 이메일 주소, 블로그 주소, 생일 정보 정도면 충분할 것이다. 필자는 다음과 같은 구조의 프로젝트를 만들어 봤다.


[그림 1. 샘플 프로젝트의 구조]

먼저 데이터를 다루는 Model 폴더에는 회원 정보 클래스인 Profile.cs가 있고 회원 정보를 제공하는 서비스를 시뮬레이션하는 MockProfileService가 있다. 물론 제대로 하려면 웹 서버를 구축하여 서버에서 데이터를 가져와야 겠지만 그렇게 구성되었다고 가정하겠다. MockProfileService는 다음과 같이 회원 정보를 랜덤하게 반환하는 GetAnyProfile 메서드와 주어진 회원 정보를 업데이트하는 UpdateProfile 메서드를 노출하고 있다.

public Profile GetAnyProfile() //생략
public void UpdateProfile(Profile profile) //생략

[리스트 1. MockProfileService 클래스가 노출하고 있는 메서드]

다음으로 회원 카드를 보여주기 위한 유저 컨트롤인 CardView가 View폴더에 있다. 익스프레션 블렌드를 사용하여 다음과 같이 간단하게 디자인했다.


[그림 2. 간단한 회원 카드 디자인(말할 것도 없이 엉망이다.)]

디자인은 IdLabel, ProfileIamge, BritdayInput과 같이 직관적인 이름으로 오브젝트를 배치했다. 뭔가 아니다 싶은 디자인이 보이는가? 이것이 필자가 개발자라는 직업을 선택한 이유 중 하나이다. 어쨌든 지금은 디자인의 품질이 중요한게 아니므로 이쯤하고 넘어가도록 하자.

앞에서 디자인한 유저 컨트롤의 코드 비하인드인 CardView.xaml.cs 파일을 열어보면 Profile 속성을 설정했을 때 해당 회원의 데이터를 화면에 보여주고, 또한 TextBox의 Text속성이 변경되었을 때 ProfileUpdated 이벤트를 던지는 코드가 작성되어 있다.

private Profile _profile;
public Profile Profile
{
    get { return _profile; }
    set
    {
        _profile = value;
        // 입력 받은 정보로 UI를 업데이트
        IdLabel.Text = _profile.Id;
        BirthdayInput.Text = _profile.Birthday.ToString("yyyy-MM-dd");
        EmailInput.Text = _profile.Email;
        BlogInput.Text = _profile.Blog;
    }
}

// 입력창의 포커스가 벗어났을 때 값을 변경하는 코드들
void BirthdayInput_LostFocus(object sender, RoutedEventArgs e)
{
    Profile.Birthday = DateTime.Parse(BirthdayInput.Text);
    FireProfileUpdated();
}

[리스트 2. CardView의 코드 비하인드에 작성된 지루한 코드의 일부]

마지막으로 루트 비주얼인 Page.xaml에는 버튼 하나와 MyCard라는 이름의 CardView 하나를 올려놓았고 Page의 코드 비하인드인 Page.xaml.cs를 보면 MockProfileService의 인스턴스를 하나 만들어놓고 컨트롤이 초기화되거나 버튼을 클릭했을 때 서비스의 GetAnyProfile 메서드를 호출하여 MyCard의 Profile 속성을 설정하게 되어 있으며, MyCard에서 ProfileUpdated 이벤트가 발생했을 때 MockProfileService의 UpdateProfile 메서드를 호출하여 변경된 데이터를 업데이트하고 있다.

솔루션을 빌드하고 실행해 보면 버튼을 클릭할 때마다 샘플로 작성한 데이터 중 하나를 랜덤으로 표시하는 것을 볼 수 있고 텍스트 박스 중 하나를 변경하면 변경된 데이터가 실행되고 있는 동안에는 유지되는 것을 확인할 수 있다. 자세한 코드와 동작은 다운로드 받은 파일의 압축을 풀고 [DataBinding Take1] 폴더에 있는 소스코드를 열어 직접 확인하길 바란다.


[그림 3. 회원 카드 애플리케이션의 동작 화면]

어쨌든 이 애플리케이션은 잘 동작한다. 실무에서 이 정도 코드면 충분하다. 아니, 충분했었다. 그럼 도대체 무엇때문에 잘 동작하는 코드를 바꿔야 할까? 이 프로젝트는 협업과 유지보수의 관점에서 적어도 네 가지 이상의 문제점을 가지고 있다. 여러분도 읽기 전에 한 번 생각해보길 바란다.

  • 불필요한 임시 데이터가 XAML에 직접 하드코딩 되어 있다. 혹시 임시 데이터라고 아무렇게나 데이터를 넣었다가 실제 런타임에 고객에게 노출된 적은 없는가?
  • 디자인에 들어가야 할 구성요소의 종류가 강제되고 있으며 이름을 정확히 맞춰주지 않으면 CardView.xaml.cs에 있는 코드는 무용지물이 된다. 디자이너의 표현력이 코드 때문에 제한받고 있지 않은가?
  • CardView에서 Profile 속성의 세터(setter)를 보자. Profile 데이터 하나하나를 다시 설정하고 있다. 만약 더 많은 속성이 추가되거나 변경된다면 그때마다 이렇게 단순 반복적이고 소모적인 코드를 다시 작성하고 싶은가?
  • 디자인이 변경되었을 때 역시 이 과정을 다시 반복할 것인가?
  • 또한 입력한 값이 유효한지 확인하는 처리까지 들어간다면 복잡도는 더욱 늘어날 것이다.

데이터 바인딩 예제

데이터 바인딩은 컨트롤의 특수한 속성과 XAML의 몇 가지 표현식을 사용한다. 데이터 바인딩을 설명하기 전에 먼저 사용 예제를 보도록 하자. 앞에서 작성한 코드에서 아주 조금만 고쳐도 상당히 쾌적해진다. 물론 모든 문제를 한꺼번에 해결할 수는 없지만 하나씩 단계적으로 잡아 나가도록 하겠다. 역시 자세한 코드는 [DataBinding Take2 - binding basic] 폴더의 코드를 참고하길 바란다.

먼저 CardView에 작성했던 지루한 설정/업데이트 코드들을 시원하게 날려버리자. 컴포넌트 초기화를 제외한 모든 코드를 말 그대로 지우면 된다. Profile을 받고 XAML에 어떤 디자인에 어떻게 설정해야 할지 고민할 필요가 없다.

public partial class CardView : UserControl
{
    public CardView()
    {
        InitializeComponent();
    }
}

[리스트 3. CardView.xaml.cs: 단순해진, 아니 텅비게 된 CardView의 코드 비하인드]

앞의 코드를 제거하면 업데이트 등으로 사용했던 이벤트가 삭제되어 Page.xaml.cs에서 오류가 발생한다. 여기에서도 불필요한 이벤트 처리등을 제거하자. 다만 버튼을 클릭하여 회원 정보를 다시 받아야 할 때 다음과 같이 MyCard의 DataContext 속성을 설정하기만 하면 된다.

private void UpdateProfile()
{
    // 서비스 호출(웹 서비스 등의 호출을 통해 받았다고 가정)
    Profile profile = _service.GetAnyProfile();

// 회원 카드의 정보 업데이트
    MyCard.DataContext = profile;
}

[리스트 4. Page.xaml.cs: CardView에 대한 의존성이 줄어든 Page의 코드 비하인드]

CardView의 인스턴스인 MyCard를 사용하는 곳이 UpdateProfile 메서드 단 한 곳으로 줄었다. 나중에 뭔가 변경이 생기더라도 간편하게, 대부분은 전혀 손대지 않고도 적용이 가능할 것이다.

다음으로 DataContext에 설정된 회원 데이터를 CardView에 보여줘야 한다. CardView.xaml에 데이터 바인딩 표현식을 사용하여 데이터를 표시하면 된다. 지면 관계상 핵심적인 부분만 표시하였다.

<TextBlock Text="{Binding Id}" />
<Image Source="{Binding Picture}" />
<Grid >
        <TextBlock Text="Birthday" />
        <TextBox Text="{Binding Birthday, Mode=TwoWay}" />
        <TextBlock Text="Email" />
        <TextBox Text="{Binding Email, Mode=TwoWay}"  />
        <TextBlock Text="Blog" />
        <TextBox Text="{Binding Blog, Mode=TwoWay}" />
</Grid>

[리스트 5. CardView.xaml: Binding 표현식으로 설정된 CardView]

센스있는 독자라면 굳이 설명하지않더라도 위의 코드가 무엇을 의미하는지 눈치 챘을 것이다. <TextBlock Text="{Binding Id}" …/> 코드를 예로 들어 간단하게 말로 하자면 “TextBlock의 Text 속성을 Id라는 이름을 가진 속성으로 바인딩(Binding)한다.”라고 할 수 있다. 물론 여기에서 Id라는 이름을 가진 속성은 CardView 유저 컨트롤의 DataContext에 설정된 데이터에서 가져오는 것임은 말 할 것도 없다.

아래쪽에는 좀 더 흥미있는 표현식이 보이는데, <TextBox Text="{Binding Birthday, Mode=TwoWay}" …/> 코드를 예로 들자면 “TextBox의 Text 속성을 Birthday라는 이름을 가진 속성으로 바인딩하되, 바인딩 모드는 양방향(TwoWay)로 한다.”라고 할 수 있다. 여기에서 TwoWay라는 모드는 그 이름에서 추측할 수 있듯이 바인딩 된 대상 오브젝트의 속성이 변경될 경우 역으로 바인딩 된 원본 데이터의 속성도 변경시켜 서로 동기화시킨다는 의미이다. 보통 TextBox나 CheckBox, RadioButton과 같이 사용자가 값을 입력하거나 선택할 수 있는 컨트롤의 속성에 많이 이용된다. 실행결과는 당연하게도 앞의 프로젝트와 동일하다.


[그림4. 데이터 바인딩을 사용한 실행 화면]

자 어떤가? 데이터 바인딩을 사용하여 50라인이 넘는 코드를 삭제하고 아주 단순한 코드와 표현식만으로 서비스에서 받은 데이터를 효과적으로 표시하는 것이 가능해졌다. 특히 CardView의 XAML 코드에는 x:Name이라는 속성 없이도 데이터를 표현하는 것이 가능하므로 디자이너가 더 이상 개발자와 골치아픈 이름 짓기 문제로 싸울 필요가 없게 되었다. 물론 의미가 있는 오브젝트에 x:Name으로 그 오브젝트를 설명하는 것은 훌륭한 시도이다. 그러나 이 이름은 어디까지나 참고일 뿐이지 실제 코드 작성에 반드시 필요한 것이 아니므로 디자이너든 개발자든 마음대로 이름을 지어도 된다. 이것만으로도 행복하지 않은가!

또한, CardView를 제어하고 CardView에서 일어나는 동작을 감시하여 별도의 처리를 해줘야 했던 Page 유저 컨트롤 역시 CardView와의 의존성이 줄어들고 CardView에게 필요한 데이터를 줘 버리고 모른척할 수 있게 되었다. 서로의 의존성을 줄이는 것, 이것이야말로 개체지향 프로그래밍의 성배가 아니었던가!

물론 여기에는 문제점도 남아있다. 블렌드로 CardView 유저 컨트롤을 열어보자.


[그림5. 데이터 바인딩된 유저 컨트롤의 모습]

데이터 바인딩을 위해 사용한 표현식때문에 앞에서 임시로 넣었던 데이터는 온데간데가 없다. 다만 해당 속성에는 데이터 바인딩을 사용하고 있음을 알리는 노란색 테두리가 있을 뿐. 이래서야 디자이너가 디자인을 변경할 때 결과물을 쉽게 예측하기 어렵다. 아니, 불가능하다.

또한 데이터 바인딩을 사용한 실행 결과를 보면 날짜 필드가 우리가 기대한 것과는 달리 년/월/일만 나온게 아니라 월/일/년에 시간까지 나온다. 이래서야 단순 바인딩은 제대로 쓸 수 없다.

사실 필자도 처음에는 이런 문제점 때문에 데이터 바인딩을 사용하지 않았었다! 그렇다면 데이터 바인딩으로 디자이너와의 협업을 보다 원활하게 한다는 필자의 말이 거짓이란 말인가. 물론 아니다. 필자는 앞으로 진행되는 연재를 통해 이 문제를 우아하게(?) 해결하는 법을 소개하고자 한다. 물론 데이터 바인딩을 소개하기 위해 건너뛴 데이터 바인딩의 이론적인 기초 및 중요한 문법에 대해서도 소개할 것을 약속한다.

참고 자료

Posted by gongdo

매년 MIX에서는 실버라이트에 대한 굵직한 릴리즈와 정보가 쏟아지죠. 이번 MIX09에서는 드디어 Silverlight 3가 베일을 벗었어요. 2가 나온지 얼마나 되었다고 벌써 3가 다가오고 있죠. 물론 잦은 릴리즈가 현업에 압박으로 다가올 수도 있지만 개발 프로세스를 잘 준비한다면 2에서 할 수 있는 것과 3에서만 되는 것… 이렇게 구분을 하여 대응할 수 있을거에요.

마이크로소프트는 실버라이트의 릴리즈 주기를 9~12개월로 빠르게 한다는 계획을 발표한 적도 있는데요, 그만큼 RIA의 개발이 정형화되지 않았기에 그만큼 현업의 요구를 빠르게 수용하겠다는 의미로 받아들일 수도 있죠.

앞으로의 실버라이트의 큰 뼈대는 2를 기반으로 계속하여 새로운 기능이 추가되는 형식이다…라고 얘기들 하는데요, 물론 닷넷 코드 베이스라는 개발 프로세스를 놓고 보면 맞는 말이지만, 솔직히 제 느낌으로는 오프라인 지원 등의 너무나도 굵직한 내용 때문에 3를 기반으로 새로운 기능이 추가되거나 향상된다… 라고 얘기하고 싶어요.

어쨌든, 실버라이트 3가 제게 강한 인상을 주는건 단순히 새로운 기능들 때문이 아니에요. Perspective 3D, 비트맵 프로세싱, GPU 가속, H.264 지원(아직 불완전하지만), 오프라인 및 데스크탑 설치 지원, 폰트 가독성의 지속적인 향상, 더 많은 컨트롤 지원, 스타일 병합, 브라우저 스택을 사용하지 않는 순수 HTTP 요청, 블렌드 3의 비약적 향상, 데이터 친화적인 개발 환경… 이것들은 바로 실버라이트 2에서 Adoption Blocker로 작용하던 요소들이었고 실버라이트 3는 이런 요소를 적극적으로 해소하기 시작한 첫 버전이라고 생각해요. 전에 없이 현업의 요구사항과 개발 커뮤니티에서 나온 훌륭한 실험 결과를 적극적으로 그리고 대규모로 수용한 첫 버전이란 말이죠.

실버라이트 1.1과 2에서는 사실 몇몇 시나리오에서 필요한 기능을 중심으로 홍보되었고 또 사용되었어요. 바로 미디어와 딥줌이죠. 물론 텍스트 지원 향상, 컨트롤 모델 지원 등의 굵직한 내용도 있었고 미디어와 딥줌 외의 다른 분야에도 시도된 사례가 많지만 상업적으로 의미가 있었던 건 역시 미디어와 딥줌 뿐이었다고 생각해요.
(그간 다른 곳에 적용하려고 얼마나 삽질을 했었던지 =_=)

실버라이트 3는 이제 그간 플래시만이 독보적인 지위를 차지하고 있던 모든 RIA 시장에 참가할 수 있게 되었어요. 뿐만 아니라 깜짝 놀랄만한 오프라인/데스크탑 설치 시나리오 지원으로 AIR만이 할 수 있었던 그것도 가능해졌죠. 비공식적이기도 하고 물론 짜는 사람에 따라 다르겠지만 단순한 코드 실행 성능은 이미 실버라이트 2가 앞서 있다는 점을 감안했을 때 앞으로의 상황이 너무나도 기대되고 흥분돼요.

자, 좋은 얘기는 다 했고 아쉬운 점을 얘기해보자면… 웹 카메라와 마이크로폰! 최근 닌텐도 DS에도 채용되어 더욱 재밌는 사용자 경험을 제공할 수 있는 기회가 아쉽게도 다음으로 미뤄졌어요. 뭐 크게 아쉬운 기능은 아니지만서도 말이죠. 다음으로 블렌드. 물론 이번에 엄청난 기능 향상과 추가가 있었지만 제가 보기엔 (아직 써보지 않았어요) 아주 자잘한 인터랙션들은 크게 개선되지 않은 것 같아요. 예를 들어 타임라인에서 키프레임을 드래그하여 선택한다거나 하는… 사실 없어도 못쓰는 건 아니지만 그런게 디자이너의 접근을 막는 요소이기도 하죠. 여튼 이 얘기는 글로벌 서밋때도 나왔으니 아마도 개선될 거에요.

아직 써보지도 않고 써내려간 얘기라 기대감이 더 크네요. 부디 2 베타때 겪었던 엄청난 버그의 홍수는 없길 바라면서 버추얼 머신에 실버라이트 3 개발환경을 설치하며 주절거려봤어요. Silverlight Rocks! XD

Posted by gongdo

아침을 후다닥 먹고 Bill의 키노트 듣는 중…
무선 인터넷 연결되는게 없네요 –_-

최근 들어 마이크로소프트의 컨퍼런스에 빠지지 않는 내용은 바로 Experience죠. 키노트에서도 사용자 경험에 대한 얘기를 무척이나 강조하네요.

특히 디자인 경험에 있어서 스케치, 프로토타이핑에서의 과정과 문제점들 종이를 사용한 유용한 기법들에 대한 간단한 소개도 있었고요. 또한 어떤 상태가 전환될 때에는 트랜지션Transition이 수반되어야 과정의 변화를 자연스럽게 전할 수 있다는 얘기를 강조… 아마도 실버라이트의 Visual State Manager를 염두한 것이겠죠. 그러면서 살짝 SketchFlow의 스크린 샷도 나오고요.^^ SketchFlow는 아래에…

여튼 마이크로소프트가 UX와 디자인 경험에 얼마나 투자를 하고 있는지 강조하는게 인상적이었고 저도 좋아하는 모델인 마이크로소프트 ARC 마우스의 디자인 사례도 소개하더군요. 또 ZUNE의 사례에서는 단순한 하드웨어 혹은 소프트웨어가 아니라 eco-system을 고려하는 설계 철학도 설명하고요.

 

다음으로는 스캇거쓰리 쑈쑈쑈~

Expression Web 3가 제일 먼저 소개되었는데요, 이런저런 향상이 있지만 가장 인상적이었던 것은 SuperPreview라고 불리우는 기능이에요. SuperPreview는,

- 클라우드에 올라가 있는 렌더링 엔진을 사용
- 인스톨되지 않아도 다른 브라우저들을 사용하여 preview
- 오버레이, 스플릿 모드 등으로 동시에 각 브라우저간 차이를 찾을 수 있음
- SuperPreview: IE6, 7, 8은 지금 Beta 가능

이런 특성들을 가지고 있는데요, 진짜 웹 호환성 작업에 엄청나게 도움을 줄 거에요. 진짜 대단!

 

다음으로는 ASP.NET MVC 1.0
- Shipping today! 라는군요^^

ASP.NET 4 and VS 2010에 대한 소개도 있었고… 
- Web Forms
- MVC
- AJAX
- Distributed Caching

- Code focused improvements
- javascript/AJAX/jQuery tooling
- SharePoint
-Publishing & deployment

 

 

웹 개발에 필요한 도구를 App Gallery까지 포함하여 설치할 수 있는 Web Platform Installer에 대한 소개도 있었어요.
정말 편리할 듯!

 

Commerce Server 2009도 – Today! 릴리즈되고요^^

 

다음으로 드디어 실버라이트 3 소식.

실버라이트를 사용한 새 버전의 버추얼 어스가 준비되고 있다는군요. 거 왜 우주까지 보이는거 있잖아요.
Netflix사와 실버라이트 미디어 경험에 대한 얘기가 이어졌고요.
 

SL3 Media
- GPU 가속
- New codec (H.264, AAC, MPEG-4)
- Raw bitstream Audio/Video API
- Improved logging for media analytics

IIS Media Services
- On-demand smooth streaming
- Live smooth streaming[new]
- Edge caching
- Web playlists
- Bitrate throttling
- Advanced logging

와 같은 기능이 제공된대요. 역시 가장 환영할만 한 건 GPU 가속을 사용한 성능 향상!
그리고 Smooth Streaming은 사용자의 대역폭에 따라 서버에 준비된 다양한 bitrate의 동영상을 자동으로 선택하여 스트리밍하는데 중간에 버퍼링이 거의 없게 만드는 기술이죠. 이건 직접 보면 끝내줘요^^
그렇지만 가장 인상적이었던 건 라이브 방송을 보면서도 내부적으로 데이터를 저장하고 있다가 일시정지나 이전 장면 조그셔틀과 같은 기능을 사용할 수 있어요. 이제 TV 필요 없겠어요 정말…;

NBC 방송은 2010 밴쿠버 올림픽 중계에 실버라이트의 이런 모든 기능을 적극적으로 사용할 것이며 100% Smooth Streaming과 Full 720p HD 화면으로 중계할거래요.

 

Graphics 기능에서는,
- GPU
- Perspective 3D
- Bitmap & pixel API
- Pixel shader effect
- DeepZoom improvements

가 제공되는데요, 스캇구가 Perspective 3D를 활용한 멋진 데모를 보여줬어요. 이건 말로는 어려우니 나중에 동영상으로 확인하시길.

 

Application Development 을 위해 다음과 같은 사항들이 지원된다고 하고요,
- Deep linking, navigation and SEO
- Improved text quality
- Multi-touch support
- 100+ controls
- Library caching support

 

Vertigo에서 구현한 RollingStone 잡지 데모가 나왔어요.
기본적으로 딥줌을 사용한 것이지만 정말 차원이 다르네요.
검색, DeepLink, DeepZoom, Perspective 3D가 녹아들어간 데모. 직접 보셔야해요!

 

Epxression Blend 3에는 다음의 기능이 추가되고요

Workflow
Expression Blend 3
- SketchFlow
- Adobe Photoshop & Illustrator file import
- Behaviors
- Designing with data
- Source code control
- Xaml, C# & VB code intellisense

 

그리고 이번 키노트에서 가장 멋졌던 것은 바로 Expression Blend 3에 탑재된 Sketch Flow! 
근데 이건 꼭꼭꼭 동영상을 봐야 해요!

SketchFlow
- Prototype에 적합한 컨트롤 스타일을 제공하고,
- SketchFlow Player
- with Transition(Animation)
- Feedback을 직접 컨셉위에서 수정
- Document export 기능!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- Behavior를 사용하여 Action, Navigate 등을 사용 가능!! 커스터마이징하여 복잡한 Drag&Drop등까지도 가능
- Photoshop importing
- SampleData 매니징 기능, Drag&Drop –> 곧바로 ListBox로 삽입 –> Stle을 사용하여 단순한 ListBox를 수정
- DataTemplate도 향상되었네~

 

Eclipse Support for Silverlight on MAC, 이건 뭐 그닥…

 

나머지는 졸려서 그냥 메모만…

Data
- Data binding improvements
- Validation error templates
- Server data push improvements
- Binary XML networking support
- Multi-Tier REST data support

 

Silverlight Business Apps
- SAP NetWeaver Support

 

Out of Browser Scenarios
- Extend media experiences
- Companion applications for your web site
- Lightwegiht data snacking applications

- Simple consumer friendly experience
- Safe, secure, sandboxed environment
- Built-in auto-update support
- Build offline-aware applications
- Integrate with underlying operating system

KEXP
90.3 FM Seattle / Tom Mara & Aaron Starkey
- 뭐야 Air랑 똑같이 되잖아~~~
- Desktop 바로가기 도 가능
- Offline/Online 감지
- 자동 업데이트

 

이 모든 기능들이 들어간 실버라이트의 다운로드 용량은 무려 2에 비해서도 40kb가 줄어들었대요!
그러면서도 당연히 Silverlight 2와 하위호환성을 갖고요.

 

마지막으로 오늘 지금 당장 다운로드 받을 수 있는 것들.

Download Now!
- Silverlight 3 beta : http://www.microsoft.com/downloads/details.aspx?FamilyID=D09B6ECF-9A45-4D99-B752-2A330A937BC4&displaylang=en
- Expression Blend 3 beta : http://download.microsoft.com/download/F/9/C/F9C8EAEE-B3B6-44C9-9854-3C0AF0602AE1/Blend_Trial_en.exe
- Silverlight 3 Tools for Visual Studio 2008 : http://www.microsoft.com/downloads/details.aspx?FamilyID=11DC7151-DBD6-4E39-878F-5081863CBB5D&displaylang=en
- IIS Media Services : http://www.microsoft.com/web/gallery/install.aspx?appsxml=www.microsoft.com/web/webpi/2.0/WebProductList.xml;www.microsoft.com/web/webpi/2.0/WebProductList.xml&appid=145;146

 

단, 모든 베타/프리뷰는 기존 버전을 삭제하고 설치해야 하니까 개발 머신에서 하기보담 VPC등을 이용하세요^^

저도 빨리 깔아보고 싶은데 시간이 부족하네요.
그럼 또 중계를…

Posted by gongdo

그간 몇몇 업체들에 실버라이트 교육을 다니면서 쌓아두었던 Hands On Lab 자료를 방출합니다. 사실 네이버 실버라이트 카페에는 이미 자료로 올라와 있는 상태인데, 정작 제 블로그에는 안올렸더군요.^^

핸즈온랩은 아주 기초적인 부분을 하나하나 설명하는게 아니라 그냥 순서대로 따라하면서 익힐 수 있도록 준비했어요. 실전은 감이 중요하달까요? 대략 아래의 내용이 준비되어 있고 계속 추가할 예정(어디까지나 예정 ㅋㅋ). 잘게 쪼갠건 물론 포스팅 개수를 늘리기 위해서아주 짧은 시간 동안 하나씩 클리어하는 기분을 느껴보시라는 배려에요(…).

번호가 안정해진 건 언제 할지 모른다는 얘기. 암튼 계속 연재할 수 있도록 부담을 팍팍 밀어주세요.^^

Posted by gongdo
RenderTransform은 모든 UIElement가 가지고 있는 속성으로 해당 오브젝트의 레이아웃(위치, 크기 등의 속성)이 확정된 이후 그 상태에서 확대, 기울기, 회전, 이동 등의 추가적인 변형(transform)을 설정해요. 좀 더 구체적으로 말하자면, 실버라이트 렌더링 엔진은 어떤 UIElement를 화면에 렌더링 할 때 먼저 모든 오브젝트의 레이아웃을 정리하고 그 후 각 오브젝트별로 RenderTransform을 적용하여 최종적으로 화면에 보여주죠.
RenderTransform속성을 수정해도 레이아웃에 영향을 주지 않아서 비용이 많이 들어가는 레이아웃 정리 작업을 수행하지 않으므로 특히 애니메이션 등의 고속 처리에 매우 유용해요.

Posted by gongdo
몇몇 이벤트는 이벤트가 발생한 자식에서 부모 방향으로 VisualTree를 타고 올라가는 성격을 가지고 있죠. 대표적으로 MouseLeftButtonDown과 같은 이벤트인데요, 이런 이벤트를 '버블링'된다 라고 말해요. 마치 버블을 타고 올라가는 것 처럼 말이죠.

바로 이렇게!

여튼 백문이 불여일타! HOL을 보고 직접 느껴보세요.

Posted by gongdo
후우.. 오랫만이네요. 늘 그렇듯이 바쁘죠. 바빠.
아주 짧은 HOL이에요. VisualTree에 대한 간략한 설명과 함께 VisualTreeHelper를 설명하고 있죠.


Posted by gongdo

훈스닷넷의 스터디 모임에서 크로스 도메인 접근에 대한 라이브 데모를 하다가 황당한 상황을 겪었는데요, 올바른 것으로 보이는 crossdomain.xml 파일이 있어도 실버라이트가 접근에 실패하는 경우가 있어서 포스팅합니다. 그나저나 라이브 데모는 항상 자제해야 해요. 역시 –_-;

여러 차례 블로깅했던 것 같지만, 실버라이트에서 크로스 도메인에 있는 리소스에 접근하기 위해서는 해당 서버가 실버라이트를 위한 clientaccesspolicy.xml 또는 플래시에서 사용하는 crossdomain.xml을 제공해야 하죠. MSDN의 도메인 영역을 넘을 수 있는 서비스 만들기 문서에 잘 설명이 되어 있어요.

그러나 실버라이트는 crossdomain.xml이 제공하는 모든 기능을 다 사용할 수는 없고 다만 모든 도메인에 대해 허용하는 설정만을 처리할 수 있죠. 그래서 보통 다음과 같은 설정만을 허용해요.

<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
    <allow-access-from domain="*" secure="true" />
</cross-domain-policy>

여기서 가장 중요한 부분은 allow-access-from 엘리먼트에 domain="*" 설정이 반드시 존재해야 하고 secure 어트리뷰트가 true이어야 한다는 점이죠. secure 어트리뷰트는 옵셔널이고 없을 경우 true이기 때문에 없어도 무방해요. 또한 다음과 같이 다른 allow-access-from 엘리먼트와 함께 있는 것도 상관 없어요. 예를 들어, openapi.naver.com/crossdomain.xml을 보면…

<!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
    <allow-access-from domain="static.campaign.naver.com" secure="false" />
    <allow-access-from domain="flashdev.naver.com" secure="false" />
    <allow-access-from domain="*" secure="true" />
</cross-domain-policy>

allow-access-from 엘리먼트가 여러 개 있는 걸 볼 수있죠. 그렇지만 여전히 domain 어트리뷰트와 secure 어트리뷰트가 적절하게 설정되어 있어서 실버라이트에서 접근할 수 있어요.

문제는, MSDN에서는 정확히 문서화되어 있지 않지만 아무리 allow-access-from 엘리먼트에 domain 어트리뷰트와 secure 어트리뷰트가 적절하게 설정되어 있다고 해도 그 외에 다른 어트리뷰트가 정의되어 있을 경우 해당 설정은 실버라이트에서 접근할 수 없는 것으로 판단해버려요. 예를 들어, gongdosoft.tistory.com/crossdomain.xml을 보면…

<!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
    <allow-access-from domain="*" to-ports="*" />
</cross-domain-policy>

언뜻보면 될 것 같지만 to-ports라는 실버라이트가 인식할 수 없는 어트리뷰트가 붙어 있어서 여기에 대한 요청은 실패해요.

여러 가지로 테스트 해 봤는데 결국 실버라이트가 허용하는 crossdomain.xml의 조건은 다음과 같이 정리할 수 있어요.

  • allow-access-from 엘리먼트에 반드시 domain="*" 어트리뷰트가 있어야 한다.
  • secure 어트리뷰트는 반드시 true이어야 하고 생략해도 된다.
  • 위의 두 어트리뷰트 외에 어떠한 어트리뷰트도 들어가서는 안된다.

아직까지 실버라이트를 위한 clientaccesspolicy.xml을 지원하는 웹사이트는 그리 많지 않아요. 그래서 crossdomain.xml과의 공생은 당분간 필요하죠. 혹시 crossdomain.xml이 있는데도 불구하고 크로스 도메인 접근이 되지 않는다면 위의 조건에 맞는지 점검해 보세요. 그리고 제대로 되어 있지 않다면 해당 서비스 제공 업체에 수정을 요청할 수도 있겠죠. 저도 당장 티스토리에 요청해야겠어요!

Posted by gongdo

18일, 실버라이트 2의 업데이트가 조용히 릴리즈 되었네요.
실버라이트 SDK가 깔려 있을 경우에는 http://go2.microsoft.com/fwlink/?LinkID=119972 여기에서 받으세요. 최종 사용자용과 다른 점은 디버깅 지원을 위한 몇 가지 어셈블리가 포함되어 있다는 것 외에는 똑같아요.
(최종 사용자용 런타임은 기존과 마찬가지로 http://go.microsoft.com/fwlink/?LinkID=107362 여기)

참고로 GDR은 General Distribution Release의 약자로 말 그대로 일반 배포용 릴리즈를 말하죠.
GDR 1로 표시된 걸로 봐서 GDR 2도 나올 가능성도? ^^

  • McAfee가 스캐닝할 때 실버라이트에서 발생하는 문제 수정.
  • UI Automation 안정성 수정
    • .Net Framework 3.0또는 3.5가 설치되어 있지 않을 때 몇몇 기능을 사용하면 실패하는 문제
    • 태블릿 지원 향상
  • Mac유저가 환경 설정을 커스터마이징하여 Arial과 Verdana 폰트를 제거했을 때 발생하는 문제 수정.
  • 몇몇 언어에서 Isolated Storage의 IncreaseQuotaTo 메서드를 사용할 때 발생하는 문제 수정.

예… 뭐 특별할 건 없고요. 아마 윈도 업데이트 등으로 업데이트가 자동으로 배포될 듯하고 딱히 개발 환경에 영향주는 일은 없겠네요.

저 처럼 괜히 설레이지 마세요 ㅋ

[추가]
밝힐 수 없는 소스에 의하면(ㅋㅋ 이런 말 해보고 싶었어..) 위의 요소 외에도 몇 가지 업데이트가 있었다고 해요.
뭐 그래도 딱히 중요한 요소는 별로 없지만 다른 OS에서의 호환성이나 이런 면이 나아졌다고 하니 업데이트 해보세요.


한 가지 작은 수정 점. 마우스 오른쪽 클릭했을 때 나오는 컨텍스트 메뉴에서 Configuration이 빠졌어요. 한글 윈도에서는 Silverlight 구성으로 나오다가 Silverlight만 표시되죠. 근데 어딘가 썰렁해 보이는 건 좀... -_-


인증샷.ㅋ

Posted by gongdo

[추가]
앗! 아니나 다를까 킹크랩에 잘 소개가 되어 있었군요. 필독!
http://kingcrap.com/entry/Composite-Application-Guidance-for-WPF-June-2008-Release
[수정]
앗! 이건 작년에 나왔던 WPF 전용 버전에 대한 내용이었네요;; 날짜를 잘 못봤습니...
그래도 기본 골격은 같으니 참고;;
--------------------------------------------------------------------------------------

Pattern & Practice 팀에서 다시 한번 유용한 라이브러리를 공개했네요. Prism이란 이름으로 개발되던 라이브러리인데 버전 2.0이 되면서 'Composite Application Guidance for WPF & Silverlight'라는 무시무시한 이름으로 공개를 했네요. 걍 프리즘이라고 계속  부르면 안되나 –_-;

Composite Application Guidance(이하 프리즘 2–_-)는 WPF와 실버라이트 애플리케이션을 보다 모듈화 된 형태로 쉽게 만들기 위해 디자인되었다고 해요. Guidance라는 거창한 이름이 붙은건 이 배포 패키지가 단순히 라이브러리 뿐만 아니라 즉시 실행해 볼 수 있는 핸즈온랩과 XAML 및 디자인 패턴 가이드를 포함하고 있기 때문이에요.

프리즘 2는 사실 엔터프라이즈 비즈니스 영역에 포커스를 주고 있는데요, 많은 패턴이나 라이브러리/프레임워크가 그렇듯이 애플리케이션에 따라 이 라이브러리를 도입했을 때 장점이 클 수도 그렇지 않을 수도 있겠죠. 그러나 만약 비슷한 패턴이 필요로 할 때 이렇게 검증된 라이브러리를 쓰는 것이 직접 개발하는 것에 비해 훨씬 큰 이득을 얻을 수 있을 거에요.

요즘은 너무 바빠서 자세히는 못봤고, 링크 모음:

http://blogs.msdn.com/blaine/archive/2009/02/18/prism-2-0-is-live.aspx

http://msdn.microsoft.com/en-us/library/dd458809.aspx

Posted by gongdo

XAP 사이즈, 압축하지 않겠는가?에서 얘기한 것 처럼 실버라이트 배포에 가장 중요한 점 중 하나는 바로 용량을 줄이는거죠. 그래서 배경화면이나 복잡한 디자인 등에 사용하는 이미지 파일은 프로젝트에 포함하지 않고 웹 서버쪽에 두는 것이 정석이에요. 그런데 이렇게 하면 개발자들이야 어차피 디자인을 보면서 작업하는 일이 많지 않아서 별로 문제가 안되지만, 디자이너들에겐 치명적인 문제가 되죠. 생각해보세요 배경화면을 보면서 디자인을 해야 하는데 이미지가 프로젝트에 없어서 보이지 않는다니!

요컨대, 다음과 같이 배경 이미지 한장을 보여주는 아주 간단한 샘플을 놓고 보죠.

image image

최초로 Images 폴더에 ducks.jpg를 추가하면 기본적으로 Build Action이 Resource가 되어 프로젝트를 빌드할 때 어셈블리에 리소스로 포함되고 이 이미지의 용량은 고스란히 XAP파일의 용량이 증가하는 결과로 이어지죠. 그렇다고 이미지를 삭제하고 웹서버에만 놓기엔 디자이너가 볼 수가 없어서 애매하죠.

이럴 때는 간단하게 해당 이미지 파일을 프로젝트에 그대로 놓은 상태에서 Build Action만 None으로 설정하세요.

image

이렇게 하면 비주얼 스튜디오의 디자인 뷰에는 나오지 않지만 블렌드로 열어보면…

image

잘 보이죠? 그러면서도 배포 사이즈는 이미지의 크기가 빠지게 되죠.

별거 없지만 첨부된 프로젝트를 보면서 참고하세요.


Posted by gongdo

많은 실버라이트 블로그에서 Moonlight 1.0의 정식 출시를 축하하고 있네요.
대표로 스캇 형님과 Miguel의 블로그의 링크만 달아둡니다.

http://weblogs.asp.net/scottgu/archive/2009/02/11/moonlight-1-0-release.aspx
http://tirania.org/blog/archive/2009/Feb-11.html

문라이트는 일반적인 리눅스와 Unix/X11 기반의 OS에서 실버라이트를 구동하기 위한 플러그인으로, 현재 파이어 폭스 2와 3에서 동작한다고 해요. 문라이트는 리눅스에서 닷넷 프레임워크를 구현하는 Mono 프로젝트의 서브 프로젝트로 출발하여, 현재 Novell사와 마이크로소프트사가 협력하여 만들고 있어서 사실상 공식 지원이 된다고 얘기할 수 있어요.

실버라이트 3가 나오는 마당에 1.0이 웬말이냐…라고 하실지도 모르겠지만 실버라이트의 런타임은 하위 버전의 애플리케이션도 동작시킬 수 있어야 하니까 순서라는게 있는거죠. 문라이트팀이 실버라이트의 릴리즈를 따라잡길 바래요.

전 리눅스 안써본지도 몇 년이 되어서 리뷰는 패스 :D

Posted by gongdo

Expression Encoder는 다양한 미디어 파일을 인코딩하여 서버로 배포하기 위한 다양한 기능을 제공하는데요, 특히 실버라이트를 위해 인코딩된 결과물을 곧바로 Silverlight Streaming 서버에 올릴 수 있는 플러그인이 마련되어 있어요.

먼저 Expression Encoder 2Encoder Service Pack 1을 설치한 후 Silverlight Streaming Publishing Plug-In for Expression Encoder v2가 설치되어 있어야 해요. 그리고 당연하겠지만 Silverlight Streaming에 동영상 올리기#1에서 설명했던 것처럼 live 계정으로 streaming에 가입되어 있어야 하고요.

1. 우선 http://streaming.live.com/에 가서 Manage Account 메뉴를 클릭하면 자신의 Account ID와 Key를 볼 수 있어요. 이걸 잘 복사해 놓으시고 다음을 진행하세요.

image

2. 인코더를 실행하고 Import 버튼을 클릭해서 인코딩할 동영상 파일을 선택하거나 탐색기에서 드래그&드랍하여 인코딩할 동영상 파일을 Items에 올려놓으세요. 이 글은 인코더의 기능을 설명하고자 하는게 아니라서 다른 탭에 있는 여러가지 옵션과 기능들은 설명하지 않아요.

image

(인코딩 데모때마다 혹사당하는 Bear.wmv^^)

3. Silverlight Streaming에 게시하기 위해서는 먼저 출력 템플릿을 정해줘야 해요. 템플릿은 인코딩된 결과물로 자동으로 재생 가능한 실버라이트 미디어 플레이어 프로젝트와 웹 애플리케이션을 생성해주죠. 템플릿은 반드시 Silverlight 2에 있는 것 중 하나를 선택하세요.

image

템플릿을 선택하면 템플릿의 preview를 볼 수 있어요.

image

4. Output 탭의 하단에 있는 Publish 패널에서 Publish To를 Silverlight Streaming으로 선택하세요. 만약 Silverlight Streaming이 나오지 않는다면 플러그인을 제대로 설치하지 않아서 그렇겠죠?

image

5. Silverlight Streaming을 선택하면 다음과 같은 옵션들이 나올거에요. Publish after encode에 체크하고, 앞에서 얻었던 Account ID와 Account Key를 입력하세요. 접혀있는 패널을 열어보면 해당 계정의 남은 공간과 상태를 확인할 수 있어요.

image

6. 이제 Encode를 눌러 인코딩을 진행해보죠. 인코딩이 끝나면 다음과 같이 업로드가 시작돼요.

image

업로드가 완료되면 다음과 같이 자동으로 가상 서버가 하나 뜨면서 선택한 템플릿의 실버라이트 미디어 플레이어 애플리케이션을 볼 수 있죠.

image

7. 그럼 Silverlight Streaming에 어떻게 올라갔는지 볼까요? Streaming 페이지에서 Manage Account를 클릭하면 앞에서 인코딩할 때 설정했던 이름으로 애플리케이션이 올라가 있는 것을 볼 수 있어요.

image

클릭해 보면… 여러가지 옵션을 확인할 수 있죠. Launch Application Test Page를 클릭해서 업로드된 미디어 플레이어 및 동영상 파일을 확인해 보세요.

8. 이렇게 올린 애플리케이션은 두 가지 방법으로 퍼갈 수 있는데요, 아쉽게도 <object>로 곧바로 올리는 방법은 아직 지원되지 않고 iframe을 사용하거나 LiveControl을 이용해서 올리는 두 가지 방법만 지원해요. 그리고 한가지 또 아쉬운 점은 동영상 파일의 절대 URL을 알 수 없다는 점이죠.

Silverlight Streaming은 나름 활용할 여지가 많은데요, 기회가 되면 다른 활용 방법을 소개하도록 하지요.

Posted by gongdo

실버라이트로 미디어 플레이어를 만들다보면 가끔 소스를 올려놓을 적당한 곳이 없을 때가 있는데요, 이럴 때 Silverlight Streaming이라는 좋은 서비스를 잘 활용해 보세요.

Silverlight Streaming은 live.com에서 제공하는 서비스 중 하나로 개인 live계정에 대해 무료로 제공해줘요. 구체적으로,

  • 10GB의 저장 공간
  • 한달에 5TB의 트래픽
  • 실버라이트와 호환되는 WMV/VC-1 코덱의 동영상 하나당 104M까지
  • 또는 하나당 최대 1.4Mbps의 프레임 레이트와 최대 10분 길이의 동영상

이 정도면 꽤나 멋진 조건이죠. 특히 WMV 또는 VC-1 코덱으로 인코딩된 영상은 사이즈 제한 등이 없어서 쓸만해요. 게다가 Expression Encoder에서 곧바로 Silverlight Streaming에 인코딩된 영상을 올리는 기능을 지원해서 편리하죠. 이 외에도 광고 등의 조건에 따라 몇 가지 옵션이 더 있는데 여기에서는 가장 기본적인 계정만 만들어보도록 하죠.

http://streaming.live.com/에 가서 다음 순서에 따라 등록해 보세요.

 

1. live 계정으로 sign-in. 당연하겠지만 없으면 만들어야죠?^^

image

2. Sign-in 했으면 왼쪽에 Manage Videos를 클릭하고 Upload a video를 클릭. 오른쪽 아래에 보면 사용 가능한 공간도 나와 있죠.

image 

3. 파일을 선택하고 업로드. 이 때 "The video is a Silverlight-compliant WMV file."을 체크하면 업로드하는 파일이 실버라이트에서 재생 가능한 형식일 경우 별도의 인코딩 작업 없이 올라가요. 제목은 나중에 URL에서 접근할 때 사용하니 적당히 이름 지으면 되고요.

image

Upload를 클릭하면 파일이 업로드 되는데, 물론 시간대에 따라 다르지만 생각보다 업로드 속도가 괜찮아요. 지금 14MB에 2분25초라고 표시되어 있지만 실제로는 2분이 안걸렸어요. 처음 이 서비스가 나왔을 때에는 진짜 모뎀에 필적하는 속도였는데 이 정도면 별 불편없이 쓸 수 있을 듯.

image

4. 업로드가 끝나면 다음과 같이 업로드된 동영상의 목록과 재생된 회수, 그리고 남은 Streaming 공간 등이 표시돼요.

image

5. 업로드한 동영상 제목을 클릭해 보면 다음과 같이 해당 영상을 곧바로 재생할 수 있는 기본 미디어 플레이어가 하나 제공되고 그 플레이어를 퍼갈 수 있는 <iframe>태그와 동영상을 직접 링크할 수 있는 URL도 제공돼요.

사실 이 외에도 내가 만든 실버라이트 애플리케이션도 올리는 서비스도 있는데 그건 다음 기회에.^^

Posted by gongdo
사실 실버라이트로 할 수 있는 가장 효과적인 애플리케이션은 미디어 플레이어죠. 미디어 플레이어를 만들기 전에 먼저 미디어 파일에 접근하고 재생하며 정보를 얻는 기능을 제공하는 MediaElement에 대해 알아보는 핸즈온랩이에요.


미디어 플레이어 만들 때 동영상 올리기 힘들죠?
Silverlight Streaming을 써보세요. 가장 중요한 건 무료라는거~~
http://gongdosoft.com/374 여기 참고.
Posted by gongdo

사실 이 부분은 C#을 해보셨다면 아주 쉽게 넘어갈 수 있어요. 간단하게 어떤 오브젝트에 대해 이벤트 핸들러를 붙여 코드에서 제어하는 방법을 소개하고 있어요. 실버라이트에서 이벤트 핸들러를 선언하고 연결하는 방법은 크게 세 가지죠. 한번 확인해 보세요. :D


Posted by gongdo

핸즈온랩의 두 번째 시간인데요, 이 실습을 하기전에 먼저 Expression Blend를 열어서 그냥 눈에 익을 때까지 막 만져보세요. 정확히 뭐가 어떻게 돌아가는지 어떤 패널과 어떤 탭이 어떤 역할을 하는지 몰라도 돼요. 우선은 그냥 보고 느끼는거죠. 그리고 나서 이 실습을 진행하는 게 효과적일거에요.


Posted by gongdo

이제는 너무 많이 봐서 지겨울 수도 있지만, 역시 시작이 반이죠. Visual Studio를 설치하고 실버라이트 개발 환경을 갖췄다면 그냥 따라하면서 시작하세요. 개발 환경에 익숙치 않다면 의문이 많이 생기겠지만 지금은 우선 따라하는게 중요해요. 글쎄, 일단 해보시라니까요? :D


Posted by gongdo

http://2009.visitmix.com/MIXtify/TenKDisplay.aspx?SubmissionID=0113

올리고 5일이 지나도 등록이 안되길래 실패한줄 알았더니… 올라왔네요 아이고 좋아라 >ㅅ<

자 남은 건 여러분의 도움뿐! 점수좀 줍쇼~ 굽신굽신.

Posted by gongdo

My 10k entry has been accepted to 10k gallery!
Click here and check it out. :D

"Ink 10k" looks like simple painting application. But it draws "TEXT" whereas ordiniry applications draw just line or curve.
All text you draw is coming from Twitter. Yeah! It uses Twitter search API and dynamically grabs messages.
It recongnizes pressure of pen, if you use a pen tablet as well.
And also it has a undo/redo functionality by pushing 'Ctrl+Z' or 'Ctrl+Y'.

Here's my paint.

I know, I'm horrible at painting ;~>
Why don't you try better one?

Posted by gongdo

IsolatedStorage를 사용하면 Blend에서 디자인이 안보인다?에서 DesignerProperties 클래스를 활용하여 Blend에서 실행중인지 여부를 구분할 수 있다고 했는데요, DesignerProperties를 보다 정확하게 활용할 수 있는 간단한 방법을 소개합니다.

기본적으로 DesignerProperties는 다음과 같이 사용하면 되죠.

if (DesignersProperties.GetIsInDesignMode(AnyUIElementInVisualTree) == true)
    // 디자인 모드
else
    // Web 모드

간단하죠? 그런데 한 가지 문제는 반드시 인자로 VisualTree에 올라가 있는 UIElement를 필요로 한다는 점이에요. 보통 UserControl이나 Control 안에 코드를 작성할 때에는 단순히 GetIsDesignerMode(this)를 해도 무방하지만 Model과 같이 따로 UIElement를 확정할 수 없는 코드에서는 난감하죠. 이럴 때에는 다음과 같이 해결할 수 있어요.

if (DesignersProperties.GetIsInDesignMode(Application.Current.RootVisual) == true)
    // 디자인 모드
else
    // Web 모드

Application이 지원하는 RootVisual은 전역에서 일관적으로 접근할 수 있는데다가 비주얼 트리VisualTree의 최상위 오브젝트를 의미하니까 완벽해 보여요. 그렇지만 여기에도 문제는 있는데요, 바로 Application이 아직 RootVisual을 결정하지 못한 상태일 때에 이 검사는 실패한다는 점이죠. 일반적인 실버라이트 애플리케이션의 App.xaml.cs를 보죠.

public App()
{
    this.Startup += this.Application_Startup;
    this.Exit += this.Application_Exit;
    this.UnhandledException += this.Application_UnhandledException;

    InitializeComponent();
}

private void Application_Startup(object sender, StartupEventArgs e)
{
    this.RootVisual = new Page();
}

7행의 InitializeComponent();가 실행되기 직전에는 Application.Current.RootVisual이 실버라이트 애플리케이션을 다운로드 받는 동안 보이는 로딩 화면Splash에 대한 비주얼 트리를 담고 있죠.

image

그 후 애플리케이션이 초기화 되고 로딩 화면이 사라진 후 Application_Startup 이벤트 핸들러가 호출되는데요, 12행의 RootVisual을 new Page로 설정하기 직전까지는 Application.Current.RootVisual이 null 값을 가지고 있어요.

즉, 애플리케이션이 초기화 된 직후부터 RootVisual을 설정하기 전까지 Application.Current.RootVisual이 null이므로 이 사이에는 DesignerProperties.GetIsDesignMode(Application.Current.RootVisual)이란 코드는 안전하지 않게 되는거죠. 한 가지 다행인 점은 앞서 본 것 처럼 App 클래스의 생성자에서 RootVisual이 null이 아니기 때문에 이를 이용하여 다음과 같이 안전한 코드를 작성할 수 있어요.

// 초기값은 블렌드에서 실행중이라고 가정합니다.
private bool _isBlend = true;
/// 
/// 블렌드에서 실행중인지 여부를 반환합니다.
/// 
public bool IsBlend
{
    get { return _isBlend; }
    private set { _isBlend = value; }
}

public App()
{
    // 블렌드에서 실행중인지 여부를 검사합니다.
    IsBlend = DesignerProperties.GetIsInDesignMode(RootVisual);

    this.Startup += this.Application_Startup;
    this.Exit += this.Application_Exit;
    this.UnhandledException += this.Application_UnhandledException;

    InitializeComponent();
}

여기서 중요한 점은 최초에는 블렌드에서 실행중이라고 가정한다는 거에요. 그 이유는 내부적으로 블렌드에서 실행될 때에 분명히 Application의 생성자가 호출되지만 우리가 그것을 확인할 수 없기 떄문에 실행 코드의 ‘모호성’을 줄이기 위해서에요. 반면 웹 페이지로 동작하고 있다면 이 코드는 100% 동작한다고 확신할 수 있으니 초기 값을 안전하기 가져가는 것이 좋겠죠.

그리고 코드를 애플리케이션 전역에서 공유할 수 있도록 App 클래스의 static 멤버로 정의했기 때문에 애플리케이션의 어떤 코드에서도 안전하게 가져갈 수 있겠죠.

만약 라이브러리에서도 블렌드에서 실행중인지 여부를 검사해야 한다면 여기에서 한 단계 더 나아가 위의 코드를 래핑하는 전역 클래스Static Class를 하나 만들 수도 있고요. 이 부분은 각자 프로젝트에 맞춰서 해보세요.

Posted by gongdo

MVVM+Command Pattern+Dependency Injection #1에 이어지는 Ninject를 사용한 Dependency Injection편이에요 :D

Ninject는 ‘번개처럼 빠른 인젝션’을 모토로 한 DI 프레임워크죠. Unity랑 비교해 보면 Unity가 전통적인 개발자에게 친숙한 인터페이스와 네이밍을 가진 반면 Ninject는 모던하고 약간은 팬시한 문법과 네이밍을 가졌고 다양한 조건에서 좀 더 자동화된 인젝션을 지원한다는 느낌이 들어요. 여튼 이 전에 만든 프로젝트를 정말로 약간만 수정하면 Unity를 Ninject로 바꿀 수 있어요. 이것이 바로 모듈화된 프로그래밍의 장점이죠 ;~)

우선 프로젝트 먼저 받아가시고요~


1. Ninject 라이브러리 참조

당연하겠지만 제일 먼저 라이브러리를 교체해야겠죠?

image

Ninject는 현재 5개의 어셈블리로 이루어져 있고 3개는 Extension, 2개는 거의 필수로 사용되는 어셈블리죠. 그 중 필수인 Ninject.Core와 Ninject.Conditions를 참조하면 돼요.
※Ninject.Condition 역시 사실 필수는 아니지만 프로젝트가 조금만 커져도 거의 필수로 사용됩니다.

2. ServiceConfiguration 수정

/// 
/// 서비스에서 사용할 인젝션 매핑을 설정하는 클래스
/// 
public class ServiceConfiguration : StandardModule
{
    public override void Load()
    {
        bool isBlend = App.IsBlend;
        bool isWeb = !isBlend;

        // IPhotoSerivce에 대한 바인딩 설정
        // 블렌드에서 실행할 때 바인딩만 다르게 표현
        if (isBlend)
        {
            Bind< IPhotoService>().To< MockPhotoService>();
        }
        else
        {
            Bind< IPhotoService>().To< LivePhotoService>().Only(When.Context.Variable("ServiceName").IsNotDefined);
            Bind< IPhotoService>().To< LivePhotoService>().Only(When.Context.Variable("ServiceName").EqualTo("Live"));
            Bind< IPhotoService>().To< FlickrPhotoService>().Only(When.Context.Variable("ServiceName").EqualTo("Flickr"));
            Bind< IPhotoService>().To< DaumPhotoService>().Only(When.Context.Variable("ServiceName").EqualTo("Daum"));
            Bind< IPhotoService>().To< NaverPhotoService>().Only(When.Context.Variable("ServiceName").EqualTo("Naver"));
        }

        // 공통으로 사용할 바인딩
        // 서비스 제공자의 목록을 싱글턴으로 바인딩
        Bind< PhotoServiceProviderList>().ToConstant< PhotoServiceProviderList>(
            new PhotoServiceProviderList
            {
                new PhotoServiceProvider { Name = "Live", DisplayName = "Live", LogoSource = "/MashupImageSearch;component/Resource/WindowsLive2_20.png" },
                new PhotoServiceProvider { Name = "Flickr", DisplayName = "Flickr", LogoSource = "/MashupImageSearch;component/Resource/flickr2_20.png" },
                new PhotoServiceProvider { Name = "Daum", DisplayName = "Daum", LogoSource = "/MashupImageSearch;component/Resource/daum_20.png" },
                new PhotoServiceProvider { Name = "Naver", DisplayName = "Naver", LogoSource = "/MashupImageSearch;component/Resource/naver_20.png" },
            }
        ).Using< SingletonBehavior>();
    }
}

Unity가 UnityContainer를 제공했던 것과 유사하게 Ninject는 StandardModule을 제공해요. Ninject에서 Module이란 하나의 서비스를 구성하는 DI들의 바인딩 구성을 정의하죠.

바인딩(매핑)을 등록하는 문장도 굉장히 다른데요, RegisterType()이라는 하나의 메서드로 인자의 위치나 배열에 따라 순서를 정했던 Unity와 달리 Ninject는 Bind<타입>().To<타입>().조건… 과 같은 명시적인 형식을 가지고 있어요. 마치 LINQ 표현식의 발전 과정을 보는 듯하죠.

동적 조건 식은 조금 복잡한데요, Unity가 단순히 이름만 가지고 바인딩을 결정했던 것과는 달리 Ninject는 굉장히 다양한 시나리오와 커스터마이징이 가능한 파라미터들로 바인딩을 결정할 수 있어요. Only(When.Context.Variable(“키”).조건…과 같이 “오직 컨텍스트에 특정 변수가 조건을 만족할 때만 바인딩”으로 글을 읽듯이 읽을 수 있죠. 굉장히 가독성이 좋은 문법이라고 봐요. 다만 지네릭과 람다 익스프레션 문법을 굉장히 많이 활용하기 때문에 익숙하지 않다면 어려울 수 있겠네요.

마지막으로 Unity의 RegisterInstance()메서드는 Ninject는 Bind<타입>().ToConstant<타입>() 으로 표현하는 걸 볼 수 있고 인젝션할 대상의 라이프 타임 관리를 .Using<라이프타임 동작>() 과 같이 할 수있어요. 여기에서 서비스 제공자의 목록은 항상 같으므로 단순한 SingletonBehavior로 지정했죠.

3. ServiceLocator 수정

/// 
/// 서비스에서 사용할 설정 모듈에서 타입을 제공하는 클래스
/// 
public class ServiceLocator
{
    #region Singlton 컨테이너
    private static IKernel _kernel;
    /// 
    /// 클래스 초기화.
    /// 서비스에서 사용할 DI 컨테이너를 초기화합니다.
    /// 
    static ServiceLocator()
    {
        if (_kernel == null)
        {
            _kernel = new StandardKernel(new ServiceConfiguration());
        }
    }

    /// 
    /// 주어진 타입에 대한 인젝션을 수행한 참조를 반환합니다.
    /// 
    /// 인젝션을 처리할 대상
    /// 인젝션이 처리된 결과
    public static T Get< T>()
    {
        return _kernel.Get< T>();
    }

    /// 
    /// 주어진 타입에 대한 인젝션을 수행한 참조를 반환합니다.
    /// 
    /// 인젝션을 처리할 대상
    /// 파라미터 이름
    /// 인젝션이 처리된 결과
    public static T Get< T>(string name)
    {
        return _kernel.Get< T>(With.Parameters.Variable("ServiceName", name));
    }
    #endregion Singlton 컨테이너

    /// 
    /// 설정에 따라 이미지 검색 뷰모델의 인스턴스를 반환합니다.
    /// 
    public ImageSearchViewModel ImageSearchViewModel
    {
        get { return Get< ImageSearchViewModel>(); }
    }

    /// 
    /// 설정에 따라 포토 서비스 제공자의 목록을 반환합니다.
    /// 
    public PhotoServiceProviderList PhotoServiceProviders
    {
        get { return Get< PhotoServiceProviderList>(); }
    }
}		

Unity에서는 UnityContainer가 인젝션 바인딩(매핑) 정보를 보관하고 관리하는 역할을 모두 수행하지만 Ninject는 Kernel이 관리하는 Module에서 인젝션 바인딩 정보를 이용하여 인젝션을 관리해요. 특별히 다른 점은 없고 다만 파라미터를 줄 때도 Kernel.Get<타입>(With.Parameters.Variable(“변수이름”, 변수값) 같은 식으로 명시적이고 가독성 있는 문법을 지원해요.

이 정도 수정만으로 Unity를 Ninject로 대체하는 마이그레이션이 끝났어요. 실행 결과는 당연히 동일하고요. 간단하죠?

DI 자체에 대해서는 언제 한번 자세히 다루고 싶지만 아직 내공이 딸려서 정확한 설명은 힘드네요. 시간도 부족하고요. 우선은 지금까지 했던 프로젝트를 기반으로 지식을 확장해가면서 경험을 쌓아도 충분할거라고 봐요.

Posted by gongdo

매쉬업 이미지 검색 + MVVM모델 + CommandPattern 예제에서 길버트님이 MVVM과 Command Pattern을 활용하여 화면 표시를 담당하는 View와 데이터 및 비즈니스 로직을 담당하는 Model, 그리고 View에 표시될 데이터와 View의 흐름 제어를 담당할 ViewModel이 적용될 수 있는 멋진 데모를 보여주셨죠.

사용된 패턴과 용어 정리

MVVM은 MVC 패밀리(?)의 일족으로 MVP(Model View Presentation)과 매우 유사해요. 사실 전 MVC/MVP/MVVM은 모두 같은 원리이고 구현의 관점이 약간씩 다를 뿐이라고 생각해요. 여튼 중요한 건 뷰와 데이터를 어떻게 분리한 상태에서 컨트롤 할 것이냐죠.

Commnad Pattern은 주로 애플리케이션 전역에서 동일하게 받을 수 있는 사용자의 제어 명령에 대한 이벤트를 효과적이고 표준적으로 받기 위한 구현 방법이에요. 커맨드 패턴은 특히 MVVM처럼 뷰와 모델이 분리된 환경에서 매우 효과적으로 코드를 짤 수 있게 도와주는 역할을 하죠.

Dependency Injection(DI) Pattern은 클래스의 독립성을 극한까지 끌어올리기 위해 고안된 방법 중 하나에요. 인젝션의 핵심은 어떤 클래스가 참조하거나 이용하는 인스턴스를 그 클래스가 생성하지 않고 외부에서 생성자, 프로퍼티 또는 메서드를 사용하여 ‘주입’inject시켜 보다 투명하고 분리된 클래스를 작성하는 데 있어요.

이 글에서는 DI에대한 설명은 접고 그냥 어떻게 활용되었는지를 보도록 하고 자세한 정보는 다음 링크들을 체크해 보세요.

길버트님의 예제 리모델링!

백문이 불여일타! 일단 샘플 받아서 열어보시고요~


동작은 길버트님의 예제와 완벽하게 동일하므로 동작 완구는 생략했어요. 리모델링 과정을 알아보죠.
※보통 프로그래밍에서 어떤 코드를 좀 더 적절하게 나누고 최적화하는 과정을 리팩터링이라고 합니다. 여기에서는 리모델링으로 표현했음에 주의하세요.

1. 문제점 분석

우선 기존 프로젝트에서 어떤 부분을 왜 고쳐야 하는지 생각해보죠. 기존 프로젝트에서는 각 포토 서비스를 IPhotoService라는 인터페이스만 상속받으면 되는 독립 클래스로 만들수 있었어요. 한 가지 문제는 이렇게 새 서비스를 만들어도 View와 ViewModel이 추가된 서비스를 알 수 없기 때문에 View와 ViewModel 모두를 고쳐야 한다는 점이죠. 이는 말하자면 Model의 독립성은 확보했지만 View와 ViewModel이 Model에 종속되어 있는 그림이 되죠. 특히 View는 ComboBox 컨트롤에 하드코딩으로 목록이 들어있는 형태라서 만약 디자이너 프로세스와 개발 프로세스가 나뉘어 있다면 Model이 추가될 때마다 디자이너가 View를 만져줘야 하는 상황이 발생하겠죠.

이를 해결하려면 View는 Model에 대해 전혀 알 필요가 없어야 하고 ViewModel은 Model의 추가/변경/삭제에 대해 코드의 수정 없이 대처할 수 있어야 해요.

2. View를 독립시키기

OOP의 기본 중에 기본이죠. 하나의 클래스는 그 이름으로 대표되는 최소한의 일만 수행하도록 쪼개고 또 쪼개는 것. 기존 프로젝트에서 콤보 박스에 들어가는 데이터를 생각해보면 각 서비스 제공자의 이름(Flickr, Live…)과 로고 이미지로 구성되어 있었죠? 그리고 이 부분이 새 Model이 추가될 때마다 함께 수정해야 할 부분이었고요. 이 부분을 분리해보죠.

image

포토 서비스 제공자의 이름과 로고를 처리하기 위해 IPhotoServiceProvider와 PhotoServiceProvider 클래스를 추가했고 내용은…

/// 
/// 기본적인 포토 서비스 제공자의 메타 정보를 관리하기 위한 클래스
/// 
public class PhotoServiceProvider : IPhotoServiceProvider
{
    /// 
    /// 서비스 제공자의 이름을 설정하거나 반환합니다.
    /// 
    public string Name { get; set; }

    /// 
    /// 화면에 표시할 이름을 설정하거나 반환합니다.
    /// 
    public string DisplayName { get; set; }

    /// 
    /// 서비스 제공자의 로고 이미지 소스를 설정하거나 반환합니다.
    /// 
    public string LogoSource { get; set; }
}

public class PhotoServiceProviderList : List< IPhotoServiceProvider>
{
}

간단하죠? 그리고 이 데이터는 하나가 아닌 여럿으로 구성될 것이므로 지네릭 타입의 List도 하나 선언해줬어요.

이제 View에서 데이터 템플릿을 사용하여 하드 코딩된 디자인을 걷어내보죠.

    
        
            
                
                
            
        
    

아직 데이터 바인딩의 소스를 설정하지 않았지만 DataTemplate을 사용해서 기존 디자인 형식을 그대로 가져왔고 대신 Image의 Source와 TextBlock의 Text 속성을 위에서 만든 IPhotoServiceProvider가 제공하는 속성으로 바인딩을 해줬어요. 데이터 바인딩의 소스는 나중에 추가하기로 하죠.

자 이제 View는 Model에 변경이 있더라도 영향을 받지 않도록 준비가 되었어요. 그러나 아직 데이터 소스를 어디에서 가져와서 언제 어떻게 설정할지는 미지수인데요, 이를 위해 드디어 Dependency Injection기법을 사용하여 몇 가지를 준비해보죠.

3. Dependency Injection 프레임워크 선택

DI는 개념이 그렇게까지 복잡한 것은 아니라 물론 직접 구현할 수도 있겠죠. 그러나 언제나 그렇지만 이런 표준적인 라이브러리는 다른 사람이 만든 훌륭한 구현체가 있기 마련이죠. 바로 제가 라이브러리 문서화의 중요성에서 언급한 Unity Application Block이나 Ninject처럼 말이죠. 이 중에서 좀 더 보편적이고 무엇보다 마이크로소프트가 운영하는 Pattern&Practice 프로젝트에서 만들어진 Unity를 사용할께요. 기회가 되면 Ninject도 사용해 볼 거에요.

imageimage

Unity Application Block for Silverlight를 다운로드 받으면 Microsoft.Practices.Unity.dll이란 어셈블리를 찾을 수 있는데요, 이걸 프로젝트에서 참조하면 돼요. 테스트 할 수 있도록 프로젝트의 DLL폴더에 어셈블리 파일을 포함시켜뒀으니 이걸 사용하셔도 되고요.

4. DI 설정 및 전역 서비스 제공 클래스 작성

DI는 외부에서 작성되어 애플리케이션 전역 혹은 일부에 영향을 주게 돼요. 저는 YouCard라는 짱멋진 프로젝트에 영향을 받아 DI 바인딩(또는 매핑) 정보를 저장하고 관리하는 ServiceConfiguration 클래스와 이 설정으로부터 애플리케이션이 사용할 인스턴스화된 오브젝트를 애플리케이션 전역에 제공할 ServiceLocator 클래스를 만들었어요.

먼저 ServiceConfiguration을 보면…

/// 
/// 서비스에서 사용할 인젝션 매핑을 설정하는 클래스
/// 
public class ServiceConfiguration : UnityContainer
{
    /// 
    /// 생성자.
    /// 여기에서 애플리케이션이 공통으로 사용할 컨테이너를 설정합니다.
    /// 
    public ServiceConfiguration()
    {
        // 디자인일 때와 그렇지 않을 때 서비스 바인딩을 달리 합니다.
        if (App.IsBlend)
        {
            RegisterType< IPhotoService, MockPhotoService>();
        }
        else
        {
            // 일반일 때에는 각 서비스의 바인딩을 이름으로 구분하여 등록합니다.
            RegisterType<  IPhotoService,  LivePhotoService>();    // 이름이 없을 때는 라이브를 사용
            RegisterType<  IPhotoService,  LivePhotoService>("Live");
            RegisterType<  IPhotoService,  FlickrPhotoService>("Flickr");
            RegisterType<  IPhotoService,  DaumPhotoService>("Daum");
            RegisterType<  IPhotoService,  NaverPhotoService>("Naver");
        }

        // 공통으로 사용할 바인딩
        // 서비스 제공자의 목록
        PhotoServiceProviderList providers = new PhotoServiceProviderList
        {
            new PhotoServiceProvider { Name = "Live", DisplayName = "Live", LogoSource = "/MashupImageSearch;component/Resource/WindowsLive2_20.png" },
            new PhotoServiceProvider { Name = "Flickr", DisplayName = "Flickr", LogoSource = "/MashupImageSearch;component/Resource/flickr2_20.png" },
            new PhotoServiceProvider { Name = "Daum", DisplayName = "Daum", LogoSource = "/MashupImageSearch;component/Resource/daum_20.png" },
            new PhotoServiceProvider { Name = "Naver", DisplayName = "Naver", LogoSource = "/MashupImageSearch;component/Resource/naver_20.png" },
        };
        // 인스턴스 자체를 등록
        RegisterInstance< PhotoServiceProviderList>(providers);
    }
}

가장 핵심적인 건 ServiceConfiguration 클래스가 UnityContainer를 상속받고 있다는 점이죠. UnityContainer는 바로 Unity에서 DI에 대한 매핑 정보를 저장하고 관리하는 기본 클래스에요. 기본적으로 RegisterType 메서드로 FromT 타입에 대해 ToT 타입으로 주입Injection하도록 매핑하고 또한 RegisterInstance 메서드로 FromT타입에 대해 등록한 인스턴스를 주입하도록 매핑하죠. 여기에서는 이 정도로만 설명하고 DI에 대한 건 기회가 되면 다른 글에서 소개할께요.

여기서 한 가지 센스있는 코드가 있는데요^^, 바로 실버라이트가 디자인 모드(블렌드)에서 동작중인지 아니면 정상적으로 실행되고 있는지에 따라 타입 매핑을 다르게 했어요. 이를 통해 디자이너가 블렌드에서도 여기에서 주어진 샘플 데이터를 볼 수 있죠.

이제 새로운 Model이 추가된다면 새 Model 클래스를 작성하고 ServiceConfiguration에 새로 만들어진 Model 타입을 매핑하고 새 Model을 설명하는 PhotoServiceProvider를 추가해주기만 하면 돼요. WOW~!

다음으로 ServiceLocator를 보죠.

/// 
/// 서비스에서 사용할 컨테이너를 초기화하고
/// 인젝션에 필요한 정보를 담고 있는 싱글턴 클래스
/// 
public class ServiceLocator
{
    #region Singlton 컨테이너
    private static ServiceConfiguration _container;
    /// 
    /// 클래스 초기화.
    /// 서비스에서 사용할 DI 컨테이너를 초기화합니다.
    /// 
    static ServiceLocator()
    {
        _container = new ServiceConfiguration();
    }

    /// 
    /// 주어진 타입에 대한 인젝션을 수행한 참조를 반환합니다.
    /// 
    /// 인젝션을 처리할 대상
    /// 인젝션이 처리된 결과
    public static T Get< T>()
    {
        return _container.Resolve< T>();
    }

    /// 
    /// 주어진 타입에 대한 인젝션을 수행한 참조를 반환합니다.
    /// 
    /// 인젝션을 처리할 대상
    /// 옵셔널 문자열
    /// 인젝션이 처리된 결과
    public static T Get< T>(string name)
    {
        return _container.Resolve< T>(name);
    }
    #endregion Singlton 컨테이너

    /// 
    /// 생성자.
    /// 
    public ServiceLocator()
    {
    }

    /// 
    /// 설정에 따라 이미지 검색 뷰모델의 인스턴스를 반환합니다.
    /// 
    public ImageSearchViewModel ImageSearchViewModel
    {
        get { return Get< ImageSearchViewModel >(); }
    }

    /// 
    /// 설정에 따라 포토 서비스 제공자의 목록을 반환합니다.
    /// 
    public PhotoServiceProviderList PhotoServiceProviders
    {
        get { return Get< PhotoServiceProviders>(); }
    }
}

ServiceLocator는 ServiceConfiguration에 대한 싱글턴 인스턴스를 하나 가지고 있으면서 애플리케이션에서 사용될 인젝션 대상 클래스의 인스턴스를 얻을 수(Get) 있도록 도와주죠. 그리고 바인딩의 편의를 위해 ImageSearchModelView와 PhotoServiceProviders 속성은 별도로 노출하고 있어요. 이 ServiceLocator는 말 그대로 애플리케이션 전역에서 접근할 수 있는 DI 컨테이너를 서비스하는 클래스이죠.

5. 애플리케이션 전역에 DI 컨테이너를 제공할 수 있도록 준비

애플리케이션 전역에서 ServiceLocator를 접근할 수 있도록 ServiceLocator의 인스턴스를 하나 만들어야죠. 여기에서 App.xaml의 진가가 나오는데요, 바로 App.xaml의 Resources에 ServiceLocator의 새 인스턴스를 하나 등록해두면 애플리케이션 전역에서 StaticResource로 접근할 수 있게 돼요. App.xaml을 보면…

    
        
    

View의 DataContext의 Source를 전역 리소스인 ServiceLocator로 지정했고 데이터의 경로Path는 ImageSearchViewModel로 지정했어요. 즉, ServiceLocator 클래스의 인스턴스에서 ImageSearchViewModel 속성을 읽어 오는거죠.

아주 간단하게 전역 리소스를 만들었죠? 다음으로 넘어가죠.

6. 전역 리소스에서 View에 데이터 바인딩

앞서 우리는 ServiceLocator를 전역에서 접근할 수 있도록 리소스로 만들었는데요, 이걸로 각 View의 ViewModel을 안전하게 접근하여 데이터 바인딩 할 수 있어요.

 

즉, ServiceLocator 클래스의 인스턴스에서 ImageSearchViewModel 속성으로 데이터 바인딩을 하는 거죠.

아직 하나 더 해줘야 할 게 있는데요, 앞서 DataTemplate만 설정하고 실제 데이터 소스를 설정하지 않은 ComboBox죠.

    
        
            
                
                
            
        
    

자, 이제 ViewModel을 독립시킬 차례에요.

7. ViewModel 리모델링

ViewModel은 View가 가질 데이터와 View를 표시하는 로직을 담고 Model로부터 데이터를 얻거나 넘겨주죠. 먼저 기존 ViewModel에서는 제공될 Model에 대한 정보를 이미 가지고 있었어요. 여러 개의 서비스를 사용할 수 있도록 각 서비스가 IPhotoService 인터페이스로 추상화가 되어있지만 결국 실체화된 인스턴스를 만들기 위해 Model의 Concrete 타입을 사용해요. 요컨대 ServiceTypes enum이 있었고 ComboBox에서 서비스를 변경하면 이 enum값에 따라 new LivePhotoService(); new FlickrPhotoService();와 같이 Model의 확정된 타입을 ‘생성’했었죠.

세부적인 코드는 첨부한 파일을 참고하시길 바라고 가장 핵심적으로 수정된 부분을 볼께요. 먼저 생성자 부분.

/// 
/// 기본 생성자
/// 
public ImageSearchViewModel(IPhotoService service)
{
    InitProperties(service);
    SearchCommands.Search.Executed += new EventHandler< HugeFlow.CommandPattern.ExecutedEventArgs>(Search_Executed);
    SearchCommands.Navigate.Executed += new EventHandler< HugeFlow.CommandPattern.ExecutedEventArgs>(Navigate_Executed);
    SearchCommands.ShowLargeImage.Executed += new EventHandler< HugeFlow.CommandPattern.ExecutedEventArgs>(ShowLargeImage_Executed);
    SearchCommands.ChangeService.Executed += new EventHandler< HugeFlow.CommandPattern.ExecutedEventArgs>(ChangeService_Executed);
    SearchCommands.NavigateOriginalImage.Executed += new EventHandler< HugeFlow.CommandPattern.ExecutedEventArgs>(NavigateOriginalImage_Executed);
}

생성자에서는 파라미터로 IPhotoService를 받도록 했고 InitProperties로 전달하고 InitProperties에서는 클래스의 멤버 변수인 _photoService에 전달 받은 IPhotoService의 인스턴스를 ‘주입’하게 되죠. 그리고 만약 블렌드에서 동작중이라면 타이머를 돌려 검색을 시뮬레이션 하고 있고요.

다음으로 ComboBox에서 서비스를 변경했을 때 처리를 보죠.

void ChangeService_Executed(object sender, HugeFlow.CommandPattern.ExecutedEventArgs e)
{
    PhotoServiceProvider provider = e.Parameter as PhotoServiceProvider;
    _photoService = ServiceLocator.Get< IPhotoService>(provider.Name);   // 컨테이너에서 서비스의 참조 얻기

    if (_photoService != null)
    {
        _photoService.SearchImageCompleted += new PhotoSearchEventHandler(_photoService_SearchImageCompleted);
    }

    InitSearch();
}

전에는 각 서비스 모델 별로 직접 생성을 했지만 이제는 ServiceLocator로부터 주어진 PhotoServiceProvider의 이름으로 생성해야 할 타입을 ServiceLocator가 유추하고 생성하여 반환하도록 했어요. 이로써 ViewModel은 외부 Model의 어떤 Concrete타입에도 의존하지 않고 단지 Model들의 추상화된 인터페이스만으로도 완벽하게 동작할 수 있게 되었어요.

꽤 길어졌네요. DI란게 짧은 글 하나로 완벽하게 정리가 될 만한 내용도 아니고 언제 어떻게 적용해야 하는지에 대한 것도 경험이 필요한거죠. 저역시 이제 막 이런 패턴들을 시도하고 익히고 있는 중이에요. 뭔가 설명할 수 있을 만큼 익히면 다시 정리하여 포스팅하기로 할께요. 그리고 다음 포스팅은 똑같은 프로젝트를 Unity가 아닌 Ninject를 사용하여 구현해보도록 하죠.

Posted by gongdo

요즘 실버라이트용 DI(Dependency Injection) 프레임워크로 Pattern&Practice 팀에서 제공하는 Unity Application Block과 독특한 특징을 가진 Ninject, 이 둘을 보고 있죠. 사실 DI의 원리나 구성은 대략 파악했다고 생각하는데 실제 구현은 참 난감한 부분이 많은 것 같아요. 그래서 우리는 이런 공개된 프레임워크의 덕분에 좀 더 쉬운 삶-바로 제가 추구하는 그것!-을 살 수 있어요.

여튼 이 둘은 서로 스타일이 꽤 달라요. 우선 네이밍 부터가 꽤 달라서 이게 과연 같은 패턴을 사용하는 그것이 맞나 싶을 정도. 아주 조금씩만 써보고 있어서 아직 확실하게 감은 못잡았지만 공부하면서 느끼는 차이는 바로 문서화의 차이에요.

Unity는 마이크로소프트의 P&P에서 나온 만큼 철저하게 MSDN 스타일로 구성되어 있죠. MSDN의 장점은 어떤 라이브러리를 매우 광범위하고 자세하게 기술하고 있다는 점인데요 반면 단점은 Getting started를 봐도 딱 그 정도까지만 쓸 수 있을 뿐 그 이상의 기능을 구현하려면 어떤 클래스를 어떻게 사용할지 찾기가 꽤 어렵다는 점이죠. 그래도 최근엔 HOWTO 토픽도 많이 생기고 해서 이런 점은 많이 해소 되었지만, 그래도 여전히 문서가 너무 딱딱해서 어느정도 기합을 넣고 보지 않으면 잘 읽히지가 않아요.

Ninject는 일단 메인 사이트 부터가 굉장히 팬시해서 둘러보는데 부담이 없어요. 그리고 Ninject는 MSDN처럼 완전히 정리된 문서따위는 없지만 Wiki를 통해 아주 깔끔하게 스텝별로 익힐 수 있도록 되어 있어요. 이게 중요한데요, Ninject는 Ninject를 설명할 때 기능 하나하나에 대한 사용법이나 설명을 독립적으로 하는게 아니라 단계별로 기본적인 사용법과 함께 Ninject가 어떤 부분에 주안점을 가지고 있는지 이해할 수 있도록 구성되어 있죠.

베스트 케이스라면 Getting started를 Ninject스타일로 친절하게 구성하고 세부적인 레퍼런스를 MSDN 스타일로 구성하는 거지만 둘 중 하나를 선택하라면 Ninject 스타일의 손을 들어주고 싶네요. 왜냐면 프레임워크나 라이브러리라는 건 일단 처음 사용하기가 굉장히 괴롭거든요. 일종의 진입 장벽이랄까요? 그런 걸 쉽게 덜어주고 그 라이브러리의 설계 철학이나 흐름을 잡는게 중요하다고 봐요.

왜 이런 소릴 하고 있냐면…

Ninject에서는 모듈(Unity의 컨테이너 개념)에 인터페이스와 클래스의 바인딩을 등록할 때 아주 간단하면서도 유용한 조건을 붙여서 하나의 인터페이스에 여러개의 클래스를 조건에 따라 자동으로 인젝션 되도록 되어 있어요. 예를 들면,

public class Swordsman { 
  [Inject] public IWeapon Weapon { get; set; } 
}

public class Ninja { 
  [Inject] public IWeapon MeleeWeapon { get; set; } 
  [Inject, Range] public IWeapon RangeWeapon { get; set; } 
}

Bind().To(); 
Bind().To().Only(When.Context.Target.HasAttribute());

이건 “IWeapon이라는 인터페이스를 Sword클래스와 Shuriken(수리검^^) 클래스에 바인딩을 하되 Context.Target(인젝션 대상, 여기에서는 Weapon, MeleeWeapon, RangeWeapon과 같은 프로퍼티)이 RangeAttribute라는 어트리뷰트를 가지고 있을 때만 Shuriken을 바인딩 해라”를 의미해요. 정말 기가 막히게 팬시하면서 읽기 쉬운 코드 아닌가요? 거의 영어 문장을 읽는 것과 비슷하게 느껴질 정도로요.

또한 예제 코드 역시 Foo나 Bar 따위가 아니라 검사(Swordman)과 닌자(Ninject를 떠올리는!)가 각각 자신의 역할에 맞게 사용할 무기들을 선택하는 과정이 머리속에 그려지도록 잘 선택되었고요.

다른 방법의 예제를 보면

public class Ninja { 
  [Inject] public IWeapon MeleeWeapon { get; set; } 
  [Inject, Tag("range")] public IWeapon RangeWeapon { get; set; } 
}

Bind().To(); 
Bind().To().Only(When.Context.Tag == "range");

비슷한데 Tag라는 미리 지원되는 어트리뷰트를 가지고 문자열 비교를 할 수 있게 한거에요. 이러면 매번 어트리뷰트를 새로 만들지 않아도 되니 편리하겠죠. 마찬가지로 문장은 정말 쉽게 읽히고요.

거기에 한 단계 더 생각해서, 단지 인젝션 대상의 이름 규칙(convention)만으로도 바인딩 할 수 있게 지원하죠.

public class Service { 
  public Service(ISource remoteSource, ISource localSource) { 
    //... 
  } 
}

Bind().To().Only(When.Context.Target.Name.BeginsWith("remote")); 
Bind().To().Only(When.Context.Target.Name.BeginsWith("local"));

Service라는 클래스는 생성될 때 두 개의 ISource를 받죠. 아래의 바인딩 문은 이 클래스에 인젝션할 때 “타겟이 remote라는 이름으로 시작할 때”, “타겟이 local이라는 이름으로 시작할 때”라는 조건을 붙인 걸 읽을 수 있어요. (진짜 볼 수록 감탄…)

참 쉽죠? (ㅋㅋ)

반면 Unity에서 비슷하게 하나의 컨테이너에 하나의 인터페이스를 여러 개의 클래스와 바인딩하고 싶은데 도무지 어디에서 그런 설정을 해야할지 난감하네요. 아마도 Configuration이라는 클래스를 상속받아 구현하는 것 같은데 실버라이트용에서는 그 Configuration이 빠졌다고 하는군요. 여튼 “동적인 조건부 인젝션”을 어떻게 할지 난감해요.

네, 솔직히 저는 Ninject의 손을 들어주고 있는데요, 다만 Unity for Silverlight는 단일 어셈블리에 용량도 조금이나마 작아서 딱 DI만 쓴다고 봤을 때 좀 더 가벼운데 Ninject는 3개의 어셈블리에 용량도 약간 더 커요. 그래서 Unity쪽도 해보고 둘 중에 하나를 선택하자..는 기분으로 써보고 있는데 이놈의 Unity 문서는 읽다보면 스르르 잠에 빠져서 진도가 안나가네요 ㅠ.ㅜ

그래서 오늘의 결론.

라이브러리 문서화를 잘 합시다. 이왕이면 단계별로 가장 핵심적인 내용을 익힐 수 있도록!
AND
Unity 써보신 분 조건적 인젝션 바인딩을 컨테이너에 어떻게 넣는지 좀 알려줍쇼 ㅠ.ㅜ 굽신굽신 oTL oTL

Posted by gongdo

실버라이트에서 커스텀 컨트롤을 만들 때 각 커스텀 컨트롤들의 기본 스타일은 반드시 /themes/generic.xaml 파일에서  ResourceDictionary로 등록해야 했죠. 때문에 라이브러리가 커지다보면 수많은 기본 스타일들이 하나의 파일에 등록이 되어 있어서 찾아보기가 굉장히 힘들었죠.

과연 마이크로소프트의 개발자들은 이런 문제를 어떻게 해결하고 있는지 궁금했었는데요, Jeff Wilcox의 블로그에서 그에 대한 답을 내놓았네요.

아니나다를까 마이크로소프트의 내부 개발자들은 저런 문제를 쉽게 해결하기 위한 툴들을 만들어쓰고 있네요. 바로 기본 스타일들을 정의하는 Xaml들을 자유롭게 배치하고 대신 해당 xaml의 Build Action을 DefaultStyle이라는 키워드로 선택하기만 하면 빌드시 /themes/generic.xaml에 자동으로 통합을 해주는 MSBuild 툴을 만들어 쓰고 있다고 하는군요.

간단하게 말하자면 MSBuild 프레임워크에 포함된 Task라는 항목이 있는데, 이게 어떤 액션이 일어날 때 처리할 일을 정의하는 넘이죠. 그래서 빌드할 때 프로젝트에 포함된 아이템 중에서 DefaultStyle이라는 키워드의 Build Action이 붙은 모든 아이템들을 가져와서 처리한 후 /themes/generic.xaml에 덮어쓰는거죠. 여기에서 Build Action에 원하는 키워드를 추가하는 방법이 가장 중요한 요소인데요, VSProject를 구성하는 엘리먼트 중에 숨겨진 요소가 있다는군요. 참 Win32부터 그런 것들이 많았었죠 –_-;;

자세한 내용은 Jeff Wilcox의 포스팅에 잘 소개되어 있으니 소스 설명은 하지 않겠고요, 단순히 해당 내용을 구현한 결과물을 소개할께요.

다운 받은 파일의 압축을 풀면 세 개의 파일이 나오는데요, 각각 정해진 위치로 폴더를 만들고 복사하기만 하면 돼요.

  • Engineering.Build.dll
    -> C:\Program Files\Microsoft SDKs\Silverlight\v2.0\Libraries\Extra
  • SilverlightAppWithMultipleResourceDictionary.zip
    -> %내 문서%\Visual Studio 2008\Templates\ProjectTemplates\Visual C#\Silverlight
  • SilverlightDefaultStyleItem.zip
    -> %내 문서%\Visual Studio 2008\Templates\ItemTemplates\Visual C#\Silverlight

zip 파일 들은 내 문서에 있는 Visual Studio 2008 폴더를 찾아서 넣으면 돼요.

자 이제 비주얼 스튜디오를 열고 프로젝트를 하나 만들어 볼까요?

image

Visual C#/Silverlight 타입에 보면 My Templates 그룹 아래에 Silverlight Application with Multiple…이 보이네요. 이 템플릿으로 새 프로젝트를 만들면,

image

보안 경고가 나오는데요, 이건 사용자 정의 템플릿을 사용할 때 항상 뜨는 걸로 아래에 있는 Load project normally를 선택해야 제대로 진행돼요.

image

프로젝트를 만들면 기본 실버라이트 프로젝트와 크게 다르지 않고 다만 /themes/generic.xaml이 기본으로 포함되어 있는 것을 볼 수 있죠.

다음으로 새 DefaultStyle 정의를 위한 xaml 파일을 만들어보죠.

image

프로젝트에 새 아이템을 추가하면…

image

위와 같이 카테고리에서 Visual C#/Silverlight를 선택하면 My Templates 그룹Silverlight Default Style Item이 있죠? 클릭하고 이름을 적당히 넣은 후 추가 버튼을 클릭하면 다음과 같이 보안 경고 메시지가 나와요. 저 믿죠? ㅋㅋ Trust를 클릭하여 커스텀 아이템을 추가하도록 허용합니다.

image

xaml 파일이 프로젝트에 추가되는데 여기에서 Build Action은 수동으로 DefaultStyle을 선택해야 하고 Custom Tool 설정은 삭제하세요.

image

네, 그리고 xaml의 내용에는 기본 스타일을 바로 작성할 수 있도록 ResourceDictionary를 추가해뒀어요. Enjoy it! :)

마지막으로 테스트를 해봐야죠? 새로 생성한 MyControlStyle.xaml에 다음과 같이 간략하게 코드를 작성해보죠.

image

이제 빌드를 하면?

image

/themes/generic.xaml이 외부에서 변경되었다는 메시지가 나오는데요 이건 어쩔 수가 없어요. 아쉽지만 매번 DefaultStyle로 설정한 xaml이 빌드될 때마다 Yes를 눌러줘야죠.

최종 결과물은 위와 같이 별도로 작성한 Style이 generic.xaml에 자동으로 복사된 모습. 자 이제 한 곳에 보기 싫게 몰아넣지 말고 각 커스텀 컨트롤 별로 연관성 있게 파일을 관리할 수 있어요.

단점이라면 하나의 xaml이라도 DefaultStyle을 사용한다면 generic.xaml이 자동으로 생성되기 때문에 모든 DefaultStyle을 별도의 파일로 관리해야 한다는 점이죠.

그렇지만 저는 각 커스텀 컨트롤의 스타일을 별도의 파일로 관리하고 공통으로 사용할 스타일이나 리소스 또한 common.xaml 등으로 따로 관리하는 것이 좋다고 봐요.

여튼 라이브러리나 커스텀 컨트롤을 많이 사용하는 프로젝트에서는 굉장히 유연성있는 스타일 관리를 할 수 있는 좋은 방법이죠. 피드백 바랍니다. :D

Posted by gongdo

개발자들이 블렌드에서 작업을 하기 꺼려하는 이유 중 하나는 바로 XAML을 직접 편집할 때 인텔리센스를 지원하지 않는 다는 점이죠!

잘 알려지진 않았지만 블렌드는 외부 애드인을 사용할 수 있는 준비가 되어 있는데요, 이미 블렌드 2의 CTP 시절에 애드인을 사용하여 인텔리센스를 지원하는 방법이 코드플렉스에 소개되었었죠.

참고 : Intellisense For Expression Blend

당연하겠지만 버전 문제로 위의 링크에 있는 코드는 제대로 동작하지 않아요. 다행히 소스를 제공하기에 새 블렌드 버전에 맞춰서 어셈블리 참조만 바꿔서 동작하도록 수정했고 설치도 한방에 되도록 간단한 커맨드 라인 툴도 만들었어요.

 

파일을 다운 받아 압축을 해제한 후 install.cmd를 반드시! 관리자 권한으로 실행하면 다음과 같이 설치되었다는 메시지가 표시되고 끝. 간단하죠?

그런데 블렌드의 애드인은 설정 등의 값으로 설정하는 것이 아니라 매 실행마다 파라미터를 통해 전달해줘야 해요. 그래서 Blend.exe를 직접 실행해서는 안되고 바로 가기 링크를 바꿀 필요가 있죠. 위의 설치 과정에서 자동으로 바로 가기의 링크도 수정해줘요. 단, 해당 바로 가기는 Windows Vista 이상에서만 동작할거에요. XP에서 사용중이라면 다음과 같은 커맨드라인 파라미터를 사용하여 애드인을 실행해야 해요.

start Blend.exe -addin:Addins\Expression.Blend.IntelliSense.dll

위의 명령어는 블렌드가 설치된 C:\Program Files\Microsoft Expression\Blend 2\Blend.bat 라는 이름으로 자동으로 복사가 되니 참고하세요.

 

여튼 이렇게 프로그램 바로 가기로 실행하면…

XAML 뷰에서 인텔리센스가 지원되는 것을 볼 수 있어요. 물론 비주얼 스튜디오에서 지원하는 것에 비교하면 정말 허접하지만 그나마 없는 것보다는 낫네요. 또 한가지 아쉬운 점은 Split 모드에서는 인텔리센스가 지원되지 않는 다는 점인데 혹시 관심 있는 분이라면 코드플렉스에서 소스를 다운받아 수정해 볼 수도 있겠죠.

Posted by gongdo

SBS드라마 가문의 영광의 홈페이지가 오픈되었는데요, 실버라이트를 적극적으로 활용하고 있어서 소개합니다. ^^

http://tv.sbs.co.kr/gamun/

image

일단 대문부터 실버라이트를 도입했다는 점, 이제 실버라이트를 활용하는 것에 자신이 생겼다고 말 할수도 있겠죠?

image

메인 화면도 큼지막하게 적용했네요. 각 메뉴를 나름 심플하면서도 재미있게 배치했고요 촬영장 스케치나 예고편은 메뉴에서 미리보기를 제공하네요.

우측 상단은 광고…인데 위아래 검은 영역이 없었으면 좋았을 걸 그랬어요. 뭔가 레이아웃이 깨져보이네요. 전체적으로 가장 아쉬웠던 부분.

예고편 미리보기 페이지에도 실버라이트가 적용되어 있네요.

http://tv.sbs.co.kr/gamun/ria/index.jsp?page=preview

image

오른쪽에 메뉴를 눌러보면 세심하게 애니메이션 처리를 한 것을 볼 수 있어요. 근데 저는 애니메이션 속도가 빠른게 좋은데…^^

기본적인 동영상 플레이어가 갖춰야 할 기능들과 구성을 갖추고 있네요. 사용자 입장에서야 귀찮지만 광고모델도 충실하게 구현하고 있고요.

세부적으로는 약간씩 아쉬운 점이 눈에 띄긴 하지만 이제 막 오픈한 따끈따끈한 사이트니까요^^

국내 최초의 실버라이트 서비스인 NView부터 뉴스 뷰어를 거쳐 드라마 홈페이지까지… 국내에서는 SBS가 실버라이트를 가장 적극적으로 도입하고 있는 사이트 중 하나죠. 듣기로는 SBSi의 내부 인력들이 캐고생해가면서 만들었다고 하는데요, 앞으로도 멋진 서비스가 하나하나 만들어지길 바래요.

Posted by gongdo


훈스닷넷 제 2회 실버라이트 컨퍼런스에 참여해주신 여러분께 감사드려요^^

P.S.
이미지 만들어준 294님 감사.
Posted by gongdo
실버라이트 애니메이션 모델에서 가장 아쉬운 점 세 가지를 들어보자면,
1. Path를 따라가는 애니메이션 작성 불가.
2. 임의의 스토리보드를 거꾸로 재생 불가.
3. 스토리보드의 특정 키프레임 혹은 특정 시점에서 발생되는 이벤트 부재.
정도에요.

이 중에서 스토리보드를 거꾸로 재생하는 것은 어느 정도 제한이 있긴 하지만 상당히 간단한 코드로 가능해요.
피터씨가 포스팅한
10. Expression Blend_ListBox 간지나게 보이기
11. Expression Blend_Menu에서 스토리보드를 거꾸로 재생하는 프로토타입 코드가 있었죠.

우선 동작 완구부터...

별거 없어요. 그냥 Forward하면 정방향으로, Reverse하면 역방향으로 애니메이션이 진행되는 거죠.
위 정도의 애니메이션이라면 그냥 노가다로 거꾸로 진행되는 걸 하나 만들어도 그만이죠.
그러나 애니메이션에 들어가는 키프레임이 수백개가 넘는다면? 악몽이죠. 그걸 거꾸로 돌리고 싶다면...

역방향 재생을 어떻게 할 수 있을지 간단하게 정리해 보자면...
1. 실버라이트는 스토리보드를 거꾸로 재생하는 메서드는 제공하지 않지만 AutoReverse 속성을 true로 설정하면 한번은 정방향으로 재생하고 그 직후 다시 역방향으로 재생할 수 있어요.
2. 또한 실버라이트의 스토리보드는 Seek 메서드를 사용하여 특정 시간으로 점프할 수 있어요.
3. 그러므로 역방향으로 재생할 때에는 스토리보드의 AutoReverse 속성을 true로 설정한 후 스토리보드를 처음부터 시작하고 곧바로 Seek메서드를 호출하여 스토리보드의 총 길이 만큼 이동하면 그 직후 AutoReverse에 의해 스토리보드가 거꾸로 가게 되죠.

이 외에도 범용적으로 사용하기 위해 스토리보드의 실제 총 길이를 계산하는 메서드도 필요하고 정방향으로 재생할 때에는 AutoReverse에 의해 역방향으로 재생되지 않도록 적절한 시점에 Pause를 걸어주는 처리도 필요하죠.

이 모든 처리를 하나의 클래스(ReverseStoryboard)에서 사용할 수 있도록 만들어봤어요.
소스 코드를 다운로드해 보시면 쉽게 이해할 수 있을거에요.

사용법은 간단해요.
// 타겟 스토리보드를 가지고 새 인스턴스 생성
private ReverseStoryboard rs = new ReverseStoryboard(TargetStoryboard);
rs.BeginForward();  // 정방향으로 재생
// 또는...
rs.BeginReverse();  // 역방향으로 재생
게다가 빌트인 Storyboard처럼 Completed 이벤트도 지원하고 Completed 이벤트에서 정방향 재생이었는지 역방향 재생이었는지를 알 수 있어요.
참 쉽죠? ;)

Posted by gongdo


티스토리 툴바