자바스크립트 검색엔진 최적화의 기본사항 이해하기

자바스크립트는 웹을 강력한 애플리케이션 플랫폼으로 만드는 여러 기능을 제공한다는 점에서 웹 플랫폼의 중요한 부분을 차지합니다. 자바스크립트 기반의 웹 애플리케이션이 Google 검색에서 개선되도록 만들면 사용자가 웹 앱에서 제공하는 콘텐츠를 검색할 수 있으므로 신규 사용자를 발굴하고 기존 사용자의 재참여를 유도할 수 있습니다. Google 검색은 Chromium의 Evergreen 버전을 사용하여 자바스크립트를 실행하지만, 몇 가지 부분을 최적화할 수 있습니다.

이 가이드에서는 Google 검색이 자바스크립트를 처리하는 방법과 자바스크립트 웹 앱을 Google 검색에 최적화하기 위한 권장사항을 설명합니다.

Googlebot이 자바스크립트를 처리하는 방법

Googlebot은 다음 세 가지 주요 단계로 자바스크립트 웹 앱을 처리합니다.

  1. 크롤링
  2. 렌더링
  3. 색인 생성

Googlebot은 페이지를 크롤링 및 렌더링하고 페이지의 색인을 생성합니다.

Googlebot은 크롤링 대기열에서 URL을 가져와
      크롤링한 다음 처리 단계로 넘깁니다. 처리 단계에서는 크롤링 대기열로 돌아가는
      링크를 추출하고 페이지를 렌더링 대기열에 추가합니다. 페이지는 렌더링 대기열에서 렌더러로 이동하고,
      렌더러는 렌더링된 HTML을 처리 단계로 다시 넘기며, 여기서 콘텐츠의 색인이 생성되고 링크가 추출되어
      크롤링 대기열에 전달됩니다.

Googlebot은 HTTP 요청을 전송하여 크롤링 대기열에서 URL을 가져올 때는 크롤링이 허용되는지 먼저 확인합니다. Googlebot이 robots.txt 파일을 읽고 URL이 허용되어 있지 않으면 이 URL에 관한 HTTP 요청과 URL 자체를 건너뜁니다.

그런 다음 Googlebot은 HTML 링크의 href 속성에 있는 다른 URL에 관한 응답을 파싱하고 크롤링 대기열에 이러한 URL을 추가합니다. 링크가 검색되지 않도록 하려면 NOFOLLOW 메커니즘을 사용하세요.

URL 크롤링 및 HTML 응답 파싱은 HTTP 응답의 HTML에 모든 콘텐츠가 포함된 전통적인 방식의 웹사이트 또는 서버 측 렌더링 페이지에서 원활하게 작동합니다. 일부 자바스크립트 사이트에서는 초기 HTML에 실제 콘텐츠가 포함되어 있지 않으며 Googlebot이 자바스크립트에서 생성되는 실제 페이지 콘텐츠를 보기 위해 자바스크립트를 실행해야 하는 앱 셸 모드를 사용하기도 합니다.

로봇 메타태그 또는 헤더에서 Googlebot에 페이지의 색인을 생성하지 말라고 명시되어 있지 않은 한 Googlebot은 모든 페이지를 렌더링 대기열에 추가합니다. 페이지는 몇 초 동안 이 대기열에 머무를 수 있지만, 그보다 오래 있을 수는 없습니다. Googlebot의 리소스가 허용되면 헤드리스 Chromium이 페이지를 렌더링하고 자바스크립트를 실행합니다. Googlebot은 렌더링된 링크의 HTML을 다시 파싱하고 발견된 URL을 크롤링 대기열에 추가합니다. 또한 렌더링된 HTML을 사용하여 페이지의 색인을 생성합니다.

서버 측 또는 사전 렌더링은 사용자와 크롤러가 웹사이트를 더욱 신속하게 로드할 수 있게 만들고 모든 봇이 자바스크립트를 실행할 수 없다는 점에서 여전히 좋은 방법입니다.

고유한 제목과 스니펫을 사용하여 페이지 설명하기

고유하고 구체적인 제목과 유용한 메타 설명을 작성하면 사용자가 목표에 가장 적합한 결과를 빠르게 찾을 수 있습니다. 가이드라인에서 좋은 제목과 설명의 조건을 알아보세요.

자바스크립트를 사용하여 메타 설명과 제목을 설정하거나 변경할 수 있습니다.

호환되는 코드 작성하기

브라우저에서는 여러 API를 제공하며 자바스크립트는 빠르게 진화하는 언어입니다. 따라서 Googlebot이 지원하는 API 및 자바스크립트 기능은 일부 제한되어 있습니다. 코드가 Googlebot과 호환되는지 확인하려면 자바스크립트 문제해결 가이드라인을 참조하세요.

의미 있는 HTTP 상태 코드 사용

Googlebot은 HTTP 상태 코드를 사용하여 페이지를 크롤링할 때 문제가 있었는지 확인합니다.

