개발/Frontend

Next.js 안 써도 괜찮아, React SEO 최적화 총정리

dev-power 2025. 12. 10. 22:32

React로 열심히 만든 웹사이트, 구글에 검색하면 안 나오던 경험 있으신가요?

 

분명 배포도 잘 됐고, 링크 타고 들어가면 잘 보이는데 구글에 사이트 이름을 쳐도 내 사이트가 안 나옵니다. "혹시 검색엔진이 고장난 건가?" 싶어서 찾아보니, React로 만든 사이트는 검색엔진 최적화(SEO)가 어렵다는 얘기가 나옵니다. 그리고 어김없이 등장하는 해결책: "Next.js 쓰세요."

 

물론 Next.js가 SEO에 유리한 건 맞습니다. 하지만 지금 당장 프로젝트를 다 갈아엎을 수는 없잖아요? 다행히도 React 만으로도 SEO 최적화를 충분히 할 수 있습니다! 이 글에서는 React 프로젝트에서 검색엔진 최적화를 하는 (거의) 모든 방법을 알려드리겠습니다.

 

 

 

SEO란?


SEO는 Search Engine Optimization, 즉 검색엔진 최적화입니다. 쉽게 말해서 구글이나 네이버 같은 검색 엔진이 내 사이트를 잘 찾아서 검색 결과 상위에 올려주도록 만드는 작업입니다.

 

검색엔진은 크롤러라는 프로그램을 통해 인터넷을 돌아다니며 웹사이트 정보를 수집합니다. 크롤러는 쉽게 말해 자동으로 웹사이트를 방문해서 내용을 읽고 기록하는 로봇이라고 생각하면 됩니다. 이렇게 수집한 정보를 정리하는 과정을 색인화(indexing)라고 하는데, 내 사이트가 이 색인에 잘 등록되어야 사람들이 검색했을 때 내 사이트가 나타나는 거죠.

 

그런데 왜 React는 SEO가 어렵다고 하는 걸까요?

 

 

 

React의 근본적인 문제: CSR


React는 기본적으로 CSR(Client Side Rendering) 방식입니다. 서버는 거의 빈 HTML을 보내주고, 브라우저에서 JavaScript가 실행되면서 화면을 그립니다.

 

크롤러가 받는 HTML은 이런 식입니다:

<!DOCTYPE html>
<html>
  <head>
    <title>My React App</title>
  </head>
  <body>
    <div id="root"></div>
    <script src="/bundle.js"></script>
  </body>
</html>

보시다시피 `<div id="root"></div>` 하나만 덩그러니 있습니다. 실테 콘텐츠는 JavaScript가 실행되어야 나타나는데, 크롤러가 JavaScript를 얼마나 잘 실행하는지는... 불확실합니다.

 

 

 

Next.js는 왜 SEO 가 좋다고 하는가?


React는 기본적으로 CSR(Client Side Rendering) 방식입니다. 브라우저가 빈 HTML을 받아온 뒤, JavaScript를 실행해서 화면을 그립니다. 문제는 크롤러가 방문했을 때 JavaScript가 실행되기 전의 빈 HTML만 보게 될 수 있다는 점입니다. 마치 책을 펼쳤는데 글자가 안 보이는 것처럼요.

 

반면 Next.js는 SSR(Server Side Rendering) 방식을 지원합니다. 서버에서 미리 HTML을 완성해서 보내주기 때문에, 크롤러가 방문하면 이미 완성된 내용을 바로 읽을 수 있습니다. 그래서 "Next.js가 SEO에 좋다"는 말이 나오는 겁니다.

 

하지만 Next.js를 안 쓴다고 포기할 필요는 없습니다. React에서도 크롤러가 우리 사이트를 잘 읽을 수 있게 만드는 방법들이 있으니까요.

 

 

 

React에서 SEO 최적화하는 법


1. React Helmet으로 메타 정보 관리하기

메타 정보는 페이지의 제목, 설명, 대표 이미지 같은 정보를 담고 있습니다. 검색 엔진은 이 정보를 보고 "아, 이 페이지는 이런 내용이구나"하고 대강 판단합니다.

 

React Helmet은 이런 메타 정보를 React 컴포넌트 안에서 쉽게 관리할 수 있게 해주는 라이브러리입니다.

 

먼저 설치합니다:

npm install react-helmet-async

그리고 이렇게 사용하면 됩니다:

import { Helmet } from 'react-helmet-async';

