블로그 URL 마이그레이션에서 검색 순위 보존하기
목차
블로그 포스트의 URL 구조를 /{slug} 에서 /p/{post_sn} 로 변경하면서, 기존 링크들을 301 리다이렉트로 연결했다. 미미해 보이지만 이 작업이 없었으면 SEO 관점에서 큰 손실이 있었을 작업이었다.
URL 구조 변경의 배경
팀에서 블로그 시스템을 다시 설계하면서 포스트 고유 ID 기반의 URL 구조로 옮기기로 했다. 이전에는 /{slug} 형태로 글의 제목을 URL에 직접 넣었는데, 여러 문제가 있었다. 제목을 수정할 때마다 URL이 바뀐다든지, 한글 slug 처리의 복잡함, 그리고 무엇보다 post_sn 같은 고정된 식별자로 URL을 만드는 게 더 안정적이라는 판단이었다.
하지만 발행된 글들은 이미 외부(SNS, 블로그 링크, 검색 결과)에 /{slug} 형태로 널리 퍼져 있었다. 만약 이 링크들이 그냥 404로 떨어진다면?
SEO와 사용자 경험의 교차점
여기서 중요한 게 301 리다이렉트다. HTTP 상태코드 301은 "영구적으로 이동했다"는 뜻인데, 검색 엔진은 이 신호를 받으면 기존 URL의 SEO 랭킹을 새 URL로 그대로 이전해준다. 반면 302 임시 이동이나 자동 리프레시라면 검색 엔진이 구분하지 못하고 시간이 지나면서 래밍킹이 떨어진다.
내가 이 작업을 할 당시 팀에서 거론했던 옵션들:
| 방식 | 장점 | 단점 | SEO 영향 |
|---|---|---|---|
| 301 redirect | 검색 랭킹 유지, 사용자 투명 | 서버 처리 필요 | 최고 |
| 302 임시 이동 | 빠른 구현 | 랭킹 손실 위험 | 낮음 |
| 404 후 재색인 | 깨끗한 정리 | 유입 수개월 손실 | 최악 |
| Meta canonical | URL 정리 | 리다이렉트 아님, 실제 이동 불가 | 약함 |
결국 301을 선택했다. 사용자가 예전 링크를 따라와도 자동으로 새 URL로 이동되고, 검색 엔진도 당신의 "인기도"를 그대로 인식한다.
구현: app/main.py의 302 패턴
코드상으로는 간단하다. Flask 같은 프레임워크라면:
@app.route('/<slug>')
def legacy_post_redirect(slug):
# slug를 post_sn으로 매핑하는 로직
post_sn = lookup_post_sn_by_slug(slug)
if post_sn:
return redirect(f'/p/{post_sn}', code=301)
return abort(404)
핵심은 code=301 이다. 그냥 redirect(...) 하면 기본이 302이기 때문에 명시적으로 301을 지정해야 한다.
이 라우트가 하는 일:
- 레거시 URL 패턴(/{slug})을 받으면
- 데이터베이스나 캐시에서 이 slug에 해당하는 post_sn을 찾아
- /p/{post_sn}으로 301 리다이렉트
회고: "작은" 결정이 큰 임팩트인 이유
이 작업을 하면서 깨달은 건, URL 구조 같은 기술 결정이 검색 트래픽과 직결된다는 거였다. 개발 팀은 보통 내부 코드 구조만 생각하는데, 실제로는 검색 엔진과 사용자 북마크, 공유된 링크 모두가 당신의 URL과 연결되어 있다.
특히 블로그처럼 시간이 지날수록 가치가 있는 콘텐츠를 다루는 서비스라면 더 그렇다. 기존 글의 SEO 랭킹은 시간과 백링크의 축적인데, 그걸 한 번에 날려먹을 수 있다.
비슷한 작업을 다시 만난다면:
- URL 구조 변경 전에 마이그레이션 계획을 먼저 세운다
- 301 리다이렉트 구현과 함께 검색 엔진 콘솔(Google Search Console 등)에 변경을 신고한다
- 당분간 모니터링해서 실제로 리다이렉트가 잘 되고 있는지 확인한다
- 너무 많은 레거시 URL이 남으면 일정 기간 후 정리하되, 그 과정도 301로 단계적으로 한다
작은 HTTP 상태코드 하나가 팀의 트래픽과 사용자 경험을 얼마나 크게 바꿀 수 있는지 배운 작업이었다.
🛒 이 글과 어울리는 추천 상품
*위 링크는 쿠팡파트너스 활동의 일환이며, 일정액의 수수료를 제공받을 수 있습니다.
댓글 0
첫 댓글 달아줘.