본문 바로가기
카테고리 없음

[중급] Dart 비동기 프로그래밍/ 비동기 코드 작성 시 주의사항 및 패턴

by Maccrey Coding 2024. 9. 6.
반응형

 

 

비동기 프로그래밍은 현대 애플리케이션에서 필수적인 기술입니다. Dart에서는 Future와 Stream을 통해 비동기 작업을 쉽게 처리할 수 있지만, 비동기 코드를 작성할 때는 몇 가지 주의사항과 패턴을 염두에 두어야 합니다.

이 글에서는 비동기 코드 작성 시 주의할 점과 유용한 패턴을 설명하겠습니다.

1. 비동기 코드 작성 시 주의사항

1.1 에러 처리

비동기 코드에서는 예외가 발생할 가능성이 높습니다. 따라서 적절한 에러 처리를 통해 애플리케이션의 안정성을 유지하는 것이 중요합니다.

  • try-catch 블록 사용: 비동기 함수 내에서 예외가 발생할 수 있는 코드는 try-catch 블록으로 감싸야 합니다.
Future<void> fetchData() async {
  try {
    // 비동기 작업
    String data = await Future.delayed(Duration(seconds: 2), () => throw 'Error');
    print(data);
  } catch (e) {
    print('Error occurred: $e');
  }
}
  • onError 콜백 사용: Stream을 사용할 때 onError 콜백을 활용하여 스트림에서 발생하는 오류를 처리합니다.
Stream<int> fetchNumbers() async* {
  yield* Stream.periodic(Duration(seconds: 1), (count) {
    if (count == 5) throw 'Error occurred';
    return count;
  });
}

void main() {
  fetchNumbers().listen(
    (data) => print('Number: $data'),
    onError: (error) => print('Stream Error: $error'),
  );
}

1.2 자원 관리

비동기 작업이 끝난 후 자원(예: 파일, 네트워크 연결 등)을 적절하게 해제하는 것이 중요합니다.

  • close() 호출: StreamController와 같은 객체를 사용할 때는 작업이 끝난 후 close() 메서드를 호출하여 자원을 해제합니다.
final controller = StreamController<int>();

void start() {
  controller.stream.listen((data) {
    print('Received: $data');
  });
  // 데이터 추가
  controller.add(1);
  controller.close(); // 자원 해제
}

1.3 비동기 작업의 순서 보장

비동기 작업은 실행 순서가 보장되지 않기 때문에, 순서에 의존하는 작업을 처리할 때는 주의해야 합니다.

  • await 사용: await를 사용하여 비동기 작업이 완료될 때까지 기다리고, 작업 순서를 보장합니다.
Future<void> process() async {
  await task1();
  await task2();
}

1.4 비동기 작업의 취소

비동기 작업이 필요 없게 되거나, 특정 조건에서 취소해야 할 때는 취소 메커니즘을 구현합니다.

  • Stream의 cancel 사용: Stream을 사용할 때는 구독을 취소할 수 있는 기능을 제공하여 불필요한 데이터 수신을 방지합니다.
final subscription = stream.listen((data) {
  print('Received: $data');
});
// 조건에 따라 구독 취소
subscription.cancel();

2. 비동기 코드 패턴

2.1 Future 패턴

비동기 작업의 결과를 반환하는 패턴입니다. 여러 비동기 작업을 순차적으로 처리할 때 유용합니다.

Future<void> processTasks() async {
  await task1();
  await task2();
  await task3();
}

2.2 Stream 패턴

연속적인 데이터 흐름을 처리하는 패턴입니다. 데이터가 지속적으로 생성되는 경우에 적합합니다.

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

2.3 Future.wait 패턴

여러 비동기 작업을 병렬로 실행하고, 모든 작업이 완료될 때까지 기다리는 패턴입니다.

Future<void> processMultipleTasks() async {
  await Future.wait([
    task1(),
    task2(),
    task3(),
  ]);
}

2.4 async/await 패턴

비동기 작업을 동기적으로 작성하는 패턴입니다. 코드의 가독성을 높이는 데 유용합니다.

Future<void> fetchData() async {
  try {
    String result = await fetchDataFromServer();
    print(result);
  } catch (e) {
    print('Error: $e');
  }
}

2.5 Retry 패턴

비동기 작업이 실패할 경우 재시도하는 패턴입니다. 네트워크 요청과 같은 불안정한 작업에 유용합니다.

Future<void> fetchDataWithRetry({int retries = 3}) async {
  for (int attempt = 1; attempt <= retries; attempt++) {
    try {
      String data = await fetchDataFromServer();
      print(data);
      return;
    } catch (e) {
      if (attempt == retries) {
        print('Failed after $retries attempts: $e');
        rethrow;
      }
      await Future.delayed(Duration(seconds: 2)); // Retry delay
    }
  }
}

2.6 Debouncing 패턴

사용자의 입력이나 이벤트 발생을 일정 시간 동안 지연시킨 후 처리하는 패턴입니다. 입력 필드와 같은 상황에서 유용합니다.

import 'dart:async';

void main() {
  final debounceDuration = Duration(milliseconds: 300);
  Timer? debounceTimer;

  void onUserInput(String input) {
    debounceTimer?.cancel();
    debounceTimer = Timer(debounceDuration, () {
      print('Processing input: $input');
    });
  }

  // Simulating user input
  onUserInput('A');
  onUserInput('Ab');
  onUserInput('Abc');
}

 

비동기 프로그래밍은 현대 애플리케이션에서 필수적인 기술입니다.

Dart에서는 Future와 Stream을 통해 비동기 작업을 효율적으로 처리할 수 있습니다.

비동기 코드를 작성할 때는 에러 처리, 자원 관리, 작업 순서 보장, 취소 메커니즘 등을 고려해야 하며, 다양한 패턴을 활용하여 복잡한 비동기 작업을 간결하고 효과적으로 처리할 수 있습니다.

구독!! 공감과 댓글은 저에게 큰 힘이 됩니다.

Starting Google Play App Distribution! "Tester Share" for Recruiting 20 Testers for a Closed Test.

 

Tester Share [테스터쉐어] - Google Play 앱

Tester Share로 Google Play 앱 등록을 단순화하세요.

play.google.com

반응형