상세 컨텐츠

본문 제목

[Flutter] HTTP 통신 라이브러리 - DIO

플러터/라이브러리

by 감자 바보 2024. 12. 18. 15:12

본문

반응형

 

공식 문서 : dio | Dart package

📘 1. Dio란?

DioDart로 작성된 HTTP 클라이언트 라이브러리로, Flutter 애플리케이션에서 서버와의 통신을 쉽게 관리할 수 있도록 설계되었습니다. Dio동기 및 비동기 요청, 인터셉터, 글로벌 설정, 요청 취소, 파일 업로드 및 다운로드 등 다양한 기능을 제공합니다.


📘 2. 주요 특징

특징 설명

인터셉터 요청, 응답, 오류를 가로채어 추가 로직을 실행할 수 있습니다. 예: 로깅, 인증 토큰 추가 등.
글로벌 설정 기본 URL, 타임아웃, 헤더 등 모든 요청에 공통적으로 적용되는 설정을 관리할 수 있습니다.
요청 취소 필요에 따라 진행 중인 요청을 취소할 수 있습니다.
파일 업로드 및 다운로드 멀티파트 폼 데이터 업로드 및 파일 다운로드를 간편하게 처리할 수 있습니다.
재시도 로직 실패한 요청을 자동으로 재시도할 수 있는 기능을 지원합니다.
타입 지원 JSON, FormData 등 다양한 데이터 타입을 지원합니다.
인터넷 연결 상태 체크 네트워크 상태에 따라 요청을 처리할 수 있는 기능을 제공합니다.
미들웨어 지원 요청 처리 과정에 미들웨어를 추가하여 유연하게 요청을 조작할 수 있습니다.

📘 3. 설치 및 설정

🔍 1️⃣ Dio 패키지 설치

pubspec.yaml 파일에 Dio 패키지를 추가합니다.

dependencies:
  dio: ^5.3.2 # 최신 버전 확인 후 적용

터미널에서 다음 명령어를 실행하여 패키지를 설치합니다.

flutter pub get

🔍 2️⃣ Dio 인스턴스 생성

Dio를 사용하기 위해 인스턴스를 생성합니다. 글로벌 설정을 적용할 수도 있습니다.

ㅑmport 'package:dio/dio.dart';

final dio = Dio(); // 기본 인스턴스 생성

// 글로벌 설정 예시
final dio = Dio(
  BaseOptions(
    baseUrl: '<https://api.example.com/>', // 기본 URL 설정
    connectTimeout: 5000, // 연결 타임아웃 (밀리초)
    receiveTimeout: 3000, // 응답 타임아웃 (밀리초)
    headers: {
      'Content-Type': 'application/json',
      'Accept': 'application/json',
    },
  ),
);


📘 4. 기본 사용법

🔍 1️⃣ GET 요청

import 'package:dio/dio.dart';

void fetchData() async {
  try {
    Response response = await dio.get('/users'); // 예: <https://api.example.com/users>
    print(response.data);
  } catch (e) {
    print('Error: $e');
  }
}

🔍 2️⃣ POST 요청

import 'package:dio/dio.dart';

void createUser() async {
  try {
    Response response = await dio.post(
      '/users',
      data: {
        'name': 'John Doe',
        'email': 'john.doe@example.com',
      },
    );
    print(response.data);
  } catch (e) {
    print('Error: $e');
  }
}

🔍 3️⃣ PUT, DELETE 요청

// PUT 요청
void updateUser() async {
  try {
    Response response = await dio.put(
      '/users/1',
      data: {
        'name': 'Jane Doe',
      },
    );
    print(response.data);
  } catch (e) {
    print('Error: $e');
  }
}

// DELETE 요청
void deleteUser() async {
  try {
    Response response = await dio.delete('/users/1');
    print(response.data);
  } catch (e) {
    print('Error: $e');
  }
}


📘 5. 고급 기능

🔍 1️⃣ 인터셉터 추가

인터셉터는 요청과 응답을 가로채어 추가 작업을 수행할 수 있게 합니다.

dio.interceptors.add(
  InterceptorsWrapper(
    onRequest: (options, handler) {
      print('REQUEST[${options.method}] => PATH: ${options.path}');
      return handler.next(options); // 요청 계속 진행
    },
    onResponse: (response, handler) {
      print('RESPONSE[${response.statusCode}] => DATA: ${response.data}');
      return handler.next(response); // 응답 계속 진행
    },
    onError: (DioError e, handler) {
      print('ERROR[${e.response?.statusCode}] => MESSAGE: ${e.message}');
      return handler.next(e); // 에러 계속 진행
    },
  ),
);

🔍 2️⃣ 요청 취소

특정 요청을 취소할 수 있습니다.

import 'package:dio/dio.dart';

void fetchDataWithCancel() async {
  CancelToken cancelToken = CancelToken();

  try {
    Response response = await dio.get(
      '/long-request',
      cancelToken: cancelToken,
    );
    print(response.data);
  } catch (e) {
    if (CancelToken.isCancel(e)) {
      print('Request canceled! ${e.message}');
    } else {
      print('Error: $e');
    }
  }

  // 요청을 취소하려면
  cancelToken.cancel('Request canceled by user.');
}

🔍 3️⃣ 파일 업로드

멀티파트 폼 데이터로 파일을 업로드할 수 있습니다.

import 'package:dio/dio.dart';
import 'dart:io';

