FlutterㅣDio 통합 정리

휘Bin·2023년 7월 29일
1
post-thumbnail

Dio?

공식 문서에서는 Dio를 'A powerful Http client for Dart'라고 말한다. 즉, http처럼 서버와 통신을 하기 위한 패키지이다. 사용하기 쉽게 많은 기능을 제공하고 있고, 커스텀해서 사용할 수 있어 더 편리하고 알맞게 사용할 수 있다.

쉽게 말하면, Dio는 flutter에서 api통신을 할 때 필요한 라이브러리이다.
물론, Dio를 사용하지 않아도 api통신에 문제는 없으나, 그냥 깡코딩으로 하려면 api통신을 위해 구현 시간이 경우에 따라 오래 걸린다. 따라서 개발 시간을 단축하면서도 좀 더 탄탄한 구성을 만들 수 있게 한다.

현재 DIO 라이브러리는 pub.dev에서 좋아요 수가 5809개로, 신뢰도가 높고 인기 있는 라이브러리이다.

사용법

https://pub.dev/packages/dio

위 공식 사이트를 참고하고,
dependencies에 등록하고 다운하면 쉽게 이용할 수 있다.

몇 가지 기능들을 소개해보면 아래와 같다.

Request & Response

var dio = Dio();

// 첫번째 방법
final response = await dio.get('/test?id=12&name=wendu');

// 두번째 방법
final response = await dio.get('/test', queryParameters: {'id': 12, 'name': 'wendu'});

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

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

requset와 response는 위와 같이 요청 메소드url을 함께 작성해주면 된다.
각 메서드 별로 같이 넘길 수 있는 여러가지 파라미터들이 있다. 요청 방법에는 여러가지가 있는데, 위 코드처럼 쿼리로 넘길 수도 있고, body를 통해 넘길 수도 있다.

Options

var dio = Dio(); 

dio.options.baseUrl = 'https://www.xx.com/api';
dio.options.connectTimeout = 5000; //5s
dio.options.receiveTimeout = 3000;

var options = BaseOptions(
  baseUrl: 'https://www.xx.com/api',
  connectTimeout: 5000,
  receiveTimeout: 3000,
);

Dio dio = Dio(options);


// BaseOptions 객체
BaseOptions({
    String? method,
    int? connectTimeout,
    int? receiveTimeout,
    int? sendTimeout,
    String baseUrl = '',
    Map<String, dynamic>? queryParameters,
    Map<String, dynamic>? extra,
    Map<String, dynamic>? headers,
    ResponseType? responseType = ResponseType.json,
    String? contentType,
    ValidateStatus? validateStatus,
    bool? receiveDataWhenStatusError,
    bool? followRedirects,
    int? maxRedirects,
    RequestEncoder? requestEncoder,
    ResponseDecoder? responseDecoder,
    ListFormat? listFormat,
    this.setRequestContentTypeWhenNoPayload = false,
  })

dio 객체를 생성하면 공통적으로 사용하고 싶은 것들을 BaseOptions를 통해 지정할 수 있다. 자주 사용되는 옵션들은 아래와 같다.

  • baseUrl : 요청할 기본 주소를 설정

  • connectTimeout : 서버로부터 응답받는 시간을 설정

  • receiveTimeout : 파일 다운로드 등과 같이 연결 지속 시간을 설정

  • headers : 요청 header 데이터를 설정

옵션의 경우에는 각 요청마다 설정도 가능하고 처음 dio객체를 생성할 때 설정도 가능하다. 공통부분은 dio 생성시에 설정하고, 나머지는 요청에 맞게 설정하는 것을 추천한다.

Interceptor

dio의 가장 강력한 부분 중 하나이다.
Interceptor는 요청때마다 가로채는 역할을 하는데, Interceptor를 통해 요청때마다 반복적인 작업을 처리할 수 있다. 토큰의 유효성을 검사하거나 로그를 처리하거나 등등.

또한, Interceptor에서는 3가지 메서드가 있다. 각각 아래와 같이 요청, 응답, 에러가 발생했을 때 동작을 처리할 수 있다.

class Interceptor {
  void onRequest(
    RequestOptions options,
    RequestInterceptorHandler handler,
  ) =>
      handler.next(options);

  void onResponse(
    Response response,
    ResponseInterceptorHandler handler,
  ) =>
      handler.next(response);

  void onError(
    DioError err,
    ErrorInterceptorHandler handler,
  ) =>
      handler.next(err);
}

Lock/unlock the interceptors

인터셉터를 통해 요청을 lock/unlock 할 수 있다. 공식 문서에 따르면, 이런 경우에는 대기열에 추가되어 인터셉터가 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);
    }
  }
));

위의 코드는 토큰의 유무를 검사하는 예시 코드이다. 인터셉터를 통해 요청때마다 토큰의 유무를 검사해 만약 토큰이 벗다면 새로운 토큰을 요청해 다시 이후에 요청을 진행하게 된다. 이 때 토큰이 없다면, 인터셉터를 통해 lock해서 대기열에 넣은 후 토큰을 받고 나서 다시 unlock하게 된다.

Resolve and reject the request

dio.interceptors.add(InterceptorsWrapper(
  onRequest:(options, handler) {
   return handler.resolve(Response(requestOptions:options,data:'fake data'));
  },
));
Response response = await dio.get('/test');

print(response.data);
//'fake data'

모든 요청에 대해 응답을 제어할 수 있는데, 위의 코드처럼 모든 요청에 대해 fake data를 반환하는 것이 하나의 예시이다.

Log

// 기본 Log
dio.interceptors.add(LogInterceptor());

// CustomLog
dio.interceptors.add(CustomLogInterceptor());

class CustomLogInterceptor extends Interceptor {
  @override
  void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
    print('REQUEST[${options.method}] => PATH: ${options.path}');
    super.onRequest(options, handler);
  }

  @override
  void onResponse(Response response, ResponseInterceptorHandler handler) {
    print(
      'RESPONSE[${response.statusCode}] => PATH: ${response.requestOptions.path}',
    );
    super.onResponse(response, handler);
  }

  @override
  void onError(DioError err, ErrorInterceptorHandler handler) {
    print(
      'ERROR[${err.response?.statusCode}] => PATH: ${err.requestOptions.path}',
    );
    super.onError(err, handler);
  }
}

Log 지정도 가능하다. dio에서 기본적으로 제고아는 로그를 사용할 수 있다. 커스텀의 경우에는 Interceptor 객체를 상속받아 각 메서드를 구현해주면 된다.

profile
One-step, one-step, steadily growing developer

0개의 댓글

관련 채용 정보