본문 바로가기
Framework/- Next.js

Nextjs SEO 개선 과정 정리

by Yoojacha 2023. 7. 17.

2023년 온라인 졸업전시 웹사이트를 배포한 후 검색 엔진 최적화를 진행하고 프로젝트를 마무리한 과정을 기록해두기 위해서 글을 작성했습니다. 전 과정을 기록하지 않고 출처에 나와있는 과정들을 따라했기에 생략이 되어있을 수도 있습니다!


sitemap.xml과 robots.txt 설정

티스토리 블로그의 검색 최적화와 비슷했습니다. robots.txt를 생성하고, Sitemap 정보에 대해서 특정 url로 진입하라는 내용을 넣어주면 됐습니다. sitemap의 경우에 블로그는 동적으로 생성되는 로직이 자동으로 되어있었습니다.

 

하지만 Nextjs는 Sitemap을 직접 넣어주거나, 동적으로 생성되게 하기 위해서 pages/api 폴더를 사용했습니다. 그리고 api 폴더로 강제되는 url을 next.config.js  파일에서 rewrites 함수를 통해서 다른 url로 매핑했습니다.

// pages/api/sitemap.ts

import { NextApiRequest, NextApiResponse } from "next";

export default function handler(req: NextApiRequest, res: NextApiResponse) {
  res.statusCode = 200;
  res.setHeader("Content-Type", "text/xml");

  // Instructing the Vercel edge to cache the file
  res.setHeader("Cache-control", "stale-while-revalidate, s-maxage=3600");

  // generate sitemap here
  const xml = `
  <?xml version="1.0" encoding="UTF-8"?>
  <urlset
        xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9
        http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">
    <url>
      <loc>https://sak-exhibition.vercel.app/</loc>
      <lastmod>2023-07-17T11:19:38+00:00</lastmod>
      <priority>1.00</priority>
    </url>
    <url>
      <loc>https://sak-exhibition.vercel.app/home</loc>
      <lastmod>2023-07-17T11:19:38+00:00</lastmod>
      <priority>0.80</priority>
    </url>
    <url>
      <loc>https://sak-exhibition.vercel.app/about</loc>
      <lastmod>2023-07-17T11:19:38+00:00</lastmod>
      <priority>0.64</priority>
    </url>
    <url>
      <loc>https://sak-exhibition.vercel.app/exhibition</loc>
      <lastmod>2023-07-17T11:19:38+00:00</lastmod>
      <priority>0.64</priority>
    </url>
    <url>
      <loc>https://sak-exhibition.vercel.app/contactus</loc>
      <lastmod>2023-07-17T11:19:38+00:00</lastmod>
      <priority>0.64</priority>
    </url>
  </urlset>`;

  res.end(xml);
}

// next.config.js

/** @type {import('next').NextConfig} */
const nextConfig = (phase) => {
  return {
    async rewrites() {
      return [
        {
          source: "/sitemap.xml",
          destination: "/api/sitemap",
        },
      ];
    },
    // ...다른 설정들
  };
};

module.exports = nextConfig;

Google Search Console과 Naver Search Advisor 연결

검색 로그를 대시보드에서 확인하기 위해서는 아래처럼 meta 태그를 넣어주면 됩니다. 저는 혹시몰라서 환경변수에 추가해서 넣어주었습니다.

        {/* Naver Site Verification*/}
        <meta
          name="naver-site-verification"
          content={process.env.NEXT_PUBLIC_NAVER_SITE_VERIFICATION}
        />
        {/* Google Site Verification*/}
        <meta
          name="google-site-verification"
          content={process.env.NEXT_PUBLIC_GOOGLE_SITE_VERIFICATION}
        />

검색을 해보니 GitHub 레포지토리의 About에 적히는 내용들이 description 속성으로 들어가서 검색을 했을 때, 보여지는 것을 보고, 아래 오른쪽 이미지 처럼 영어와 한글을 둘 다 적어주었습니다.


