저희 서비스의 SEO를 개선시키기위해 논의를 하였는데요, 이와 더불어 React-Helmet이란 라이브러리를 인지하게 되었습니다.
리액트 라이브러리인 React-Helmet을 이용하여 meta태그를 주입할 수 있다는 것을 알았는데요, 이를 이용하여 SEO를 개선할 수 있는 작업에 대해서 살펴보고, 우리 서비스는 어떤 선택을 할 것인지에 대해서 이야기 해보려 합니다.
SPA 서비스의 경우 index.html파일이 하나이기 때문에, 라우트 별로 meta태그를 설정하는 것이 까다롭습니다.
SPA의 경우 다음과 같은 특징을 가지는데요,
- SPA의 특성상 중요한 컨텐츠가 자바스크립트 로딩 후에 나타난다.
- SPA는 URL이 동적으로 변경되기 때문에 크롤러가 URL변화를 완벽히 감지하지 못한다.
- 구글 크롤러의 경우, 자바스크립트를 실행할 수 있기 때문에 SPA라고 할지라도 어느정도 정보를 감지할 수 있지만 여전히 한계가 존재한다.
React-Helmet
Helmet은 단순한 HTML 태그를 받아서, 단순한 HTML 태그를 출력합니다. 매우 간단하며, 리액트를 처음 접하는 사용자에게도 친숙한 라이브러리입니다.
React-Helmet을 다음과 같이 소개합니다. 예시를 통해 살펴보겠습니다.
import { Helmet } from "react-helmet";
// ...
return (
<div className="application">
<Helmet>
<meta charSet="utf-8" />
<title>My Title</title>
<link rel="canonical" href="http://mysite.com/example" />
</Helmet>
...
</div>
);
위와같이 Helmet 컴포넌트를 불러와 HTML태그를 내부 자식 요소로 넣어주면 됩니다.
그렇다면 React-Helmet은 어떤 원리로 작동하는 걸까요?
제 상식으론 SPA에서 크롤링 봇이 감지할 수 있는 meta태그를 넣는다는 것이 잘 와닿지 않았습니다.
그래서 내부 코드를 자세히 들여다 보았습니다.
const updateTags = (type, tags) => {
const headElement = document.head || document.querySelector(TAG_NAMES.HEAD);
// ...
const updateAttributes = (tagName, attributes) => {
const elementTag = document.getElementsByTagName(tagName)[0];
// ...
위 와같은 메서드 이외에도 직접 DOM트리의 head에 접근하여 이를 수정하는 코드를 확인할 수 있었습니다.
결론. SSR 프레임워크 사용하기.
React-Helmet을 사용하더라도, 결국 DOM에 직접 접근하여 head태그를 사용하는 것이므로 SEO 향상에는 크게 도움이 되지 않을 것 같습니다.
기본적으로 구글 크롤러 기준으로 자바스크립트를 실행하기 때문에, 어느정도는 효과가 있을 것이라 생각되지만 근본적인 해결책이 되지 않는다고 생각합니다.
따라서 근본적으로 SEO를 개선하기 위해선 SSR환경이 필요합니다.
기존 몇몇 페이지를 정적으로 제공할 방법을 찾고 있었고, SEO를 개선할 수 있음에 우리는 Next.js로의 마이그레이션을 결정하게 되었습니다.
간단하게 Next.js에서 SEO설정하는 방법 톺아보기
(App Router를 기준으로 합니다)
정적 metadata
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: '...',
description: '...',
}
export default function Page() {}
해당 컴포넌트에서 metadata 객체를 만들어 export하면 설정이 가능합니다.
동적 metadata
import type { Metadata, ResolvingMetadata } from 'next'
type Props = {
params: { id: string }
searchParams: { [key: string]: string | string[] | undefined }
}
export async function generateMetadata(
{ params, searchParams }: Props,
parent: ResolvingMetadata
): Promise<Metadata> {
// read route params
const id = params.id
// fetch data
const product = await fetch(`https://.../${id}`).then((res) => res.json())
// optionally access and extend (rather than replace) parent metadata
const previousImages = (await parent).openGraph?.images || []
return {
title: product.title,
openGraph: {
images: ['/some-specific-page-image.jpg', ...previousImages],
},
}
}
export default function Page({ params, searchParams }: Props) {}
generateMetadata메서드를 export해주는 것으로 설정가능 합니다.
이 두가지 설정은 Server Component에서만 설정 가능하다는 것! CSR에서는 설정하는 것이 무용지물이겠죠? (React-helmet과 같은 이유)
'Front End > React' 카테고리의 다른 글
[React] Drag & Drop (DnD) 직접 구현하기 (2). Mouse Event를 사용하여 구현하기 (0) | 2024.09.17 |
---|---|
[React] 이미지 로딩, 렌더 사이 깜박임 현상 해결하기 (0) | 2024.09.05 |
[React-query] refetch와 invalidate의 차이 (0) | 2024.06.07 |
[React] Context를 알아보자. (0) | 2024.04.30 |
React에서의 MSW 기본 사용법 (0) | 2024.04.14 |