Dart에서 HTTP 요청을 보내는 작업은 http
패키지를 사용하여 이루어진다. 이 패키지는 pub.dev
에서 제공되며, 매우 간단한 방식으로 HTTP 클라이언트 기능을 사용할 수 있다. 주로 REST API와 같은 서버 통신에 많이 사용된다.
1. http
패키지 설치
먼저 http
패키지를 설치해야 한다. 이는 Dart의 패키지 관리자 pub
을 통해 간단히 설치할 수 있다. 프로젝트의 pubspec.yaml
파일에 다음 내용을 추가한다.
dependencies:
http: ^0.13.0
그 후, pub get
명령을 실행하여 패키지를 설치한다.
2. HTTP GET 요청 보내기
HTTP GET 요청은 서버로부터 데이터를 가져오는 데 사용된다. 예를 들어, 특정 API에서 데이터를 받아오는 경우, http.get
메소드를 사용한다. Dart에서 HTTP GET 요청을 보내는 기본 구조는 아래와 같다.
import 'package:http/http.dart' as http;
void fetchData() async {
final response = await http.get(Uri.parse('https://example.com/data'));
if (response.statusCode == 200) {
// 성공적으로 데이터를 받아왔을 때
print(response.body);
} else {
// 오류가 발생했을 때
print('Failed to load data');
}
}
코드 설명
http.get
메소드를 통해 GET 요청을 보낸다. 이 메소드는 비동기 함수이므로await
키워드를 사용하여 요청이 완료될 때까지 기다린다.Uri.parse
는 문자열 URL을Uri
객체로 변환한다.- 요청이 성공하면 상태 코드가
200
일 때 데이터를 출력하며, 실패한 경우에는 오류 메시지를 출력한다.
3. HTTP POST 요청 보내기
HTTP POST 요청은 주로 서버에 데이터를 전송할 때 사용된다. 이는 사용자 로그인, 데이터 업로드 등의 작업에 주로 활용된다. POST 요청을 보내는 기본 구조는 다음과 같다.
import 'package:http/http.dart' as http;
void sendData() async {
final response = await http.post(
Uri.parse('https://example.com/data'),
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
},
body: '{"name": "John", "age": 30}',
);
if (response.statusCode == 200) {
// 성공적으로 데이터를 전송했을 때
print('Data sent successfully');
} else {
// 오류가 발생했을 때
print('Failed to send data');
}
}
코드 설명
http.post
메소드를 사용하여 POST 요청을 보낸다. GET 요청과 달리, POST 요청은 데이터(body
)를 서버로 전송한다.headers
를 통해 요청의 타입을 지정하며, 주로application/json
형식으로 데이터를 전송한다.- 요청의 응답 상태 코드를 확인하여 요청이 성공했는지 실패했는지를 판별한다.
4. Query Parameters 사용
GET 요청을 보낼 때, URL에 직접 파라미터를 추가하여 서버로 데이터를 보낼 수 있다. 이를 Query Parameters
라고 한다. Dart에서 Query Parameters를 추가하는 방법은 Uri
객체를 사용하는 것이다.
import 'package:http/http.dart' as http;
void fetchDataWithParams() async {
final uri = Uri.https('example.com', '/data', {'name': 'John', 'age': '30'});
final response = await http.get(uri);
if (response.statusCode == 200) {
print(response.body);
} else {
print('Failed to load data');
}
}
코드 설명
Uri.https
메소드를 통해 HTTPS URI를 생성하며, Query Parameters를 Map 형태로 전달할 수 있다.Uri
객체를 사용하여 GET 요청을 보내고, 서버로부터 데이터를 받아온다.
5. 비동기 처리
Dart에서는 비동기 처리로 인해 코드가 멈추지 않고 동작할 수 있게 설계되어 있다. 이로 인해 HTTP 요청은 비동기적으로 실행되며, 요청이 완료될 때까지 다른 작업을 수행할 수 있다.
비동기 HTTP 요청은 Dart의 Future
클래스를 사용하여 처리된다. await
키워드를 사용하면 비동기 함수를 호출하고 그 함수가 완료될 때까지 대기할 수 있다. Dart에서 비동기 HTTP 요청을 효과적으로 처리하기 위한 방법은 다음과 같다.
Future<void> fetchData() async {
try {
final response = await http.get(Uri.parse('https://example.com/data'));
if (response.statusCode == 200) {
print('Data fetched: ${response.body}');
} else {
print('Failed to load data');
}
} catch (e) {
print('Error occurred: $e');
}
}
코드 설명
- 비동기 처리를 위한
async
와await
를 사용한다. try-catch
구문을 통해 오류가 발생할 경우 적절히 처리할 수 있다.
6. HTTP 헤더 추가하기
HTTP 요청을 보낼 때, 헤더에 추가 정보를 포함할 수 있다. 이 정보는 서버가 요청을 처리하는 데 필요한 메타데이터를 제공하는 데 사용된다. 예를 들어, Content-Type
헤더를 추가하여 요청 본문이 어떤 형식인지 서버에 알리거나, 인증 토큰을 포함할 수 있다.
import 'package:http/http.dart' as http;
void sendRequestWithHeaders() async {
final response = await http.get(
Uri.parse('https://example.com/data'),
headers: <String, String>{
'Authorization': 'Bearer your_api_token_here',
'Custom-Header': 'CustomHeaderValue',
},
);
if (response.statusCode == 200) {
print('Request successful: ${response.body}');
} else {
print('Request failed: ${response.statusCode}');
}
}
코드 설명
headers
파라미터는 Map 형태로 작성되며,key
가 헤더의 이름,value
가 헤더의 값이다.- 여기서는
Authorization
헤더에 Bearer 토큰을 포함하여 인증을 수행한다. Custom-Header
라는 이름의 커스텀 헤더를 추가할 수도 있다.
7. JSON 데이터 처리
서버와 통신할 때 가장 흔히 사용되는 데이터 형식은 JSON이다. Dart에서는 dart:convert
패키지의 jsonEncode
와 jsonDecode
함수를 사용하여 JSON 데이터를 처리한다.
JSON 데이터를 사용한 POST 요청
POST 요청을 보내면서 JSON 데이터를 서버로 전송할 때는 Content-Type
헤더를 application/json
으로 설정하고, 본문을 JSON 형식으로 인코딩해야 한다.
import 'dart:convert';
import 'package:http/http.dart' as http;
void sendPostWithJson() async {
final response = await http.post(
Uri.parse('https://example.com/data'),
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
},
body: jsonEncode(<String, String>{
'name': 'John',
'age': '30',
}),
);
if (response.statusCode == 200) {
print('Data sent successfully: ${response.body}');
} else {
print('Failed to send data: ${response.statusCode}');
}
}
JSON 데이터 디코딩
서버에서 JSON 응답을 받을 때는 jsonDecode
함수를 사용하여 Dart 객체로 변환할 수 있다.
import 'dart:convert';
import 'package:http/http.dart' as http;
void fetchAndDecodeJson() async {
final response = await http.get(Uri.parse('https://example.com/data'));
if (response.statusCode == 200) {
final Map<String, dynamic> data = jsonDecode(response.body);
print('Name: ${data['name']}');
print('Age: ${data['age']}');
} else {
print('Failed to load data');
}
}
코드 설명
jsonEncode
함수는 Map을 JSON 문자열로 변환하여 POST 요청의 본문으로 전송한다.jsonDecode
함수는 JSON 응답을 Map이나 List와 같은 Dart 객체로 변환한다.jsonEncode
와jsonDecode
를 통해 Dart 객체와 JSON 간의 상호 변환을 쉽게 처리할 수 있다.
8. Multipart 요청
Multipart 요청은 파일을 업로드할 때 자주 사용된다. Dart에서는 http.MultipartRequest
클래스를 사용하여 이러한 요청을 처리할 수 있다. 이 방법을 사용하면 이미지나 문서와 같은 파일을 서버로 업로드할 수 있다.
import 'dart:io';
import 'package:http/http.dart' as http;
void uploadFile() async {
var request = http.MultipartRequest(
'POST',
Uri.parse('https://example.com/upload'),
);
request.files.add(
await http.MultipartFile.fromPath(
'file',
'/path/to/file.jpg',
),
);
var response = await request.send();
if (response.statusCode == 200) {
print('File uploaded successfully');
} else {
print('Failed to upload file');
}
}
코드 설명
http.MultipartRequest
는 파일을 포함한 POST 요청을 보낼 수 있게 해준다.http.MultipartFile.fromPath
메소드를 사용하여 파일을 요청에 추가한다.request.send
메소드를 통해 요청을 전송한다. 응답은StreamedResponse
로 제공되며, 성공 시 파일 업로드가 완료된다.
9. HTTP 요청의 상태 코드 처리
HTTP 요청을 보낼 때, 서버는 다양한 상태 코드로 응답한다. 이 상태 코드를 확인함으로써 요청이 성공했는지, 아니면 실패했는지 판별할 수 있다. 상태 코드는 3자리 숫자로 표현되며, 다음과 같은 카테고리로 나뉜다.
- 1xx: 정보 응답 (처리 중)
- 2xx: 성공 (요청 성공)
- 3xx: 리다이렉션 (다른 페이지로 리디렉션)
- 4xx: 클라이언트 오류 (잘못된 요청)
- 5xx: 서버 오류 (서버 내부 오류)
Dart에서 HTTP 요청을 처리할 때, response.statusCode
를 통해 상태 코드를 확인할 수 있다.
import 'package:http/http.dart' as http;
void checkStatusCode() async {
final response = await http.get(Uri.parse('https://example.com/data'));
if (response.statusCode == 200) {
print('Request successful');
} else if (response.statusCode == 404) {
print('Resource not found');
} else if (response.statusCode == 500) {
print('Server error');
} else {
print('Unexpected status code: ${response.statusCode}');
}
}
코드 설명
response.statusCode
로 서버의 응답 상태 코드를 확인할 수 있다.- 상태 코드에 따라 적절한 처리를 할 수 있도록 분기문을 사용한다. 예를 들어,
200
은 요청 성공,404
는 리소스를 찾을 수 없음을 의미한다.
10. HTTP 요청에 Timeout 적용하기
네트워크 상황에 따라 HTTP 요청이 지연되거나 응답이 없을 수 있다. 이런 경우를 대비해, 요청에 타임아웃을 설정할 수 있다. Dart에서는 timeout
메소드를 사용하여 요청이 일정 시간 내에 완료되지 않으면 타임아웃을 발생시킨다.
import 'package:http/http.dart' as http;
void fetchDataWithTimeout() async {
try {
final response = await http.get(Uri.parse('https://example.com/data'))
.timeout(const Duration(seconds: 5));
if (response.statusCode == 200) {
print('Data fetched successfully');
} else {
print('Failed to load data: ${response.statusCode}');
}
} catch (e) {
print('Request timed out: $e');
}
}
코드 설명
timeout
메소드를 사용하여 요청이 5초 내에 완료되지 않으면 타임아웃을 발생시킨다.try-catch
구문을 통해 타임아웃이나 기타 네트워크 오류가 발생할 때 적절한 예외 처리를 수행한다.
11. Stream을 사용한 대규모 데이터 처리
HTTP 요청을 통해 대규모 데이터를 다운로드할 경우, 모든 데이터를 한 번에 메모리로 로드하는 대신, Stream
을 사용하여 데이터를 점진적으로 처리할 수 있다. Dart의 StreamedResponse
객체는 이러한 데이터 흐름을 처리하는 데 사용된다.
import 'dart:convert';
import 'package:http/http.dart' as http;
void fetchLargeData() async {
var request = http.Request('GET', Uri.parse('https://example.com/large-data'));
var response = await request.send();
if (response.statusCode == 200) {
await for (var bytes in response.stream.transform(utf8.decoder)) {
print('Received chunk: $bytes');
}
} else {
print('Failed to fetch data');
}
}
코드 설명
http.Request
객체를 사용하여 요청을 생성하고,request.send()
를 호출하여 데이터를 스트리밍한다.response.stream.transform(utf8.decoder)
를 통해 응답 스트림을 UTF-8로 디코딩한다.await for
구문을 사용하여 데이터가 부분적으로 수신될 때마다 처리할 수 있다.
12. Dart에서 HTTPS 인증서 검사 우회
Dart에서 HTTPS 요청을 보낼 때, 기본적으로 인증서 검사가 수행된다. 하지만 테스트 환경 등에서 인증서 검사를 우회하고자 할 때는 Dart의 HttpClient
를 커스터마이징하여 처리할 수 있다. 이 방법은 보안상 위험할 수 있으므로 실제 운영 환경에서는 권장되지 않는다.
import 'dart:io';
import 'package:http/http.dart' as http;
class CustomHttpOverrides extends HttpOverrides {
@override
HttpClient createHttpClient(SecurityContext? context) {
return super.createHttpClient(context)
..badCertificateCallback = (X509Certificate cert, String host, int port) => true;
}
}
void main() {
HttpOverrides.global = CustomHttpOverrides();
fetchData();
}
void fetchData() async {
final response = await http.get(Uri.parse('https://example.com'));
if (response.statusCode == 200) {
print('Request successful');
} else {
print('Failed to load data');
}
}
코드 설명
HttpOverrides
를 상속받은 클래스를 정의하고,createHttpClient
메소드를 오버라이드하여badCertificateCallback
을 통해 모든 인증서를 신뢰하도록 설정한다.HttpOverrides.global
에 이 설정을 적용하여, 이후의 모든 HTTP 요청에 대해 이 설정이 적용된다.