firestore에서 where절 이용해 filter기능 구현 및 더보기 무한스크롤 기능 구현

둘둘·2020년 5월 29일
2

firebase

목록 보기
2/2
post-thumbnail

지난번엔 Customer페이지에서 sub collection을 추가하는 방식에 대해 써보았는데
오늘은 Customer페이지를 진입하게 해주는...!
바로 전 단계의 All 페이지에서 filter기능을 추가한 내용을 글로 써보려고 한다!

내가 구글링을 이상하게 해서인지 정말 정말 내용이 나오지 않았다.
자꾸 where을 이용하라는데 공식문서에서 하란대로 했는데도 안됐다.

이게 공식문서의 예시.. 이게 끝
구글링 해서 나오는 블로그 글이라 stackoverflow 글도 도움이 1도 안되서
검색만 오지게 했던 기억이 난다 ㅠㅠ

그러던 중.....
혹쉬...? 혹쉬나 하고 공식문서에 나오는 저 test.firestore.js 를 호기심에 눌러봤는데...
세상에.. 거기에 답이 있었다. 그 답안의 로직을 참고해 코드를 짜보았다!

state 안에 초기 값 설정하기

  • users : []
  • filteredList : []
  • keyword : ""
  • selectedUserId : ""

사실 여기까지만 설정해도 되지만, 무한 스크롤 기능을 구현하기 위해 몇가지 더 추가했다

  • lastVisible : []
  • loading : false
  • limit : 20

기존의 user리스트 불러오는 함수

이건 constructor 안에서 실행해서 그때마다 불러오도록 처리쓰~
여기에 state안의 limit 값을 이용해 초기에 불러오는 값을 조정하고,
(처음엔 limit 없이 막 불러와서 firestore 용량 몇 시간만에 다 초과쓰~~~)
추후 무한스크롤 기능을 만들기 위해 lastVisible의 값을 저장해둔다!

  getUser = () => {
    const { limit } = this.state;
    this.setState({ loading: true });
    const users = [];

	// firestore에서 유저 목록 불러오는 함수
    initialUserQuery
      .limit(limit) // 내가 처음에 정해준 limit 개수만큼만 불러오기(난 20개)
      .get() 
      .then(docSnapshots => {
      	// 리스트 중 가장 마지막 데이터 (나의 경우 20번째 유저의 정보) 
        let lastVisible = docSnapshots.docs[
          docSnapshots.docs.length - 1
        ].data();

	  // document의 id를 넣어놓는 배열
        const id_list = [];
        docSnapshots.forEach(doc => {
          id_list.push(doc.id);
          users.push(doc.data());
          users.forEach((el, idx) => {
            el.id = id_list[idx];
          });
        });
        this.setState({ users, lastVisible });
        // document 정보가 담긴 users 배열 완성!
       //가장 마지막 유저의 정보가 담긴 lastVisible 객체 완성!
      });
  };

더 불러오기 함수

  loadMore = () => {
    const { lastVisible, limit } = this.state;

	// 초기 limit 이후에 그 다음 limit 개수만큼 불러올 배열
  	// 코드를 지금 다시 보니 이름을 새로 짓는 게 좋을 것 같다
    let nextVisible = [];
    // limit 한도 늘리기
    this.setState(state => ({
      limit: state.limit + 5,
    }));
    
    initialUserQuery
      // firestore의 startAt 메소드를 이용해 해당 document부터 부를수있다
      .startAt(lastVisible.timestamp)
      .limit(limit)
      .get()
      .then(docSnapShots => {
        let lastVisible = docSnapShots.docs[
          docSnapShots.docs.length - 1
        ].data();
        
        // 새롭게 들어오는 값들의 document 이름이 들어갈 배열 만들기
        const new_id_list = [];
        docSnapShots.docs.forEach(doc => {
          new_id_list.push(doc.id);
          nextVisible.push(doc.data());
          nextVisible.forEach((el, idx) => {
            el.id = new_id_list[idx];
          });
        });
        // 이렇게 하면 limit만큼 더 불러와진 새로운 user 리스트 배열 완성!
        // lastVisible은 새로운 유저 리스트의 마지막 유저 document로 업뎃!
        this.setState({
          users: [...this.state.users, ...nextVisible],
          lastVisible,
        });
      });
  };

firestore 공식 문서를 읽어보면 메소드에 대해서 잘 나와있는데
더 불러오기 함수에서의 핵심은 startAt 메소드 인것같다!

scroll 이벤트 만들기

  handleScroll = () => {
    if (
      window.innerHeight + document.documentElement.scrollTop !==
      document.documentElement.offsetHeight
    )
      return;
    this.setState({ loading: true });
    this.loadMore();
  };

