2025-02-22T01:33:42
This commit is contained in:
223
docs/07_코루틴.md
Normal file
223
docs/07_코루틴.md
Normal file
@@ -0,0 +1,223 @@
|
||||
# **코틀린의 코루틴과 비동기 프로그래밍**
|
||||
|
||||
코틀린은 **코루틴(Coroutines)** 을 통해 **비동기(Asynchronous) 프로그래밍**을 쉽고 직관적으로 구현할 수 있다.
|
||||
코루틴은 **스레드보다 가볍고 효율적**이며, **콜백 지옥을 피할 수 있는 강력한 기능**을 제공한다.
|
||||
|
||||
이 글에서는 **코루틴의 개념과 주요 기능**을 설명하고,
|
||||
**비동기 프로그래밍을 구현하는 예제**를 함께 살펴보겠다. 🚀
|
||||
|
||||
---
|
||||
|
||||
## **1. 비동기 프로그래밍이란?**
|
||||
비동기 프로그래밍은 **작업이 완료될 때까지 기다리지 않고 다음 코드를 실행하는 방식**이다.
|
||||
즉, **시간이 오래 걸리는 작업(예: 네트워크 요청, 파일 I/O 등)** 도 프로그램이 멈추지 않고 실행된다.
|
||||
|
||||
### **1.1. 전통적인 방식 (콜백 지옥)**
|
||||
자바에서는 비동기 작업을 콜백(callback) 방식으로 처리해야 한다.
|
||||
콜백이 중첩되면 **콜백 지옥(Callback Hell)** 이 발생할 수 있다.
|
||||
|
||||
#### **자바의 콜백 예제**
|
||||
```java
|
||||
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. 기본적인 코루틴 사용**
|
||||
```kotlin
|
||||
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` 사용 예시 (결과 값 없음)**
|
||||
```kotlin
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
fun main() = runBlocking {
|
||||
launch {
|
||||
delay(1000L)
|
||||
println("launch: 비동기 작업 완료!")
|
||||
}
|
||||
}
|
||||
```
|
||||
> - `launch {}` 는 단순히 비동기 작업을 실행하고, 결과를 반환하지 않음.
|
||||
> - `Job` 객체를 반환하지만, 보통 `join()`을 호출하지 않는 이상 결과를 기다리지 않음.
|
||||
|
||||
---
|
||||
|
||||
### **4.2. `async` 사용 예시 (결과 값 반환)**
|
||||
```kotlin
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
fun main() = runBlocking {
|
||||
val result = async {
|
||||
delay(1000L)
|
||||
"async: 비동기 작업 완료!"
|
||||
}
|
||||
println(result.await()) // 결과 값을 기다림
|
||||
}
|
||||
```
|
||||
> - `async {}` 는 **결과 값을 반환하는 비동기 작업**을 실행.
|
||||
> - `await()` 을 호출해야 실제 값을 가져올 수 있음.
|
||||
|
||||
---
|
||||
|
||||
## **5. 여러 개의 비동기 작업 실행 (`async` 활용)**
|
||||
비동기 작업을 병렬로 실행하려면 **`async {}` 를 여러 개 실행**하면 된다.
|
||||
```kotlin
|
||||
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` 키워드**를 사용해야 한다.
|
||||
```kotlin
|
||||
suspend fun fetchData(): String {
|
||||
delay(1000L) // 1초 대기
|
||||
return "데이터 가져옴"
|
||||
}
|
||||
|
||||
fun main() = runBlocking {
|
||||
val data = fetchData()
|
||||
println(data)
|
||||
}
|
||||
```
|
||||
> - `suspend` 함수는 **코루틴 내에서만 실행 가능**
|
||||
> - `delay()` 같은 비동기 함수 호출 가능
|
||||
|
||||
---
|
||||
|
||||
## **7. 예외 처리 (`try-catch` 활용)**
|
||||
코루틴에서도 **예외 처리를 `try-catch` 로 할 수 있음**.
|
||||
```kotlin
|
||||
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` 사용 가능 |
|
||||
| 실행 효율 | 스레드 사용 | 가벼운 코루틴 사용 |
|
||||
|
||||
> **코루틴을 사용하면:**
|
||||
> ✅ **비동기 코드가 더 간결하고 직관적**
|
||||
> ✅ **콜백 지옥 없이 순차적 스타일로 작성 가능**
|
||||
> ✅ **스레드보다 가볍고 효율적**
|
||||
|
||||
코틀린에서 **비동기 프로그래밍을 쉽게 구현하려면 코루틴을 적극 활용하자!** 🚀
|
||||
Reference in New Issue
Block a user