akjfal

Next13 head 설정 방법들 본문

하루의 이슈

Next13 head 설정 방법들

akjfal 2023. 6. 2. 10:13

next13에서 head에 title을 변경하려고하니 방법이 많이 바뀌었습니다. 그리고 SSR, CSR에 title을 변경해주는 방식들이 다양해서 관련한 방법들을 정리해보았습니다.

해당 글에서는 title만을 설정해주고 있으나, 기타 head에 들어가는 값들은 필요한 값을 넣어주면 됩니다.

github code


SSR에서 head 변경

여러 페이지에 고정값 일괄 설정하기

Next13으로 넘어오면서 기존에 사용하던 방식이 아닌 Medata를 export하는 방식으로 title을 설정해줍니다.

import Head from 'next/head';

/**
 * title이 변하지 않습니다.
 * @returns 
 */
export default function Page() {
    return (
      <div>
        <Head>
            <title>default title</title>
        </Head>
        default page
      </div>
    )
  }

만약 모든 페이지에 일괄적으로 Head를 설정해주고 싶다면, Next13에서 나온 layout.tsx에서 export 해주면 됩니다.

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  )
}

export const metadata = {
  title: 'Layout Title',
}

한 페이지에 고정값 설정하기

특정 페이지에서는 head의 값을 다르게 해주는 경우도 있습니다. 이럴 땐 metadata를 각 페이지에서 export 해주면 됩니다. 이럴 시 Layout.tsx에서 export된 값을 무시하고 page에서 설정한 값으로 노출됩니다.

export default function Page() {
    return (
      <div>
        default page
      </div>
    )
}

export const metadata = {
	title: 'Next13 Page Title'
}

URL을 기반으로 동적으로 설정하기

dynamic route나 query를 통해서 head의 값들을 설정해줄 수 있습니다.

export default function Component() {
  return (
    <div>
      next13
    </div>
  )
}

type Props = {
  params: { id: string };
  searchParams: { [key: string]: string | string[] | undefined };
};

// next13/main?query=url
export async function generateMetadata ({params, searchParams}: Props) {
  const {id} = params;

  return {
    title: id + searchParams.query
  }
};

JS 로직을 통해 동적으로 설정하기

지금까지는 고정된 값을 설정하는 법을 설명드렸습니다. 그런데 head의 값은 동적인 값이 들어가야 할 수도 있습니다. 이때 처리하는 방법은 Layout과 Page에서와 같이 똑같은 위치에 generateMetadata로 대체해서 넣어주면 됩니다.

export default function Page() {
  return (
    <div>
      default page
    </div>
  )
}

export async function generateMetadata () {
  const data = await getData();
  
  return {
    title: data
  }
};

// API
const getData = () => new Promise((resolve) => resolve('SSR-DYNAMIC'));

이렇게 설정해주면 SSR에서 API를 호출해 값을 가져온 뒤, 설정해준 값으로 title이 설정됩니다.


CSR에서 Head 변경

CSR에서 title 설정하기

CSR에서 title을 설정해주고 싶다면 기존에 React에서 title을 설정해주듯이 설정해주면 됩니다.

// page
import Component from './component'

export default async function Page() {

  return (
    <div>
      <Component />
    </div>
  )
}
// component
'use client'
import {useEffect, useState} from 'react'

export default function Component(){
    const [data, setData] = useState('')

    const callApi = async () => {
        const res = await getData();
        setData(res)
    }

    useEffect(()=> {
        callApi();
    }, [])

    useEffect(()=>{
        if(data){
            document.title = data;
        }

      }, [data])

    return (
        <div>component</div>
    )
}

const getData:()=> Promise<string> = () => new Promise((resolve) => resolve('title'));

React Helmet으로 설정하기

React에서 head를 설정할 때 React-Helmet으로 설정해주기도 하기 때문에 해당 사항도 적용이 되는지 확인해보았습니다.

// page는 위와 동일
// component
'use client'
import {useEffect, useState} from 'react'
import {Helmet} from 'react-helmet';

export default function Component(){
    const [data, setData] = useState<string>('');

    const callApi = async () => {
        const res = await getData();
        setData(res)
    }

    useEffect(()=> {
        callApi();
    }, [])

    return (
        <>
            <Helmet><title>{data}</title></Helmet>
            <div>component</div>
        </>
    )
}

const getData:()=>Promise<string> = () => new Promise((resolve) => resolve('title'));

documen.title에 넣었을 때와 동일하게 동작합니다.

참고) Helmet의 로직

Helmet의 경우 head의 title을 설정해줄 때 로직을 까보면

const updateTitle = (title, attributes) => {
    if (typeof title !== "undefined" && document.title !== title) {
        document.title = flattenArray(title);
    }

    updateAttributes(TAG_NAMES.TITLE, attributes);
};

위와 같은 함수가 동작합니다. <Helmet>의 <title>이나 속성 title을 통해서 가져온 값들을 document.title에 넣어줘서 설정해주고 있습니다. 또한 위와 같은 함수들을

const HelmetSideEffects = withSideEffect(
    reducePropsToState,
    handleClientStateChange,
    mapStateOnServer
)(NullComponent);

react-side-effect 라이브러리의 withSideEffect를 통해서 동작시키고 있습니다.

Comments