안드로이드 개발에서 ViewBinding과 DataBinding은 UI와 코드의 결합 방식을 개선해 주는 중요한 바인딩 기술이다. 두 방법을 비교하기 위해 사용자가 EditText에 입력한 글자 수에 따라 TextView의 색상이 바뀌도록 구성해 보았다. 이를 통해 ViewBinding과 DataBinding의 차이점과, DataBinding에서 제공하는 BindingAdapter를 활용해보았다. 또한 TextWatcher와 DataBinding의 동작 방식을 비교하면서 어떤 상황에서 어떤 바인딩이 유리한지 알아보았다.
코드 및 설명
1. ViewBinding을 사용한 구현 (FragmentTopViewBinding)
FragmentTopViewBinding에서는 ViewBinding을 사용하여 TextWatcher로 EditText의 텍스트 변화를 감지하고 글자 수에 따라 TextView의 색상을 변경하였다.
// FragmentTopViewBinding.kt
package com.yujin45.androidstudy
import android.graphics.Color
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.yujin45.androidstudy.databinding.FragmentTopViewBindingBinding
class FragmentTopViewBinding : Fragment() {
private var _binding: FragmentTopViewBindingBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentTopViewBindingBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.editText.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
val textLength = s?.length ?: 0
if (textLength >= 3) {
binding.feedbackText.setTextColor(Color.BLUE)
binding.feedbackText.text = "글자 수가 충분합니다."
} else {
binding.feedbackText.setTextColor(Color.RED)
binding.feedbackText.text = "글자 수가 부족합니다."
}
}
override fun afterTextChanged(s: Editable?) {}
})
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
- ViewBinding은 XML의 각 뷰에 직접 접근할 수 있도록 바인딩 객체를 제공한다. TextWatcher를 통해 EditText의 변화를 감지하고, 글자 수가 3 이상이면 파란색, 그 외에는 빨간색으로 TextView의 색상을 변경하였다. ViewBinding의 경우 TextWatcher로 직접 변경 사항을 감지하고, UI를 업데이트하는 방식이다.
❓ TextWatcher의 동작 과정은?
- TextWatcher는 EditText 등 텍스트가 입력될 수 있는 컴포넌트에 텍스트 변화 이벤트를 리스닝하는 인터페이스로, 텍스트가 변경될 때마다 호출되는 세 가지 콜백 메서드를 제공한다.
TextWatcher 메서드

