λ³Έλ¬Έ λ°”λ‘œκ°€κΈ°
πŸ’» 개발자 이야기/Flutter νƒν—˜μΌμ§€

Flutter의 Async await 그리고 Future Class

by μ •μ„ ν•œ 2023. 1. 30.
728x90
λ°˜μ‘ν˜•

μ–΄λ–€ ν”Œλž«νΌ, ν”„λ‘œκ·Έλž˜λ° μ–Έμ–΄λ₯Ό μ‚¬μš©ν•˜λ˜ κ°œλ°œμžμ—κ²Œ 비동기 ν”„λ‘œκ·Έλž˜λ°μ΄λž€ ꡉμž₯히 μ€‘μš”ν•œ μš”μ†Œμž…λ‹ˆλ‹€. 
μ•ˆλ“œλ‘œμ΄λ“œ 개발자인 μ €μ—κ²Œ μžˆμ–΄μ„œλŠ” μ½”ν‹€λ¦°μ˜ 코루틴이 ꡉμž₯히 μ€‘μš”ν•œ μš”μ†Œμ˜€κ³ , 이번 ν”ŒλŸ¬ν„°μ—μ„œμ˜ 비동기 ν”„λ‘œκ·Έλž˜λ°μ€ Future 클래슀λ₯Ό μ΄μš©ν•©λ‹ˆλ‹€.

이 κΈ€μ—μ„œ 비동기 ν”„λ‘œκ·Έλž˜λ°μ„ μ„€λͺ…ν•˜μ§€λŠ” μ•Šμ„ ν…Œμ§€λ§Œ 살짝 μ§§κ²Œλ‚˜λ§ˆ 비동기 ν”„λ‘œκ·Έλž˜λ°μ— λŒ€ν•œ λ‚΄μš©μ„ λ‹΄μ•„λ³΄μžλ©΄... 보톡 비동기 ν”„λ‘œκ·Έλž˜λ°μ„ κ΅¬ν˜„ν•˜λŠ” λͺ©μ μ€ APIλ₯Ό ν†΅ν•΄μ„œ λ°›μ•„μ˜€κ²Œ λ˜λŠ” λ³€ν™”ν•˜λŠ” μ–΄λ– ν•œ 값듀을 ν†΅ν•΄μ„œ 화면을 ꡬ성해 μ£Όκ±°λ‚˜ μƒνƒœκ°€ λ³€ν™”λ˜κ±°λ‚˜ ν•˜λŠ” λ“±μ˜ 처리λ₯Ό μ—°μ†μ μœΌλ‘œ ν•΄μ•Ό ν•  λ•Œ κ΅¬ν˜„μ„ ν•©λ‹ˆλ‹€.
μ™œλƒν•˜λ©΄ μ–΄λ– ν•œ 값에 μ˜ν•΄ μ’…μ†μ μœΌλ‘œ 데이터듀을 μ²˜λ¦¬ν•΄μ•Ό ν•  텐데 κ·Έ λͺ¨λ“  κ²°κ³Όκ°€ μ „λ‹¬λ˜λŠ” κ·Έ μ‹œκ°„ λ™μ•ˆ μ „μ²΄μ˜ μ½”λ“œκ°€ 기닀렀쀄 ν•„μš”λŠ” μ—†κΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€. 이런 뢀뢄듀을 λΉ„λ™κΈ°μ μœΌλ‘œ κ΅¬ν˜„ν•˜μ—¬ μ „μ²΄μ½”λ“œκ°€ ν•˜λ‚˜μ˜ 호좜둜 인해 λ©ˆμΆ”λŠ” 것을 μ§€μ–‘ν•˜λŠ” 것이 비동기 ν”„λ‘œκ·Έλž˜λ°μ˜ λͺ©μ μ΄λΌκ³  ν•  수 μžˆκ² μŠ΅λ‹ˆλ‹€.

