본문 바로가기

모바일(Mobile)/안드로이드(Android)

[Android] 데이터 바인딩(DataBinding)

데이터 바인딩(DataBinding)이란?

 

안드로이드 스튜디오 개발자 사이트에서 정의한 표현을 인용하자면 다음과 같이 정의할 수 있습니다.

 

프로그래매틱 방식이 아니라 선언적 형식으로 레이아웃의 UI 구성요소를 앱의 데이터 소스와 결합할 수 있는 지원 라이브러리

 

안드로이드에서 앱은 xml을 기반으로 하여 화면을 설계하고, Java나 Kotlin 소스 코드를 통해 필요한 로직을 구현하고 앱의 동작을 만들어낼 수 있습니다.

 

이와 관련하여 아래의 글을 참조하면 향후 이어지는 글에 대해 조금은 도움이 되실거 같습니다 😀

 

https://1-hee.tistory.com/86

 


따라서, 데이터 바인딩이 없더라도 자바나 코틀린 소스 코드에서 레이아웃을 참조하여 기능을 구현하고 값을 바꿀 수 있습니다. 그리고 이렇게 사용자에게 보여질 뷰에 필요로 하는 데이터를 보여주도록 구현하는 방식이 프로그래매틱 방식 인 것입니다.

 

fig 1.0. 안드로이드 앱의 프로그래매틱 방식의 데이터 처리 과정

 

물론 경우에 따라서는 이러한 프로그래매틱 방식이 가독성이 더 적합한 경우도 있겠지만, 오늘날 우리가 사용하는 대부분의 앱들은 과거와 다르게 수많은 기능을 포함하고 있으므로, 기존의 방식만으로 구현한다면 유지 보수와 가독성 측면에서는 앱이 커질수록 나빠지게 되는 것입니다.

 

따라서, 복잡하게 하나의 소스 코드에서 모든 것을 처리하는 것이 아니라,

앱의 동작에 필요한 로직이나 동작 등을 소스 코드에서 관리하고, 화면에 보여질 데이터 에 대한 기능을 xml 에 위임하여 코드를 분리하고, 가독성을 높여서 유지 보수성을 높이고자 한 것이 데이터 바인딩(DataBinding) 이라고 할 수 있겠습니다.

 

사실 데이터 바인딩을 적용하고, 필요한 데이터를 xml에 정의했다고 해서 xml 파일이 마치 자바 파일처럼 변신하는 느낌은 아닙니다.

 

굳이 역할을 구체적으로 정의 해보자면, 데이터 바인딩(DataBinding)을 위한 설계도 정도로 볼 수 있을 것 같습니다.

실제 사용자 인터페이스에 데이터를 맵핑하게 할 주체는 내부적으로 정의된 인터페이스에 의해 구현된 클래스가 담당합니다.

 

Java로 앱 개발을 했다면 xxxImpl 형태로 앱 빌드시 생성되게 되며 런타임에 이 클래스가 데이터 처리에 대한 기능을 전담하게 됩니다. 그리고 웬만하면 이들 클래스까지 수정해가며 개발할 일은 없을 것이므로 이런 클래스가 있구나 하고 존재 여부와 역할(Role)에 대해서만 잘 알아두시면 좋을 것 같습니다.

 

대부분의 개발 도구에서는 이러한 소스 코드를 살펴볼 수 있도록 기능을 제공하고 있으니 잘 활용하면 좋습니다.

 

다시 본론으로 돌아와서, 데이터 바인딩을 적용할 때에는 크게 두 가지 작업이 필요합니다.

 

① 화면의 표시를 담당할 UI에 데이터 바인딩이 적용될 데이터에 대한 정의 in XML

 

② 액티비티 또는 프래그먼트에서 뷰 생성시(=onCreate)에 데이터 바인딩 작업 처리 in Java/Kotlin

 

다음 포스트에서 다루게 될 내용이지만, 위의 ②번 또한 다른 클래스에게 기능을 위임하여 완전히 로직을 분리해낼 수 있습니다.

 

분리하는 이유는 데이터 바인딩을 하는 이유와 같고, 아래의 소스 코드에서는 액티비티 클래스에서 ②번 기능을 구현했습니다.

 


데이터 바인딩 사용해보기, How to Use DataBinding

step 0. 앱 수준의 gradle 파일에 DataBinding을 사용함을 정의

android {

    ( . . . )

    buildFeatures
    {
        dataBinding = true
        viewBinding = true
    }
}

 

본 포스팅에서는 데이터 바인딩(DataBinding)과 뷰 바인딩(ViewBinding)을 모두 적용하여 코드를 작성했으므로

buildFeatures 스코프 안쪽에 두 가지 옵션을 모두 true로 바꾸어 줍니다.

step1. 화면의 표시를 담당할 UI에 데이터 바인딩이 적용될 데이터에 대한 정의

before

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context=".MainActivity"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

      <TextView
          android:id="@+id/text_view"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:text="Hello World!"
          app:layout_constraintBottom_toBottomOf="parent"
          app:layout_constraintEnd_toEndOf="parent"
          app:layout_constraintStart_toStartOf="parent"
          app:layout_constraintTop_toTopOf="parent"
          />
</androidx.constraintlayout.widget.ConstraintLayout>

 

프로젝트를 갓 생성했다면 위와 같이 constraintlayout 으로 간단한 텍스트 뷰 하나가 구현되어 있을 것입니다.

