피트니스 산업과 IT, 그리고 스타트업

IT

플러터, 페이지네이션과 무한스크롤

핏더스트리 2025. 4. 29. 09:22
728x90
반응형

플러터, 페이지네이션과 무한스크롤

앱을 만들다 보면 리스트를 스크롤할 때마다 데이터를 계속 불러오는 무한스크롤(infinite scroll) 기능이 필요할 때가 많아요. 예를 들면 쇼핑 앱에서 상품 목록을 내릴 때마다 새로운 상품이 로딩되거나, 뉴스 앱에서 기사 목록이 끝없이 이어지는 것처럼요. 이런 기능을 효율적으로 구현하려면 페이지네이션(pagination) 개념을 잘 이해하고 적용해야 해요.

 

Flutter에서도 무한스크롤을 만들 수 있는 다양한 방법이 있는데, 제대로 설계하지 않으면 퍼포먼스 문제가 생기거나, 중복 로딩이 발생하거나, 사용자 경험이 좋지 않을 수 있어요. 이번 글에서는 Flutter에서 페이지네이션과 무한스크롤을 구현하는 기본 개념부터, 실습에 바로 적용할 수 있는 핵심 포인트까지 정리해볼게요.

 

페이지네이션이란 무엇일까?

페이지네이션은 데이터를 한 번에 다 불러오는 대신, 조금씩 나눠서 불러오는 방법이에요. 예를 들어 서버에 1,000개의 데이터가 있다면 처음에는 20개만 가져오고, 사용자가 더 보고 싶을 때 추가로 20개씩 불러오는 방식이죠.

 

이 방식은 다음과 같은 이유로 매우 중요해요.

  • 성능 최적화: 한 번에 너무 많은 데이터를 가져오면 메모리 사용량이 급격히 늘어나고, 앱이 느려질 수 있어요. 페이지네이션은 필요한 만큼만 데이터를 불러와서 부담을 줄여줘요.
  • 네트워크 효율성: 모바일 네트워크 환경에서는 데이터 전송량이 중요해요. 페이지 단위로 통신하면 필요 이상의 데이터를 주고받지 않아 네트워크 비용과 속도를 아낄 수 있어요.
  • 사용자 경험 개선: 처음 앱을 열었을 때 빠르게 화면을 구성할 수 있고, 스크롤할 때마다 자연스럽게 이어지는 흐름을 제공할 수 있어요.

Flutter에서는 ListView와 ScrollController를 조합해 페이지네이션을 쉽게 구현할 수 있어요. 특히 스크롤이 끝에 다다랐을 때 새로운 데이터를 요청하는 로직을 넣는 게 핵심이에요. 본격적인 무한스크롤 구현 방법은 다음 본론에서 자세히 살펴볼게요.

 

Flutter에서 무한스크롤 구현하는 기본 방법

Flutter에서 무한스크롤을 구현하려면 핵심은 딱 두 가지예요. 하나는 스크롤 이벤트를 감지하는 것, 다른 하나는 필요할 때 데이터를 추가로 로딩하는 것이에요. 이를 위해 주로 ListView.builder와 ScrollController를 함께 사용해요.

 

먼저 ListView.builder는 리스트를 효율적으로 그리는 데 최적화된 위젯이에요. 데이터가 많더라도 화면에 보이는 부분만 렌더링하기 때문에 성능이 좋아요. 여기에 ScrollController를 연결해서, 스크롤이 리스트 끝에 가까워졌을 때 새로운 데이터를 요청하는 방식으로 무한스크롤을 구현할 수 있어요.

 

간단한 기본 흐름은 이래요.

  1. ScrollController를 생성하고 ListView.builder에 연결해요.
  2. ScrollController의 addListener()를 사용해서 스크롤 위치를 계속 감시해요.
  3. 스크롤이 리스트 끝에 가까워지면 새로운 데이터를 요청하는 함수를 호출해요.
  4. 데이터를 받아오면 리스트에 추가하고, setState로 화면을 갱신해요.

코드로 보면 이런 구조예요.

class InfiniteScrollPage extends StatefulWidget {
  @override
  _InfiniteScrollPageState createState() => _InfiniteScrollPageState();
}

class _InfiniteScrollPageState extends State<InfiniteScrollPage> {
  final ScrollController _scrollController = ScrollController();
  List<int> _items = List.generate(20, (index) => index);
  bool _isLoading = false;

  @override
  void initState() {
    super.initState();
    _scrollController.addListener(() {
      if (_scrollController.position.pixels >= _scrollController.position.maxScrollExtent - 200 && !_isLoading) {
        _loadMoreItems();
      }
    });
  }

