7.0 KiB
코틀린의 코루틴과 비동기 프로그래밍
코틀린은 코루틴(Coroutines) 을 통해 비동기(Asynchronous) 프로그래밍을 쉽고 직관적으로 구현할 수 있다.
코루틴은 스레드보다 가볍고 효율적이며, 콜백 지옥을 피할 수 있는 강력한 기능을 제공한다.
이 글에서는 코루틴의 개념과 주요 기능을 설명하고,
비동기 프로그래밍을 구현하는 예제를 함께 살펴보겠다. 🚀
1. 비동기 프로그래밍이란?
비동기 프로그래밍은 작업이 완료될 때까지 기다리지 않고 다음 코드를 실행하는 방식이다.
즉, 시간이 오래 걸리는 작업(예: 네트워크 요청, 파일 I/O 등) 도 프로그램이 멈추지 않고 실행된다.
1.1. 전통적인 방식 (콜백 지옥)
자바에서는 비동기 작업을 콜백(callback) 방식으로 처리해야 한다.
콜백이 중첩되면 콜백 지옥(Callback Hell) 이 발생할 수 있다.
자바의 콜백 예제
void fetchData(Callback callback) {
new Thread(() -> {
try {
Thread.sleep(2000); // 2초 대기 (네트워크 요청 가정)
callback.onSuccess("데이터를 가져왔습니다!");
} catch (InterruptedException e) {
callback.onError(e);
}
}).start();
}
interface Callback {
void onSuccess(String data);
void onError(Exception e);
}
public static void main(String[] args) {
fetchData(new Callback() {
@Override
public void onSuccess(String data) {
System.out.println(data);
}
@Override
public void onError(Exception e) {
System.out.println("에러 발생: " + e.getMessage());
}
});
}
fetchData()가 비동기적으로 데이터를 가져오지만, 콜백을 계속 중첩해서 작성해야 한다.- 코드가 복잡해지고 가독성이 나빠지는 문제가 있다.
2. 코루틴이란?
코루틴(Coroutines) 은 비동기 프로그래밍을 간결하고 가독성 높게 구현할 수 있도록 도와준다.
코루틴을 사용하면 콜백 없이도 순차적인 코드 스타일로 비동기 작업을 작성할 수 있다.
코루틴의 특징:
- 스레드보다 가벼움 (필요할 때만 실행되고, 자동으로 일시 중단됨)
- 순차적 스타일로 작성 가능 (콜백 없이 비동기 코드 작성 가능)
- 비동기 코드가 직관적이고 읽기 쉬움
3. 코틀린에서 코루틴 사용하기
3.1. 기본적인 코루틴 사용
import kotlinx.coroutines.*
fun main() = runBlocking { // 코루틴 블록 시작
launch {
delay(1000L) // 1초 대기
println("코루틴 실행!")
}
println("메인 함수 실행")
}
출력 결과:
메인 함수 실행
코루틴 실행!
runBlocking {}: 메인 함수에서 코루틴을 실행하는 블록launch {}: 새로운 코루틴을 실행delay(1000L): 1초 동안 비동기 대기 (스레드 차단 없이 실행 가능)
4. 코루틴 빌더 (launch vs async)
코루틴을 실행할 때는 launch 와 async 를 사용할 수 있다.
| 빌더 | 반환값 | 특징 |
|---|---|---|
launch |
없음 (Job 반환) |
단순히 코루틴을 실행 |
async |
Deferred<T> 반환 |
결과 값을 반환하는 비동기 작업 |
4.1. launch 사용 예시 (결과 값 없음)
import kotlinx.coroutines.*
fun main() = runBlocking {
launch {
delay(1000L)
println("launch: 비동기 작업 완료!")
}
}
launch {}는 단순히 비동기 작업을 실행하고, 결과를 반환하지 않음.Job객체를 반환하지만, 보통join()을 호출하지 않는 이상 결과를 기다리지 않음.
4.2. async 사용 예시 (결과 값 반환)
import kotlinx.coroutines.*
fun main() = runBlocking {
val result = async {
delay(1000L)
"async: 비동기 작업 완료!"
}
println(result.await()) // 결과 값을 기다림
}
async {}는 결과 값을 반환하는 비동기 작업을 실행.await()을 호출해야 실제 값을 가져올 수 있음.
5. 여러 개의 비동기 작업 실행 (async 활용)
비동기 작업을 병렬로 실행하려면 async {} 를 여러 개 실행하면 된다.
import kotlinx.coroutines.*
fun main() = runBlocking {
val time = measureTimeMillis {
val job1 = async { fetchData(1) }
val job2 = async { fetchData(2) }
println(job1.await())
println(job2.await())
}
println("총 실행 시간: $time ms")
}
suspend fun fetchData(id: Int): String {
delay(1000L) // 네트워크 요청 시뮬레이션
return "데이터 $id 가져옴"
}
출력 결과:
데이터 1 가져옴
데이터 2 가져옴
총 실행 시간: 1003 ms
async {}로 실행된 두 개의 코루틴이 병렬로 실행됨.await()를 호출하면 해당 코루틴이 완료될 때까지 기다림.- 실행 시간은 1초 내외 (동시 실행되었기 때문).
6. suspend 함수 사용하기
코루틴에서 비동기 함수를 만들려면 suspend 키워드를 사용해야 한다.
suspend fun fetchData(): String {
delay(1000L) // 1초 대기
return "데이터 가져옴"
}
fun main() = runBlocking {
val data = fetchData()
println(data)
}
suspend함수는 코루틴 내에서만 실행 가능delay()같은 비동기 함수 호출 가능
7. 예외 처리 (try-catch 활용)
코루틴에서도 예외 처리를 try-catch 로 할 수 있음.
import kotlinx.coroutines.*
fun main() = runBlocking {
try {
val result = async { errorTask() }.await()
println(result)
} catch (e: Exception) {
println("예외 발생: ${e.message}")
}
}
suspend fun errorTask(): String {
delay(500L)
throw RuntimeException("오류 발생!")
}
출력 결과:
예외 발생: 오류 발생!
try-catch를 사용하면 비동기 코드에서도 예외를 안전하게 처리 가능.
8. 정리
| 기능 | 자바 | 코틀린 |
|---|---|---|
| 비동기 실행 | 콜백 기반 | 코루틴 기반 (launch, async) |
| 가독성 | 콜백 중첩 발생 | 순차적 코드 스타일 |
| 예외 처리 | 예외 전달 어려움 | try-catch 사용 가능 |
| 실행 효율 | 스레드 사용 | 가벼운 코루틴 사용 |
코루틴을 사용하면:
✅ 비동기 코드가 더 간결하고 직관적
✅ 콜백 지옥 없이 순차적 스타일로 작성 가능
✅ 스레드보다 가볍고 효율적
코틀린에서 비동기 프로그래밍을 쉽게 구현하려면 코루틴을 적극 활용하자! 🚀