2023. 5. 1. 09:41ㆍNext.js/SEO
1. AMP(Accelerated Mobile Pages)
AMP란 Accelerated Mobil Pages의 약자로 구글에서 시작한 프로젝트이다. 말 그대로 모바일전용 빠른 웹페이지인건데, 구글은 2016년부터 AMP를 사용하는 웹페이지에 더 높은 검색 순위를 부여하기 시작했고, 이 때문에 개발자들은 AMP 구축 및 유지 보수 비용이 발생했었다.
그러나 코어 웹 바이탈Core Web Vitals 업데이트와 함께 구글은 AMP를 검색 캐러셀의 필수 요건에서 제외시켰다. 덕분에 AMP때문에 골머리를 앓게 될 필요성이 줄어들었고, Next.js가 AMP를 지원하고는 있지만 코어 웹 바이탈점수가 높다면 굳이 집착할 필요도 없어졌다. (웹 바이탈에 대해선 나중에 더 자세히 다룸. 지금은 그냥 페이지 성능 측정 도구정도로만 알고 넘어가자)
이 얘기를 굳이 하는 이유는 Next.js를 쓰면 더 나은 개발자 경험을 바탕으로 AMP구현이 가능하고, SEO 측면에서 좀더 나을 수는 있으나, 투자되는 비용과 시간을 고려하면 현 시점에선 너무 AMP에 애쓸 필요가 없다는 얘기를 하기 위함이다. 그러니까 '아 그냥 그런게 있구나' 하고 넘어가도 좋다.
2. URL 구조
URL 구조는 SEO 전략에서 중요한 부분이다. 물론 구글이 SEO 측면에서 각 요소별 가중치같은걸 공개한 바는 없지만, URL이 검색 순위 요소의 얼마나 큰 부분이 됐건 중요하다는 사실에는 변함이 없다.
2.1 URL 원칙
아래 사항들은 URL을 만들때 따라야할 몇 가지 원칙들이니 최대한 준수하길 권장한다.
1) 시맨틱(Semantic)
URL 정의 시 ID나 임의의 숫자 대신 명확한 의미를 가지는 단어를 사용할 것
/learn/course-1/lesson-1 (X)
/learn/basics/create-nextjs-app (O)
2) 논리적이고 일관성있는 패턴
URL은 페이지 간에 일관된 패턴으로 구성되어야 한다. 예를 들어, 제품판매 URL을 구성한다면 각 제품마다 다른 경로를 쓸게 아니라 모든 제품 페이지를 그룹화하는 폴더같은걸 만들어서 구성하는게 좋다
3) 키워드 중심
구글 시스템은 여전히 상당부분을 웹사이트에 포함된 키워드에 기반하고 있다. 따라서 페이지의 목적을 쉽게 이해할 수 있게끔 URL을 키워드 중심으로 구성하는 것이 좋다.
4) 파라미터에 기반하지 않은
매개변수를 써서 URL을 구성하는 방식은 피하는게 좋다. 대부분의 경우 시맨틱하지 않으며, 검색 엔진이 URL을 혼동하여 검색 순위를 낮출 수 있기 때문.
2.2 Next.js 라우트 정의
튜토리얼에서 언급한 바 있지만 Next.js는 pages라는 개념하에 파일 시스템 기반의 라우팅을 사용하고 있다. 즉, pages 디렉토리 안에 파일을 추가하면 route로써 자동으로 사용이 가능하다. '이게 되나' 싶을수도 있는데, 파일 시스템 만으로도 대부분의 URL 패턴을 커버할 수 있기 때문에 너무 걱정할 필요는 없다.
common | Next.js | |
Homepage | https://www.sample.com | pages/index.js |
Listings | https://www.sample.com/products | pages/products.js pages/products/index.js 둘 다 가능 |
Detail | https://www.sample.com/products/product | pages/products/product.js |
또한, 동적 라우팅을 사용해 제품ID나 블로그 이름을 URL의 슬러그로 사용할 수 있다. 단, 동적 라우팅을 사용할 땐 대괄호 [ ]를 사용한다는 점을 잊지 말자.
common | Next.js | |
Product | https://www.sample.com/products/shirt | pages/products/[product].js |
Blog | https://www.sample/com/blog/seo-in-nextjs | pages/blog/[blog-name].js |
아래는 각각 SSG와 SSR의 라우팅 예시이다.
SSG
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/${params.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
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;
웹사이트를 만들다보면 특정 영역에 대해선 크롤링 및 색인화를 차단하고 싶을 수 있다. 예를 들어 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. 메타 데이터
메타 데이터는 웹사이트 콘텐츠의 요약본으로, 사이트에 제목, 설명, 이미지 등을 첨부하는데 사용된다.
3.1 타이틀
타이틀 태그는 SEO에서 가장 중요한 요소중 하나로 꼽힌다. 그 이유는
- 사용자가 웹사이트 접속을 위해 클릭했을 때 표시되는 내용이고
- 구글이 페이지를 파악하기위해 사용하는 메인 요소중 하나이기 때문이다
<title>iPhone 12 XS Max For Sale in Colorado - Big Discounts | Apple</title>
타이틀에 키워드를 사용하면 검색 엔진에서 순위가 높아지므로 키워드 사용을 권장한다
3.2 디스크립션
타이틀 만큼은 아니지만 SEO에서 중요한 요소로 꼽히는 것중 하나이다. 구글측 설명에 따르면 디스크립션 태그가 순위 결정에 고려되진 않지만, 검색 결과의 클릭률엔 영향을 미칠 수 있다고 한다.
따라서 사이트의 타이틀을 보완하는 용도로 사용하길 권장한다. 특히, 제목에는 미처 다 나타내지 못한 키워드들이 있다면 디스크립션 태그를 활용하길 바란다. 이러한 키워드는 향후 사용자의 검색 결과에 포함됐을 때 굵은 글씨로 표시되기 때문이다.
<meta
name="description"
content="Check out iPhone 12 XR Pro and iPhone 12 Pro Max. Visit your local store and for expert advice."
/>
Next.js에선 사이틀과 디스크립션 태그 모두 Head 컴포넌트 안에서 작성하면 된다. Head 컴포넌트는 어느 페이지건 껴넣을 수 있으니 타이틀, 디스크립션을 적극 활용해 페이지의 콘텐츠를 나타내보자.
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;
3.3 Open Graph
페이스북에서 개발한 오픈 그래프 프로토콜은 웹 페이지에서 메타데이터가 사용되는 방식을 표준화한 것이다. 오픈 그래프 태그로 제공할 정보량은 개발자 마음대로긴 하지만, 어찌됐든 제공된 모든 오픈 그래프 조각은 서로 맞물려 사이트를 표현하는데 쓰인다.
Pinterest, 트위터, Linkedin같은 타 소셜 미디어 회사들도 URL 공유시 리치 카드를 표현하기위해 오픈 그래프를 사용한다. 예를 들어, 트위터의 경우 트위터 카드 태그가 있다. (리치카드란 2016년 구글이 도입한 새로운 검색 결과 형식으로, 캐러셀을 활용해 시각적으로 콘텐츠를 나타내는 방식이다)
사실 오픈 그래프 태그가 SEO 검색 엔진 순위에 도움이 되는건 아니지만, 사람들이 소셜 미디어나 텔레그램같은 프라이빗 메시징 툴에서 콘텐츠 공유시 요긴하게 쓰이므로 적극 활용할 것을 권장한다.
오픈 그래프 태그를 추가하는 방법은 meta태그의 property 속성을 아래와 같이 설정해주면 된다.
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;
3.4 Structured Data and JSON-LD
이게 뭔소리인고
4. On Page SEO
On Page SEO(온페이지 SEO)라는 말이 되게 어색한데, 간단히 말하자면 페이지의 전체 구조를 구성하는 제목(heading)과 링크를 의미한다. 제목은 곧 문서에서의 중요도를 나타내며, 링크는 웹을 연결하는 역할을 한다.
4.1 제목(Heading)과 h1태그
제목은 유저가 페이지의 구조를 이해하기 쉽게 하고, 다음 문단에 이어질 내용을 파악할 수 있게끔 해준다. 뿐만 아니라 검색 엔진이 페이지의 어떤 부분이 가장 중요한지를 파악하게끔 돕는 역할도 한다.
HTML에선 제목을 나타내는 태그로 <h1> ~ <h6> 총 6단계가 있으며, 숫자가 낮아질수록 더 중요도(혹은 단계)가 높아진다. h1 태그는 메타 태그로 따지면 title태그와 비슷한 역할이므로 페이지당 한번만 사용할 것을 권장하고 있다. 아래는 MDN 공식문서에서 따온 h 태그 사용법이다.
Usage Notes
- 사용자 에이전트가 제목의 정보를 사용해 자동으로 문서 콘텐츠의 표를 만드는 등의 작업을 수행할 수 있습니다.
- 글씨 크기를 위해 제목 태그를 사용하지 마세요. 대신 CSS의 font-size 속성을 사용하세요.
- 제목의 단계(레벨)를 건너뛰는 것을 피하세요. 언제나 <h1>로 시작해서, <h2>, 순차적으로 기입하세요.
- 페이지 당 하나의 <h1>만 사용하세요. 여러 개를 써도 오류는 나지 않겠지만, 단일 <h1>이 모범 사례로 꼽힙니다. 논리적으로 생각했을 때도, <h1>은 가장 중요한 제목이므로 전체 페이지의 목적을 설명해야 할 것입니다. 두 개의 제목을 가진 책이나, 여러 개의 이름을 가진 영화는 볼 수 없죠! 또한 스크린 리더 사용자와 SEO에도 더 적합합니다.
4.2 내부 링크
인터넷은 링크로 연결되며, 한 웹 사이트에서 다른 웹 사이로의 연결이 없었다면 아마 지금의 인터넷이란건 존재하지 않았을 것이다. 또한, 보다 많은 링크를 받는 웹 사이트일수록 유저가 더 신뢰하는 웹 사이트이기 마련이다.
당연해보이는 소리지만 구글은 페이지 순위 알고리즘PageRank algorithm에 이 원칙을 적용했다. 이 알고리즘을 간략히 설명하자면 데이터베이스의 모든 링크를 살펴보고
1) 어떤 페이지가 얼마나 많은 링크를 수신하는지(quantity),
2) 또 어떤 도메인으로부터 링크를 받는지(quality)
를 조사해 점수를 매기는 알고리즘이다. 만약 어떤 페이지가 스팸 웹사이트로부터 링크가 많이 걸려있다면 이 알고리즘은 해당 페이지를 가치가 전혀 없는 것으로 판단할 것이다. 반면 대형 언론사 웹사이트같은 곳에서 링크를 수신하는 페이지라면 매우 가치있는 페이지로 판단할 것이다.
어찌됐든 링크는 결국 온페이지 SEO 관점에서 아주 중요하다고 할 수 있으며, 페이지를 만들때 내/외부적으로 적절히 링크를 포함시켜야하는 이유가 된다. 단, 링크 사용시 href를 활용해야 페이지순위 알고리즘에 포착될 수 있다.
4.3 next/link
Next.js에선 Link 컴포넌트를 제공하므로 아래와 같이 간단하게 사용할 수 있다.
function NavLink({ href, name }) {
return (
<Link href={href}>
<a>{name}</a>
</Link>
);
}
export default NavLink;
그런데 만약 Link 컴포넌트의 자식으로 <a>태그를 래핑하는 커스텀 컴포넌트 추가시 passHref라는 속성을 명시해줘야 한다. 이런 경우는 보통 styled-components같은 라이브러리 사용시 필요하며, passHref를 명시하지 않으면 SEO에 악영향을 미친다. 그 이유는 검색 엔진은 기본적으로 HTML 요소를 탐색하는데, 스타일드 컴포넌트는 엄밀히 말하면 JS이므로 포착되지 않기 때문이다.
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 > SEO' 카테고리의 다른 글
[SEO] 크롤링과 인덱싱(색인) (0) | 2023.04.30 |
---|---|
[SEO] Introduction (0) | 2023.04.30 |