2 Results for '데이터 바인딩'

  1. 2009.04.07 데이터 바인딩#2 데이터 바인딩의 원리와 사용법 (1)
  2. 2009.03.27 데이터 바인딩#1 맛보기
간 마이크로소프트웨어 4월호에 기고한 내용이에요. 데이터 바인딩의 기초적인 원리와 어떻게 사용하는지에 대한 설명 및 소스가 있어요.
--------------------------

지난 글에서 실버라이트로 데이터 바인딩을 살짝 맛보았다이번에는실버라이트의 데이터 바인딩이 구체적으로 어떻게 이루어졌는지그리고 어떤 기능들을 제공하는지 알아보겠다

데이터 바인딩의 동작 원리 

데이터 바인딩은 기본적으로 UI 엘리먼트와 데이터를 연결하는 것을말한다이를 구현하는 것이 바로 Binding 클래스이고, ystem.Windows.Data 네임스페이스에서 찾을 수 있다지난글에서 우리가 사용했던 Text=”{Binding Path=Name}” 과 같은 바인딩 표현식에서도 Binding 지시자를 통해 UI 엘리먼트와 데이터가 연결되는 것을하였다이들 사이의 관계를 도식화 해보자면 다음과 같다.


[그림 1. 데이터 바인딩의 구조]

l  바인딩 엔진, Binding 클래스는 바인딩 원본과 바인딩 대상의 사이에서 서로의 값을 설정하거나 변화를 감시하는 역할을 수행한다.

l  바인딩 원본은 어떤 종류의 CLR 개체도 사용할 수 있으며 바인딩 원본의 바인딩 할 속성도 제한이 없다.

l  바인딩 대상은 반드시 FrameworkElement에서 파생된 클래스이어야 하고 바인딩 대상 속성은 반드시 DependencyProperty이어야 한다.

l  화살표는 바인딩의 방향을 나타내며 구성에 따라 단방향 혹은 양방향으로 연결할 수 있다.

l  바인딩에 의해 값이 변경될 때 ValueConverter가 지정되어 있을 경우 값이 적절하게 변환되어 설정된다.

 

바인딩은 다른 실버라이트 개체들과 마찬가지로 XAML과 코드로 선언할 수 있다.

[XAML]
<TextBlock Text="{Binding Id}" />

