구글의 검색앤진 최적화(SEO) 초보자 가이드를 참고하며 검색엔진 최적화를 해보았다.

SEO란?

구글은 이렇게 설명한다.

검색엔진 최적화, 즉 검색엔진에서 찾기 쉽도록 사이트를 개선하는 프로세스입니다. 관련 직무를 수행하는 사람의 직책을 의미하기도 합니다. 예: 우리는 웹 인지도를 높이기 위해 SEO를 새로 고용했다.

구글은 웹 크롤러를 사용하여 자동적으로 그리고 지속적으로 크롤링하는데, 그 수가 수십억개라 한다. 우연하게 구글에 크롤링된다면 다행이지만, 순전히 운에 맡길 수는 없다. 검색엔진이 크롤링하는 조건에 맞춰 사이트를 정비해주면, 좀 더 빠르고 정확하게 구글에 나의 페이지를 구글에 검색되게 할 수 있다. 이것이 바로 SEO이다.

SEO를 위해 해야 할 일은?

구글은 컨텐츠에 대한 근본적인 최적화 사항을 제시한다. 사이트를 재밌고 유용하게, 우수한 품질로 만들어야 된다. 독자가 원하는 컨텐츠를 제공해야한다. 페이지의 신뢰를 구축해야한다. 적절한 양을 제공해야한다. 올바른 문법에 맞게 작성되어야 한다. 등 뻔하지만 중요한 사항들이다. 유튜브로 따지면 어그로끄는 제목과 썸네일을 만들지 말라는 것이다. 이런 것들은 당연하게 꼭 지켜야 하는 것들이고, 이것보다 조금 더 적극적으로 SEO하려면 어떻게 해야할까?

구글이 내 콘텐츠를 이해할 수 있도록 해야한다.

이 블로그는 SEO.js 컴포넌트를 따로 작성하여 SEO와 관련된 요소들을 모아두었다. 그리고 모든 페이지에 SEO가 가능하도록 layout에다 <SEO />를 추가하고 필요한 내용들은 props로 받았다.

src/layouts/index.js
import React from 'react';
import { Helmet } from 'react-helmet';
import config from '../../contents/config';

import Header from './Header';
import Footer from './Footer';
import SEO from '../components/SEO';
import '../styles/index.scss';

const Layout = (props) => {
  const { children } = props;

  return (
    <>
      <SEO />
      <Header />
      <main id="main">{children}</main>
      <Footer />
    </>
  );
};

export default Layout;

HTML 문서 안 <head>요소 잘 작성하기

<head>는 웹 브라우저에는 표시되지 않는다. <title>, <meta>태그 등이 포함되어 있고, 페이지에 대한 전반적인 데이터를 포함하고 있다. 여기에 포함되는 정보들을 정확하게 작성하면 구글이 내 콘텐츠를 더 잘 이해할 수 있다.(HTML에 대한 구조를 자세히 알고싶다면, HTML 시작하기을 참고.)

<head>에서 SEO를 위해 작성할 요소는 다음과 같다.

고유하고 정확한 페이지 제목을 <title>에 담기

<head><title>은 브라우저의 상단 탭에 나타나는 제목이다. 페이지의 내용을 정확하게 설명하는 제목을 담고 있어야 한다. 의미없는 '페이지1', '제목없음' 이런 타이틀을 사용하거나, 페이지와 관련이 없는 제목을 사용해선 안된다. 또한 각 페이지 별 고유한 제목이 있어야 한다. 그리고 길이가 너무 길거나 불필요한 키워드가 있으면 정확한 SEO가 될 수 없다.

title tag result

title은 페이지에 나타나지 않고, 브라우저 탭이나 상태표시줄에 표시된다.

'post 제목 | 블로그 title' 형식으로 작성하였다.

페이지의 정보를 <meta>태그를 사용하여 작성하기

<meta name="description"><title>보다 조금 더 길게 페이지의 정보를 담을 수 있다. 한문장 혹은 한단락으로 요약하여 메타 태그의 content에 담으면 구글에서 이를 이용하여 스내핏을 생성하기도 한다. 스니펫은 보통 보여지는 제목+요약글로 구성된 검색결과를 넘어서 순서나 요약 등 따로 구글이 구조화 시켜 보여주는 정보를 의미한다.
제목과 마찬가지로 페이지 내용을 정확하게 요약해야하고, 각 페이지마다 고유한 설명을 작성하는 것이 좋다. 또한 '블로그입니다.' 이런 의미없는 설명, '자바스크립트에 관한 페이지' 이런 일반적인 설명, 그리고 '리액트, 자바스크립트, 프론트엔드' 처럼 키워드만 담은 형태는 피해야한다. 반대로 문서 전체를 넣어두는 것도 피해야한다.

