본문 바로가기
안드로이드/기타

ViewBinding vs DataBinding

by 나이아카 2023. 1. 2.

 compose 공부를 시작하면서 어느덧 놓아주어야 하는 것이 아닌가 생각이 드는 viewBinding, dataBinding입니다. 하지만 몇 년 정도는 더 사용되지 않을까 생각이 됩니다. 사실 compose ui가 아직 다른 안드로이드 개발자들이 사용하기에는 NHN, Naver, 우아한 형제들 같은 대기업들도 테스트 단계에 있고 실 적용을 위해 실제로 작성하는 중이니 아마 그런 기업들이 전부 compose ui로 넘어간 이후에야 본격적으로 주 기술스택으로 편입되지 않을까 생각합니다.(물론 아직까지 compose ui의 물결이 없다거나 적다는 것은 아닙니다. android weekly만 봐도...)

 저도 대부분의 코드가 DataBinding으로 이루어져 있고, ViewBinding의 기능을 DataBinding에서 충분히 사용 가능하니 ViewBinding을 쓸 일이 크게 없는데요. 그러다 보니 많은 사람들이 두 개를 구분하지 않고 그냥 다 DataBinding으로 작업을 하는 것 같습니다(물론 아닐 수 있습니다). 하지만, 이 두 개를 구분해서 사용하는 것이 더 효율적일 수도 있기 때문에 이를 알아보기 위해 글을 작성하게 되었습니다.


 안드로이드 UI에는 크게 2가지가 있습니다(작게 나누면 더 많지만, 지금은 ViewBinding과 DataBinding을 위한 비교니 크게 나누겠습니다). 동적인 데이터를 사용하는 View와 그렇지 않고 빌드 시간에 이미 데이터가 확정되는 경우인데요. 전자의 경우 DataBinding이나 RxJava, Compose State를 이용해 동적으로 작성하는 것이 보편화되어 있다고 생각합니다. 그러다보니 이제 많은 안드로이드 개발자분들은 반응형으로 앱을 작성하는데 어려움이 크지 않아 가장 익숙한 형태가 아닐까 싶습니다. 이렇게 동적인 데이터를 사용하는 화면은 거의 대부분이지만, 특정 예를 들어보자면 recyclerView의 ViewHolder나 소켓통신등을 이용해 실시간으로 데이터를 주고 받는 채팅, 주식 화면 등 사용자가 상호작용하지 않아도 데이터가 실시간으로 변경되는 화면이 가장 확실한 예라고 볼 수 있습니다.

 그러나 특정 화면들은 이미 데이터가 모두 정적으로 등록되어 고정되거나, 한 번 화면을 구성할 때 고정시켜두면 변경이 일어나지 않는 화면들이 존재합니다. 예를 들어 웹뷰로 그려지는 프래그먼트라던가, 고정된 메뉴를 가진 메뉴바, 하단 내비게이션바를 가진 메인 프래그먼트 등으로 예를 들 수 있습니다.

 위를 통해 ViewBinding과 DataBinding을 사용할 수 있는 화면의 종류에 대해서 잠깐 이야기를 나눴는데 사실 이 두 종류 모두 DataBinding으로 작업한다고 해서 syntax error나 성능상의 이슈를 발생시키지는 않습니다. 그렇다면 이 두 가지의 공통점 먼저 알아보겠습니다.

 먼저 binding이 도입되기 이전 기본적으로 사용되던 fintViewById와 조금 더 발전된 kotlin Extension은 xml의 view id를 통해 view를 바로 참조합니다. 이는 현재 inflate되어 있지 않은 view의 id를 참조하거나 잘못된 타입으로 접근하는 경우(TextView의 id인데, Layout에 참조하는 경우 등) NPE 및 다른 Exception들을 발생시킬 수 있고, 또 다른 xml과 id가 겹치는 경우 잘못된 view를 참조하는 문제도 발생할 수 있습니다. 특히 findByViewId의 경우에는 사용하기 위한 view의 개수만큼의 변수를 선언해 사용해주어야 하기 때문에 비대한 UI를 사용하는 경우, 변수만으로도 fragment code를 더럽히는 경우가 발생합니다.

 그래서 이를 해결하기 위해 binding을 이용할 수 있습니다. ViewBinding과 DataBinding은 기본적으로 Binding class를 이용해 뷰를 참조하기 때문에 그 뷰의 타입과 이름이 고정되어 사용하게 됩니다. 없는 view의 id를 참조할 일이 없으며 각 xml 별로 binding class가 생성되기 때문에 참조하는 binding class(xml)에 존재하지 않는 뷰는 참조할 수 없게 되어 id와 관련된 에러를 없애 줍니다. 이는 개발자가 직접 findViewById를 통해 변수로 선언해주었던 UI의 값들을 이제는 안드로이드 프레임워크에서 직접 매칭을 시킨 후 class로 변환시켜 사용할 수 있도록 도와준다는 것을 의미합니다.(또한, Kotlin Extension에서 잘못된 UI import로 인해 발생하는 에러들을 사전에 syntax error로 만들어 차단해줍니다.)

 이정도의 차이점이라면 사실 바인딩이 엄청나게 매력적이다 라고 할수는 없겠죠. 어쨌든 Binding Class의 생성으로 인한 파일의 증대도 무시할 수는 없을 테니까요.(물론 요즘은 그런거 신경 잘 안쓰는 것 같습니다) 그렇다면 두 Binding은 어떤 특징을 지니는 지 알아보겠습니다.

 먼저 DataBinding을 사용하려면 <layout>  </layout> 태그를 이용해 xml 코드를 감싸주어야 합니다. 그리고 <data></data>의 코드 내부에서 <variable></variable> or <import></import>를 이용해 특정 클래스를 import하거나(주로 visibility를 위한 View를 많이 사용합니다.) variable로 변수를 받아(주로 ViewModel이 그 대상이 됩니다.) 각 UI에 여러가지 형태로 작성됩니다.

    @BindingAdapter("setGlideImage")
    @JvmStatic
    fun ImageView.GlideImage(fileUrl : String) {
        Glide.with(this.context)
            .load(fileUrl)
            .into(this)

    }
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>
        <variable
            name="fileUrl"
            type="String" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="90dp"
        android:layout_height="90dp">

        <androidx.appcompat.widget.AppCompatImageView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            setGlideImage="@{fileUrl}"/>

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

 간단한 예로는 위와 같이 ImageView에서 String을 variable된 데이터로 받아 Glide로 이미지를 추출하는 코드가 있습니다. 이런 코드를 작성하게 되면 실제 저 화면을 사용하는 Kotlin 코드에서는 binding.fileUrl = data 라는 코드를 뷰 생성시점에 초기화해주기만 하면 됩니다.(위의 코드는 recyclerView의 한 화면이기 때문에 실제로 adapter를 연결하는 화면은 fragment에 존재합니다.) 이를 통해 kotlin 에서 작성되는 UI 코드를 줄이는 역할을 하기도 합니다.

 하지만 ViewBinding의 경우 xml에 추가적인 작업을 해줄 필요는 없습니다. 그래서 추가적인 작업이 적기 때문에 컴파일 시간을 단축시킬 수 있습니다. ViewBinding Class는 들어가서 보면 아래와 같이 getRoot 메소드 하나만 가지고 있는 것을 확인할 수 있습니다.

