대메뉴 바로가기 본문 바로가기

데이터 기술 자료

데이터 기술 자료 상세보기
제목 파워업 안드로이드 개발 : 안드로이드 데이터 바인딩 라이브러리 분석기
등록일 조회수 7724
첨부파일  

파워업 안드로이드 개발

안드로이드 데이터 바인딩 라이브러리 분석기



2015년 ‘구글 I/O’ 행사에서 발표한 새로운 안드로이드 개발 도구들 중 ‘안드로이드 데이터 바인딩 라이브러리(Android Data Binding Library)’에 대해 소개하고자 한다. 현재(2015년 8월) 베타 버전이긴 하지만 안드로이드 데이터 바인딩은 개발에 바로 적용할 수 있고 코딩 스타일과 아키텍처에 변화를 줘야 하는 부분이므로 미리 숙지하고 적응하는 것이 좋다. 간단한 예제를 통해 안드로이드 데이터 바인딩에 대해 알아본다.



데이터 바인딩(Data Binding)에 대해 위키피디아(Wikipedia)는 ‘사용자 인터페이스(User Interface)와 비즈니스 로직(Business logic)을 서로 연결하는 프로세스’로 정의하고 있다. 이를 통해 MVVM 패턴 즉, View와의 의존성을 완벽히 분리할 수 있는 Model-View-ViewModel 디자인 패턴 구현이 가능하다. 이는 곧 UI를 수정하는 코드를 작성하지 않아도 알아서 데이터를 파악한 뒤 자동으로 바꿔주는 기능을 구현할 수 있음을 의미한다. 웹 프로그래밍이나, 윈도우 프로그래밍에서 자주 쓰이던 이 기법을 이제 안드로이드 앱 개발에서도 데이터 바인딩 라이브러리를 통해 사용할 수 있게 됐다.





왜 안드로이드 데이터 바인딩인가?

물론 기존에도 View에 데이터를 바인딩하거나 유사하게 MVVM 패턴을 구현하는 것이 가능했다. findViewById를 통해 View에 접근하고 특정 로직(logic)에 따라 데이터(data)가 변화되면 해당 View에 데이터를 직접 설정(set)하고 이러한 부분을 별도의 ViewModel class로 분리해 관리하면 MVVM 패턴 구현이 가능하다. 하지만 이는 소스 부분에서 View의 변경에 대한 처리를 직접 해야 하고 View 처리가 복잡해지면 ViewModel class가 커지는 단점이 있다.

안드로이드 데이터 바인딩 라이브러리를 사용하면 View 자체에서 데이터 변경에 대한 동적 처리가 가능하다. 그를 통해 View Model에 데이터만 남게 함으로써 완벽하게 View-ViewModel을 분리할 수 있다. 이는 곧 findViewById와 작별 인사를 해도 된다는 의미이고, 좀 더 간결한 코드와 완벽한 MVVM 패턴 구현을 가능하게 한다.



환경 설정하기

그럼 이제 안드로이드 데이터 바인딩을 시작해보자. 먼저 환경 설정이다. 환경 설정에 앞서 당연히 안드로이드 스튜디오는 1.3(Canary Channel - Preview)으로 업데이트해야 한다. 첫 번째로 프로젝트의 최상위 build.gradle의 dependencies에 gradle 1.3.0-beta4와 dataBinder 1.0-rc1을 추가해 주면 된다(2015년 8월 3일 기준).



<리스트 1> gradle과 data Binding class path 추가 dependencies { classpath "com.android.tools.build:gradle:1.3.0-beta4" classpath "com.android.databinding:dataBinder:1.0-rc1" }



올해 7월까지만 해도 가이드는 1.3.0-beta3을 기준으로 작성됐다. 그러나 이대로 하면 플러그인 에러가 발생했다. 해당 beta 버전에 문제가 있는 듯했다. 당시 1.3.0-beta1로 다운그레이드하니 정상 동작했다. 현재는 앞서 설명한 방법으로 하면 정상적으로 동작한다. 필자가 추천하는 방법은 +를 이용해 가장 최신 버전이 적용되게 하는 방법이다.



<리스트 2> +를 이용한 class path 추가 dependencies { classpath "com.android.tools.build:gradle:1.3.+" classpath "com.android.databinding:dataBinder:1.+" }



