flutter pagination 이론 , 구현

Pyo·2023년 10월 16일
0

최근 회사 프로젝트중에 Pagination을 구현해야 할 일있었는데 마침 듣고 있는 강의의 내용도 이부분에 해당해서 정리를 해보고 flutter로 Pagination을 구현해 보려고 한다.

Pagination

많은 데이터를 부분적으로 나눠서 불러오는 기술로 , 엄청난 양의 데이터를 모두 불러오지 않고 부분적으로 여러 부분으로 쪼개서 보여주는 것

Pagination 사용이유

애플리케이션을 사용하다 보면 엄청나게 많은 양의 데이터가 필요하다. 그럴때마다 매번 모든 데이터를 가져오게 된다면 메모리 적으로도 매우 부담이 될것고,속도도 너무 느려지기 때문에 앱의 성능 또한 떨어지게 된다.그렇기 때문에 Paigination을 사용하여 데이터를 나눠서 가져오게 된다면 문제점들을 해결할수 있다.

Pagination 종류,특징

Paginatio의 종류로는 Page Based Pagination,Cursor Based Pagination 정도로 나눌수 있다. 각각의 특징을 알아보겠다.

Page Based Paginaiton

Page Based Paginaiton은 보통 페이지 숫자를 누르면 다음 페이지로 넘어가는 형태의 UI로 현대의 UI에서 페이지 기반 페이지네이션은 더이상 많이 사용하지 않는다. 요청을 보낼 때 몇 개의 데이터를 가져올지, 몇 번째 페이지를 가져올 지를 명시한다. "(데이터 개수, 페이지 수)"로 표현하겠다.
예를 들어, 한 번에 10개의 데이터를 가져오고자 할 때, 두 번째 페이지의 데이터를 요청한다면 "skip(10개, 첫 번째 페이지) take(10개, 두 번째 페이지)"의 과정을 통해10번째부터 19번째 데이터를 가져올 수 있다. 이전 페이지나 다음 페이지를 요청할때도 마찬가지로 "take(10개, 현재페이지 +- 1)" 이다.

장점

  • 알고리즘 자체가 간단해서 구현하기는 어렵지 않다.

단점

  • Pagination 도중에 데이터베이스에서 데이터 추가시 문제가 발생한다. 예를들어 1페이지에서 2페이지로 Pagination을 할때 1페이지에 데이터를 추가하게되면, 데이터가 뒤페이지로 밀려서 1페이지에서 봤던 데이터를 중복해서 보게 된다.
  • Pagination 도중에 데이터베이스에서 데이터 삭제시에도 문제가 발생한다. 예를들어 1페이지에서 2페이지로 Pagination을 할때 1페이지에서 데이터를 삭제하게 되면 , 데이터가 앞페이지로 당겨지면서 2페이지에서 봐야할 데이터가 1페이지로 당겨지게 된다.

Cursor Based Paginaiton

Cursor Based Paginaiton은 가장 최근에 가져온 데이터를 기준으로 다음 데이터를 가져오는 Pagination으로 스크롤 형태의 리스트에서 자주 사용한다. 요청을 보낼때 마지막 데이터의 기준값(ID등 Unique 값)과 몇개의 데이터를 가져올지 명시한다. "(데이터 개수,마지막 데이터의 id값)'로 표현하겠다.
예를 들어 , 한번에 10개의 데이터를 가져오고자 할 때, 마지막 데이터 id를 20으로 요청한다면 skip과정없이 "take(10개,20)"의 과정을 통해 21번째부터 30번째 데이터를 가져올수 있다.

장점

  • Page Based Paginaiton이랑 다르게 요청을 보낼때 마지막 데이터의 기준값을 보내기 때문에 Paginaiton시,데이터가 추가되거나 삭제가 되어도 데이터의 기준값을 기반으로 쿼리가 작성되기때문에 데이터가 누락되거나 중복될 확률이 적다.

단점

  • Page Based Paginaiton에 비해 구현이 어려울수 있고, 데이터의 기준값이 고유한값이 아닐경우에는 데이터가 중복되어 문제가 될수 있다.

이제 정리한 내용들을 바탕으로 간단하게 코드로 구현해 보겠다.

Page Based Pagination 코드,실행 화면

Page Based Paginaion 코드

pagination_model.dart

import 'package:freezed_annotation/freezed_annotation.dart';

part 'pagination_model.g.dart';
part 'pagination_model.freezed.dart';

@freezed
class PaginationModel with _$PaginationModel {
  factory PaginationModel({
    required int userId,
    required int id,
    required String title,
    required String body,
  }) = _PaginationModel;

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

https://jsonplaceholder.typicode.com/ 에서 api를 사용했고 , 필요한 데이터 모델을 생성했다.

pagination_screen.dart

import 'package:flutter/material.dart';

import 'package:dio/dio.dart';

import 'package:flutter_practice/src/default_layout_widget.dart';
import 'package:flutter_practice/src/pagination/pagination_model.dart';

class PaginationScreen extends StatefulWidget {
  const PaginationScreen({super.key});

  @override
  _PaginationScreenState createState() => _PaginationScreenState();
}

class _PaginationScreenState extends State<PaginationScreen> {
  List<PaginationModel> items = [];
  int currentPage = 1;

  bool isLoading = false;
  late List<PaginationModel> pageList;
  final dio = Dio();
  @override
  void initState() {
    super.initState();
    fetchData();
  }

  void fetchData() async {
    setState(() {
      isLoading = true;
    });

    setState(() {
      items.clear();
    });

    final resp = await dio.get('https://jsonplaceholder.typicode.com/posts',
        queryParameters: {'_page': '$currentPage', '_limit': 10});
    List<dynamic> respData = resp.data;
    pageList = respData.map((e) => PaginationModel.fromJson(e)).toList();
    if (resp.statusCode == 200) {
      setState(() {
        items.addAll(pageList);
      });
    }

    setState(() {
      isLoading = false;
    });
  }

  @override
  Widget build(BuildContext context) {
    return DefaultLayoutWidget(
      child: Column(
        children: [
          (isLoading)
              ? const Center(
                  child: CircularProgressIndicator(),
                )
              : Expanded(
                  child: ListView.builder(
                    itemCount: items.length,
                    itemBuilder: (context, index) {
                      return Row(
                        children: [
                          Text('${items[index].id} : '),
                          Expanded(child: Text(items[index].title))
                        ],
                      );
                    },
                  ),
                ),
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              ElevatedButton(
                onPressed: () {
                  if (currentPage > 1) {
                    currentPage--;
                    fetchData();
                  }
                },
                child: const Text('이전'),
              ),
              const SizedBox(
                width: 16.0,
              ),
              ElevatedButton(
                onPressed: () {
                  if (currentPage < 10) {
                    currentPage++;
                    fetchData();
                  }
                },
                child: const Text('다음'),
              )
            ],
          )
        ],
      ),
    );
  }
}

실행 화면


여기 까지 Page Based Pagination 코드를 작성해 보았다. 최근 많이 사용하고 있는 Cursor Based Pagination에 대해서는 조금더 자세하게 정리해보고 싶어서 따로 정리해 보려고 한다.

참고

https://betterdev.tistory.com/17
https://www.inflearn.com/course/%ED%94%8C%EB%9F%AC%ED%84%B0-%EC%8B%A4%EC%A0%84/dashboard

0개의 댓글