업무 중 다른 팀이 개발한 프로젝트를 라이브러리로 가져와 사용할 일이 있었다. 메서드를 사용하던 와중에 처음 보는 컴파일 에러를 발견했다.
Suspend function 'suspend fun test()' can only be called from a coroutine or another suspend function.
호출한 메서드가 suspend 함수이기 때문에 코루틴이나 다른 suspend 함수 내에서만 호출 가능하다는 문구이다.
suspend 함수란?
코루틴에서 함수를 실행할 때 도중에 멈출 수 있음을 표시하기 위해 사용한다.
함수가 실행 중 멈추는 경우에 스레드를 점유하며 성능이 저하되는 현상을 방지하기 위해, suspend 함수에서는 함수가 멈춰있는 동안 해당 스레드를 다른 코루틴 프로세스가 점유할 수 있도록 한다.
https://kotlinlang.org/docs/composing-suspending-functions.html
Composing suspending functions | Kotlin
kotlinlang.org
suspend 함수를 호출하는 모든 함수도 마찬가지로 실행 도중 멈출 가능성이 있기 때문에, 연쇄적으로 모두 suspend 함수로 선언해주어야 한다.

runBlocking

suspend 함수를 사용하는 방법 중 하나는 runBlocking 스코프 내에서 호출하는 것이다.
Runs a new coroutine and blocks the current thread interruptibly until its completion
runBlocking은 코루틴을 동기적으로 실행하는 함수이다. 즉 runBlocking 블록 안에 있는 함수를 위해 코루틴 스레드를 생성하고, 현재 스레드를 해당 코루틴 스레드가 끝날 때까지 블로킹하는 역할이다.
runBlocking 스코프 내에서 suspend 함수가 실행될 때 실행 도중 잠깐 멈춘다고 가정해보자.
원래 suspend 함수를 선언하는 목적은 함수가 중간에 멈추더라도 스레드를 점유하지 않고 다른 프로세스가 사용할 수 있도록 하기 위함이다.
하지만 runBlocking을 쓰면 동기로 실행되기 때문에 다른 프로세스가 이 스레드를 사용할 수 없다.
suspend 연쇄적으로 사용 vs runBlocking
suspend로 선언된 함수를 사용할 때 두 가지 선택지가 있다.
첫 번째로는 모든 호출 함수를 연쇄적으로 suspend 선언하는 것, 두 번째로는 runBlocking을 사용해서 현재 스레드를 멈추고 코루틴 스레드를 실행하는 것.
첫 번째 방법의 장점으로는 현재 스레드를 Blocking하지 않기 때문에 처리량이 늘고 지연이 적어진다. 그러나 연쇄적인 suspend 함수 사용으로 리팩터링 범위가 커진다.
두 번째 방법의 장점은 코틀린과 같이 동기 프로그래밍을 사용하는 경우, 굳이 비동기 프로그래밍 세계로 진입하지 않고도 suspend 함수를 사용할 수 있다. 모든 호출 함수를 suspend로 변경하지 않아도 되어서 간단하다. 하지만 호출 스레드를 블로킹하기 때문에 처리량이 느려질 수 있다.
성능에 문제가 없을까?
runBlocking을 사용하여 suspend 함수 호출 도중 멈추게 된다면, 스레드를 해당 프로세스가 계속 점유하게 되니 프로덕션의 성능에 문제가 생기지 않을까?
서버는 클라이언트 요청이 들어올 때마다 스레드를 생성에서 해당 요청에 대해 할당해둔다.
동기 방식으로 스레드를 지속적으로 잡아놨을 때, suspend 함수에 의해 스레드가 점유된다면 문제가 생길 수 있다.
그래서 블로킹이 문제가 됨을 확인하기 위해서, 배포 이전에 프로덕션과 유사한 환경에서 부하 상황을 시뮬레이션하고 활성 스레드 수가 급증하는지, 혹은 대기작업 큐 길이가 증가하는지 관찰할 필요가 있다.
'Language > Kotlin' 카테고리의 다른 글
| 코틀린 알아가기 (1) (0) | 2025.04.01 |
|---|