Notice
Recent Posts
Recent Comments
«   2025/07   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

생존개발

Data binding 시작하기(수정 중) 본문

안드로이드

Data binding 시작하기(수정 중)

Robin Lim 2020. 5. 4. 00:43

출처 - https://www.raywenderlich.com/7711166-data-binding-in-android-getting-started#toc-anchor-001

 

Data Binding in Android: Getting Started

In this Android Data Binding tutorial, you’ll learn how to link UI components to data in your app using a declarative format.

www.raywenderlich.com

raywenerlich 라는 사이트를 번역(의역)해봤습니다. (저퀄번역...)

오역, 맞춤법, 부드러운 문장의 흐름 등을 위한 제보는 언제나 환영입니다.

학습에 사용되는 파일은 위의 링크를 통해 받아주세요.

 

안드로이드 Data binding: 시작하기

 

 

이 튜토리얼을 통해 앱에서 UI 컴포넌트와 데이터를 어떻게 연결하는지 배울 수 있습니다.

 

 

버전

Kotlin1.3, Android 10.0, Android Studio 3.5

 

코드파일과 UI를 연결하는 작업은 안드로이드 개발자에겐 가장 흔한 작업 중 하나입니다.

간단해 보이지만, 이 프로세스는 많은 런타임오류를 발생시키고 이 작업에 많은 시간을 소비하게 됩니다.

만약 이런 작업들을 다룰 수 있는 라이브러리가 있다면 정말 좋지 않을까요?

 

Data binding이 그 해결책이 될겁니다.

안드로이드는 2018 구글 I/O에서 Jetpack라이브러리 중의 하나로 이 라이브러리를 소개했습니다.

 

Data binding은 View와 데이터 소스간의 커뮤니케이션을 확실하고 간결하게 다룰 수 있도록 도와줍니다.

특히 현재 안드로이드 개발에 가장 많이 사용되는 아키텍쳐 중 하나인 MVVM 아키텍쳐에 있어 중요합니다.

 

이 튜토리얼에서 우리는 GoBuy라는 쇼핑 리스트 앱을 만들며 현재 안드로이드 프로젝트에 어떻게 Data binding 라이브러리를 적용하는지 배울겁니다.

그 과정에서 우리는 이러한 것들을 배웁니다.

 

  • 현존하는 프로젝트에 Data binding을 활성화하는 법

  • 현존하는 레이아웃 파일을 Data binding을 사용하도록 변환하는 법

  • Data binding라이브러리를 통해 생성된 코드로 Activity와 Fragment에서 작업을 수행하는 법

  • Recyclerview와 ViewHolder에서 Data binding을 사용하는 법

참고: 이 튜토리얼은 코틀린을 이용해 기본적인 안드로이드 개발을 할 줄 안다는 가정하에 진행됩니다.

만약 코틀린이 처음이라면 코틀린 소개 튜토리얼을 이용하시고 안드로이드 개발을 처음 하신다면 안드로이드 개발 시작하기를 먼저 읽고 기본을 다져주세요.

 

 

 

시작하기

1. 상단 또는 하단에 있는 Download Materials 버튼을 눌러 프로젝트를 다운받고 압축을 해제합니다.

2. 압축파일 에서 'begin' 프로젝트와 'end' 프로젝트를 추출합니다.

3. 3.5이상 버전의 안드로이드 스튜디오에서 Import project탭을 통해 'begin' 프로젝트를 엽니다.

4. Gradle sync가 끝나면 프로젝트의 구조를 확인합니다. 

 

 

상단의 스크린샷을 보면 폴더구조가 MVVM 아키텍쳐를 따른 것을 볼 수 있습니다.

Model, View, Viewmodel에 대한 파일이 있습니다.

이번 튜토리얼에서는 view 폴더에 있는 파일들과 아래 스크린샷에서 볼 수 있는 리소스 레이아웃 파일들을 이용할 겁니다.

 

앱을 빌드하고 실행해보세요.

앱의 초기버전을 볼 수 있습니다.

 

 

화면 우측 상단에 있는 Add Item 버튼을 눌러 상품을 추가해보세요.

