FrontEnd/Flutter / / 2024. 5. 25. 00:07

Future 그리고 async

서두

Android 혹은 Flutter 와 같은 모바일 애플리케이션을 개발하다 보면 항상 부딪히는 난관이 있다. 바로 '비동기'이다.

모바일 애플리케이션 개발 전 항상 절차지향(?)적인 프로그래밍으로 "A다음은 B가 오는게 맞지!" 라는 생각을 가진 내게 비동기는 정말 크나큰 벽이였다.

이런 이유를 알아보기 위해 왜 '비동기'가 탄생했는지부터 알아봐야 한다.

비동기란 무엇일까?

비동기는 말 그대로 동기가 아님을 의미한다.

둘 이상의 이벤트가 동시에 발생하지 않는, 좀 더 직관적으로는 하나의 이벤트가 진행중일 때 그 이벤트가 종료되길 기다리지 않고 다른 이벤트도 진행된다는 의미이다.

이는 모바일에서 가장 흔히 볼 수 있는데, UI를 보여주는 작업 또한 하나의 이벤트이다. 그런데 네트워크 통신 또는 DB에서 데이터를 가져오는 IO 작업을 수행할 때, 화면이 보이지 않거나 움직이지 않는다면 사용자 입장에서는 얼마나 답답하게 느껴질것인가!

이러한 문제를 해결하기 위해 비동기가 자주 사용된다.

물론  데이터가 무조건 존재해야 화면을 그려주는 경우 흔히들 동그란 웨이팅 창이 뜨며 기다리는 작업이 있는데 이는 추후에 설명하기로 하자

이번 글에서는 내가 Future를 사용함에 있어서 "그래서 async, await 언제 붙여야 하는데?" 에 대한 삽질을 조금이나마 공유해보고 싶어 작성했다.


첫 번째 예제를 보자

void main() async {

  print('시작');
  second3();
  second1();
  print('끝');
}

Future<void> second3() async {
  Future.delayed(Duration(seconds: 3), () {
    print('3초 후에 실행');
  });
}

Future<void> second1() async {
  Future.delayed(Duration(seconds: 1), () {
    print('1초+ 후에 실행');
  });
}

위 예제와 같은 경우 Future 안에 전부 await을 걸지 않고 해당 프로그램을 실행해보고자 한다.

이러한 경우 당연히 await을 사용하지 않았기 때문에 메인 쓰레드에서 '시작'과 '끝'을 출력한 후, second3와 second1을 비동기적으로 실행한다. 그리하여 결과는 아래 그림과 같이 나온다

다음 예제를 확인해 보자.

void main() async {

  print('시작');
  await second3();
  await second1();
  print('끝');
}

Future<void> second3() async {
  Future.delayed(Duration(seconds: 3), () {
    print('3초 후에 실행');
  });
}

Future<void> second1() async {
  Future.delayed(Duration(seconds: 1), () {
    print('1초 후에 실행');
  });
}

처음 내가 했던 예상은 await 을 사용했기 때문에 second3이 먼저 끝난 후, second1이 끝날 줄 알았지만 결과값은 전혀 달랐다.

예제2 실행 결과

기본적으로 Future.delayed 함수는 비동기적으로 실행된다. await 키워드는 해당 비동기 작업이 완료될 때까지 기다리도록 하지만 Future.delayed 함수가 반환하는 Future 객체는 지정된 시간이 경과한 후에 비동기적으로 완료된다.

따라서 await 키워드가 Future.delayed 함수 호출을 기다리지 않고 실행된다.

main 함수에서 second3 와 second1 함수 호출에는 await 키워드가 사용되었지만, Future.delayed 함수 호출 자체가 비동기적으로 실행되어 즉시 반환하여 다음과 같은 결과가 나왔다.

 

쓰다보니 조금 장황해졌지만, 이 글을 조금 더 풀어 쓰기 위해 다음과 같은 예시를 들 수 있다.

void main() async {

  print('시작');
  await second3();
  await second1();
  print('끝');
}

Future<void> second3() async {
  Future.delayed(Duration(seconds: 3), () {
    print('3초 후에 실행');
  });
  print('second3 실행');
}

Future<void> second1() async {
  Future.delayed(Duration(seconds: 1), () {
    print('1초 후에 실행');
  });
  print('second1 실행');
}

second3 와 second1 은 await에 걸려 순서대로 처리되며, 바로 출력이 가능한 커맨드에 대해서는 출력되었지만 결국 Future.delayed는 await을 사용하지 않아 그대로 뒤로 밀려(비동기) 처리되는 것이다.

 

이중 Future.delayed에 대해 한 가지만 await을 걸어보자!

void main() async {

  print('시작');
  await second3();
  await second1();
  print('끝');
}

Future<void> second3() async {
  Future.delayed(Duration(seconds: 3), () {
    print('3초 후에 실행');
  });
  print('second3 실행');
}

Future<void> second1() async {
  await Future.delayed(Duration(seconds: 1), () {
    print('1초 후에 실행');
  });
  print('second1 실행');
}

second1 함수 내에 Future.delayed 에 await을 사용하니 해당 내용의 메서드가 실행되었고 second3 의 Future.delayed 는 await이 걸려있지 않기 때문에 비동기 적으로 실행되었다

 

이제 반대의 경우도 생각해보자! 

함수 내에서의 await은 사용했지만 main 함수 내에서 await을 사용하지 않을 경우 어떻게 될까?

void main() async {

  print('시작');
  second3();
  await second1();
  print('끝');
}

Future<void> second3() async {
  await Future.delayed(Duration(seconds: 3), () {
    print('3초 후에 실행');
  });
  print('second3 실행');
}

Future<void> second1() async {
  await Future.delayed(Duration(seconds: 1), () {
    print('1초 후에 실행');
  });
  print('second1 실행');
}

정답은 다음과 같다. await 이 걸려있는 second1이 전부 실행된 후에, await이 걸려있지 않는 second3이 실행된다.

순차적으로 사용하고 싶다면 모두 await을 걸어주면 우리가 평소에 사용하던 방식대로 출력이 된다.


이번 글을 적으면서 다시금 비동기에 대해 어려움을 느꼈다.

하지만 이렇게 삽질을 해가는 과정을 거치면서 더욱 성장해가는게 아닐까 싶다.

'FrontEnd > Flutter' 카테고리의 다른 글

Flutter 기본 인터페이스  (0) 2024.05.19
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유