[Flutter] Restaurant Pagination API 작업

겨레·2024년 7월 19일
0

CustomInterceptor를 사용해서 실제 detail 정보를 가져오기에 성공함!
마찬가지로 RestaurantScreen의 dio에도 같은 과정을 진행해보자!


① dio에 Interceptor 추가하기
restaurant_screen.dart로 가서 dio에 Interceptor 추가해준다.

이번에도 마찬가지로 쌩으로 dio(드래그한 부분)를 사용하는 게 아니라
RestaurantDetailScreen에서 했던 것처럼
RestaurantRepository로 드래그된 부분을 대체하고 싶음!

자동으로 원래 모델까지 변환되어야 하는데,
지금 RestaurantRepository 안에서 paginate( ) 함수를
아직 구현 안 했기 때문에 사용을 못하고 있는 상황인 거임!
paginate( ) 함수를 구현해보자!


② paginate( ) 함수 구현

토큰이 필요하니까 @Headers도 필요함.
아래 @Headers 그대로 복붙하기.

이렇게 accessToken을 true로 전환만 잘 해 놓으면,
dio.dart에서 Interceptor로 알아서 onRequest 함수를 통해
headers를 잘 추가해준다.


그럼 이 paginate라는 함수는 어떤 모델을 반환할까?

Future<List< RestaurantModel>> paginate( ); 이라고
생각할 수 있는데, 1차원적인 생각임!

만약 Future<List< RestaurantModel>> 이 형태를 반환받고 있다고 한다면,
현재 response에서 data 키에 해당되는
값들만 받아야 함. (postman의 GET /restaurant에서 확인)

하지만, 실제로 받고 있는 값은 meta라는 키와 data라는 키 안에
각각 다른 데이터가 들어가 있음.
즉, data 리스트가 아니라 meta까지 포함된 이 전체를 구성해줘야 함.

스웨거를 보면 페이지네이션하는 기능들이 몇 개 있는데,
어떤 서버든 페이지네이션 패턴은 비슷함.

restaurant을 보면 after, count를 넣어야하고
이런 식으로 데이터가 들어옴.

레스토랑 평점 페이지네이션도 after, count가 들어가고
메타, 데이터도 그대로 있음.

유일하게 다른 점은 데이터 리스트 안에 어떤 형태의 데이터가 들어가냐임!

즉, 그냥 이 구조만 따로 변경을 받겠다는 것!

그럼 이걸 어떻게 할 수 있을까?

restaurant_model.dart에 RestaurantModel을 보면
이 부분은 아까 말한 것처럼 그냥 data의 리스트에 해당되는 타입!


③ 폴더 및 파일 생성

common/model/cursor_pagination_model.dart
페이지네이션에 해당되는 형태를 생코드로 만들어보자.


④ CursorPagination 클래스 선언

그리고 이게 @JsonSerializable( )이라는 가정 하에
part 만들어서 임포트하기

import 'package:json_annotation/json_annotation.dart';

part 'cursor_pagination_model.g.dart';

()
class CursorPagination{

}

이러한 가정 하에 meta와 data라는 클래스가 들어가야 하는
구조를 만들려면 먼저 CursorPaginationMeta 클래스를 만들어준다.



⑤ CursorPaginationMeta 클래스 선언

import 'package:json_annotation/json_annotation.dart';

part 'cursor_pagination_model.g.dart';

()
class CursorPagination {}

()
class CursorPaginationMeta {
  final int count;
  final bool hasMore;

  CursorPaginationMeta({
    required this.count,
    required this.hasMore,
  });

  factory CursorPaginationMeta.fromJson(Map<String, dynamic> json) =>
      _$CursorPaginationMetaFromJson(json);
}



이후 차근차근 코드 작성해준다.

  • cursor_pagination_model.dart 지금까지 코드
import 'package:flutter_actual/restaurant/model/restaurant_model.dart';
import 'package:json_annotation/json_annotation.dart';

part 'cursor_pagination_model.g.dart';

() //  4. @JsonSerializable() 달아주기 
// 1. CursorPagination 클래스 만들기
class CursorPagination {
  // 5. 
  final CursorPaginationMeta meta;
  final List<RestaurantModel> data;

  CursorPagination({
    required this.meta,
    required this.data,
  });

  // 6. factory constructor 생성 후, 
  // flutter pub run build_runner build watch 
  factory CursorPagination.fromJson(Map<String, dynamic> json) =>
      _$CursorPaginationFromJson(json);
}

() // 4. @JsonSerializable() 달아주기 
// 2. CursorPaginationMeta 클래스 만들기 
class CursorPaginationMeta {
  final int count;
  final bool hasMore;

  CursorPaginationMeta({
    required this.count,
    required this.hasMore,
  });

  // 3. factory constructor 생성
  factory CursorPaginationMeta.fromJson(Map<String, dynamic> json) =>
      _$CursorPaginationMetaFromJson(json);
}

cursor_pagination_model.g.dart 파일도 잘 생겨난 것을 볼 수 있다.



postman에 있는 형태 똑같이 class로
@JsonSerializable( )을 통해 매핑할 수 있게 calss를 만들었음.