Open Graph 동적으로 설정

Open Graph Protocol

Naver Search Advisor에서 저희 웹사이트의 조회결과를 알려주는 데 Open Graph에 대해서 설정되어있지 않다라는 것을 알려주었습니다. 찾아보니 Open Graph Protocol 이라는 것을 알게 되었고, 자세히 들어다 보지는 않았지만 네이버에서 가이드해주는 것 대로, meta 태그만 잘 넣어주면 되는 것으로 파악했습니다.

 

meta 태그를 주어진 형식대로 잘 넣어주면, 카카오톡에 링크를 공유 했을 때, 해당하는 open graph meta  태그를 보고 썸네일, 제목, 설명, 링크를 통해서 카드를 생성해줍니다.


Google Analytics 연결

사실 웹사이트가 규모가 있거나, 수익을 창출하는 웹사이트는 아니였지만, 하는 김에 경험해보자는 마음으로 GA도 연결해봤습니다. Nextjs의 경우에는 13버전 부터 Script 태그를 따로 제공해주기 때문에 검색하는데 시간이 걸렸습니다. 막상 찾으면 금방 적용하니 어렵지는 않았습니다.

      {/* 구글 애널리틱스 */}
      <Script
        strategy="afterInteractive"
        src={`https://www.googletagmanager.com/gtag/js?id=${ga_key}`}
      />
      <Script
        id="gtag-init"
        strategy="afterInteractive"
        dangerouslySetInnerHTML={{
          __html: `
            window.dataLayer = window.dataLayer || [];
            function gtag(){dataLayer.push(arguments);}
            gtag('js', new Date());
            gtag('config', '${ga_key}', {
              page_path: window.location.pathname,
            });
          `,
        }}
      />


meta tag 동적 할당

// pages/exhibition/[...slug].tsx

export default function ProjectPage({
  projectData,
}: {
  projectData: IProject;
}) {
  return (
    <>
      <Head>
        <title>SAK-Exhibition | {projectData.name}</title>
        <meta property="og:type" content="website" />
        <meta
          property="og:title"
          content={`SAK-Exhibition | ${projectData.name}`}
        />
        <meta property="og:description" content={projectData.excerpt} />
        <meta
          property="og:image"
          content={`https://sak-exhibition.vercel.app/posts-images/${projectData.slug.replaceAll(
            "/",
            "_"
          )}/thumbnail.jpg`}
        />
        <meta
          property="og:url"
          content={`https://sak-exhibition.vercel.app/exhibition/${projectData.slug}`}
        />
      </Head>

      <MainLayout>
        <Project projectData={projectData} />
      </MainLayout>
    </>
  );
}

 

전체 코드

// pages/_app.tsx

export default function App({ Component, pageProps }: AppProps) {
  const router = useRouter();
  const clientId = process.env.NEXT_PUBLIC_NAVER_MAP_CLIENT_ID;
  const ga_key = process.env.NEXT_PUBLIC_GOOGLE_ANALYTICS_KEY;

  return (
    <>
      <Head>
        <title>SAK-Exhibition</title>
        <meta
          name="description"
          content="SAK-Exhibition Generated by Sae-sak"
        />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <meta name="msapplication-TileColor" content="#FBFBFB" />
        <meta name="msapplication-TileImage" content="/ms-icon-144x144.png" />
        {/* Naver Site Verification*/}
        <meta
          name="naver-site-verification"
          content={process.env.NEXT_PUBLIC_NAVER_SITE_VERIFICATION}
        />
        {/* Google Site Verification*/}
        <meta
          name="google-site-verification"
          content={process.env.NEXT_PUBLIC_GOOGLE_SITE_VERIFICATION}
        />
      </Head>

      {/* 구글 애널리틱스 */}
      <Script
        strategy="afterInteractive"
        src={`https://www.googletagmanager.com/gtag/js?id=${ga_key}`}
      />
      <Script
        id="gtag-init"
        strategy="afterInteractive"
        dangerouslySetInnerHTML={{
          __html: `
            window.dataLayer = window.dataLayer || [];
            function gtag(){dataLayer.push(arguments);}
            gtag('js', new Date());
            gtag('config', '${ga_key}', {
              page_path: window.location.pathname,
            });
          `,
        }}
      />
      
      {/* 네이버지도 API */}
      <Script
        type="text/javascript"
        src={`https://openapi.map.naver.com/openapi/v3/maps.js?ncpClientId=${clientId}`}
        defer
      ></Script>

      {/* 렌더링할 컴포넌트 */}
      <Component {...pageProps} key={router.route} />
    </>
  );
}

