프로젝트 하면, Repository 를 만드는 경우가 생긴다.
그 이유와 사용법을 알아보자!
https://pub.dev/packages/retrofit
api 요청부터 모델을 만드는 과정을 전부다 자동화로 만들자! 그래서 이 패키지가 나온것이다.
api 요청 부분 두군데를 비교해보곘다
반환 받는 값이랑 요청 URL 전환되는 모데일이 다르다.
과정은 같다.
1. api 요청을 한다.
2. api 응답을 받는다.
3. 모델로 변경한다. (해당모델의 constructor 인 fromJson()을 사용해서!)
4. 다른값들을 추가로 전달해준다.
사실은 repository 라고 하기엔 많이 부족하지만, 이렇게 명명하자
retrofit 의 stardard 를 맞춰서 작성을 해주도록 하자
retrofit 은 api 에서 실제로 받는 응답 형태와 클래스의 반환값에 완전히 똑같은 형태를 넣어줘야한다는 것이 굉장히 중요하다!
그러면 자동으로 JSON 형태의 값이 매핑되어 클래스의 인스턴스가 된다.
import 'package:authentication_study/restaurant/model/restaurant_detail_model.dart';
import 'package:dio/dio.dart';
import 'package:retrofit/retrofit.dart';
part 'restaurant_repository.g.dart';
()
abstract class RestaurantRepository {
// baseURL = http://$ip/restaurant;
factory RestaurantRepository(Dio dio, {String baseUrl}) =
_RestaurantRepository;
// http://$ip/restaurant/;
// @GET("/")
// paginate();
// http://$ip/restaurant/:id;
("/{id}")
Future<RestaurantDetailModel> getRestaurantDetail({
// id 변수를 매변 요청할 때마다 넣어줘야하는데, @Path 어노테이션으로 정의해주면 된다.
() required String id,
});
}
1) 자동으로 getRestaurantDetail 함수가 생긴걸 볼 수 있다.
2) dio 로 fetch 를 하는데, Options 를 보면 GET, headers 담아서, extra 까지 보내고, '/id' 값을 넣는다.
3) 응답이 오면 RestaurantDetailModel.fromJson 에 자동으로 매핑을 하고
value 에 담아서 리턴해준다.
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'restaurant_repository.dart';
// **************************************************************************
// RetrofitGenerator
// **************************************************************************
// ignore_for_file: unnecessary_brace_in_string_interps,no_leading_underscores_for_local_identifiers
class _RestaurantRepository implements RestaurantRepository {
_RestaurantRepository(
this._dio, {
this.baseUrl,
});
final Dio _dio;
String? baseUrl;
// 1) 설명 참조
Future<RestaurantDetailModel> getRestaurantDetail({required id}) async {
const _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{};
final _headers = <String, dynamic>{};
final _data = <String, dynamic>{};
// 2) 설명 참조
final _result = await _dio.fetch<Map<String, dynamic>>(
_setStreamType<RestaurantDetailModel>(Options(
method: 'GET',
headers: _headers,
extra: _extra,
)
.compose(
_dio.options,
'/${id}',
queryParameters: queryParameters,
data: _data,
)
.copyWith(baseUrl: baseUrl ?? _dio.options.baseUrl)));
// 3) 설명 참조
final value = RestaurantDetailModel.fromJson(_result.data!);
return value;
}
RequestOptions _setStreamType<T>(RequestOptions requestOptions) {
if (T != dynamic &&
!(requestOptions.responseType == ResponseType.bytes ||
requestOptions.responseType == ResponseType.stream)) {
if (T == String) {
requestOptions.responseType = ResponseType.plain;
} else {
requestOptions.responseType = ResponseType.json;
}
}
return requestOptions;
}
}
api 통신하는 부분을 다 지우고 retrofit 에 맞게 적용을 해주자!
저렇게 긴 코드가 2줄로 줄어들고, g.dart 파일에 자동으로 로직이 추가된다.
repository.dart 파일에 retrofit 으로 선언했으니 맞게 변경을 해준다.
설명 1 )
api 요청 반환타입에 맞춰서 타입도 변경을 해준다.
삭제 ! )
이 뷰단에서 매핑하는 함수를 호출할 필요가 없다. 왜냐, g.dart 에서 retrofit 이 다~ 생성이 했기 떄문이다.
액세스 토큰 오류로 401 에러가 나니깐 우선은 강제로 헤더를 넣어주자
// headers 를 두군데에서 가져온다고 한다면 하나를 숨겨주자
import 'package:dio/dio.dart' hide Headers;
("/{id}")
// header 강제로 넣기
({
'authorization': 'Bearer ~~~ 액세스 토큰 넣어준다.'
})
...
원래는 api 요청을 하고, 응답 받아서 매핑하는 과정을 2줄로 줄여서 작업할 수있다. 나머지 작업들은 g.dart 파일에 로직이 다 구현이 되어있다.
많이 도움 됐습니다~