snippet example

google snippet 예시

구글은 검색 결과를 추천 스내핏이라는 형태로 보여주기도 한다.

<meta>태그에 Open Graph도 함께 넣어주려한다. Open Graph란 소셜에서 풍부한 정보를 가능하게 한다. 오픈그래프가 마크업이 없다면, 대략적으로 제목과 이미지, 설명등을 파악하여 넣게되는데, 이때 정확도를 높이고 원하는 정보를 보여주기 위해 미리 메타데이터에 마크업해버리는 것이다. 풍부한 정보(?)라 하면 애매한데 우리가 엄~~청 많이 본 것이다. 바로 카카오톡의 미리보기이다.

<meta>태그 안에 property="og:***" 형태로 프로퍼티를 명시하고, content="컨텐츠의 ***값"으로 정보를 삽입한다. 오픈그래프는 각 페이지마다 4개의 프로퍼티를 요구한다.

  • og:title
  • og:type

    • type 목록에 맞춰 작성해줘야 한다.
    • 딱 맞아 떨어지는 type이 없다고 생각된다면 website가 무난하다.
  • og:image
  • og:url

이 외에도 필요한 것이 있다면 추가하면 된다. 카톡에서는 og:description이 사용되므로 추가하였다.

open graph kakao talk

카카오톡에서 미리보기로 사용되는 오픈그래프

좌측은 오픈그래프 추가 전 결과 물이다. 아마 카카오 톡 측에서 설정해둔 접근법으로 만들어낸 정보로 이루어져 있다. 우측은 오픈그래프 추가 이후 결과물로, 내가 직접 설정한 값으로 표시되고 있다.

모두 다 같은 정보가 아니라, 각 페이지별로 정보를 받기위해 아래와 같은 형식으로 정보를 담아서 SEO.js에서 적용시킨다. 아래는 블로그 포스트 페이지인데, graphql로 받은 정보를 이용할 수 있다.

src/templates/post.js
...
const seo = {
  description: excerpt,
  url: 페이지주소,
  image: frontmatter.thumbnail.childImageSharp.fixed.src,
};
...

리액트에서는 react-helmet을 사용하면 편리하게 <head>태그를 작성할 수 있다.

src/components/SEO.js
import React from 'react';
import { Helmet } from 'react-helmet';
import config from '../config';

const SEO = (props) => {
  const { pageTitle, pageSEO } = props;

// 내려받은 pageTitle로 정의
  let title = pageTitle;

// config를 통해 사이트의 기본정보로 우선 정의
  let description = config.siteDescription;
  let url = config.siteUrl;
  let image = config.siteLogo;

// 페이지별 정보가 따로 있다면 재할당
  if (pageSEO) {
    description = pageSEO.description;
    url = pageSEO.url;
    if (pageSEO.image) {
      image = pageSEO.image;
    }
  }

  return (
    <Helmet>
      // 타이틀
      <title>{title}</title>
      // 메타데이터
      <meta name="description" content={description} />
      <meta name="url" content={url} />
      <meta name="image" content={url} />
      // 오픈그래프
      <meta property="og:title" content={title} />
      <meta property="og:type" content="website" />
      <meta property="og:description" content={description} />
      <meta property="og:url" content={url} />
      <meta property="og:image" content={image} />
    </Helmet>
  );
};

export default SEO;

정확한 태그 사용하기

마찬가지로 <body>안에서도 태그의 각 역할에 맞춰 사용하게 되면, 구글이 내 콘텐츠를 이해하는데 도움이 된다. 표제 태그안엔 표제를 넣고, 이미지 태그엔 이미지를 넣어야 구글이 이해를 한다는 것이다.

표제태그 <h*>안에는 표제만 넣기

