나이 계산은 단순히 현재연도 - 출생연도로 끝나지 않습니다.
생일 전/후 경계, 2월 29일 출생자, 잘못된 날짜 입력, 시차로 인한 일수 오차까지 처리해야 결과를 신뢰할 수 있습니다.

문제: 나이 계산에서 실제로 자주 틀리는 지점

1) 생일 전인데 나이를 1살 더해버리는 오류

연도 차이만 쓰면 생일이 지나지 않았을 때 오답이 납니다.

2) 날짜 문자열 파싱 신뢰성 문제

브라우저별 new Date('YYYY-MM-DD') 해석 차이로 예상치 못한 결과가 나올 수 있습니다.

3) 윤년 출생자(2월 29일) 다음 생일 계산

평년에는 2월 29일이 없으므로, 다음 생일 기준일을 별도로 안전하게 잡아야 합니다.

4) 일수 계산 시 타임존 오차

시/분이 섞인 상태로 차이를 구하면 1일 단위 D-day가 흔들릴 수 있습니다.

원리: 안전 파싱 + UTC 일수 계산 + 경계 조건 분리

H3. 날짜 파싱은 숫자 분해 후 유효성 재검증

  • YYYY-MM-DD를 직접 split('-')
  • new Date(y, m-1, d, 12, 0, 0)로 생성
  • 다시 getFullYear/getMonth/getDate로 원본과 일치하는지 검증

이렇게 하면 2026-02-31 같은 값은 즉시 무효 처리됩니다.

H3. 만 나이는 생일 경과 여부로 보정

  1. 기본값: targetYear - birthYear
  2. 아직 생일 전이면 -1
  3. 최소 0으로 클램프

H3. 총 일수는 UTC 기준으로 계산

  • 날짜를 Date.UTC(year, month, day)로 변환
  • 두 UTC 타임스탬프 차이를 86,400,000으로 나눠 일수화

로컬 시각/서머타임 변화 영향을 줄여 D-day가 안정됩니다.

H3. 다음 생일은 “해당 연도 후보 → 이미 지났으면 다음 해”

  • 출생 월/일 기준 후보 생일 생성
  • 올해 후보가 기준일보다 이전이면 다음 해 후보 사용
  • 2월 29일은 해당 연도의 마지막 유효일(평년 2/28)로 보정

핵심 코드: 렌더 루프에서 계산 + 출력 일괄 처리

const toDate = (value) => {
  if (!value) return null;
  const [y, m, d] = value.split('-').map(Number);
  if (!y || !m || !d) return null;

  const parsed = new Date(y, m - 1, d, 12, 0, 0, 0);
  const isValid =
    parsed.getFullYear() === y &&
    parsed.getMonth() === (m - 1) &&
    parsed.getDate() === d;

  return isValid ? parsed : null;
};

const dayDiff = (a, b) => {
  const toUTCDate = (d) => Date.UTC(d.getFullYear(), d.getMonth(), d.getDate());
  return Math.floor((toUTCDate(b) - toUTCDate(a)) / 86400000);
};

const getNextBirthday = (b, t) => {
  const month = b.getMonth();
  const day = b.getDate();

  const safeDate = (y) => {
    const lastDay = new Date(y, month + 1, 0).getDate();
    return new Date(y, month, Math.min(day, lastDay), 12, 0, 0, 0);
  };

  let candidate = safeDate(t.getFullYear());
  if (dayDiff(t, candidate) < 0) candidate = safeDate(t.getFullYear() + 1);
  return candidate;
};

핵심은 입력 검증 → 계산 → 표시를 한 렌더 흐름으로 유지해 상태 불일치를 줄인 점입니다.

예외 처리: 신뢰 가능한 결과를 위한 방어선

1) 생년월일/기준일 누락

둘 중 하나라도 없으면 계산 대신 안내 메시지 표시.

2) 생년월일 > 기준일

논리적으로 불가능한 입력이므로 결과를 숨기고 오류 안내.

3) 세는나이 표시 토글

요구사항에 따라 한국식 나이는 체크박스로 ON/OFF 가능하게 분리.

4) 복사 API 실패 폴백

navigator.clipboard 실패 시 textarea + execCommand('copy')로 대체.

5) 초기값 안정화

기준일이 비어 있으면 오늘 날짜(ISO)로 자동 세팅해 첫 진입 UX를 보장.

내부 링크: 같이 쓰면 좋은 계산 흐름

요약

만나이 계산기 구현의 포인트는 다섯 가지입니다.

  • 날짜 파싱을 직접 검증해 잘못된 입력 차단
  • 만 나이는 생일 경계 보정으로 정확도 확보
  • 일수 계산은 UTC 기반으로 오차 최소화
  • 2월 29일 출생자 생일을 연도별 유효일로 보정
  • 입력/복사/초기값 예외 처리로 실사용 안정성 강화

FAQ

Q1. 왜 new Date('YYYY-MM-DD')를 바로 쓰지 않나요?

브라우저/환경별 해석 차이와 타임존 이슈를 줄이기 위해 숫자 분해 + 재검증 방식을 사용합니다.

Q2. 2월 29일 출생자는 평년에 언제 생일로 계산되나요?

해당 월의 마지막 유효일을 사용해 평년에는 2월 28일 기준으로 계산합니다.

Q3. 총 개월 수와 만 나이는 왜 다르게 보일 수 있나요?

개월 수는 “일(day) 단위 경과”를 반영하고, 만 나이는 “생일 도달 여부”를 기준으로 계산하기 때문입니다.

Q4. 기준일을 미래로 넣어도 되나요?

가능합니다. 특정 미래 시점의 만 나이와 다음 생일까지 남은 일수를 확인할 수 있습니다.