[Flutter] Dio 패키지를 통한 http 통신

Cherrie KimCherrie Kim
6 min read

A powerful HTTP networking package for Dart/Flutter, supports Global configuration, Interceptors, FormData, Request cancellation, File uploading/downloading, Timeout, Custom adapters, Transformers, etc.

Dio는 Flutter/Dart 애플리케이션 개발에서 사용할 수 있는 강력하고 유연한 HTTP 클라이언트 라이브러리이다. 플러터의 대표적 api 통신 라이브버리인 http 보다 더 많은 기능(파일 업로드/다운로드, 인터셉터 등)을 제공하여, 개발자가 복잡한 네트워크 요청과 응답을 쉽게 처리할 수 있도록 도와준다.

사용은 이런 식으로 매우 간단하다!

import 'package:dio/dio.dart';

final dio = Dio();

void getHttp() async {
  final response = await dio.get('https://dart.dev');
  print(response);
}

Dio의 주요 특징

  • 강력한 요청 처리 기능: Dio는 GET, POST, DELETE, PUT 등 모든 유형의 HTTP 요청을 지원한다. 또한, FormData를 사용한 파일 업로드와 다운로드, 스트림을 통한 대용량 파일 처리 등 복잡한 네트워크 작업을 쉽게 처리할 수 있다.

  • 인터셉터: 요청 및 응답 인터셉터를 사용하여 네트워크 요청과 응답을 가로챌 수 있다. 이를 통해 모든 요청에 공통 헤더를 추가하거나 응답 데이터를 전처리하는 등의 작업을 수행할 있다고 한다.

  • 요청 취소: Dio는 요청 취소 기능을 제공하여, 더 이상 필요하지 않은 HTTP 요청을 취소할 수 있다. 이는 특히 사용자 인터페이스와 상호 작용하는 동안 유용하게 사용될 수 있다.

  • 풍부한 구성 옵션: Dio는 타임아웃 설정, 커스텀 쿠키/헤더 관리, SSL 인증서 검증 등 다양한 구성 옵션을 제공한다. 이를 통해 애플리케이션의 보안 및 네트워크 요구 사항을 충족시킬 수 있다.

  • 오류 처리: Dio는 오류 발생 시 상세한 정보를 제공하며, 다양한 HTTP 오류 상태를 쉽게 처리할 수 있도록 한다.

확실히 http보다는 훨씬 풍부한 기능이 있는 것 같지만, 이번 글에서는 Dio를 활용한 기본적인 http 통신 위주로만 정리해보고자 한다.

HTTP 통신

GET

final dio = Dio();

void request() async {
  Response response;
  response = await dio.get(
    '/test',
    queryParameters: {'id': 12, 'name': 'dio'},
  );
  // 위와 동일: response = await dio.get('/test?id=12&name=dio');
  print(response.data.toString());
}

POST

response = await dio.post('/test', data: {'id': 12, 'name': 'dio'});

PUT

response = await dio.put('/test', data: {'id': 12, 'name': 'dio'});

DELETE

response = await dio.delete('/test', data: {'id': 12});

이외 유용한 기능 (일부)

동시에 다중 요청 수행

response = await Future.wait([dio.post('/info'), dio.get('/token')]);

파일 다운로드

response = await dio.download(
  'https://pub.dev/',
  (await getTemporaryDirectory()).path + '/pub.html',
);

FormData 전송 (여러개의 파일 전송도 가능)

final formData = FormData.fromMap({
  'name': 'dio',
  'date': DateTime.now().toIso8601String(),
});
final response = await dio.post('/info', data: formData);

Options

options는 Dio를 사용하여 네트워크 요청을 수행할 때 다양한 HTTP 설정을 구성하는 데 사용된다. 각각의 Dio 요청은 기본적으로 Dio 인스턴스의 기본 설정을 상속받지만, Options 객체를 통해 특정 요청에 대한 구성을 오버라이딩을 통해 커스터마이징할 수 있는 것이다. Options 클래스를 통해 요청의 헤더, 메소드, 타임아웃, 콘텐츠 유형 등을 세밀하게 조정할 수 있고, 이를 통해 요청마다 다른 매개변수를 쉽게 지정할 수 있다.

/// The HTTP request method.
String method;

/// Timeout when sending data.
///
/// Throws the [DioException] with
/// [DioExceptionType.sendTimeout] type when timed out.
///
/// `null` or `Duration.zero` means no timeout limit.
Duration? sendTimeout;

/// Timeout when receiving data.
///
/// The timeout represents:
///  - a timeout before the connection is established
///    and the first received response bytes.
///  - the duration during data transfer of each byte event,
///    rather than the total duration of the receiving.
///
/// Throws the [DioException] with
/// [DioExceptionType.receiveTimeout] type when timed out.
///
/// `null` or `Duration.zero` means no timeout limit.
Duration? receiveTimeout;

/// Custom field that you can retrieve it later in [Interceptor],
/// [Transformer] and the [Response.requestOptions] object.
Map<String, dynamic>? extra;

/// HTTP request headers.
///
/// The keys of the header are case-insensitive,
/// e.g.: `content-type` and `Content-Type` will be treated as the same key.
Map<String, dynamic>? headers;

/// Whether the case of header keys should be preserved.
///
/// Defaults to false.
///
/// This option WILL NOT take effect on these circumstances:
/// - XHR ([HttpRequest]) does not support handling this explicitly.
/// - The HTTP/2 standard only supports lowercase header keys.
bool? preserveHeaderCase;

