티스토리 뷰

반응형

📌 Dart의 비동기 처리 (async, await, Future) 완벽 정리

Dart에서는 **비동기 프로그래밍(Asynchronous Programming)**을 통해 네트워크 요청, 파일 읽기, 데이터베이스 처리 등 시간이 오래 걸리는 작업을 효율적으로 실행할 수 있습니다.
Dart의 비동기 처리는 Future, async, await 키워드를 기반으로 이루어지며, 이를 이해하면 Flutter의 비동기 UI 처리 및 API 호출도 쉽게 할 수 있습니다.


🔹 1. 비동기 프로그래밍이 필요한 이유

기본적으로 Dart은 싱글 스레드(Single Thread) 언어이지만, 비동기 처리를 통해 UI의 **프리징(Freeze, 멈춤)**을 방지할 수 있습니다.
예를 들어, 네트워크 요청 중 화면이 멈춘다면 사용자 경험이 매우 나빠집니다.

💡 비동기 처리 적용 전

void main() {
  print("1. 데이터 요청 시작");
  fetchData();  // 시간이 걸리는 작업
  print("2. 다음 작업 실행");
}

void fetchData() {
  // 네트워크 요청을 시뮬레이션 (3초 대기)
  sleep(Duration(seconds: 3));
  print("3. 데이터 로드 완료");
}

출력:

1. 데이터 요청 시작
(3초 대기)
3. 데이터 로드 완료
2. 다음 작업 실행
  • fetchData()가 3초 동안 멈춰 있기 때문에 UI도 멈추게 됨
  • 이를 해결하기 위해 비동기 처리를 활용합니다.

🔹 2. Future와 비동기 함수

Future는 미래에 완료될 값을 의미합니다.
즉, 시간이 걸리는 작업(예: API 호출)을 처리할 때 사용됩니다.

🔸 1️⃣ Future 기본 문법

Future<String> fetchData() {
  return Future.delayed(Duration(seconds: 3), () {
    return "데이터 로드 완료";
  });
}

void main() {
  print("1. 데이터 요청 시작");
  fetchData().then((result) {
    print("3. $result");
  });
  print("2. 다음 작업 실행");
}

출력:

1. 데이터 요청 시작
2. 다음 작업 실행
(3초 대기)
3. 데이터 로드 완료

📌 코드 설명

  • Future.delayed(Duration(seconds: 3), () {...}) : 3초 후 데이터를 반환하는 비동기 함수
  • .then((result) {...}) : 작업이 완료되면 실행되는 콜백 함수
  • fetchData() 실행 중에도 다음 코드(print("2. 다음 작업 실행"))가 실행됨 → 비동기 실행됨

🔸 2️⃣ async & await 사용 (더 직관적인 비동기 처리)

async와 await을 사용하면 비동기 코드를 더 읽기 쉽게 만들 수 있습니다.

Future<String> fetchData() async {
  await Future.delayed(Duration(seconds: 3));
  return "데이터 로드 완료";
}

void main() async {
  print("1. 데이터 요청 시작");
  
  String result = await fetchData();
  print("3. $result");

  print("2. 다음 작업 실행");
}

출력:

1. 데이터 요청 시작
(3초 대기)
3. 데이터 로드 완료
2. 다음 작업 실행

📌 코드 설명

  • async : 비동기 함수임을 선언
  • await : 비동기 작업이 완료될 때까지 기다림 (코드를 동기적으로 실행하는 것처럼 보이게 함)
  • fetchData()의 실행이 완료된 후 다음 코드(print("2. 다음 작업 실행"))가 실행됨
  • 비동기 코드이지만 동기 코드처럼 작성 가능 → 가독성 증가!

🔹 3. 여러 개의 비동기 처리

🔸 1️⃣ Future.wait() 사용 (여러 Future 병렬 실행)

여러 개의 비동기 작업을 동시에 실행하고, 모든 작업이 완료될 때까지 기다릴 수 있습니다.

Future<String> task1() async {
  await Future.delayed(Duration(seconds: 2));
  return "작업 1 완료";
}

Future<String> task2() async {
  await Future.delayed(Duration(seconds: 3));
  return "작업 2 완료";
}

void main() async {
  print("1. 작업 시작");

  List<String> results = await Future.wait([task1(), task2()]);
  print("3. 모든 작업 완료: $results");

  print("2. 다음 작업 실행");
}

출력:

1. 작업 시작
(2초 후)
(3초 후)
3. 모든 작업 완료: [작업 1 완료, 작업 2 완료]
2. 다음 작업 실행

📌 Future.wait()의 장점

  • 여러 개의 비동기 작업을 동시에 실행 → 성능 최적화 가능
  • 모든 작업이 끝날 때까지 기다렸다가 결과를 한 번에 받을 수 있음

🔸 2️⃣ try-catch를 사용한 예외 처리

비동기 작업에서 에러가 발생할 경우 try-catch를 사용하여 예외를 처리할 수 있습니다.

Future<String> fetchData() async {
  await Future.delayed(Duration(seconds: 2));
  throw Exception("데이터 요청 실패!"); // 강제로 에러 발생
}

void main() async {
  try {
    print("1. 데이터 요청 시작");
    String result = await fetchData();
    print("3. $result");
  } catch (e) {
    print("에러 발생: $e");
  }

  print("2. 다음 작업 실행");
}

출력:

1. 데이터 요청 시작
(2초 후)
에러 발생: Exception: 데이터 요청 실패!
2. 다음 작업 실행
  • throw Exception("메시지")를 사용하여 에러 발생을 시뮬레이션
  • try-catch를 사용하여 예외 발생 시 프로그램이 멈추지 않고 적절한 대응 가능

🔹 4. Stream을 사용한 비동기 데이터 처리

Future는 한 번만 실행되고 종료되지만, Stream은 여러 개의 데이터를 순차적으로 처리할 때 사용됩니다.

Stream<int> countStream() async* {
  for (int i = 1; i <= 5; i++) {
    await Future.delayed(Duration(seconds: 1));
    yield i;
  }
}

void main() async {
  print("카운트 시작");
  await for (var count in countStream()) {
    print("카운트: $count");
  }
  print("카운트 종료");
}

출력:

카운트 시작
(1초 후) 카운트: 1
(1초 후) 카운트: 2
(1초 후) 카운트: 3
(1초 후) 카운트: 4
(1초 후) 카운트: 5
카운트 종료
  • async*을 사용하여 스트림 데이터를 생성
  • yield를 사용하여 순차적으로 데이터를 전달
  • await for을 사용하여 스트림 데이터를 하나씩 처리

🛠 정리

개념 설명

Future 비동기 작업을 나타내는 객체
async 비동기 함수를 정의하는 키워드
await 비동기 작업이 끝날 때까지 대기
Future.wait() 여러 개의 Future를 동시에 실행
try-catch 예외 발생 시 처리
Stream 여러 개의 데이터를 순차적으로 비동기 처리

🔥 Dart 비동기 처리 꿀팁

  1. async와 await을 사용하면 비동기 코드를 동기 코드처럼 읽기 쉽게 작성 가능
  2. Future.wait()을 활용하면 여러 개의 비동기 작업을 병렬로 실행 가능
  3. try-catch를 사용하여 에러 발생 시 예외 처리 필수!
  4. 스트림(Stream)을 사용하면 실시간 데이터 처리가 가능

 

반응형