비동기 ν”„λ‘œκ·Έλž˜λ°μ€ μ–΄λ €μš΄ κ°œλ…μ€ μ•„λ‹™λ‹ˆλ‹€. κ²°κ³Όλ₯Ό 기닀리기보단 κ·Έ μž‘μ—…μ€ κ·Έ μž‘μ—…λŒ€λ‘œ 처리되고 κ²°κ³Όλ₯Ό λ„μΆœν•˜λ„λ‘ 두고 λ‹€λ₯Έ μ½”λ“œλ“€μ΄ μ •μƒμ μœΌλ‘œ 처리될 수 μžˆλ„λ‘ ν•΄μ£ΌλŠ” 것이 비동기 ν”„λ‘œκ·Έλž˜λ°μž…λ‹ˆλ‹€.


그럼 Flutter의 Futureλž€ 무엇인지 μ‚΄νŽ΄λ³ΌκΉŒμš”.
μžμ„Έν•œ 글은 Dart의 λ¬Έμ„œλ₯Ό μ°Έμ‘°ν•˜λŠ” 것이 κ°€μž₯ 쒋은 방법일 것 κ°™μŠ΅λ‹ˆλ‹€. 이 λ¬Έμ„œμ—λŠ” Future의 κ°œλ…κ³Ό ꡬ쑰 그리고 κ΅¬ν˜„ 방법에 λŒ€ν•œ λ‹€μ–‘ν•œ μ˜ˆμ‹œλ₯Ό λ“€μ–΄μ„œ μ„€λͺ…ν•΄μ£Όκ³  μžˆλŠ”λ°μš”. μ΄κΈ€μ—μ„œλŠ” async ~await ν‚€μ›Œλ“œλ₯Ό μ΄μš©ν•œ Future μ‚¬μš©λ²•μ— κ΄€ν•΄μ„œλ§Œ μž‘μ„±ν•  μ˜ˆμ •μ΄λΌ λ‹€λ₯Έ κ΅¬ν˜„ 방법을 μ°Έμ‘°ν•˜μ‹ λ‹€λ©΄ κ³΅μ‹λ¬Έμ„œλ₯Ό μ°Έμ‘°ν•˜μ—¬ κ΅¬ν˜„ν•˜μ‹œκΈ° λ°”λžλ‹ˆλ‹€.

Futureλ₯Ό μ΄μš©ν•˜μ—¬ 비동기 ν”„λ‘œκ·Έλž˜λ°μ„ μœ„ν•œ ν•¨μˆ˜λ₯Ό κ΅¬ν˜„ν•˜κ³  κ΄€λ ¨ 클래슀의 μ°Έμ‘° λ‚΄μš©μ„ μΊ‘μ²˜ν•΄ λ³΄μ•˜μŠ΅λ‹ˆλ‹€.
κ·Έ λ‚΄μš©μ„ μš”μ•½ν•˜λ©΄ Future은 비동기 μ—°μ‚°μ˜ κ²°κ³Όλ₯Ό λ§ν•©λ‹ˆλ‹€.

값을 λ°˜ν™˜ 및 μ „λ‹¬ν•¨μœΌλ‘œμ¨ 얻은 결과에 λŒ€ν•΄μ„œ 비동기식 연산은 κ·Έ κ²°κ³Όλ₯Ό λ°”λ‘œ μ œκ³΅ν•  수 없기에 비동기식 연산은 ν”„λ‘œκ·Έλž¨ μ™ΈλΆ€μ˜ κ²°κ³Ό(파일의 읽기, DB쿼리, μ›Ή νŽ˜μ΄μ§€μ˜ λ‘œλ”©)λ₯Ό κΈ°λ‹€λ €μ•Ό ν•©λ‹ˆλ‹€. Future은 κ²°κ³Όκ°€ λ‚˜μ˜¬ λ•ŒκΉŒμ§€μ˜ λͺ¨λ“  연산을 μ°¨λ‹¨ν•˜λŠ” λŒ€μ‹ μ— 비동기식 연산에 λŒ€ν•œ 결과와 ν•¨κ»˜ μ΅œμ’…μ μΈ κ²°κ³Όλ₯Ό μ¦‰μ‹œ λ°˜ν™˜ν•˜λ„λ‘ ν•œλ‹€.λΌλŠ” λ‚΄μš©μž…λ‹ˆλ‹€.

abstract class Future<T> { }

 

