본문 바로가기
Flutter

Flutter에서 Isolate 활용하기: 고성능 비동기 작업 처리

by 안될개발 2025. 1. 25.

Flutter에서 Isolate 활용하기: 고성능 비동기 작업 처리

Flutter에서 Isolate 활용하기: 고성능 비동기 작업 처리

Flutter는 단일 스레드 기반의 이벤트 루프에서 실행되며, 기본적으로 모든 UI 작업은 메인 스레드에서 처리됩니다. 하지만 무거운 작업(예: 대규모 데이터 파싱, 이미지 처리, 계산 작업 등)을 메인 스레드에서 실행하면 앱이 멈추거나 프레임 드롭이 발생할 수 있습니다. 이 문제를 해결하기 위해 Dart는 Isolate라는 독립적인 스레드 환경을 제공합니다.


Isolate란?

Isolate는 Dart에서 제공하는 경량 스레드로, 다음과 같은 특징을 가지고 있습니다:

  • 독립적인 메모리 공간과 이벤트 루프를 가집니다.
  • 다른 Isolate와 메모리를 공유하지 않습니다.
  • 데이터를 주고받기 위해 메시지 기반 통신(SendPort, ReceivePort)을 사용합니다.

Isolate는 멀티코어 CPU 환경에서 병렬로 작업을 처리할 수 있어, Flutter에서 무거운 작업을 분리하는 데 유용합니다.


Isolate가 필요한 상황

  1. 대규모 데이터 파싱
    • JSON이나 XML 같은 대량의 데이터를 처리할 때.
  2. 이미지 처리
    • 이미지 필터링, 압축, 변환 등 CPU 사용량이 높은 작업.
  3. 복잡한 계산 작업
    • 암호화, 해싱, 물리 엔진 연산 등.

Isolate의 기본 구조

Isolate 간 데이터는 직접 공유되지 않으며, SendPort와 ReceivePort를 사용하여 메시지를 주고받습니다.

기본 구조:

import 'dart:isolate';

void isolateTask(SendPort sendPort) {
  int result = 0;
  for (int i = 0; i < 1000000000; i++) {
    result += i;
  }
  sendPort.send(result); // 결과를 메인 Isolate로 전송
}

void main() async {
  ReceivePort receivePort = ReceivePort(); // 결과를 받을 포트
  await Isolate.spawn(isolateTask, receivePort.sendPort); // 새로운 Isolate 생성

  // 결과 수신
  receivePort.listen((message) {
    print("Isolate 결과: $message");
    receivePort.close();
  });
}

Flutter에서 compute를 활용한 간단한 Isolate 사용

Flutter는 Isolate 사용을 간소화하기 위해 compute 함수를 제공합니다. compute는 별도의 Isolate에서 작업을 실행하고 결과를 반환합니다.

예제: JSON 파싱

import 'dart:convert';
import 'package:flutter/foundation.dart';

List<dynamic> parseJson(String jsonString) {
  return jsonDecode(jsonString);
}

void main() async {
  const jsonData = '{"data": [1, 2, 3, 4, 5]}';
  final result = await compute(parseJson, jsonData); // 별도의 Isolate에서 실행
  print(result); // 출력: {data: [1, 2, 3, 4, 5]}
}

장점:

  • compute는 간단한 데이터 변환 작업에 적합.
  • 직접 SendPort와 ReceivePort를 다룰 필요 없음.

단점:

  • 하나의 함수와 하나의 파라미터만 전달 가능.
  • 복잡한 작업에는 적합하지 않음.

고급 Isolate 사용법

1. Isolate와 상태 관리

복잡한 데이터를 다루거나 작업 상태를 관리해야 할 경우, 직접 Isolate를 생성하고 메시지를 주고받는 방식이 필요합니다.

예제: 상태 기반 이미지 처리

import 'dart:isolate';

void processImage(SendPort sendPort) {
  // 이미지 처리 시뮬레이션
  final processedImage = "이미지 처리 완료";
  sendPort.send(processedImage); // 결과 전송
}

void main() async {
  ReceivePort receivePort = ReceivePort();
  await Isolate.spawn(processImage, receivePort.sendPort);

  receivePort.listen((message) {
    print("결과: $message");
    receivePort.close();
  });
}

2. Isolate를 사용한 지속적인 작업

무거운 작업을 지속적으로 실행하거나 중단 가능한 작업을 구현할 수 있습니다.

예제: 지속적인 작업 처리

import 'dart:isolate';

void continuousTask(SendPort sendPort) {
  int counter = 0;
  while (true) {
    counter++;
    sendPort.send(counter);
  }
}

void main() async {
  ReceivePort receivePort = ReceivePort();
  await Isolate.spawn(continuousTask, receivePort.sendPort);

  receivePort.listen((message) {
    print("Counter: $message");
    if (message == 100) {
      print("작업 종료");
      receivePort.close();
    }
  });
}

Isolate 사용 시 주의사항

  1. 메모리 사용량
    • Isolate는 독립적인 메모리를 사용하므로 과도한 Isolate 생성은 메모리 사용량을 증가시킬 수 있습니다.
  2. 메시지 크기
    • 큰 데이터나 복잡한 객체를 주고받는 작업은 성능에 영향을 줄 수 있습니다.
  3. UI 작업
    • UI와 관련된 모든 작업은 반드시 메인 스레드에서 실행되어야 합니다. Isolate는 UI 작업을 처리할 수 없습니다.
  4. 정리
    • 작업이 완료되면 ReceivePort를 닫아야 리소스 누수를 방지할 수 있습니다.

Isolate vs. Future

특징 Isolate Future (async/await)

메모리 공유 불가능 (독립적인 메모리 공간 사용) 가능
작업 처리 방식 병렬 처리 가능 단일 스레드에서 비동기 처리
사용 사례 CPU 집약적인 작업, 대규모 데이터 처리 간단한 비동기 작업, 네트워크 요청 등
메시지 전달 방식 SendPort와 ReceivePort를 통한 메시지 기반 통신 직접 데이터 반환

Isolate를 활용한 고성능 Flutter 앱 설계

Isolate는 무거운 작업을 분리하여 앱 성능을 최적화하고 사용자 경험을 개선하는 데 강력한 도구입니다. Flutter에서 compute를 사용하여 간단하게 활용할 수도 있고, 직접 SendPort와 ReceivePort를 통해 복잡한 작업을 제어할 수도 있습니다.

성능 최적화를 위해 Isolate를 적절히 활용하면, 대규모 데이터를 다루는 앱이나 고성능 작업을 처리하는 Flutter 앱에서도 뛰어난 사용자 경험을 제공할 수 있습니다.


결론

Flutter의 Isolate는 멀티코어 환경에서 병렬 처리 능력을 활용하여 성능 문제를 해결할 수 있는 강력한 도구입니다. 단, 사용 시 메모리와 리소스 관리에 주의해야 하며, UI 작업과의 분리를 명확히 이해하고 설계해야 합니다. Isolate를 활용하여 앱의 성능을 극대화하고 싶다면, 위의 사례를 참고해 직접 구현해 보시기 바랍니다.