Gatsby Head API로 메타 태그 적용하기. 그 이름 SEO.

작성 : 2023-04-20수정 : 2023-05-05

목차 펼치기


머리말

내가 어떤 멋진 글을 쓰고 어떤 멋진 사이트를 만들어도 검색엔진님께서 봐주지 않으시면 그게 무슨 의미가 있을까. 검색엔진님께서 내 이름을 확인해 주었을 때, 나는 비로소 존재하게 된다. 확인해주지 아니하신다면 어찌 존재한다 할 수 있을까. 아아,,, 검색엔진, 검색엔진, 검색엔진. 부르다 죽을 그 이름.



구현 방식 확인

Gatsby는 정적 사이트 생성기로, 미리 build를 다 해놓기 때문에 페이지별

<title>

<meta>

태그를 어떻게 동저긍로 가져갈 수 있을 지 찾아보았다. 예전에는 주로

react-helmet

플러그인을 주로 사용하였는데, Gatsby에 Head API 가 출시된 후 이제는 react-helmet 공식 문서에서도 Head API의 사용을 권고하고 있다. 심지어는 컴파일 과정 중 react-helmet 플러그인이 감지되면, Head API를 사용하라는 메세지까지 보여주고 있다. 이렇게까지 하는데 사용하지 않을 이유가 있을까.


 react-helmet 플러그인이 감지되면, gatsby-head API의 공식 문서 경로를 알려주고있다.

react-helmet 플러그인이 감지되면, gatsby-head API의 공식 문서 경로를 알려주고있다.


Gatsby Head API 적용하기


Compared to 

react-helmet

 or other similar solutions, Gatsby Head is easier to use, more performant, has a smaller bundle size, and supports the latest React features.
개츠비 헤드는 리액트 헬멧이나 다른 유사한 솔루션에 비해 사용하기 쉽고 성능이 뛰어나며 번들 크기가 더 작고 최신 리액트 기능을 지원합니다.


이 문서에서까지 react-helmet을 언급하며 Head API를 사용해야될 이유를 설명해주고 있다. 그래그래… 알겠어 쓸게. 쓴다니까.


Head API는 오직 페이지 단위에서만 적용가능하며,

Head

라는 이름으로 JSX 문법으로 작성된 컴포넌트를 export 해주면 자동으로 적용된다.

  • 페이지가 unmount 되면 해당 내용은

    <head>

    태그 내에서 사라지므로, 모든 페이지에 개별로 설정해주어야한다.

  • link

    ,

    meta

    ,

    style

    ,

    title

    ,

    base

    ,

    script

    ,

    noscript

    태그만 사용가능하다.

    • <script type="application/ld+json">

      가 적용이 가능하다 !


React Context나 GraphQL query를 통한 데이터도 파라미터로 받을 수 있지만, 기본이 되는

pathname

params

파라미터만 살펴보았다.

  • location.pathname

    을 통해 URL의 pathname을 알 수 있다.

  • params

    로 URL 파라미터를 받아올 수 있는데, 오직

    client-only routes

    에서만 알 수 있다. 이 기능은 URL 파라미터에 따라 다른 컨텐츠를 보여줘야할 때 유용하게 사용할 수 있는 기능인데, 이를 잘 활용하면 Gatsby에서 SPA를 구현할 수도 있다. 여기서 깊게 알 내용은 아니므로 이런게 있다는 것만 알아두고 넘어가자.


typescript
1export const Head: HeadFC = ({
2	location, params, data, pageContext
3}) => (
4  <>
5    <title>Hello World {location.pathname}</title>
6    <meta name="description" content="Hello World" />
7  </>
8)

기본적인 Gatsby Head API와 파라미터.



SEO 컴포넌트 추가하기


Head API를 통해

<meta>

태그들을 손쉽게 적용하기 위한 방법으로, 공식 문서에서

<SEO>

컴포넌트예시로 제공해주고 있다. 문서에서 제공해주는 코드들을 간단히 복사하여 붙여넣어 적용 가능하다. 이를 나에게 맞게 수정하여 사용하였다.


gatsby-config에 siteMetadata 설정

typescript
1module.exports = {
2  siteMetadata: {
3    title: `Weezip`,
4    description: '글쓰는 프론트엔드 개발자의 블로그. 편하고 예쁜 걸 좋아합니다.',
5    siteUrl: `https://weezip.treefeely.com`,
6  },
7}

gatsby config에 siteMetadata 설정하기

twitterUsername

image

를 사용하지 않았는데, 트위터가 없어서 사용하지 않았고 아직 이미지가 없어서 적용하지 않았다. 추후

image

는 추가할 예정이다.


siteMetadata에 설정된 데이터를 조회하는 hook 생성

typescript
1import { graphql, useStaticQuery } from 'gatsby'
2
3export const useSiteMetadata = () => {
4  const data = useStaticQuery(graphql`
5    query {
6      site {
7        siteMetadata {
8          title
9          description
10          siteUrl
11        }
12      }
13    }
14  `)
15
16  return data.site.siteMetadata
17}

siteMetadata hook 코드

siteMetadata

에서 정의하지 않은 내용을 사용할 경우 오류가 발생하니 1:1로 잘 매칭해서 조회하도록 하자.


SEO 컴포넌트 생성 w. typescript, og tags