다이얼로그에 내용을 채워 Save 버튼을 클릭하세요.

수정과 삭제가 가능한 옵션과 함께 아이템이 리스트에 나타나게 됩니다.

 

 

 

친구를 위해 샌드위치를 만든다고 가정해봅시다.

빵 하나, 잼 한 통, 치즈 두 개, 마요네즈 한 통을 사야합니다.

이 재료들을 쇼핑 리스트에 추가해보세요.

 

 

위 스크린샷과 비슷한 결과가 나왔나요?

다른 값들을 넣어보며 앱에 익숙해져보세요.

 

프로젝트에 들어가기에 앞서 Data binding에 대해 조금 더 공부해봅시다.

 

 

Data binding은 왜 사용할까?

모든 개발자들이 깔끔하고 이해하기 쉬운 코드를 원하지만 말처럼 쉬운 일은 아닙니다.

릴리즈를 기다리는 사용자들, 그에 앞서 결과물을 원하는 클라이언트들에 맞추다보면 어느새 코드는 뒤죽박죽이 되어있기 마련입니다.

 

이를 아는 안드로이드 팀은 이런 작업을 보다 쉽게 할 수 있게 개발을 표준화하기로 결정합니다.

그 결과, 안드로이드 팀은 Data binding 라이브러리를 포함한 Jetpack 라이브러리를 출시합니다.

이 라이브러리에는 몇 가지 장점이 있습니다.

 

  1. 간결한 코드: 액티비티와 프래그먼트에 짧은 코드만을 사용해서 가독성 좋은 코드를 만들 수 있도록 도와줍니다.

  2. 적은 오류: 컴파일 시점에서 바인딩상태를 확인할 수 있기 때문에 미리 대처가 가능합니다.

  3. 앱 성능 향상: onCreate에서 바인딩을 하지 않기 때문에 앱의 성능이 향상됩니다.

  4. view와 코드의 안전한 연결: 런타임에서 바인딩하지 않기 때문에 findViewById()보다 안전하게 UI 컴포넌트들을 가져옵니다.

  5. view와 action의 안전한 연결: Data binding을 사용하면 요구되는 메서드들을 implement하지 않을 경우 충돌이 발생할 가능성이 있는 onClick()에 의존하는 것보다 안전합니다.

  6. 쉬움: 코드가 짧고 에러가 적기때문에 테스트와 유지보수가 용이합니다.

이제 Data binding의 이점이 무엇인지 이해하셨을겁니다.

지금부터는 Data binding의 어떻게 동작하는지 알아봅시다.

 

 

Data binding은 어떻게 동작할까?

Data binding은 XML 레이아웃의 view들과 데이터 객체를 연결하는 것입니다.

Data binding라이브러리는 이 프로세스에 필요한 클래스를 생성합니다.

 

Data binding을 사용하는 레이아웃 XML파일은 data 요소와 view root 요소를 갖는 <layout> 태그를 루트 레이아웃으로 사용한다는 차이점이 있습니다.

여기서 view root 요소는 아직 바인딩하지 않은 레이아웃 파일의 root 입니다.(ex.ConstraintLayout, LinearLayout ....)

 

 

JetPack 라이브러리는 Data binding을 사용하면 레이아웃파일들은 각각 DataBinding 클래스를 자체 생성합니다.

이 클래스 파일명은 레이아웃 파일명의 Pascal 케이스 + 파일명 끝에 Binding이 붙어 만들어집니다.

 

예를들어 activity_main.xml라는 파일이 있다면 ActivityMainBinding이라는 바인딩 클래스를 갖게 되는 것입니다.

 

Data binding에는 대표적으로 세 가지의 활용이 있습니다.

  • 데이터 표시

  • 사용자 이벤트 처리

  • 레이아웃 변수에 대한 action 호출

이를 염두에두고 Data binding을 위한 프로젝트를 준비해봅시다.

 

 

Data binding 환경 구성

먼저, 프로젝트에 Data binding을 사용하기 위한 환경을 구성해봅시다.

build.gradle(Module: app)을 열고 TODO 주석이 달린 곳을 아래의 코드로 변경해주세요.

dataBinding {
  enabled true
}

 