나처럼 마크다운으로 포스트를 작성하면, 표제를 적을때, # 어쩌구 형태로 작성하게 된다. #은 <h1>으로 변환이 된다. <h1>~<h6>는 숫자가 작을 수록 중요한 표제이다.
<h1>은 페이지의 전체를 아우르는 대표 표제어이다. 그렇기 때문에 1개만 있어야 효과적이고(대표 표제어가 두개라면 글을 나눠야하는 것이 맞다) 그 아래에 두번째 표제어 <h2>가 있어야한다. 또한 그 내용은 <h1>과 관련되어 좀 더 세부적인 내용을 담고 있어야 한다. 즉 각 표제들은 콘텐츠의 계층 구조를 형성하는 태그이기 때문에 순서에 맞게 작성되어야 한다. 이렇게 작성된 표제들은 개요의 의미를 가지고 있고, 이는 사용자 및 구글이 글을 이해하는데 도움을 준다.

주의해야 할 점은, 표제태그는 스타일로 강조를 위해 사용하는 것이 아니다. 텍스트를 강조하는 <strong>, <em> 과 같은 태그도 있으니 목적에 맞게 사용해야한다. <h6>까지 존재하긴 하지만, 구글에선 <h1>~<h3>까지 의미있게 살펴본다고 알려져 있다. 그러므로 <h1>~<h3>까진 더욱 신경써서 작성하고, 표제 태그를 너무 남발하지 않도록 주의한다.

이미지 최적화 하기

이미지를 삽입할 경우, <img>태그를 사용하여 삽입하게된다. 이때 alt 속성을 이용하여 (의미를 잘 담고있는)파일 이름 및 설명을 넣어주어야 한다. alt 속성은 이미지가 로드되지 않을때 사용자에게 이미지에 대한 정보를 줄 수 있고, 또한 구글의 이미지 검색에 검색어로 사용되는 등 구글이 컨텐츠를 이해하는데 도움을 준다. alt속성의 텍스트는 짧으면서도 명확하게 이미지를 설명하는 것이 좋다. image.jpg와 같은 의미없고 일반적인 설명은 피해야하고, 너무 길지 않아야한다. 참고로, 일반적으로 지원되는 파일 형식(jpg, gif, png, bmp, webp 등)이 좋다.

구조화된 데이터 마크업 <script>로 추가하기

페이지에 구조화된 데이터를 입력하면 구글에게 콘텐츠에 대한 정보를 확실하게 제공할 수 있다. 예를 들어, 도서에 관련된 포스팅이라고 한다면, 미리 이 페이지는 책에 관련된 페이지이며, 그 책의 제목, 저자, 발행연도, ISBN (국제표준도서번호) 등 정보를 미리 명시를 하는 것이다. 이러한 정보가 미리 제시되어 있다면 구글이 콘텐츠를 이해하기 더 쉽고, 특정 콘텐츠들은 리치결과로 보여주기도 한다.

당연하게도 거짓 정보, 상관없는 마크업을 작성해서는 안된다. 구글은 구조화된 데이터 마크업을 JSON-LD형식으로 권장하며, 대부분 schema.org용어를 사용한다.

나의 블로그의 구조화된 데이터 작성하기

구조화된 데이터는 <head>안에 <script>로 삽입해야한다. JSON형태로 만들어진 데이터를 배열로 관리한다. 사이트 전반에 관련한 기본 구조화된 데이터를 만들어 배열의 기본으로 담아둔다. 그 후, 각 페이지에서 구조화된 데이터를 추가해야 하는 상황이 온다면, 배열에 push한다.

이 블로그는 프론트앤드 기술과 관련하여 기록하는것이 목적이므로 구조화된 Article데이터를 작성하면 되고 Article 객체는 BlogPosting을 기반으로 작성하면 된다. 또한 필요시에 Breadcrumb(탐색경로) 데이터를 추가할 수 있다.
일단 기본적으로 '@type': 'WebSite'를 구조화된 데이터로 할당해두었다. 블로그 포스트가 주된 콘텐츠이므로, 포스트 페이지에서만 구조화된 데이터를 추가하면 될 것아 '@type': 'BreadcrumbList'@type': 'BlogPosting'을 추가하였다. 최종 완성된 배열을 JSON 문자열로 반환(JSON.stringfy()메소드 사용)하여 <script>로 리턴해준다.

src/components/SEO.js
import React from 'react';
import { Helmet } from 'react-helmet';
import urljoin from 'url-join';

import config from '../config';

