기존에는 Http 라이브러리를 이용하여 api서버와 네트워크 통신을 하였다. 하지만 최근 인프런에서 강의를 몇개 듣고있는데 모두 Dio를 이용하여 네트워크 통신을 하였다. 그래서 이번에는 Dio에 대해 정리 해보려고 한다.
Dio
앱 개발에서 네트워크 통신은 중요한 부분이다. Dio는 이를 위한 탁월한 도구로, HTTP 통신을 쉽고 효과적으로 처리할 수 있도록 도와준다. 이 라이브러리는 HTTP와 유사한 방식으로 동작하지만, 더 간편한 사용법과 다양한 고급 기능을 제공하여 개발자들에게 큰 편의를 제공한다. Dio를 통해 플러터 앱에서 네트워크 작업을 보다 효율적으로 관리하고 제어할 수 있다.
dependencies:
dio: ^replace-with-latest-version
우선 Dio를 사용하기 위해서는 pubspec.yaml에 dio를 추가해야 한다.
Http VS Dio
우선 기존 사용하던 Http방식과 Dio방식의 차이점을 알아보겠다.
import 'package:flutter/material.dart';
import 'package:dio/dio.dart';
import 'package:http/http.dart' as http;
import 'package:flutter_practice/src/default_layout_widget.dart';
class DioScreen extends StatelessWidget {
const DioScreen({super.key});
@override
Widget build(BuildContext context) {
final dio = Dio();
return DefaultLayoutWidget(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () async {
final resp = await dio.get(
'https://reqres.in/api/users?page=1',
queryParameters: {'page': 1},
);
print(resp.data);
},
child: const Text('Dio Get'),
),
const SizedBox(
width: 100,
),
ElevatedButton(
onPressed: () async {
final resp = await http.get(
Uri.parse(
'https://reqres.in/api/users?page=1',
),
);
print(resp.data);
print(
},
child: const Text('Http Get'),
),
],
),
);
}
}
왼쪽 버튼은 Dio방식으로 네트워크 통신을 하여 데이터를 받아오고 , 오른쪽 버튼은 Http방식으로 네트워크 통신을 하여 데이터를 받아온다.
Dio : {page: 1, per_page: 6, total: 12, total_pages: 2, data: [{id: 1, email: george.bluth@reqres.in, first_name: George, last_name: Bluth, avatar: https://reqres.in/img/faces/1-image.jpg}, {id: 2, email: janet.weaver@reqres.in, first_name: Janet, last_name: Weaver, avatar: https://reqres.in/img/faces/2-image.jpg}, {id: 3, email: emma.wong@reqres.in, first_name: Emma, last_name: Wong, avatar: https://reqres.in/img/faces/3-image.jpg}, {id: 4, email: eve.holt@reqres.in, first_name: Eve, last_name: Holt, avatar: https://reqres.in/img/faces/4-image.jpg}, {id: 5, email: charles.morris@reqres.in, first_name: Charles, last_name: Morris, avatar: https://reqres.in/img/faces/5-image.jpg}, {id: 6, email: tracey.ramos@reqres.in, first_name: Tracey, last_name: Ramos, avatar: https://reqres.in/img/faces/6-image.jpg}], support: {url: https://reqres.in/#support-heading, text: To keep ReqRes free, contributions towards server costs are appreciated!}}
Http : {"page":1,"per_page":6,"total":12,"total_pages":2,"data":[{"id":1,"email":"george.bluth@reqres.in","first_name":"George","last_name":"Bluth","avatar":"https://reqres.in/img/faces/1-image.jpg"},{"id":2,"email":"janet.weaver@reqres.in","first_name":"Janet","last_name":"Weaver","avatar":"https://reqres.in/img/faces/2-image.jpg"},{"id":3,"email":"emma.wong@reqres.in","first_name":"Emma","last_name":"Wong","avatar":"https://reqres.in/img/faces/3-image.jpg"},{"id":4,"email":"eve.holt@reqres.in","first_name":"Eve","last_name":"Holt","avatar":"https://reqres.in/img/faces/4-image.jpg"},{"id":5,"email":"charles.morris@reqres.in","first_name":"Charles","last_name":"Morris","avatar":"https://reqres.in/img/faces/5-image.jpg"},{"id":6,"email":"tracey.ramos@reqres.in","first_name":"Tracey","last_name":"Ramos","avatar":"https://reqres.in/img/faces/6-image.jpg"}],"support":{"url":"https://reqres.in/#support-heading","text":"To keep ReqRes free, contributions towards server costs are appreciated!"}}
Http Decode : {page: 1, per_page: 6, total: 12, total_pages: 2, data: [{id: 1, email: george.bluth@reqres.in, first_name: George, last_name: Bluth, avatar: https://reqres.in/img/faces/1-image.jpg}, {id: 2, email: janet.weaver@reqres.in, first_name: Janet, last_name: Weaver, avatar: https://reqres.in/img/faces/2-image.jpg}, {id: 3, email: emma.wong@reqres.in, first_name: Emma, last_name: Wong, avatar: https://reqres.in/img/faces/3-image.jpg}, {id: 4, email: eve.holt@reqres.in, first_name: Eve, last_name: Holt, avatar: https://reqres.in/img/faces/4-image.jpg}, {id: 5, email: charles.morris@reqres.in, first_name: Charles, last_name: Morris, avatar: https://reqres.in/img/faces/5-image.jpg}, {id: 6, email: tracey.ramos@reqres.in, first_name: Tracey, last_name: Ramos, avatar: https://reqres.in/img/faces/6-image.jpg}], support: {url: https://reqres.in/#support-heading, text: To keep ReqRes free, contributions towards server costs are appreciated!}}
결과를 보면 Http를 통해 받아온 데이터를 decode한 결과값이 Dio를 통해 받아온 결과값이다. 따라서 Dio를 통해 받아오게 되면 decode과정없이 바로 Map으로 데이터를 바꿔줄수 있다.
기능 및 사용법
그럼 Dio의 기능들을 간단한 코드로 알아보겠다.
Dio에서는 다양한 HTTP 메서드(GET, POST, PUT, DELETE 등)를 지원하며, Dio 인스턴스를 생성하여 서버에 요청을 보낼 수 있다. Options객체를 통해 header등 여러가지 옵션 설정이 가능하고 , request()를 이용할경우 HTTP 메서드를 설정 할수 있다.
var dio = Dio();
// get
final reps1 = await dio.get('https://example.com/api/searchBooks',
queryParameters: {'query': 'flutter', 'page': 5},);
// post
final reps2 = await dio.post('https://example.com/api/endpoint',
options: Options(headers: {'Authorization': 'Bearer token'},),
data: {'username': 'pyo', 'password': 'test'},);
// reqeust
final reps3 = await dio.request('https://example.com/api/endpoint',
data: {'query': 'flutter', 'page': 5},
options: Options(method: 'POST'),);
Dio를 이용할경우 options로 Default값을 설정하거나,Dio 객체를 생성할 때 생성자의 매개변수로 BaseOptions 객체를 지정하여 다양하게 설정할 수 있다. header설정도 가능하며,connectTimeout,receiveTimeout 타임 아웃을 설정할 수 있으며 baseUrl로 서버 URL의 공통 부분을 명시해 놓으면 이후 실제 서버에 요청할 때는 path 부분만 지정할 수 있다.
var dio1 = Dio();
dio.options.baseUrl = 'https://example.com/api/endpoint';
dio.options.connectTimeout = 3000;
dio.options.receiveTimeout = 3000;
var dio2 = Dio(
BaseOptions(
baseUrl: "https://example.com/api/endpoint",
connectTimeout: 3000,
receiveTimeout: 3000,
headers: {
HttpHeaders.contentTypeHeader: 'application/json',
HttpHeaders.acceptHeader: 'application/json'
},
));
Interceptor는 Dio에서 네트워크 요청과 응답을 가로채고 처리하는 중요한 기능이다. 이를 통해 에러 핸들링, 헤더 수정, 로깅, 공통 작업 처리 등을 수행할 수 있다.
다음은 기본적인 Interceptor를 Dio에 추가하는 방법이다. 이렇게 추가한 Interceptor는 모든 요청과 응답에 적용된다.
dio.interceptors.add(InterceptorsWrapper(
onRequest:(options, handler){
return handler.next(options); //continue
},
onResponse:(response,handler) {
return handler.next(response); // continue
},
onError: (DioError e, handler) {
return handler.next(e);//continue
}
));
Interceptor를 상속받는 CustomInterceptors 클래스를 만들어서 더 세밀한 제어를 할 수 있다.
import 'package:dio/dio.dart';
class CustomInterceptors extends Interceptor {
@override
void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
print('REQUEST[${options.method}] => PATH: ${options.path}');
return super.onRequest(options, handler);
}
@override
Future onResponse(Response response, ResponseInterceptorHandler handler) {
print('RESPONSE[${response.statusCode}] => PATH: ${response.request?.path}');
return super.onResponse(response, handler);
}
@override
Future onError(DioError err, ErrorInterceptorHandler handler) {
print('ERROR[${err.response?.statusCode}] => PATH: ${err.request.path}');
return super.onError(err, handler);
}
}
이러한 Custom Interceptor 클래스를 만들면 각각의 요청과 응답 단계에서 원하는 작업을 수행할 수 있다.
Interceptor를 활용하는 예시:
onRequest: 요청 전에 토큰 유무 확인 및 토큰 재발급 로직 추가
onResponse: 응답 후 공통 처리 및 Repository로 데이터 전달
onError: 오류 처리, 오류 메시지 처리 또는 모의 데이터 반환 등
Interceptor를 사용하여 Dio로 네트워크 요청을 보낼 때 요청과 응답을 효과적으로 관리하고 처리할 수 있다.
FormData는 폼 데이터를 구성하고 전송하는 데 사용되며, 주로 파일 업로드 또는 다중 파트 데이터를 처리하는 데 유용하다.
FormData formData = FormData.fromMap({
'username ': 'pyo',
'email': 'pyo@test.com',
});
formData.files.add(
MapEntry(
"profile_picture",
await MultipartFile.fromFile(
"path/to/file.jpg",
filename: "profile.jpg",
),
),
);
try {
Response response = await dio.post(
'https://example.com/api/endpoint',
data: formData,
);
// 성공적인 응답 처리
print(response.data);
} catch (e) {
// 오류 처리
print("Error: $e");
}
Dio에 대해서 공부한 내용들을 적어보았다. 이번에 Dio를 처음 사용해보았는데 이해가 잘 가지 않는 부분도 있었지만 Http를 사용했을때 보다 편했던 점도 많았던것같다. 당분간은 Dio를 계속 사용하면서 적응해 가는 시간이 필요할 것 같다.
https://velog.io/@leeeeeoy/Flutter-Dio-%EA%B0%84%EB%8B%A8-%EC%A0%95%EB%A6%AC
https://kyungsnim.net/175
https://sudarlife.tistory.com/entry/%ED%94%8C%EB%9F%AC%ED%84%B0-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-API-%ED%86%B5%EC%8B%A0%EC%97%90-%ED%8E%B8%EB%A6%AC%ED%95%9C-dio%EC%9D%98-%EA%B8%B0%EB%8A%A5%EC%A0%95%EB%A6%AC