화면에 보이지 않아 놓치기 쉬운 canonical, 메타데이터, Open Graph, sitemap, RSS를 Next.js App Router에서 어떻게 점검하고 고쳤는지 정리했습니다.
Ian3 min read8
화면 밖의 정보
플린랩스 홈페이지는 Next.js 14 App Router로 만들었습니다. 페이지 디자인과 애니메이션을 다듬는 데 시간을 많이 썼는데, 정작 검색엔진과 메신저 공유 미리보기가 읽는 정보는 한참 뒤에야 점검했습니다. 사람에게는 보이지 않으니 빠져도 티가 나지 않는 부분입니다.
점검해 보니 빠지거나 어긋난 곳이 다섯 군데 있었습니다. 하나씩 어떻게 고쳤는지 정리합니다.
빠진 canonical
canonical은 "이 페이지의 대표 주소는 이것"이라고 검색엔진에 알려 주는 태그입니다. 쿼리 문자열이나 끝의 슬래시만 다른 URL이 따로 수집되면, 같은 페이지끼리 검색 순위를 나눠 갖게 됩니다.
블로그 글에는 있었는데, 회사소개·서비스·기술·솔루션 페이지 13개에는 없었습니다. App Router에서는 루트 layout에 metadataBase를 한 번 지정하면 각 페이지는 상대 경로만 쓰면 됩니다.
홈 페이지도 손봤습니다. 제목과 설명을 따로 쓰지 않고 루트 layout의 기본값을 물려받고 있었습니다. 사이트 전체용 문구는 홈을 설명하기에 모호해서 검색엔진이 임의로 바꿔 보여줄 수 있습니다. 그래서 홈 전용 제목과 설명을 명시했습니다.
덮어쓰이는 openGraph
Next.js의 메타데이터는 layout에서 page로 내려가며 합쳐지지만, 합치는 깊이는 한 단계뿐입니다. 페이지가 openGraph를 선언하면 루트에 있던 openGraph 객체는 통째로 교체됩니다. 페이지에서 url만 바꾸려 했는데 사이트 이름, 타입, 공유 카드 이미지까지 함께 사라지는 식입니다.
title과 description은 일부러 넣지 않았습니다. 빠져 있으면 Next.js가 페이지의 값으로 채워 주기 때문입니다. 블로그 글은 shareCard(path, { type: 'article', images: [post.coverImage] })처럼 커버 이미지를 넘기고, 커버가 없는 글은 사이트 카드를 씁니다.
사이트 카드 자체도 바꿨습니다. 로고 하나만 있던 이미지를 next/og의 ImageResponse로 그린 1200×630 카드로 교체했습니다. 강조 바, 한 줄 소개, 로고 심볼이 들어갑니다. 코드로 그리기 때문에 문구를 바꿔도 디자인 툴을 열 필요가 없습니다. 글 페이지에는 BlogPosting JSON-LD도 함께 넣어 제목, 요약, 게시일을 구조화 데이터로 전달합니다.
빠짐없는 sitemap
sitemap은 검색엔진에 "수집할 페이지는 이것들"이라고 건네는 목록입니다. 기존 sitemap.ts에는 홈, 솔루션, 블로그, 문의 페이지만 있었고, 회사소개·서비스·기술 페이지 11개가 빠져 있었습니다. 블로그 글도 목록 API의 첫 페이지만 가져오고 있어서 글이 늘어나면 뒤쪽 글은 sitemap에서 빠질 구조였습니다.
정적 페이지는 changeFrequency와 priority를 붙여 모두 나열했고, 글은 전체 페이지 수를 확인한 뒤 나머지 페이지를 병렬로 가져옵니다.
API가 응답하지 않으면 빈 배열을 돌려줍니다. 글 목록을 못 가져와도 정적 페이지가 담긴 sitemap은 계속 나가야 하기 때문입니다.
RSS 피드
마지막은 /feed.xml입니다. Route Handler 하나로 RSS 2.0 문서를 만들고, revalidate = 3600으로 한 시간마다 다시 생성합니다. 제목과 요약은 XML 특수문자를 이스케이프하고, 피드 리더가 원본 주소를 알 수 있게 atom:link로 자기 자신을 가리킵니다. sitemap과 마찬가지로 API가 응답하지 않으면 빈 피드를 내보냅니다.
RSS는 예전보다 덜 쓰이지만, 피드 리더로 기술 블로그를 구독하는 개발자는 아직 있습니다. 만드는 데 드는 비용이 파일 하나라면, 기술 블로그에는 RSS를 기본으로 두는 게 맞다고 생각합니다.
마치며
다섯 가지 모두 수정 자체는 짧았습니다. 문제는 화면에 보이지 않으니 빠져도 아무도 모른다는 점입니다. 배포한 뒤에는 운영 주소에 직접 요청해서 확인했습니다.