이 코드는 Gradle에게 이 프로젝트에 Data binding 라이브러리를 사용하겠다고 말하는 것을 의미합니다.

 

앱을 빌드하고 실행해보세요.

처음과 같은 화면이 나오는 것을 볼 수 있습니다.

다음으로, Data binding 사용을 위해 XML파일을 변환해봅시다.

 

 

기존 XML파일을 Data binding 레이아웃으로 변환하기

다음의 간단한 3단계 절차로 기본 XML레이아웃을 Data binding레이아웃으로 변환해봅시다.

 

  1. 레이아웃 전체를 <layout> 태그로 감싸기

  2. 레이아웃 변수 추가(선택 사항)

  3. 레이아웃 표현식 추가(선택 사항)

튜토리얼 프로젝트에 있는 세 개의 XML레이아웃 파일에 위 3단계 절차를 반복해줍니다.

먼저 activity_grocery_list.xml에 적용해봅시다.

파일 최상단에 있는 TODO 주석을 아래 코드로 교체해줍시다.

<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">
 <data>
 </data>

그리고 이 태그를 파일 최하단에서 닫아줘야 합니다.

63번째 줄에서 아래 코드로 태그를 닫아줍시다.

</layout>

 

위 코드를 적용함으로써 이제 이 레이아웃은 Data binding을 사용할 수 있게 되었습니다.

만약 데이터바인딩을 사용하지 않으면 기본 레이아웃 파일이 되고 자동 바인딩파일을 생성하게 됩니다.

 

 

에러 제거

ConstraintLayout 태그에서 중복선언경고(노란색 하이라이트)를 볼 수 있을 것입니다.

xmlns:android, xmlns:app, xmlns:tools 로 시작하는 라인을 제거합니다.

참고: 안드로이드 스튜디오에는 위 과정들을 위한 단축키가 있습니다.

방법1. 루트 레이아웃 태그에 커서 포커싱 -> (Windows) Ctrl + Enter / (Mac) Option + Enter

방법2. 우클릭 -> Show context actions -> Convert to data binding layout

 

참고: 이 코드스니펫을 추가하기만 하면 코드 들여쓰기가 올바르지 않은 걸 볼 수 있습니다. 다음 방법을 통해 코드를 정렬할 수 있습니다.

방법1. 상단 메뉴바 Code메뉴 클릭 -> reformat code  클릭

방법2. (Windows) Ctrl+Alt+L / (Mac) Option + Command + L

 

위 과정을 grocery_list_item.xml 파일에 똑같이 적용시켜주세요.

 

앱을 빌드하고 실행해봅시다.

항목을 추가해보고 몇몇 아이템들을 삭제해보기도 하면 여전히 충돌없이 정상적으로 작동하는걸 볼 수 있습니다.

 

 

 

자동생성된 Binding 클래스

우리는 앞서 Data Binding 라이브러리가 바인딩 클래스를 자동으로 생성한다는 것을 배웠습니다.

이제 생성된 클래스들을 확인해 볼 수 있습니다.

아래 스크린샷을 통해 생성된 파일들이 어디에 생성되었는지 살펴봅시다.

참고: 다른 코드 인스턴스들을 포함하는 Java(generated)라는 별개의 파일이 있습니다.

이 튜토리얼에서는 건드리지 않겠지만 이렇게 파일들이 존재한다는 것만 알고 넘어갑니다.

 

 

액티비티, 프래그먼트, 관련 코드에서 Data binding 활성화하기

이제 앱과 레이아웃이 데이터 바인딩을 사용한다는 것을 알았으니 코드를 변경해야합니다.

앞서 말했듯, view 폴더에 있는 세 개의 파일에 작업을 합니다.

 

Activity에 Data binding 추가

GroceryListActivity.kt파일을 엽니다.

TODO 주석이 몇개 보일겁니다.

XML레이아웃 파일들에 데이터바인딩을 적용하는 법을 배울 때처럼 이 TODO 주석들도 바꿔봅시다.

 

첫 번째로, 53번째 줄에 있는 TODO에 따라 view 아이템들(Button, Recyclerview, Textview)을 제거합니다.

