Strong 감자의 공부

안드로이드 api 통신_callback함수 본문

앱개발/StudyHub 앱 출시 회고

안드로이드 api 통신_callback함수

ugyeong 2024. 12. 27. 19:28

api 통신 시 callback 함수와 viewModelScope를 사용했다.(callback은 비동기/ 동기로 사용가능하며, callback을 호출하는 함수에 따라 callback이 비동기/동기 중 어떻게 작동할지 결정된다.)

  • 동기 콜백: 호출하는 함수가 콜백을 호출한 뒤, 콜백 작업이 완료될 때까지 호출하는 함수가 대기합니다.
  • 비동기 콜백: 호출하는 함수가 콜백을 호출한 뒤 대기하지 않고 바로 종료됩니다.

위에 글만 읽으면 이해가 안가니 예제를 가져오겠슴다. 

 

1. 동기식 callback예제

람다로 구성된 아래 코드를 간단히 설명하자면

executeCallback {} 안에 있는 코드가(= println("Inside callback") ) callback() 호출 시 실행되는 문이다.

 

executeCallback(callback: () -> Unit)를 뜯어보자면

()는 함수의 매개변수, Unit은 함수 호출 후 반환값이 없음을 말한다.

 

println("Inside callback")은 단순 프린트 문이라 매개변수도, 반환값도 없다.

=>람다를 사용하지 않고 함수 작성할때도 리턴값 없이 프린트 문만 출력 한다면 반환값을 void로 하는것과 같다.

fun executeCallback(callback: () -> Unit) {
    println("Before callback")
    callback() // 콜백 호출
    println("After callback")
}

fun main() {
    executeCallback {
        println("Inside callback") // 콜백의 본문 내용
    }
}

// 결과
Before callback
Inside callback
After callback

 

 

혹시 매개변수랑 리턴값이 있을 때를 궁금해할 사람들을 위해 chat gpt에서 예시 긁어왔슴당.

input은 callback으로 전달되는 "kotlin"입니다.

fun executeCallback(callback: (String) -> String) {
    println("Before callback")
    val result: String = callback("Kotlin") // 콜백 함수의 반환값을 받음
    println("Callback returned: $result") // 반환값 출력
}

fun main() {
    executeCallback { input ->
        println("Inside callback with input: $input") // 매개변수를(executeCallback에서 callback("kotlin")) 사용해 문자열 반환
        "Hello, $input!" // 리턴값
    }
}

// 결과 
Before callback
Inside callback with input: Kotlin
Callback returned: Hello, Kotlin!

 

 

 

2. 비동기식 callback예제

우선 알아둬야 할건 네트워크 요청은 비동기이다. 그렇기 때문에 api 호출하는 함수를 일반함수로만 작성하면 안된다!!

 

아래는 내가 작성한 코드이다.

// fragment.kt 코드

private val viewModel: ChangeNickNameViewModel by viewModels()

...

binding.btnComplete.setOnClickListener {
            viewModel.isDuplicationNickname(object : CallBackListener {
                override fun isSuccess(result: Boolean) {
                    if (result) {
                      // 성공 시
                    } else {
                       // 실패 시
                    }
                }
            })
        }
        
        
        
// viewModel.kt 코드

fun isDuplicationNickname(params: CallBackListener) {
        viewModelScope.launch {
            val response =
                RetrofitManager.api.checkNicknameDuplication(nickname = nickname.value.toString())
            if (response.isSuccessful) {
                params.isSuccess(true)
            } else {
                params.isSuccess(false)
            }
        }
    }
    
    
 // interface.kt 코드
 
 interface CallBackListener {
    fun isSuccess(result: Boolean)
}

 

🔗 코드 작동 순서

fragment에서 btnComplete라는 버튼이 눌리면 viewModel.kt에 작성된 isDuplicationNickname함수가 호출된다.

호출 시 fragment에서 구체화한 CallBackListener 구현체(isSuccess)가 호출 함수( isDuplicationNickname ) 전달된다. 다시 fragment.kt로 반환되는거 아님!

 

여기서 의문점...! 

 

✔️ 호출된  isDuplicationNickname은 네트워크 응답값을 기다렸다가 끝나나? ❌

  • 해당 일반 함수는 그냥 끝난다. = 끝나니까 네트워크 응답값이 나중에 와도 못받음!

 

✔️ 호출된  isDuplicationNickname 함수가 종료되면 해담 함수 안의  params.isSuccess(true) or (false)은 어떻게 동작하는가?

  • viewModelScope.launch로 코루틴을 시작하면, 이 코루틴은 백그라운드에서 네트워크 요청을 처리된다.
  • isDuplicationNickname 함수는 종료되더라도 코루틴 내의 네트워크 작업은 계속 진행된다.

 

✔️ 만약 viewModelScope.launch와 같은 코루틴 스코프로 감싸지 않으면?

  • 코루틴이 아닌 일반 함수로 동작하게 된다.
  • 네트워크 요청이 실행되더라도 함수 자체는 결과를 기다리지 않고 그대로 종료된다.

 

 

TⓂ️ℹ️

처음 서버와 통신하는 기능을 구현할 때 callback 개념을 몰라 넘 막막했다...

코루틴 스코프같은 비동기 컨텍스트도 몰라 api 요청 시 함수 종료로 에러가 발생했는데 그 당시 나땜에 프젝 진행 안될까봐 발동동 구르며 전전긍긍 앓았다🥹🥹

해결하기위해 구글링하는 과정에서 많은 북마크를 날렸고 그때의 급급함이 북마크에 고스란히 남아있다..(북마크 언제 정리하지😑)

 

2022-2023 심정

 

발에 불 떨어지면 어떻게든 하는 나..🫠