강디너의 개발 일지

Firebase realtime database pagination 삽질기 본문

Javascript/삽질

Firebase realtime database pagination 삽질기

강디너 2020. 5. 25. 21:44
728x90

목적

firebase database 특정 데이터를 '조건'에 맞춰 페이징 + 정렬하고 싶다.

- group/showGroup 아래의 데이터들을 likes/count가 높은 수 대로 맞춰서 정렬하고 싶다.

 

firebase database 생김새

 

 

아래 코드는 group/showGroup 아래 likes/count에 대해서 정렬하는 코드입니다.

하지만 정렬만 하는 기능일 뿐 페이징을 하지 못합니다.

firebase database 기본 쿼리에서는 페이징 기능을 따로 지원하지 않기 때문에 자체적으로 만들어야 합니다.

// group/showGroup 아래 likes/count 에 대해서 정렬
export function getOpenGroup() {
    return firebase.database().ref('group/showGroup').orderByChild('likes/count');
}

// group/showGroup 목록의 key length를 가져오려고 만든 함수
export function getOpenGroupLength() {
    return firebase.database().ref('group/showGroup');
}

https://firebase.google.com/docs/database/admin/retrieve-data?hl=ko

 

아래 코드는  초기값을 성정하는 코드입니다.

목록의 총길이를 저장 후, 정렬한 데이터를 가져옵니다.

  const init = () => {
    getOpenGroupLength()
      .once('value', snapshot => {
      	// 길이 저장
        groupLength.value += snapshot.numChildren();
        
        // 최초 리스트 길이가 perPage 보다 작을 경우 최초 리스트 길이만 부르기
        if (groupLength.value < perPage +1) perPage = groupLength.value -1;
        
        getOpenGroup()
          .limitToLast(perPage + 1)
          .on("child_added", snapshot => {
            getData(snapshot);
          });
      });
  };

여기서 중요한 점은 limitToLast (또는 limitToFirst) perPage + 1로 해야 한다는 점입니다.

그 이유는 데이터를 1개 더 가져온 후 해당 key값, value값을 저장해서 다음 페이지의 시작점이 돼야 하기 때문입니다.

 

 const getData = (snapshot) => {
    if (!snapshot.val()) return;
        tempArr.push(snapshot.val());
		
        // 1번
        tempKey.push(snapshot.key);
        tempValue.push(snapshot.val().likes.count);
        lastKey.value = tempKey[0];
        lastValue.value = tempValue[0];
        
        // 3번
        if (groupLength.value < perPage + 1) {
          isFinish.value = true;
          openGroups.value.push(snapshot.val());
        }
        
		// 2번
        if (tempArr.length === perPage + 1) {
          tempArr.shift();
          openGroups.value.push(...tempArr.reverse());
          groupLength.value -= perPage;
        }
  };

예를 들어 페이지 한 개에 목록을 4개를 보여야 한다면

limitToLast 값을 5로 설정 후 받은 데이터를 temp에 넣어줍니다.

 

1번

그다음 맨 마지막 값을 lastKey, lastValue에 넣어줍니다. 여기서 tempKey [0], tempValue [0]을 뽑아간 것은 마지막 값을 firebase에서 첫 번째로 주기 때문입니다. (로그로 찍어보세요!!)

2번

원하는 목록보다 1개 더 받았으니깐 삭제시킵니다.

likes/count의 수가 높을수록 정렬하고 싶었기 때문에 limitToLast로 받은 후 tempArr를 reverse 해서 넘겼습니다. 

그 후 총목록에서 내가 받은 만큼 빼줍니다.

3번

groupLength 변수에 목록의 총길이를 넣어놨었는데 groupLength 가 5보다 작으면 마지막 페이지란 뜻이니깐 종료시킨 후 temp 데이터를 삭제 없이 넘깁니다.

 

그다음 페이지를 부르는 코드입니다.

  const getOpenGroupData = () => {
    if (isFinish.value) return;
    tempArr = [];
    tempKey = [];
    tempValue = [];
    
    getOpenGroup()
      .endAt(lastValue.value, lastKey.value)
      .limitToLast(perPage + 1)
      .on("child_added", snapshot => {
          getData(snapshot);
      });
  };

endAt( lastValue, lastKey) 값을 넣어줘서 시작점을 알려줍니다.

예전에는 키값만 넣어줘도 된다더니... 둘 다 넣어줘야 합니다.

endAt 이 안 먹히는 줄 알고 엄청 개고생을... 했습니다.

 

 

아래 코드는 스크롤 최하단 or 버튼 클릭 시 목록이 더 나오는 코드입니다.

 

결과 코드

import {
  ref,
  onMounted,
  watch
} from "@vue/composition-api";
import {
  getOpenGroup,
  getOpenGroupLength
} from '@/services/group';

export const openGroup = (perPage, page, isScroll = true) => {
  const openGroups = ref([]);
  let tempKey = [];
  let tempValue = [];
  let tempArr = [];
  const groupLength = ref(0);
  const lastKey = ref('');
  const lastValue = ref(0);
  const isFinish = ref(false);

  const getData = (snapshot) => {
    if (!snapshot.val()) return;
        tempArr.push(snapshot.val());

        tempKey.push(snapshot.key);
        tempValue.push(snapshot.val().likes.count);
        lastKey.value = tempKey[0];
        lastValue.value = tempValue[0];
        
        if (groupLength.value < perPage + 1) {
          isFinish.value = true;
          openGroups.value.push(snapshot.val());
        }

        if (tempArr.length === perPage + 1) {
          tempArr.shift();
          openGroups.value.push(...tempArr.reverse());
          groupLength.value -= perPage;
        }
  };

  const getOpenGroupData = () => {
    if (isFinish.value) return;
    tempArr = [];
    tempKey = [];
    tempValue = [];
    
    getOpenGroup()
      .endAt(lastValue.value, lastKey.value)
      .limitToLast(perPage + 1)
      .on("child_added", snapshot => {
          getData(snapshot);
      });
  };

  const init = () => {
    getOpenGroupLength()
      .once('value', snapshot => {
        groupLength.value += snapshot.numChildren();
        
		// 최초 리스트 길이가 perPage 보다 작을 경우 최초 리스트 길이만 부르기
        if (groupLength.value < perPage +1) perPage = groupLength.value -1;
        getOpenGroup()
          .limitToLast(perPage + 1)
          .on("child_added", snapshot => {
            getData(snapshot);
          });
      });
  };

  const readMore = () => {
    if (isFinish.value) return;
    page.value += 1;
  };

  const scroll = () => {
    if (!isScroll) return;
    window.onscroll = () => {
      let bottomOfWindow =
        Math.max(
          window.pageYOffset,
          document.documentElement.scrollTop,
          document.body.scrollTop
        ) +
        window.innerHeight ===
        document.documentElement.offsetHeight;

      if (bottomOfWindow) {
        page.value += 1;
      }
    };
  };

  watch(page, () => {
    if (page.value === 1) return init();
    getOpenGroupData();
  });

  onMounted(() => {
    scroll();
  });

  return {
    openGroups,
    readMore,
    isFinish,
  };
};

https://github.com/DinnerKang/study_vue/blob/master/deali-music/src/composible/openGroup.js

 

- 5/27 수정

커피한잔...
Comments