두 번째로 데이터 바인딩을 적용하고 싶은 모듈에 데이터 바인딩 플러그인(data binding plugin)을 적용하면 된다. 적용 모듈의 build.gradle에 <리스트 3>과 같이 추가한다.



<리스트 3> data binding plugin 적용 apply plugin: 'com.android.databinding'



compileSdkVersion과 targetSdkVersion은 MNC로 업데이트하지 않아도 된다. 즉, MNC가 아닌 하위 버전도 적용이 가능하다는 얘기다. 실제로 필자는 킷켓 기기에서 테스트했다. 몇몇 블로그에는 SDK 버전을 MNC로 하라고 설명하는데 이는 잘못된 정보다.



데이터 바인딩해보기

자, 이제 모든 준비가 끝났다. 본격적으로 코딩을 해보자. MainActivity와 데이터 역할을 할 Friend 클래스를 만들자. Friend 클래스에는 firstName, lastName 2개의 멤버 변수가 있다. 화면에는 firstName, lastName를 표시한다.



<리스트 4> Friend Data class 코드 public class Friend { private String firstName; private String lastName; public Friend(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } public String getFirstName() { return this.firstName; } public String getLastName() { return this.lastName; } public void setFirstName(String firstName) { this.firstName = firstName; } public void setLastName(String lastName) { this.lastName = lastName; } }



데이터 바인딩을 적용하려면 layout에 최상위 태그(tag)를 선언하고 바로 아래에 태그를 추가한다. 태그 안에는 해당 layout에 적용할 데이터의 class name을 지정할 수 있다.



<리스트 5> 레이아웃 xml 코드 < ?xml version="1.0" encoding="utf-8"?> < layout xmlns:android="http://schemas.android.com/apk/res/android"> < data> < variable name="friend" type="com.dev.edgarkim.databindingtest.Friend"/> < /data> < RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent"> < TextView android:id="@+id/textView" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@{friend.firstName}" /> < TextView android:id="@+id/textView2" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/textView" android:text="@{friend.lastName}" /> < Button android:id="@+id/button" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="76dp" android:onClick="onButtonClick" android:text="Set Friend" /> < /RelativeLayout> < /layout>



android:text="@{friend.lastName}" 그리고 해당 데이터를 태그에서 지정한 접근자를 이용해 View에 적용할 수 있다. MainActivity에서는 <리스트 6>과 같이 DataBindingUtil로 View를 바인드한다.



<리스트 6> MainActivity class 코드 package com.dev.edgarkim.databindingtest; import android.app.Activity; import android.databinding.DataBindingUtil; import android.os.Bundle; import android.view.View; import com.dev.edgarkim.databindingtest.databinding.ActivityMainBinding; public class MainActivity extends Activity { Friend myFriend; MyHandler handler; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main); myFriend = new Friend("FirstName", "LastName"); binding.setFriend(myFriend); } public void onButtonClick(View view) { myFriend.setFirstName("donghoon"); myFriend.setLastName("kim"); } }



ActivityMainBinding은 자동으로 생성된다. 생성 시점은 최상의 컴포넌트가 인 .xml이 생성되거나 수정될 때 자동으로 생성된다. 새로운 데이터를 설정할 수 있게 Button에 함수도 추가하자. 실행을 해보면 MainActivity에 생성한 myFriend의 데이터가 적용된 것을 확인할 수 있다.





기존 방법으로 하려면 findViewById를 통해 리소스 ID로부터 View를 가져와 setText를 해야 한다. 데이터 바인딩을 이용하면 findViewById와는 굿바이해도 된다. 지금까지 가장 간단하게 데이터 바인딩을 적용하는 방법을 알아봤다. 이제부터는 데이터 바인딩을 통한 몇 가지 추가 기능을 살펴본다.



BaseObservable로 View 변경하기

Button을 클릭해 보자. View가 변하지 않는다. Friend를 바인드했으니 View에 바로 변경된 데이터가 적용될 것 같으나 되지 않는다. 어떻게 해야 할까? 기존 방법대로 한다면 setText를 하면 된다. 필자는 안드로이드 데이터 바인딩을 이용하는 방법 두 가지를 발견했다. 첫 번째는 바인딩을 데이터가 변화시킬 때마다 호출하는 것이다. OnButtonClick 함수에서 마지막에 Activity MainBinding 객체를 통해 setFriend를 한 번 더 해보자. 실행하면 새로 설정한 데이터가 View에 표시되는 것을 확인할 수 있다.



