검색 엔진이 사이트맵을 크롤링할 때 페이지의 수정 시간을 잘못 인식하면, 검색 결과에 엉뚱한 날짜가 표시되거나 오래된 콘텐츠로 판단되어 순위가 떨어진다. 대부분의 개발자들은 사이트맵에 lastmod 태그를 넣기만 하면 끝이라고 생각하지만, 실제로는 파일의 물리적 수정 시간, HTTP Last-Modified 헤더, 그리고 데이터베이스의 업데이트 시간까지 모두 일치해야 한다. 이번 글에서는 검색 엔진 최적화를 위해 반드시 알아야 할 lastmod 태그 작성법과 Last-Modified 헤더 설정, 그리고 실전에서 흔히 겪는 실수들을 완벽하게 정리해서 소개하겠다.

 

1단계: lastmod와 Last-Modified 헤더의 역할 이해하기

검색 엔진은 페이지 업데이트 여부를 판단하는 두 가지 방법을 사용한다. 첫 번째는 XML 사이트맵의 <lastmod> 태그이고, 두 번째는 HTTP 응답 헤더의 Last-Modified 값이다. 이 두 값이 일치하지 않으면 검색 엔진이 혼란스러워하고, 결국 잘못된 날짜가 검색 결과에 표시된다.

lastmod 태그는 사이트맵에서 페이지가 마지막으로 수정된 ISO 8601 형식의 날짜를 명시한다. 예를 들어 2024년 1월 15일 오후 3시 30분이라면 "2024-01-15T15:30:00+09:00" 형태로 작성해야 한다. 시간대(timezone offset)를 명시하지 않으면 Google은 UTC로 해석한다.

Last-Modified 헤더는 HTTP 응답에 포함되는 서버 헤더로, 실제 파일이나 리소스가 마지막으로 수정된 시간을 RFC 2822 형식("Mon, 15 Jan 2024 15:30:00 GMT")으로 나타낸다. 이 값이 없으면 검색 엔진은 Date 헤더를 기준으로 판단한다.

 

2단계: 올바른 lastmod 태그 작성과 시간대 처리

사이트맵에서 lastmod를 작성할 때 가장 흔한 실수는 데이터베이스 타임스탐프와 시간대를 무시하는 것이다. PHP에서 현재 시간을 기록할 때 대부분 UTC+0 기준이므로, 한국 시간대(UTC+9)로 변환해야 검색 결과에 올바르게 표시된다.

✗ 잘못된 코드:

<?php
// 데이터베이스에서 가져온 수정 시간 (UTC, timezone 정보 없음)
$updated_at = '2024-01-15 15:30:00';

// 사이트맵에 그대로 넣음 → Google은 UTC로 해석
echo "<lastmod>{$updated_at}</lastmod>";
// 결과: <lastmod>2024-01-15 15:30:00</lastmod>
// Google 해석: 2024-01-15 15:30:00 UTC (한국 시간 자정)
?>

✓ 올바른 코드:

<?php
// 방법 1: DateTime으로 타임존 명시
$updated_at = '2024-01-15 15:30:00'; // DB에서 가져온 UTC 시간
$datetime = new DateTime($updated_at, new DateTimeZone('UTC'));
$datetime->setTimezone(new DateTimeZone('Asia/Seoul')); // 한국 시간으로 변환
echo "<lastmod>" . $datetime->format('Y-m-d\\TH:i:sP') . "</lastmod>";
// 결과: <lastmod>2024-01-16T00:30:00+09:00</lastmod>

// 방법 2: timestamp 기반 (더 정확함)
$timestamp = time(); // Unix timestamp (UTC 기준)
$kst_datetime = date('Y-m-d\\TH:i:sP', $timestamp + 32400); // 9시간 추가
echo "<lastmod>{$kst_datetime}</lastmod>";
?>

위 코드에서 핵심은 항상 timezone offset을 명시하는 것이다. "+09:00"을 붙이면 Google은 해당 시간을 한국 시간대로 정확하게 해석한다.

 

3단계: HTTP Last-Modified 헤더 설정하기

사이트맵의 lastmod가 완벽해도 실제 페이지의 Last-Modified 헤더가 없거나 다르면, 검색 엔진은 혼동한다. PHP에서는 header() 함수로 Last-Modified를 설정하되, 반드시 페이지 최상단에 위치해야 하고, ob_start()는 사용하지 않는 것이 안전하다.

✗ 잘못된 코드:

<?php
// 어딘가 출력이 있은 후 헤더 설정 시도
echo "<!DOCTYPE html>";
header('Last-Modified: Mon, 15 Jan 2024 15:30:00 GMT');
// 결과: "headers already sent" 경고 발생, 헤더 적용 안 됨
?>

✓ 올바른 코드:

<?php
// 최상단에서 데이터베이스 쿼리
$pdo = new PDO('mysql:host=localhost;dbname=blog', 'user', 'pass', [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
]);

$post_id = $_GET['id'] ?? 1;
$stmt = $pdo->prepare('SELECT updated_at FROM posts WHERE id = ?');
$stmt->execute([$post_id]);
$post = $stmt->fetch(PDO::FETCH_ASSOC);

if ($post) {
    // UTC 타임스탐프를 RFC 2822 형식으로 변환
    $datetime = new DateTime($post['updated_at'], new DateTimeZone('UTC'));
    $last_modified = $datetime->format('D, d M Y H:i:s T'); // RFC 2822
    header('Last-Modified: ' . $last_modified);
}
?>
<!DOCTYPE html>
<html>
<head>
    <title>블로그 글</title>