404 코드(찾을 수 없는 페이지) 또는 401 코드(로그인이 필요한 페이지)와 같이 의미 있는 상태 코드를 사용하여 페이지를 크롤링하거나 색인을 생성하지 않아야 한다고 Googlebot에 알려야 합니다. HTTP 상태 코드를 사용하면 페이지가 새로운 URL로 이동했는지 Googlebot에 알려 색인이 정확하게 업데이트되도록 할 수 있습니다.

다음은 HTTP 상태 코드 목록과 이를 사용해야 하는 상황에 관한 설명입니다.

HTTP 상태 사용 상황
301/302 페이지가 새로운 URL로 이동했습니다.
401/403 권한 문제로 페이지를 사용할 수 없습니다.
404/410 페이지를 더 이상 사용할 수 없습니다.
5xx 서버 측에서 문제가 발생했습니다.

단일 페이지 앱에서 soft 404 오류 방지

클라이언트 측에서 렌더링된 단일 페이지 앱에서 라우팅이 클라이언트 측 라우팅으로 구현되는 경우가 많습니다. 이 경우 의미 있는 HTTP 상태 코드를 사용하는 것은 불가능하거나 비효율적입니다. 클라이언트 측 렌더링 및 라우팅을 방지하기 위해 soft 404 오류를 사용할 때 다음 전략 중 한 가지를 사용하세요.

  • 서버가 404 HTTP 상태 코드로 응답하는 경우 URL에 자바스크립트 리디렉션을 사용합니다(예: /not-found).
  • 자바스크립트를 사용하여 오류 페이지에 <meta name="robots" content="noindex">를 추가합니다.

다음은 리디렉션 접근방식 관련 샘플 코드입니다.

fetch(`/api/products/${productId}`)
.then(response => response.json())
.then(product => {
  if(product.exists) {
    showProductDetails(product); // shows the product information on the page
  } else {
    // this product does not exist, so this is an error page.
    window.location.href = '/not-found'; // redirect to 404 page on the server.
  }
})

다음은 NOINDEX 접근방식 관련 샘플 코드입니다.

fetch(`/api/products/${productId}`)
.then(response => response.json())
.then(product => {
  if(product.exists) {
    showProductDetails(product); // shows the product information on the page
  } else {
    // this product does not exist, so this is an error page.
    // Note: This example assumes there is no other meta robots tag present in the HTML.
    const metaRobots = document.createElement('meta');
    metaRobots.name = 'robots';
    metaRobots.content = 'noindex';
    document.head.appendChild(metaRobots);
  }
})

프래그먼트 대신 History API 사용하기

Googlebot이 페이지에서 링크를 찾을 때 HTML 링크의 href 속성에 있는 URL만 고려합니다.

클라이언트 측 라우팅 방식을 사용하는 단일 페이지 애플리케이션의 경우 History API를 사용하여 웹 앱의 다양한 보기 간 라우팅을 구현합니다. Googlebot이 링크를 찾을 수 있도록 하기 위해 다양한 페이지 콘텐츠 로드 시 프래그먼트를 사용하지 마세요. 다음은 Googlebot이 링크를 크롤링하지 않으므로 좋지 않은 사례입니다.

<nav>
  <ul>
    <li><a href="#/products">Our products</a></li>
    <li><a href="#/services">Our services</a></li>
  </ul>
</nav>

<h1>Welcome to example.com!</h1>
<div id="placeholder">
  <p>Learn more about <a href="#/products">our products</a> and <a href="#/services">our services</p>
</div>
<script>
window.addEventListener('hashchange', function goToPage() {
  // this function loads different content based on the current URL fragment
  const pageToLoad = window.location.hash.slice(1); // URL fragment
  document.getElementById('placeholder').innerHTML = load(pageToLoad);
});
</script>

대신 History API를 구현하여 링크 URL에 Googlebot이 액세스하도록 하면 됩니다.

<nav>
  <ul>
    <li><a href="/products">Our products</a></li>
    <li><a href="/services">Our services</a></li>
  </ul>
</nav>

<h1>Welcome to example.com!</h1>
<div id="placeholder">
  <p>Learn more about <a href="/products">our products</a> and <a href="/services">our services</p>
</div>
<script>
function goToPage(event) {
  event.preventDefault(); // stop the browser from navigating to the destination URL.
  const hrefUrl = event.target.getAttribute('href');
  const pageToLoad = hrefUrl.slice(1); // remove the leading slash
  document.getElementById('placeholder').innerHTML = load(pageToLoad);
  window.history.pushState({}, window.title, hrefUrl) // Update URL as well as browser history.
}

// Enable client-side routing for all links on the page
document.querySelectorAll('a').forEach(link => link.addEventListener('click', goToPage));

</script>

메타 로봇 태그 신중히 사용하기

