Skip to content

feat: 구글 검색 유입을 위한 SEO 메타태그 및 크롤링 인프라 구성#318

Merged
heeeeyong merged 3 commits into
refactorfrom
feat/seo-meta-tags
Mar 5, 2026
Merged

feat: 구글 검색 유입을 위한 SEO 메타태그 및 크롤링 인프라 구성#318
heeeeyong merged 3 commits into
refactorfrom
feat/seo-meta-tags

Conversation

@ho0010
Copy link
Copy Markdown
Collaborator

@ho0010 ho0010 commented Mar 4, 2026

📝작업 내용

  • react-helmet-async 설치, main.tsxHelmetProvider 추가
  • 공통 SEO 컴포넌트 SEOHead 생성 (src/components/common/SEOHead.tsx)
  • SearchBook.tsx: 책 제목·저자 기반 동적 title/description 주입
  • GroupDetail.tsx: 모임명·책 제목 기반 동적 title/description 주입
  • public/robots.txt 추가: 크롤러 허용/차단 경로 명시
  • public/sitemap.xml 추가: 주요 경로 정적 sitemap 구성

참고

  • 구글봇은 헤드리스 크롬으로 JS를 실행하므로 CSR 환경에서도 동적 메타태그 인덱싱 가능
  • 배포 후 Google Search Console에 sitemap 제출 필요

Summary by CodeRabbit

릴리스 노트

  • 새 기능

    • 페이지별 동적 메타데이터 지원 추가
  • 기타

    • 검색 엔진 최적화 설정 구성
    • 사이트맵 및 검색봇 규칙 파일 추가
    • 의존성 추가: react-helmet-async

ho0010 added 3 commits March 4, 2026 17:26
- react-helmet-async 패키지 설치 (v3.0.0)
- main.tsx에 HelmetProvider 추가
- 공통 SEOHead 컴포넌트 생성 (src/components/common/SEOHead.tsx)
  - title, description props 기반 동적 메타태그 주입
  - 기본값: 기존 index.html의 title/description 유지
- SearchBook.tsx: 책 제목(title), 저자명(authorName) 기반 동적 title/description 주입
- GroupDetail.tsx: 모임명(roomName), 책 제목(bookTitle) 기반 동적 title/description 주입
- 데이터 로드 전(null 상태)에는 기본값 유지
- public/robots.txt 추가: 크롤러 허용/차단 경로 명시
- public/sitemap.xml 추가: 주요 경로 정적 sitemap 구성
- Google Search Console 제출로 인덱싱 속도 단축 예정
@ho0010 ho0010 self-assigned this Mar 4, 2026
@ho0010 ho0010 added the ✨ Feature 기능 개발 label Mar 4, 2026
@vercel
Copy link
Copy Markdown

vercel Bot commented Mar 4, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
thip Ready Ready Preview, Comment Mar 4, 2026 9:04am

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 4, 2026

워크스루

이 PR은 react-helmet-async 라이브러리를 통합하여 애플리케이션에 SEO 메타데이터 관리 기능을 추가합니다. HelmetProvider를 루트에 추가하고, SEOHead 컴포넌트를 생성하여 동적 페이지 제목과 설명을 관리합니다. robots.txt와 sitemap.xml 정적 파일도 함께 추가됩니다.

변경 사항

응집도 / 파일(들) 요약
의존성 추가
package.json
react-helmet-async ^3.0.0 라이브러리 추가
SEO 설정
public/robots.txt, public/sitemap.xml
검색 엔진 크롤러 지시문 및 사이트맵 설정 추가
SEO 제공자 통합
src/main.tsx
HelmetProvider로 App 컴포넌트를 래핑하여 메타데이터 관리 컨텍스트 제공
SEO 컴포넌트
src/components/common/SEOHead.tsx
제목과 설명을 받아 동적으로 페이지 메타데이터를 렌더링하는 새로운 SEOHead 컴포넌트
페이지 메타데이터 적용
src/pages/groupDetail/GroupDetail.tsx, src/pages/searchBook/SearchBook.tsx
각 페이지에서 SEOHead를 사용하여 동적 메타데이터 설정

시퀀스 다이어그램

sequenceDiagram
    participant Browser as Browser
    participant App as App Root
    participant HelmetProvider as HelmetProvider
    participant Page as Page Component
    participant SEOHead as SEOHead Component
    participant Helmet as Helmet (DOM)

    Browser->>App: Load Application
    App->>HelmetProvider: Wrap with HelmetProvider
    HelmetProvider->>App: Provide Helmet Context
    
    App->>Page: Render Page (e.g., GroupDetail)
    Page->>Page: Load page data
    
    Page->>SEOHead: Render with title & description
    SEOHead->>Helmet: Update <title> & <meta> tags
    Helmet->>Browser: Inject metadata into <head>
    
    Browser->>Browser: Display page with SEO metadata
Loading

예상 코드 검토 노력

🎯 3 (Moderate) | ⏱️ ~20 분

관련 가능성 있는 PR들

  • feat: Rooms 도메인 API 연동 #117: src/pages/groupDetail/GroupDetail.tsx 수정으로 직접적인 코드 레벨 관련성 (SEOHead 사용 및 Promise 리팩토링 vs. 룸 API 사용으로 페이지 재작성)
  • feat: 책 상세 페이지 #52: SearchBook.tsx 파일 변경 - SEOHead 추가로 메타데이터 렌더링 수정, 같은 컴포넌트에서 직접적인 코드 레벨 관련성

제안 라벨

📃 Docs

제안 검토자

  • heeeeyong
  • ljh130334