</head>
<body>
    ...
</body>
</html>

Last-Modified 헤더는 반드시 RFC 2822 형식(예: "Mon, 15 Jan 2024 15:30:00 GMT")이어야 한다. strtotime()과 date() 조합으로도 설정할 수 있지만, DateTime 클래스가 타임존 처리에 더 안전하다.

 

4단계: 실전 XML 사이트맵 생성 함수 제작

동적 페이지가 많은 사이트에서는 매번 수동으로 lastmod를 계산하기 어렵다. 아래는 데이터베이스 레코드를 자동으로 사이트맵으로 변환하는 PHP 함수다.

<?php
function generate_sitemap_with_lastmod(PDO $pdo) {
    $sitemap = '<?xml version="1.0" encoding="UTF-8"?>' . PHP_EOL;
    $sitemap .= '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' . PHP_EOL;
    
    // 블로그 글 목록
    $stmt = $pdo->query('SELECT id, updated_at FROM posts WHERE status = "published" ORDER BY updated_at DESC');
    
    while ($post = $stmt->fetch(PDO::FETCH_ASSOC)) {
        $post_id = htmlspecialchars($post['id']);
        
        // DB 타임스탐프 (UTC) → ISO 8601 + 타임존
        $datetime = new DateTime($post['updated_at'], new DateTimeZone('UTC'));
        $datetime->setTimezone(new DateTimeZone('Asia/Seoul'));
        $lastmod = $datetime->format('Y-m-d\\TH:i:sP');
        
        $url = "https://example.com/post/{$post_id}";
        
        $sitemap .= '  <url>' . PHP_EOL;
        $sitemap .= '    <loc>' . $url . '</loc>' . PHP_EOL;
        $sitemap .= '    <lastmod>' . $lastmod . '</lastmod>' . PHP_EOL;
        $sitemap .= '    <priority>0.8</priority>' . PHP_EOL;
        $sitemap .= '  </url>' . PHP_EOL;
    }
    
    $sitemap .= '</urlset>';
    
    return $sitemap;
}

// sitemap.php에서 호출
header('Content-Type: application/xml; charset=utf-8');
echo generate_sitemap_with_lastmod($pdo);
?>

이 함수의 장점은 데이터베이스의 updated_at을 자동으로 가져와서 타임존 변환까지 처리하므로, 수동 실수가 최소화된다는 점이다.

 

5단계: 흔한 실수와 디버깅 팁

✗ 실수 1: 타임존 정보 없이 직접 날짜 문자열 사용

데이터베이스에서 "2024-01-15 15:30:00"을 가져와서 사이트맵에 그대로 넣으면, Google은 UTC로 해석한다. 한국 시간대가 필요하면 반드시 "+09:00"을 붙여야 한다.

✗ 실수 2: Last-Modified 헤더를 GMT가 아닌 다른 형식으로 설정

Last-Modified는 반드시 GMT(UTC) 기준이어야 한다. 한국 시간대(+09:00)로 설정하면 검색 엔진이 잘못 해석한다. 반드시 'GMT' 또는 'T' 형식을 사용하고, 서버 시스템 시간대와 무관하게 UTC 기준으로 변환해야 한다.

✓ 올바른 Last-Modified:

<?php
// 한국 시간대로 저장된 시간을 UTC로 변환 후 GMT 형식으로
$korea_time = '2024-01-15 15:30:00';
$datetime = new DateTime($korea_time, new DateTimeZone('Asia/Seoul'));
$datetime->setTimezone(new DateTimeZone('UTC')); // UTC로 변환
echo $datetime->format('D, d M Y H:i:s') . ' GMT'; // RFC 2822 + GMT
// 결과: Mon, 15 Jan 2024 06:30:00 GMT (한국 시간 15:30의 UTC 환산)
?>

✗ 실수 3: 조건부 lastmod 생략

어떤 페이지는 lastmod를 넣고 어떤 페이지는 생략하면, 검색 엔진이 일관성 없는 정보로 판단하기 쉽다. 사이트맵에 포함된 모든 URL에 lastmod를 명시하는 것이 권장된다.

디버깅 방법: Google Search Console 확인

Search Console의 "색인" 섹션에서 특정 페이지를 검색하면, Google이 수집한 Last-Modified 값과 현재 페이지 상태를 비교할 수 있다. 만약 날짜가 미래 시간으로 표시되거나 엉뚱한 값이 보이면, 위의 타임존 변환 코드를 다시 확인해야 한다.

 

마무리: lastmod는 SEO 신뢰도를 결정하는 신호

검색 결과에 정확한 수정 날짜가 표시되는 것은 단순한 미관 문제가 아니다. 검색 엔진은 lastmod와 Last-Modified 헤더의 일관성을 통해 사이트의 신뢰도를 판단하고, 이는 크롤링 우선순위와 색인 여부에까지 영향을 미친다. 타임존 처리, RFC 형식 준수, 그리고 데이터베이스 업데이트 시간의 일관성이 모여서 검색 엔진 최적화의 기초를 이룬다는 점을 잊지 말자. 이 글의 "올바른 코드" 섹션을 참고해서 현재 사이트의 사이트맵 생성 로직을 점검하고, Last-Modified 헤더를 명시하면, 검색 결과의 신뢰도와 순위 유지에 눈에 띄는 개선이 생길 것이다.