κ·Έλž˜μ„œ Futureλ₯Ό μ„ μ–Έν•  λ•Œμ—λŠ” return에 λŒ€ν•œ <Type>을 μ§€μ •ν•΄μ£Όμ–΄μ•Ό ν•©λ‹ˆλ‹€. κ·Έλž˜μ•Ό 결과에 λŒ€ν•œ λ°˜ν™˜κ°’μ„ 미리 μ •μ˜ν•˜κ³  κ·Έ λͺ¨λΈμ— λ§žλŠ” 데이터에 λŒ€ν•œ 처리λ₯Ό μ§„ν–‰ν•  수 있겠죠. κ°œλ…μ€ κ°„λ‹¨ν•©λ‹ˆλ‹€.

async와 await ν‚€μ›Œλ“œλ₯Ό μ‚¬μš©ν•˜λ©΄ μ•„λž˜μ™€ 같이 ꡬ성을 ν•΄λ³Ό μˆ˜κ°€ μžˆμŠ΅λ‹ˆλ‹€. μ΄λ•Œ ν•΄λ‹Ή ν‚€μ›Œλ“œλ“€μ„ Dartμ—μ„œ μ‚¬μš©ν•˜κΈ° μœ„ν•΄ 2κ°€μ§€μ˜ 원칙이 μ‘΄μž¬ν•©λ‹ˆλ‹€.

  1. await ν‚€μ›Œλ“œλ₯Ό ν•¨μˆ˜μ—μ„œ μ‚¬μš©ν•˜λ €λ©΄ ν•¨μˆ˜λŠ” async ν•¨μˆ˜μ—¬μ•Όλ§Œ ν•œλ‹€.
  2. async ν•¨μˆ˜λŠ” Futureλ₯Ό λ°˜ν™˜ν•΄μ•Ό ν•œλ‹€.
static Future<List<WebtoonModel>> getTodaysToons() async {
	List<WebtoonModel> webtoonInstances = [];

	final url = Uri.parse('$baseUrl/$today');
	final response = await http.get(url);

	if (response.statusCode == 200) {
		final List<dynamic> webtoons = jsonDecode(response.body);
		for (var webtoon in webtoons) {
			webtoonInstances.add(WebtoonModel.fromJson(webtoon));
  		}

		return webtoonInstances;
	}
	throw Error();
}

getTodaysToons() ν•¨μˆ˜λ₯Ό μ •μ˜ν•˜μ—¬ λ°˜ν™˜ κ°’μœΌλ‘œ Future<List<WebtoonModel>> 을 μ„ μ–Έν•˜μ˜€μŠ΅λ‹ˆλ‹€.
ν•˜μ§€λ§Œ ν•¨μˆ˜μ˜ λ‚΄λΆ€ return값을 μ‚΄νŽ΄λ³΄λ©΄ List<WebtoonModel> 이죠. ν˜Ήμ€ throw에 κ±Έλ¦¬λŠ” Errorκ°€ 이 ν•¨μˆ˜μ˜ λ°˜ν™˜κ°’μ΄ λ©λ‹ˆλ‹€.

async와 awaitν‚€μ›Œλ“œλŠ” μ΅μˆ™ν•œ ν‚€μ›Œλ“œμ΄κΈ°μ— 크게 μ„€λͺ…을 ν•˜μ§€λŠ” μ•Šκ² μ§€λ§Œ,
awaitν‚€μ›Œλ“œλ₯Ό λ§Œλ‚˜λ©΄ ν•΄λ‹Ή μ½”λ“œλŠ” 진행상황을 멈μΆ₯λ‹ˆλ‹€. λ˜ν•œ κ·Έ μ‹œμ μ— ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•œ 곳에 ν•΄λ‹Ή ν•¨μˆ˜μ˜ return인 Future을 μ „λ‹¬ν•©λ‹ˆλ‹€. 
await에 λŒ€ν•œ μ½”λ“œκ°€ μ™„λ£Œλ  λ•ŒκΉŒμ§€ ν•΄λ‹Ή ν•¨μˆ˜λŠ” μ •μ§€μƒνƒœλ‘œ λŒ€κΈ°ν•©λ‹ˆλ‹€. await의 λŒ€κΈ°κ°€ μ’…λ£Œλ˜κ³  μ½”λ“œ μ§„ν–‰ μˆœμ„œμ— 따라 return값은 Futureλ₯Ό λ°˜ν™˜ν•˜λ„λ‘ ν•˜λ©° List<WebtoonModel>이 Futureλ₯Ό 톡해 λ°˜ν™˜λ©λ‹ˆλ‹€. (μ΄λ•Œ ν•¨μˆ˜μ˜ λͺ¨λ“  return 값은 Futureμž…λ‹ˆλ‹€.)