TextWatcher는 다음과 같은 세 가지 메서드를 제공한다.
- beforeTextChanged(CharSequence s, int start, int count, int after)
- 이 메서드는 텍스트가 변경되기 직전에 호출된다.
- 텍스트 변화가 시작되기 전의 텍스트 내용, 시작 위치(start), 삭제될 문자의 개수(count), 추가될 문자의 개수(after)를 매개변수로 전달한다.
- onTextChanged(CharSequence s, int start, int before, int count)
- 이 메서드는 텍스트가 변경되는 도중에 호출된다.
- 새로 입력된 텍스트, 변경이 시작된 위치(start), 변경된 이전 텍스트 길이(before), 새로 추가된 텍스트 길이(count)가 매개변수로 전달된다.
- afterTextChanged(Editable s)
- 이 메서드는 텍스트가 변경된 후 호출된다.
- 텍스트가 완전히 변경된 후 Editable 객체가 매개변수로 전달된다.
- 여기서 텍스트의 추가적인 수정이 가능하지만, 무한 루프를 방지해야 한다.
2. DataBinding을 사용한 구현 (FragmentBottomDataBinding)
- FragmentBottomDataBinding에서는 DataBinding과 BindingAdapter를 사용해 동일한 기능을 구현하였다.
- User 데이터 클래스: DataBinding의 Observable 기능을 통해 inputText 속성의 변경을 감지하여 TextView의 색상을 동적으로 업데이트한다.
// User.kt
package com.yujin45.androidstudy
import androidx.databinding.BaseObservable
import androidx.databinding.Bindable
import com.yujin45.androidstudy.BR
class User : BaseObservable() {
@get:Bindable
var inputText: String = ""
set(value) {
field = value
notifyPropertyChanged(BR.inputText)
notifyPropertyChanged(BR.textLongEnough)
}
@get:Bindable
val isTextLongEnough: Boolean
get() = inputText.length >= 3
}
2. BindingAdapter로 텍스트 색상 설정: BindingAdapter를 사용하여 isTextLongEnough의 상태에 따라 텍스트 색상을 설정한다.
// BindingAdapters.kt
package com.yujin45.androidstudy
import android.graphics.Color
import android.widget.TextView
import androidx.databinding.BindingAdapter
@BindingAdapter("app:textColorBasedOnLength")
fun setTextColorBasedOnLength(view: TextView, isTextLongEnough: Boolean) {
view.setTextColor(if (isTextLongEnough) Color.BLUE else Color.RED)
}
3. FragmentBottomDataBinding 구현: XML에서 DataBinding을 사용해 User 객체의 속성을 바인딩하고, BindingAdapter를 통해 텍스트 색상을 동적으로 변경한다.
❓ DataBinding은 xml에서 ViewBinding이 아니라고 명시하지 않아도 되는가?
- fragment_bottom_databinding.xml에서 XML 루트 태그로 <layout>을 사용했기 때문에, 별도로 tools:viewBindingIgnore="true"를 추가하지 않아도 DataBinding이 자동으로 활성화되고, ViewBinding이 비활성화된다.
// FragmentBottomDataBinding.kt
package com.yujin45.androidstudy
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.yujin45.androidstudy.databinding.FragmentBottomDataBindingBinding
class FragmentBottomDataBinding : Fragment() {
private lateinit var binding: FragmentBottomDataBindingBinding
private val user = User()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentBottomDataBindingBinding.inflate(inflater, container, false)
binding.user = user
//binding.lifecycleOwner = this // LiveData에서 필요
return binding.root
}
}
- DataBinding은 UI와 데이터 모델 간의 결합을 자동으로 관리하며, BindingAdapter를 통해 특정 조건에 따른 UI 속성을 XML에서 설정할 수 있다. app:textColorBasedOnLength를 통해 isTextLongEnough 속성에 따라 텍스트 색상을 동적으로 설정하였다.
❓ binding.lifecycleOwner = this 가 뭘까?
- lifecycleOwner를 설정하면 DataBinding이 해당 UI 컴포넌트의 생명 주기를 인식하고, 이를 통해 LiveData를 활용한 데이터 변화를 UI에서 안전하게 처리할 수 있다.
- lifecycleOwner를 설정하지 않으면 LiveData와 DataBinding의 결합이 이루어지지 않아서 데이터가 변경되어도 UI가 업데이트되지 않는다. 특히 Fragment와 같은 컴포넌트는 라이프사이클을 갖기 때문에 DataBinding이 생명 주기를 인식하게 설정하는 것이 중요하다.
- DataBinding에서 lifecycleOwner 설정은 아래와 같은 경우에 유용하다:
- LiveData와의 자동 업데이트 연동: LiveData를 사용하면 lifecycleOwner를 통해 데이터 변경 시 자동으로 UI가 업데이트된다.
- Fragment 및 Activity의 생명 주기 관리: 생명 주기를 인식하여, 프래그먼트가 사라질 때 자동으로 LiveData를 해제함으로써 메모리 누수를 방지한다.
- 따라서 binding.lifecycleOwner = this를 설정함으로써 DataBinding이 UI 생명 주기와 연동되어 LiveData 및 Observable 데이터의 자동 갱신 기능을 원활하게 사용할 수 있다.
❓BaseObservable과 @Bindable 를 쓸 때도 lifecycleOwner = this 가 필요한가?
- 위의 우리가 진행한 예시에서는 LiveData를 사용하지 않고, BaseObservable과 @Bindable을 통해 DataBinding이 User 객체의 속성 변경을 자동으로 UI에 반영하도록 설정되어 있다.
- lifecycleOwner는 LiveData와 같이 생명 주기에 따라 UI 업데이트가 필요할 때만 설정하는 것이므로, 이 경우 생략해도 동작에는 문제가 없다. 그래서 주석 처리 해두었다.
3. DataBinding과 ViewModel을 사용한 구현 (FragmentDataBindingWithViewModel)
DataBinding과 ViewModel, LiveData를 통해 동일한 기능을 구현하였다.
// UserViewModel.kt
package com.yujin45.androidstudy
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.map
class UserViewModel : ViewModel() {
// 양방향 바인딩을 위해 MutableLiveData를 사용
val inputText = MutableLiveData<String>("")
// 글자 수가 3 이상일 때 상태를 true로 표시하는 LiveData
val isTextLongEnough: LiveData<Boolean> = inputText.map { it.length >= 3 }
}
✅ LiveData와 양방향 바인딩에 대한 주의 사항
- 권장 방식: LiveData는 일반적으로 단방향 데이터 흐름을 권장한다. 이는 LiveData가 보통 UI에서 데이터 모델로의 역방향 업데이트가 아닌, 데이터 모델에서 UI로의 변경 사항 전달을 담당하기 때문이다.
- 양방향 바인딩 설정: 하지만 양방향 바인딩이 필요한 경우 BindingAdapter와 InverseBindingAdapter를 설정하여 EditText의 변화를 LiveData에 반영할 수 있다.
BindingAdapter와 InverseBindingAdapter 구현
// BindingAdapters.kt
package com.yujin45.androidstudy
import android.text.Editable
import android.text.TextWatcher
import android.widget.EditText
import android.widget.TextView
import androidx.databinding.BindingAdapter
import androidx.databinding.InverseBindingAdapter
import androidx.databinding.InverseBindingListener
@BindingAdapter("app:textColorBasedOnLength")
fun setTextColorBasedOnLength(view: TextView, isTextLongEnough: Boolean) {
view.setTextColor(if (isTextLongEnough) Color.BLUE else Color.RED)
}
// EditText에서 값을 가져와 설정하기 위한 BindingAdapter
@BindingAdapter("app:inputText")
fun setInputText(view: EditText, value: String?) {
if (view.text.toString() != value) {
view.setText(value)
}
}
// InverseBindingAdapter - EditText의 변화를 ViewModel의 MutableLiveData에 반영
@InverseBindingAdapter(attribute = "app:inputText", event = "inputTextAttrChanged")
fun getInputText(view: EditText): String {
return view.text.toString()
}
// Listener 설정을 통해 EditText의 텍스트 변경 감지
@BindingAdapter("app:inputTextAttrChanged")
fun setInputTextListener(view: EditText, listener: InverseBindingListener?) {
if (listener != null) {
view.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
listener.onChange()
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
})
}
}
ViewModel과 DataBinding 결합 시 양방향 바인딩 설정 시의 유의점
LiveData는 단방향으로 데이터가 흐르도록 설계되어 있지만, 이 예제에서는 BindingAdapter와 InverseBindingAdapter를 통해 EditText의 텍스트 변화를 ViewModel의 LiveData에 수동으로 전달하여 양방향 바인딩을 구성하였다. 이는 코드의 재사용성을 높여주며, UI에서 데이터 모델로의 데이터 흐름을 원활히 처리할 수 있게 한다.
양방향으로 구현하려고 하다 보니 LiveData의 기본 단방향 흐름과 BindingAdapter를 활용한 양방향 바인딩을 모두 경험할 수 있었다. 상황에 따라 단방향 또는 양방향을 선택해 사용하며, ✅ LiveData는 가급적 단방향으로 사용하는 것이 좋다. 이제는 LiveData는 단방향으로 사용하도록 하자.
위와 관련한 참고 포스팅 링크 ▼
https://yujinius45.tistory.com/132
[Android] LiveData와 DataBinding의 양방향 바인딩 시 발생하는 오류 해결하기
문제 상황안드로이드 개발에서 LiveData와 DataBinding을 결합해 양방향 바인딩을 구현하려고 할 때 아래와 같은 오류가 발생할 수 있다.The expression 'userViewModelInputText.getValue()' cannot be inverted, so it canno
yujinius45.tistory.com
TextWatcher의 동작 방식과 DataBinding의 차이점
- TextWatcher 동작 방식:
- TextWatcher는 텍스트가 입력될 때마다 매번 콜백을 실행하여 변화를 감지하고, 변화된 내용을 UI에 적용하는 방식이다.
- 직접적인 콜백 방식이므로 각 이벤트마다 수동으로 UI 요소를 업데이트해야 한다.
- 텍스트의 변화가 발생할 때마다 TextWatcher가 호출되어 UI의 변화를 즉각적으로 반영한다.
- DataBinding의 차이점:
- DataBinding은 데이터 모델과 UI 간의 결합을 통해 UI 요소가 데이터 모델에 자동으로 바인딩되고 업데이트되도록 한다.
- BindingAdapter나 @Bindable과 같은 어노테이션을 통해 속성 변경을 관찰하고, 데이터와 UI를 자동으로 동기화한다.
- 예를 들어, User 객체의 inputText가 변경되면 BindingAdapter가 그 변화를 감지하고, 설정한 규칙에 따라 UI 요소를 자동으로 업데이트한다.
주요 차이점 요약
| 요소 | TextWatcher | DataBinding |
| 동작 방식 | 콜백을 통해 직접 UI 업데이트 | 데이터 모델과 UI의 자동 결합 |
| 사용 위치 | EditText와 같은 텍스트 입력 뷰 | 모든 UI 요소 및 데이터 클래스에 사용 가능 |
| UI 업데이트 | 수동으로 변경된 텍스트 상태에 맞춰 직접 적용 | 데이터 모델의 속성 변경 시 자동 적용 |
| 적용 범위 | EditText 등 텍스트 변화가 필요한 곳 | 데이터 바인딩이 필요한 전체 UI 요소 |
TextWatcher는 특정 입력 필드의 변화에 대한 세밀한 제어가 필요할 때 유용하지만, DataBinding은 데이터와 UI 간의 결합을 통해 코드의 간결함과 유지 보수성을 높여주는 장점이 있다.
결과
ViewBinding과 DataBinding은 각각의 방식으로 UI를 바인딩하고 데이터 변화를 반영할 수 있었다.

