← 전체 글로 돌아가기

Docker

Next.js에서 metadataBase를 빼먹고 공유 이미지 URL이 깨진 기록

배포 후 OG 이미지가 상대 경로로 만들어진 원인과 확인 기준을 짧게 정리했다.

이상했던 지점

게시글 상세 페이지에는 opengraph-image.png가 있었고 브라우저에서 직접 열면 이미지도 보였다. 그런데 메신저 미리보기에서는 이미지가 빠졌다. HTML을 열어 보니 og:image가 절대 URL이 아니라 상대 경로처럼 만들어져 있었다.

<meta property="og:image" content="/posts/example/opengraph-image.png">

로컬에서는 대충 넘어갔지만, 외부 크롤러는 이 주소를 어떤 도메인 기준으로 해석해야 하는지 분명하지 않았다. 특히 배포 주소와 프리뷰 주소가 섞이는 환경에서는 더 헷갈렸다.

설정을 어디에 둘지 정했다

App Router에서 메타데이터를 만들 때 기준 URL을 한 곳에 두기로 했다. 나는 루트 레이아웃 쪽에 metadataBase를 넣고, 환경 변수로 운영 도메인을 받게 했다.

import type { Metadata } from 'next'

export const metadata: Metadata = {
  metadataBase: new URL(process.env.NEXT_PUBLIC_SITE_URL ?? 'https://example.com'),
  title: {
    default: 'My Blog',
    template: '%s | My Blog',
  },
}

그리고 페이지별 메타데이터에서는 경로만 넘겼다.

export async function generateMetadata(): Promise<Metadata> {
  return {
    openGraph: {
      images: ['/og/default.png'],
    },
  }
}

이렇게 하면 최종 HTML에는 기준 URL이 붙은 주소가 만들어진다.

내가 한 번 더 확인한 것

이미지를 고친 뒤 바로 메신저에서 확인했는데 계속 예전 결과가 나왔다. 이때 코드가 틀린 줄 알고 다시 뒤집을 뻔했다. 실제로는 미리보기 캐시가 남아 있었다. 그래서 먼저 HTML 원문을 확인하고, 그 다음 각 서비스의 캐시 갱신 도구나 새 URL로 확인했다.

curl -s https://example.com/posts/sample | grep -i 'og:image'

브라우저 화면보다 HTML의 메타 태그를 먼저 보는 편이 빠르다. 화면에서 이미지가 보이는 것과 크롤러가 읽는 URL이 올바른 것은 다른 문제였다.

짧은 점검표

  • 운영 환경의 NEXT_PUBLIC_SITE_URL 값이 실제 공개 도메인인지 본다.
  • metadataBase가 루트 또는 공통 설정에 있는지 확인한다.
  • og:image 결과가 https://로 시작하는지 HTML에서 직접 본다.
  • 이미지 응답이 200이고 로그인 없이 접근되는지 확인한다.
  • 메신저 캐시 때문에 수정 결과가 늦게 보일 수 있음을 따로 기록한다.

남은 메모

SEO나 공유 이미지는 코드가 맞아도 바로 체감되지 않을 때가 많다. 그래서 이제는 “화면에서 보인다” 대신 “크롤러가 읽을 HTML에 절대 URL로 들어갔다”를 완료 기준으로 잡는다.