앱을 개발하다보면 네트워크 통신 기능을 개발할 상황이 생깁니다.
Flutter는 HTTP프로토콜을 http라이브러리를 통해 네트워크 통신을 지원하고 있습니다.
Http 라이브러리만을 통해서 네트워크 통신 기능을 개발할 수 있지만 보일러플레이트 코드의 양이 많아 유지보수가 어렵습니다. 이를 개선하기 위해 Dio 패키지를 사용할 수 있습니다.
Dio패키지는 타임아웃, 인터셉터, 파일 업/다운로드, 요청취소와 같은 강력한 기능을 제공해주기 때문에 Dio를 사용해서 쉽고 빠르게 네트워크 통신 기능을 구현할 수 있습니다 :)
Http 통신을 위해 반복적으로 사용할 Dio 객체를 전역으로 생성합니다.
connectTimeout, receiveTimeout, baseUrl등을 BaseOptions 파라미터에 담아 생성할 수 있습니다.
final Dio dio = Dio(
BaseOptions(
contentType: Headers.jsonContentType,
connectTimeout: const Duration(milliseconds: 5000),
receiveTimeout: const Duration(milliseconds: 3000),
),
);
위 샘플 api url을 사용하겠습니다. 위 url은 다음과 같은 데이터를 리턴합니다.
{
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
}
다음으로 비동기 메소드인 dio.get()를 호출해 데이터를 받아옵니다.
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
class MainApp extends StatefulWidget {
const MainApp({super.key});
@override
State<MainApp> createState() => _MainAppState();
}
class _MainAppState extends State<MainApp> {
static const String apiUrl = "https://jsonplaceholder.typicode.com/todos/1";
String todo = '';
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () async {
final response = await dio.get(apiUrl);
setState(() {
todo = response.data.toString();
});
},
child: const Text('GET TODO'),
),
Text(
'TODO: $todo',
textAlign: TextAlign.center,
),
],
),
),
),
);
}
}
위 코드를 실행하면 아래와 같은 결과를 얻을 수 있습니다.
위 스크린샷에 나와있는 것처럼 예상했던 데이터가 로그에 찍히고 화면에 노출되는 것을 확인할 수 있습니다.
초장에서 설명했던 것처럼 Dio의 강력한 기능중 하나는 인터셉터입니다.
Dio에서 제공하는 Intercepter클래스를 상속받는 커스텀 인터셉터 클래스를 생성한 뒤
onRequest, onResponse, onError 메소드를 구현할 수 있습니다.
class CustomInterceptor extends Interceptor {
CustomInterceptor();
// api 요청 보낼때
@override
void onRequest(
RequestOptions options, RequestInterceptorHandler handler) async {
logger.d('[REQUEST] ${options.method}] ${options.uri}');
return super.onRequest(options, handler);
}
// api 응답 받을때
@override
void onResponse(Response response, ResponseInterceptorHandler handler) {
logger.d(
'[RESPONSE] ${response.requestOptions.method}] ${response.requestOptions.uri}');
return super.onResponse(response, handler);
}
// api 통신 중 에러 발생했을때
@override
void onError(DioException err, ErrorInterceptorHandler handler) async {
logger.e(
'[ERROR] ${err.requestOptions.method}] ${err.requestOptions.uri} StatusCode:${err.response?.statusCode}, ${err.message}, ${err.type}');
}
}
생성한 인터셉터를 dio객체를 생성할 때, interceptors.add() 메소드로 주입합니다.
final Dio dio = Dio(
BaseOptions(
contentType: Headers.jsonContentType,
connectTimeout: const Duration(milliseconds: 5000),
receiveTimeout: const Duration(milliseconds: 3000),
),
)..interceptors.add(CustomInterceptor());
API 콜을 Dio로 보냈을 때 위와 같은 로그가 찍히는 것을 확인할 수 있습니다.
전체 코드 깃허브
https://github.com/KoreanTuna/Dio-Study