SEO와 메타(SEO and Meta)

SEO와 메타(SEO and Meta)

Nuxt의 head 태그 관리는 Unhead가 담당해요. 합리적인 기본값과 강력한 컴포저블들, 그리고 앱의 head·SEO 메타 태그를 관리하는 다양한 설정 옵션을 제공하죠. 전역 설정부터 반응형 컴포저블까지, 메타 관리를 위한 방법을 정리할게요.

출처: https://nuxt.com/docs/4.x/getting-started/seo-meta

Nuxt Config

nuxt.config.tsapp.head 속성으로 앱 전체의 head를 정적으로 커스터마이즈할 수 있어요. 이 방식은 반응형 데이터를 제공하지 못하므로, app.vue에서 useHead()를 쓰는 걸 권장해요. 사이트 기본 제목·언어·favicon처럼 잘 바뀌지 않는 태그는 여기서 설정하는 게 좋아요.

export default defineNuxtConfig({
  app: {
    head: {
      title: 'Nuxt', // 기본 fallback 제목
      htmlAttrs: {
        lang: 'en',
      },
      link: [
        { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' },
      ],
    },
  },
})

Nuxt는 사이트가 기본 동작하게 몇몇 태그를 기본 제공해요. viewport: width=device-width, initial-scale=1charset: utf-8이 바로 그것이에요. 대부분 덮어쓸 필요는 없지만, 키 축약형으로 업데이트할 수 있어요.

useHead

useHead 컴포저블 함수는 반응형 입력을 지원해서 head 태그를 프로그래밍 방식으로 관리할 수 있게 해줘요.

useHead({
  title: 'My App',
  meta: [
    { name: 'description', content: 'My amazing site.' },
  ],
  bodyAttrs: {
    class: 'test',
  },
  script: [
    { innerHTML: 'console.log(\'Hello world\')' }
  ],
})

보안에 민감한 환경에서는 useHeadSafe 컴포저블도 살펴보길 권장해요.

useSeoMeta

useSeoMeta 컴포저블은 사이트의 SEO 메타 태그를 완전한 타입 안전을 갖춘 객체 형태로 정의하게 해줘요. name 대신 property를 잘못 쓰는 흔한 실수를 예방해 줘요.

useSeoMeta({
  title: 'My Amazing Site',
  ogTitle: 'My Amazing Site',
  description: 'This is my amazing site, let me tell you all about it.',
  ogDescription: 'This is my amazing site, let me tell you all about it.',
  ogImage: 'https://example.com/image.png',
  twitterCard: 'summary_large_image',
})

컴포넌트

useHead를 쓰는 걸 권장하지만, 템플릿에서 head 태그를 정의하려면 컴포넌트가 더 편할 수도 있어요. Nuxt는 <Title>, <Base>, <NoScript>, <Style>, <Meta>, <Link>, <Body>, <Html>, <Head> 컴포넌트를 제공해요. 대문자 표기를 쓰는 건 유효하지 않은 네이티브 HTML 태그와 구분하기 위해서예요.

<script setup lang="ts">
const title = ref('Hello World')
</script>
<template>
  <div>
    <Head>
      <Title>{{ title }}</Title>
      <Meta name="description" :content="title" />
      <Style>body { background-color: green; }</Style>
    </Head>
    <h1>{{ title }}</h1>
  </div>
</template>

컴포넌트를 <Head><Html>로 감싸는 걸 권장하는데, 태그가 더 직관적으로 중복 제거(dedupe)되기 때문이에요.

타입

useHead, app.head, 컴포넌트에 쓰이는 비반응형 타입은 MetaObject 인터페이스로 정의돼요. title, titleTemplate, templateParams, base, link, meta, style, script, noscript, htmlAttrs, bodyAttrs 키를 담고 있어요.

기능

반응성

모든 속성에 computed 값, getter, 또는 reactive 객체를 제공해 반응성을 지원해요.

const description = ref('My amazing site.')
useHead({
  meta: [
    { name: 'description', content: description },
  ],
})

Title Template

titleTemplate 옵션으로 사이트 제목을 동적으로 커스터마이즈할 수 있어요. %s가 제목으로 치환되는 문자열이거나, 함수일 수 있어요. 함수를 쓰려면 nuxt.config가 아닌 app.vue에서 설정해 모든 페이지에 적용하는 걸 권장해요.

useHead({
  titleTemplate: (titleChunk) => {
    return titleChunk ? `${titleChunk} - Site Title` : 'Site Title'
  },
})

Template Params

templateParamstitleTemplate에 기본 %s 외의 추가 플레이스홀더를 제공해서 더 동적인 제목을 만들 수 있어요. tagPosition: 'bodyClose' 옵션은 적용 대상 태그를 <body> 끝에 붙여주기도 해요.

예시

definePageMeta와 함께

app/pages/ 디렉토리에서 definePageMetauseHead를 함께 써서 현재 라우트에 기반한 메타데이터를 설정할 수 있어요.

// 페이지에서 (빌드 때 매크로로 추출되므로 동적 설정 불가)
definePageMeta({
  title: 'Some Page',
})
// 레이아웃에서 라우트 메타 사용
const route = useRoute()
useHead({
  meta: [
    { property: 'og:title', content: `App Name - ${route.meta.title}` }
  ],
})

외부 CSS

useHead 컴포저블의 link 속성이나 <Link> 컴포넌트로 Google Fonts 같은 외부 CSS를 활성화할 수 있어요.

더 알아보기