참고

https://darrengwon.tistory.com/246

 

SEO의 첫걸음 : meta tag + og meta

meta 태그를 알아보자 (웹표준과 SEO) 처음 웹개발을 배울 때 웹표준을 배웠던 것 사실만 어렴풋이 기억난다. 그 이후 개발을 해오면서는 거의 신경쓰지 않고 개발했던 것 같다. 얼마 전, 이 블로그

darrengwon.tistory.com

https://www.zodaland.com/tech/16

 

ZODALAND

sitemap 제출은 검색 엔진 최적화를 위해 제출하는게 좋다. 그래서 초간단 Next.js 사이트맵 생성하기 가이드를 작성해본다.

www.zodaland.com

https://jisoo-log.tistory.com/19

 

Next.js 다이나믹 메타 태그 넣기

저번 글에 이어서 SEO 작업을 계속 하고 있다. 이번주엔 다이나믹 메타 태그 작업을 했다. (메타태그 종류나, 검색최적화를 위한 추천 등의 내용은 없습니다) https://jisoo-log.tistory.com/18 Next.js 정적,

jisoo-log.tistory.com

https://velog.io/@bluestragglr/%EC%A3%BC%EB%8B%88%EC%96%B4%EB%8F%84-%ED%95%A0-%EC%88%98-%EC%9E%88%EB%8A%94-Next.js-SEO-robots.txt%EC%99%80-sitemap.xml-%EC%9E%90%EB%8F%99-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0

 

주니어도 할 수 있는 Next.js SEO - robots.txt와 sitemap.xml 자동 생성하기

본 포스트는 개발과 배포에 대한 지식은 가지고 있지만 아직 조금 낯설고 어려운 주니어 개발자들을 대상으로 작성되었습니다. Circle CI, GitHub Actions같은 CI(Continuous Integration) 툴과 자동화 배포에

velog.io

https://wikidocs.net/197689

 

09) Next.js에서 성능 및 SEO 최적화

[TOC] ## Next.js의 내장 성능 최적화 이해하기 Next.js는 성능을 위한 다양한 내장 최적화 기능을 제공합니다. Next.js의 중요한 기능 중 하나는 자동 코…

wikidocs.net

https://velog.io/@zinukk/d-v8gyfq4x

 

Next.js SEO 최적화 적용하기

SEO를 적용해보자!

velog.io

https://otter-log.world/post/next-seo-optimazation

 

Next 블로그 seo 최적화 하기 | 오터 로그

meta 태그와 sitemap 구성하기

otter-log.world

https://searchadvisor.naver.com/guide/markup-content

 

콘텐츠 마크업

페이지 제목 HTML 문서의 <head> 태그내에 있는 <title> 태그를 활용합니다 페이지 제목 ◆ 페이지 제목 작성 TIP! 사이트 메인 페이지의 title 태그는 사이트의 성격을 잘 표현할 수 있는 브랜드명으로

searchadvisor.naver.com

https://blog.ab180.co/posts/open-graph-as-a-website-preview

 

링크의 미리보기 제목, 설명, 이미지를 결정하는 open graph 태그

링크의 미리보기 제목, 설명, 이미지를 결정하는 open graph 태그에 대해 알아보자

blog.ab180.co

 

댓글