/** A type which binds the views in a layout XML to fields. */
public interface ViewBinding {
    /**
     * Returns the outermost {@link View} in the associated layout file. If this binding is for a
     * {@code <merge>} layout, this will return the first view inside of the merge tag.
     */
    @NonNull
    View getRoot();
}

 그래서 ViewBinding 작업을 하는데 컴파일 시간이 매우 적게 들어가는 반면, DataBinding시 생성되는 ViewDataBinding Class는 1692줄을 가지고 있는 거대한 덩어리 클래스입니다. 그래서 상대적으로 클래스를 생성하는데 소모되는 시간이 큰 편입니다. 그리고 더 큰 클래스를 이용함으로서 앱 용량을 더 크게 만들 수 있습니다.

 하지만 ViewBinding은 null safety하게 각 UI를 제공해주는 것이 전부인 반면, DataBinding은 각 데이터를 variable에 작성해 필요시에 UI에 xml 코드에서 직접 관리가 가능하며, LiveData, StateFlow, ObservableArrayList등을 이용하면 데이터의 변화에 반응해 UI의 값이 변하는 반응형 UI를 작성할 수 있게 됩니다. 더욱이 editTextView와 같은 UI에 데이터를 양방향 바인딩을 통해 변수와 editText의 text를 동일하게 유지시킬 수도 있습니다. 그래서 더 데이터와 UI간의 변경 사항을 긴밀하게 관리할 수 있게 됩니다.

 결론적으로 ViewBinding은 가벼운 클래스이기 때문에 기능은 UI를 좀 더 안전하게 사용해주는 것이 전부인 기능이고, DataBinding은 반응형 UI의 생성 및 양방향 데이터 바인딩 등의 작업을 할 수 있는 XML을 코드로 변환시켜주는 무거운 클래스입니다. 이는 잦은 데이터의 변동이나, 관리가 필요한 화면의 경우 DataBinding을, 고정된 정보를 뿌려주고 수정 사항이 거의 없는 경우에는 ViewBinding을 이용해 제작하면 반응형 UI와 컴파일 시간을 효과적으로 관리할 수 있습니다.


Kotlin Extension에서 DataBinding으로 이전할 때는 ViewBinding이라는 친구를 잘 모르고 사용했고, DataBinding이 ViewBinding을 상속하고 있다 보니 별 생각 없이 모든 프래그먼트를 DataBinding으로 작업을 했었는데, 요즘에는 알고도 귀찮거나 시간이 없어서 그냥 계속 DataBinding 코드를 들고가고 있습니다. 더 효율적으로 고쳐야지 하면서도 사실 이거보다 훨씬 더 큰 이슈들이 많다보니 뒤로 밀리는 느낌이죠. 모자란 실력을 향상시키는 일은 멀고도 험한 것 같습니다. 

댓글