SPA 개발을 하다보면 SEO에 대한 피드백을 한번씩 받기 마련이다.
이는 아무래도 클라이언트에서 자바스크립트로 모든 DOM요소를 완성해버리는 SPA특성상 검색 엔진 봇이 사이트에 처음 진입했을때 빈 html을 보게 되기 때문이다.
그리고 검색 엔진 봇은 이렇게 빈 html나 메타 정보를 제공하지 않는 페이지들을 싫어하고 SEO점수를 낮춰버리게 된다.
문제는 단순히 점수가 낮아지는게 문제가 아니라 이게 웹사이트 수익성에 악영향을 미친다는 것이다.
클라이언트 사이드 렌더링(CSR)의 단점
CSR의 치명적인 단점으로 항상 거론되는 두가지가 첫째로, 페이지 로딩 속도가 다소 느릴 수 있다는 것, 둘째로, 구글 검색 노출이 어려운 편일 수 있다는 것이다. 물론 요즘 구글 봇은 SPA 사이트일지라도 알아서 잘 크롤링한다고 어디서 주워들은거 같긴한데, 그렇다고 근본적으로 해결이 됬다고 보기는 힘들 것 같다.
반면, 서버사이드 렌더링(SSR)을 도입하게 되면 html을 서버에서 완성해서 브라우저에 내려주기 때문에 SPA 사이트에 비해 자바스크립트를 따로 돌려보지 않아도 검색 엔진 봇이 사이트의 html을 더 많이 조회할 수 있기 때문에 구글 검색 노출이 SPA에 비해 상대적으로 쉬워질 수 있고, 초기 페이지 로딩 속도도 개선될 수 있다. 요즈음 프론트엔드 시장에서 Next.js를 핵심 기술 스택으로 밀고 가는 회사들이 많아진 것도 이런 SSR의 장점을 차용하고 CSR의 단점을 보완하기 위한 맥락이라고 생각한다.
Next.js 못하면?
Next.js를 아직 경험해보지 못했거나, 도입하기 어려운 경우가 있을 수 있다. 예를 들어, 최근 회사든 사이드 프로젝트이든, Next.js에 대한 사용경험이 없거나, 서버에 대한 이해도가 없는 경우에는 도입하는게 쉽지 않을 수 있다. 그러한 맥락에서 SPA로 개발하더라도 SEO 최적화를 챙길 수 있는 방법이 있는데, Sitemap.xml, robots.txt 생성, 동적태그 삽입등이 있다.
robots.txt
일단 robots.txt, sitemap.xml, 두가지 파일 모두 따로 생성해주지 않으면 기본적으로는 없는 파일이다.
우선, robots.txt는 구글 봇과 같은 웹 크롤러에게 특정 웹 페이지나 디렉토리를 크롤링 허용/차단할 지 알려주는 규칙을 정의하는 파일이다. 예를 들어 다음과 같은 속성을 정의할 수 있다.
User-agent: *
Disallow: /admin/
Disallow: /private/
Allow: /public/
User-agent는 사용자에 대한 분류이고, Disallow는 말 그대로 페이지에서 봇에게 크롤링을 허가하지 않을 페이지, Allow는 허락하는 페이지를 정의하는 필드라고 볼 수 있다.
이러한 파일의 목적은 기본적으로 특정, 페이지, 이미지, 또는 디렉토리를 크롤러가 접근하지 않도록 제한하고, 검색 엔진 크롤러에게 중요한 컨텐츠만 탐색하도록 유도하는 것이라고 볼 수 있다.
Sitemap.xml
다음으로 Sitemap.xml은 이름에서 느껴지듯이 웹사이트의 페이지 정보를 나열하여 검색 엔진이 웹사이트 구조를 보다 효율적으로 크롤링할 수 있도록 돕는 파일이다. 다음과 같은 설정을 정의할 수 있다.
- <loc>: 페이지 URL.
- <lastmod>: 마지막 수정 날짜.
- <changefreq>: 콘텐츠 변경 빈도 (예: daily, weekly).
- <priority>: 페이지의 우선순위 (0.0 ~ 1.0).
// 사이트맵 경로
const routes = [
{ loc: 'https://quizy-fe.vercel.app/', priority: 1.0 },
{ loc: 'https://quizy-fe.vercel.app/main', priority: 0.9 },
{ loc: 'https://quizy-fe.vercel.app/search', priority: 0.8 },
{ loc: 'https://quizy-fe.vercel.app/game', priority: 0.8 },
];
// sitemap.xml 생성
const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${routes
.map(
(route) => `
<url>
<loc>${route.loc}</loc>
<lastmod>${new Date().toISOString()}</lastmod>
<priority>${route.priority}</priority>
</url>
`,
)
.join('')}
</urlset>`;
결과적으로 robots.txt와 sitemap.xml 파일을 서빙하는 사이트는 검색 엔진에게 친화적이라고 볼 수 있다.
검색 엔진을 사람으로 비유하면 처음 놀러온 여행지에서 길을 헤매고 있는 나에게 차근차근 길을 알려주는 사람과 그렇지 않은 사람, 둘 중에 어떤 사람한테 더 높은 점수를 줄지는 뻔하다.
동적 태그
마지막으로 SEO 점수를 높일려면 각 페이지마다 메타 태그나 페이지 제목 등을 넣어주는 것이 좋다. html이 결국 문서인것을 생각해보면 제목과 본문 내용에 대한 설명이 있는 글과, 제목없이 본문만 있는 글이라면 당연히 제목이나 설명이 붙어있는 글이 더 정리되어 보이는 것과 같은 맥락이라고 볼 수 있다.
리액트에서는 이를 위해 React-helmet이라는 라이브러리가 있는데, 이는 자바스크립트로 동적으로 메타 태그나 페이지 제목 등을 각 페이지에 주입해주기 위한 라이브러리이다.
import { Helmet } from "react-helmet";
function MyPage() {
return (
<div>
<Helmet>
<title>My Page Title</title>
<meta name="description" content="This is my page description." />
<meta property="og:title" content="My Page Title" />
<meta property="og:description" content="This is my page description." />
</Helmet>
<h1>Welcome to My Page</h1>
</div>
);
}
export default MyPage;
다음과 같이 각 페이지에 맞는 meta 태그(description, keywords 등)을 동적으로 추가해줌으로써 검색 엔진에게 페이지 정보를 제대로 인식하도록 돕는 것이다.
하지만, 이 방식의 단점은 결국 React-helemt도 자바스크립트로 돌아가는 라이브러리이기 때문에 CSR만 사용하는 경우 메타 태그가 늦게 주입되어 검색 엔진이 이를 제때 인식하지 못할 수 있다. 물론 요즘 봇들은 잘 한다고 하지만, 완벽한 최적화라고 볼 수는 없는 것이다.
결과
최근 프로젝트에서 검색 엔진 최적화를 시도하며 앞서 언급한 방법들을 사용해보았다.
결과적으로, lighthouse기준으로는 SPA사이트임에도 불구하고 많이 개선되는 것을 확인했다. (80점 -> 100점)
'프론트엔드' 카테고리의 다른 글
[프론트엔드] localhost에 https 적용하기 (0) | 2025.05.03 |
---|---|
[프론트엔드] Zustand vs Jotai (0) | 2025.02.26 |
[프론트엔드] dvh (CSS 단위) (0) | 2024.11.22 |
[프론트엔드] SSR/CSR 환경별 쿠키 관리 방법 (0) | 2024.11.04 |
[프론트엔드] 브라우저 렌더링 과정, SSR과 CSR의 개발 환경 차이를 잘 알아야 하는 이유 (feat. Rollup) (2) | 2024.10.09 |