[공통] 페이지 단위 SEO 메타데이터 인프라 추가#1257
Conversation
- src/static/seo.ts: SITE_NAME, DEFAULT_TITLE, DEFAULT_DESCRIPTION,
DEFAULT_OG_IMAGE, TWITTER_CARD_TYPE 상수와 buildAbsoluteUrl 헬퍼 추가
- src/components/seo/Seo/index.tsx: title, description, canonical,
Open Graph, Twitter Card, noindex 옵션을 처리하는 공통 컴포넌트 추가.
메타 태그에 key 부여로 페이지 단위 override 동작 보장
- src/pages/_document.tsx: 페이지별로 변동되는 메타(description,
og:title, og:description, og:image, og:image:width) 제거.
Seo 컴포넌트가 담당
- src/pages/_app.tsx: <Head><title>을 <Seo title={pageTitle} />로 교체.
Component.title fallback을 'KOIN'에서 undefined로 변경하여
Seo의 DEFAULT_TITLE이 적용되도록 조정
Walkthrough새로운 ChangesSEO 메타데이터 인프라
🎯 2 (Simple) | ⏱️ ~12분 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning Review ran into problems🔥 ProblemsTimed out fetching pipeline failures after 30000ms Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. 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. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (4)
src/components/seo/Seo/index.tsx (3)
22-30: 💤 Low value함수 컴포넌트에 명시적 반환 타입을 추가하면 타입 안정성이 향상됩니다.
TypeScript best practice에 따라 컴포넌트의 반환 타입을 명시하는 것을 권장합니다.
♻️ 제안하는 수정
-export default function Seo({ +export default function Seo({ title, description = DEFAULT_DESCRIPTION, url, image = DEFAULT_OG_IMAGE, type = 'website', noindex = false, imageAlt, -}: SeoProps) { +}: SeoProps): JSX.Element {As per coding guidelines: Next.js, React, TypeScript 팀 코드 컨벤션 및 공식 스타일 가이드(React/TS best practices)를 우선적으로 반영하여, 가독성·안정성을 검토해주세요.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/seo/Seo/index.tsx` around lines 22 - 30, The Seo function component currently lacks an explicit return type; update the declaration of Seo to include a React return type (e.g., add ": JSX.Element" or ": React.ReactElement" after the parameter list) to improve type safety and clarity, ensuring the component signature (function Seo(...) : JSX.Element) still uses the existing SeoProps param and that any necessary React types are imported where used.
12-20: 💤 Low valueSeoProps 인터페이스를 export하면 재사용성이 향상됩니다.
다른 컴포넌트에서 SEO 관련 props 타입을 참조해야 할 경우를 대비해 인터페이스를 export하는 것을 고려해보세요.
♻️ 제안하는 수정
-interface SeoProps { +export interface SeoProps { title?: string; description?: string; url?: string; image?: string; type?: 'website' | 'article'; noindex?: boolean; imageAlt?: string; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/seo/Seo/index.tsx` around lines 12 - 20, The SeoProps interface is currently unexported which prevents reuse; update the declaration for SeoProps (the interface named SeoProps in this file) to be exported (e.g., export interface SeoProps ...) so other components can import and reuse the type, and then update any consumer files to import { SeoProps } from this module as needed.
33-33: ⚡ Quick wincanonical URL에서 해시 프래그먼트(#)도 제거해야 합니다.
현재 구현은 쿼리 파라미터(
?)만 제거하지만,router.asPath는 해시 프래그먼트(#section)도 포함할 수 있습니다. RFC에 따르면 canonical URL에는 프래그먼트를 포함하지 않아야 합니다.♻️ 제안하는 수정
- const canonicalUrl = buildAbsoluteUrl(url ?? router.asPath?.split('?')[0]); + const canonicalUrl = buildAbsoluteUrl(url ?? router.asPath?.split('?')[0]?.split('#')[0]);또는 더 명확하게:
- const canonicalUrl = buildAbsoluteUrl(url ?? router.asPath?.split('?')[0]); + const pathWithoutQuery = router.asPath?.split('?')[0] ?? ''; + const pathWithoutFragment = pathWithoutQuery.split('#')[0]; + const canonicalUrl = buildAbsoluteUrl(url ?? pathWithoutFragment);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/seo/Seo/index.tsx` at line 33, 현재 canonicalUrl 생성에서 쿼리만 제거하고 해시 프래그먼트(`#`)는 제거하지 않으므로 canonical에 프래그먼트가 포함될 수 있습니다; 수정하려면 Seo component의 canonicalUrl 계산(참조: canonicalUrl, buildAbsoluteUrl, router.asPath)을 변경해 router.asPath에서 쿼리와 해시를 모두 제거하도록 하거나 더 안전하게는 URL 파서(new URL(..., origin) 또는 URL constructor)를 사용해 search와 hash를 빈값으로 설정한 뒤 buildAbsoluteUrl에 전달하여 프래그먼트를 완전히 제거하세요.src/static/seo.ts (1)
10-15: ⚡ Quick win타입 안정성을 위해 명시적 반환 타입 추가를 권장합니다.
TypeScript best practice에 따라 함수의 반환 타입을 명시하면 코드 가독성과 타입 추론 성능이 향상됩니다.
♻️ 제안하는 수정
-export const buildAbsoluteUrl = (path?: string) => { +export const buildAbsoluteUrl = (path?: string): string => { if (!path) return KOIN_BASE_URL; if (/^https?:\/\//.test(path)) return path; const normalized = path.startsWith('/') ? path : `/${path}`; return `${KOIN_BASE_URL}${normalized}`; };As per coding guidelines: Next.js, React, TypeScript 팀 코드 컨벤션 및 공식 스타일 가이드(React/TS best practices)를 우선적으로 반영하여, 가독성·안정성을 검토해주세요.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/static/seo.ts` around lines 10 - 15, The function buildAbsoluteUrl lacks an explicit return type; update its signature to declare a return type of string (e.g. export const buildAbsoluteUrl = (path?: string): string => ...) and ensure any referenced constant like KOIN_BASE_URL is typed as string so the compiler can verify all return branches produce a string.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@src/components/seo/Seo/index.tsx`:
- Around line 22-30: The Seo function component currently lacks an explicit
return type; update the declaration of Seo to include a React return type (e.g.,
add ": JSX.Element" or ": React.ReactElement" after the parameter list) to
improve type safety and clarity, ensuring the component signature (function
Seo(...) : JSX.Element) still uses the existing SeoProps param and that any
necessary React types are imported where used.
- Around line 12-20: The SeoProps interface is currently unexported which
prevents reuse; update the declaration for SeoProps (the interface named
SeoProps in this file) to be exported (e.g., export interface SeoProps ...) so
other components can import and reuse the type, and then update any consumer
files to import { SeoProps } from this module as needed.
- Line 33: 현재 canonicalUrl 생성에서 쿼리만 제거하고 해시 프래그먼트(`#`)는 제거하지 않으므로 canonical에
프래그먼트가 포함될 수 있습니다; 수정하려면 Seo component의 canonicalUrl 계산(참조: canonicalUrl,
buildAbsoluteUrl, router.asPath)을 변경해 router.asPath에서 쿼리와 해시를 모두 제거하도록 하거나 더
안전하게는 URL 파서(new URL(..., origin) 또는 URL constructor)를 사용해 search와 hash를 빈값으로
설정한 뒤 buildAbsoluteUrl에 전달하여 프래그먼트를 완전히 제거하세요.
In `@src/static/seo.ts`:
- Around line 10-15: The function buildAbsoluteUrl lacks an explicit return
type; update its signature to declare a return type of string (e.g. export const
buildAbsoluteUrl = (path?: string): string => ...) and ensure any referenced
constant like KOIN_BASE_URL is typed as string so the compiler can verify all
return branches produce a string.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: b4bcedc8-fdd9-49e0-b470-0f06b1e32a91
📒 Files selected for processing (4)
src/components/seo/Seo/index.tsxsrc/pages/_app.tsxsrc/pages/_document.tsxsrc/static/seo.ts
ParkSungju01
left a comment
There was a problem hiding this comment.
metadata 방식으로 변경하니 SEO 관리가 더 명확해진 것 같네요. 수고하셨습니다!
What is this PR? 🔍
<Seo>컴포넌트 +_document정리)Changes 📝
수정 파일
호환성
ScreenShot 📷
N/A
Test CheckList ✅
yarn lint:eslintyarn tsc --noEmitPrecaution
✔️ Please check if the PR fulfills these requirements
developbranch unconditionally?main?yarn lint🤖 Generated with Claude Code
Summary by CodeRabbit
릴리스 노트