Front-End/Flutter

[Flutter] DIO 사용 방법

Voyage_dev 2023. 10. 28. 17:15

Dio란?

(A powerful http client for dart)

http 라이브러리 처럼 서버와 통신을 하기 위해 필요한 패키지. http와 다르게 사용하기 쉽게 보다 많은 기능들을 제공하고 있으며 커스텀을 통해 사용하기 편하다.

  • Rest API 통신에 가장 인기 많은 라이브러리
  • http와 다르게 응답 받은 JSON 파일을 dart:convert를 통해 디코딩 할 필요 없이 디코딩된 상태로 리턴된다
  • options, interceptor를 통해 다양한 기능을 한 번에 핸들링 할 수 있다

설정

// pubspec.yaml

dependencies:
  dio: ^5.3.3

주요 기능

// 기초

Dio _dio = Dio();

// 첫번째
final response = await _dio.get('/test?id=1&name=voyage');

// 두번째
final response = await _dio.get('/test', queryParameters: {'id': 1, 'name': 'voyage'});

// 세번째
final response = await _dio.request(
  '/test',
  data: {'id':1,'name':'xx'},
  options: Options(method:'GET'),
);

// post 첫번째
final response = await _dio.post('/test', data: {'id': 1, 'name': 'voyage'});

// post 두번째

Map<String, dynamic> _data = {
		"test" : 1,
    "test2" : 2,
};
await _dio.post($url,data:_data);

// put
Map<String, dynamic> _data = {
	"test" : 1,
    "test2" : 2,
};
await _dio.put($url,data:_data);

// delete
Map<String, dynamic> _data = {
	"test" : 1,
    "test2" : 2,
};
await _dio.delete($url,data:_data);
  • axios와 비슷하게 각 메서드 별로 함께 넘길 수 있는 여러가지 파라미터들이 있다. (쿼리 + body)

dioService.dart

class DioServices {
  static final DioServices _dioServices = DioServices._internal();
  factory DioServices() => _dioServices;
  Map<string, dynamic=""> dioInformation = {};

  static Dio _dio = Dio();

  DioServices._internal() {
    BaseOptions _options = BaseOptions(
      baseUrl:'<https://www.xx.com/api>' || $url;,
      connectTimeout: const Duration(milliseconds: 10000),
      receiveTimeout: const Duration(milliseconds: 10000),
      sendTimeout: const Duration(milliseconds: 10000),
      // headers: {},
    );
    _dio = Dio(_options);
    _dio.interceptors.add(DioInterceptor());
  }

  Dio to() {
    return _dio;
  }
}
</string,>

Dio를 싱클톤으로 즉, 인스턴스를 제한하여 기존 인스턴스를 리턴하고 하나의 인스턴스만 생성되도록 하는 패턴으로 기존 객체를 가져다 사용하는 패턴을 사용한다

 

BaseOptions에서 사용하는 옵션들은

  • baseUrl: 요청할 기본 주소를 설정할 수 있다.
  • connectTimeout: 서버로부터 응답받는 시간을 설정할 수 있다.
  • receiveTimeout: 파일 다운로드 등과 같이 연결 지속 시간을 설정할 수 있다.
  • headers: 요청의 header 데이터를 설정할 수 있다. ex) 인증 토큰

공통적으로 사영되는 것은 dio 생성시 설정, 그 외에 것들은 요청에 맞게 설정하는 식이 좋다.

 

Dio Interceptor

  • Interceptor는 요청 때 마다 가로채는 역할을 한다.
  • DioInterceptor 객체를 생성 후 Interceptor 객체를 상속받는다.
  • Interceptor 객체에는 onRequest / onResponse / onError 함수를 override 하여 재 정의해서 사용할 수 있다.
  • 로그 및 상태 핸들링을 할 수 있다.
    • ex) 로그인 토큰이 만료 되었을 때, onError 부분에 401 에러 조건을 추가 후 로그인 페이지로 보내주는 작업
class DioInterceptor extends Interceptor {
  @override
  void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
    logger.e("BaseUrl ${options.baseUrl}");
    logger.e("Path ${options.path}");
    logger.e("Parameters ${options.queryParameters}");
    logger.e("Data ${options.data}");
    logger.e("Connect Timeout ${options.connectTimeout}");
    logger.e("Send Timeout ${options.sendTimeout}");
    logger.e("Receive Timeout ${options.receiveTimeout}");
    super.onRequest(options, handler);
  }

  @override
  void onResponse(Response response, ResponseInterceptorHandler handler) {
    logger.e(response.statusCode);
    logger.e(response.data);
    logger.e("BaseUrl ${response.requestOptions.baseUrl}");
    logger.e("Path ${response.requestOptions.path}");
    logger.e("Parameters ${response.requestOptions.queryParameters}");
    logger.e("Data ${response.requestOptions.data}");
    logger.e("Connect Timeout ${response.requestOptions.connectTimeout}");
    logger.e("Send Timeout ${response.requestOptions.sendTimeout}");
    logger.e("Receive Timeout ${response.requestOptions.receiveTimeout}");
    super.onResponse(response, handler);
  }

  @override
  void onError(DioException err, ErrorInterceptorHandler handler) async {
    logger.e("Error ${err.error}");
    logger.e("Error Message ${err.message}");
    super.onError(err, handler);
  }
}
Dio _dio = DioServices().to();
await _dio.get("요청URL");

Interceptors Lock / unlock

dio.interceptors.add(InterceptorsWrapper(
  onRequest: (Options options, handler) async {
    print('send request:path:${options.path},baseURL:${options.baseUrl}');
    if (csrfToken == null) {
      print('no token,request token firstly...');
      //lock the dio.
      dio.lock();
      tokenDio.get('/token').then((d) {
        options.headers['csrfToken'] = csrfToken = d.data['data']['token'];
        print('request token succeed, value: ' + d.data['data']['token']);
        print( 'continue to perform request:path:${options.path},baseURL:${options.path}');
        handler.next(options);
      }).catchError((error, stackTrace) {
        handler.reject(error, true);
      }) .whenComplete(() => dio.unlock()); // unlock the dio
    } else {
      options.headers['csrfToken'] = csrfToken;
      handler.next(options);
    }
  }
));
  • Interceptor를 통해 요청을 lock 혹은 unlock 할 수 있다.
  • 위에 코드는 토큰의 유무를 검사 즉, Interceptor를 통해 요청 때 마다 토큰의 유무를 검사하여 만약 토큰이 없다면 새로운 토큰을 요청해서 다시 이후에 요청을 진행한다. 이때 토큰이 없다면 Interceptor를 lock해서 대기열에 넣운 후 토큰을 받은 뒤 다시 unlock하는 역할이다.

 

 

출처 : 아래의 사이트들을 보면서 큰 공부 하였습니다

https://velog.io/@leeeeeoy/Flutter-Dio-%EA%B0%84%EB%8B%A8-%EC%A0%95%EB%A6%AC

https://velog.io/@tygerhwang/Flutter-Dio