공식 문서 : dio | Dart package
Dio는 Dart로 작성된 HTTP 클라이언트 라이브러리로, Flutter 애플리케이션에서 서버와의 통신을 쉽게 관리할 수 있도록 설계되었습니다. Dio는 동기 및 비동기 요청, 인터셉터, 글로벌 설정, 요청 취소, 파일 업로드 및 다운로드 등 다양한 기능을 제공합니다.
특징 설명
인터셉터 | 요청, 응답, 오류를 가로채어 추가 로직을 실행할 수 있습니다. 예: 로깅, 인증 토큰 추가 등. |
글로벌 설정 | 기본 URL, 타임아웃, 헤더 등 모든 요청에 공통적으로 적용되는 설정을 관리할 수 있습니다. |
요청 취소 | 필요에 따라 진행 중인 요청을 취소할 수 있습니다. |
파일 업로드 및 다운로드 | 멀티파트 폼 데이터 업로드 및 파일 다운로드를 간편하게 처리할 수 있습니다. |
재시도 로직 | 실패한 요청을 자동으로 재시도할 수 있는 기능을 지원합니다. |
타입 지원 | JSON, FormData 등 다양한 데이터 타입을 지원합니다. |
인터넷 연결 상태 체크 | 네트워크 상태에 따라 요청을 처리할 수 있는 기능을 제공합니다. |
미들웨어 지원 | 요청 처리 과정에 미들웨어를 추가하여 유연하게 요청을 조작할 수 있습니다. |
pubspec.yaml 파일에 Dio 패키지를 추가합니다.
dependencies:
dio: ^5.3.2 # 최신 버전 확인 후 적용
터미널에서 다음 명령어를 실행하여 패키지를 설치합니다.
flutter pub get
Dio를 사용하기 위해 인스턴스를 생성합니다. 글로벌 설정을 적용할 수도 있습니다.
ㅑmport 'package:dio/dio.dart';
final dio = Dio(); // 기본 인스턴스 생성
// 글로벌 설정 예시
final dio = Dio(
BaseOptions(
baseUrl: '<https://api.example.com/>', // 기본 URL 설정
connectTimeout: 5000, // 연결 타임아웃 (밀리초)
receiveTimeout: 3000, // 응답 타임아웃 (밀리초)
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
),
);
import 'package:dio/dio.dart';
void fetchData() async {
try {
Response response = await dio.get('/users'); // 예: <https://api.example.com/users>
print(response.data);
} catch (e) {
print('Error: $e');
}
}
import 'package:dio/dio.dart';
void createUser() async {
try {
Response response = await dio.post(
'/users',
data: {
'name': 'John Doe',
'email': 'john.doe@example.com',
},
);
print(response.data);
} catch (e) {
print('Error: $e');
}
}
// PUT 요청
void updateUser() async {
try {
Response response = await dio.put(
'/users/1',
data: {
'name': 'Jane Doe',
},
);
print(response.data);
} catch (e) {
print('Error: $e');
}
}
// DELETE 요청
void deleteUser() async {
try {
Response response = await dio.delete('/users/1');
print(response.data);
} catch (e) {
print('Error: $e');
}
}
인터셉터는 요청과 응답을 가로채어 추가 작업을 수행할 수 있게 합니다.
dio.interceptors.add(
InterceptorsWrapper(
onRequest: (options, handler) {
print('REQUEST[${options.method}] => PATH: ${options.path}');
return handler.next(options); // 요청 계속 진행
},
onResponse: (response, handler) {
print('RESPONSE[${response.statusCode}] => DATA: ${response.data}');
return handler.next(response); // 응답 계속 진행
},
onError: (DioError e, handler) {
print('ERROR[${e.response?.statusCode}] => MESSAGE: ${e.message}');
return handler.next(e); // 에러 계속 진행
},
),
);
특정 요청을 취소할 수 있습니다.
import 'package:dio/dio.dart';
void fetchDataWithCancel() async {
CancelToken cancelToken = CancelToken();
try {
Response response = await dio.get(
'/long-request',
cancelToken: cancelToken,
);
print(response.data);
} catch (e) {
if (CancelToken.isCancel(e)) {
print('Request canceled! ${e.message}');
} else {
print('Error: $e');
}
}
// 요청을 취소하려면
cancelToken.cancel('Request canceled by user.');
}
멀티파트 폼 데이터로 파일을 업로드할 수 있습니다.
import 'package:dio/dio.dart';
import 'dart:io';
void uploadFile() async {
String filePath = '/path/to/file.jpg';
File file = File(filePath);
FormData formData = FormData.fromMap({
'file': await MultipartFile.fromFile(file.path, filename: 'upload.jpg'),
'description': 'File upload example',
});
try {
Response response = await dio.post(
'/upload',
data: formData,
onSendProgress: (int sent, int total) {
print('$sent / $total');
},
);
print(response.data);
} catch (e) {
print('Error: $e');
}
}
파일을 다운로드하고 진행 상황을 추적할 수 있습니다.
import 'package:dio/dio.dart';
import 'dart:io';
void downloadFile() async {
String url = '<https://example.com/file.zip>';
String savePath = '/path/to/save/file.zip';
try {
Response response = await dio.download(
url,
savePath,
onReceiveProgress: (int received, int total) {
if (total != -1) {
print('${(received / total * 100).toStringAsFixed(0)}%');
}
},
);
print('File downloaded: ${response.statusCode}');
} catch (e) {
print('Error: $e');
}
}
요청이 실패했을 때 자동으로 재시도할 수 있습니다. 이를 위해 인터셉터를 사용하여 구현할 수 있습니다.
import 'package:dio/dio.dart';
class RetryInterceptor extends Interceptor {
final Dio dio;
final int retries;
RetryInterceptor({required this.dio, this.retries = 3});
@override
void onError(DioError err, ErrorInterceptorHandler handler) async {
if (retries > 0 && _shouldRetry(err)) {
try {
// 재시도
Response response = await dio.request(
err.requestOptions.path,
options: Options(
method: err.requestOptions.method,
headers: err.requestOptions.headers,
),
data: err.requestOptions.data,
queryParameters: err.requestOptions.queryParameters,
);
return handler.resolve(response);
} catch (e) {
return handler.next(err);
}
}
return handler.next(err);
}
bool _shouldRetry(DioError err) {
// 재시도 조건 설정 (예: 네트워크 오류, 서버 오류 등)
return err.type == DioErrorType.other || err.type == DioErrorType.connectTimeout;
}
}
// 인터셉터 추가
dio.interceptors.add(RetryInterceptor(dio: dio, retries: 3));
Dio는 다양한 에러 유형을 지원하며, 이를 통해 세밀한 에러 처리가 가능합니다.
에러 유형 설명
DioErrorType.connectTimeout | 연결 타임아웃 오류 |
DioErrorType.sendTimeout | 데이터 전송 타임아웃 오류 |
DioErrorType.receiveTimeout | 응답 수신 타임아웃 오류 |
DioErrorType.response | 서버로부터 유효하지 않은 응답을 받았을 때 발생 |
DioErrorType.cancel | 요청이 취소되었을 때 발생 |
DioErrorType.other | 기타 네트워크 오류 (예: DNS 문제, 소켓 예외 등) |
void fetchDataWithErrorHandling() async {
try {
Response response = await dio.get('/endpoint');
print(response.data);
} on DioError catch (e) {
if (e.type == DioErrorType.connectTimeout) {
print('Connection Timeout Exception');
} else if (e.type == DioErrorType.sendTimeout) {
print('Send Timeout Exception');
} else if (e.type == DioErrorType.receiveTimeout) {
print('Receive Timeout Exception');
} else if (e.type == DioErrorType.response) {
print('Received invalid status code: ${e.response?.statusCode}');
} else if (e.type == DioErrorType.cancel) {
print('Request to API server was cancelled');
} else {
print('Unexpected error: $e');
}
} catch (e) {
print('Something went wrong: $e');
}
}
Dio는 요청을 취소할 수 있는 기능을 제공합니다. 이를 통해 사용자가 필요하지 않은 요청을 취소할 수 있습니다.
import 'package:dio/dio.dart';
void fetchDataWithCancel() async {
CancelToken cancelToken = CancelToken();
try {
Response response = await dio.get(
'/long-request',
cancelToken: cancelToken,
);
print(response.data);
} catch (e) {
if (CancelToken.isCancel(e)) {
print('Request canceled! ${e.message}');
} else {
print('Error: $e');
}
}
// 특정 시점에 요청을 취소하려면
// cancelToken.cancel('Request canceled by user.');
}
예를 들어, 사용자가 화면을 떠날 때 진행 중인 요청을 취소할 수 있습니다.
class MyWidget extends StatefulWidget {
@override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
CancelToken? _cancelToken;
@override
void dispose() {
_cancelToken?.cancel('Widget disposed');
super.dispose();
}
void fetchData() async {
_cancelToken = CancelToken();
try {
Response response = await dio.get(
'/endpoint',
cancelToken: _cancelToken,
);
print(response.data);
} catch (e) {
if (CancelToken.isCancel(e)) {
print('Request canceled! ${e.message}');
} else {
print('Error: $e');
}
}
}
@override
Widget build(BuildContext context) {
return Container(
// UI 구성
);
}
}
[Flutter] Webview 라이브러리 - webview_flutter (1) | 2025.01.03 |
---|---|
[Flutter] 카메라 및 이미지 라이브러리 - ImagePicker (1) | 2024.12.19 |
[Flutter] API Key나 다른 민감 정보 분리 - DOTENV (0) | 2024.12.16 |