  void _loadMoreItems() async {
    setState(() {
      _isLoading = true;
    });

    // 예시: 데이터 추가를 2초 동안 기다린 후 처리
    await Future.delayed(Duration(seconds: 2));
    setState(() {
      _items.addAll(List.generate(20, (index) => _items.length + index));
      _isLoading = false;
    });
  }

  @override
  void dispose() {
    _scrollController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      controller: _scrollController,
      itemCount: _items.length + (_isLoading ? 1 : 0),
      itemBuilder: (context, index) {
        if (index == _items.length) {
          return Center(child: CircularProgressIndicator());
        }
        return ListTile(title: Text('Item ${_items[index]}'));
      },
    );
  }
}

이 코드에서는 리스트를 스크롤하다가 끝에서 200픽셀 정도 남았을 때 _loadMoreItems()를 호출해요. 데이터를 추가로 불러오는 동안에는 로딩 인디케이터를 보여주고, 새 데이터가 추가되면 자연스럽게 스크롤이 이어져요.

 

이 방식만 알아도 기본적인 무한스크롤은 어렵지 않게 만들 수 있어요. 다만 실제 서비스에서는 여기서 끝나지 않고, 더 매끄럽고 안전한 구현을 위해 추가로 고려해야 할 부분들이 있어요. 그런 고급 팁은 다음 본론에서 이어서 이야기해볼게요!

 

더 부드럽고 안전한 무한스크롤을 만드는 팁

기본적인 무한스크롤은 어렵지 않게 구현할 수 있지만, 실제 서비스 수준에서는 몇 가지 추가적인 포인트를 신경 써야 해요. 그래야 사용자 경험을 더 부드럽게 만들고, 오류 가능성도 줄일 수 있어요. 여기서는 꼭 챙겨야 할 팁들을 정리해볼게요.

 

첫 번째, 중복 로딩 방지하기예요.
네트워크가 느리거나 스크롤이 너무 빠르면 _loadMoreItems()가 여러 번 호출될 수 있어요. 그래서 항상 isLoading 플래그를 잘 활용해서, 로딩 중에는 추가 요청을 막아야 해요. 이것만 제대로 해도 데이터 중복 로딩이나 UI 깨짐 문제를 예방할 수 있어요.

 

두 번째, 끝까지 데이터를 다 불러온 경우 처리하기예요.
예를 들어 서버에 총 100개의 아이템만 있는데 계속해서 더 불러오려고 하면 불필요한 요청이 발생해요. 이럴 때는 마지막 페이지에 도달했는지 확인하고, 더 이상 추가 로딩을 하지 않도록 hasMore 같은 변수를 따로 관리하는 것이 좋아요.

 

세 번째, 오류 처리와 사용자 알림 추가하기예요.
네트워크 오류가 발생했을 때 단순히 실패하는 것으로 끝내지 않고, "다시 시도" 버튼을 보여주거나 사용자에게 로딩 실패를 알려주는 방식으로 UX를 개선할 수 있어요. 안정적인 앱이라면 이런 작은 배려가 사용자의 신뢰를 높여줘요.

 

네 번째, 로딩 인디케이터의 자연스러운 위치 선정이에요.
리스트 끝에 도달했을 때 로딩 스피너를 자연스럽게 리스트 맨 아래에 보여주면 사용자도 "아, 지금 데이터가 더 불러와지고 있구나"를 직관적으로 알 수 있어요. 뜬금없이 화면 중간에 로딩창이 뜨는 것보다는 훨씬 자연스러운 흐름을 만들 수 있어요.

 

이런 작은 디테일들을 챙기면, 단순한 무한스크롤 구현이 아니라 완성도 높은 사용자 경험을 제공하는 기능으로 업그레이드할 수 있어요.

 

마치며...

Flutter에서 무한스크롤을 구현하는 것은 생각보다 어렵지 않지만, 잘하려면 세심한 설계가 필요해요. 페이지네이션은 퍼포먼스, 네트워크 효율성, 사용자 경험까지 모두 챙길 수 있는 중요한 기법이에요.

 

단순히 데이터를 불러오는 것에서 끝나는 게 아니라, 언제, 얼마나, 어떻게 불러올지를 고민하는 것이 진짜 무한스크롤을 잘 구현하는 길이에요. 스크롤을 감지하고, 로딩 상태를 관리하고, 에러 상황까지 세심하게 처리하는 것. 이런 기본기를 다져두면 Flutter 앱을 만들 때 훨씬 더 자연스럽고 안정적인 사용자 경험을 제공할 수 있게 될 거예요.

 

Flutter 개발자로서 한 단계 더 성장하고 싶다면, 무한스크롤 구현은 꼭 마스터해야 할 필수 기술이라는 것, 꼭 기억해두세요!

728x90
반응형