프로그래밍 실전 가이드
프로그래밍의 핵심 개념과 실무 활용
학습 항목
이미지 로딩 중...
Dart Async 비동기 프로그래밍 완벽 가이드
Dart의 비동기 프로그래밍을 처음부터 끝까지 다룹니다. Future, async/await, Stream 등 실무에서 필수적인 비동기 처리 패턴을 실전 예제와 함께 학습합니다.
들어가며
이 글에서는 Dart Async 비동기 프로그래밍 완벽 가이드에 대해 상세히 알아보겠습니다. 총 10가지 주요 개념을 다루며, 각각의 개념에 대한 설명과 실제 코드 예제를 함께 제공합니다.
목차
- Future_기본_사용법
- async_await_패턴
- 에러_처리_try_catch
- Future_wait_병렬_처리
- Stream_기본_개념
- Stream_listen_구독
- StreamController_커스텀_스트림
- Stream_변환_map_where
- FutureOr_타입
- Completer_수동_Future_제어
1. Future_기본_사용법
개요
Future는 미래에 완료될 작업을 나타내는 객체입니다. 네트워크 요청이나 파일 읽기 같은 시간이 걸리는 작업에 사용됩니다.
코드 예제
Future<String> fetchUserData() {
return Future.delayed(
Duration(seconds: 2),
() => 'User: John Doe'
);
}
void main() {
print('데이터 요청 시작');
fetchUserData().then((data) => print(data));
print('다른 작업 계속 진행');
}
설명
Future.delayed로 2초 후 데이터를 반환하며, then()으로 완료 후 실행할 콜백을 등록합니다. 비동기이므로 다른 작업이 블로킹되지 않습니다.
2. async_await_패턴
개요
async/await는 비동기 코드를 동기 코드처럼 읽기 쉽게 작성할 수 있게 해줍니다. 콜백 지옥을 피하고 코드 가독성을 높입니다.
코드 예제
Future<void> loadUserProfile() async {
print('프로필 로딩 시작');
final user = await fetchUserData();
final orders = await fetchOrders(user);
print('완료: $user, 주문: $orders개');
}
Future<int> fetchOrders(String user) async {
await Future.delayed(Duration(seconds: 1));
return 5;
}
설명
async 함수 내에서 await 키워드로 Future가 완료될 때까지 기다립니다. 순차적으로 읽히지만 실제로는 비동기로 동작합니다.
3. 에러_처리_try_catch
개요
비동기 작업에서 발생하는 에러는 try-catch로 처리할 수 있습니다. Future의 catchError 메서드도 사용 가능합니다.
코드 예제
Future<String> fetchData() async {
try {
await Future.delayed(Duration(seconds: 1));
throw Exception('네트워크 오류');
} catch (e) {
print('에러 발생: $e');
return '기본 데이터';
}
}
void main() async {
final result = await fetchData();
print('결과: $result');
}
설명
try-catch 블록으로 비동기 함수의 예외를 처리합니다. 에러 발생 시 기본값을 반환하거나 대체 로직을 실행할 수 있습니다.
4. Future_wait_병렬_처리
개요
여러 비동기 작업을 동시에 실행하고 모두 완료될 때까지 기다립니다. 순차 실행보다 훨씬 빠릅니다.
코드 예제
Future<void> loadDashboard() async {
final results = await Future.wait([
fetchUserData(),
fetchNotifications(),
fetchStatistics(),
]);
print('모든 데이터 로드 완료');
print('User: ${results[0]}');
}
Future<String> fetchNotifications() async {
await Future.delayed(Duration(seconds: 1));
return '알림 3개';
}
Future<String> fetchStatistics() async {
await Future.delayed(Duration(seconds: 1));
return '통계 데이터';
}
설명
Future.wait()는 여러 Future를 병렬로 실행하고 모든 결과를 리스트로 반환합니다. 대시보드처럼 독립적인 데이터를 한 번에 로드할 때 유용합니다.
5. Stream_기본_개념
개요
Stream은 연속적인 데이터의 흐름을 나타냅니다. 이벤트, 실시간 데이터, 파일 읽기 등에 사용되며 여러 값을 순차적으로 받을 수 있습니다.
코드 예제
Stream<int> countStream() async* {
for (int i = 1; i <= 5; i++) {
await Future.delayed(Duration(seconds: 1));
yield i;
}
}
void main() async {
await for (final count in countStream()) {
print('카운트: $count');
}
print('스트림 완료');
}
설명
async* 함수는 Stream을 생성하며, yield로 값을 하나씩 방출합니다. await for로 스트림의 각 값을 순차적으로 받아 처리합니다.
6. Stream_listen_구독
개요
Stream을 listen()으로 구독하면 데이터가 발생할 때마다 콜백이 실행됩니다. 구독 취소도 가능합니다.
코드 예제
void main() {
final stream = Stream.periodic(
Duration(seconds: 1),
(count) => count + 1
).take(5);
final subscription = stream.listen(
(data) => print('받은 데이터: $data'),
onDone: () => print('완료'),
onError: (e) => print('에러: $e'),
);
}
설명
Stream.periodic은 주기적으로 데이터를 생성하고, listen()으로 구독합니다. onDone과 onError 콜백으로 완료와 에러를 처리합니다.
7. StreamController_커스텀_스트림
개요
StreamController로 직접 Stream을 제어할 수 있습니다. 원하는 시점에 데이터를 추가하거나 스트림을 닫을 수 있습니다.
코드 예제
import 'dart:async';
class ChatRoom {
final _controller = StreamController<String>();
Stream<String> get messages => _controller.stream;
void sendMessage(String msg) => _controller.add(msg);
void close() => _controller.close();
}
void main() {
final chat = ChatRoom();
chat.messages.listen((msg) => print('메시지: $msg'));
chat.sendMessage('안녕하세요');
chat.sendMessage('반갑습니다');
}
설명
StreamController로 커스텀 스트림을 만들고, add()로 데이터를 추가합니다. 채팅, 알림 등 실시간 이벤트 처리에 적합합니다.
8. Stream_변환_map_where
개요
Stream도 리스트처럼 map, where 등으로 변환할 수 있습니다. 데이터를 필터링하거나 가공하여 새로운 Stream을 생성합니다.
코드 예제
Stream<int> numberStream() async* {
for (int i = 1; i <= 10; i++) {
yield i;
}
}
void main() async {
final evenSquares = numberStream()
.where((n) => n % 2 == 0)
.map((n) => n * n);
await for (final value in evenSquares) {
print('짝수의 제곱: $value');
}
}
설명
where()로 짝수만 필터링하고 map()으로 제곱을 계산합니다. 원본 Stream을 변경하지 않고 새로운 Stream을 생성하는 함수형 프로그래밍 패턴입니다.
9. FutureOr_타입
개요
FutureOr<T>는 동기 값(T) 또는 비동기 값(Future<T>)을 모두 반환할 수 있는 타입입니다. 조건에 따라 즉시 또는 나중에 값을 반환할 때 유용합니다.
코드 예제
import 'dart:async';
FutureOr<String> getData(bool useCache) {
if (useCache) {
return 'cached data';
} else {
return Future.delayed(
Duration(seconds: 1),
() => 'fresh data'
);
}
}
void main() async {
print(await getData(true));
print(await getData(false));
}
설명
캐시가 있으면 즉시 반환하고, 없으면 Future를 반환합니다. await는 두 경우 모두 처리하므로 호출하는 쪽에서 타입을 신경 쓰지 않아도 됩니다.
10. Completer_수동_Future_제어
개요
Completer는 Future를 직접 완료시킬 수 있는 객체입니다. 콜백 기반 API를 Future로 변환할 때 유용합니다.
코드 예제
import 'dart:async';
Future<String> waitForUser() {
final completer = Completer<String>();
Timer(Duration(seconds: 2), () {
completer.complete('사용자 입력 완료');
});
return completer.future;
}
void main() async {
print('입력 대기 중...');
final result = await waitForUser();
print(result);
}
설명
Completer로 Future를 생성하고, complete()로 직접 완료시킵니다. 타이머나 이벤트 리스너 같은 콜백을 Future로 감쌀 때 사용합니다.
마치며
이번 글에서는 Dart Async 비동기 프로그래밍 완벽 가이드에 대해 알아보았습니다. 총 10가지 개념을 다루었으며, 각각의 사용법과 예제를 살펴보았습니다.
관련 태그
#Dart #Async #Future #Stream #await