[마이 프로젝트] 블로그 목록에 '마감 임박' 딱지 자동으로 붙이기

블로그 목록에 마감 임박 딱지가 자동으로 붙는 모습을 나타낸 대표 이미지
날짜만 있으면 딱지는 저절로 붙습니다.

목록에 ‘마감 임박’ 같은 표시를 손으로 붙이면 금방 틀어집니다. 어제 임박이던 게 오늘은 끝났을 수 있으니까요. 그래서 날짜만 보고 스스로 판정하게 만들었습니다. 이 글은 그 함수들을 그대로 공개합니다.

날짜 형식이 제각각이었다

첫 번째 문제가 이것이었습니다. 받아온 데이터의 마감일이 전부 다른 모양이었습니다. 2026-09-30도 있고, 2026.09.30도 있고, 2026년 9월 30일까지처럼 문장으로 된 것도 있었습니다.

그래서 형식을 맞추려 하지 않고, 문자열 안에서 날짜처럼 보이는 것을 찾아내는 쪽으로 갔습니다.

function deadlineTime(value){  var matches = Array.from(String(value||'')    .matchAll(/(20\d{2})[-./]?(\d{1,2})[-./]?(\d{1,2})/g));  var match = matches.length ? matches[matches.length-1] : null;  return match    ? new Date(+match[1], +match[2]-1, +match[3], 23,59,59).getTime()    : 8640000000000000;   // 날짜를 못 찾으면 '아주 먼 미래'로 둔다}

두 가지를 눈여겨볼 만합니다. 날짜가 여러 개 나오면 마지막 것을 씁니다. “9월 1일부터 9월 30일까지” 같은 문장에서 마감일은 뒤에 오기 때문입니다.

그리고 날짜를 못 찾으면 아주 먼 미래 값을 돌려줍니다. 이러면 “아직 안 끝났다”로 취급됩니다. 날짜를 못 읽었다고 멀쩡한 항목을 종료 처리하는 것보다 낫다고 봤습니다.

날짜 문자열에서 마감 시각을 뽑아 상태를 판정하는 흐름
한 번 시각으로 바꿔 두면 나머지는 비교만 하면 됩니다.

이 코드가 기대는 자바스크립트 동작

짧은 함수지만 줄마다 표준 동작에 기대고 있습니다. MDN 문서에 적힌 내용으로 하나씩 풀어 보겠습니다.

  • matchAll은 g 플래그가 필수입니다. MDN에 따르면 전역(g) 플래그가 없는 정규식을 넘기면 TypeError가 납니다. 위 코드의 정규식 끝에 /g가 붙은 이유입니다.
  • 결과는 한 번만 훑을 수 있는 반복자입니다. MDN은 matchAll이 돌려주는 값을 다시 시작할 수 없는(not restartable) 반복자라고 설명하고, 배열로 바꿀 때 Array.from()이나 전개 구문을 쓰라고 안내합니다. 코드가 Array.from으로 감싼 덕분에 “마지막 일치 항목”을 인덱스로 꺼낼 수 있습니다.
  • 월은 0부터 셉니다. Date 생성자에서 월은 0(1월)부터 11(12월)까지라서, 코드의 +match[2]-1이 필요합니다.
  • 연·월·일 숫자는 지역 시간으로 읽힙니다. MDN은 둘 이상의 인수를 받는 생성자가 연, 월, 일, 시, 분, 초를 지역 시간으로 해석한다고 설명합니다. 그래서 마감일 23시 59분 59초는 보는 사람 기기의 시간대 기준이 됩니다.
  • ‘아주 먼 미래’ 값은 표현 가능한 마지막 시각입니다. Date가 담을 수 있는 시각은 기준 시점에서 ±8,640,000,000,000,000밀리초까지이고, 이를 넘으면 NaN이 되어 “Invalid Date”가 됩니다. 코드의 8640000000000000은 그 상한과 같은 값이라, 날짜 객체로 바꿔도 깨지지 않는 가장 먼 시각입니다.

시각으로 바꿔 두면 나머지는 쉽다

날짜를 숫자로 바꿔 두면 판정은 비교 한 줄이 됩니다.

function isUrgent(value){  var time = deadlineTime(value);  if (time === 8640000000000000) return false;   // 마감 없는 건 임박 아님  var days = (time - Date.now()) / 86400000;  return days >= 0 && days <= 14;                // 14일 이내면 임박}function isExpired(value){  var time = deadlineTime(value);  return time !== 8640000000000000 && time < Date.now();}function isAlwaysOpen(value){  return /(상시|수시|연중|항시)/.test(String(value||''));}

isAlwaysOpen은 날짜가 아예 없는 경우를 위한 것입니다. “상시 모집”처럼 적힌 항목에 마감 딱지를 붙이면 이상하니까 따로 걸러 냅니다.

입력 문자열별로 어떻게 읽히는지

정규식은 “20으로 시작하는 네 자리 연도, 구분 기호(-, ., / 중 하나 또는 없음), 한두 자리 월, 구분 기호, 한두 자리 일”을 찾습니다. 이 규칙을 같은 코드로 직접 실행해 보면(한국 시간대 기준) 아래처럼 읽힙니다.

입력 문자열읽은 결과판정에 쓰이는 값
2026-09-302026년 9월 30일2026-09-30 23:59:59
2026.09.302026년 9월 30일2026-09-30 23:59:59
202609302026년 9월 30일2026-09-30 23:59:59
2026-09-01 ~ 2026-09-30일치 항목 두 개 중 마지막2026-09-30 23:59:59
2026년 9월 30일까지일치 없음(연도 뒤에 ‘년’ 글자가 와서 규칙에 안 맞음)아주 먼 미래
9월 1일부터 9월 30일까지일치 없음(연도가 없음)아주 먼 미래
상시 모집일치 없음아주 먼 미래(isAlwaysOpen이 따로 거름)
2026.13.45없는 날짜인데도 숫자는 일치2027-02-14 23:59:59로 넘어감

이 표에서 주의할 곳은 두 군데입니다. 첫째, 문장형인 2026년 9월 30일까지는 이 정규식으로는 읽히지 않고 먼 미래로 처리됩니다. 문장형 날짜를 읽으려면 ‘년·월·일’ 글자를 허용하도록 정규식을 넓혀야 합니다. 둘째, 2026.13.45 같은 없는 날짜는 오류가 나지 않고 다음 해로 넘어간 값이 됩니다. MDN도 범위를 벗어난 값은 윗단위로 넘어간다고 설명합니다(예: 12월은 다음 해 1월, 0일은 전달 마지막 날). 데이터를 넣는 쪽에서 날짜 형식만 지켜도 이 문제는 대부분 피할 수 있습니다.

‘신규’는 다른 날짜를 본다

신규 표시는 마감일이 아니라 수정된 날짜를 봅니다. 30일 안에 갱신됐으면 새 것으로 칩니다.

마감 판정에서 정한 세 가지 기준을 정리한 설명 이미지
기준을 숫자로 정해 두면 다툴 일이 없습니다.

기준 숫자를 정한 이유

14일과 30일이라는 숫자에 대단한 근거는 없습니다. 다만 한 번 정해 두면 화면 전체가 같은 기준으로 움직입니다. 여기저기서 다르게 판정하는 것보다 낫습니다.

바꾸고 싶으면 이 두 숫자만 고치면 됩니다. 판정하는 곳이 함수 안에 모여 있어서 그렇습니다.

딱지 판정 규칙 한눈에 보기

딱지보는 값조건
마감 임박마감 시각지금부터 마감까지 0일 이상 14일 이하
마감(종료)마감 시각마감 시각이 이미 지남
상시문자열상시·수시·연중·항시 중 하나가 들어 있음
신규수정된 날짜30일 안에 갱신됨
딱지 없음마감 시각날짜를 못 찾아 먼 미래로 처리됨(임박도 종료도 아님)

14일과 30일은 글 앞에서 말한 대로 정해 둔 기준일 뿐이며, 두 숫자만 바꾸면 화면 전체의 판정이 같이 바뀝니다.

임박 판정은 (마감 시각 − 현재 시각)을 하루(86,400,000밀리초)로 나눈 값이 0 이상 14 이하일 때입니다. 마감 시각을 그날 23시 59분 59초로 잡았기 때문에, 마감일 당일에 보면 이 값은 1보다 작은 양수가 되어 여전히 임박으로 판정되고, 다음 날 0시가 지나면 종료로 바뀝니다.

판정 함수를 고칠 때는 위의 입력 표를 그대로 시험 목록으로 삼는 것이 안전합니다. 표의 모든 줄이 예상한 값으로 나오는지 다시 돌려 보면, 정규식을 바꾼 뒤 기존 형식이 깨졌는지 바로 알 수 있습니다. 문장형 날짜를 읽도록 정규식을 넓힐 때는 “9월 1일부터 9월 30일까지”처럼 연도가 없는 문자열을 어떻게 볼지도 먼저 정해야 합니다. 이것은 이 글의 코드가 정한 규칙이 아니라 고려할 선택입니다.

블로그 테마에 적용하는 위치

이런 코드는 블로그 테마 안에 넣어야 목록 화면에서 돌아갑니다. Blogger 도움말에 따르면 HTML은 블로그를 고른 뒤 왼쪽 메뉴의 테마에서 ‘내 테마’ 옆 더보기를 눌러 HTML 편집으로 들어가 고치고 저장합니다. CSS만 더할 때는 맞춤설정의 고급 메뉴에서 CSS 추가를 씁니다. 위젯 코드를 쓸 때의 규칙은 별도 도움말(위젯 태그)에 있는데, 위젯마다 id='main'인 b:includable이 하나 있어야 하고, 조건은 b:if, 반복은 b:loop로 쓴다고 안내합니다. 이 도움말은 수정 전 백업을 따로 언급하지 않으므로, 고치기 전에 현재 HTML을 복사해 두는 것을 권합니다.

한계

날짜 형식이 더 특이하면 여전히 못 읽습니다. 예를 들어 “예산 소진 시까지” 같은 표현은 날짜가 아니라서 먼 미래로 처리됩니다. 지금은 그게 더 안전하다고 보고 그대로 뒀습니다.

그리고 시간대를 따지지 않습니다. 연·월·일 숫자를 지역 시간으로 읽는 동작(MDN) 때문에 브라우저 기준으로 계산되고, 해외에서 보면 하루 차이가 날 수 있습니다.

참고 자료

기준일: 2026-10-18. 위 표의 읽힘 결과는 본문의 deadlineTime 코드를 한국 시간대(Asia/Seoul)에서 실행한 값이며, 기기·시간대에 따라 시각 표기는 달라질 수 있습니다.