void uploadFile() async {
  String filePath = '/path/to/file.jpg';
  File file = File(filePath);

  FormData formData = FormData.fromMap({
    'file': await MultipartFile.fromFile(file.path, filename: 'upload.jpg'),
    'description': 'File upload example',
  });

  try {
    Response response = await dio.post(
      '/upload',
      data: formData,
      onSendProgress: (int sent, int total) {
        print('$sent / $total');
      },
    );
    print(response.data);
  } catch (e) {
    print('Error: $e');
  }
}

🔍 4️⃣ 파일 다운로드

파일을 다운로드하고 진행 상황을 추적할 수 있습니다.

import 'package:dio/dio.dart';
import 'dart:io';

void downloadFile() async {
  String url = '<https://example.com/file.zip>';
  String savePath = '/path/to/save/file.zip';

  try {
    Response response = await dio.download(
      url,
      savePath,
      onReceiveProgress: (int received, int total) {
        if (total != -1) {
          print('${(received / total * 100).toStringAsFixed(0)}%');
        }
      },
    );
    print('File downloaded: ${response.statusCode}');
  } catch (e) {
    print('Error: $e');
  }
}

🔍 5️⃣ 재시도 로직

요청이 실패했을 때 자동으로 재시도할 수 있습니다. 이를 위해 인터셉터를 사용하여 구현할 수 있습니다.

import 'package:dio/dio.dart';

class RetryInterceptor extends Interceptor {
  final Dio dio;
  final int retries;

  RetryInterceptor({required this.dio, this.retries = 3});

  @override
  void onError(DioError err, ErrorInterceptorHandler handler) async {
    if (retries > 0 && _shouldRetry(err)) {
      try {
        // 재시도
        Response response = await dio.request(
          err.requestOptions.path,
          options: Options(
            method: err.requestOptions.method,
            headers: err.requestOptions.headers,
          ),
          data: err.requestOptions.data,
          queryParameters: err.requestOptions.queryParameters,
        );
        return handler.resolve(response);
      } catch (e) {
        return handler.next(err);
      }
    }
    return handler.next(err);
  }

  bool _shouldRetry(DioError err) {
    // 재시도 조건 설정 (예: 네트워크 오류, 서버 오류 등)
    return err.type == DioErrorType.other || err.type == DioErrorType.connectTimeout;
  }
}

// 인터셉터 추가
dio.interceptors.add(RetryInterceptor(dio: dio, retries: 3));


📘 6. 에러 처리

Dio는 다양한 에러 유형을 지원하며, 이를 통해 세밀한 에러 처리가 가능합니다.

🔍 1️⃣ 에러 유형

에러 유형 설명

DioErrorType.connectTimeout 연결 타임아웃 오류
DioErrorType.sendTimeout 데이터 전송 타임아웃 오류
DioErrorType.receiveTimeout 응답 수신 타임아웃 오류
DioErrorType.response 서버로부터 유효하지 않은 응답을 받았을 때 발생
DioErrorType.cancel 요청이 취소되었을 때 발생
DioErrorType.other 기타 네트워크 오류 (예: DNS 문제, 소켓 예외 등)

🔍 2️⃣ 에러 처리 예시

void fetchDataWithErrorHandling() async {
  try {
    Response response = await dio.get('/endpoint');
    print(response.data);
  } on DioError catch (e) {
    if (e.type == DioErrorType.connectTimeout) {
      print('Connection Timeout Exception');
    } else if (e.type == DioErrorType.sendTimeout) {
      print('Send Timeout Exception');
    } else if (e.type == DioErrorType.receiveTimeout) {
      print('Receive Timeout Exception');
    } else if (e.type == DioErrorType.response) {
      print('Received invalid status code: ${e.response?.statusCode}');
    } else if (e.type == DioErrorType.cancel) {
      print('Request to API server was cancelled');
    } else {
      print('Unexpected error: $e');
    }
  } catch (e) {
    print('Something went wrong: $e');
  }
}


📘 7. 요청 취소

Dio는 요청을 취소할 수 있는 기능을 제공합니다. 이를 통해 사용자가 필요하지 않은 요청을 취소할 수 있습니다.

🔍 1️⃣ 요청 취소 예시

import 'package:dio/dio.dart';

void fetchDataWithCancel() async {
  CancelToken cancelToken = CancelToken();

  try {
    Response response = await dio.get(
      '/long-request',
      cancelToken: cancelToken,
    );
    print(response.data);
  } catch (e) {
    if (CancelToken.isCancel(e)) {
      print('Request canceled! ${e.message}');
    } else {
      print('Error: $e');
    }
  }

  // 특정 시점에 요청을 취소하려면
  // cancelToken.cancel('Request canceled by user.');
}

🔍 2️⃣ UI에서 요청 취소하기

예를 들어, 사용자가 화면을 떠날 때 진행 중인 요청을 취소할 수 있습니다.

class MyWidget extends StatefulWidget {
  @override
  _MyWidgetState createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  CancelToken? _cancelToken;

  @override
  void dispose() {
    _cancelToken?.cancel('Widget disposed');
    super.dispose();
  }

  void fetchData() async {
    _cancelToken = CancelToken();

    try {
      Response response = await dio.get(
        '/endpoint',
        cancelToken: _cancelToken,
      );
      print(response.data);
    } catch (e) {
      if (CancelToken.isCancel(e)) {
        print('Request canceled! ${e.message}');
      } else {
        print('Error: $e');
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      // UI 구성
    );
  }
}

728x90
반응형

관련글 더보기