[C#]
TextBlock tb = new TextBlock();
Binding binding = new Binding("Id");
tb.SetBinding(TextBlock.TextProperty, binding);

 [리스트 1. 데이터의 Id속성을 UI Text속성에 바인딩하는 코드 조각]

이렇게 Binding 개체에 의해 연결된 바인딩은 명시적으로 데이터 바인딩 할 원본 개체의 인스턴스를 설정하지 않으면 기본적으로 DataContext 속성에 설정되어 있는 데이터 개체로부터 바인딩 할 원본 속성을 찾는다. DataContext는 실버라이트의 비주얼 트리를 타고 하위 개체로 전파되므로 공통적인 데이터 바인딩을 하는 UI 집합에서 편리하게 사용할 수 있다.


[그림 2. DataContext를 이용한 바인딩 원본 개체 설정]

바인딩의 기능과 표현식

 바인딩은 단순히 데이터와 UI 엘리먼트를 연결하는 기능 외에도 다양한 바인딩 시나리오를 위한 기능이 준비되어 있다. 각각의 기능과 표현식에 대해 알아보자. 아래에 소개한 기능과 표현식의 모든 소스 코드는 [DataBindingFeatures]의 각 폴더에서 찾아 볼 수 있다.

1.      바인딩 할 원본 개체의 속성 설정

앞서 소개한 것처럼 바인딩 할 원본 개체의 속성은 Binding 클래스의 Path 속성을 사용하여 지정한다. XAML에서 바인딩 표현식을 사용할 때에는 Path지시자를 생략하고 바인딩 할 원본 개체의 속성만 지정할 수 있다

또한 Path 속성을 아예 생략할 수도 있는데, 이 경우 바인딩 대상 속성에는 바인딩 원본 개체 그 자체가 바인딩 된다.

[XAML]
<TextBlock Text="{Binding Path=Name}" />
<!-- 또는 -->
<TextBlock Text="{Binding Name }" />
<!-- 또는 -->
<TextBlock Text="{Binding }" />

[리스트 2. 바인딩 경로를 설정하는 XAML 표현식]

2.      명시적인 데이터 원본 설정

데이터 원본은 Binding클래스의 Source 속성으로 설정할 수 있다. Source 속성을 생략할 경우 자동으로 DataContext에서 바인딩할 속성을 가져온다. 일반적인 경우 Source 속성을 생략하고 DataContext로부터 바인딩 받는 경우가 대부분이지만 때로 명시적인 DataContext가 아닌 명시적인 원본 설정이 필요할 때도 있다.

[XAML]
<TextBlock Text="{Binding Path=Id, Source={StaticResource MyProfile} }" />

[리스트 3. 명시적으로 바인딩 원본을 설정하는 XAML 표현식]

XAML에서 Source를 명시적으로 사용하기 위해서는 먼저 해당 엘리먼트를 포함하는 사용자 컨트롤이나 App.xaml에 반드시 바인딩할 원본을 리소스로 등록한 후 StaticResource 표현식을 사용하여 설정해야 한다. 위의 예제는 리소스로 등록된 MyResource 개체를 바인딩 원본으로 사용하여 MyResource.Id 속성을 Text 속성에 바인딩한다는 의미이다.

3.      데이터의 흐름 설정

Binding 클래스의 Mode 속성으로 원본을 대상에 어떤 방향으로 바인딩 할지 설정할 수 있다. 데이터 흐름에는 다음과 같은 세 종류가 있으며 Mode 속성이 생략 될 경우 자동으로 OneWay 방식이 선택된다.

l  OneTime
최초로 바인딩 원본이 설정될 때 단 한번만 바인딩 대상을 변경하는 모드.

l  OneWay
바인딩된 원본이 변경사항을 알려줄 때마다 자동으로 바인딩 대상도 변경하는 모드.

l  TwoWay
바인딩 대상이 변경되었을 때 역으로 바인딩 원본도 변경하는 양방향 모드.

[XAML]
<TextBlock Text="{Binding Id, Mode=OneTime}" />
<!-- 또는 -->
<TextBlock Text="{Binding Id, Mode=OneWay}" />
<!-- 또는 -->
<TextBlock Text="{Binding Id, Mode=TwoWay}" />

[리스트 4. 바인딩 흐름을 설정하는 XAML 표현식

, OneWay TwoWay 모드가 정상적으로 동작하기 위해서는 데이터 원본 오브젝트가 반드시 다음의 INotifyPropertyChanged 인터페이스를 구현해야 한다.

4.      데이터 원본의 변경 알림

위에서 본 데이터 바인딩의 OneWay혹은 TwoWay 모드는 데이터 원본이 변경되면 자동으로 바인딩 대상도 변경되어야 한다. 이를 위해 데이터 원본 개체는 자신의 데이터가 변경될 때 바인딩 개체에 그 사실을 알려줘야 한다.

이 과정은 모든 CLR 개체에서 자동으로 되는 것이 아니라 INotifyPropertyChanged라는 인터페이스를 상속받은 개체에서 코드를 통해 명시적으로 구현을 해야 한다.

[C#]
public class Profile : INotifyPropertyChanged
{
    private string _id;
    public string Id
    {
        get { return _id; }
        set
        {
            _id = value;
            FirePropertyChanged("Id");
        }
    }
    #region INotifyPropertyChanged Members
    public event PropertyChangedEventHandler PropertyChanged;
    protected void FirePropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
    #endregion
}

[리스트 5. INotifyPropertyChanged 인터페이스를 구현하는 원본 개체의 예제

이렇게 데이터 원본은 PropertyChanged 이벤트를 발생시키고 바인딩 엔진은 그 이벤트를 받아 바인딩 대상 속성을 변경한다.

5.      데이터 컨버팅

때로는 바인딩 원본 속성의 타입과 바인딩 대상 속성의 타입이 일치하지 않을 수도 있다. 예를 들어 TextBlock Text 속성에 DateTime 타입의 값을 바인딩한다면 시스템의 로캘에 따라 “18/03/2009 PM 08:09:00”과 같이 원치 않는 형식으로 바인딩되는 것을 볼 수 있다. 이 때 IValueConverter 인터페이스를 상속하는 클래스를 하나 만들고 IValueConverter 인터페이스의 Convert 메서드와 ConvertBack 메서드를 구현하면 된다. 이렇게 구현한 ValueConverter는 먼저 XAML에 리소스로 등록을 한 후 Binding 클래스의 Converter속성에 리소스를 설정하면 된다. 또한 ConverterParameter 속성을 사용하면 값을 변환할 때 옵션을 부여할 수 있다.

[XAML]
<TextBox Text="{Binding Birthday,
                 Mode=TwoWay,
                 Converter={StaticResource DateConverter},
                 ConverterParameter=yyyy MM dd}" />
 [C#]
public class DateValueConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        // 생략
        return ;
    }
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        // 생략
        return ;
    }
    #endregion
}

[리스트 6. IValueConverter 인터페이스를 구현하는 원본 개체의 예제

Convert 메서드는 데이터 원본에서 바인딩 대상 방향으로 설정할 때 사용되고 ConvertBack 메서드는 반대로 바인딩 대상의 변화를 데이터 원본에 설정할 때 사용된다. 참고로 앞의 코드 조각에서 볼 수 있듯이 바인딩 표현식은 보기 좋게 정리하기 위해 표현식의 각 지시자 사이에 엔터를 포함한 공백을 넣을 수 있다.

6.      데이터 유효성 검사

TwoWay 바인딩이 설정된 TextBox에서 사용자가 문자열을 입력하면 입력된 문자열이 다시 데이터 원본 속성에 설정이 된다. 이 때 기본적으로 데이터 원본 속성의 설정자에서 사용자 입력의 유효성을 검사할 수도 있고 ValueConverter를 사용하여 유효한 형태로 변환할 수도 있다. 그러나 이렇게 할 경우 사용자가 잘못된 입력을 했다는 것을 상위 UI에 알려주려면 별도의 이벤트나 메서드를 사용해야 한다. 특히 복잡한 UI를 가진 입력 폼 등의 애플리케이션은 표준적인 입력 오류를 알려주는 수단이 필요하다.

Binding 클래스는 데이터 유효성 검사를 위해 ValidatesOnExceptions 속성과 NotifyOnValidationError 속성을 지원한다. 이 두 속성을 모두 True로 설정하면 데이터 원본의 속성이 바인딩 엔진에 의해 설정 중에 발생된 예외를 잡아서 BindingValidationError 이벤트로 전달한다. BindingValidationError 이벤트는 모든 FrameworkElement가 가지고 있는 이벤트로 이벤트가 발생한 엘리먼트에서부터 시작하여 이벤트가 처리될때까지 상위 엘리먼트로 계속 라우팅되는 버블 이벤트이다.

[XAML]
<TextBox
    Text="{Binding Path=Age,
    NotifyOnValidationError=true,
    ValidatesOnExceptions=true}" />
[C#]
public class Profile : INotifyPropertyChanged
{
    private int _age;
    public int Age
    {
        get { return _age; }
        set
        {
            if (value < 0 || value > 150)
                throw new Exception("나이는 0~150까지만 가능합니다.");
            _age = value;
            FirePropertyChanged("Age");
        }
    }
}
// 바인딩 에러 처리
void Page_BindingValidationError(object sender, ValidationErrorEventArgs e)
{
    // 에러가 발생할 때 처리
    if (e.Action == ValidationErrorEventAction.Added)
        ErrorCaption.Text = e.Error.Exception.Message;
    // 에러가 해결되었을 때 처리
    else if (e.Action == ValidationErrorEventAction.Removed)
        ErrorCaption.Text = "";
    e.Handled = true;
}

[리스트 7. BindingValidationError를 이용한 데이터 유효성 검사 예제

지금까지 실버라이트 데이터 바인딩의 원리와 중요한 기능들을 살펴보았다. 물론 이것들은 데이터 바인딩의 가장 기초적인 부분이다. 보다 효과적인 사용을 위해서는 세심한 데이터 모델 설계와 UI의 구성, 그리고 무엇보다도 디자이너와의 협업이 중요하다. 다음 글에서는 데이터 바인딩을 보다 효과적으로 사용하는 방법과 디자이너와 협업하기 위한 방법에 대해 알아보도록 하겠다.

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

다음호에 봐요 :D
저작자 표시 동일 조건 변경 허락
신고
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


티스토리 툴바