직접 DataBinding을 사용해보니 DataBinding의 경우 BindingAdapter를 사용하여 코드가 더 간결해졌고, 데이터 모델의 변화가 UI에 자동으로 반영되었다. 위의 캡쳐본을 보면 좀 더 간결해진 것을 볼 수 있다. 또한, 저렇게 구현해보니 BindingAdapter를 통해 커스텀 속성을 별도의 파일로 분리하여 관리하면 재사용성이 크게 향상될 것 같다는 생각이 들었다. 분리된 커스텀 속성 정의는 프로젝트 내 여러 XML 파일에서 동일한 방식으로 재사용 가능하기 때문에, 코드의 일관성과 유지 보수성이 높아진다는 점이 큰 장점이다.
마무리
이번 포스팅을 통해 DataBinding의 BindingAdapter 활용과 ViewBinding의 비교를 진행해 보았다. 상황에 따라 더 적합한 바인딩 방식을 선택할 수 있으며, 특히 공통 로직을 분리하여 관리하려는 경우 DataBinding과 BindingAdapter가 좋은 선택이 될 수 있을 것이다.
'Android' 카테고리의 다른 글
| c (0) | 2025.03.14 |
|---|---|
| [Android] 안드로이드 DataBinding 완벽 가이드: 개념, 도입 배경, 자동화의 원리와 ViewBinding과의 차이 (0) | 2024.11.17 |
| [Android] LiveData와 DataBinding의 양방향 바인딩 시 발생하는 오류 해결하기 (2) | 2024.11.16 |
| [Android] Unresolved reference: Transformations 에러 해결 (3) | 2024.11.16 |
| [Android] com.android.adblib.tools.EmulatorCommandException: Whitespace not allowed in path string 에러 해결 (1) | 2024.11.16 |