본문 바로가기
D.evelop/Next.js

[NextJS]🕐카운트다운 구현하기(+TS, +chatGPT를 통한 코드리뷰)

by Danne 2023. 10. 17.

- NextJS (App Router)

- Typescript

더보기

CodeSandbox를 사용해보고 있는데, 작업환경 대로 세팅하기가 쉽지 않았습니다.

NextJS로 세팅을 하니, 타입스크립트가 자동으로 설정돼있었습니다. 

설정을 바꾸는 법도 있다고 들었는데, 이 참에 공부해보자 싶어 도전했습니다. (간단한 자료형을 추가하는 것 정도.)

 

구글링으로 막히면 동기 개발자 친구분께 SOS!!

 


카운트다운 구현하기

 

[code 구현]

https://codesandbox.io/p/sandbox/agitated-jackson-38lcv4

 

 

 

[ 최초 코드 ]

└ /page.tsx

"use client";

import { useEffect, useState, useRef } from "react";

export default function Home() {
  const endDate = "2030-01-04 00:00:00";
  const [counter, setCounter] = useState(0);
  const [day, setDay] = useState(0);
  const [hour, setHour] = useState(0);
  const [min, setMin] = useState(0);
  const [sec, setSec] = useState(0);
  const time = useRef(0);
  const timerId: any = useRef(null);

  useEffect(() => {
    setCounter(new Date(endDate).getTime() - new Date());  // 오류

    timerId.current = setInterval(() => {
      setCounter(new Date(endDate).getTime() - new Date()); //오류
      time.current -= 1;
    }, 1000);

    return () => clearInterval(timerId.current);
  }, []);

  useEffect(() => {
    const days = Math.floor(counter / (1000 * 60 * 60 * 24));
    const hours = Math.floor(counter / (1000 * 60 * 60));
    const mins = Math.floor(counter / (1000 * 60));
    const secs = Math.floor(counter / 1000);

    setDay(days);
    setHour(hours - days * 24);
    setMin(mins - hours * 60);
    setSec(secs - mins * 60);
    if (counter < 0) {
      clearInterval(timerId.current);
      setCounter(0);
    }
  }, [counter]);

  return (
    <main className="flex min-h-screen flex-col items-center justify-between p-24  bg-gradient-to-b">
      <p>
        {day} : {hour} : {min} : {sec}
      </p>
    </main>
  );
}

"use client";

샘플코드라 바로 page.tsx 에서 작업을 했는데 렌더링이 되지 않았습니다

NextJS의 App Router버전에서 클라이언트 구성요소를 사용하려면 "use client" 지시어를 추가해야합니다.

 

NEXT.JS공식문서

 

 

TypeError

// 오류난 코드
setCounter(new Date(endDate).getTime() - new Date());

// 개선한 코드
setCounter(new Date(endDate).getTime() - new Date().getTime());

new Date(endDate).getTime() - new Date() 연산과정에서 계속 오류가 발생했습니다.

 

서로 다른 두 자료형을 연산해서 발생한 이유였습니다.

new Date() 생성자는 object 형이 반환됩니다.
new Date().getTime() 은 number형을 반환합니다.

number - object 연산을 시도해서 난 오류였습니다.

타입스크립트를 사용하니 자료형에 엄격한 것이 체감됩니다.

 

 

 

 

✅ 스터디 동기분이 chatGPT4로 코드 피드백을 해주셨습니다.

✅ 첫 작업 전 UI기획서를 보고

시간을 "일 / 시 / 분 /  초" 단위로 분리하여 상태를 각각 관리 해야하는게 좋을 까?

객체로 묶어 한 번에 관리하는게 좋을까?

를 고민했었고, 나는 전자를 택했었습니다.

 

초기 기획서였고, 버전업이 되면 계속 일, 시, 분, 초가 따로 사용될 여지도 있을 것같은 예상이 가장 큰 이유였습니다.

"초"단위의 카운트다운은 없었지만 나중에 필요할 까봐 만들어뒀었거든요.

어차피 분단위 계산을 위해 필요한 계산이긴 해서.

(경험이 남긴 방어막이랄까)

 

하지만 setCounter가 호출 될 때마다 useState의 리랜더링이 다중적으로 발생하는데, 랜더링 비용을 절약하는 react의 장점을 상쇄시켜버리게 되는걸 깨달았습니다.

그래서 동시에 연산되어야하는 같은 자료형의 같은 성격의 UI는 되도록 한 객체로 묶어 useState로 관리해 주는 것이 더 좋다는 생각입니다.

 

 

 

📍 chatGPT 가 최적화 해준 코드

"use client";
import { useEffect, useState, useRef } from "react";

export default function Home() {
  const endDate = "2030-01-04 00:00:00";
  const [time, setTime] = useState({
    days: 0,
    hours: 0,
    minutes: 0,
    seconds: 0
  });
  
  const timerId = useRef(null);

  useEffect(() => {
    const computeTime = () => {
      const diff = new Date(endDate).getTime() - new Date().getTime();

      if (diff <= 0) {
        clearInterval(timerId.current);
        return;
      }

      const days = Math.floor(diff / (1000 * 60 * 60 * 24));
      const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
      const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
      const seconds = Math.floor((diff % (1000 * 60)) / 1000);

      setTime({ days, hours, minutes, seconds });
    }

    computeTime(); // Compute initially
    timerId.current = setInterval(computeTime, 1000);

    return () => clearInterval(timerId.current);
  }, []);

  return (
    <main className="flex min-h-screen flex-col items-center justify-between p-24">
      <p className="fixed left-0 top-0 flex w-full justify-center border-b border-gray-300 bg-gradient-to-b from-zinc-200 pb-6 pt-8 backdrop-blur-2xl dark:border-neutral-800 dark:bg-zinc-800/30 dark:from-inherit lg:static lg:w-auto  lg:rounded-xl lg:border lg:bg-gray-200 lg:p-4 lg:dark:bg-zinc-800/30">
        {time.days} : {time.hours} : {time.minutes} : {time.seconds}
      </p>
    </main>
  );
}

 

 

 

 

💡 팀과 함께하는 경우, 일관 된 방식을 선택하여 프로젝트의 일관성을 유지하는 것이 중요하다.

 

 

💡

간단하던 페이지에 input이 "더 보태, 더 보태"가되면서 상태값을 어떻게 분리해야할지 고민을 하게 되는 지점이 생기게 됩니다.

- 객체 등으로 합치는게 효율적이라고 생각했는데, 작업 마무리 시점에 UX 기획이 수정되면서 페이지 구성자체가 분리되는 경우도 생기도 합니다.

시+분+초가있던 한 페이지가, 시 페이지 / 분 페이지 / 초 페이지로 나뉘다가 합쳐지는 등....

 

이런 경우를 계속 겪을 수록 과거의 경험을 끌고와서 미리 예측 하며 작업을 하게 되는데, 요즘은 작게 쪼개버리는 식으로 작업을 하게 됩니다.
하지만 지나치게 분리된 소스를 다시 하나씩 찾아붙이는 작업에 또 다른 비용이 발생되기도 합니다.


이런식으로 많은 가능성을 열어놓고 작업을 하려다보면 진척이 되나 싶을 때고 있고요.

 

이게 참...경험이 쌓이는 건지, 겁이 쌓이는건지 모르겠기도합니다요.😅

늘 주객전도가 되지 않은 적정선을 지키려고 노력해 봅니다.

 

 


 

 

피드백과 수정사항 마구 공유주시면,
마구마구 감사합니다.

열심히 배워가는 중입니다🤓

반응형

댓글