개발/front-end

angular Infinite Scroll을 이용한 페이징 처리

희운1205 2024. 7. 17. 15:35
반응형

angular  Infinite Scroll  페이징 처리

일반적으로 게시판처럼 데이터를 계속 누적해서 보여줘야 할 때, 페이징을 통해 데이터를 나누어 보여줍니다. 보통 페이징은 "이전, 다음, 첫 페이지, 끝 페이지, 1, 2, 3, 4, ..."와 같은 형태로 구성됩니다. 그러나 데이터 양이 많고, 특히 화면이 작은 모바일 환경에서는 이러한 방식이 불편할 수 있습니다.

이 문제를 해결하기 위해, 스크롤 이벤트를 감지하여 자동으로 페이징을 처리하는 방식이 많이 사용됩니다. 사용자가 스크롤을 내릴 때마다 다음 페이지의 데이터를 자동으로 로드하여, 번호를 누르지 않아도 계속해서 데이터를 볼 수 있게 합니다.

다음은 Angular에서 디렉티브(directive)를 사용하여 스크롤 이벤트를 감지하고, 페이징을 자동으로 처리하는 방법입니다.

1. scroll.directive.ts 생성

import { Directive, HostListener, Output, EventEmitter, Input } from '@angular/core';

interface ScrollEvent {
  isReachingBottom: boolean;
  originalEvent: Event;
  isWindowEvent: boolean;
}

@Directive({
  selector: '[appScroll]'
})
export class AppScrollDirective {

  @Input() bottomOffset = 100; // 하단에서 얼마나 떨어졌을 때 이벤트를 발생시킬지 설정
  @Output() onScroll = new EventEmitter<ScrollEvent>();

  // 스크롤 이벤트 핸들러
  @HostListener('scroll', ['$event']) 
  @HostListener('window:scroll', ['$event']) 
  public handleScroll(event: Event): void {
    if (event.target === document || event.target === window) {
      this.windowScrollEvent(event);
    } else {
      this.elementScrollEvent(event);
    }
  }

  // 윈도우 스크롤 처리
  // 브라우저 전체 창에 대해 스크롤을 감지하여 전체 페이지를 스크롤할 때 사용
  private windowScrollEvent(event: Event): void {
    const scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
    const isReachingBottom = (document.documentElement.offsetHeight - (window.innerHeight + scrollTop)) < this.bottomOffset;
    const emitValue: ScrollEvent = { isReachingBottom, originalEvent: event, isWindowEvent: true };
    this.onScroll.emit(emitValue);
  }

  // 호스트 요소 스크롤 처리
  // 특정 요소에 대해 스크롤을 감지하여 특정 영역에서만 스크롤이 발생하는 경우 사용
  private elementScrollEvent(event: Event): void {
    const target = event.target as HTMLElement;
    const scrollPosition = target.scrollHeight - target.scrollTop;
    const offsetHeight = target.offsetHeight;
    const isReachingBottom = (scrollPosition - offsetHeight) < this.bottomOffset;
    const emitValue: ScrollEvent = { isReachingBottom, originalEvent: event, isWindowEvent: false };
    this.onScroll.emit(emitValue);
  }
}

2. list.component.ts 생성 ( scroll.directive.ts 사용하는 컴포넌트)

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-list',
  templateUrl: './list.component.html',
  styleUrls: ['./list.component.css']
})
export class ListComponent implements OnInit {
  items: number[] = [];
  page = 0;

  ngOnInit(): void {
    this.loadMore();
  }

  loadMore(): void {
    this.page++;
    const newItems = Array.from({ length: 20 }, (_, i) => i + this.items.length);
    this.items.push(...newItems);
  }

  onScroll(event: { isReachingBottom: boolean; originalEvent: Event; isWindowEvent: boolean }): void {
    if (event.isReachingBottom) {
      this.loadMore();
    }
  }
}

3. list.component.html (템플릿 생성)

<div class="item-list" appScroll (onScroll)="onScroll($event)">
  <div class="item" *ngFor="let item of items">
    Item {{ item }}
  </div>
</div>

4. list.component.css(css 생성)

.item-list {
    height: 80vh;
    overflow: auto;
 }

 .item {
    padding: 16px;
    border-bottom: 1px solid #ccc;
 }

 

반응형