토끼의 시

🐰 메타데이터를 담은 헬멧을 쓰고,
SEO의 길을 밝혀가네요.
검색 엔진들이 반가워하며,
사이트맵을 따라 춤을 춘답니다! 🗺️✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목은 변경 사항의 핵심 내용을 정확히 반영하고 있습니다. SEO 메타태그와 크롤링 인프라 구성이라는 주요 변경 사항을 명확하게 요약하고 있습니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/seo-meta-tags

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
src/pages/groupDetail/GroupDetail.tsx (1)

94-96: 단일 Promise에 Promise.all 사용은 불필요합니다.

Promise.all에 단일 요소 배열만 전달하고 있어 불필요한 복잡성이 추가됩니다. minLoadingTime과의 병렬 실행이 목적이라면 두 Promise를 함께 배열에 포함해야 합니다.

♻️ 수정 제안
        const minLoadingTime = new Promise(resolve => setTimeout(resolve, 500));
-       const [response] = await Promise.all([getRoomDetail(Number(roomId))]);
+       const [response] = await Promise.all([getRoomDetail(Number(roomId)), minLoadingTime]);
-       await minLoadingTime;

또는 단순화:

        const minLoadingTime = new Promise(resolve => setTimeout(resolve, 500));
-       const [response] = await Promise.all([getRoomDetail(Number(roomId))]);
-       await minLoadingTime;
+       const response = await getRoomDetail(Number(roomId));
+       await minLoadingTime;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/groupDetail/GroupDetail.tsx` around lines 94 - 96, The current code
creates minLoadingTime and uses Promise.all with a single getRoomDetail promise
which is unnecessary; either run getRoomDetail and minLoadingTime in parallel by
awaiting Promise.all([getRoomDetail(Number(roomId)), minLoadingTime]) and
destructure the room detail from the result, or simplify by awaiting
getRoomDetail(Number(roomId)) then awaiting minLoadingTime sequentially; update
the usage of response accordingly (e.g., rename to roomDetail) so consumers of
the fetched data use the correct variable.
public/sitemap.xml (1)

1-15: 기본 sitemap 구조는 적절합니다.

정적 sitemap으로 주요 페이지를 포함하고 있습니다. 향후 동적 페이지(/search/book/:isbn, /group/detail/:roomId)가 많아지면 서버 사이드에서 동적 sitemap 생성을 고려해볼 수 있습니다.

<lastmod><changefreq> 태그 추가도 선택적으로 고려해보세요.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@public/sitemap.xml` around lines 1 - 15, 현재 정적 sitemap.xml에 주요 정적 경로만 포함되어
있는데, 향후 동적 페이지(`/search/book/:isbn`, `/group/detail/:roomId`)를 고려하여 서버사이드로 동적
sitemap을 생성할 수 있도록 준비하고, 또한 각 <url> 항목에 선택적으로 <lastmod>와 <changefreq> 태그를 추가하여
크롤러에게 업데이트 시기와 빈도를 알리도록 수정하세요; 구현 방법은 정적 public/sitemap.xml 대신 서버 엔드포인트(예:
/sitemap.xml)를 만들어 URL 집합을 동적으로 생성하고 각 항목에 대해 lastmod 값(예: 게시일 또는 업데이트 타임스탬프)과
changefreq 값을 포함하도록 반환하면 됩니다.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@public/sitemap.xml`:
- Around line 1-15: 현재 정적 sitemap.xml에 주요 정적 경로만 포함되어 있는데, 향후 동적
페이지(`/search/book/:isbn`, `/group/detail/:roomId`)를 고려하여 서버사이드로 동적 sitemap을 생성할
수 있도록 준비하고, 또한 각 <url> 항목에 선택적으로 <lastmod>와 <changefreq> 태그를 추가하여 크롤러에게 업데이트 시기와
빈도를 알리도록 수정하세요; 구현 방법은 정적 public/sitemap.xml 대신 서버 엔드포인트(예: /sitemap.xml)를 만들어
URL 집합을 동적으로 생성하고 각 항목에 대해 lastmod 값(예: 게시일 또는 업데이트 타임스탬프)과 changefreq 값을 포함하도록
반환하면 됩니다.

In `@src/pages/groupDetail/GroupDetail.tsx`:
- Around line 94-96: The current code creates minLoadingTime and uses
Promise.all with a single getRoomDetail promise which is unnecessary; either run
getRoomDetail and minLoadingTime in parallel by awaiting
Promise.all([getRoomDetail(Number(roomId)), minLoadingTime]) and destructure the
room detail from the result, or simplify by awaiting
getRoomDetail(Number(roomId)) then awaiting minLoadingTime sequentially; update
the usage of response accordingly (e.g., rename to roomDetail) so consumers of
the fetched data use the correct variable.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: a1675a90-7d34-4991-8aa0-d9f67df9b5ad

📥 Commits

Reviewing files that changed from the base of the PR and between 64a6adc and 5dc7646.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (7)
  • package.json
  • public/robots.txt
  • public/sitemap.xml
  • src/components/common/SEOHead.tsx
  • src/main.tsx
  • src/pages/groupDetail/GroupDetail.tsx
  • src/pages/searchBook/SearchBook.tsx

Copy link
Copy Markdown
Collaborator

@heeeeyong heeeeyong left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CSR 구조에서도 동적으로 메타태그를 적용해 사용할 수 있다니 되게 좋은것같습니다!

@heeeeyong heeeeyong merged commit 99963c7 into refactor Mar 5, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ Feature 기능 개발

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants