Dart async/await 함수: 앱이 “Loading…”이라고 표시할 때 실제로 하는 일
Source: Dev.to
“Loading…” 이 실제로 의미하는 것
오랫동안 나는 “loading”이 내 앱이 멈췄다는 뜻이라고 생각했다—깨진 건 아니고, 단지 데이터가 나타날 때까지 정중히 기다리는 상태라고. 로딩 스피너를 볼 때마다 앱이 그렇게 동작한다는 걸 받아들였다.
그런데 어느 날은 그 설명이 맞지 않게 느껴졌다. 내 Dart 앱이 이렇게 동작한다고 상상해 보았다:
- 코드를 실행한다
await에 도달한다- 모든 것이 멈춘다
- 앱이 기다린다
- 데이터가 도착한다
- 앱이 계속 진행한다
단순하고, 선형적이며, 위안이 되는 흐름.
하지만 실제 앱은 그렇지 않다. 버튼은 여전히 반응하고, 애니메이션은 움직이며, 로그는 계속 출력된다—앱은 분명 살아 있다. 그렇다면 정확히 무엇을 “기다리고” 있는 걸까?
앱에 “Loading…” 이 표시될 때, 그것은 앱이 무엇을 하고 있는지를 설명하는 것이 아니라 당신이 무엇을 기다리고 있는지를 설명한다. 뒤에서는 Dart 앱이 여전히 실행 중이며, 네트워크 요청이나 데이터베이스 호출 같은 느린 작업 때문에 스스로를 차단하지 않는다. 대신 조용히 이렇게 말한다:
“이건 나중에 다시 처리할게.”
그리고는 계속 진행한다.
Dart에서 async와 await이 실제로 작동하는 방식
이때 나는 깨달았다: await는 전체 프로그램을 멈추는 것이 아니라 하나의 함수만을 일시 중지한다는 것을. Dart 함수가 await에 도달하면 그 함수는 자리를 비킨다. 이벤트 루프는 계속 돌아가고, 다른 작업들은 진행되며, UI 업데이트도 이루어지고, 앱은 응답성을 유지한다. 당신이 기다리는 Future가 끝나면 그때 그 함수가 차분히 다시 실행된다. 전혀 멈추지 않는다.
초보자들은 보통 단계별 사고를 한다:
- 1번째 줄이 실행된다.
- 비동기 코드가 그 사고 모델을 깨뜨린다.
그 뒤로는 시작하고, 멈추고, 다시 시작하고, 겹치는 과정이 이어진다. 무작위가 아니라, 직선적인 흐름도 아니다. 비동기 코드를 선형적인 이야기로 억지로 끼워 넣으려다 멈추면 신비롭게 느껴졌다. 하지만 그것은 마법이 아니라 스케줄링이었다.
디버깅 및 코드 작성에 미치는 영향
이제 로딩 스피너를 볼 때는 다르게 해석한다. “앱이 멈췄다”는 의미가 아니다. “앱이 이 작업이 끝나기를 기다리는 동안 다른 일을 하고 있다”는 뜻이다. 이 작은 관점의 변화가 디버깅 방식, 로그 해석 방식, 그리고 Dart에서 비동기 함수를 작성하는 방식을 바꾸었다.
Dart, Flutter, 혹은 최신 프레임워크를 사용한다면 비동기는 어디에나 있다. 대부분의 실수는 문법 오류가 아니라 잘못된 사고 방식에서 온다. 앱이 항상 실행 중이고 await는 단지 정중한 일시 정지일 뿐이라는 것을 이해하면 상황이 더 차분하고, 명확하며, 덜 부서지기 쉬워진다.
결국, 내 앱은 절대 멈추지 않았다. 바쁘게 돌아가고 있었을 뿐—그저 내가 모든 세부 사항을 알 필요가 없다고 생각했을 뿐이다.