typescript
1import * as React from 'react'
2import { useSiteMetadata } from '../../services/use-site-metadata'
3
4interface Props {
5  title?: string
6  description?: string
7  pathname?: string
8  children?: React.ReactNode
9}
10
11const SEO = ({ title, description, pathname, children }: Props) => {
12  const { title: defaultTitle, description: defaultDescription, image, siteUrl } = useSiteMetadata()
13
14  const seo = {
15    title: title || defaultTitle,
16    description: description || defaultDescription,
17    image: `${siteUrl}${image}`,
18    url: `${siteUrl}${pathname || ``}`,
19  }
20
21  return (
22    <>
23      <title>{seo.title}</title>
24      <meta name="description" content={seo.description} />
25      <meta property="og:type" content={'website'} />
26      <meta property="og:title" content={seo.title} />
27      <meta property="og:description" content={seo.description} />
28      <meta property="og:url" content={seo.url} />
29      <meta property="og:site_name" content={'Weezip'} />
30      <meta property="og:locale" content={'ko_KR'} />
31      {/* <meta property="og:image" content={''} /> */}
32      {/* <meta property="og:image:width" content={''} /> */}
33      {/* <meta property="og:image:height" content={''} /> */}
34      <meta property="twitter:title" content={seo.title} />
35      <meta property="twitter:description" content={seo.description} />
36      <meta property="twitter:url" content={seo.url} />
37
38      {/* <meta name="keywords" content={keywords} /> */}
39      {/* <meta name="image" content={seo.image} /> */}
40      {/* <meta name="twitter:card" content="summary_large_image" /> */}
41      {/* <meta name="twitter:image" content={seo.image} /> */}
42      {/* <meta name="twitter:creator" content={seo.twitterUsername} /> */}
43      {/* <link
44        rel="icon"
45        href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='0.9em' font-size='90'>👤</text></svg>"
46      /> */}
47      {children}
48    </>
49  )
50}
51
52export default SEO

SEO 컴포넌트 생성. meta tag의 guide를 꿈꾸며.

typescript와 og 태그까지 추가한 내 SEO 컴포넌트 코드이다. 아직

<meta>

태그들을 어떻게 적용하는 게 제일 좋은 방법인지 잘 모르겠다. 이 부분은 나중에는 다른 곳에서도 이 코드를 참고하여 구현하면 되도록 나만의 best practice가 될 때까지 차근차근 디벨롭할 예정이다.



SEO 컴포넌트 적용

typescript
1// 기본 SEO Head를 사용할 경우
2export const Head: HeadFC = () => {
3  return <SEO />
4}
5
6// title, description을 수정해서 사용할 경우
7export const Head: HeadFC = () => {
8  return (
9    <SEO title={`게시글 목록`} description={`어떤 글이 있을까요?`}/>
10  )
11}
12
13// 특정 태그를 추가 또는 overwrite해서 사용할 경우
14export const Head: HeadFC = () => {
15  return (
16    <SEO title={`게시글 목록`} description={`어떤 글이 있을까요?`}>
17      <link rel="canonical" href={`https://weezip.freefeely.com/list`} />
18    </SEO>
19  )
20}

SEO 컴포넌트 사용 예제

페이지 단위의 코드 내에서,

Head

라는 이름의 JSX 컴포넌트를 export 해주면 된다. 동적으로

title

,

description

등을 구성하고 싶을 경우 JSX를 리턴하기 전에 데이터를 처리해서 건네주면 된다.


client-only routes와 Head API의

params

를 활용하여 게시글 페이지의

<meta>

태그들이 동적으로 구성될 수 있도록 하였다.

canonical

은 아직 어찌할 바를 못 정해서 단순 주석처리만 해두었다. 추후 정리할 예정이다.

typescript
1export const Head: HeadFC = ({ data, pageContext }: any) => {
2  const content = notionNodeToJson(getNotionNodeByUrl(data, pageContext.slug))
3
4	// ... 설정할 title, description 데이터를 정제한다.
5
6	const title = ''
7	const description = ''	
8
9  return (
10    <SEO title={title} description={description}/>
11  )
12}

/post/{id} 페이지에 적용한 Head 코드의 예시



전역 메타 태그 설정

개별 페이지마다 설정이 필요한 태그들도 있지만, 모든 페이지에서 공통으로 적용되는 설정들이 있다. 또는 특정 스크립트 설치를 위해 index 페이지 쪽을 건드려야할 때가 있는데 Gatsby에서는 어떻게 해야되는 지 찾아보았다. 기본으로 적용되는

html.js

를 내 소스 트리로 복사해와서 커스터마이징 하도록 되어 있었다. 복사한 후 해당 파일에서 수정을 진행했다.

shell
1$ cp .cache/default-html.js src/html.js

기본 html.js 파일 내 소스로 복사해오기.


javascript
1<meta name="author" content="ethan" />
2<meta property="og:locale" content="ko_KR" />
3<meta property="og:site_name" content="WeeZip" />
4<meta property="og:type" content="website" />

전역으로 설정한 meta tag



Canonical URL 설정


크롤러에게 원본 URL에 대한 평가를 제대로 받기 위해서 Canonical URL 설정은 굉장히 중요하며, 대표 URL이 설정되어 있지 않을 경우 크롤러로부터 불이익을 받기도 한다. list 페이지는 여러 파라미터가 하나의 페이지로 수집되어야 하기에 별도의 canonical을 설정해주었다.


typescript
1export const Head: HeadFC = () => {
2  return (
3    <SEO title={`게시글 목록`} description={`어떤 글이 있을까요?`}>
4      <link rel="canonical" href={`https://weezip.freefeely.com/list`} />
5    </SEO>
6  )
7}

Wanna get in touch?

All Icons byiconiFy