밤 11시에 예약해 둔 글이 있었습니다. 다음 날 오전 9시에 자동으로 올라가도록 맞춰 뒀죠. 그런데 아침에 일어나 보니 글은 이미 새벽에 올라가 있었고, 방문자 통계도 엉망이었습니다. 코드를 백 번 봐도 시간 계산에는 문제가 없었어요. 범인은 코드가 아니라 서버의 시간대(timezone) 설정이었습니다.
시간 버그의 90%는 '계산'이 아니라 '기준'에서 옵니다.
오늘은 PHP에서 가장 자주, 그리고 가장 조용히 사람을 괴롭히는 타임존 문제를 처음부터 끝까지 풀어보겠습니다. 한 번 제대로 이해해 두면 예약 발행, 댓글 시간, 정산 마감 같은 곳에서 두 번 다시 같은 실수를 하지 않게 됩니다.
"분명 오후 6시로 맞췄는데" — 증상부터
처음 이 문제를 만나면 대부분 코드를 의심합니다. date()로 찍어보면 분명 엉뚱한 시간이 나오고, 어떤 서버에서는 맞고 어떤 서버에서는 틀립니다. 로컬 PC에서는 멀쩡하던 코드가 실제 서버에 올리자마자 9시간씩 어긋나죠.
이때 핵심은 "PHP가 시간을 만들 때 어떤 기준을 쓰느냐"입니다. 우리가 보는 '오후 6시'는 한국 기준(KST)이지만, 많은 서버는 기본적으로 세계 표준시(UTC)로 동작합니다. 한국은 UTC보다 9시간 빠르기 때문에, 기준이 어긋나면 정확히 그 9시간만큼 글이 일찍 혹은 늦게 처리됩니다.
범인은 '서버의 기본 시간대'
PHP는 날짜·시간을 다룰 때 date.timezone이라는 설정을 봅니다. 이 값이 비어 있거나 UTC로 되어 있으면, date()나 strtotime() 같은 함수가 전부 UTC 기준으로 계산합니다.
문제는 이게 '조용히' 동작한다는 점입니다. 오류도 경고도 없이, 그냥 9시간 어긋난 값을 멀쩡한 얼굴로 돌려줍니다. 그래서 디버깅이 더 어렵습니다. 지금 내 서버가 어떤 시간대인지 한 줄로 확인할 수 있습니다.
echo date_default_timezone_get(); // 예: UTC ← 이게 범인
echo date('Y-m-d H:i:s'); // 서버 기준 현재 시각여기서 UTC가 찍힌다면, 한국 시간을 기대한 모든 코드가 9시간씩 어긋나 있다는 뜻입니다.
UTC와 KST, 9시간의 정체
용어를 한 번만 정리하고 가겠습니다. 헷갈리는 사람이 정말 많은데, 표로 보면 간단합니다.
| 용어 | 뜻 | 비고 |
|---|---|---|
| UTC | 세계 표준시 | 기준점(0시) |
| KST | 한국 표준시 | UTC보다 +9시간 |
date.timezone | PHP 기본 시간대 설정 | 여기서 한 번에 결정 |
즉 서버가 UTC로 오전 0시일 때, 한국은 이미 오전 9시입니다. 예약 발행이 9시간 일찍 일어난 게 아니라, 서버 입장에서는 정확히 제 시간이었던 겁니다. 우리 기준과 서버 기준이 달랐을 뿐이죠.
해결: 시간대를 '세 곳'에서 맞춘다
타임존은 한 곳만 고친다고 끝나지 않습니다. 데이터가 흐르는 길목마다 기준이 같아야 합니다. 크게 세 곳입니다.
1) PHP 코드에서 고정
가장 확실한 방법은 코드 시작점(공통 설정 파일)에서 시간대를 한 번 못 박는 것입니다.
// config 최상단에 한 줄
date_default_timezone_set('Asia/Seoul');이 한 줄이면 이후 모든 date(), strtotime(), DateTime이 한국 기준으로 계산됩니다. 서버 설정에 의존하지 않으니, 다른 서버로 옮겨도 동일하게 동작합니다.
2) 데이터베이스 연결에서 맞추기
PHP만 고쳐도 MySQL이 다른 기준이면 NOW()나 CURRENT_TIMESTAMP가 또 어긋납니다. 접속 직후 세션 시간대를 맞춰 주는 게 안전합니다.
SET time_zone = '+09:00';3) 화면에 보여줄 때 일관되게
저장은 항상 같은 기준으로 하고, 보여줄 때만 포맷을 바꾸는 습관을 들이면 혼란이 줄어듭니다. 저장은 기준 시간, 표시는 사용자 시간 — 이 원칙만 지켜도 대부분의 사고를 막을 수 있습니다.
흔한 오해와 체크리스트
마지막으로, 사람들이 자주 빠지는 함정을 정리했습니다.
- "내 PC에서 잘 되니까 괜찮다" → 로컬과 서버의 시간대는 거의 항상 다릅니다.
- "DB에 한국 시간으로 저장했으니 됐다" → 어떤 기준으로 넣었는지 모르면 꺼낼 때 또 어긋납니다.
- "서버 시간을 KST로 바꾸면 끝" → 서버 전체를 바꾸면 다른 서비스가 영향을 받을 수 있어, 코드에서 고정하는 편이 안전합니다.
점검 순서는 이렇습니다. ①date_default_timezone_get()로 현재 기준 확인 → ②코드에서 Asia/Seoul 고정 → ③DB 세션 시간대 확인 → ④그래도 어긋나면 저장 시점의 기준을 의심.
마무리
정리하면 이렇습니다. 첫째, 시간 버그는 계산이 아니라 기준의 문제입니다. 둘째, 기준은 코드에서 Asia/Seoul로 한 번 고정하는 게 가장 안전합니다. 셋째, PHP·DB·표시 세 길목의 기준을 같게 맞추세요.
처음엔 9시간이라는 숫자가 황당하게 느껴지지만, 한 번 원리를 잡고 나면 다시는 헷갈리지 않습니다. 혹시 지금 운영 중인 서비스가 있다면, 오늘 딱 한 줄 — date_default_timezone_get() — 만 찍어보세요. 의외로 많은 분들이 거기서 범인을 발견합니다. 작은 확인 하나가 새벽에 잘못 올라가는 글을 막아줍니다.