이 객체들은 activty_grocery_list의 아이템들을 참조합니다.

Data binding 라이브러리를 사용하면 이제 이런 변수들을 생성할 필요가 없습니다.

라이브러리가 다 만들어주거든요.

 

이 세 줄의 코드를 지우고 아래 코드로 바꿔줍시다.

private lateinit var binding: ActivityGroceryListBinding

이 코드는 activity_grocery_list.xml 에서 자동으로 생성된 바인딩 클래스의 인스턴스를 생성합니다.

나중에 onCreate에서 초기화합니다.

 

이 코드를 적용하면 안드로이드 스튜디오는 몇몇 의존성 라이브러리를 추가할 것을 제안합니다.

제안대로 의존성 라이브러리를 추가합니다.

에러가 좀 나겠지만 곧 수정하니 걱정마세요.

import com.raywenderlich.android.gobuy.databinding.ActivityGroceryListBinding

이 클래스를 import하면 이전 단계의 바인딩 클래스를 참조하게 됩니다.

 

뷰 바인딩하기

두 번째로, onCreate에 위치한 setContentView(R.layout.plain_activity) 를 아래 코드로 변경해주세요.

binding = DataBindingUtil.setContentView(this, R.layout.activity_grocery_list)

 

 

이 코드는 DataBindingUtil이라는 데이터바인딩 라이브러리클래스를 사용해 content view를 설정합니다.

변경하기 전의 코드와 매우 유사해 보이지만 현재는 데이터 바인딩을 위한 코드입니다.

 

세 번째로, findViewById코드를 제거하세요.

 

이제 앞선 변경 이 후로 에러표시가 되어있는 onCreate의 세 줄의 findViewById 라인을 제거할 수 있습니다.

뷰를 사용할 때 view의 아이디가 아닌 레이아웃 변수를 사용하기 때문에 이 라인들을 제거할 수 있는 것입니다.

 

Data binding을 사용하기 전에는 레이아웃과 코드를 연결하는데 있어서 findViewById가 가장 보편적인 방법이었습니다.

하지만 findViewById를 이용하는 방법은 런타임에서 많은 문제가 발생하기도 하고 가끔은 출시 이후에도 에러가 발견되지 않기도 합니다.

 

자식뷰에 접근하기

onCreate에 있는 상단 스크린샷에 해당하는 라인들을 지우고 아래 코드로 대체합니다. 

// 1
binding.rvGroceryList.layoutManager = LinearLayoutManager(this)

// 2
binding.rvGroceryList.adapter = GroceryAdapter(viewModel.groceryListItems, this, ::editGroceryItem, ::deleteGroceryItem)

// 3
binding.addItemButton.setOnClickListener {
  addGroceryItem()
}

위 과정을 단계별로 분류 하면 다음과 같습니다.

 

  1. 리사이클러뷰를 레이아웃 파일에 상응하는 LayoutManager에 바인딩합니다.

    리사이클러뷰는 별개의 주제이긴 하지만 조금 더 깊이 들어가자면 LinearLayoutManager는 grocery item 리스트를 표시하는 뷰의 아이템입니다.

  2. 리사이클러뷰는 아이템을 가져와 레이아웃에 배치하는 어댑터를 갖습니다.

  3. addItemButton()을 클릭 메서드에 바인딩합니다.

TODO 주석들때문에 조금 헷갈리시나요?

아래 스크린샷을 통해 onCreate가 어떤 모습을 갖춰야하는지 확인해보세요.

뒷부분에 있는 세 가지 메서드에 여전히 에러표시가 남아있습니다. 다음 단계를 통해 수정해보도록 하겠습니다.

 

에러 제거하기

deleteGroceryItem메서드 내부의 코드 마지막 줄을 아래의 코드로 변경해주세요.

//groceriesTotal.text = viewModel.getTotal().toString()
binding.rvGroceryList.adapter?.notifyDataSetChanged()

상단의 코드는 아이템 제거 시 grocery 리스트를 표시하는 리사이클러뷰에서 아이템을 제거했으며 데이터셋을 새로 고쳐야 함을 어댑터에 알립니다.

