EP19. API response object mapping
API Response ํํ๋ json์ผ๋ก ๋์ด์์ต๋๋ค. Flutter์์ json์ ๊ฒฐ๊ตญ string ํ์
์ผ๋ก API Response๋ฅผ ํ์ฉํ๊ธฐ ์ํด์๋ Flutter์ Object๋ก ๋ณํ/๋งคํํ๋ ๊ณผ์ ์ด ํ์ํฉ๋๋ค.
ex) Response
{
"code": 10000,
"message": "Success",
"result": {
"fcmTokens": [
"token_1",
"token_2",
"token_3",
"token_4"
]
}
}
JSON
๋ณต์ฌ
1. ๊ณตํต API Response Format Class ๊ตฌํ
ํจํค์ง lib/network๋๋ ํ ๋ฆฌ ํ์์ response_format.dartํ์ผ์ ์ถ๊ฐํฉ๋๋ค.
/// API ์๋ต ํ์์ ์ ์ํ๋ ํด๋์ค.
/// ์ ๋ค๋ฆญ ํ์
T๋ JSON์ผ๋ก๋ถํฐ ํ์ฑ๋ ๊ฐ์ฒด ํ์
์ ๋ํ๋
๋๋ค.
class ResponseFormat<T> {
/// ์๋ต ์ฝ๋.
final int code;
/// ์๋ต ๋ฉ์์ง.
final String message;
/// ์๋ต ๊ฒฐ๊ณผ. ์ ๋ค๋ฆญ ํ์
T ๋๋ null์ผ ์ ์์ต๋๋ค.
final T? result;
/// ์์ฑ์.
ResponseFormat({
required this.code,
required this.message,
this.result,
});
/// JSON์ผ๋ก๋ถํฐ ResponseFormat ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ ํฉํ ๋ฆฌ ์์ฑ์.
/// [json]์ ์๋ต JSON ๊ฐ์ฒด์ด๋ฉฐ, [fromJsonT]๋ ์ ๋ค๋ฆญ ํ์
T๋ฅผ ํ์ฑํ๋ ํจ์์
๋๋ค.
factory ResponseFormat.fromJson(Map<String, dynamic> json, T Function(Map<String, dynamic>) fromJsonT) {
T? parsedResult;
if (json['result'] != null) {
parsedResult = fromJsonT(json['result']);
} else {
parsedResult = null;
}
return ResponseFormat(
code: json['code'],
message: json['message'],
result: parsedResult,
);
}
}
/// JSON ์๋ต์์ ๊ฒฐ๊ณผ๊ฐ ์๋ ๊ฒฝ์ฐ๋ฅผ ๋ํ๋ด๋ ํด๋์ค.
class EmptyResult {
/// ์์ฑ์.
EmptyResult();
/// JSON์ผ๋ก๋ถํฐ EmptyResult ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ ํฉํ ๋ฆฌ ์์ฑ์.
/// [json]์ ์๋ต JSON ๊ฐ์ฒด์
๋๋ค.
factory EmptyResult.fromJson(Map<String, dynamic> json) {
return EmptyResult();
}
}
Dart
๋ณต์ฌ
2. APIClient ํ์ฑ ๋ก์ง ๊ตฌํ
import 'dart:convert';
import 'package:http/http.dart' as http;
+import 'package:swm_flutter_package/network/response_format.dart';
import 'package:swm_flutter_package/services/auth_service.dart';
class ApiClient {
@@ -21,46 +22,47 @@ class ApiClient {
// GET ์์ฒญ์ ์ฒ๋ฆฌํ๋ ๋ฉ์๋์
๋๋ค.
// ์ง์ ๋ endpoint๋ก GET ์์ฒญ์ ๋ณด๋ด๊ณ ์๋ต์ ์ฒ๋ฆฌํฉ๋๋ค.
- Future<String> get(String endpoint) async {
+ Future<ResponseFormat<T>> get<T>(String endpoint, T Function(Map<String, dynamic>) fromJsonT) async {
final headers = _getHeaders();
final response = await http.get(
Uri.parse('$baseUrl$endpoint'),
headers: headers,
);
- return _handleResponse(response);
+ return _handleResponse<T>(response, fromJsonT);
}
// POST ์์ฒญ์ ์ฒ๋ฆฌํ๋ ๋ฉ์๋์
๋๋ค.
// ์ง์ ๋ endpoint๋ก POST ์์ฒญ์ ๋ณด๋ด๊ณ ์๋ต์ ์ฒ๋ฆฌํฉ๋๋ค.
- Future<String> post(String endpoint, Object? body) async {
+ Future<ResponseFormat<T>> post<T>(String endpoint, Object? body, T Function(Map<String, dynamic>) fromJsonT) async {
final headers = _getHeaders();
final response = await http.post(
Uri.parse('$baseUrl$endpoint'),
headers: headers,
body: jsonEncode(body),
);
- return _handleResponse(response);
+ return _handleResponse<T>(response, fromJsonT);
}
// DELETE ์์ฒญ์ ์ฒ๋ฆฌํ๋ ๋ฉ์๋์
๋๋ค.
// ์ง์ ๋ endpoint๋ก DELETE ์์ฒญ์ ๋ณด๋ด๊ณ ์๋ต์ ์ฒ๋ฆฌํฉ๋๋ค.
- Future<String> delete(String endpoint, Object? body) async {
+ Future<ResponseFormat<T>> delete<T>(String endpoint, Object? body, T Function(Map<String, dynamic>) fromJsonT) async {
final headers = _getHeaders();
final response = await http.delete(
Uri.parse('$baseUrl$endpoint'),
headers: headers,
body: jsonEncode(body),
);
- return _handleResponse(response);
+ return _handleResponse<T>(response, fromJsonT);
}
// HTTP ์๋ต์ ์ฒ๋ฆฌํ๋ ๋ฉ์๋์
๋๋ค.
// ์ํ ์ฝ๋๊ฐ 200-299์ผ ๊ฒฝ์ฐ ์๋ต ๋ณธ๋ฌธ์ ๋ฐํํ๊ณ , ๊ทธ๋ ์ง ์์ผ๋ฉด ์์ธ๋ฅผ ๋ฐ์์ํต๋๋ค.
- String _handleResponse(http.Response response) {
+ ResponseFormat<T> _handleResponse<T>(http.Response response, T Function(Map<String, dynamic>) fromJsonT) {
if (response.statusCode >= 200 && response.statusCode < 300) {
- return response.body;
+ final jsonResponse = jsonDecode(response.body) as Map<String, dynamic>;
+ return ResponseFormat<T>.fromJson(jsonResponse, fromJsonT);
} else {
- throw Exception('Failed to load data: ${response.statusCode}');
+ return ResponseFormat(code: -999, message: "Parsing Error", result: null);
}
}
}
Diff
๋ณต์ฌ
โข
fromJson ์์ฑ์๋ฅผ ํ๋ผ๋ฏธํฐ๋ก ์ ๋ฌ๋ฐ์ ์ฌ์ฉํ๋๋ก ๋ณ๊ฒฝํฉ๋๋ค.
โข
์ ์ฒด ์ฝ๋ ์ฒจ๋ถ
3. APIClient๋ฅผ ์ฌ์ฉํ๋ ๊ณณ์์์ Result ํ์ ์ ๋ฌ
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_package_sample/views/home/home_view_model.dart';
import 'package:provider/provider.dart';
import 'package:swm_flutter_package/network/api_client.dart';
+import 'package:swm_flutter_package/network/response_format.dart';
class HomeView extends StatelessWidget {
HomeView({super.key});
@@ -55,7 +56,7 @@ class HomeView extends StatelessWidget {
ElevatedButton(
onPressed: () {
// '์ถ๊ฐ' ๋ฒํผ์ ๋๋ ์ ๋ API์ POST ์๋ํฌ์ธํธ๋ฅผ ํธ์ถ
- apiClient.post('/users/v1/fcmToken', {'fcmToken': textController.text})
+ apiClient.post<EmptyResult>('/users/v1/fcmToken', {'fcmToken': textController.text}, (json) => EmptyResult.fromJson(json))
.then((value) => print(value)) // ์๋ต์ ์ถ๋ ฅ
.catchError((error) => print(error)); // ์๋ฌ๋ฅผ ์ถ๋ ฅ
},
@@ -65,7 +66,7 @@ class HomeView extends StatelessWidget {
ElevatedButton(
onPressed: () {
// '์ญ์ ' ๋ฒํผ์ ๋๋ ์ ๋ API์ DELETE ์๋ํฌ์ธํธ๋ฅผ ํธ์ถ
- apiClient.delete('/users/v1/fcmToken', {'fcmToken': textController.text})
+ apiClient.delete<EmptyResult>('/users/v1/fcmToken', {'fcmToken': textController.text}, (json) => EmptyResult.fromJson(json))
.then((value) => print(value)) // ์๋ต์ ์ถ๋ ฅ
.catchError((error) => print(error)); // ์๋ฌ๋ฅผ ์ถ๋ ฅ
},
@@ -75,8 +76,8 @@ class HomeView extends StatelessWidget {
ElevatedButton(
onPressed: () {
// '์กฐํ' ๋ฒํผ์ ๋๋ ์ ๋ API์ GET ์๋ํฌ์ธํธ๋ฅผ ํธ์ถ
- apiClient.get('/users/v1/fcmToken')
- .then((value) => print(value)) // ์๋ต์ ์ถ๋ ฅ
+ apiClient.get<FCMTokensResult>('/users/v1/fcmToken', (json) => FCMTokensResult.fromJson(json))
+ .then((value) => print(value.result?.tokens)) // ์๋ต์ ์ถ๋ ฅ
.catchError((error) => print(error)); // ์๋ฌ๋ฅผ ์ถ๋ ฅ
},
child: Text('์กฐํ'),
@@ -88,4 +89,14 @@ class HomeView extends StatelessWidget {
),
);
}
+}
+
+class FCMTokensResult {
+ final List<String> tokens;
+
+ FCMTokensResult({required this.tokens});
+
+ factory FCMTokensResult.fromJson(Map<String, dynamic> json) {
+ return FCMTokensResult(tokens: List<String>.from(json['fcmTokens']));
+ }
}
Diff
๋ณต์ฌ
โข
APIClient๋ฅผ ํ์ฉํด request๋ฅผ ๋ง๋ค๋ Resultํ์
์ ์ ๋ค๋ฆญ์ผ๋ก ์ ๋ฌํฉ๋๋ค.
โข
์ ๋ฌํ ํ์
์ด Response Result ํ์
์ผ๋ก ์ ์ฉ๋์ด ๋ฐํ๋ฉ๋๋ค.
โข
์ ์ฒด ์ฝ๋ ์ฒจ๋ถ