Flutter에서는 기본적으로 Http 라이브러리를 이용하여 REST API를 연결할 수 있었는데요. 요새 아주 핫한 Dio를 이용하여 REST API를 이번 글을 통해서 해보도록 하겠습니다.
Flutter에서 Http 통신을 위한 여러 라이브러리 중 하나로 근래의 가장 인기가 많아진 라이브러리입니다. 그 이유는 간편한 사용때문인 것 같습니다.
기본적으로 백엔드는 이전에 제가 공부용으로 만든 서버를 이용하겠습니다. 해당 서버는 사람의 정보를 이용해서 CRUD를 수행하는 서버로 아래 링크를 눌러 이동하면 해당 서버의 제작글을 볼 수 있습니다.
가장 기본적으로 간편한 상태관리 라이브러리인 GetX를 통해서 MVC 구조를 만들어주겠습니다.
# flutter pub add get
프로젝트가 실행되면 바로 Controller를 사용할 수 있어야겠죠. 그래서 InitBinding을 생성하여 Binding을 수행합니다.
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return GetMaterialApp(
home: const App(),
initialBinding: InitBinding(), // 초기 바인딩
);
}
}
아래는 InitBinding 파일입니다.
class InitBinding implements Bindings {
void dependencies() {
Get.put(HumanController());
}
}
이를 통해서 앱이 실행됨과 동시에 HumanController를 사용할 수 있습니다.
Model은 DB의 Entity를 담는 그릇의 역활을 한다고 보면됩니다만, 기본적으로 통신을 수행하려면 fromJson, toJson 메소드가 필요합니다. 그래서 freezed 를 이용하여 간단하게 Model을 만들어보겠습니다.
import 'package:freezed_annotation/freezed_annotation.dart';
part 'human.freezed.dart';
part 'human.g.dart';
class Human with _$Human {
factory Human({
required int id,
required String name,
}) = _Human;
factory Human.fromJson(Map<String, dynamic> json) => _$HumanFromJson(json);
}
freezed 를 사용하는 방법 역시 아래 링크를 이동하면 확인할 수 있습니다.
저희는 가장 기본적으로 두가지 메소드만 실행해볼것입니다. 하나는 Get이고 다른 하나는 Put입니다. 이를 위해서는 Repository 를 만들어서 DB에 접근하는 Endpoint를 만들도록 하겠습니다.
class HumanRepository {
final dio = Dio();
Future<List<Human>> getCoffees() async {
try {
return dio.get("http://localhost:8080/human").then((response) {
print(response.statusCode); // 성공시 200
List<Human> humans = [];
for (var data in response.data) {
final human = Human.fromJson(data);
humans.add(human);
}
return humans;
});
} catch (e) {
throw Exception();
}
}
putCoffees(Map<String, dynamic> json) async {
try {
return dio
.put("http://localhost:8080/human/${json["id"]}", data: json)
.then((response) {
print(response.statusCode); // 성공시 201
return Human.fromJson(response.data);
});
} catch (e) {
throw Exception();
}
}
}
제가 만든 서버는 요청을 완벽하게 수행해낸다면, Get은 200을 Put은 201을 반환합니다. 해당 statusCode를 통해 오류에 대한 예외처리가 가능한 것이죠. 위 코드에서는 Dio를 이용하여 아주 간단하게 Json데이터를 가져와 Human 객체들을 만들어냅니다.
이제 대망의 Controller 생성입니다. 기본적으로 GetX를 사용하기 때문에 onReady 이벤트를 이용하여 fetchData 메소드를 수행할 것인데요. 이 경우에는 GetxController를 상속받아야 해당 기능을 사용할 수 있습니다.
class HumanController extends GetxController {
final Rx<List<Human>> _humans = Rx<List<Human>>([]);
final TextEditingController _id = TextEditingController();
final TextEditingController _name = TextEditingController();
final humanRepository = HumanRepository();
List<Human> get humans => _humans.value;
TextEditingController get id => _id;
TextEditingController get name => _name;
void onReady() {
super.onReady();
_fetchData();
}
void _fetchData() {
humanRepository.getCoffees().then((data) {
_humans.value = data;
});
}
void putData() {
final human =
Human(id: int.parse(_id.value.text), name: _name.value.text.toString());
humanRepository.putCoffees(human.toJson()).then((value) {
_humans.value.add(value);
_humans.refresh();
});
}
}
View에서는 이 모든 과정을 보여주고 Put 메소드를 실행해야 합니다. 상단의 두개의 TextField를 나머지는 ListView.builder를 이용하여 데이터를 뿌려주겠습니다.
class App extends GetView<HumanController> {
const App({super.key});
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: [
Padding(
padding: const EdgeInsets.all(8.0),
child: GestureDetector(
onTap: controller.putData, child: const Icon(Icons.add)),
)
],
),
body: Column(
children: [
TextField(
controller: controller.id,
keyboardType: TextInputType.number,
),
TextField(
controller: controller.name,
),
Expanded(
child: Obx(() => (controller.humans.isEmpty)
? const Center(
child: CircularProgressIndicator.adaptive(),
)
: ListView.builder(
itemCount: controller.humans.length,
itemBuilder: (context, index) => Obx(() {
final human = controller.humans[index];
return ListTile(
title: Text(human.id.toString()),
subtitle: Text(human.name),
);
}))),
),
],
));
}
}
위는 앱을 실행하면 바로 서버에 저장되어 있는 H2 DB에 저장되어 있는 Human 객체들을 불러옵니다. 이제 Put을 수행할 것인데요. 본래 id는 서버에서 결정하므로 이름속성만 전달하는 것이 맞으나, 결과만 보기 위해서 임의로 4라는 id와 유리를 입력하겠습니다.
이로써, Dio를 이용하여 Http 통신을 하는 법에 대해서 알아보았습니다.