코루틴 기초
- 가벼운 비동기 처리 방식
- 기존의 Thread, Callback 기반보다 간결하고 안전하게 비동기 작업 처리 가능
코루틴 사용 이유
- 복잡한 콜백 지옥(callback hell) 제거
- UI 스레드 방지
- 동기식처럼 읽히는 비동기 처리
- 네트워크 통신, 데이터베이스 작업 등에 많이 사용
기본 예시
fun main() = runBlocking {
launch {
delay(1000L)
println("World!")
}
println("Hello,")
}
Hello,
World!
주요 키워드
키워드 설명
suspend | 일시 중단 가능한 함수에 사용 |
launch | 새 코루틴을 실행 (결과 X) |
async | 결과를 반환하는 코루틴 (Deferred) |
runBlocking | main 함수나 테스트에서 사용 (코루틴 시작점) |
delay | 일시 중단 함수 (non-blocking sleep) |
CorountineScope
- 코루틴을 실행할 수 있는 범위가 제공
- 스코프가 사라지면 해당 스코프 내의 코루틴들도 모두 취소
CoroutineScope(Dispathers.Main).launch {
...
}
withContext
- 일시적으로 스레드를 전환
- 예시: IO 작업은 Dispatcher.IO에서 UI 작업은 Dispatchers.Main에서 작업할 경우
withContext(Dispatchers.IO) { val data = api.fetch() }
suspend
- 일시 중단이 가능한 함수에 붙이는 키워드
- 보통 코루틴 내부에서만 호출 가능
suspend fun getUserData(): String {
delay(1000)
return "희우!"
}
launch
- 새로운 코루틴을 실행
- 반환값은 없음 (Job을 리턴)Job
- 비동기 작업을 수행하고 결과는 필요 없을 때 사용
scope.launch {
delay(1000)
println("1초 후 출력")
}
async/await
- 코루틴을 시작하고 결과를 반환
- Deferred 객체를 반환
- await()을 통해 async의 결과값을 가져옴
val result = async {
getUserData()
}
println(result.await())
runBlocking
- 일반 함수에서 코루틴을 호출
- 현재 스레드를 차단
- 주의: 실제 작업보다는 테스트나 학습용으로 사용
fun main() = runBlocking {
launch {
delay(1000)
println("Hello from coroutine")
}
}
delay
- 코루틴을 일정 시간 동안 일시 중단
- Thread.sleep()과 달리 스레드를 차단하지 않음
delay(1000)
문제
서술형
1. 다음 main 함수가 "Hello," 출력 후 1초 뒤에 "Coroutine!"을 출력하도록 launch와 delay를 이용해 코드를 구현하시오.
fun main() = runBlocking {
// TODO: 코루틴을 launch로 실행하고, delay를 이용해서 "Coroutine!"을 1초 뒤에 출력
println("Hello,")
}
답:
fun main() = runBlocking {
println("Hello,")
launch{
delay(1000)
println("Coroutin!")
}
}
보충:
- runBlocking 블럭이 launch보다 먼저 종료되어 “Coroutin!”이 출력되지 않고 종료될 수 있음
- → launch뒤에 join()을 붙여서 해당 코루틴이 끝날 때까지 기다리기
- join() 사용 이유
- launch는 비동기 실행이기 때문에, main이 먼저 끝나면 코루틴이 출력 전에 사라질 수 있음
- join()을 사용하여 launch한 코루틴이 끝날 때까지 main 스레드를 블럭화
fun main() = runBlocking {
println("Hello,")
val job = launch {
delay(1000)
println("Coroutine!")
}
job.join() // 코루틴이 끝날 때까지 기다림
}
2. 다음 코드의 실행 결과를 예측하고 어떤 차이가 있는지 설명해보시오.
import kotlinx.coroutines.*
fun main() = runBlocking {
val job = launch {
delay(1000L)
println("launch 안의 코루틴")
}
val deferred = async {
delay(1000L)
"async 안의 결과"
}
println("메인 함수 대기 중")
job.join()
val result = deferred.await()
println("async 결과: $result")
}
답:
메인 함수 대기 중
launch 안의 코루틴
async 결과: async 안의 결과
우선 main 함수에서 lauch와 async를 사용 후 대기되며 대기 중 “메인 함수 대기 중”이 출력된다.
launch는 job.join()으로 결과값을 대기 한 후 출력된다.
async도 launch와 같이 1000L만큼 delay되지만 await()으로 result에 호출될때까지 기다린다.
result값을 호출되고 반환값이 함께 출력된다.
3. 아래 코드에서 Job을 이용해 코루틴을 중간에 취소하고, 결과를 확인해보세요.
import kotlinx.coroutines.*
fun main() = runBlocking {
val job = launch {
repeat(5) { i ->
println("코루틴 실행 중... $i")
delay(500L)
}
}
delay(1200L)
println("코루틴 취소!")
job.cancel()
println("메인 종료")
}
답:
코루틴 실행 중 … 0
코루틴 실행 중 … 1
코루틴 실행 중 … 2
코루틴 취소!
메인 종료
job.cancel()로 인하여 launch 내부의 repea(5)는 모두 실행되지 못하고 delay(1200L) 후 종료된다.
3. async를 사용해서 두 개의 코루틴 작업을 동시에 실행하고, 결과를 더해 출력하시오.
import kotlinx.coroutines.*
fun main() = runBlocking {
val deferred1 = async {
delay(1000L)
10
}
val deferred2 = async {
delay(1000L)
20
}
val sum = deferred1.await() + deferred2.await()
println("합계: $sum")
}
3-1. 총 실행 시간은 몇 초일까요?
답: 1000L, 비동기 실행으로 동시에 실행된다.
3-2. await()을 쓰지 않으면 어떻게 될까요?
답: 값을 반환받지 못한다. 작업을 실행되지만 출력을 할 수 없다.
실습형
1. async와 await의 동작 확인하기
다음 조건을 만족하는 코드를 직접 작성해보세요.
- runBlocking 블록 안에서,
- async를 사용해 1초 후 "Hello"를 반환하는 코루틴과
- 2초 후 "World"를 반환하는 코루틴을 각각 작성하세요.
- 두 결과를 await하여 "Hello World"를 출력해보세요.
- 총 실행 시간은 약 2초 정도가 되도록 작성하세요.
[힌트]
- delay()와 await()을 적절히 사용하면 병렬적으로 실행되게 만들 수 있어요.
import kotlin.coroutines.*
fun main() = runBlocking {
val hello = async {
delay(1000L)
"Hello"
}
val world = async {
delay(2000L)
"World"
}
val greeting = hello.await() + " " + world.await()
println(greeting)
}
2. 여러 코루틴 동시 실행과 순서 제어
아래 조건에 맞는 코틀린 코루틴 코드를 작성
- runBlocking 내에서 3개의 코루틴을 async로 실행한다.
- 각각 코루틴은 서로 다른 시간(1초, 2초, 3초)만큼 delay 후에 자신의 인덱스(1, 2, 3)를 문자열로 반환한다.
- 모든 코루틴 결과를 기다린 뒤(모두 완료될 때까지) 세 개의 결과를 순서대로 합쳐서 출력한다.
- 출력 형식은 "Results: 1 2 3" 이다.
[힌트]
- await()를 활용하고, async로 코루틴을 시작하는 즉시 실행을 시작할 수 있어.
import kotlin.coroutines.*
fun main() = runBlocking {
val first = async {
delay(1000L)
1
}
val second = async {
delay(2000L)
2
}
val third = async {
delay(3000L)
3
}
val count = "Results: " + first.await() + " " + second.await() + " " + third.await()
println(count)
}
3. 코루틴 취소 (Coroutine Cancellation) 실습
아래 조건에 맞춰 코드를 작성
- runBlocking 내에서 launch로 코루틴을 실행한다.
- 코루틴은 5번에 걸쳐 500ms씩 delay하며 "작업 중: i" 를 출력한다. (i는 0~4)
- 1200ms 뒤에 코루틴을 취소(cancel)한다.
- 코루틴이 취소되면 "코루틴이 취소되었습니다."를 출력하도록 처리한다.
답:
import kotlin.coroutine.*
fun main() = runBlocking {
val job = launch {
repeat(5) {
delay(500L)
println("작업 중: $i")
}
}
delay(1200L)
job.cancel()
job.join()
println("코루틴이 취소되었습니다.")
}
보충: cancelAndJoin()을 사용하는 방법이 있음
- 코루틴을 취소하고 코루틴이 완전히 종료될때까지 대기
- cancel() + join() → cancelAndJoin()
'개발새발 > 코틀린' 카테고리의 다른 글
[코틀린/Kotlin] 인라인 함수 (1) | 2025.09.01 |
---|---|
[코틀린/Kotlin] 위임(Delegation) (2) | 2025.08.28 |
[코틀린/Kotlin] sealed class/when (0) | 2025.08.22 |
[코틀린/Kotlin] 스코프 함수 (0) | 2025.08.21 |
[코틀린/Kotlin] null처리와 스마트캐스트 (0) | 2025.08.20 |