function BlogPost() {
  return (
    <>
      <Helmet>
        <title>React SEO 최적화 완벽 가이드</title>
        <meta name="description" content="React 프로젝트에서 검색엔진 최적화하는 모든 방법을 알려드립니다." />
        <meta property="og:title" content="React SEO 최적화 완벽 가이드" />
        <meta property="og:description" content="Next.js 없이도 React에서 SEO를 완벽하게 구현하는 방법" />
        <meta property="og:image" content="https://mysite.com/thumbnail.jpg" />
        <link rel="canonical" href="https://mysite.com/blog/react-seo" />
      </Helmet>
      
      <article>
        <h1>React SEO 최적화 완벽 가이드</h1>
        <p>본문 내용...</p>
      </article>
    </>
  );
}

여기서 og:로 시작하는 메타 태그들은 Open Graph 태그라고 하는데, 카카오톡이나 페이스북에 링크를 공유했을 때 예쁘게 보이는 미리보기를 만들어줍니다. canonical 태그는 "이 페이지의 원본 주소는 여기예요"라고 검색엔진에게 알려주는 역할을 합니다.

 

 

구글에서 검색했을 때 나오는 제목과 설명이 바로 이 메타 태그에서 나온 것입니다.

 

참고로 React19 버전에서는 helmet을 사용할 수 없습니다. 이런 경우에는 public/index.html에 메타 정보를 넣으면 됩니다. public/index.html 파일은 크롤러가 바로 읽을 수 있기 때문에 여기에다 메타 정보를 넣으면 됩니다.

 

사실 이 방법에는 단점이 있습니다. 모든 페이지가 같은 메타 정보를 갖게 된다는 것입니다. 블로그라면 각 글마다 다른 제목과 설명이 필요한데, 이 방법으로는 안 됩니다. 그래서 다음 방법이 필요합니다.

 

 

2. Prerendering

진짜 제대로 된 SEO를 하려면 Prerendering이 필수입니다. Prerendering은 빌드할 때 각 페이지를 미리 HTML로 만들어두는 것입니다. 그러면 크롤러가 JavaScript 실행을 기다릴 필요 없이 완성된 HTML을 바로 읽을 수 있습니다.

 

react-snap 이라는 도구를 사용합니다:

npm install react-snap

 

package.json에 추가:

{
  "scripts": {
    "build": "react-scripts build",
    "postbuild": "react-snap"
  },
  "reactSnap": {
    "include": [
      "/",
      "/about",
      "/blog",
      "/blog/react-seo"
    ]
  }
}

이제 npm run build를 하면 각 경로마다 완성된 HTML 파일이 생성됩니다.

 

예를 들어 /blog/react-seo 페이지는 이런 식으로 완성된 HTML이 만들어집니다:

<!DOCTYPE html>
<html>
  <head>
    <title>React SEO 완벽 가이드 - 내 블로그</title>
    <meta name="description" content="React에서 SEO 최적화하는 방법을 알려드립니다" />
  </head>
  <body>
    <div id="root">
      <header>
        <nav>...</nav>
      </header>
      <main>
        <article>
          <h1>React SEO 완벽 가이드</h1>
          <p>본문 내용이 여기 다 들어있음...</p>
        </article>
      </main>
    </div>
    <script src="/bundle.js"></script>
  </body>
</html>

크롤러가 이 페이지를 방문하면 완성된 콘텐츠를 바로 읽을 수 있습니다.

 

Prerendering을 하고 나면, 이제 React Helmet이나 시맨틱 태그가 의미가 생깁니다. HTML이 완성된 상태로 저장되니까요.

 

 

3. React Helmet으로 페이지별 메타 정보 관리 (Prerendering 필수)

주의: 이 방법은 Prerendering과 함께 써야 의미가 있습니다! 순수 CSR에서는 크롤러가 못 봅니다.

npm install react-helmet-async

 

App.js에서 HelmetProvider로 감싸줍니다:

import { HelmetProvider } from 'react-helmet-async';

function App() {
  return (
    <HelmetProvider>
      {/* 라우터 설정 등 */}
    </HelmetProvider>
  );
}

 

각 페이지에서 사용:

import { Helmet } from 'react-helmet-async';

function BlogPost() {
  return (
    <>
      <Helmet>
        <title>React SEO 완벽 가이드</title>
        <meta name="description" content="React 프로젝트에서 검색엔진 최적화하는 모든 방법" />
        <meta property="og:title" content="React SEO 완벽 가이드" />
        <meta property="og:description" content="Next.js 없이도 React에서 SEO 구현하기" />
        <meta property="og:image" content="https://mysite.com/thumbnail.jpg" />
        <link rel="canonical" href="https://mysite.com/blog/react-seo" />
      </Helmet>
      
      <article>
        <h1>React SEO 완벽 가이드</h1>
        <p>본문 내용...</p>
      </article>
    </>
  );
}

 

 