사실 이 부분은 데이터 바인딩에 중요한 부분은 아니므로, 원한다면 다른 LinearLayout 등으로 개발자의 입맛이나 기획 의도에 맞게 바꾸어 쓰셔도 무방합니다. 😀

 

데이터 바인딩을 적용하기 위해서는 before 처럼 작성되어 있던 xml 코드를 아래와 같이 <layout/> 태그로 감싸주어야 합니다.

그리고 기존에 루트 레이아웃에 정의되어 있던 옵션들을 <layout/> 태그로 옮겨주세요.

 

그렇지 않을 경우 Duplicate class found 와 같이 매우 고통스러운(?) 오류를 마주하게 될 수도 있습니다.

 

전체 xml 코드는 아래와 같습니다.

 

after

<?xml version="1.0" encoding="utf-8"?>
<layout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context=".MainActivity">
    
    <data>
        <variable
            name="user"
            type="com.example.test.databindingtest.data.User" />
        <variable
            name="activity"
            type="com.example.test.databindingtest.MainActivity" />
    </data>

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

        <TextView
            android:id="@+id/textView1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.name}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            />

        <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Button"
            android:onClick="@{activity::onClickListener}"
            app:layout_constraintTop_toBottomOf="@+id/textView1"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            />

    </androidx.constraintlayout.widget.ConstraintLayout>

</layout>

 

step2. 메인 액티비티에서 뷰 생성시(=onCreate)에 데이터 바인딩 작업 처리

데이터 바인딩을 위해 액티비티에 별도로 변수를 선언하고, 그 값을 데이터 바인딩에 사용할 수도 있습니다.

 

하지만 이는 복잡한 코드를 분리하여 가독성을 높이고 유지 보수성을 높이고자 하는 의도와는 맞지 않으므로 이번에는 굳이 귀찮더라도 데이터 클래스(data class) 를 정의하여 사용하도록 하겠습니다.

 

데이터 바인딩(DataBinding) 에 사용할 데이터 클래스(data class)는 자바 클래스에 getter와 setter를 통해 객체의 프로퍼티를 접근할 수 있도록 설계된 클래스를 의미합니다.

 


Java classes whose sole purpose is to hold data and make it accessible via getters and setters

 

https://jaxlondon.com/blog/data-classes-in-java-introduction-to-java-records/

 

Data Classes in Java: Introduction to Java Records - JAX London 2023

In this article, Dr. Carola Lilienthal is explaning how to decompose a monolith in your own company and what might be the challenges and mistakes on the way

jaxlondon.com

 

그리고 이는 곧 객체 지향 프로그래밍의 특징 중 하나인 캡슐화(Encapsulation)와 정보 은닉을 잘 따르는 경우라고 볼 수 있겠습니다.

 


step 2-1. 데이터 바인딩을 전담해줄 데이터 클래스 생성

package com.example.test.databindingtest.data;

public class User {
    private String name;

    public User(String name){
        this.name = name;
    }

    public String getName(){
        return this.name;
    }
    public void setName(String name){
        this.name = name;
    }
}

 

위와 같이 데이터 바인딩에 사용할 데이터 클래스를 생성합니다.

 

생성할 위치는 프로젝트 내부에 편한 곳에 선언해주세요!

 

보통 데이터 클래스의 경우에는 프로젝트 네임스페이스 밑의 data 패키지로 구분하여 분류하는 것이 일반적이긴 합니다! 😀

 

 

 

step 2-2. 메인 액티비티에서 데이터 클래스를 활용하여 데이터 바인딩 작업 처리

package com.example.test.databindingtest;

(...)

public class MainActivity extends AppCompatActivity {

    private ActivityMainBinding binding;
    private User user;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
				// 뷰바인딩과 다르게 데이터 바인딩은 DataBindingUtil 클래스의 메서드로 세팅한다.
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        user = new User("james");
        binding.setUser(user);
        binding.setActivity(this); // 액티비티에 이미 구현된 메서드를 데이터 바인딩으로 참조할 것이므로 액티비티도 세팅해주어야 함.
    }

    public void onClickListener(View view){
        binding.button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Toast.makeText(getApplicationContext(), "버튼 클릭", Toast.LENGTH_SHORT).show();// 버튼 동작을 확인하는 Toast 메세지
                user.setName(user.getName()+"K"); // 이름에 K를 계속 붙이는 기능
                binding.invalidateAll(); // View에 데이터가 변경되었음을 알린다.
            }
        });
    }
}

 

메인 액티비티에서 위의 코드와 같이 뷰바인딩을 위한 binding 과 데이터 클래스(data class) 변수를 추가해주시고, 데이터 바인딩을 위해 DataBindingUtil 클래스의 메서드를 통해 컨텐츠를 세팅해줍니다.

 

그리고 데이터 바인딩(DataBinding) 에 사용할 데이터 클래스(data class) 를 생성자를 통해 선언해주시고,

binding 변수를 통해 user 변수와 activity 변수를 세팅해주세요.

 

액티비티 변수의 경우 this를 사용하여 메인 액티비티를 가르키도록 해줍니다.

 

그다음 onClickListener 라는 메서드를 하나 선언한 뒤 위의 코드와 같이 기능을 구현하면

버튼을 누를 때마다 이름에 K가 추가되는 앱이 완성되었습니다!

 


📑 참고한 사이트