<리스트 7> 데이터 변경 시 객체를 새로 바인딩하는 방법 public void onButtonClick(View view) { myFriend.setFirstName("donghoon"); myFriend.setLastName("kim"); binding.setFriend(myFriend); }



<리스트 7>의 방법은 데이터가 변경될 때마다 해당 객체를 새로 바인딩해줘야 하므로 추천하지 않는다. 필자가 찾은 두 번째 방법은 Friend를 Observable class로 만드는 것이다.



<리스트 8> Observable로 만든 Friend Class 코드 package com.dev.edgarkim.databindingtest; import android.databinding.BaseObservable; import android.databinding.Bindable; import com.dev.edgarkim.databindingtest.BR; public class Friend extends BaseObservable { private String firstName; private String lastName; public Friend(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } @Bindable public String getFirstName() { return this.firstName; } @Bindable public String getLastName() { return this.lastName; } public void setFirstName(String firstName) { this.firstName = firstName; notifyPropertyChanged(BR.firstName); } public void setLastName(String lastName) { this.lastName = lastName; isClose = true; notifyPropertyChanged(BR.lastName); } }



데이터 바인딩의 BaseObservable을 상속해 사용하면 된다. get FirstName과 getLastName에 Bindable annotation을 추가하고, set 함수에서 notifyPropertyChanged를 호출한다. Bindable을 get 함수에 하지 않고 멤버 변수 선언 부위에 직접 추가해도 된다. 단, 이때 멤버 변수가 public이어야 한다. BaseObservable을 상속받으면 addOnPropertyChangedCallback으로 데이터 변경 시에 확인할 수 있다. 실행해 보면 Button 클릭 시에 설정한 데이터로 View가 바뀌는 것을 확인할 수 있다.





데이터의 상태에 따른 View의 속성 변경과 다른 이벤트 발생시키기

이번에는 데이터 바인딩을 이용해 이벤트(event)나 View의 속성을 변경하는 방법을 알아본다. 변수의 상태에 따라 textView의 색상과 클릭 이벤트를 다르게 하는 기능을 만들어 보자. 구현을 위해 Friend class에 isClose 변수를 추가하고, onButtonClick 시 set하도록 하자. 이때 isClose는 public으로 만들고 선언부 바로 위에 Bindable을 추가하자. 그리고 클릭 이벤트를 위해 onClickFriend와 onClickNotFriend를 MyHandler라는 class에 구현한다.



<리스트 9> MyHandler Class 코드 package com.dev.edgarkim.databindingtest; import android.content.Context; import android.view.View; import android.widget.Toast; public class MyHandler { Context mContext; MyHandler(Context context) { mContext = context; } public void onClickFriend(View view) { Toast.makeText(mContext, "Hello My Friend", Toast.LENGTH_SHORT).show(); } public void onClickNotFriend(View view) { Toast.makeText(mContext, "You are not My Friend !!!", Toast.LENGTH_SHORT).show(); } }



<리스트 10> Friend Class 최종 package com.dev.edgarkim.databindingtest; import android.databinding.BaseObservable; import android.databinding.Bindable; import com.dev.edgarkim.databindingtest.BR; public class Friend extends BaseObservable { private String firstName; private String lastName; @Bindable public boolean isClose; public static final String displayName = "displayName"; public Friend(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } @Bindable public String getFirstName() { return this.firstName; } @Bindable public String getLastName() { return this.lastName; } public void setFirstName(String firstName) { this.firstName = firstName; notifyPropertyChanged(BR.firstName); } public void setLastName(String lastName) { this.lastName = lastName; isClose = true; notifyPropertyChanged(BR.lastName); } public void setIsClose(boolean isClose) { this.isClose = isClose; notifyPropertyChanged(BR.isClose); } }



