위 블로그 내용을 공부하고 프로젝트에 적용해야해서 번역본 정리해둔다
JavaScript SEO는 검색 엔진이 웹 애플리케이션을 제대로 찾아내도록 하면서 풍부한 사용자 경험을 제공하기 위해 필수적입니다.
JavaScript 프레임워크가 제공하는 동적 기능은 매우 유용하지만, 검색 엔진이 JS 콘텐츠를 제대로 해석하지 못하면 웹사이트가 검색 결과에서 노출되지 않을 수 있습니다. 구글과 같은 검색 엔진은 어느 정도 JavaScript를 실행할 수 있지만, 그 기능만 전적으로 믿고 방치하는 것은 위험합니다.
결국, JavaScript를 활용하여 최적의 사용자 경험을 제공하되, 동시에 검색 엔진 친화적인 웹사이트를 유지하는 것이 중요합니다. 다음은 모든 개발자가 알아두면 좋을 10가지 JavaScript SEO 팁으로, 각 항목마다 코드 예시와 실전 가이드를 함께 제공합니다.
1. 서버 사이드 렌더링(SSR)과 정적 렌더링
JavaScript로 구현된 웹사이트는 검색 엔진이 클라이언트 사이드 JavaScript를 제대로 실행하지 못해 인덱싱이 어려워질 수 있습니다. 특히 콘텐츠가 클라이언트 사이드 JavaScript에 크게 의존하는 경우, 크롤러가 최종 렌더링된 페이지를 제대로 보지 못해 인덱싱이 누락되거나 잘못될 수 있습니다.
이 문제를 해결하려면 서버 사이드 렌더링(SSR) 또는 정적 렌더링을 통해 콘텐츠를 사전에 렌더링하면 좋습니다.
- 서버 사이드 렌더링(SSR): 서버에서 미리 HTML을 렌더링한 뒤 클라이언트로 전달
- 정적 렌더링: 빌드 시점에 HTML을 생성하여 정적으로 호스팅
이렇게 하면 검색 엔진은 클라이언트 사이드 JavaScript를 실행할 필요 없이, 이미 완성된 HTML 콘텐츠를 바로 볼 수 있어 인덱싱이 원활해집니다.
Next.js 예시 코드
// pages/index.js
import React from 'react';
const Home = ({ data }) => (
<div>
<h1>{data.title}</h1>
<p>{data.description}</p>
</div>
);
export async function getServerSideProps() {
// Fetch data at runtime
const res = await fetch('https://api.example.com/data');
const data = await res.json();
return { props: { data } };
}
export default Home;
위 예시에서 Next.js는 서버에서 데이터를 받아와 미리 페이지를 렌더링합니다. 이렇게 하면 검색 엔진이 페이지의 완성된 HTML을 곧바로 확인할 수 있어, 콘텐츠가 풍부한 웹사이트의 SEO가 크게 개선됩니다.
2. rel="canonical"을 사용해 중복 콘텐츠 문제 방지
JavaScript 프레임워크에서는 여러 경로로 동일한 페이지를 생성하는 경우가 발생합니다. 파라미터, 필터, 사용자 이동 상태 등에 따라 다른 URL이 만들어지면, 검색 엔진은 이것들을 서로 다른 페이지로 오인할 수 있습니다. 이렇게 되면 같은 콘텐츠가 여러 URL에 분산되어 랭킹 신호가 흩어지는 문제가 생길 수 있습니다.
이를 방지하기 위해서는 <link rel="canonical"> 태그를 사용하여, 해당 페이지의 대표 URL(우선순위가 높은, 가장 중요한 URL)을 검색 엔진에 알려주는 것이 좋습니다.
예시
<head>
<link rel="canonical" href="https://www.example.com/original-page" />
</head>
위와 같이 rel="canonical"을 설정하면 중복 페이지 신호를 한 곳으로 집중시킬 수 있습니다. 이 태그가 없으면, 쌓아온 백링크 권한(authority)이 분산되거나 인덱싱이 혼란스러워질 수 있으므로, JavaScript를 통해 동적으로 생성된 페이지 URL을 잘 살펴보고 필요하다면 반드시 canonical 태그를 설정하십시오.
3. 클라이언트 사이드 라우팅 시 주의사항
React Router 같은 클라이언트 사이드 라우팅 라이브러리를 사용하면 동적인 싱글 페이지 애플리케이션(SPA)을 쉽게 만들 수 있습니다. 하지만 링크를 적절히 처리하지 않거나 콘텐츠 로딩을 잘못 구현하면, 검색 엔진 크롤러가 중요한 콘텐츠를 놓칠 수 있습니다.
검색 엔진이 SPA를 제대로 인덱싱하려면 내부 링크를 <a> 태그 대신 라우팅 라이브러리에서 제공하는 <Link> 컴포넌트를 사용하는 등 표준 방법으로 제공해야 합니다. 그래야 검색 엔진이 해당 링크를 탐색하고 콘텐츠를 인덱싱할 수 있습니다.
React Router 예시
import { BrowserRouter as Router, Route, Link } from 'react-router-dom';
function App() {
return (
<Router>
<nav>
<Link to="/about">About Us</Link>
<Link to="/contact">Contact</Link>
</nav>
<Route path="/about" component={About} />
<Route path="/contact" component={Contact} />
</Router>
);
}
이렇게 내부 링크를 <Link> 컴포넌트로 작성하면, 검색 엔진이 구조를 잘 파악하여 페이지를 색인할 수 있습니다.
4. 지연 로딩(Lazy Loading)을 현명하게 활용
지연 로딩은 페이지 속도를 높이고 성능을 개선하는 데 매우 효과적입니다. 사용자가 필요할 때까지 이미지나 비필수 콘텐츠를 로드하지 않는 방식이죠. 그러나 구현이 잘못되면 중요한 콘텐츠가 로드되지 않아서 검색 엔진이 인덱싱하지 못할 수 있습니다.
- 상단(above-the-fold) 콘텐츠는 즉시 로드하도록 설정하여 검색 엔진이 놓치지 않도록 해야 합니다.
- Intersection Observer API 등을 사용해 효율적으로 이미지를 지연 로딩하되, 핵심 콘텐츠는 빠르게 보여주어야 합니다.
Intersection Observer를 활용한 예시
// Lazy loading images
const images = document.querySelectorAll('img[data-src]');
const imgObserver = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
observer.unobserve(img);
}
});
});
images.forEach(img => {
imgObserver.observe(img);
});
위 코드에서는 스크롤 위치(뷰포트)에 들어온 이미지만 실제로 로드합니다. 하지만 중요한 이미지는 페이지가 로드되자마자 표시하도록 구성해, 검색 엔진이 인덱싱 시 놓치지 않도록 주의합시다.
5. 중요 페이지는 사전 렌더링(Pre-render)하기
만약 JavaScript가 복잡해서 사용자와 검색 엔진이 콘텐츠에 접근하기 어렵다면, 프리렌더링(Pre-rendering) 서비스 사용을 고려해볼 수 있습니다. Prerender.io나 Rendertron 같은 서비스는 미리 Static HTML 스냅샷을 만들어 검색 엔진에게 제공하므로, JS 실행 없이도 페이지 내용을 인덱싱할 수 있게 해줍니다.
Express 설정 예시
const express = require('express');
const prerender = require('prerender-node');
const app = express();
app.use(prerender.set('prerenderToken', 'YOUR_TOKEN_HERE'));
app.get('/', (req, res) => {
res.send('Hello World!');
});
app.listen(3000);
이 구성은 검색 엔진에게 JavaScript가 처리된 완성본 HTML을 제공하여, JS를 직접 실행하지 않고도 콘텐츠를 읽을 수 있게 해줍니다. 특히 로그인 뒤나 복잡한 인터랙션 후에만 볼 수 있는 중요한 콘텐츠가 있는 경우, 이러한 프리렌더링을 적용해볼 만합니다.
6. 메타 태그를 동적으로 설정해 소셜 공유와 SEO 강화
메타 태그(예: <title>, <meta name="description">)는 검색 엔진뿐 아니라 소셜 미디어 플랫폼에서도 페이지 내용을 파악하는 중요한 요소입니다. JavaScript 기반 웹사이트에서는 동적으로 페이지를 생성하기 때문에, 이 메타 정보를 실시간으로 정확히 설정해 주는 것이 좋습니다.
예를 들어, react-helmet을 사용하면 렌더링할 때마다 메타 정보를 업데이트할 수 있습니다.
React Helmet 예시
import { Helmet } from 'react-helmet';
function BlogPost({ title, description }) {
return (
<div>
<Helmet>
<title>{title}</title>
<meta name="description" content={description} />
</Helmet>
<h1>{title}</h1>
<p>{description}</p>
</div>
);
}
이렇게 동적으로 메타 태그를 설정하면 검색 엔진과 소셜 미디어가 페이지의 제목과 설명을 제대로 읽어갈 수 있어, 노출과 클릭률이 개선될 수 있습니다.
7. robots.txt로 JavaScript 파일을 막지 말 것
robots.txt에서 JS 파일을 차단하면 검색 엔진 크롤러는 페이지 렌더링 과정을 제대로 파악할 수 없게 됩니다. 즉, 사이트 구조와 콘텐츠 노출에 악영향을 끼치죠. 검색 엔진이 실제 웹페이지처럼 JavaScript를 실행해볼 수 있도록, 중요한 JS 리소스는 차단하지 않는 것이 좋습니다.
안전한 robots.txt 구성 예시
User-agent: *
Disallow: /private/
Allow: /js/
위 예시는 /private/ 디렉터리는 크롤러 접근을 막되, /js/에 있는 JavaScript 파일들은 접근을 허용합니다. 정기적으로 robots.txt를 검토하여 중요한 자원이 차단되지 않았는지 확인하세요.
8. 브레드크럼(breadcrumbs)을 도입해 크롤링 및 UX 개선
브레드크럼은 사용자가 현재 페이지의 위치를 쉽게 파악하게 해주고, 검색 엔진에게 사이트 구조를 명확히 전달할 수 있는 방법이기도 합니다. 구글은 검색 결과에 브레드크럼을 표시해줄 수 있으며, 이는 사용자 클릭률 향상에도 도움이 됩니다.
JSON-LD 같은 구조화된 데이터를 활용하면, 검색 엔진이 사이트 계층 구조를 더욱 정확히 이해합니다.
JSON-LD 예시
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "Home",
"item": "https://www.example.com/"
},
{
"@type": "ListItem",
"position": 2,
"name": "Blog",
"item": "https://www.example.com/blog"
}
]
}
</script>
이렇게 구조화된 데이터를 제공하면 구글(및 기타 검색 엔진)과 같은 플랫폼이 사이트 내의 콘텐츠 계층을 더 잘 이해하고, 검색 결과에 브레드크럼을 표시해줌으로써 더 높은 클릭 유도를 기대할 수 있습니다.
9. JavaScript 복잡도를 줄여 크롤 예산(Crawl Budget) 관리
크롤 예산이란 검색 엔진이 일정 시간 동안 사이트에서 크롤링할 수 있는 페이지 수를 말합니다. JavaScript가 너무 복잡하거나 API 호출이 너무 많으면 페이지 로딩이 느려지고, 검색 엔진이 적은 수의 페이지만 탐색하게 될 수 있습니다.
- 팁
- 초기 페이지 로드시 불필요한 API 호출을 최소화
- 핵심 CSS와 필수 JS는 인라인 처리 등으로 의존성 최소화
- Lighthouse 같은 툴로 JavaScript 성능을 자주 점검
예시: 불필요한 API 호출 최소화
function loadData() {
if (!sessionStorage.getItem('dataLoaded')) {
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => {
// Process data
console.log(data);
sessionStorage.setItem('dataLoaded', true);
})
.catch(error => console.error('Error fetching data:', error));
}
}
document.addEventListener('DOMContentLoaded', loadData);
위 코드에서는 sessionStorage를 사용해 이미 한 번 가져온 데이터를 재사용하므로, 매번 페이지 로드할 때마다 API를 호출하지 않아도 됩니다. 이렇게 하면 페이지 로딩 속도가 빨라지고, 결과적으로 검색 엔진 크롤러가 더 많은 페이지를 효율적으로 탐색할 수 있게 됩니다.
10. window.history.replaceState()로 URL을 깔끔하게 유지
싱글 페이지 애플리케이션(SPA)에서는 해시(#)나 복잡한 쿼리 문자열이 붙은 URL이 생길 수 있습니다. 이는 SEO 측면에서 깔끔하지 않고, 사용자 경험 측면에서도 불편할 수 있습니다.
window.history.replaceState()를 사용하면 페이지 전체를 새로고침하지 않고도 현재 URL을 더 간결하고 의미 있게 바꿀 수 있습니다.
예시
// Clean up URL after loading dynamic content
window.history.replaceState(null, 'New Page Title', '/new-url-path');
이렇게 하면 페이지 리로드 없이도 브라우저 주소 표시줄에 있는 URL이 깔끔하게 변경됩니다. 검색 엔진에도 더 명확한 URL을 제공할 수 있으므로, 인덱싱이 원활해지고 사용자에게도 좋은 인상을 줍니다.
결론
JavaScript의 강력한 기능을 최대한 활용하면서 검색 엔진이 콘텐츠를 제대로 인덱싱할 수 있도록 만드는 것이 JavaScript SEO의 핵심입니다. 위에서 소개한 전략들을 고려해 다음과 같은 목표를 달성해 보세요.
- SSR이나 정적 렌더링을 통해 크롤러가 완성된 HTML을 볼 수 있도록 하기
- 중복 콘텐츠는 rel="canonical"로 해결
- 클라이언트 사이드 라우팅 시에도 크롤러가 이해할 수 있는 내부 링크를 사용
- Lazy Loading은 핵심 콘텐츠를 놓치지 않도록 신중하게 적용
- 프리렌더링을 통해 복잡한 JS 페이지도 인덱싱 가능하게 만들기
- 메타 태그 동적 설정으로 SEO와 소셜 공유 최적화
- robots.txt로 JS 파일을 함부로 차단하지 않기
- 브레드크럼과 JSON-LD로 사이트 구조를 명확히 알리기
- 크롤 예산을 고려해 JS 복잡도를 줄이고 성능 최적화하기
- URL을 깔끔하게 유지해 사용성과 SEO 모두 만족
JavaScript를 활용해서 풍부한 사용자 경험을 제공하더라도, 검색 엔진과 사용자 모두가 콘텐츠에 원활히 접근할 수 있어야 최고의 효과를 거둘 수 있습니다. 검색 엔진 친화적이면서도 사용자 친화적인 웹사이트를 만드는 것이야말로 최선의 해답이겠죠.
'일 > 공부' 카테고리의 다른 글
더이상 개발자가 블로그를 하는 것이 의미가 있을까? (0) | 2025.01.17 |
---|---|
DAISYUI 소개 | 스타일링 프레임워크 (0) | 2025.01.13 |
고령 개발자를 위한 조언 (0) | 2025.01.12 |
HTMX의 미래 (1) | 2025.01.11 |
당신은 Next.js가 필요하지 않다 (0) | 2025.01.09 |