[Capstone-Start] Dart ํ์ต โ Future, async/await, Stream
๐ ์ผ์: 2026. 05. 16
๐ฌ ๋ด์ฉ: Dart์ ๋น๋๊ธฐ ๋ชจ๋ธ โ Future<T>, async/await, Stream<T>์ JavaScript Promise ๋น๊ต
์ด์ ๋ Dart์ ํ์
์์คํ
์ ์ ๋ฆฌํ๊ณ , ์ค๋์ ๋ ๋ฒ์งธ ํ์ต ์ฃผ์ ๋ก ๋น๋๊ธฐ ๋ชจ๋ธ์ ๊ณต๋ถํ์ต๋๋ค. Flutter๋ API ํธ์ถ, ํ์ผ ์
์ถ๋ ฅ, ์ ๋๋ฉ์ด์
๋ฑ ๋น๋๊ธฐ ์ฝ๋๊ฐ ๋์ฒ์ ์๊ณ , ํต์ฌ ์์ ฏ์ธ FutureBuilder/StreamBuilder๊ฐ ๋น๋๊ธฐ ๊ฒฐ๊ณผ๋ฅผ ์์ ฏ ํธ๋ฆฌ์ ์ง์ ๋ฌถ์ด์ฃผ๋ ๊ตฌ์กฐ๋ผ ์ด ๋ถ๋ถ์ ๋ชจ๋ฅด๋ฉด ํ ์ค๋ ๋ชป ์งญ๋๋ค.
๋คํํ Dart์ ๋น๋๊ธฐ ๋ชจ๋ธ์ JavaScript์ ๊ฑฐ์ ๋์ผํฉ๋๋ค. Single-threaded ์ด๋ฒคํธ ๋ฃจํ ์์์ Future์ Stream์ด ๋๊ณ , async/await ๋ฌธ๋ฒ๋ ๊ฐ์ต๋๋ค. ๊ทธ๋์ ํ์ต ์์ฒด๋ ์ด๋ ต์ง ์์๋๋ฐ, Dart๋ง์ ๋ํ
์ผ(์: unawaited, async*/yield, ์๋ฌ ์ ํ)์ ์ ๋ฆฌํด ๋ก๋๋ค.
1. Dart์ ๋์์ฑ ๋ชจ๋ธ โ Single-threaded ์ด๋ฒคํธ ๋ฃจํ
Dart๋ ๊ธฐ๋ณธ์ ์ผ๋ก single isolate ์์์ single-threaded๋ก ๋์ํฉ๋๋ค. JavaScript์ V8๊ณผ ๊ฐ์ ๋ชจ๋ธ๋ก, ๋ชจ๋ ๋น๋๊ธฐ ์์ ์ ์ด๋ฒคํธ ํ์ ๋ฑ๋ก๋์๋ค๊ฐ ์์ฐจ์ ์ผ๋ก ์ฒ๋ฆฌ๋ฉ๋๋ค.
1
2
3
4
5
6
7
8
9
10
11
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Dart Isolate โ
โ โโโโโโโโโโโ โโโโโโโโโโโโโโโโ โ
โ โ Stack โ โ Event Loop โ โ
โ โโโโโโโโโโโ โโโโโโโโโฌโโโโโโโ โ
โ โฒ โผ โ
โ โ โโโโโโโโโโโโโโโโโโโโโ โ
โ โโโโโโโ Event Queue โ โ
โ โ Microtask Queue โ โ
โ โโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
๋ณ๋ ฌ ์ฒ๋ฆฌ๊ฐ ํ์ํ๋ฉด Isolate(๋ณ๋ ํ๋ก์ธ์ค์ ๊ฐ๊น์ด ๊ฒฉ๋ฆฌ ๋จ์)๋ฅผ ๋์์ผ ํ๋๋ฐ, ์บก์คํค ๋ฒ์์์๋ ์ด๋ฏธ์ง ์ฒ๋ฆฌ๋ ๋ฌด๊ฑฐ์ด JSON ํ์ฑ ์ ๋๊ฐ ์๋๋ฉด ๊ฑฐ์ ์ ์ด๋ค๊ณ ํฉ๋๋ค. ์ผ๋จ single isolate๋ก ์ถฉ๋ถํฉ๋๋ค.
2. Future<T> โ Dart ๋ฒ์ ์ Promise
Future<T>๋ โ์ธ์ ๊ฐ T ํ์
๊ฐ์ ๋ฐํํ ๊ฒโ์ด๋ผ๋ ๋ฏธ๋์ ์ฝ์์
๋๋ค. JavaScript์ Promise<T>์ ์๋ฏธยท๋์์ด ๊ฑฐ์ ๋์ผํฉ๋๋ค.
1
2
3
4
Future<int> fetchUserId() async {
await Future.delayed(const Duration(seconds: 1));
return 42;
}
then ์ฒด์ด๋ vs async/await
Future๋ .then / .catchError / .whenComplete๋ก๋ ๋ค๋ฃฐ ์ ์์ง๋ง, ๊ฐ๋
์ฑ์ ์ํด ๊ฑฐ์ ๋ชจ๋ ์ฝ๋์์ async/await์ ์๋๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// .then ๋ฐฉ์ โ ๊ฐ๋
์ฑ ๋จ์ด์ง
fetchUserId()
.then((id) => fetchUser(id))
.then((user) => print(user.name))
.catchError((e) => print('์๋ฌ: $e'));
// async/await โ sequentialํ๊ฒ ์ฝํ
try {
final id = await fetchUserId();
final user = await fetchUser(id);
print(user.name);
} catch (e) {
print('์๋ฌ: $e');
}
๋ณ๋ ฌ ์ฒ๋ฆฌ โ Future.wait
์ฌ๋ฌ Future๋ฅผ ๋์์ ๊ธฐ๋ค๋ฆฌ๋ ค๋ฉด Future.wait์ ์๋๋ค. TypeScript์ Promise.all๊ณผ ๊ฐ์ต๋๋ค.
1
2
3
4
5
6
final results = await Future.wait([
fetchUser(1),
fetchUser(2),
fetchUser(3),
]);
// results๋ List<User>
๋ณ๋ ฌ ์คํ์ด ์๋ฏธ ์๋ ๊ฑด ๋
๋ฆฝ์ ์ธ ์์
์ผ ๋์
๋๋ค. ์๋ก ์์กดํ๋ฉด ๊ทธ๋ฅ await์ ๋ ๋ฒ ๋ถ๋ฅด๋ ๊ฒ ๋ง์ต๋๋ค.
1
2
3
4
5
6
7
8
// โ ์๋ชป โ ๋ ํธ์ถ์ด ๋
๋ฆฝ์ ์ธ๋ฐ sequential
final user = await fetchUser(1);
final orders = await fetchOrders(1);
// โ
๋ณ๋ ฌ
final results = await Future.wait([fetchUser(1), fetchOrders(1)]);
final user = results[0] as User;
final orders = results[1] as List<Order>;
unawaited โ ์๋์ ์ผ๋ก ๊ธฐ๋ค๋ฆฌ์ง ์์ ๋
๋น๋๊ธฐ ํจ์๋ฅผ ํธ์ถํ๋ฉด์ ๊ฒฐ๊ณผ๋ฅผ ๊ธฐ๋ค๋ฆฌ์ง ์์ผ๋ฉด, Dart ๋ถ์๊ธฐ๊ฐ unawaited_futures ๊ฒฝ๊ณ ๋ฅผ ๋์๋๋ค. ๋ก๊น
์ด๋ fire-and-forget ํจํด์ฒ๋ผ ์๋์ ์ผ๋ก ๋ฌด์ํ ๋๋ unawaited() ํจ์๋ก ๋ช
์ํฉ๋๋ค.
1
2
3
4
5
6
import 'dart:async';
void main() {
unawaited(logToServer('app_started')); // ๊ฒฐ๊ณผ๋ฅผ ๊ธฐ๋ค๋ฆฌ์ง ์์์ ๋ช
์
// ๋ค์ ์์
์ผ๋ก ์งํ
}
์ด ์์ wrapper ํ ์ค์ด โ๋ฒ๊ทธ๊ฐ ์๋๋คโ๋ผ๋ ์๋๋ฅผ ์ฝ๋์ ๋ฐ์์ฃผ๋ ์ญํ ์ ํฉ๋๋ค.
3. async/await์ JavaScript์ ๊ฑฐ์ ๊ฐ๋ค
๋ฌธ๋ฒ์ JavaScript์ ๋์ผํฉ๋๋ค. async๋ก ํ์๋ ํจ์๋ ์๋์ผ๋ก Future๋ฅผ ๋ฐํํ๊ณ , await์ Future๋ฅผ ํ์ด์ ๊ฐ์ ๊บผ๋
๋๋ค.
1
2
3
4
Future<User> fetchUser(int id) async {
final response = await dio.get('/users/$id');
return User.fromJson(response.data);
}
๋ค๋ง ํ ๊ฐ์ง ๋ค๋ฅธ ์ ์ด ์์ต๋๋ค. JavaScript๋ async ํจ์๊ฐ ๋ช
์์ ์ผ๋ก return์ด ์์ด๋ Promise<void>๋ฅผ ๋ฐํํ์ง๋ง, Dart๋ Future<void>๋ฅผ ๋ช
์์ ์ผ๋ก ์ ์ธํด์ผ ๋ถ์๊ธฐ๊ฐ ๋ง์กฑํฉ๋๋ค.
1
2
3
Future<void> sendLog(String event) async {
await dio.post('/logs', data: {'event': event});
}
์๋ฌ ์ ํ
async ํจ์ ์์์ throwํ๋ฉด ๊ทธ๋๋ก Future๊ฐ reject๋ฉ๋๋ค. try/catch๋ก ์ก์ ์ ์์ต๋๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
Future<User> fetchUser(int id) async {
try {
final response = await dio.get('/users/$id');
return User.fromJson(response.data);
} on DioException catch (e) {
// dio๊ฐ ๋์ง 4xx/5xx
throw NetworkError(e.message ?? 'unknown');
} catch (e) {
// ๊ทธ ์ธ ๋ชจ๋ ์์ธ
throw UnknownError(e);
}
}
on TypeName catch (e) ๊ตฌ๋ฌธ์ Java์ catch (TypeName e)์ ๊ฐ์ต๋๋ค. ํ์
๋ณ๋ก catch ๋ธ๋ก์ ๋ถ๊ธฐํ ์ ์์ด ๊ฐ๋
์ฑ์ด ์ข์ต๋๋ค.
4. Stream<T> โ ์ฌ๋ฌ ๊ฐ์ ์๊ฐ์ฐจ๋ก emit
Future๋ โํ ๋ฒ ๊ฐ์ด ์ค๊ณ ๋โ์ด์ง๋ง, Stream์ โ๊ฐ์ด ์ฌ๋ฌ ๋ฒ ์๊ฐ์ฐจ๋ก emit๋๋คโ๋ ์๋ฏธ์
๋๋ค. RxJS์ Observable์ด๋ Kotlin์ Flow์ ๋น์ทํ ๊ฐ๋
์
๋๋ค.
Stream ๊ตฌ๋
1
2
3
4
5
6
7
8
9
final clock = Stream.periodic(
const Duration(seconds: 1),
(count) => count,
);
await for (final tick in clock) {
print('tick: $tick');
if (tick >= 3) break;
}
๋๋ .listen์ ์ง์ ํธ์ถํ ์๋ ์์ต๋๋ค.
1
2
3
4
5
6
final subscription = clock.listen((tick) {
print('tick: $tick');
});
// ๋์ค์ ๊ตฌ๋
ํด์
await subscription.cancel();
async* / yield โ Stream ์์ฑ
ํจ์ ์์ฒด๊ฐ Stream์ emitํ๋๋ก ๋ง๋ค๋ ค๋ฉด async*์ yield๋ฅผ ์๋๋ค. Python์ generator์ ๊ฐ์ ๋ฉ์ปค๋์ฆ์
๋๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
Stream<int> countTo(int n) async* {
for (var i = 1; i <= n; i++) {
await Future.delayed(const Duration(milliseconds: 500));
yield i;
}
}
void main() async {
await for (final v in countTo(5)) {
print(v); // 1, 2, 3, 4, 5
}
}
Broadcast Stream
๊ธฐ๋ณธ Stream์ single-subscription์ด๋ผ ํ ๋ฒ์ ํ ๋ช
๋ง ๊ตฌ๋
ํ ์ ์์ต๋๋ค. ์ฌ๋ฌ ๊ณณ์์ ๊ฐ์ ์ด๋ฒคํธ๋ฅผ ๋ณด๋ ค๋ฉด .asBroadcastStream()์ผ๋ก broadcast๋ก ๋ฐ๊ฟ์ผ ํฉ๋๋ค. ์๋ฆผ ํจ๋, ์์น ์ถ์ ๊ฐ์ ๊ณณ์์ ์์ฃผ ์ฐ์
๋๋ค.
1
2
3
final broadcast = sourceStream.asBroadcastStream();
broadcast.listen((e) => print('subscriber A: $e'));
broadcast.listen((e) => print('subscriber B: $e'));
5. ์บก์คํค์์ ๋ฏธ๋ฆฌ ๊ทธ๋ ค๋ณธ ์ฌ์ฉ ์๋๋ฆฌ์ค
์์ง ์ฝ๋๋ฅผ ์ง์ง ์์์ง๋ง, ํ์ตํ ๋ด์ฉ์ ์บก์คํค ํ๋ฉด์ ์ด๋ป๊ฒ ๋งคํํ ์ง ๋จธ๋ฆฟ์์ผ๋ก ๊ทธ๋ ค๋ดค์ต๋๋ค.
| ํ๋ฉด / ๊ธฐ๋ฅ | ๋น๋๊ธฐ ํจํด | Future / Stream |
|---|---|---|
| ์๋จ ๋ถ์ API ํธ์ถ | ํ ๋ฒ ํธ์ถ ํ ๊ฒฐ๊ณผ | Future<DietAnalysis> |
| ๋์๋ณด๋ ๋ฐ์ดํฐ fetch | ์ฌ๋ฌ ๋๋ฉ์ธ ๋ณ๋ ฌ ํธ์ถ | Future.wait |
| ์๋ฆผ ํจ๋ ์ค์๊ฐ ๊ฐฑ์ | ์๊ฐ์ฐจ๋ก ๋์ฐฉํ๋ ์ด๋ฒคํธ | Stream<Notification> |
| ์ด๋ ํ์ด๋จธ / ์คํธ๋ฆญ ์นด์ดํฐ | 1์ด๋ง๋ค emit | Stream.periodic |
| ์์น ์ถ์ (Place ํ๋ฉด) | ์์น๊ฐ ๋ฐ๋ ๋๋ง๋ค emit | Stream<Position> |
Flutter์์๋ FutureBuilder<T>์ StreamBuilder<T>๊ฐ ์ด ๋ ํจํด์ ์์ ฏ ํธ๋ฆฌ์ ์ง์ ๋ฌถ์ด์ฃผ๋ ์์ ฏ์ด๋ผ๊ณ ํฉ๋๋ค. snapshot.connectionState๋ก ๋ก๋ฉ/์๋ฃ/์๋ฌ ์ํ๋ฅผ ๋ถ๊ธฐํ๋ ํ๋ฆ์ด ํ์ค ํจํด์
๋๋ค.
6. ํ์ต ํ ์๊ฒ ๋ ๊ฒ / ๋ด์ผ ๊ณํ
์ค๋ ์ ๋ฆฌํ๋ฉด์ ์๊ฒ ๋ ๊ฐ์ฅ ์ค์ํ ํฌ์ธํธ๋ ๋ค์์ ๋๋ค.
- Dart ๋น๋๊ธฐ๋ JavaScript์ ๋ชจ๋ธยท๋ฌธ๋ฒ์ด ๊ฑฐ์ ๋์ผํฉ๋๋ค.
Future=Promise,async/await๋ ๊ฐ์ต๋๋ค. ํ์ต ๋น์ฉ์ด ๊ฑฐ์ ์๋ค๋ ๊ฒ ํฐ ๊ฐ์ ์ ๋๋ค. Stream์ ํ ๋ฒ์ด ์๋๋ผ ์ฌ๋ฌ ๋ฒ emit๋๋ ๋น๋๊ธฐ์ ๋๋ค. RxJS Observable๊ณผ ๊ฐ์ ๊ฐ๋ ์ด๋ผ ์์ ฏ์ ์ค์๊ฐ ๋ฐ์ดํฐ๋ฅผ ๋ฌถ์ ๋ ์์ฐ์ค๋ฝ์ต๋๋ค.unawaited()๋ก fire-and-forget์ ๋ช ์ํฉ๋๋ค. ์๋ํ์ง ์์ ๋๋ฝ๋ await๋ฅผ ์ก๊ธฐ ์ํ ์์ ์ฅ์น์ ๋๋ค.
๋ด์ผ์ Dart์ ํจ์ โ ์ผ๊ธ ๊ฐ์ฒด, typedef, ํด๋ก์ ๋ฅผ ๊ณต๋ถํ ๊ณํ์
๋๋ค. Flutter์ ํต์ฌ ํจํด์ธ Builder(ListView.builder, FutureBuilder ๋ฑ)๊ฐ ๋ชจ๋ โํจ์๋ฅผ ์ธ์๋ก ๋ฐ๋ ์์ ฏโ์ด๋ผ, ํจ์ ํ์
์ ์ ๋ค๋ฃจ์ง ๋ชปํ๋ฉด ์์ ฏ ์ฝ๋๊ฐ ๊นจ๋ํ๊ฒ ์ ๋์ต๋๋ค.
๐ ์์ฝ
Dart์ ๋น๋๊ธฐ ๋ชจ๋ธ์ JavaScript์ ๊ฑฐ์ ๋์ผํ single-threaded ์ด๋ฒคํธ ๋ฃจํ ์์ Future<T> + Stream<T> ํจํด์
๋๋ค. Future๋ ํ ๋ฒ ๊ฐ, Stream์ ์ฌ๋ฌ ๋ฒ ๊ฐ์
๋๋ค. Future.wait๋ก ๋ณ๋ ฌ ์ฒ๋ฆฌ, unawaited๋ก ์๋์ ๋ฌด์๋ฅผ ํํํ๊ณ , async*/yield๋ก Stream์ ์ง์ emitํ ์ ์์ต๋๋ค. Flutter์์๋ ์ด ๋ ๋น๋๊ธฐ ํ์
์ด FutureBuilder/StreamBuilder๋ก ์์ ฏ ํธ๋ฆฌ์ ์ง์ ์ฐ๊ฒฐ๋๋ ๊ตฌ์กฐ๋ผ ๋น๋๊ธฐ ๋ชจ๋ธ ์ดํด๊ฐ ํ๋ฉด ์ฝ๋ ์์ฑ์ ๊ธฐ์ด๊ฐ ๋ฉ๋๋ค.