3일 동안 보지 않기 모달 구현(next.js, localStorage)

silnoon·2022년 7월 26일
4

사용자가 뉴스 스크립트를 보며 쉐도잉할 수 있도록 도와주는 서비스를 만들고 있습니다. 서비스를 만들던 중 모달을 만들며 고민했던 내용을 정리해 보았습니다.

  • next.js, typescript, styled-components 를 사용하고 있습니다.

👾 구현해야 할 기능

  • 스크립트 내에서 드래그를 통한 하이라이트 기능을 제공하는데, 같은 단어를 하이라이트 할 수 없기 때문에 모달을 띄워 해당 내용을 안내해 줍니다.
  • 3일 동안 보지 않기를 누르면 모달이 꺼지고, 3일 동안 사용자에게 모달이 띄워지지 않습니다.

  • 확인을 누르면 모달은 단순히 꺼집니다.


🤔 쿠키 vs 로컬 스토리지

해당 기능을 구현하려면 사용자가 같은 부분을 하이라이트하여 생긴 모달의 3일 동안 보지 않기 버튼을 눌렀을 때의 시간을 구해서 저장해 놓고, 나중에 사용자가 다시 같은 부분을 하이라이트 했을 때 시간과 비교해 봐야겠다고 생각했습니다.

쿠키는 클라이언트/서버 측에서 쿠키 데이터를 사용할 수 있으나, 로컬스토리지는 로컬 환경에서만 사용 가능하다는 차이점이 있습니다.

제가 구현해야 하는 기능은 단순히 버튼을 클릭한 시간만을 저장해야 합니다. 서버와의 연결은 따로 필요가 없습니다. 따라서 로컬 스토리지를 사용해도 충분할 것이라고 생각했습니다.

하지만 여기서 주의해야 하는 점은 storage 타입은 문자열 데이터만 저장하기 때문에 제가 원하는 기능을 구현하려면, 클릭한 시간을 문자열로 바꾸고 나중에 숫자로 다시 바꿔주는 과정이 추가로 필요합니다.



로컬스토리지(localStorage)

localStorage는 기본적으로 키(key), 값(value)을 보관합니다.

window.localStorage.setItem(key, value) : key에 value를 저장합니다.

window.localStorage.getItem(key) : key에 저장된 value를 가져옵니다.


Next.js에서 로컬스토리지 사용하기

Next.js에서 아무 곳에서나 window.localStorage를 사용하게 되면 ReferenceError: localStorage is not defined 오류를 볼 수 있습니다. 찾아봤더니 Next는 노드 환경에서도 쓰일 수 있어 무조건 브라우저에서만 실행되는 것이 아니기 때문에 나는 오류였습니다.

저는 useEffect를 사용해 해당 오류를 해결하였습니다. useEffect는 마운트 되었을 때만 실행되는 CSR 전용 훅이기 때문에 브라우저라는 확신을 준다고 합니다.



🖋 기능 구현하기


1. '3일 동안 보지 않기' 클릭한 시간 localStorage에 저장하기

모달 명은 StHighlightModal입니다.

const handleExpireTime = () => {
    const timeClicked = JSON.stringify(new Date().getTime());
    localStorage.setItem('timeClicked', timeClicked);
  };

return (
    <StHighlightModal>
      <StHighlightModalContent>
        <ImageDiv src={icAlert} className="alert" layout="fill" alt="" />
        <p>같은 단어를 하이라이트할 수 없어요!</p>
        <StTimeClosedSet
          onClick={() => {
            closeModal();
            handleExpireTime();
          }}>
          <ImageDiv src={icEmptyBox} className="checkbox" layout="fill" alt="checkbox" />
          <span>3일 동안 보지 않기</span>
        </StTimeClosedSet>
        <StOkayButton onClick={closeModal}>확인</StOkayButton>
      </StHighlightModalContent>
    </StHighlightModal>
  );

StTimeClosedSet은 체크박스 이미지와 3일 동안 보지 않기 텍스트를 감싼 div입니다. 이 div를 클릭하면 handleExpireTime()이 실행됩니다. 여기서 클릭한 시간을 담아 timeClicked라는 key에 value로 저장해 줍니다.

getTime은 1970년 1월 1일 00시 00분 00초 UTC를 기준으로 경과 한 밀리초를 반환합니다. 숫자만을 반환하기 때문에 경과한 시간을 계산하기 쉽습니다.

클릭 후 개발자 도구에서 localStorage를 확인하면 값이 잘 들어온 것을 확인할 수 있습니다.

이제 클릭한 시간을 가져왔으니, 사용자가 다시 같은 부분에 하이라이트를 했을 때 3일이 지나지 않았다면 모달이 렌더링 되지 않게 해주면 됩니다.

2. 렌더링될 때 시간을 구해서 3일이 경과했는지 판단하기

하이라이트 모달을 불러오는 페이지 return문에 있는 부분입니다. highlightAlert상태가 true일 때 렌더링 됩니다.

{highlightAlert && <HighlightModal closeModal={() => setHighlightAlert(false)} />}
  useEffect(() => {
    if (highlightAlert) {
      const now = new Date().getTime();
      const timeSaved = Number(localStorage.getItem('timeClicked'));
      if (timeSaved) {
        const gapHour = (now - timeSaved) / 1000 / 60 / 60;
        if (gapHour < 72) {
          setHighlightAlert(false);
        }
      }
    }
  }, [highlightAlert]);

now에 모달이 렌더링 되어야 할 순간의 시간(같은 부분을 하이라이트 했을 때)을 구해서 localStorage에 저장되어 있는 시간을 비교합니다. 경과된 시간을 gapHour가 판단해 highlightAlert 상태를 변경해 줍니다.

저는 3일을 기준으로 삼았기 때문에 72시간으로 지정해 주었습니다.

아직 저 모달을 띄울 상태 구현이 안 된 상태이기 때문에 useEffect에 빈 배열[ ]을 dependency에 담아주었지만, 구현이 완료된다면 모달을 띄울 상태를 담아줄 예정입니다.
사용자가 이미 하이라이트가 된 부분에 하이라이트를 했을 때 highlightAlert의 값은 true로 변경됩니다. 따라서 이를 dependency 값에 넣어주었습니다. (수정 완)


3. 결과

이미 하이라이트가 된 곳에 하이라이트를 해도 더 이상 모달이 나타나지 않습니다. timeClicked도 localStorage에 잘 담겨있음을 확인할 수 있습니다.



🔍 마치며

localStorage를 통해 3일 동안 보지 않기 모달을 구현해 보았습니다. 시간 계산을 자유롭게 변경할 수 있으니 n시간, n일 등 다양하게 활용할 수 있을 것입니다. 하이라이트 기능이 구현된 후에, useEffect의 dependency에 highlightAlert를 넣어주어 모달이 뜨게 할지 막을지 적절히 구현할 수 있었습니다. :)

profile
원래 실눈캐가 사기캐잖아요 (v ̄▽ ̄)

0개의 댓글

관련 채용 정보