나는 습관 추적기의 핵심 논리를 하나의 훅으로 재구성했다. 제가 배운 것은 다음과 같습니다.

작성자

카테고리:

← 피드로
DEV Community · Alexander Kazanski · 2026-07-09 개발(SW)

Alexander Kazanski

Habit and streak tracking looks simple until you actually build it: timezones, missed days, “does logging at 1am count for yesterday,” and the eternal question of whether a streak breaks at midnight or at the user’s personal reset time. Most of that complexity belongs in a hook, not scattered across your components.

Extracting the logic

function useStreak(entries, { resetHour = 0 } = {}) {
  const adjustedDate = (timestamp) => {
    const d = new Date(timestamp);
    d.setHours(d.getHours() - resetHour);
    return d.toDateString();
  };

  const streak = useMemo(() => {
    const days = [...new Set(entries.map((e) => adjustedDate(e.loggedAt)))].sort();
    let current = 0;
    for (let i = days.length - 1; i >= 0; i--) {
      const expected = new Date();
      expected.setDate(expected.getDate() - (days.length - 1 - i));
      if (days[i] === adjustedDate(expected.getTime())) current++;
      else break;
    }
    return current;
  }, [entries, resetHour]);

  return { streak };
}

Enter fullscreen mode Exit fullscreen mode

The resetHour parameter matters more than it looks. A user who works night shifts and logs a midnight workout shouldn’t lose their streak because your app assumes everyone’s day ends at 12am.

Why this belongs in a hook, not a utility function

You could write this as a plain function and call it from useMemo in every component that needs it. The reason to wrap it as a hook is composability — you can layer useStreak with other hooks (useNotificationSchedule, useMilestone) without threading the same recalculation logic through five components. It also makes this logic trivially unit-testable in isolation from any UI.

The honest limitation

Streaks are a motivational device, not a health metric, and they can push people toward all-or-nothing thinking — one missed day feels like failure instead of a data point. If you’re building this for a health-adjacent product, consider surfacing “longest streak” alongside “current streak,” so a broken streak doesn’t erase the user’s sense of progress. Small framing decision, real behavioral impact.

원문에서 계속 ↗

코멘트

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다