총 수량을 텍스트로 나타내는 부분은 튜토리얼 후반에 수정을 할겁니다. 일단은 주석처리를 해둡니다.

 

onDialogPositiveClick에도 이전 단계와 비슷

한 작업을 수행해야겠죠?

위 코드를 아래의 코드로 수정해줍니다.

binding.rvGroceryList.adapter?.notifyDataSetChanged()

 이 코드는 add버튼을 클릭 시 데이터리스트가 변경됐음을 어댑터에 알리는 것을 의미합니다.

 

마찬가지로 나중에 수정하기 전까지만 총 수량을 텍스트로 나타내는 다음 코드를 주석처리해줍니다.

//groceriesTotal.text = viewModel.getTotal().toString()

 

addItemButton을 다루는 두 가지 에러만 남았군요. 짐작 할 수 있듯이 해당 라인을 바인딩 객체를 참조하도록 변경해야합니다.

onDialogPositiveClick메서드 내부에 에러가 생기는 라인을 다음 코드로 변경해주세요.

Snackbar.make(binding.addItemButton, "Item Added Successfully",
        Snackbar.LENGTH_LONG).setAction("Action", null).show()

onDialogNegativeClick메서드 내부도 마찬가지로 다음 코드로 변경해주세요.

Snackbar.make(binding.addItemButton, "Nothing Added",
        Snackbar.LENGTH_LONG).setAction("Action", null).show()

 

addItemButton을 binding.addItemButton으로 바꾸면서 변경전과 달리 binding객체를 사용하게 되었습니다.

 

모든 에러를 다 해결했네요.

드디어 변경사항을 테스트해볼 수 있게 되었습니다.

 

앱을 빌드하고 실행해봅시다.

코드를 변경하기 전처럼 앱을 테스트해보세요. 차이점이 느껴지시나요?

 

앱은 여전히 정상적으로 작동할겁니다.

하지만 왼쪽 하단에 총 수량을 나타내는 text가 보이질 않습니다.

아까 코드를 수정할 때 주석처리를 해놨기 때문입니다.

다음 단계에서 고쳐봅시다.

 

어댑터에 Data binding 적용하기

액티비티에 했던 것처럼 바인딩클래스와 GroceryAdapter를 연결해볼겁니다.

과정은 액티비티에서의 절차와 상당히 유사합니다.

 

GroceryAdapter.kt파일을 열어서 코드 맨 아래 ViewHolder클래스를 아래 코드로 변경해줍니다.

classViewHolder(val binding: GroceryListItemBinding) : RecyclerView.ViewHolder(binding.root) {
 ​fun bind(item: GroceryItem) {
   ​binding.apply {
     ​//itemName = "${item.amount}x: ${item.itemName}"//price = item.price.toString()
   ​}
 ​}
}

코드를 변경하기 전과 후를 비교해봅시다.

ViewHolder는 원래 view를 참조하기만 했습니다.

하지만 binding을 적용하면서 이젠 item과 price에 값도 지정합니다.

두 줄은 지금은 에러가 발생하기때문에 주석처리해두었습니다.

 

여기까지 변경하고나면 아까 ViewHolder에서 view를 선언하는 부분을 지웠기 때문에 onBindViewHolder에서 더 이상 view객체들을 찾지 못할겁니다.

아래와 같이 코드를 변경해서 고쳐봅시다.

holder.bind(items[position])
holder.binding.buttonEdit.setOnClickListener { itemEditListener(position) }
holder.binding.buttonDelete.setOnClickListener { itemDeleteListener(position) }

이 코드로 리스트 아이템들을 리사이클러뷰에 바인딩하고 edit 버튼과 delete 버튼에 리스너를 적용했습니다.

 

마지막으로 하나만 더 변경하겠습니다.

onCreateViewHolder의 코드를 다음으로 변경해주세요.

val layoutInflater = LayoutInflater.from(context)
val binding: GroceryListItemBinding = DataBindingUtil.inflate(layoutInflater,
        R.layout.grocery_list_item,
        parent, false)
return ViewHolder(binding)

 

'안드로이드' 카테고리의 다른 글

[안드로이드] Toast 사용법  (0) 2020.03.17