[낙서 #10] 2022년 2월 2일

낙서·2022년 2월 2일
0

낙서

목록 보기
10/22

Next.js SEO

Crawling and Indexing

HTTP Status Codes

HTTP resonse status code들은 HTTP request가 성공적으로 완료되었는지 표시한다.

200

'HTTP 200 OK' success status response code는 request가 성공했음을 알린다.
구글에 인덱싱되기 위해서는 반드시 상태 코드 200을 받아야한다. Next.js가 페이지를 성공적으로 렌더링 했을 때의 기본 코드이다.

301/308

'HTTP 301 Moved Permanently' redirect status response code는 요청한 리소스가 destination URL로 이동되었음을 알린다. 이것은 영구적인 리다이렉트이고 리다이렉트 종류 중 가장 많이 사용된다. 리다이렉트들은 다양한 이유들로 사용될 수 있지만 URL이 A 포인트에 B 포인트로 이동되었음을 알리는 용도로 주로 사용된다. 만약 콘텐츠가 다른 곳으로 이동되었다면 현재, 예비 고객들을 잃지 않고 봇들이 계속 사이트를 인덱싱 할 수 있도록 리다이렉트가 필요하다.
Next.js는 301대신 더 최근 버전이면서 더 좋은 옵션인 308을 사용한다. 두가지의 차이점은 301의 경우 request method를 POST에서 GET으로 변경하는 것을 허용하지만 308은 허용하지 않는다.

getStaticProps 함수에서 props 대신 리디렉션을 반환하여 Next.js에서 308 리디렉션을 트리거 할 수 있다.

//  pages/about.js
export async function getStaticProps(context) {
  return {
    redirect: {
      destination: '/',
      permanent: true // triggers 308
    }
  }
}

next.config.js에서 permanent: true key를 사용 할 수도 있다.

//next.config.js

module.exports = {
  async redirects() {
    return [
      {
        source: '/about',
        destination: '/',
        permanent: true // triggers 308
      }
    ]
  }
}

302

HTTP 302 Found redirect status response code 는 리소스 요청이 임시적으로 URL이 옮겨진 것을 알린다.
302보다는 301이 대부분 사용된다. 일시적으로 사용자를 특정 페이지로 리디렉션하거나(예: 프로모션 페이지) 위치 기반으로 사용자를 리디렉션하는 경우는 302에 해당되지 않는다.

404

HTTP 404 Not Found client error response code는 서버가 요청된 리소스를 찾을 수 없는 경우를 표시한다.
유저가 존재하지 않는 URL을 방문 할 시에 발생하고 검색 순위에 영향을 줄 수 있으니 페이지에서 빈번한 에러가 발생하면 안된다.
Next.js는 자동적으로 존재하지 않는 URL들에 방문시 404 상태 코드를 리턴한다.
간혹 404 상태 코드를 리턴하길 원하는 경우가 있는데 아래 코드처럼 props로 전달하면 된다.

export async function getStaticProps(context) {
  return {
    notFound: true // triggers 404
  }

빌드타임에 정적으로 만들어지는 커스텀 404 페이지는 pages/404.js 파일을 생성하면 만들 수 있다.

// pages/404.js
export default function Custom404() {
  return <h1>404 - Page Not Found</h1>
}

410

HTTP 410 Gone client error response code는 origin server에서 타겟 시소스 접근이 더이상 불가하고 이것이 영구적인 경우를 알린다.
자주 사용되지는 않지만 지워질 콘텐츠가 있을 때 사용 될 수 있다.
사용 에:

  • E-Commerce: 상품들이 영구적으로 목록에서 지워짐
  • Forum: 유저에 의해 지워진 Threads
  • Blog: 사이트에서 지워진 블로그 포스트
    이 상태코드는 봇들이 절대 크롤링하지 않도록 한다.

500

HTTP 500 Internal Server Error response code는 서버가 request fullfill 과정 중 예상치못한 상황을 만난 경우를 표시한다.
Next.js 는 예상치못한 앱 에러 발생시 자동적으로 500 상태 코드를 리턴한다.
빑드타임에 정적으로 만들어진 커스텀 500 에러 페이지는 pages/500.js로 만들 수 있다.

// pages/500.js
export default function Custom500() {
  return <h1>500 - Server-side error occurred</h1>
}

503

HTTP 503 Service Unavailable server error response code는 서버가 request를 처리할 준비가 되지 않음을 알린다.
웹사이트가 다운되었고 특정 시간동안 다운 상태가 지속 될 것이 예상될 때 사용하는 것이 추천된다. 장기적으로 순위를 잃는 것을 막아준다.

Robots.txt

robots.txt란?

robots.txt 파일은 어떤 페이지와 파일들이 해당 사이트에서 크롤러가 request 할 수 있는지 혹은 request 할 수 없는지 검색엔진 크롤러들에게 알려준다. robots.txt 파일은 웹 표준 파일으로 대부분의 좋은 봇들은 이것을 도메인에서 request들을 하기 전에 사용한다.

CMS, admin, e-commerce user accounts, api routes 같이 웹사이트에서 크롤링 혹은 인덱싱되지 않도록 보호하고 싶은 영역이 있을 수 있다. 이 파일들은 호스트의 root에 위치시키거나 대안으로 /robots.txt 경로를 destination URL로 리디렉션 할 수 있다.

robots.txt 파일을 Next.js 프로젝트에 추가하는 방법

Next.js는 정적 파일을 제공할 수 있기 때문에 쉽게 robots.txt 파일을 추가 할 수 있다. public 폴더에 robots.txt를 추가한다. 아래와 같은 예시를 내용으로 입력 할 수 있다.

//robots.txt

# Block all crawlers for /accounts
User-agent: *
Disallow: /accounts

# Allow all crawlers
User-agent: *
Allow: /

앱을 yarn dev로 실행시키면 http://localhost:3000/robots.txt 경로가 가능해진다. static assets을 제공하는 public 폴더는 URL에 포함되지 않는다. 다른 것으로 이름을 바꿔서는 안된다.

XML Sitemaps

Sitemaps는 구글과 소통하기 가장 쉬운 방법 중 하나다. 웹사이트에 속한 URL들을 알려주고 업데이트될 때도 알려준다. 구글은 쉽게 새로운 콘텐츠를 감지하고 웹사이트를 더 효율적으로 크롤링한다.

XML Sitemaps가 가장 널리 알려지고 널리 사용되는데 RSS, Atom, Text 파일들로도 만들어 질 수 있다.

Sitemap은 페이지, 비디오 그리고 웹사이트의 다른 파일들의 정보와 관계를 제공한다. 구글같은 검색 엔진들은 웹사이트를 더 스마트하게 크롤링하기 위해서 이 파일을 사용한다.

구글에 의하면 아래의 경우에 sitemap이 필요하다고 한다.

  • 사이트가 매우 클 경우: 구글 웹 크롤러가 새로운 페이지 크롤링을 지나칠 수 있다.
  • 사이트가 서로 잘 연결되지 않은 컨텐츠 페이지들을 많이 보관하는 경우: 페이지들이 서로 참조가 잘 안되어 있는 경우 사이트맵에 그것들을 리스트해서 구글이 페이지들을 간과하지 않도록 할 수 있다.
  • 사이트가 새로 만들어졌고 연결된 외부 링크가 적을 경우: 구글봇과 다른 크롤러들은 페이지들간의 링크를 따라 움직이기도 하는데 결과적으로 링크되어있지 않다면 페이지를 찾지 못 할 수 있다.
  • 사이트가 풍부한 미디어(비디오, 이미지 등)를 많이 가지고 있거나 구글 뉴스에 보여지는 경우: 구글이 sitemaps로 부터 추가적인 정보를 인식 할 수도 있다.

sitemap들은 훌륭한 검색엔진 성능에 필수는 아니지만 크롤링과 인덱싱 과정에서 컨텐츠를 더 빠르게 읽고 순위를 매길 수 있도록 할 수 있다.

sitemap을 사용하고 웹사이트에 새로운 콘텐츠가 생길 때 사이트맵을 동적으로 만드는 것이 좋다. 정적인 sitemaps도 효과가 있지만 지속적인 탐색을 제공하지 않으므로 구글에 덜 유용 할 수 있다.

Next.js 프로젝트에 Sitemaps 추가하는 방법

두가지 방법이 있다.

수동

만약 관계적으로 간단하고 정적인 사이트라면 수동적으로 sitemap.xml을 프로젝트 public 폴더에 만드는 방법을 사용 할 수 있다.

<!-- public/sitemap.xml -->
<xml version="1.0" encoding="UTF-8">
  <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
    <url>
      <loc>http://www.example.com/foo</loc>
      <lastmod>2021-06-01</lastmod>
    </url>
  </urlset>
</xml>
getServerSideProps (동적 사이트맵)

웹사이트가 다이나믹한 경우 사용하는 것이 좋다. getServerSideProps를 활용하여 주문형 XML sitemap을 생성 할 수 있다.

pages 폴더에 새로운 페이지로 만들 수 있다. pages/sitemap.xml.js로 만들어 사용한다. 이 페이지의 목적은 동적인 페이지들의 URL들을 알기 위해 API로 부터 데이터를 받는 것이다. 그 다음 /sitemap.xml XML 파일을 response로 작성 할 것이다.

예시

//pages/sitemap.xml.js
const EXTERNAL_DATA_URL = 'https://jsonplaceholder.typicode.com/posts'

function generateSiteMap(posts) {
  return `<?xml version="1.0" encoding="UTF-8"?>
   <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
     <!--We manually set the two URLs we know already-->
     <url>
       <loc>https://jsonplaceholder.typicode.com</loc>
     </url>
     <url>
       <loc>https://jsonplaceholder.typicode.com/guide</loc>
     </url>
     ${posts
       .map(({ id }) => {
         return `
       <url>
           <loc>${`${EXTERNAL_DATA_URL}/${id}`}</loc>
       </url>
     `
       })
       .join('')}
   </urlset>
 `
}

function SiteMap() {
  // getServerSideProps will do the heavy lifting
}

export async function getServerSideProps({ res }) {
  // We make an API call to gather the URLs for our site
  const request = await fetch(EXTERNAL_DATA_URL)
  const posts = await request.json()

  // We generate the XML sitemap with the posts data
  const sitemap = generateSiteMap(posts)

  res.setHeader('Content-Type', 'text/xml')
  // we send the XML to the browser
  res.write(sitemap)
  res.end()

  return {
    props: {}
  }
}

export default SiteMap

검색엔진을 위한 특수 meta tag

Meta robot tag들은 검색엔진이 항상 준수하는 지시문이다. 이 robots tag들을 추가하는 것은 웹사이트가 더 쉽게 인덱싱 될 수 있도록한다.

directives(지시) 와 suggestions(제안)에는 차이가 있다. meta robots tagsrobots.txt 파일들은 directives이고 항상 처리된다. Canonical tags들은 구글이 처리할지 말지 결정 할 수 있는 제안이다.

페이지 단계 meta tag들에 있어서는 여러 옵션들이다. 하지만 다음 예시는 보편적으로 SEO에 사용되는 예시이다.

<meta name="robots" content="noindex,nofollow" />

robots 태그는 아마 가장 보편적으로 사용되는 것이다. 기본적으로 이것은 index,follow 값을 디폴트로 가지고 있어서 이것은 표시 할 필요없다. all도 이것을 대체 할 수 있다.

<meta name="robots" content="all" />

robots 태그를 noindex,nofollow로 설정하면 이것은 아래 내용들을 검색엔진에게 알려준다.

noindex

페이지를 검색 결과에 보이지 않게 한다. noindex를 표시하지 않으면 페이지는 인덱싱 될 수 있고 검색 결과에 나올 수 있다.

웹사이트를 만들 때, 특정 페이지들이 인덱싱되지 않기를 원할 수 있다. 보편적으로 설정 페이지, 내부 검색 페이지, 정책 같은 페이지들이 그렇다.

nofollow

이 페이지에 있는 링크들을 따라가지 않도록 하기 위해 사용한다. 이것을 생략하면 로봇들이 링크들을 팔로우하고 크롤링 할 수 있도록 허용한다. 다른 페이질에서 발견된 링크들이 크롤링을 허용하게 할 수 있기 때문에 nofollw robots tag가 있으면 크롤되지 않도록 할 수 있다.

Googlebot tag

<meta name="googlebot" content="noindex,nofollow" />

가끔 googlebot 태그를 볼 수 있는데 구글 특정 태그이다. 이 태그를 사용하면 구글봇에만 특정 규칙을 적용 할 수 있다. tag들이 충돌하면 더 제한적인 태그가 적용된다.

robots.txt를 통해 크롤되지 않을 URL들을 추가 할 수 있지만 meta tag는 좀 더 유연하게 noindex 할 수 있다. 예를들어 만약 상품들 페이지에 필터를 적용하고 결과가 없을 때 이 페이지에 noindex를 사용해야한다.

robots.txt 파일을 통해 크롤링이 제한된 URL은 구글에서 크롤링하지 않는다. 하지만 페이지가 이미 인덱싱된 후에 규칙이 추가되면 페이지가 인덱싱된 상태로 남아있을 수 있다. 페이지가 확실하게 인덱싱되지 않도록 하는 방법은 noindex tag를 사용하는 것이다.

구글은 아주 드물게 페이지를 크롤링하지 않고 인덱싱하도록 결정하는데 페이지가 특정 검색 결과를 충족하고 페이지가 유저가 원하는 것을 가졌다고 확신 할 때 그렇다.

Google tag

nositelinkssearchbox
<meta name="google" content="nositelinkssearchbox" />

유저가 사이트를 검색 할 때, 구글 검색 결과들은 가끔 사이트 다이렉트 링크들과 함께 검색 상자를 표시한다. 이 태그는 구글이 sitelinks 검색 상자를 표시하지 않도록 해준다.

notranslate
<meta name="google" content="notranslate" />

구글이 사이트가 유저가 읽기 쉬운 언어가 아닌 것을 인지하면 가끔 검색 결과를 번역한 링크를 제공한다.

보통 더 많은 그룹의 사용자들에게 컨텐츠를 제공 할 수 있도록 하지만 번역을 원하지 않는 상황들도 많다. 이 메타 태그는 구글에게 이 페이지 번역을 제공하지 않도록 한다.

예시

import Head from 'next/head'

function IndexPage() {
  return (
    <div>
      <Head>
        <title>Meta Tag Example</title>
        <meta name="google" content="nositelinkssearchbox" key="sitelinks" />
        <meta name="google" content="notranslate" key="notranslate" />
      </Head>
      <p>Here we show some meta tags off!</p>
    </div>
  )
}

export default IndexPage

tag들이 중복되지 않도록 key 항목을 사용하여 태그가 한번 렌더링되도록 한다.

Canonical Tags

Canonical Tags란

canonical URL은 검색엔진이 생각하기에 웹사이트의 중복된 페이지들 중에서 가장 대표적이라고 생각되는 페이지의 URL이다.

검색엔진에 직접적으로 canonical URL을 전달 할 수 있지만 따로 알리지 않고 URL들을 그룹화하기도 한다. 구글이 경로들을 찾을 수 있다면 자동적으로 이 과정이 이루어질 수 있다. 구글은 이것들을 잘 감지하지만 시스템은 규모가 거대해서 모든 특수한 경우들을 커버하지는 않는다. Canonical tag들은 웹사이트의 좋은 퍼포먼스를 위해 다루어져야하는 중요한 요소이다. 만약 구글이 콘텐츠가 동일한 여러 URL을 찾으면 중복으로 간주하여 URL을 검색결과에서 강등시킬 수도 있다.

만약 같은 컨텐츠를 두개의 다른 웹사이트에서 실행 한다면 검색엔진은 둘 중 하나가 랭크되기를 선택하거나 둘 다 강등시킬 수도 있다. 이럴 때 canonical tag가 매우 유용하다. 구글에게 어떤 url이 중복된 것들 중 original인지 알려준다. 중복된 페이지들은 검색 순위가 좋지 않게 만들 수 있다.

예를들어 커머스 사이트에서 상품들을 여러개의 url을 통하여 접속 할 수 있는 경우 두가지 모두 사용 가능한 URL들 이지만 canonical tag를 사용하여 중복을 막고 랭크될 페이지를 정 할 수 있다.

<link rel="canonical" href="https://example.com/products/phone" />

Canonical tag들은 SEO 성능에 기본적인 요소이며 마케팅 툴로서도 사용 할 수 있다.

Example

import Head from 'next/head'

function IndexPage() {
  return (
    <div>
      <Head>
        <title>Canonical Tag Example</title>
        <link
          rel="canonical"
          href="https://example.com/blog/original-post"
          key="canonical"
        />
      </Head>
      <p>This post exists on two URLs.</p>
    </div>
  )
}

export default IndexPage

Rendering and Ranking

JavaScript는 웹 개발 환경에서 중요한 부분이다. 과거에는 대부분의 프로그램 언어들은 컨텐츠를 서버에서 직접적으로 보냈었다.

자바스크립트 같은 기술을 이용해, 브라우저에서 정보를 가져오는 것이 이전에 비해서 인기있어졌다. 이것은 검색엔진들에게 영향을 미졌고 그들의 페이지를 읽는 능력에 영향을 미쳤다. 과거에는 대부분의 봇들은 서버로부터 첫 HTML 파일만 파싱하고 브라우저에 로드했었다.

Rendering Strategies

Static Site Generation (SSG)

Static site generation은 HTML이 빌드타임에 생성된다. 이 HTML은 각각의 요청에 사용된다. 정적 사이트 생성은 모든 HTML 파일이 pre-rendered되기 때문에 아마 SEO에 있어서 최고의 전략일 것이다. 또한 페이지 성능에도 도움이 된다. 페이지 성능은 SEO에 있어서 순위 책정 요소이기도 하다.

Server-Side Rendering (SSR)

SSG처럼 서버사이드렌더링은 pre-render 된다. 그렇기 때문에 SEO에도 좋다. 하지만 SSG처럼 빌드타임에 생성되지 않고 SSR의 HTML은 request 타임에 생성된다. 매우 다이나믹한 페이지들을 가졌을 때 유용하다.

Increamental Static Regeneration (ISR)

만약 많은 양의 페이지들이 있을 때. 그들을 빌드타임에 모두 생성하는 것은 어려울 것이다. Next.js는 정적 페이지들을 사이트 빌드 후에 생성하고 업데이트 할 수 있도록 도와준다.

Incremental Static Regeneration은 전체 사이트를 rebuild 할 필요 없이 개발자와 컨텐츠 생성자들이 페이지 단위로 정적 생성을 할 수 있도록 도와준다. ISR을 사용하면 수많은 페이지로 스케일이 가능하면서 정적 생성의 이점을 가질 수 있다.

Client Side Rendering (CSR)

클라이언트사이드렌더링은 개발자들이 자바스크립트를 통해 브라우저에 웹사이트들을 완전히 렌더링하도록 도와준다. 첫 페이지는 하나의 HTML 파일을 로드하고 이는 자바스크립트를 가져올 때까지 거의 컨텐츠가 없다. 그리고 브라우저가 모든것을 컴파일한다.

최적의 SEO에는 추천되지 않는다.

CSR은 무거운 데이터 대시보드, 계정 페이지들, 혹은 검색엔진 인덱싱될 필요 없는 페이지들에 적합하다.

요약

SEO에 있어서 가장 중요한 것은 페이지 데이터와 메타데이터가 자바스크립트 없이 로드될 수 있는가이다. 이 경우 SSG, SSR이 가장 좋은 선택지가 될 것이다.

Next.js의 핵심 전략 중 하나는 위의 렌더링 방법들이 페이지 단위로 될 수 있다는 것이다. 너는 아마 너의 블로그 포스트들이 정적으로 생성되기를 원할것이고 고객들의 계정 대시보드가 클라이언트사이트렌더링되길 원하고 뉴스 피드는 서버사이드 렌더링되기를 원할 것이다.

What about AMP?

2016년 구글은 AMP를 사용하는 웹페이지들에 검색엔진 우선순위를 주었다.

AMP는 개발자들에게 웹페이지들을 모바일 디바이스에서 더 빠르게 로드할수있도록 도와준 기술이다.

그리고 시간이 지남에 따라 구축 및 유지보수를 해야했다.

Core Web Vitals 페이지 경험 업데이트로 구글은 AMP 페이지들을 검색 캐러셀에 보여지기 위한 요구사항에서 제거했다. 이것은 SEO 목적에서 구글이 AMP에 가졌던 마지막 이점이었다.

AMP가 소개된 후로 Next.js같은 새로운 기술이 개발 경험(DX)를 희생하지 않으면서 웹사이트 경험을 향상시킬 수 있다는 것을 증명했다.
Next.js가 AMP를 지원하지만, 웹사이트에 이미 우수한 Core Web Vitals 점수들이 있어서 AMP의 큰 이점이 없다.

URL Structure

URL 구조는 SEO 전략에서 중요한 부분이다. 구글이 어떤 부분이 SEO에서 중요도가 있는지 드러내지는 않지만 중요도가 크든 작든 좋은 URL은 모범 사례로 고려된다. 아래 원칙들을 따르면 좋을 것이다.

  • Semantic: ID와 무작위 숫자들보다 의미있는 URL을 사용하는것이 좋다. 예) /learn/basics/create-nextjs-app/learn/course-1/lesson-1보다 좋다.
  • Patterns that are logical and consistent: URL은 일관된 패턴을 따르는 것이 좋다. 예를 들어 각각의 상품마다 각각 다른 경로를 가지는 것 대신 모든 상품들을 그룹으로 만든 폴더를 가지는 것이 좋다.
  • Keyword focused: 구글은 아직 웹사이트가 가진 키워드들을 많이 고려한다. 페이지의 목적을 쉽게 이해 할 수 있도록 URL에 키워드를 사용하는 것이 좋다.
  • Not parameter-based: URL에 파라미터를 사용하는 것은 좋지 않은 방법일 수 있다. 대부분 시멘틱하지 못하고 검색엔진들은 그것들을 헷갈려해서 검색순위를 결과에서 떨어뜨릴 수 있다.

Next.js에서 Routes는 어떻게 정의될까?

Next.js는 pages 컨셉트의 파일 시스템 라우팅을 사용한다. 파일이 pages 폴더에 추가되면 자동적으로 route로 사용 가능하다. pages 폴더 안의 파일들과 폴더들이 가장 보편적으로 사용된다.

간단한 URL들을 보고 어떻게 Next.js router에 추가할지 알아보자

블로그나 커머스 사이트에서 상품 ID 혹은 블로그 이름이 URL에 사용 된다. 이것을 dynamic routing이라고 한다.

Dynamic routing을 사용하기위해 products, blosg 서브폴더 내의 페이지 이름에 대괄호를 추가하여 사용한다. 아래는 SSG를 이용하여 페이지 최적화를 한 예시이다.

// pages/blog/[slug].js

import Head from 'next/head'

export async function getStaticPaths() {
  // Call an external API endpoint to get posts
  const res = await fetch('https://www.example.com/api/posts')
  const posts = await res.json()

  // Get the paths we want to pre-render based on posts
  const paths = posts.map(post => ({
    params: { slug: post.slug }
  }))
  // Set fallback to blocking. Now any new post added post build will SSR
  // to ensure SEO. It will then be static for all subsequent requests
  return { paths, fallback: 'blocking' }
}

export async function getStaticProps({ params }) {
  const res = await fetch(`https://www.example.com/api/blog/${slug}`)
  const data = await res.json()

  return {
    props: {
      blog: data
    }
  }
}

function BlogPost({ blog }) {
  return (
    <>
      <Head>
        <title>{blog.title} | My Site</title>
      </Head>
      <div>
        <h1>{blog.title}</h1>
        <p>{blog.text}</p>
      </div>
    </>
  )
}

export default BlogPost

SSR을 사용한 예시이다.

// pages/blog/[slug].js

import Head from 'next/head'
function BlogPost({ blog }) {
  return (
    <div>
      <Head>
        <title>{blog.title} | My Site</title>
      </Head>
      <div>
        <h1>{blog.title}</h1>
        <p>{blog.text}</p>
      </div>
    </div>
  )
}

export async function getServerSideProps({ query }) {
  const res = await fetch(`https://www.example.com/api/blog/${query.slug}`)
  const data = await res.json()

  return {
    props: {
      blog: data
    }
  }
}

export default BlogPost

Metadata

메타데이터는 웹사이트 콘텐츠의 요약이고 title, description, image를 추가하기 위해 사용된다.

Title

SEO 요소들 중 중요한 요소이다. 유저가 검색결과에서 클릭하게 될 내용이다. 그리고 구글이 페이지 무엇에 관한 것인지 이해하는 데 사용되는 주요 요소이다. 타이틀에 키워드를 사용하면 순위 향상에 도움을 주기 때문에 권장된다.

<title>iPhone 12 XS Max For Sale in Colorado - Big Discounts | Apple</title>

이 페이지는 중요한 키워드들을 가지고 있고 유저가 끌릴 수 있는 discounts 키워드가 사용되었기도 하다.

Description

description 메타 태그 또한 SEO에서 중요한 요소이다. 하지만 title 보다는 덜 중요하다. 구글에 의하면 이 요소는 순위 목적으로는 사용되지 않고 클릭 비율을 높이는데 영향을 준다고 한다.

description meta tag를 사용하여 title 정보를 보충 할 수 있다. 제목에 맞지 않는 키워드가 있는 경우 여기에 콘텐츠에 대한 더 많은 키워드를 입력한다. 만약 사용자 검색어에 포함된다면 이 키워드들은 bold 체로 나타날 것이다.

Next.js 예시

import Head from 'next/head'

function IndexPage() {
  return (
    <div>
      <Head>
        <title>
          iPhone 12 XS Max For Sale in Colorado - Big Discounts | Apple
        </title>
        <meta
          name="description"
          content="Check out iPhone 12 XR Pro and iPhone 12 Pro Max. Visit your local store and for expert advice."
          key="desc"
        />
      </Head>
      <h1>iPhones for Sale</h1>
      <p>insert a list of iPhones for sale.</p>
    </div>
  )
}

export default IndexPage

Open Graph

Open Graph protocol은 페이스북이 개발했는데 웹페이지에서 메타데이터가 사용되는 방식을 표준화 했다. 원하는 만큼 최소한의 정보를 제공 할 수 있지만 모든 오픈그래프 조각들은 연결된 웹사이트 형태를 만들기 위해 추가된다.

핀터레스트, 트위터, 링크드인 등 다른 소셜미디어 회사들은 아마 opengraph를 URL을 공유할 때 카드 형태롤 표시하기 위해 사용한다. 트위터는 twitter tag 카드를 가지고 있다.

이 오픈그래프 태그들이 SEO 관련 태그들과 비슷한점이 많지만 검색엔진순위에는 이점이 없다. 하지만 소셜미디어 공유를 위해 사용하는 것을 권장한다.

Next.js 예시

import Head from 'next/head'

function IndexPage() {
  return (
    <div>
      <Head>
        <title>Cool Title</title>
        <meta name="description" content="Checkout our cool page" key="desc" />
        <meta property="og:title" content="Social Title for Cool Page" />
        <meta
          property="og:description"
          content="And a social description for our cool page"
        />
        <meta
          property="og:image"
          content="https://example.com/images/cool-page.jpg"
        />
      </Head>
      <h1>Cool Page</h1>
      <p>This is a cool page. It has lots of cool content!</p>
    </div>
  )
}

export default IndexPage

페이지 컨텐츠를 제목, 내용, 사진으로 공유 링크로 생성하면 SEO 순위에는 직접 연관은 없지만 클릭률을 높일 수 있다.

Structured Data and JSON-LD

구조화된 데이터는 검색엔진이 페이지를 이해 할 수 있도록 도와준다. 현재는 schema.org라는 것이 있다. schema.org는 인터넷, 웹페이지, 이메일 메시지 등 기계가 해석하기 쉽도록 구조화 된 데이터에 대한 스키마를 생성, 유지, 촉진하는 임무를 가진 공동 작업 커뮤니티 단체이다.

검색엔진들은 각각 여러 어휘를 schema.org 에서 채택하여 사용한다. 어떠한 검색엔진도 schema.org의 모든 것을 커버하지는 않는다. 각각의 경우에 허용된 것을 확인해야한다.

JSON-LD product schema data를 추가한 상품 페이지 예시이다.

import Head from 'next/head'

function ProductPage() {
  function addProductJsonLd() {
    return {
      __html: `{
      "@context": "https://schema.org/",
      "@type": "Product",
      "name": "Executive Anvil",
      "image": [
        "https://example.com/photos/1x1/photo.jpg",
        "https://example.com/photos/4x3/photo.jpg",
        "https://example.com/photos/16x9/photo.jpg"
       ],
      "description": "Sleeker than ACME's Classic Anvil, the Executive Anvil is perfect for the business traveler looking for something to drop from a height.",
      "sku": "0446310786",
      "mpn": "925872",
      "brand": {
        "@type": "Brand",
        "name": "ACME"
      },
      "review": {
        "@type": "Review",
        "reviewRating": {
          "@type": "Rating",
          "ratingValue": "4",
          "bestRating": "5"
        },
        "author": {
          "@type": "Person",
          "name": "Fred Benson"
        }
      },
      "aggregateRating": {
        "@type": "AggregateRating",
        "ratingValue": "4.4",
        "reviewCount": "89"
      },
      "offers": {
        "@type": "Offer",
        "url": "https://example.com/anvil",
        "priceCurrency": "USD",
        "price": "119.99",
        "priceValidUntil": "2020-11-20",
        "itemCondition": "https://schema.org/UsedCondition",
        "availability": "https://schema.org/InStock"
      }
    }
  `
    }
  }
  return (
    <div>
      <Head>
        <title>My Product</title>
        <meta
          name="description"
          content="Super product with free shipping."
          key="desc"
        />
        <script
          type="application/ld+json"
          dangerouslySetInnerHTML={addProductJsonLd()}
          key="product-jsonld"
        />
      </Head>
      <h1>My Product</h1>
      <p>Super product for sale.</p>
    </div>
  )
}

export default ProductPage

여기에서 데이터는 string으로 하드코딩되었지만 addProductJsonLd으로 데이터를 전달하여 동적으로 만들 수 있다.

On Page SEO

페이지의 전체적인 구조를 나타내는 헤딩과 링크들을 말한다. 헤딩은 문서의 중요성을 나타내고 링크는 웹을 연결시킨다.

Headings and H1

헤딩은 사용자가 페이지 구조를 이해하는 것을 도와주고 다음 문단에 어떤 내용이 올 지 이해하게 해준다. 또 검색엔진이 페이지의 어떤 부분이 중요한지 이해하는 것을 돕는다.

Headings는 1-6까지 있고 1에 가까울 수록 중요하다. 각각의 페이지에 H1 heading tag를 사용하는 것이 권장 된다. H1은 페이지가 어떤 내용인지 나타내고 title태그와 비슷하다.

function Page() {
  return <h1>Your Main Page Heading</h1>
}

인터넷은 링크들로 연결되어있다. 웹사이트들 사이의 링크 없이는 인터넷은 존재하지 않았을 것이다. 웹사이트들 중 더 많은 링크를 받은 것은 사용자에게 더 신뢰된 것으로 여겨진다.

구글은 이 원칙을 PageRank Algorithm에서 개발했다.

PageRank 알고리즘은 높은 수준에서 데이터베이스의 모든 링크를 방문하며 어떤 도메인으로부터 얼마나 많은 링크를 받았는지 도메인들의 점수를 채점한다. 스팸 웹사이트들에게 받은 많은 링크들은 적은 값을 가지거나 값을 가지지 않는다.

큰 전국적 언론 사이트의 링크라면 검색엔진이 그 값을 크게 본다. 그렇기 때문에 링크가 중요하고 내외부적으로 페이지들 간 링크를 포함시켜야한다. PageRank 계산을 위해 Links는 언제나 href를 사용해야 한다.

Next.js는 Routes의 클라이언트 사이드 전환을 허용하는 Link 컴포넌트를 제공한다. 예시는 아래와 같다.

function NavLink({ href, name }) {
  return (
    <Link href={href}>
      <a>{name}</a>
    </Link>
  )
}

export default NavLink

a 태그와 연결을 올바르게 하기 위해 href를 추가한다. 이렇게하면 구글이 페이지를 크롤링 할 때 JavaScript에 의존하지 않고 링크를 크롤링하고 팔로우 하게 된다.

하지만 만약 Link의 자녀 컴포넌트가 a 태그를 감싸면 Link 에 passHref를 꼭 추가해야한다. styled-components 같은 라이브러리를 사용한다면 이것을 해주어야 한다. 이것이 없으면 a 태그는 href를 가지지 못하고 사이트 SEO에 영향을 미친다.

passHref 예시

import Link from 'next/link'
import styled from 'styled-components'

// This creates a custom component that wraps an <a> tag
const RedLink = styled.a`
  color: red;
`

function NavLink({ href, name }) {
  // Must add passHref to Link
  return (
    <Link href={href} passHref>
      <RedLink>{name}</RedLink>
    </Link>
  )
}

export default NavLink

ESLint를 사용하는 경우 Next.js는 잘 못 사용되는 것을 방지하기 위한 규칙이 있다.

profile
Deprecated

0개의 댓글