[Main Project] UDog / 구현하기 - 자동 로그아웃, 무한스크롤 리팩토링

soohyunee·2023년 4월 4일
0

[Main Project] UDog

목록 보기
16/18

1. 구현하기

진행 상황

  • 자동 로그아웃 구현
  • 무한스크롤 리팩토링

진행 예정

  • 데모데이

2. TIL

2-1. 자동 로그아웃 기능 구현

setTimeout을 활용한 로그아웃

다른 팀들에게 피드백을 받았는데 그 중 시간이 지났는데 로그인 상태였다라는 피드백을 받았었다. 엑세스 토큰의 유효시간이 길고 백엔드 측에서 아직 그 부분을 보완 중이어서 우선 프론트 측에서 유효시간이 지나면 로그아웃이 되게끔 구현을 해보고자 자동 로그아웃 기능을 구현해보았다.

유효시간 저장하기

우선 모멘트를 활용하여 로그인이 성공하면 현재 시간에 유효시간인 40분을 더해준다. 그리고 toISOString() 메서드를 사용해서 ISO 형식으로 바꿔준다. ISO 형식으로 바꿔주는 이유는 현재 시간과 유효시간을 비교할 것이기 때문에 비교 형식을 맞춰주는 것이다.

 const expiration = moment().add(40, 'minutes').toISOString();

TokenChecker.js

토큰이 유효한 토큰인지 체크해주는 컴포넌트를 만든다. 이 컴포넌트는 App.js에서 우선 로그인한 유저라면 App.js에서 렌더링 되는 모든 컴포넌트에서 첫 렌더링 시에 토큰이 유효한지 확인을 해준다.

const { user } = useSelector(selectUser);

{user && <TokenChecker />}

만일 컴포넌트의 첫 렌더링 시에 현재 시간이 유효시간을 초과했을 경우 로그아웃 처리가 진행된다. 로그인 세션 만료 시간이 아직 지나지 않았다면, 남은 시간을 계산하고 setTimeout 함수를 사용하여 남은 시간이 지나면 로그아웃 처리되도록 설정한다. 이때, setTimeout 함수가 반환하는 timerId 값을 저장하여 나중에 clearTimeout 함수를 사용하여 타이머를 취소할 수 있도록 한다.
마지막으로, useEffect 훅에서 return 문을 사용하여 cleanup 함수를 정의한다. 이 함수에서는 clearTimeout 함수를 사용하여 이전에 설정한 타이머를 취소한다. 이렇게 함으로써, useEffect 훅이 재실행될 때 이전에 설정한 타이머를 취소할 수 있다.

  useEffect(() => {
    const expiration = moment(localStorage.getItem('exp'));
    const now = moment();
    if (now.isAfter(expiration)) {
      dispatch(setTime('로그인 유효시간이 만료되었습니다.'));
      dispatch(logout());
    } else {
      const timeout = expiration.diff(now);
      const timerId = setTimeout(() => {
        dispatch(setTime('로그인 유효시간이 만료되었습니다.'));
        dispatch(logout());
      }, timeout);
      return () => {
        clearTimeout(timerId);
      };
    }
  }, [dispatch]);

2-2. Intersection Observer

무한스크롤 리팩토링

기존에는 스크롤 이벤트를 사용하여 무한스크롤을 구현했었다. 무한스크롤은 크게 두가지 방법으로 구현할 수 있었는데 스크롤 이벤트를 활용하는 방법과 옵저버를 활용하는 방법이었다. 처음에는 스크롤 이벤트도 사실 완벽하게 이해하고 있지 않았기 때문에 옵저버는 너무나도 생소했었고, 참고 자료가 비교적 더 많은 스크롤 이벤트로 먼저 구현한 뒤 추후에 리팩토링을 해야겠다라고 생각했었다.

스크롤 이벤트 vs 인터섹션 옵저버

우선 스크롤 이벤트는 인터섹션 옵저버에 비해 구현하기가 쉽다는 장점이 있다. 하지만 스크롤을 할 때마다 이벤트가 발생하기 때문에 성능 저하의 문제가 발생할 수 있다는 단점이 있다. 그래서 최근엔 인터섹션 옵저버를 사용하여 무한스크롤을 구현하는 것이 추천되고 있다.
인터섹션 옵저버는 자바스크립트의 내장 API로 기본적으로 브라우저 뷰포트와 설정한 요소의 교차점을 관찰해서 target 요소가 root 요소와 교차가 일어나는지(뷰포트에 포함되는지 안 되는지)를 판단하여 콜백 함수를 실행할 수 있다. 따라서, target 요소를 적절한 위치에 배치를 하면 사용자가 스크롤을 이동하는 것에 따라 데이터 요청을 할 수 있다.

무한 스크롤 구현하기

컴포넌트가 렌더링 됐을 때 옵저버가 생성되어야 하기 때문에 useEffect를 사용해준다. 의존성 배열에 전달된 값인 lastRef, hasMore, loading이 변경될 때마다 함수가 실행된다.
lastRef는 렌더링된 마지막 요소를 가리키는 React의 useRef 훅의 반환값이고, observer도 마찬가지로 useRef 훅의 반환값이다.

  const observer = useRef(null);
  const lastRef = useRef(null);

useEffect 함수 내부에서 lastRef가 존재하면(즉, 렌더링이 완료된 상태에서 마지막 요소가 존재하면), IntersectionObserver 객체를 생성하고, lastRef를 관찰한다.
IntersectionObserver의 생성자 함수에는 인자로 콜백 함수가 전달되는데 이 콜백 함수는 관찰 대상이 뷰포트와 교차되는(intersecting) 상태일 때 호출된다.
콜백 함수 내부에서는 hasMore와 loading 상태가 false이면(hasMore는 추가 데이터가 있는지 여부를 나타내고, loading은 현재 데이터를 로딩 중인지 여부를 나타낸다.), setPage 함수를 호출하여 페이지 번호를 1 증가시킨다.
return 구문에서는 IntersectionObserver 객체의 disconnect 메서드를 호출하여 객체를 제거한다.

useEffect(() => {
    if (lastRef.current) {
      observer.current = new IntersectionObserver((entries) => {
        const target = entries[0];
        if (target.isIntersecting && hasMore && !loading) {
          setPage((prevPage) => prevPage + 1);
        }
      });
      observer.current.observe(lastRef.current);
    }
    return () => {
      if (observer.current) {
        observer.current.disconnect();
      }
    };
  }, [lastRef, hasMore, loading]);

무한 스크롤 커스텀훅이 다 작성되었으니 무한 스크롤이 사용되는 컴포넌트에서는 빈 div를 만들어 주고 lastRef를 전달해준다. lastShopRef를 이용하여 마지막 HairshopList 컴포넌트를 가리키는 타겟을 설정해준다.

const { data, lastRef } = useInfiniteScroll(STYLEBOOK_ENDPOINT, PER_PAGE);

// 리스트 컴포넌트
<div ref={lastRef}></div>
profile
FrontEnd Developer

0개의 댓글