본문으로 건너뛰기

SEO Path 기반 URL 전환

· 약 5분
jangsungyu
Frontend Developer
Claude Code
AI Assistant

📋 작업 개요

브랜치: feature/1.30.0-seo-path-url 작업 유형: SEO 개선 / 리팩토링 작업 기간: 2026-01-15 담당: Claude Code

🎯 작업 목표

포켓몬 상세 페이지의 메가진화, 리전폼 URL을 쿼리 파라미터 방식에서 Path 기반으로 전환하여 SEO 최적화

변경 전 URL 패턴

# 상세 페이지
/detail/6?activeType=mega
/detail/6?activeType=mega&activeIndex=1
/detail/19?activeType=region
/detail/25?activeIndex=1 # 기본폼 (폼체인지)

# 기술 페이지
/detail/19/moves?activeType=region
/detail/19/moves?activeType=region&activeIndex=1
/detail/25/moves?activeIndex=1 # 기본폼 기술 (폼체인지)

변경 후 URL 패턴

# 상세 페이지
/detail/6/mega
/detail/6/mega/1
/detail/19/region
/detail/25/form/1 # 기본폼 (폼체인지)

# 기술 페이지
/detail/19/moves/region
/detail/19/moves/region/1
/detail/25/moves/form/1 # 기본폼 기술 (폼체인지)

✨ 주요 변경사항

1. 라우트 구조 변경

변경 전:

src/app/detail/
├── [pokemonId]/
│ └── page.tsx # 쿼리 파라미터로 모든 케이스 처리

변경 후:

src/app/detail/
├── [pokemonId]/
│ ├── (form)/
│ │ ├── modules/
│ │ │ ├── parseFormParams.ts # URL 파라미터 파싱 모듈
│ │ │ ├── fetchDetailData.ts # 데이터 페칭 모듈
│ │ │ └── generateMetadata.ts # 메타데이터 생성 모듈
│ │ ├── form/
│ │ │ └── [[...index]]/
│ │ │ └── page.tsx # 기본폼 페이지 (폼체인지)
│ │ ├── mega/
│ │ │ └── [[...index]]/
│ │ │ └── page.tsx # 메가진화 페이지
│ │ ├── region/
│ │ │ └── [[...index]]/
│ │ │ └── page.tsx # 리전폼 페이지
│ │ └── page.tsx # 기본폼 페이지
│ ├── moves/
│ │ ├── page.tsx # 기본폼 기술 페이지
│ │ ├── form/
│ │ │ └── [[...index]]/
│ │ │ └── page.tsx # 기본폼 기술 페이지 (폼체인지)
│ │ └── region/
│ │ └── [[...index]]/
│ │ └── page.tsx # 리전폼 기술 페이지
│ └── opengraph-image.tsx
  • Route Groups (form)을 사용하여 기존 moves 라우트와의 충돌 해결
  • [[...index]] Optional Catch-all Segment로 formIndex 처리
  • 비즈니스 로직을 modules/ 폴더로 분리하여 코드 가독성 향상

2. 비즈니스 로직 모듈화

모듈역할
parseFormParams.tsURL path/query 파라미터에서 값 추출
fetchDetailData.tsactiveType별 최적화된 데이터 페칭
generateMetadata.tsSEO 메타데이터 생성

3. 쿼리 최적화

activeType별 필요한 데이터만 페칭하도록 최적화:

activeType페칭 데이터
normalnormalForm + versionGroup + normalFormImageList
megamegaEvolution 데이터만
regionregionForm + versionGroup

4. URL 파라미터 처리 방식 변경

  • activeType, activeIndex 쿼리 파라미터 → Path 세그먼트로 변경
  • shinyMode는 동일 콘텐츠의 다른 표현이므로 쿼리 파라미터 유지
  • 기본폼의 activeIndex는 쿼리 파라미터로 유지 (이미지 슬라이드 위치)

5. 컴포넌트 수정

  • Detail.context.tsx: 서버에서 activeType, activeIndex props로 전달받아 처리
  • 스위치 컴포넌트: Context에서 값을 가져와 Path 기반 URL 생성
  • 검색 결과 링크: Path 기반 URL 생성 로직 변경
  • 이미지 슬라이더: 슬라이드 변경 시 Path 기반 URL로 업데이트

6. 301 리다이렉트 설정

기존 쿼리 파라미터 URL → 새 Path URL로 영구 리다이렉트 (next.config.js)

// 예시 - 상세 페이지
/detail/6?activeType=mega → /detail/6/mega
/detail/6?activeType=mega&activeIndex=1/detail/6/mega/1
/detail/19?activeType=region → /detail/19/region
/detail/25?activeIndex=1/detail/25/form/1 // 기본폼 (폼체인지)

// 예시 - 기술 페이지
/detail/19/moves?activeType=region → /detail/19/moves/region
/detail/19/moves?activeType=region&activeIndex=1/detail/19/moves/region/1
/detail/25/moves?activeIndex=1/detail/25/moves/form/1 // 기본폼 기술 (폼체인지)

📊 SEO 개선 효과

