본문 바로가기
매일 해내는 개발/Develog

[Develog] D-day와 옵저버

by 해야지 2023. 3. 9.
반응형

D-day 기능 구현과 옵저버

유저 피드백을 받으면서 D-day가 생겼다.

마감일을 기준으로 프로젝트 모집이 며칠이나 남았는지 보였으면 좋겠다는 피드백이었다.

 

처음 짠 코드는 이랬다.

//디데이 계산
export const getDays = (milliseconds: number) => {
  const date = new Date(milliseconds);
  const day = `${date.getDate() - 1}`.padStart(2);
  return `${day}`;
};
생략

const day = Number(getDays(deadline - today));

return(
<ImageWrap>	
    <ContentCardImgContainer
      src={thumbnail || defaultThumbnail}
      onClick={onNavigateToProjectDetailEvent(id)}
      alt={title + ` 프로젝트 썸네일`}
    />
    {isRecruiting && day >= 0 && ( // 현재 모집 중이고 남은 일자가 0일 이상이면
      <DeadLineIcon day={day}>
        {day === 0 ? '마감일' : `D - ${getDays(deadline - today)}`}
      </DeadLineIcon>
    )}
</ImageWrap>
...
중략



const DeadLineIcon = styled.div<{ day: number }>`
  z-index: 1000;
  position: absolute;
  top: 10px;
  right: 10px;
  width: 3.75rem;
  height: 1.75rem;
  font-style: normal;
  font-weight: 500;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  border-radius: 2.5rem;
  /* padding: 0rem 0.5rem; */
  background-color: ${({ day }) =>
    day <= 3 ? `${COLORS.red}` : `${COLORS.gray100}`};
  color: ${({ day }) => (day <= 3 ? `${COLORS.white}` : `${COLORS.gray400}`)};
  font-size: 0.75rem;
`;

 

하지만 이 부분에서 남은 일수를 계산하는 부분이 모호했다.

계산이 아니라 일자를 끼워맞췄다.

날짜 함수 때문인지 staleTime 때문인지 12시 땡하고 디데이가 업데이가 되지 않았다.

나는 12시 땡하면 D-day가 바뀌길 원했다.

그래서 디데이 계산 함수를 수정했다.

//디데이 계산
export const getDays = (milliseconds: number) => {
  const hours = Math.floor(milliseconds / (1000 * 60 * 60));
  return Number(Math.floor(hours / 24));
};

처음에 짰던 코드는 날짜로 변환해서 일자로 가져오는건 로직이었기 때문에 잘못됐고 deadline에서 지금 시간을 뺀 수를 24로 나눠 일자로 계산했다.

 const [RemainDays, setRemainDays] = useState(getDays(deadline - today));
let today: any = Date.now();

  useEffect(() => {
    const intervalId = setInterval(() => {
      const timeDiff = deadline - today;
      setRemainDays(getDays(timeDiff));
      if (timeDiff <= 0) {
        updateRecruitingMutate(id, false as any);
      }
      console.log(getDays(timeDiff), getDateAndTime(today));
    }, 1000 * 60 * 10); //10분에 한번씩 모니터링

    return () => clearInterval(intervalId);
  }, []);

그리고 setInterval을 사용하여 옵저버를 만들었다.

10분에 한번씩 모니터링 하여 업데이트할 수 있도록 한다.

테스트를 위해 현재 시간을 23시 59분으로 설정한 후 2초에 한번씩 1분이 지나게 했다.

이렇게 하면 12시가 되자마자 D-day가 업데이트 되는 것을 확인 할 수 있다.

 

하지만 리액트에서 이렇게 setInterval을 사용해 옵저버를 만들었을 때 다음과 같은 문제점이 발생한다.

  1. 메모리 누수(Memory leaks) : setInterval은 컴포넌트가 언마운트(unmount)되어도 계속 실행될 수 있다. 이는 메모리 누수로 이어지며 컴포넌트가 언마운트될 때 반드시 clearInterval을 호출하여 이를 방지해야 한다.
  2. 성능 저하 : setInterval은 일정한 시간 간격으로 작업을 수행하므로 불필요한 렌더링이 발생할 수 있다. 예를 들어, setInterval이 1초마다 실행되는 경우, 1초마다 상태(state)가 변경될 때마다 컴포넌트가 다시 렌더링되어 성능이 저하될 수 있다.
  3. 상태 업데이트 문제 : setInterval로 상태를 업데이트하는 경우, 이전 상태를 기반으로 새로운 상태를 계산해야 한다. 하지만 React에서 상태 업데이트는 비동기(asynchronous)로 처리되므로, 이전 상태가 바로 반영되지 않을 수 있다. 이로 인해 예상치 못한 결과가 발생할 수 있다.

 

그렇기 때문에 정말 꼭 필요한 곳이 아니라면 사용하지 않아도 좋을 것 같다.

우리 프로젝트에서는 invalidateQueries를 사용하기 때문에 옵저버는 필요하지 않을 것 같아서 날짜 변환 함수만 적용했다.

반응형

댓글