cursor_pagination_model.dart 코드를 보면
meta도 있고, data도 있고...
data는 어떻게 형성되었냐면 List< RestaurantModel >
RestaurantModel의 List로 형성되어 있음.

똑같음! 근데 여기서 문제는???

내가 원하는 건 CursorPagination이라는 클래스가
RestaurantModel만 data로 바뀌길 원하는 게 아님!
RestaurantModel이 변동될 수 있길 원함
!

meta라는 구조와 data라는 리스트 형태는 똑같은데,
List의 < > 안에 어떤 클래스가 들어가냐를 외부에서 바꿔주고싶음!

왜???

스웨거를 보면 restaurant 페이지네이션에서는 RestaurantModel이 들어가지만,
아래로 내려가 보면 restaurant /{rid}/rating 페이지네이션에서는
Rating이라는 Model을 넣어줘야 함.

/odrer라는 주문을 가져오는 페이지네이션도 구조는 똑같지만,
Order라는 Model을 넣어줘야 함.

이때 중요한 점은 meta 구조는 똑같고,
실제로 data가 List라는 점은 똑같다는 것!!!
그리고 이 List 안에 어떤 class가 들어가느냐가 다름!

이걸 어떻게 해결해야할까???



⑥ data 타입 변경하기

위 문제를 해결하기 위해서는 타입을 변수처럼 받아주면 되는데,
final List< RestaurantModel > data;
어떻게 이 데이터의 타입을 변경할 수 있을까??? 👉 Generic을 받아준다!

외부에서 T라는 타입을 받고, 그 타입을 받아 List에 넣어준다.
그럼 CursorPagination을 인스턴스로 만들 때,
T 타입으로 이 List 안의 타입을 지정할 수 있음.

그러면 _$CursorPaginationFromJson(json); 오류가 생긴다...

왜냐면 class CursorPagination를 빌드할 때
< T > 이 타입이 지정되지 않았기 때문에 final List< T > data;를
어떻게 Json으로부터 class로 변환할지 알 수 없어서!

그래서 외부에서 전환되는 방법을 알려줘야 함!
어떻게 알려주냐면... 코드를 추가해서
T 타입이 어떻게 fromJsonT으로부터 값을 받아오는지 정의해주자.

json, 뒤에 추가해준다!

그리고 T Function(Object? json) fronJsonT 이걸
두 번째 파라미터에 fromJsonT 라고 넣어주고 저장해준다.

@JsonSerializable( )에도 파라미터를 추가해준다.


  • cursor_pagination_model.dart 지금까지 전체 코드
import 'package:flutter_actual/restaurant/model/restaurant_model.dart';
import 'package:json_annotation/json_annotation.dart';

part 'cursor_pagination_model.g.dart';

// d. @JsonSerializable()에 파라미터 추가
(
  genericArgumentFactories: true,
) //  4. @JsonSerializable() 달아주기
// 1. CursorPagination 클래스 만들기
class CursorPagination<T> {
  // a. 외부에서 T라는 타입을 받는다
  // 5.
  final CursorPaginationMeta meta;
  final List<T> data; // b. 그 타입을 받아 List에 넣어준다

  CursorPagination({
    required this.meta,
    required this.data,
  });

  // 6. factory constructor 생성 후,
  // flutter pub run build_runner build watch
  factory CursorPagination.fromJson(
          // c.  T Function(Object? json) fromJsonT , fromJsonT 추가
          Map<String, dynamic> json,
          T Function(Object? json) fromJsonT) =>
      _$CursorPaginationFromJson(json, fromJsonT);
}

() // 4. @JsonSerializable() 달아주기
// 2. CursorPaginationMeta 클래스 만들기
class CursorPaginationMeta {
  final int count;
  final bool hasMore;

  CursorPaginationMeta({
    required this.count,
    required this.hasMore,
  });

  // 3. factory constructor 생성
  factory CursorPaginationMeta.fromJson(Map<String, dynamic> json) =>
      _$CursorPaginationMetaFromJson(json);
}

flutter pub run build_runner build 까지 해 주면,
오류 없이 잘 반영된다.



그리고
  factory CursorPaginationMeta.fromJson(Map<String, dynamic> json) =>
      _$CursorPaginationMetaFromJson(json);
}

이렇게 들어가고 나면 **이거를 어떻게 쓸 수 있을까?**

RestaurantRepository로 가면...
이제 Future<List< RestaurantModel >>가 아니고,
실제로는 CursorPagination의 RestaurantModel이다.

그리고 나중에는 < RestaurantModel > 이 클래스가 다른 모델 클래스로 바뀔 때도
CursorPagination 클래스는 그대로 유지해서 사용하면 된다.
페이지네이션을 할 때...

그리고 final List data; 데이터 키에 들어가는 List 값들(< T >)을
어떤 클래스로 정의하냐에 따라서 그냥 < RestaurantModel > 이 제너릭 안에
그 클래스 이름만 대체해주면 class CursorPagination 구조를
그대로 유지하고서 List 안에 < T > 어떤 타입이 들어가는지만 변경할 수 있는 것.

profile
호떡 신문지에서 개발자로 환생

0개의 댓글