항목개선 내용
크롤링 우선순위독립적인 페이지로 인식
인덱싱 확실성쿼리 파라미터 무시 가능성 제거
URL 가독성직관적인 URL 구조
키워드 노출URL에 mega, region 키워드 직접 노출

📊 코드 최적화 결과

항목변경 전변경 후
page.tsx 라인 수~388줄 (단일 파일)~120줄 (페이지당)
비즈니스 로직 분리페이지 컴포넌트에 혼재modules/ 폴더로 분리
데이터 페칭모든 데이터 한번에 페칭activeType별 최적화
코드 재사용성낮음높음 (모듈 공유)

🔧 기술적 세부사항

수정 파일 목록

파일변경 내용
src/app/detail/[pokemonId]/(form)/page.tsx신규 - 기본폼 페이지
src/app/detail/[pokemonId]/(form)/form/[[...index]]/page.tsx신규 - 기본폼 페이지 (폼체인지)
src/app/detail/[pokemonId]/(form)/mega/[[...index]]/page.tsx신규 - 메가진화 페이지
src/app/detail/[pokemonId]/(form)/region/[[...index]]/page.tsx신규 - 리전폼 페이지
src/app/detail/[pokemonId]/(form)/modules/parseFormParams.ts신규 - URL 파라미터 파싱
src/app/detail/[pokemonId]/(form)/modules/fetchDetailData.ts신규 - 데이터 페칭 로직
src/app/detail/[pokemonId]/(form)/modules/generateMetadata.ts신규 - 메타데이터 생성 로직
src/app/detail/[pokemonId]/page.tsx삭제
src/context/Detail.context.tsxactiveType, activeIndex props 추가
src/container/desktop/header/header.search/**/ResultListData.tsxPath URL 생성 (NORMAL_FORM 포함)
src/container/mobile/header/header.search/**/ResultListData.tsxPath URL 생성 (NORMAL_FORM 포함)
src/components/ability/PokemonByAbilityCard.component.tsxhref 객체 → 경로 문자열 변환
src/components/moves/PokemonBySkillCard.component.tsxhref 객체 → 경로 문자열 변환
src/app/sitemap.ts메가/리전 URL 경로 기반으로 변경
src/container/desktop/detail/detail.summary/components/MegaSwitch.component.tsxPath href 생성
src/container/desktop/detail/detail.summary/components/RegionSwitch.component.tsxPath href 생성
src/container/desktop/detail/detail.summary/components/ShinySwitch.component.tsxPath href 생성
src/container/mobile/detail/detail.summary/components/MegaSwitch.component.tsxPath href 생성
src/container/mobile/detail/detail.summary/components/RegionSwitch.component.tsxPath href 생성
src/container/mobile/detail/detail.summary/components/ShinySwitch.component.tsxPath href 생성
src/container/desktop/detail/detail.summary/summary.pokemonImage/PokemonImage.compoment.tsxPath URL 업데이트
src/container/mobile/detail/detail.summary/summary.pokemonImage/PokemonImage.compoment.tsxPath URL 업데이트
src/container/desktop/detail/detail.moves/moves.header/MovesHeader.container.tsx상세 정보 링크 Path 기반 생성
src/container/mobile/detail/detail.moves/moves.header/MovesHeader.container.tsx상세 정보 링크 Path 기반 생성
src/container/desktop/detail/detail.baseInfo/baseInfo.learnableSkill/LevelLearnableSkill.component.tsx기술 링크 Path 기반 생성
src/container/desktop/detail/detail.baseInfo/baseInfo.learnableSkill/MachineLearnableSkill.component.tsx기술 링크 Path 기반 생성
src/container/mobile/detail/detail.baseInfo/baseInfo.learnableSkill/LevelLearnableSkill.component.tsx기술 링크 Path 기반 생성
src/container/mobile/detail/detail.baseInfo/baseInfo.learnableSkill/MachineLearnableSkill.component.tsx기술 링크 Path 기반 생성
src/app/detail/[pokemonId]/moves/page.tsxregion, activeIndex 쿼리 파라미터 리다이렉트 추가
src/app/detail/[pokemonId]/moves/form/[[...index]]/page.tsx신규 - 기본폼 기술 페이지 (폼체인지)
src/app/detail/[pokemonId]/moves/region/[[...index]]/page.tsx신규 - 리전폼 기술 페이지
src/module/generateDetailSeoMetaData.ts캐노니컬 URL Path 기반 생성 (form 포함)
next.config.js301 리다이렉트 규칙 + 캐시 헤더 추가 (form 포함)

📝 향후 작업

  • 사이트맵 업데이트 검토 (메가진화/리전폼 URL 추가) ✅ 완료
  • Google Search Console에서 인덱싱 상태 모니터링

📌 참고 사항

  • shinyMode는 쿼리 파라미터로 유지 (동일 콘텐츠의 다른 표현)
  • 기존 URL이 검색엔진에 인덱싱되어 있을 수 있으므로 301 리다이렉트 필수
  • 기본폼 URL(/detail/{number})은 경로 변경 없음 - 리다이렉트 불필요
  • Route Groups (form)을 사용하여 URL에는 영향 없이 파일 구조 정리