본문 바로가기
개발 이야기/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
반응형