게시물을 라이브로 올렸는데, 대시보드에서는 방금 올라온 것처럼 평온하게 표시되는 반면 공개 사이트에는 '57년 전'이라고 표시되는 것을 본 적이 있다면, 이 버그가 주는 특유의 허탈함을 잘 아실 겁니다. 이 문제는 레이아웃을 깨뜨리지도 않고, 치명적인 오류를 발생시키지도 않습니다. 그저 콘텐츠의 시간을 반세기 전으로 되돌려 놓을 뿐이며, 테마 파일을 아무리 뒤져봐도 그 숫자는 변하지 않습니다.

이 문제는 일반적인 점검 과정에서도 잘 발견되지 않는 경향이 있습니다. single.php, archive.php, functions.phpgrep으로 훑으며 잘못된 get_the_date() 호출이 있는지 찾아보지만, 모든 것이 표준처럼 보입니다. 라이브 페이지를 새로고침해도 1968년의 유령은 여전히 작성자 정보(byline)에 떠돌고 있습니다. 이 단계에 이르면, 문제는 템플릿과는 거의 상관이 없으며 WordPress 스택을 통해 시간이 전달되는 방식과 밀접한 관련이 있다는 뜻입니다.

왜 특정 숫자가 계속 나타나는가

'57년 전'이라는 숫자는 무작위로 나타나는 것이 아닙니다. WordPress 테마는 종종 human_time_diff() 함수를 통해 상대적인 시간을 표시하는데, 이 함수는 게시물의 타임스탬프를 현재 시점과 비교하여 '2시간 전'과 같은 친숙한 문구를 출력합니다. 이 계산이 제대로 작동하려면 PHP에 유효한 Unix 타임스탬프, 즉 Unix 에포크(1970년 1월 1일) 이후 경과된 초 단위의 숫자가 필요합니다.

계산 단계에 도달하기 전에 무언가가 타임스탬프를 제거하거나 손상시키면, 함수는 종종 0(또는 이와 유사한 잘못된 시작점)을 현재 시간과 비교하게 됩니다. 그 결과, 약 55년 전으로 거슬러 올라가는 시간 간격이 계산되며, 매년 한 해씩 늘어나는 결과가 나타납니다. 이는 데이터베이스에 과거의 블로그 게시물이 가득 차 있는 것이 아니라, 시간 값이 누락되었거나 잘못 읽혔을 때 나타나는 증상입니다.

대시보드의 착시

이 버그의 가장 답답한 점 중 하나는 관리자 패널의 이중성입니다. wp-admin 내부의 글 목록에서는 올바른 발행 날짜가 표시되는데, 이는 WordPress가 종로 종종 MySQL wp_posts 테이블에서 데이터를 직접 가져와 상대적 시간 필터를 거치지 않고 서버 측에서 형식을 지정하기 때문입니다. 반면 프론트엔드는 human_time_diff()를 호출하는 테마나 플러그인에 의존하거나, 백엔드는 완전히 건너뛰는 PHP 타임존 변환 체인을 통해 원시 데이터베이스 값을 전달할 수 있습니다.

즉, 데이터 자체는 대개 온전합니다. 문제는 데이터베이스와 렌더링된 HTML 사이의 파이프라인에 꼬임이 발생했다는 것입니다.

PHP 버전 및 타임존 설정

먼저 PHP부터 살펴보겠습니다. 호스팅 패널에서 버전을 변경하는 것은 별것 아닌 일처럼 보이지만, PHP는 주요 릴리스에 따라 시간 객체를 다루는 방식이 다릅니다. 갑작스럽게 PHP 8.1 환경으로 전환된 곳에서 레거시 PHP 7.4 코드를 실행하는 사이트는 DateTime 객체가 비어 있거나 잘못된 문자열을 초기화하는 방식에서 미묘한 변화를 겪을 수 있습니다. 만약 php.ini 파일에서 date.timezone이 정의되어 있지 않으면, PHP는 기본값으로 UTC를 사용하며 서버가 가장 잘 알고 있다고 가정합니다. WordPress의 생각은 다를 수 있습니다.

wp-config.php 내부에 타임존 선언을 하드코딩한 사람이 있는지 확인하십시오. date_default_timezone_set( 'Asia/Kolkata' );와 같은 줄은 도움이 될 것 같지만, WordPress는 Settings > General에서 설정된 값을 통해 자체적으로 시계를 관리하기를 기대합니다. PHP 레벨에서 오프셋을 강제하면, 코어는 시간이 로컬이라고 믿고 PHP는 유니버설(UTC)이라고 믿는 경쟁 상태(race condition)가 발생할 수 있습니다. 타임스탬프 변환 중에 이러한 오프셋이 충돌하면, 템플릿으로 반환되는 결과값이 0으로 수렴할 수 있습니다.