/// The type of data that [Dio] handles with options.
///
/// The default value is [ResponseType.json].
/// [Dio] will parse response string to JSON object automatically
/// when the content-type of response is [Headers.jsonContentType].
///
/// See also:
///  - `plain` if you want to receive the data as `String`.
///  - `bytes` if you want to receive the data as the complete bytes.
///  - `stream` if you want to receive the data as streamed binary bytes.
ResponseType? responseType;

/// The request content-type.
///
/// The default `content-type` for requests will be implied by the
/// [ImplyContentTypeInterceptor] according to the type of the request payload.
/// The interceptor can be removed by
/// [Interceptors.removeImplyContentTypeInterceptor].
String? contentType;

/// Defines whether the request is considered to be successful
/// with the given status code.
/// The request will be treated as succeed if the callback returns true.
ValidateStatus? validateStatus;

/// Whether to retrieve the data if status code indicates a failed request.
///
/// Defaults to true.
bool? receiveDataWhenStatusError;

/// See [HttpClientRequest.followRedirects].
///
/// Defaults to true.
bool? followRedirects;

/// The maximum number of redirects when [followRedirects] is `true`.
/// [RedirectException] will be thrown if redirects exceeded the limit.
///
/// Defaults to 5.
int? maxRedirects;

/// See [HttpClientRequest.persistentConnection].
///
/// Defaults to true.
bool? persistentConnection;

/// The default request encoder is [Utf8Encoder], you can set custom
/// encoder by this option.
RequestEncoder? requestEncoder;

/// The default response decoder is [Utf8Decoder], you can set custom
/// decoder by this option, it will be used in [Transformer].
ResponseDecoder? responseDecoder;

/// Indicates the format of collection data in request query parameters and
/// `x-www-url-encoded` body data.
///
/// Defaults to [ListFormat.multi].
ListFormat? listFormat;
  • method: HTTP 요청 메소드를 지정한다 (GET, POST, DELETE, PUT 등).

  • headers: 요청에 포함될 HTTP 헤더를 지정한다. 예를 들어, 인증 토큰이나 커스텀 헤더 등을 추가할 수 있다.

  • contentType: 요청의 Content-Type을 설정한다. application/json, multipart/form-data 등이 여기에 해당된다.

  • responseType: 응답 데이터의 타입을 지정한다 (ResponseType.json, ResponseType.plain, ResponseType.bytes 등). 이는 Dio가 서버로부터 받은 응답을 어떻게 처리할지 결정한다.

  • followRedirects: 리디렉션을 자동으로 따를지 여부를 결정한다. 기본값은 true.

  • validateStatus: HTTP 상태 코드가 성공으로 간주되는 조건을 정의한다. 기본적으로 상태 코드가 200에서 299 사이일 때 성공으로 간주된다.

  • receiveDataWhenStatusError: 상태 코드가 에러 범위에 있을 때도 응답 본문을 받을지 여부를 결정한다.

  • connectTimeout, sendTimeout, receiveTimeout: 각각 연결 시간 초과, 데이터 전송 시간 초과, 데이터 수신 시간 초과에 대한 타임아웃을 설정한다.

사용 예시:

GET 요청을 보내면서 'Authorization' 헤더를 설정하고, 응답 유형으로 JSON을 지정해보자!

void main() async {
  var dio = Dio();
  var response = await dio.get(
    'https://example.com',
    options: Options(
      headers: {
        'Authorization': 'Bearer your_token',
      },
      responseType: ResponseType.json,
    ),
  );

  print(response.data);
}

Interceptors

Dio의 Interceptors는 HTTP 요청/응답 프로세스에 훅(Hook)을 추가하여 요청이 실행되기 전, 실행 도중, 그리고 응답을 받은 후에 사용자 정의 로직을 실행할 수 있게 해주는 강력한 기능이다. 이를 통해 개발자는 네트워크 요청의 로깅, 인증 토큰 추가, 에러 처리, 응답 데이터 전처리 등 다양한 공통 작업을 중앙에서 관리할 수 있다.

Dio에서 인터셉터를 추가하려면 'Dio' 객체의 'interceptors' 속성에 인터셉터를 추가하면 된다. 'interceptors' 속성은 'Interceptor' 객체의 리스트를 가지며, 각 인터셉터는 'onRequest', 'onResponse', 'onError' 콜백을 구현 가능하다.

공식 문서에 다양한 예시가 나와있으니 인터셉터 기능이 필요할 때는 공식 문서를 꼭! 참고해보도록 하자.

사용 예시:

void main() {
  var dio = Dio();

  dio.interceptors.add(InterceptorsWrapper(
    onRequest: (options, handler) {
      // 요청이 실행되기 전에 로직을 실행
      print('Sending request to ${options.uri}');
      // 요청을 계속 진행하려면 다음 핸들러를 호출해야 함.
      handler.next(options);
    },
    onResponse: (response, handler) {
      // 응답을 받은 후의 로직을 실행
      print('Received response from ${response.requestOptions.uri}');
      // 응답을 계속 진행하려면 다음 핸들러를 호출해야 함.
      handler.next(response);
    },
    onError: (DioError e, handler) {
      // 에러가 발생했을 때의 로직을 실행
      print('Request failed: ${e.message}');
      // 에러를 계속 진행하려면 다음 핸들러를 호출해야 함.
      handler.next(e);
    },
  ));

  // 이제 모든 요청, 응답, 에러는 위에 정의한 인터셉터를 통과한다!
}

ref

https://pub.dev/packages/dio

0
Subscribe to my newsletter

Read articles from Cherrie Kim directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Cherrie Kim
Cherrie Kim