비동기 프로그래밍은 현대 애플리케이션 개발에서 매우 중요한 개념입니다.
서버 요청, 파일 처리 등 시간이 오래 걸리는 작업을 비동기로 처리하면 앱의 응답성을 유지할 수 있습니다.
이번 포스팅에서는 Java와 Dart에서 비동기 프로그래밍의 차이점과 중요한 개념을 정리해 보겠습니다.
1. 비동기 프로그래밍이란?
동기(Synchronous): 코드가 순차적으로 실행되며, 한 작업이 끝날 때까지 다음 작업이 대기
- 단점: 오래 걸리는 작업이 있으면 전체 흐름이 멈춤(Blocking)
비동기(Asynchronous): 작업 완료 여부와 상관없이 다음 코드가 실행되고, 작업이 완료되면 결과를 처리.
작업이 완료되면 콜백, Future, 또는 이벤트 루프를 통해 결과를 처리.
- 장점: 효율적인 리소스 사용 및 빠른 응답.
2. Dart에서 비동기 프로그래밍
특징
- Dart는 단일 스레드 기반의 이벤트 루프를 사용하여 비동기 작업을 처리합니다.
- 비동기 작업은 주로 Future 객체로 처리되며, 필요에 따라 async/await 또는 then을 사용합니다.
Future 타입
- Future 객체: 미래에 값을 반환하거나 오류를 발생시킬 약속(Promise).
- async 함수 내부에서 await를 사용하면 Future가 완료될 때까지 실행을 멈췄다가 완료되면 다시 실행합니다.
1) async/await를 사용한 비동기 코드
- async/await를 사용한 비동기 코드
void main() async {
print('작업 1 시작...');
var data = await fetchData();
print('작업 2 진행...');
print('결과 데이터: $data');
}
Future<String> fetchData() {
return Future.delayed(
Duration(seconds: 3),
() => '3초 기다린 결과 데이터',
);
}
- await로 fetchData 함수 호출.
- 3초 후 Future가 완료되면 결과 반환.
2) 연산 처리에서 Future 활용
void main() async {
await addNumbers(10, 20);
print('메인 함수 완료');
}
Future<void> addNumbers(int num1, int num2) async {
print('addNumbers 함수 시작...');
var result = 0;
await Future.delayed(Duration(seconds: 3), () {
result = num1 + num2;
});
print('결과 값: $result');
}
- 비동기 연산 처리 후 await로 결과 확인.
- Future.delayed를 활용하여 연산 지연.
3) then을 사용한 비동기 처리
void main() {
addNumbersWithThen(10, 5).then((result) => print("결과: $result"));
print('main() 함수 종료');
}
Future<int> addNumbersWithThen(int num1, int num2) {
return Future.delayed(Duration(seconds: 3), () => num1 + num2);
}
- then을 사용하여 콜백 방식으로 결과 처리.
- main 함수는 결과가 준비되기 전에도 다른 작업 가능.
Future: 비동기 작업의 결과나 오류를 나타내는 객체.
async/await: 비동기 코드를 동기 방식처럼 작성 가능.
then: 콜백 방식으로 비동기 작업 완료 후 처리.
Future.delayed: 특정 시간 후 결과 반환.
3. Java에서 비동기 프로그래밍
특징
- Java는 멀티스레드 기반으로 비동기 작업을 처리하며, 비동기 작업은 주로 CompletableFuture 또는 ExecutorService를 사용합니다.
코드 예제
CompletableFuture를 사용한 비동기 코드
import java.util.concurrent.CompletableFuture;
public class Main {
public static void main(String[] args) {
System.out.println("작업 시작");
CompletableFuture<String> future = fetchData();
future.thenAccept(result -> System.out.println("결과: " + result));
System.out.println("다른 작업 실행 중...");
}
static CompletableFuture<String> fetchData() {
return CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000); // 2초 대기
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Java 결과";
});
}
}
4. Dart vs Java: 주요 차이점
특징 | Dart | Java |
비동기 객체 | Future, Stream | CompletableFuture, ExecutorService |
병렬 처리 | 단일 스레드 기반, 병렬 처리는 Isolate로 처리 | 멀티스레드 기반, 병렬 처리가 기본적으로 가능 |
코드 흐름 | async/await로 동기 코드처럼 작성 가능 | thenApply와 같은 체인 방식 사용 |
스레드 관리 | 개발자가 직접 스레드를 제어하지 않음 | 개발자가 스레드를 생성하거나 관리 가능 |
에러 처리 | try-catch 또는 catchError | try-catch 또는 exceptionally |
요약
Java의 주요 키워드
- Thread: 스레드를 생성하고 실행.
- Runnable: 실행 가능한 작업 정의.
- CompletableFuture: 비동기 작업 결과 처리.
- ExecutorService: 스레드 풀 관리.
Dart의 주요 키워드
- Future: 비동기 작업 결과를 나타냄.
- async/await: 비동기 작업을 동기 코드처럼 처리.
- Isolate: 병렬 작업을 위한 Dart의 독립 실행 환경.
- Java는 멀티스레드와 병렬 처리에 강점이 있으며, 서버 및 대규모 백엔드 시스템 개발에 적합합니다.
- Dart는 단일 스레드 기반으로 동작하며, UI 중심의 Flutter 앱 개발에 적합합니다.