서버 타임존 구성

MySQL 서버는 자체적인 로컬 시간 기준을 가지고 있습니다. WordPress는 모든 게시물에 대해 두 가지 날짜를 기록합니다. 사이트의 로컬 시간인 post_date와 세계 표준시인 post_date_gmt입니다. 만약 데이터베이스의 글로벌 time_zone 설정이 마이그레이션 후에 예를 들어 SYSTEM에서 +00:00으로 바뀐다면, PHP의 strtotime()은 저장된 문자열을 예상하는 값과 일치시키지 못할 수 있습니다. 데이터베이스에는 여전히 2024-03-15 14:30:00이 저장되어 있지만, 이를 둘러싼 문맥이 바뀌면서 PHP에서 파싱된 출력값이 false 또는 null이 됩니다.

실제 사례: 매니지드 호스트가 데이터베이스를 SYSTEM 시간을 사용하는 MySQL 5.7 노드에서 엄격한 UTC로 설정된 MariaDB 10.11 클러스터로 이전하는 경우입니다. 테마는 바뀌지 않았고, WordPress 설정도 바뀌지 않았습니다. 하지만 Unix 타임스탬프 형식인 get_the_time( 'U' )가 일부 게시물에 대해 갑자기 빈 값을 반환하게 되고, 상대적 시간 함수는 에포크-제로(epoch-zero) 계산으로 되돌아갑니다. 해결책은 애플리케이션 타임존, 데이터베이스 타임존, 그리고 운영 체제의 시계를 일치시켜 WordPress가 어떤 기준점을 사용할지 추측할 필요가 없게 만드는 것입니다.

데이터베이스 타임스탬프 형식

WordPress core stores post dates in MySQL DATETIME columns, not TIMESTAMP fields. That distinction matters because DATETIME holds a calendar value without_zone awareness in older MySQL versions, whereas TIMESTAMP converts everything to UTC behind the scenes. If a plugin or migration tool has altered your wp_posts schema, or if an old backup restored invalid zero dates into the table, WordPress may read a value like 0000-00-00 00:00:00 and quietly hand it off to your theme.

Modern MySQL modes, particularly NO_ZERO_DATE and STRICT_TRANS_TABLES, reject those zero placeholders. If a backup script inserted them during a migration, the database might have truncated or nulled them on import. You can verify this quickly with a direct SQL query:

SELECT ID, post_title, post_date, post_date_gmt 
FROM wp_posts 
WHERE post_status = 'publish' 
ORDER BY post_date DESC 
LIMIT 10;

If the raw columns look correct but the site still displays ancient history, the corruption is happening after the query. If the columns themselves contain zeroes, you have found the leak in the plumbing.

Plugin Conflicts with Date Functions

Plugins that filter date output are common culprits. Multilingual tools such as WPML or Polylang hook into get_the_date to translate month names. Caching plugins intercept the final HTML. Either layer can accidentally pass an unformatted string into a function expecting a Unix integer.

A translation plugin might replace “March 15, 2024” with its localized equivalent, but if a theme then pipes that localized string into strtotime() inside a custom function, the parser fails on non-English characters and returns false. That false value hits human_time_diff(), which compares zero against now, producing the infamous half-century gap.

Object cache layers complicate this further. Redis or Memcached can store a serialized WP_Post object from a request that briefly failed to parse the date. Every subsequent visitor receives that corrupted object straight from RAM, bypassing the database entirely. The issue persists on the live site because your production stack has cache infrastructure that your local install lacks.

A Practical Debugging Checklist

Because the symptom hides deep in the stack, you need to peel layers systematically until the dates snap back into place.

  1. Query the database directly. Open phpMyAdmin or run WP-CLI and inspect the raw post_date values. If they are correct, the database is not the problem.
  2. Switch to a default theme. Activate Twenty Twenty-Four or Twenty Twenty-Three. If the bug disappears, audit your active theme and child theme for custom functions wrapping time output.
  3. Silence the plugins. Rename the /wp-content/plugins folder temporarily, or disable all plugins from the dashboard. Re-enable them one by one, checking the frontend after each activation.
  4. Read the error log. Look for timezone warnings, DateTime errors, or strtotime() complaints. They often point to the exact