μ΄λŸ¬ν•œ ν•¨μˆ˜ 진행에 λŒ€ν•œ 결과둜 화면을 κ·Έλ €μ•Ό ν•˜λŠ”λ°, μ΄λ•ŒλŠ” Futureλ₯Ό μœ„μ ―μ— λ Œλ”λ§ ν•  수 μžˆλ„λ‘ ν•˜λŠ” FutureBuilder μœ„μ ―μ„ μ‚¬μš©ν•˜μ—¬ 화면에 κ·Έλ¦½λ‹ˆλ‹€.

Future<List<WebtoonModel>> webtoons = ApiService.getTodaysToons();
body: FutureBuilder(
    future: webtoons,
    builder: (context, snapshot) {
      if (snapshot.hasData) {
        return Column(
          children: [
            const SizedBox(
              height: 50,
            ),
            Expanded(
              child: makeList(snapshot),
            )
          ],
        );
      }
      return const Center(
        child: CircularProgressIndicator(),
      );
    },
  ),
ListView makeList(AsyncSnapshot<List<WebtoonModel>> snapshot) {
    return ListView.separated(
        scrollDirection: Axis.horizontal,
        itemCount: snapshot.data!.length,
        
        padding: const EdgeInsets.symmetric(
            vertical: 10,
            horizontal: 10,
        ),
        itemBuilder: (context, index) {
            var webtoon = snapshot.data![index];

            return Webtoon(
                title: webtoon.title,
                thumb: webtoon.thumb,
                id: webtoon.id,
            );
        },
        separatorBuilder: (context, index) => const SizedBox(
            width: 40,
		),
    );
}

μ €λŠ” async와 awaitν‚€μ›Œλ“œλ₯Ό 톡해 κ΅¬ν˜„μ„ ν•˜μ˜€λŠ”λ°, then에 λŒ€ν•˜μ—¬λ„ μ•Œκ³ λŠ” μžˆμ–΄μ•Ό μ–Έμ  κ°€ μ“Έ 수 μžˆκΈ°μ— ν•΄λ‹Ή λ‚΄μš©μ„ μ²¨λΆ€ν•©λ‹ˆλ‹€.

λ¨Όμ € μœ„μ˜ κ΅¬ν˜„λ‚΄μš©μ²˜λŸΌ await ν‚€μ›Œλ“œλŠ” μˆ˜ν–‰ 쀑인 ν•¨μˆ˜λ₯Ό 쀑간에 ν‚€μ›Œλ“œκ°€ 뢙은 λ™μž‘μ΄ μ™„λ£Œλ  λ•ŒκΉŒμ§€ 진행을 λ©ˆμΆ”λŠ” ν‚€μ›Œλ“œμž…λ‹ˆλ‹€. λ”°λΌμ„œ ν•΄λ‹Ή ν‚€μ›Œλ“œλ‘œ 인해 λ™μž‘μ΄ λ©ˆμΆ”μ–΄μ§€λ©΄ λ™μž‘μ΄ μ™„λ£Œλ  λ•ŒκΉŒμ§€ μ½”λ“œλŠ” μ‹€ν–‰λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

ν•˜μ§€λ§Œ then의 κ²½μš°λŠ” μˆ˜ν–‰μ€‘μΈ ν•¨μˆ˜λ₯Ό 쀑간에 λ©ˆμΆ”λ„λ‘ ν•˜μ§€ μ•Šκ³  μ½”λ“œλŠ” 계속 μ‹€ν–‰λ˜λ„λ‘ λ‘‘λ‹ˆλ‹€.

728x90
λ°˜μ‘ν˜•