<리스트 11> MainActivity 최종 package com.dev.edgarkim.databindingtest; import android.app.Activity; import android.databinding.DataBindingUtil; import android.os.Bundle; import android.view.View; import com.dev.edgarkim.databindingtest.databinding.ActivityMainBinding; public class MainActivity extends Activity { Friend myFriend; MyHandler handler; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main); myFriend = new Friend(null, "LastName"); handler = new MyHandler(getApplicationContext()); binding.setFriend(myFriend); binding.setHandlers(handler); } public void onButtonClick(View view) { myFriend.setFirstName("donghoon"); myFriend.setLastName("kim"); myFriend.setIsClose(true); } }



이제 layout을 변경해 보자(<리스트 12> 참조).



<리스트 12> 레이아웃 xml 최종 < ?xml version="1.0" encoding="utf-8"?> < layout xmlns:android="http://schemas.android.com/apk/res/android"> < data> < import type="com.dev.edgarkim.databindingtest.Friend" /> < variable name="handlers" type="com.dev.edgarkim.databindingtest.MyHandler"/> < variable name="friend" type="Friend"/> < /data> < RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent"> < TextView android:id="@+id/textView" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="@{friend.isClose ? @android:color/holo_red_light : @android:color/holo_blue_bright}" android:text="@{friend.firstName ?? friend.displayName}" /> < TextView android:id="@+id/textView2" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/textView" android:text="@{friend.lastName}" android:onClick="@{friend.isClose ? handlers.onClickFriend : handlers.onClickNotFriend}" /> < Button android:id="@+id/button" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="76dp" android:onClick="onButtonClick" android:text="Set Friend" /> < /RelativeLayout> < /layout>



태그 안에 임포트(import)를 이용해 데이터에 더 쉽게 접근할 수 있도록 변경한다.



<리스트 13> 태그에 임포트를 이용한 데이터 선언



Text filed에 데이터 바인딩을 통해 null point 처리를 한다. friend의 firstName이 null이면 dispalyName을 표시하도록 한다. “??” 처럼 + - / * % instanceof 등과 같은 기본적인 자바 expression 적용이 가능하다. 마지막으로 조건 연산자를 통해 데이터의 상태에 따라 글자색을 변경한다.



<리스트 14> Null check와 view 속성 변경 < TextView android:id="@+id/textView" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="@{friend.isClose ? @android:color/holo_red_light : @android:color/holo_blue_bright}" android:text="@{friend.firstName ?? friend.displayName}" />





이번에는 데이터의 상태에 따라 다른 이벤트를 발생시켜보자. 클릭 시 데이터의 값에 따라 다른 클릭 함수를 호출한다.



<리스트 15> 데이터의 상태에 따른 이벤트 발생 < TextView android:id="@+id/textView2" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/textView" android:text="@{friend.lastName}" android:onClick="@{friend.isClose ? handlers.onClickFriend : handlers.onClickNotFriend}" />





결론

지금까지 간단하게 몇 가지 안드로이드 데이터 바인딩 적용 방법을 살펴봤다. 안드로이드 데이터 바인딩은 findViewById를 쓰지 않아도 되고 간결한 코드 작성이 가능하다는 장점도 있다. 가장 큰 장점은 findViewById() 결과를 이미 바인딩 객체가 가지고 있어서 데이터 처리와 화면 표시 코드가 분리된다는 점이다. 이는 코드 단에서 UI를 변경하는 직접적인 부분들이 상당 부분 사라지게 할 수 있다. 하지만 아쉬운 점은 약간의 개발 환경 설정을 해야 하고 외곽 layout 태그를 꼭 써야 한다는 점, 그리고 디버깅이 기존보다 어렵다는 점이다. 안드로이드 데이터 바인딩은 개발하는 프로젝트의 성격에 따라 적용 가능 여부를 판단하는 것이 좋겠다. 현재 안드로이드 데이터 바인딩 라이브러리는 베타 버전이다. 그 때문에 앞으로 어떻게 바뀔지 모른다. 하지만 분명한 건 안드로이드 데이터 바인딩을 통해 좀 더 유연하고 독립적인 애플리케이션을 개발할 수 있다는 점이다. 하루빨리 정식 버전이 나오길 기대하며 글을 마친다.



출처 : 마이크로소프트웨어 9월호

제공 : 데이터 전문가 지식포털 DBguide.net