4. sitemap.xml 만들기

사이트맵은 "우리 사이트에는 이런 페이지들이 있어요"하고 목록을 정리해서 크롤러에게 알려주는 파일입니다.

작은 사이트라면 수동으로 만들 수도 있습니다. `public/sitemap.xml`:

<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>https://mysite.com/</loc>
    <lastmod>2024-12-08</lastmod>
    <changefreq>daily</changefreq>
    <priority>1.0</priority>
  </url>
  <url>
    <loc>https://mysite.com/blog</loc>
    <lastmod>2024-12-08</lastmod>
    <changefreq>weekly</changefreq>
    <priority>0.8</priority>
  </url>
  <url>
    <loc>https://mysite.com/about</loc>
    <lastmod>2024-12-01</lastmod>
    <changefreq>monthly</changefreq>
    <priority>0.5</priority>
  </url>
</urlset>
  • loc: 페이지 주소
  • lastmod: 마지막 수정일
  • changefreq: 업데이트 빈도 (daily, weekly, monthly 등)
  • priority: 중요도 (0.0 ~ 1.0) (메인 페이지는 1.0으로 높게, 덜 중요한 페이지는 낮게 줍니다)

페이지가 많다면 자동으로 생성하는 도구를 쓰는 게 좋습니다. sitemap-generator 나 빌드 스크립트로 자동화할 수 있습니다.

 

 

5. 이미지 최적화

이미지에는 꼭 alt 속성을 넣어주세요. 크롤러는 이미지를 볼 수 없기 때문에 alt 텍스트를 읽고 "이 이미지가 뭔지" 판단합니다.

 

나쁜 예:

<img src="/image.jpg" />

 

좋은 예:

<img src="/react-logo.jpg" alt="React 로고" />

 

파일 이름도 의미 있게 지어주세요. image123.jpg 보다는 react-seo-guid-thumbnail.jpg 가 훨씬 좋습니다. 

 

 

6. Code Splitting으로 초기 로딩 최적화:

import React, { lazy, Suspense } from 'react';

const HeavyComponent = lazy(() => import('./HeavyComponent'));

function App() {
  return (
    <Suspense fallback={<div>로딩 중...</div>}>
      <HeavyComponent />
    </Suspense>
  );
}

처음에 모든 코드를 다운로드하지 않고, 필요할 때만 가져옵니다. 초기 로딩 시간이 줄어들어서 사용자 경험도 좋아지고 SEO에도 유리합니다.

 

이미지 최적화:

  • WebP 포맷 사용(JPEG/PNG 보다 30% 정도 작음)
  • 적절한 크기로 리사이징 (4k 이미지를 썸네일로 쓰지 마세요)
  • CDN 사용 고려

 

 

7. Google Search Console에 사이트 등록하기

지금까지 소개한 것들은 기술적인 방법들입니다. 여기서 화룡점정을 찍기 위해 구글 서치 콘솔(Google Searfch Console)에 사이트를 등록해봅시다.

 

어? SEO 최적화 다 했는데 왜 또 등록을 해야 돼?

SEO 최적화는 크롤러가 우리 사이트를 잘 읽을 수 있게 만드는 작업입니다. 하지만 링크가 없는 새 사이트는 발견되기까지 시간이 오래 걸리거나 아예 발견되지 않을 수도 있습니다. 서치 콘솔에 등록하면 "내 사이트 크롤링해 주세요"하고 직접 요청할 수 있습니다.

 

게다가 서치 콘솔을 통해:

  • 내 사이트가 검색 결과에 얼마나 노출되는지
  • 어떤 키워드로 사람들이 찾아오는지
  • 크롤링 에러는 없는지
  • 파비콘이 제대로 인식되고 있는지

이런 걸 다 확인할 수 있습니다. 예를 들어 검색 결과에서 파비콘이 잘 안나오면 서치 콘솔에서 URL 검사 기능으로 재크롤링을 요청하면 빨리 반영됩니다.

 

쉽게 비유하자면 SEO 최적화는 가게를 예쁘게 꾸미고 간판을 다는 작업이고, 서치 콘솔 등록은 구글 지도에 내 가게르 등록하는 것이라고 할 수 있겠네요. 둘 다 하면 시너지가 납니다.