스크롤 이벤트를 처음 시도해봐서 이 방법이 정확하지 않을 수도 있다!
여튼 innerHeight값과 scrollTop 값을 이용해 이벤트를 주는건데

MDN 설명에 따르면 저게 바로 innerHeight!
의문이 드는건.. 15인치 아기사과에 콘솔을 찍어보면 915가 나오고
25인치 모니터에 콘솔을 찍어보면 717이 나온다는거 .. merge... ?!🤔

또다시 MDN의 설명!
실질적인 웹페이지에서의 스크롤 위치를 말하는 것 같다!
스크롤 바가 제일 상단에 있을때는 해당 element의 scrollTop은 0이고
스크롤을 밑으로 내리면 해당 element의 scrollTop은 점점 커지겠쥐!

여기서 위의 두 값을 더해서 offsetHeight와 비교를 해야하는데

offsetHeight는 화면에 보이는 요소들의 패딩값 등을 모두 포함한 값이다!

헷갈리니까 콘솔을 찍어서 확인해보자!!

innerHeight과 scrollTop을 더하면 offsetHeight값과 동일하게 나온다.
위의 콘솔은 스크롤을 쭉~~ 밑으로! 가장 하단에 위치했을때 찍힌다.
즉, 저 두 값의 합이 원래는 달랐다가 스크롤의 위치가 페이지의 가장 밑에 와있을 때만 같고
우리는 그 조건을 이용해서 loading의 state값과 loadMore함수를 불러주는 것이다

filtering 함수

여태 작성했던 함수 중에 가장 간단한 친구!
firestore에서 그때그때 타이핑할 때마다 state를 바꿔주면 너무 주어진 용량을 금방 써버리게 되니깐
Enter를 칠때의 값을 state에 담아주었다. 요렇게

  filteringKeywords = e => {
    if (e.key === "Enter") {
      this.setState({
        keyword: e.target.value,
      });
    }
    this.filteringUserList();
  };

유저 목록 필터링해주는 함수

state안의 keyword에 내가 검색한 키워드를 담고 나서 실행시키는 함수이다.

filteringUserList = () => {
    const { keyword, selectedUserId } = this.state;
    let filteredList = [];

    const getUserQuery = getUsersRef().where("clientName", "==", keyword);

    keyword &&
      getUserQuery.onSnapshot(doc => {
        if (!doc.docs[0]) {
          alert("오타났어유");
          window.location.reload();
          // 오타가 나면 당연히 그 정보가 없으니까 아무 목록도 불러오지 않는다
         // 그 상태에서 새로고침을 하지 않으면 목록을 불러올 수 가 없었다!
         // 그렇게 좋은 방법은 아니지만 그 당시엔 일단 이게 최선이었다 ㅠㅠ
        } else {
          const userId = doc.docs[0].id;
          //doc의 docs 배열에 보면 다양한 값이 있는데 그중 내가 필요한 id는 0번째에 있음
          this.setState({ selectedUserId: userId });
          // doc id를 받아서 selectedUserId라는 state에 저장 저장~
        }
      });

    getUserQuery
      .limit(5)
      .get()
      .then(snapshot => {
        const id_list = [];
        snapshot.forEach(doc => {
          id_list.push(selectedUserId);
          filteredList.push(doc.data());
          filteredList.forEach(el => {
            el.id = this.state.selectedUserId;
          });
        });
        this.setState({ filteredList }, () => {
          this.setState({ users: filteredList });
          // 전체 유저 목록을 필터링 된 유저 목록의 배열로 state 값을 바꿔줌
        });
      });
  };

전체적인 흐름은 처음 전체 유저를 불러오는 로직과 비슷하긴 한데
firestore의 where절에서 내가 state에 저장한 유저 이름에 해당하는 정보만 불러오게 했다!

select나 where절은 프론트도 꼭 알아야하는 상식이라고 했는데
where절을 이때 처음 써봐서 넘 감동이였다 🤩

어찌 보면 정말 간단한건데.. where절을 사용할줄 몰라서 일주일을 버렸던 것 같다.

넉낀점

구글신의 도움을 많이 받았고, 비효율적인 부분이 정말 많은 로직일 수 있지만
프로젝트 때 도전해보지 못했던 무한스크롤 기능과 filter기능을 처음으로 시도해봐서 뿌듯뿌듯!

이것까지 정리했으니 이제 드디어 1004수님의 코드를 pull 땡길수 있게 되었다
코드에 어마어마한 변화가 생겼을텐데 벌써부터 무섭고요... 😂😂😂

profile
Dooreplay! 안 되면 될 때까지,

0개의 댓글