자동화 slecs

특가 프로모션에 실시간 카운트다운 추가

목차

오늘만 제공되는 특가 프로모션에 카운트다운 타이머를 추가했다. 사용자의 로컬 시간대 기준으로 자정까지 몇 시간 몇 분 몇 초가 남았는지 HH:MM:SS 형식으로 실시간 표시하고, 자정이 지나면 자동으로 리셋되는 기능이다.

특가와 시간 압박의 심리학

이커머스 같은 마켓플레이스에서 "오늘만 특가"는 가장 효과적인 프로모션 중 하나다. 제한된 시간 안에만 할인을 받을 수 있다는 제약이 고객의 구매 결정을 가속화하기 때문이다. 심리학적으로 희소성과 시간 압박은 강력한 결정 동기가 되는데, 단순히 텍스트로 "내일 사라집니다"라고만 하는 것보다 실시간으로 틱 틱 떨어지는 타이머가 훨씬 더 긴박감을 전달한다.

그런데 지금까지 reading-panel 페이지에서는 특가 배너만 보여줬을 뿐, 정확히 언제까지 남아있는지는 별도의 설명 텍스트나 배너 섹션으로만 표현했다. 이번 작업은 그걸 활성화된 타이머로 직관적으로 시각화하면서 동시에 고객 경험을 한 단계 업그레이드하는 게 목표였다. 특가가 남은 시간을 계속 볼 수 있으면, 고객은 "지금 지나치면 내일은 원래 가격"이라는 걸 명확히 인식하게 된다.

로컬 자정 기준 카운트다운 구현

구현의 핵심은 "로컬 자정"이라는 개념이었다. 서버 시간대가 아니라 사용자의 브라우저 시간대를 기준으로 자정(00:00:00)까지의 남은 시간을 계산해야 한다. 왜냐하면 고객 입장에서 "오늘"은 자신의 현지 시간으로 정의되기 때문이다. 서울에 있는 사용자와 뉴욕에 있는 사용자는 각각 서울 자정, 뉴욕 자정까지 남은 시간이 달라야 한다는 뜻이다.

// 로컬 자정까지 남은 시간 계산 패턴
const now = new Date();
const tonight = new Date();
tonight.setHours(24, 0, 0, 0); // 내일 자정(= 오늘 00:00 + 24시간)

const msRemaining = tonight - now;
const hours = Math.floor(msRemaining / (1000 * 60 * 60));
const minutes = Math.floor((msRemaining % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((msRemaining % (1000 * 60)) / 1000);

// "HH:MM:SS" 형식으로 패딩
const display = `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`;

이 계산을 reading-panel.tsx에서 수행하고, 매초 갱신하는 타이머 인터벌을 설정했다. 또한 자정을 지나는 순간을 감지해서 자동으로 리셋되도록 처리해야 했는데, 단순히 남은 시간이 음수가 되는 걸 확인하는 것보다는, 날짜가 변경된 순간을 명시적으로 감지하는 게 더 안정적이다.

상태 관리와 자동 리셋 로직

타이머 기능을 추가할 때 항상 조심해야 할 부분이 상태 갱신의 정확성과 메모리 누수다. 아래는 구현 시 고려했던 체크리스트다:

항목 상세
인터벌 정리 컴포넌트 언마운트 시 setInterval 반드시 clearInterval
자정 감지 msRemaining이 음수가 되는 순간뿐 아니라 Date 객체의 day 값 변경 감시
초기값 처리 페이지 로드 직후 첫 번째 렌더링에서 올바른 남은 시간 표시
포맷팅 HH:MM:SS 형식으로 패딩 (예: "03:45:22", 절대 "3:45:22" X)

가장 흔한 버그는 인터벌 정리 누락인데, 사용자가 페이지를 떠나도 타이머가 계속 실행되면서 메모리를 낭비한다. 특히 reading-panel처럼 반복적으로 방문하는 페이지라면 더더욱 위험하다.

또 다른 고민은 자정을 정확히 감지하기였다. 일반적으로 "자동 리셋"은 남은 시간이 0을 지나는 순간을 감지하는 방식으로 구현할 수 있지만, 네트워크 지연이나 탭 포커스 손실로 타이머가 일시적으로 멈췄을 때를 대비하려면 현재 날짜를 명시적으로 확인하는 게 더 견고하다. 자정을 넘어가는 순간 Date 객체의 getDate()가 바뀌는지 감시하는 식이다.

기술 구현에서 배운 점들

이 작업을 통해 깨달은 건, 작은 UI 기능이라도 클라이언트 타이머 처리가 생각보다 복잡하다는 것이다. 특히:

  • 사용자의 시간대를 정확히 고려해야 한다 (서버 시간대 ≠ 클라이언트 시간대)
  • 자정 경계에서의 상태 전환이 명확하고 재현 가능해야 한다
  • 성능과 메모리를 동시에 고려해야 한다 (무한 갱신 ≠ 적절한 인터벌)
  • QA와 테스트 시나리오가 까다롭다 (자정을 넘기는 테스트를 위해 시스템 시간 조작 필요)

특히 팀에서 코드리뷰할 때 "이 타이머가 사용자 시간대를 따르나요?", "자정 정확히 리셋되나요?"라는 질문이 나올 가능성이 높으니까, 처음부터 그 부분을 명확히 주석과 문서로 남겨두는 게 좋다. 다음에 비슷한 기능을 추가할 땐 자정 경계와 시간대 변경을 포함한 테스트 케이스를 미리 설계할 계획이다.


🛒 이 글과 어울리는 추천 상품

*위 링크는 쿠팡파트너스 활동의 일환이며, 일정액의 수수료를 제공받을 수 있습니다.

댓글 0

첫 댓글 달아줘.