[Kotlin] Suspend fun: 중지함수

2 분 소요


코루틴을 통해서 우리는 비동기 처리를 진행할 수 있었다.

다음과 같이 게시글에 글을 쓰는 상황을 생각해보자!

  1. 게시글을 작성한다.
  2. 서버에 게시글 post를 요청한다.
  3. 성공 시 성공메세지를 노출시킨다.


🙄 다음과 같이 서버 요청을 처리하기 위해서 우리는 코루틴을 사용하게 된다. 물론 서버 처리를 위해 thread를 실행할 수 있지만 여기서 발생하는 오류가 있다.

ui를 변경시키는 작업은 오직 main thread에서만 가능하기에 별도의 thread 내에서 단계 3을 실행할 수 없다!


🙄 그렇다면 ui작업인 3번만을 main thread로 빼면 어떨까?? 이 또한 오류가 발생한다.

서버에 post 요청하는 코드와 성공메세지를 노출시키는 코드가 별도의 스레드에서 병렬적으로 실행되기 때문에 post를 진행한 후, 성공할 경우 메세지를 출력해야하는 위 상황에서는 오류가 발생한다.


따라서 우리는 다음과 같이 코루틴을 사용해서 위 과정을 처리할 수 있다.

CoroutineScope(Dispatchers.Main).launch{
  //2. post 요청
  server.requestPost(title, content)
  //3. ui 업데이트
  message.isVisible = true
}

어! 그렇다면 여기서 또 다시 의문이 들 것이다. 코루틴 속이더라도 어떻게 2단계 다음에 3단계가 진행되도록 할 수 있지??

이러한 작업을 진행할 수 있도록 하는게 바로 “suspend fun”이다!!


🙋‍♀️Suspend fun?

그렇다면 위 예시의 답을 알아보기 이전에 suspend 함수가 정확이 어떤 것인지 짚고 넘어가자!

단어를 통해서도 알 수 있듯이 중지함수를 나타낸다.

코루틴의 경우, 일시중지재개를 진행할 수 있다. 이때 일시중지를 할 때 사용되는 것이 바로 중지함수이다.

그렇다면 위 코드를 다시 한 번 가져와보자!

CoroutineScope(Dispatchers.Main).launch{
  //2. post 요청
  server.requestPost(title, content)
  //3. ui 업데이트
  message.isVisible = true
}

여기서 ui를 업데이트 하기 이전에 post 요청이 완료되어야 한다. 따라서 3단계가 시작되기 전에 일시중지를 한 후, post가 완료되면 재개를 함으로써 3단계를 진행하면 아무런 문제가 발생하지 않는다.

따라서 requesPostsuspend 함수가 되어야 한다.

suspend fun requestPost(title: String, content: String){
  ...//서버 요청 진행
}

suspend 함수는 위 코드와 같이 suspend 키워드를 통해 생성할 수 있다. 어 그렇다면 이렇게만 하면 모든게 완료된 것일까?? 서버 처리의 경우 main thread에서 진행하면 안된다!! 따라서 coroutineContext를 I/O로 바꿔줘야 한다!!


🙋‍♀️withContext()

위의 상황을 다시 한 번 정리해본다면 다음과 같다.

  1. main thread에서 requestPost가 진행된다.
  2. requestPost중지함수이므로 진행이 완료될 때까지 코루틴이 중지된다. 2-1. 서버 요청의 경우, Dispatcher.I/O에서 진행되어야 한다.
  3. 2단계가 끝난 후, 코루틴이 재개되고 ui가 업데이트된다.


여기서 2-1단계를 withContext()를 통해서 진행할 수 있다.

그 방식은 간단하다! 코드를 확인해보자!!

suspend fun requestPost(title: String, content: String){
  withContext(Dispatchers.IO){
    ...//서버 요청
  }
}

즉, withContext를 통해 전달된 coroutineContext에서 그 안에 작업들이 이루어지게 되는 것이다.


🙋‍♀️ Suspend 함수의 사용 시 주의사항!

☝️ suspend 함수는 아무 곳에서나 사용할 수 있을까?

그것은 아니다! 중지함수이니 만큼 중지가 이루어질 코루틴 안에서 사용되거나 또 다른 중지함수 내부에서 사용되어야 한다.


특정 값을 반환하는 코루틴이 실행되며 해당 코루틴이 끝날 때까지 await()(중지함수의 일종)을 진행한다고 하자!

이때 위해서 말한 것처럼 await()는 중지함수의 일종이므로 이를 호출하기 위해서는 다음과 같이 별도의 코루틴을 실행하거나 별도의 suspend 함수에서 호출을 진행해야 한다.

🙄 코루틴

fun udpateUi(){
  val job = CoroutineScope(Dispatchers.IO).async{
    ...
  }

  CoroutineScope(Dispatchers.Main).launch{ // 코루틴 내에서 suspend 함수!
    val data = job.await()
    ...
  }
}

🙄 중지함수

suspend fun udpateUi(){ // suspend 함수 내 suspend 함수!
  val job = CoroutineScope(Dispatchers.IO).async{
    ...
  }

  val data = job.await()
}


📃참고

댓글남기기