Android, iOS

[MVVM 정복] 6. RecyclerView 적용해보기 (feat. 코틀린 함수)

미스터머글 2020. 5. 18. 17:51
728x90
반응형

이번편에서도 코드를 보면서 학습하길 권한다.

 

conquerex/mvvm-template

MVVM 학습과 앞으로 활용을 위한 템플릿. Contribute to conquerex/mvvm-template development by creating an account on GitHub.

github.com

 

이번편까지 하면 코드는 다 입력한 것이다. 물론 MVVM을 정복한 것은 아니니 계속 긴장하고 있자. (나 스스로 한테 한 얘기)

 

먼저 MainSearchRecyclerViewAdapter를 만들자. 코드는 역시 내 Github에 있다. 블로그 포스팅 번호와 커밋 메시지의 번호가 동일하니 참고해서 학습하면 된다. 어댑터 만들면서 item_main_image, ic_image_black_24dp, item_main_image_view도 생성해야 된다는 것을 잊지말자.

 

adapter를 완성했으면 MainActivity로 가서 DI를 해준다. (앞에서 학습을 해서 그런지 신나게 적용중. 신이난다 신이나~~) 그런 다음 뷰에 세팅하고 싶은 것을 세팅하면 되는...데... 코틀린 함수 run이 보인다. 이 기회에 코틀린 함수들을 알아두자.

 

  • apply
    • inline fun T.apply(block: T.() -> Unit): T
    • 프로퍼티에 값을 할당할때 유용
  • also
    • inline fun T.also(block: (T) -> Unit): T
    • 자신을 호출한 객체를 사용하지 않거나, 값을 변경하지 않을 경우에 쓰이는 함수 (데이터를 할당하기 전에 유효성 검사)
  • run
    • inline fun <T, R> T.run(block: T.() -> R): R
    • 함수를 호출한 객체를 이어지는 블록의 수신 객체로 전달
  • let
    • inline fun <T, R> T.let(block: (T) -> R): R
    • null 검사를 하고 null 이 아닐때만 코드를 실행할때 유용합니다.
  • with
    • inline fun <T, R> with(receiver: T, block: T.() -> R): R
    • 어떤 객체의 이름을 반복하지 않고 객체에 대해 다양한 연산을 수행하려고 할 때
// 예시 : apply
// apply의 블록 에서는 오직 프로퍼티만 사용. 객체의 초기화
val peter = Person().apply {
    name = "Peter" // 코드블록으로 수신객체 전달 : 리시버는 암시적
    age = 18
    // 전달받은 수신 객체를 반환 (peter)
}

// 예시 : also
// 수신 객체를 전혀 사용 하지 않거나 수신 객체의 속성을 변경하지 않고 사용하는 경우
class Book(author: Person) {
    val author = author.also {
      requireNotNull(it.age) // 코드블록으로 수신객체 전달 : 파라미터가 명시적 (it)
      print(it.name)
      // 전달받은 수신 객체를 반환 (author)
    }
}

// 예시 : with
// Non-nullable 수신 객체 이고 결과가 필요하지 않은 경우에만 with 를 사용
val person: Person = getPerson()
with(person) { // person이라는 파라미터를 명시적으로 수신객체에 입력한다. with만.
    print(name) // 코드블록으로 수신객체 전달 : 리시버는 암시적
    print(age) // 수행 결과를 반환, return은 생략 가능
}

// 예시 : let
// 지정된 값이 null 이 아닌 경우에 코드를 실행해야 하는 경우.
// Nullable 객체를 다른 Nullable 객체로 변환하는 경우.
// 단일 지역 변수의 범위를 제한 하는 경우.
getPersonDao().let { dao -> 
    // 변수 dao의 범위는 이 블록안으로 제한
    dao.insert(person) // 코드블록으로 수신객체 전달 : 파라미터가 명시적(dao)
    // 수행 결과를 반환, return은 생략 가능
}

// 예시 : run
// 어떤 값을 계산할 필요가 있거나 여러개의 지역 변수의 범위를 제한하려면 run 을 사용
val inserted: Boolean = run {
    // person 과 personDao 의 범위를 제한 합니다.
    val person: Person = getPerson() // 코드블록으로 수신객체 전달 : 리시버는 암시적
    val personDao: PersonDao = getPersonDao()
    personDao.insert(person) // 수행 결과를 반환, return은 생략 가능
}

생각보다 알아야 할 양이 많...아 보이는 것 뿐이다. 한번 바싹 학습해놓으면 조금 헷갈릴 지라도 찾아보면서 이용하면 숙달이 될 것이다. 다시 리사이클러뷰로 돌아가보자.

 

initDataBinding에서 viewModel이 DI 처리된다. 왜냐. "by viewModel()"는 지연처리, 즉 lazy하게 동작, 또 즉 처음으로 접근할 때 동작을 하기 때문이다. 

 

viewModel.imageSearchResponseLiveData.observe(this, Observer {...}

 

viewModel.imageSearchResponseLiveData가 관찰 대상이다. 처음 등장하는 observe 메소드는 LiveData 메소드이고 관찰(혹은 구독)할 준비를 하는 것이다. (R u ready???) 그 다음에 나오는 Observer 인터페이스는 변화를 감지하여 동작하게 될 구현체를 위해 여기에 들어가게 된다. 그럼 어디서 변화를 줄까?

 

MainViewModel을 보게되면 알 수 있다. imageSearchResponseLiveData는 커스텀 접근자로 인해 _imageSearchResponseLiveData를 바라보고 있고 이것은 getImageSearch 메소드 아래에 있다. model.getData가 동작해서 ImageSearchResponse를 받고 이 response가 _imageSearchResponseLiveData.postValue에 들어가면 이 변화를 감지해서 MainActivity의 mainSearchRecyclerViewAdapter.addImageItem가 동작하게 되는 것이다. 이 흐름을 잘 따라가면 MVVM을 이해할 수 있다.

 

이 전체적인 동작을 다음편에 다루면서 마무리를 지을까한다. (드뎌 끝이다. 끄으읏)

 

참고자료
- https://medium.com/@limgyumin/%EC%BD%94%ED%8B%80%EB%A6%B0-%EC%9D%98-apply-with-let-also-run-%EC%9D%80-%EC%96%B8%EC%A0%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94%EA%B0%80-4a517292df29
- https://www.androidhuman.com/lecture/kotlin/2016/07/06/kotlin_let_apply_run_with/

 

728x90
반응형