2023. 4. 30. 22:50ㆍNext.js/SEO
이전 장에서 검색 시스템과 구글봇의 동작방식에 대해 간단히 알아봤는데, 이번엔 크롤링과 인덱싱에 영향을 미치는 요인들에 대해 알아보자.
1. HTTP 상태 코드
HTTP 응답 상태 코드는 HTTP 요청이 성공적으로 완료되었는지 여부를 나타낸다. 코드값은 RFC 9110에서 정의된 표준 코드를 사용한다. 상태 코드는 굉장히 많지만 SEO 관점에서 중요한 것은 소수이므로 차근차근 살펴보자.
200 - OK
요청이 성공했음을 나타낸다. 페이지가 구글에 색인되려면 상태 코드가 반드시 200을 반환해야 한다. Next.js에서도 페이지가 성공적으로 렌더링되면 기본적으로 200을 응답하게끔 설정돼있다.
301/308 - Moved Permanently
301은 영구 리디렉션으로, 요청한 리소스가 다른 URL로 최종 이동되었음을 나타낸다. 가장 널리 사용되는 리디렉션 타입중 하나이며, Next.js에선 영구 리디렉션으로 301대신 308을 쓰고있다.
리디렉션을 쓰는 이유야 여럿 있지만 가장 많은 사유는 URL이 A → B로 변경되었음을 알리고, 봇이 사이트를 계속 색인화할 수 있도록 하기 위해 사용한다. 즉, 검색 엔진은 이 코드를 만나면 리소스에 대한 링크를 업데이트한다. 한편, 브라우저는 리디렉션 응답 코드를 만나면 새 URL로 이동한다.
301과 308의 가장 큰 차이점은 301의 경우 request 메서드를 POST → GET으로 변경 가능하지만 308의 경우엔 그렇지 않다는 점이다.
Next.js에서 308 코드를 내보내는 방법은 다음과 같다.
1) getStaticProps 함수에서 props대신 redirect를 리턴하기
// pages/about.js
export async function getStaticProps(context) {
return {
redirect: {
destination: '/',
permanent: true, // triggers 308
},
};
}
2) next.config.js파일에서 permanent: true 세팅 해주기
//next.config.js
module.exports = {
async redirects() {
return [
{
source: '/about',
destination: '/',
permanent: true, // triggers 308
},
];
},
};
302 - FOUND
요청한 리소스가 일시적으로 다른 URL로 이동되었음 뜻한다. 유저를 특정 페이지로 일시 리디렉션 시키거나(프로모션 행사같은걸 하는 경우 관련 페이지로 이동시킨다거나) 위치를 기반으로 리디렉션하는 경우에 쓰인다. 사실 어지간해선 쓸 일이 없으며, 대부분 302 코드가 더 적절한 경우가 많다.
404 - NOT FOUND
요청한 리소스를 찾을 수 없음을 나타내는 에러 코드이다. 404 코드는 유저가 존재하지 않는 URL로 방문할 경우 보내주기에 적합한 코드이다. 그러나 404 코드가 너무 자주 발생하면 검색 순위가 낮아질 수 있으므로 주의해야 한다.
Next.js에서도 애플리케이션내 정의되지 않은 URL로 접속한 경우 404를 리턴하도록 설정돼있고, 경우에 따라 404 코드를 반환하고 싶다면 아래와 같이 설정해줄 수 있다.
export async function getStaticProps(context) {
return {
notFound: true, // triggers 404
};
}
커스텀 404 페이지를 만드는 것도 가능하므로 관심이 있다면 여기를 참고하자.
410 - GONE
원본 서버에서 요청 리소스에 대한 접근이 더이상 불가하며, 영구적으로 불가할 가능성이 있음을 나타낸다. 어지간해선 보기 힘든 코드이지만 아래 예시처럼 더 이상 필요하지 않은 콘텐츠를 삭제할 때 쓸 수 있다.
- E-Commerce : 인벤토리에서 영구 삭제된 품목
- Forum : 사용자에 의해 삭제된 글
- Blog : 블로그에서 삭제된 게시물
크롤링 관점에선 봇에게 해당 콘텐츠를 다시 크롤링하지 말라고 전달하는 역할을 한다.
500 - Internal Server Error
서버가 예기치 않게 해당 요청을 처리할 수 없음을 나타낸다. Next.js에서도 예기치 않은 에러가 발생하면 500을 리턴한다.
503 - Service Unavailable
서버가 요청을 처리할 준비가 되어있지 않음을 나타낸다. 이 코드는 웹사이트가 다운된 상태로 장기간 지속될 것으로 예상될 때 사용하는게 좋다. 이렇게 하면 장기적으로 검색 순위가 하락하는 일을 방지할 수 있기 때문.
2. robots.txt
웹사이트를 만들다보면 특정 영역에 대해선 크롤링 및 색인화를 차단하고 싶을 수 있다. 예를 들어 CMS나 관리자 페이지, 사용자 계정, 일부 API 경로 등은 보호하고 싶을 수 있는데, 이를 위해 robots.txt 파일을 쓸 수 있다.
robots.txt는 웹 표준 파일로, 검색 엔진의 크롤러에게 어떤 페이지에 request가 가능한지(혹은 불가능한지) 통보하는 역할을 한다.
robots.txt는 각 호스트의 루트에서 제공돼야한다. 혹은 루트 /robots.txt 경로를 대상 URL로써 리디렉션시킬 수 있다. 이렇게 해두면 대부분의 봇은 이를 따라가게 된다.
Next.js 프로젝트에서 robots.txt.를 작성하는 방법은 루트 디렉토리의 public 폴더안에 robots.txt 파일을 만들어주기만 하면 된다. (이게 가능한 이유는 Static File Serving때문)
//robots.txt
# Block all crawlers for /accounts
User-agent: *
Disallow: /accounts
# Allow all crawlers
User-agent: *
Allow: /
시험삼아 개발 서버를 켜서 localhost:3000/robots.txt으로 접속해보자.
3. XML Sitemaps
사이트맵은 사이트의 페이지, 비디오, 기타 다른 파일들과 이들 간의 관계에 대한 정보를 제공하는 파일이다. 구글 같은 검색 엔진은 이 파일을 읽고 사이트를 보다 지능적으로 크롤링하므로 사이트맵은 검색 엔진과 커뮤니케이션하기 가장 좋은 수단이라고 할 수 있다.
또한, 사이트맵은 웹사이트에 속하는 URL들과 업데이트 시기도 나타내는데, 구글은 이를 바탕으로 보다 쉽게 새로운 콘텐츠를 감지하고 더 효율적으로 크롤링할 수 있게 된다.
사이트맵 제작은 XML 형식이 가장 유명하고 많이 쓰이기는 하지만 RSS나 Atom으로도 만들 수 있고, 최대한 간단하게 만들고 싶다면 텍스트 파일로도 가능하다. (구글: 사이트맵 제작 및 제출하기)
3.1 사이트맵이 필요한 경우
구글에 따르면 아래와 같은 경우 사이트맵이 필요할 수 있다
1) 사이트 규모가 매우 큰 경우
사이트 규모가 매우 크면 구글 웹 크롤러가 새로운(혹은 업데이트된) 페이지중 일부를 포착하지 못할 가능성이 있으므로
2) 사이트가 방대한 양의 독립 콘텐츠를 아카이브로 가지고 있는 경우
방대한 콘텐츠들이 각각 독립되어 서로간에 잘 연결이 안되어있는 경우를 말한다. 각 페이지간 상호 참조가 잘 안이뤄진 경우 크롤러가 놓칠 가능성이 있으므로
3) 새롭게 개설한 사이트이면서 외부 링크가 거의 없는 경우
구글봇을 포함한 대부분의 웹 크롤러는 한 페이지에서 다른 페이지로 링크를 타고 이동하며 사이트를 탐색한다. 그런데 새로 개설한 사이트가 외부로의 링크를 가지지 않는다면 크롤러가 이를 인지하지 못할 가능성이 있으므로
4) 사이트에 비디오나 이미지같은 고용량 콘텐츠가 많거나 구글 뉴스에 표시되는 경우
사이트맵을 만들땐 동적인 상태를 유지하게끔 만드는걸 강력히 권장한다. 그 이유는 정적인 사이트맵도 유효하긴 하나 지속적인 검색 목적으로는 사용되지 않아 구글에 덜 유용하기 때문이다.
3.2 Next.js에 사이트맵 추가하기
Next.js에서 사이트맵을 추가할 수 있는 두 가지 방법이 있다.
1) Manual
비교적 간단하고 정적인 사이트라면 public 디렉토리에 sitemap.xml이란 이름으로 손수 만들어줄 수 있다.
<!-- 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>
2) getServerSideProps
그러나 위 방법은 정적인 사이트에 한정된 방법이며, 보통의 사이트는 동적이기 마련이다. 이 경우 getServerSideProps를 활용해 온디맨드 방식으로 XML 사이트맵을 생성할 수 있다.
구체적으로는 pages 디렉토리에 pages/sitemap.xml.js같은 이름으로 페이지를 하나 추가한다. 그 후 API를 호출해 동적 페이지의 URL 데이터를 가져오게끔 만들어주면 된다. 그 후 /sitemap.xml에 대한 응답으로 XML 파일을 작성하는 것이다.
//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;
4. Special Tags
튜토리얼에서 <Head> 컴포넌트 안에 <title>이라는 태그를 이용해 페이지의 메타 데이터를 달아준 적이 있었다. 유저에게 페이지의 정체성(?)을 알리는 역할을 했었는데, 검색 엔진을 대상으로도 이와 유사한 역할을 담당하는 특수 태그들이 존재한다.
4.1 Meta robot tags
메타 로봇 태그Meta robot tags는 검색 엔진이 항상 참조하고 따르는 지시문이다. 로봇 태그를 추가하면 검색 엔진 입장에선 웹사이트의 색인 생성이 더 쉬워지기 때문에 SEO 측면에서 보다 유리하다.
여기서 '지시문directives' 이라는 말을 썼는데, 이 말인 즉슨 항상 준수되어야 한다는 뜻으로 제안suggestions과는 구분된다. 예를 들어, 후술할 캐노니컬 태그Canonical tags의 경우엔 '제안'으로써 구글이 해당 문을 따를수도 있고 따르지 않을 수도 있다.
페이지 레벨의 메타 태그엔 수많은 옵션들이 있지만 이를 모두 다루는건 현실적으로 어렵고, 여기선 SEO와 관련돼서 흔히 쓰이는 noindex, nofollow옵션에 대해 알아보자. (자세한건 여기 참고)
<meta name="robots" content="noindex,nofollow" />
noindex
한글 그대로 직역하면 '색인없음'이 되는데, 검색 결과에서 특정 페이지를 제외시키기 위해 사용한다. noindex를 생략하면 페이지는 검색 엔진에 노출 및 색인될 수 있다. 주로 설정이나 내부 검색 페이지, 정책 페이지 등을 검색 엔진으로부터 숨기기 위해 사용한다.
nofollow
페이지의 링크를 따라 이동할 수 없게 만든다. nofollow를 생략하면 페이지의 링크를 타고 이동 및 크롤링할 수 있다. 재밌는건 X페이지와 Y페이지에 A라는 링크가 게재돼있다고 쳐보자. X페이지에선 nofollow 설정을 해두고, Y페이지에선 nofollow 해제를 해두면 봇은 Y페이지에 노출된 A 링크를 따라 크롤링을 수행한다.
4.2 Googlebot tag
대부분의 경우 robots태그가 훨씬 많이 쓰이지만 가끔씩 googlebot이라는 태그를 쓰는 경우도 있다. 말 그대로 구글만을 대상으로 하는 태그로, 구글봇에게 별도의 룰을 적용시키기 위해 쓰인다. 만약 상호 충돌하는 내용의 태그가 혼재한다면 더 제한적인 태그 규칙을 적용한다.
<meta name="googlebot" content="noindex,nofollow" />
그런데 이제와서 드는 의문이지만, 굳이 메타 태그를 써야할 필요성이 있을까? 생각해보면 robots.txt에서도 원하는 URL이나 콘텐츠들을 색인불가로 만들 수 있는데 말이다.
사실 메타 태그를 쓰는 이유는 유연성에 있는데, 필요에 따라 페이지별로 noindex같은 설정이 가능하기 때문이다. 또한, 이미 색인된 페이지에 대해서 나중에 색인불가를 지정하고 싶어진다면 이 때에도 noindex태그를 활용할 수 있다.
NOTE
매우 드문 케이스이긴 한데 구글은 크롤링을 하지 않고도 페이지를 인덱싱하기도 한다. 이 경우는 어떤 페이지가 1)유저가 원하는 내용을 담고있으면서 2)특정 검색 결과를 충족하는 페이지라고 100% 확신하는 경우 발생한다.
4.3 Google tags
nositelinkssearchbox
<meta name="google" content="nositelinkssearchbox" />
유저가 구글에서 사이트를 검색할 때 가끔씩 페이지내 검색(search box) 기능이 표시되는 경우가 있는데, 이 태그를 쓰면 해당 검색 상자를 표시하지 않을 수 있다.
notranslate
<meta name="google" content="notranslate" />
구글은 페이지가 방문자네 언어가 아닐 경우 검색 결과에 번역 링크를 제공하는 경우가 많다. 보통은 이덕분에 더 많은 사용자들에게 페이지를 노출시킬 수 있지만, 경우에 따라선 원치 않는 상황일수도 있다. 이 태그를 쓰면 구글에게 번역을 제공하지 않도록 할 수 있다.
4.4 종합 예시
아래는 흔히 쓰일 수 있는 태그를 종합해본 예시이다. 코드를 보면 알겠지만 next/head 모듈의 Head컴포넌트를 사용하며, 중복 방지를 위해 meta태그에 key값을 달아 한 번만 렌더링되도록 명시할 수도 있다.
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;
5. Canonical Tags
캐노니컬이란 '표준'을 뜻한다. 즉, 캐노니컬 URL이란 표준 URL로서 사이트내 중복되는 페이지가 여럿 있을때, 가장 대표성을 가지는 페이지 의 URL을 뜻한다. 예를 들어 아래와 같은 주소가 있고 모두 같은 페이지로 접속된다고 할 때, 과연 어떤 주소가 가장 대표성을 가진다고 할 수 있을까?
www.somewhere.com/products/phone
somewhere.com/phone
m.somewhere.com/phone
이 문제에 대해선 사람도 판단이 잘 안서는데, 검색 엔진 입장에선 더욱 어려울 수 있다. 검색 엔진은 이 문제를 해결하기 위해
1) 어떤 URL이 표준인지 나름의 기준으로 자의적 판단을 하거나
2) 페이지에 어떤 URL이 표준인지 명기된 사항을 따르는 조치를 취하고 있다.
그깟 URL 몇개 좀 중복되는게 뭐그리 큰일이냐고 생각할 수 있지만, 구글은 동일 콘텐츠를 포함하는 복수의 URL 발견 시 검색 결과에서 순위를 낮춰버릴 수도 있다.
이 현상은 두 개의 서로 다른 웹사이트를 운영하면서 각 사이트에 동일한 콘텐츠를 게시하는 경우에도 발생한다. 즉, 검색 엔진은 이중 하나만을 선택해 순위를 매기거나 두 웹사이트의 순위를 강등시켜버릴 수도 있다.
이러한 문제를 피하고 싶다면 캐노니컬 태그를 써서 구글에게 어떤 URL이 오리지널인지, 어떤 URL들이 중복될 수 있는지 명시해줄 수 있다. 때문에 SEO 측면에서 캐노니컬 태그는 매우 중요하며, 경우에 따라선 사용자 혹은 마케팅 도구로도 써먹을 수 있다.
예를 들어, 구글에다 어떤 마케팅 캠페인을 진행하고 있다고 생각해보자. 여기다가 UTM 파라미터를 갖다붙일 수도 있는데, 이 새롭고 고유한 URL이 구글봇에 의해 색인화되게끔 할 수 있다.
※ UTM 파라미터
트래픽 데이터의 추적 및 분석을 도와주는 파라미터이다. 즉, 페이지로 유입된 트래픽이 어디서 어떻게 들어온 것인지, 어떤 키워드를 검색하고 어떤 광고 소재를 클릭하고 들어온 것인지 분석하게끔 도와준다.
UTM이란 간단히 보면 그냥 URL 링크인데, 웹페이지 주소 뒤에 추적을 위한 코드를 줄줄이 갖다붙인 URL이라고 할 수 있다. 예를 들어, 광고를 하나 집행할 때 트래픽을 만들고 끌어오고자 하는 웹페이지의 원주소 대신 해당 웹페이지 주소에 추적 코드가 달린 UTM 파라미터 링크를 사용할 수 있다. 이 상태에서 고객이 해당 링크를 클릭하면 GA는 해당 트래픽을 수집하고 분석해주는 것.
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;
'Next.js > SEO' 카테고리의 다른 글
[SEO] 렌더링과 랭킹 (0) | 2023.05.01 |
---|---|
[SEO] Introduction (0) | 2023.04.30 |