메타 로봇 태그를 통해 Googlebot이 페이지의 색인을 생성하거나 링크를 따라가지 못하게 할 수 있습니다. 예를 들어 페이지 상단에 다음 메타태그를 추가하면 Googlebot이 페이지의 색인을 생성하지 않습니다.

<!-- Googlebot won't index this page or follow links on this page -->
<meta name="robots" content="noindex, nofollow">

자바스크립트를 사용하여 페이지에 메타 로봇 태그를 페이지에 추가하거나 콘텐츠를 변경할 수 있습니다. 다음 예제 코드는 API 호출이 콘텐츠를 반환하지 않는 경우 현재 페이지의 색인이 생성되지 않도록 자바스크립트로 메타 로봇 태그를 변경하는 방법을 보여 줍니다.

fetch('/api/products/' + productId)
  .then(function (response) { return response.json(); })
  .then(function (apiResponse) {
    if (apiResponse.isError) {
      // get the robots meta tag
      var metaRobots = document.querySelector('meta[name="robots"]');
      // if there was no robots meta tag, add one
      if (!metaRobots) {
        metaRobots = document.createElement('meta');
        metaRobots.setAttribute('name', 'robots');
        document.head.appendChild(metaRobots);
      }
      // tell Googlebot to exclude this page from the index
      metaRobots.setAttribute('content', 'noindex');
      // display an error message to the user
      errorMsg.textContent = 'This product is no longer available';
      return;
    }
    // display product information
    // ...
  });
    

Googlebot이 자바스크립트를 실행하기 전에 로봇 메타태그에서 noindex를 발견하면 페이지를 렌더링하거나 페이지 색인을 생성하지 않습니다.

장기간 유지되는 캐싱 사용하기

Googlebot은 네트워크 요청 및 리소스 사용을 줄이기 위해 적극적으로 캐싱을 실행합니다. WRS는 캐싱 헤더를 무시할 수 있으며, 이로 인해 WRS에서 오래된 자바스크립트 또는 CSS 리소스를 사용할 수 있습니다. 콘텐츠 지문은 main.2bb85551.js와 같이 콘텐츠의 지문을 파일 이름의 일부로 만들어 이 문제를 방지합니다. 지문은 파일의 콘텐츠에 따라 달라지므로 업데이트할 때마다 다른 파일 이름이 생성됩니다. 자세한 내용은 오래 지속되는 캐싱 전략에 관한 web.dev 가이드를 참조하세요.

구조화된 데이터 사용하기

페이지에서 구조화된 데이터를 사용할 때 자바스크립트를 사용해 필수 JSON-LD를 생성하고 페이지에 삽입할 수 있습니다. 문제를 방지하기 위해 구현을 테스트하는 것을 잊지 마세요.

웹 구성요소 권장사항 따르기

Googlebot은 웹 구성요소를 지원합니다. Googlebot은 페이지를 렌더링할 때 shadow DOM 및 light DOM 콘텐츠를 평면화합니다. 즉, Googlebot은 렌더링된 HTML에 표시되는 콘텐츠만 볼 수 있습니다. Googlebot이 렌더링된 후에도 콘텐츠를 볼 수 있는지 확인하려면 모바일 친화성 테스트 또는 URL 검사 도구를 사용하여 렌더링된 HTML을 살펴 보세요.

콘텐츠가 렌더링된 HTML에 표시되지 않으면 Googlebot이 색인을 생성할 수 없습니다.

다음 예는 shadow DOM 내의 light DOM 콘텐츠를 표시하는 웹 구성요소를 생성합니다. light DOM 및 shadow DOM 콘텐츠가 모두 렌더링된 HTML에 표시되는지 확인하기 위한 한 가지 방법은 Slot 요소를 사용하는 것입니다.

<script>
  class MyComponent extends HTMLElement {
    constructor() {
      super();
      this.attachShadow({ mode: 'open' });
    }

    connectedCallback() {
      let p = document.createElement('p');
      p.innerHTML = 'Hello World, this is shadow DOM content. Here comes the light DOM: <slot></slot>';
      this.shadowRoot.appendChild(p);
    }
  }

  window.customElements.define('my-component', MyComponent);
</script>

<my-component>
  <p>This is light DOM content. It's projected into the shadow DOM.</p>
  <p>WRS renders this content as well as the shadow DOM content.</p>
</my-component>
            

렌더링된 후에 Googlebot은 이 콘텐츠의 색인을 생성합니다.

<my-component>
  Hello World, this is shadow DOM content. Here comes the light DOM:
  <p>This is light DOM content. It's projected into the shadow DOM<p>
  <p>WRS renders this content as well as the shadow DOM content.</p>
</my-component>
    

이미지 및 지연 로드 콘텐츠 문제 해결

이미지는 상당히 큰 대역폭과 성능을 요구할 수 있습니다. 지연 로드를 사용하여 사용자가 이미지를 보려고 할 때만 이미지를 로드하는 것이 좋습니다. 검색에 적합한 방법으로 지연 로드를 구현하려면 지연 로드 가이드라인을 따르세요.