ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [프리온보딩코스 과제 회고 #1] 무한 스크롤(Infinite Scroll)
    🏄‍♀️ 원티드X위코드 프리온보딩코스 2021. 7. 27. 20:54

    1. 무한 스크롤의 원리

    화면을 스크롤할 때 콘텐츠의 끝 부분을 감지하고, 다음 페이지를 불러와 현재 페이지에 이어 붙인다.

    2. 구현 방법

    1. 전통적인 스크롤 감지(onScroll event) : 유저가 scroll을 하면 이벤트가 발생하고 현재 scroll 위치가 페이지에 끝에 닿았는지 판단한다. 하지만 이 경우 scroll 이벤트는 굉장히 빈번하게 발생하기 때문에 성능 최적화에 위배되는 문제가 있다. 그렇기 때문에 throttle 작업이 추가적으로 필요하다.
    2. Intersection Observer API : DOM 엘리먼트 간에 영역이 겹쳐지는 걸 감시한다. Intersection Observer API를 사용하면 scroll, resize와 같은 비싼 비용의 이벤트를 좀 더 쉽고 좋은 퍼포먼스로 사용할 수 있다.
    3. useRef : list의 마지막 요소에만 선택적으로 ref를 달아주고, ref가 있을 때, 새롭게 데이터를 fetch하는 방법이다.

    → 위와 같이 무한 스크롤을 구현할 수 있는 여러 가지 방법이 있었지만, 우리가 선택한 방법은 Intersection Observer API를 활용하여 구현하는 것! 이 방법을 채택하게 된 이유는 HTML, CSS, Javascript가 개입하지 않아도 되기 때문이다.

    3. 작업 내용

    3.1 구현 사항

    • 마크업, css 스타일링
    • Data Fetch API 호출 함수 작성
    • Intesection Observer를 활용한 Custom Hook 작성
    • Infinite Scroll 기능 구현
    • 각 기능별 폴더 및 파일 분리

    3.2 폴더 구조

    📂 src > api > getCommentData.js : Data Fetch API 호출 함수
    📂 src > components > InfiniteScroll.js : Infinite Scroll 기능
    📂 src > hooks > useIntersectObserver.js : Intesection Observer를 활용한 Custom Hook
    📂 src > constants > index.js : 상수 저장 파일

    📂 src > App.js : Infinite Scroll 컨테이너
    📂 src > App.css : 전체 스타일 css 파일
    📂 src > index.css : global reset css 파일

    3.3 작업 플로우

    작업 플로우는 우선, getCommentData.js 파일에 데이터를 fetch하여 가져오는 API 함수를 먼저 만들고, 그 후에 App.js에 무한 스크롤 기능을 작성하였다. 그러고 나서 분리할 수 있는 코드들을 hooks 폴더, components 폴더와 constants 폴더에 각각 useIntersectObserver.js, InfiniteScroll.js와 index.js로 분리하였다.

    3.4 결과 화면

    🔗 과제 레포지토리 링크
    🔗 과제 배포 링크

    4. 과제 회고

    4.1 새롭게 알게 된 것🆕

    1. url 호출할 때, params 객체를 보내 query string까지 불러올 수 있다!
      이전에는 axios.get(`https://jsonplaceholder.typicode.com/comments?_page=${page}&_limit=${LIMIT}`) 이런 방식으로 api를 호출했었는데, params 객체를 넣어주는 식으로 작업하는 게 훨씬 더 나은 거 같다!
      📂 src > api > getCommentData.js
      const API_ENDPOINT = `https://jsonplaceholder.typicode.com/comments`;
      const LIMIT = 10;
      .
      .
      const res = await axios.get(`${API_ENDPOINT}`, {
      	params: {
      	  _page: page,
      	  _limit: LIMIT,
      	},
      });
      📂 src > App.js
      const [page, setPage] = useState(1);
      
      const data = getCommentData(page);
    2. Intersection Observer API의 options
      • root : 타겟의 visibility(가시성)를 검사하기 위해 뷰포트 대신 사용할 요소 객체를 지정한다. null인 경우 브라우저의 뷰포트가 디폴트로 사용된다.
      • rootMargin : 바깥 여백(margin)을 이용해 root의 범위를 확장하거나 축소할 수 있다! rootMargin이 있으면 threshold 계산할 때, rootMargin 영역 만큼 더 계산한다. 그렇기 때문에 이를 주면 뚝뚝 끊기는 느낌이 아닌, 좀 더 스무스하게 스크롤하여 로딩하는 느낌을 줄 수 있다.
      • threshold : observer가 실행되기 위해 visibility가 얼마나 필요한 지 백분율로 표시한다. 설정한 비율이 노출될 때마다 callback 함수를 실행시킨다!
        const options = {
          root: null,
          rootMargin: "200px",
          threshold: 0.01,
        };

    4.2 생각해보기!💡

    1. usecallback을 사용하지 않을 때, 생기는 에러
      → usecallback을 지웠을 때, state가 겹쳐지는 등 데이터가 덮어씌워지는 현상이 발생했는데, 이는 두 번째 인자로 빈 배열을 삽입하지 않아 state가 바뀔 때마다 새로 호출되었기 때문에 생긴 에러이다. 두 번째 인자로 빈 배열을 삽입해야 해당 컴포넌트가 최초로 호출될 때 한 번만 호출된다!
    2. useEffect에 dependency array로 callback을 왜 쓰는지?
      → dependency array를 넘겨주지 않으면 매번 새로고칠 때마다 새로 호출된다. 이는 성능적으로 좋지 않기 때문에 dependency array를 넘겨주어 조건부로, 변동사항이 있는 경우에만 새로 렌더를 할 수 있게끔 하여 성능을 개선시켜줄 수 있다.
    3. 무한 스크롤이 SEO적인 측면에서는 불리하다고 하는데, 무한 스크롤을 고집해야 하는 이유가 있을까?
      → 무한 스크롤 포스팅을 찾아보던 중 어떤 한 블로그에서 "무한 스크롤이 SEO적인 측면에서는 불리하다고 해서 페이지네이션을 더 선호한다"라는 댓글을 보고 의문이 들었다! SEO적인 측면에서 불리하다면 쓰는 이유가 무엇일까에 대해서 생각을 해보았고, 아무래도 UX적인 측면을 고려해서라고 결론을 내렸다.
      SEO를 조금 포기하더라도 뭔가 딱 무한 스크롤로 유저에게 더 많은 데이터를 스무스하게 보여줄 수 있는 것에 초점을 둔다면 무한 스크롤을 쓰는 거 아닐까!라는 생각을 하였는데, 성상님도 역시나 똑같은 생각을 하고 계셨다!

      무한 스크롤과 페이지네이션 모두 저마다의 장단점을 가지고 있기 때문에 트렌드에 맞춰 선택하기보다는 각자의 서비스 플랫폼의 방향에 맞춰서 뭐가 더 잘 어울릴까를 따져보는 게 관건인 거 같다!
      🔗 더 알아보기 페이지네이션 VS 무한 스크롤 : 자사에 적합한 UX 고르는 법
    4. ( [ {isIntersecting} ] ) 이건 대체 무슨 문법인가..? 🤔
      → 성상님이 공유해주신 유튜브 강의 중 ( [ {isIntersecting} ] ) 이런 문법이 등장을 하였고... 한눈에 봐서는 이해가 가지 않았다.
       const fetchMoreObserver = new IntersectionObserver(([{ isIntersecting }]) => {
         // do something
       });
      친절한 성상님이 >>>거의 멘토님 수준으로<<< 이 문법을 설명해주셨는데 다 듣고 나서 정말 무릎을 탁 쳤다.💡
      우리 코드로 미루어보면, new IntersectionObserver(handleObserver, options);에서 handleObserver는 화살표함수로, new IntersectionObserver( (entries) => {}, options);로 사용할 수 있는데! 여기서 entries는 배열이기 때문에 배열 비구조화 할당 + 객체 비구조화 할당을 사용한 것!
      여기서 배열 비구조화 할당이란, 아래 코드와 같이
      const array = [1]
      const [one] = array; 
      // 즉, one === 1

      표현이 되는 것이고, 객체 비구조화 할당은 property 이름을 자동으로 할당해주는 역할을 하는데, 이때 entries는 원소가 하나인 배열이므로 const [a] = entries하면 aentries[0]번째 원소로 할당되고, a 안에 있는 isInterSecting{ interSecting }으로 함으로써 바로 매개변수로 사용할 수 있는 것이다!
      진짜 완전 이해가 잘 가게 설명해준 성상님께 다시 한번 cheers 🥂!!

    4.3 이번 과제를 통해 느낀 점

    페어 프로그래밍은 아예 처음이라 좀 긴장을 많이 했었는데 되게 재밌었다! 아무래도 똑같은 코드를 두 명이서 같이 보면서 고민하고 짜다 보니 혼자서 짜는 것보다 훨씬 더 수월하고 빠르게 코드를 짤 수 있었다. (에러가 발생해도 두 명이서 고민하니까 해결 시간이 두 배로 단축!!👍) 그리고 운이 좋게 MBTI 궁합이 정말 잘 맞는 짝꿍(@농담곰 성상님)을 만나서 더더욱 유쾌하고 명쾌하게 작업을 진행할 수 있었다! 같은 문제를 가지고도 다양하게 해석하고, 그 생각을 공유하는 과정이 너무 즐거웠고!(이렇게 좋은데 그동안 어떻게 혼자 공부했나 싶다😅) 궁금했던 점들을 같이 해결하다 보니 확실히 혼자서 하던 때보다는 더욱 효율적이었던 거 같다! (이쯤 되면 모든 팀플 과제에 mbti 궁합 별 팀 빌딩의 도입이 시급하다😆ㅎㅎ) 인생 첫 짝코딩이었는데 첫 단추를 잘 꿴 것 같아 아주 뿌듯하다!

    예상 시간보다 훨씬 빨리 기능 구현을 다 해서 조금 여유롭게 리팩토링과 README 편집 및 과제 회고 블로그까지 작성할 수 있었다. +) favicon까지 설정하고...ㅎㅎ🏄‍

    ▼ 이번 과제의 한 줄 요약: 모르는 것과 고민거리는 나누면 나눌수록 좋다...👍

    5. 도움을 주신 분들 🙇‍♀️