const SEO = (props) => {
  const { pageTitle, pageSEO } = props;
  ...

  const jsonLd = [
    {
      '@context': 'http://schema.org',
      '@type': 'WebSite',
      url: config.siteUrl,
      name: title,
      alternateName: config.siteTitle,
      logo: config.siteLogo
    }
  ];

  if (pageSEO.isStructuredData) {
    jasonLd.push(
      {
        '@context': 'http://schema.org',
        '@type': 'BreadcrumbList',
        itemListElement: [
            {
              '@type': 'ListItem',
              position: 1,
              item: {
                '@id': urljoin(config.siteUrl, pageSEO.isStructuredData),
                name: pageSEO.isStructuredData,
                image: config.siteLogo,
              },
            },
            {
              '@type': 'ListItem',
              position: 2,
              item: {
                '@id': url,
                name: title,
                image,
              },
            },
          ],
      },
      {
        '@context': 'http://schema.org',
        '@type': 'BlogPosting',
        url,
        name: `${title} | ${config.siteTitle}`,
        alternateName: config.siteTitle,
        description,
        headline: title,
        image: {
          '@type': 'ImageObject',
          url: image,
        },
      }
    );
  }

  ...

  return (
    <Helmet>
      ...
      <script type="application/ld+json">{JSON.stringify(jasonLd)}</script>
      ...
    </Helmet>
  );
};

구조화된 데이터 일반 가이드 라인구조화된 데이터 마크업 도우미를 통해 도움을 받을 수 있다.

그 외 방법

  • 모바일 친화적으로 만들기

    • 구글은 2016년 말 부터 사이트의 모바일 버전을 사용하여 순위를 지정하고 구조화된 데이터를 파싱하여 스니펫을 생성한다고 명시하고 있다.
    • 반응형 디자인, 별도 URL, 동적 게재등을 구글에서는 지원한다.
    • 모바일 친화성 테스트로 테스트 해볼 수 있다.

result mobile friendly

모바일 친화성 테스트 결과

  • 사이트맵 작성하기

    • gatsby에서는 gatsby-plugin-sitemap 플러그인으로 간단하게 작성 가능하다.
    • 자세한 사항은 구글의 사이트맵 관리 참고.
  • 404 페이지도 유용하게

    • 존재하지 않은 주소로 이동할 경우, 막연하게 오류를 내보내지 않고 404 맞춤 페이지로 연결하는 것이 좋다.
    • 404 페이지에 루트 페이지로 연결하는 링크가 있어야 한다.
    • 막연하거나 관계없는 메세지를 전달해서는 안되고, 404 혹은 페이지 찾을 수 없음 등 명확한 표시를 해야한다.
    • 404 페이지도 크롤링이 되어야 한다.
  • 크롤링 안되는 페이지 관리하기

    • robots.txt를 이용하여 관리
    • gatsby에서는 마찬가지로 플러그인 gatsby-plugin-robots-txt이용 가능하다.
    • 개인적으로는 about페이지를 크롤링 disallow 해두었다.

마무리

예전에는 단순히 운이 좋으면, 꾸준히 쓰면 언젠간 구글에 걸리겠지...라고 막연하게 생각했었다. SEO를 전문적으로 하는 직업이 존재할 정도로 중요하고 전문적인 분야이기 때문에 거리감을 가지고 있기도 했다. 그만큼 전문적이진 않겠지만... 그래도! 구글 문서가 생각보다 자세하게 알려주어 공부하면서 작성해보았다. 이렇게 해도 바로 그 즉시 크롤링 한다거나, 이 페이지를 반드시 무조건 크롤링 하는 것이 아니기때문에, 조금 더 지켜봐야 할 것 같다. 그래도 직접 SEO를 해보면서, 검색엔진들이 어떻게 크롤링을하는지, 구글이 어떤 기준으로 하는지 알 수 있었다. 컨텐츠가 구글이 만드는 스니펫 유형에 더 가까운 컨텐츠였다면 더 치밀하게 SEO할 수 있을 것 같은데, 내가 공부한 것들을 올리는 정도의 컨텐츠라 다양하게 못해본게 아쉽다. 만약 블로그가 아닌 다른 작업을 한다면, 그 컨텐츠의 특징에 맞게 더욱 섬세하게 SEO를 할 수 있을 것 같다.

Reference & Learn More

Google 검색엔진 최적화(SEO)초보자 가이드
Google 구조화된 데이터 작동 방식 이해
Google 구조화된 데이터 일반 가이드라인
Google robots.txt 소개
Google Webmaster Central Blog: Improve snippets with a meta description makeover
Enable Search result features for your site
Open Graph
JSON-LD
schema.org
MDN HTML 시작하기

Comments