무색
기술블로그
에세이
연구
소개

무색

소프트웨어로 비즈니스의 가능성을 만듭니다. 웹·앱 개발, 음성 AI, 자동화 콘텐츠 제작까지 — 기술이 필요한 곳에 무색이 있습니다.

연락처

contact@museck.com

사업자 정보

상호: 무색

대표: 배성재

사업자등록번호: 577-58-00836

인천광역시 연수구 인천타워대로 323, 에이동 8층 801-802호 AB-132 (송도동, 송도 센트로드)

© 2026 무색. All rights reserved.
개인정보처리방침·이용약관·연락처
INCHEON, KR
Next.js SEO: robots.ts ~ JSON-LD — bauhaus 키 비주얼
museck 만들기
2026. 2. 15.

Next.js App Router SEO 구현: robots.ts부터 JSON-LD까지 하루 만에

next.jsseojson-ldmetadata

museck.com을 배포하고 Google Search Console에 등록했는데, 상태가 처참했다. robots.txt 없음, sitemap.xml 없음, 메타데이터 없음, 구조화된 데이터 없음. 검색 엔진 입장에서는 이 사이트가 뭘 하는 건지 전혀 알 수 없는 상태였다. 하루 만에 SEO 인프라를 전부 구현한 기록이다.

출발점: SEO 제로

배포 직후의 상태를 정리하면 이렇다.

  • robots.txt — 없음. 크롤러에게 아무 안내도 없다.
  • sitemap.xml — 없음. 크롤러가 페이지를 직접 발견해야 한다.
  • og:image — 없음. SNS에 링크 공유하면 빈 카드가 나온다.
  • JSON-LD — 없음. 구조화된 데이터가 없어서 리치 결과(rich result) 불가.
  • title 태그 — 전부 동일. 모든 페이지가 같은 제목이다.

Next.js App Router의 메타데이터 API를 쓰면 이 문제들을 선언적으로 해결할 수 있다. 파일 기반으로 robots.txt, sitemap.xml을 자동 생성하고, 페이지별 메타데이터를 함수로 정의한다.

robots.ts: 크롤러 안내서

Next.js의 메타데이터 API로 robots.txt를 자동 생성한다. src/app/robots.ts 파일을 만들면 된다.

3줄이다. 모든 크롤러에게 전체 사이트를 허용하고, sitemap 위치를 알려준다.

sitemap.ts: 동적 사이트맵

정적 사이트맵은 금방 낡는다. 새 글을 쓸 때마다 수동으로 업데이트할 수는 없다. PayloadCMS에서 published 상태인 콘텐츠를 동적으로 읽어서 사이트맵을 생성한다.

posts, projects, pages 세 컬렉션을 동시에 조회한다. Promise.all()로 병렬 처리해서 속도를 챙겼다. slug: 'home'인 페이지는 루트 URL과 중복되므로 제외한다.

title.template: 일관된 제목 패턴

루트 레이아웃에 title.template을 설정하면 하위 페이지에서 제목만 지정해도 자동으로 패턴이 적용된다.

하위 페이지에서 title: '기술 블로그'만 설정하면 기술 블로그 | 무색으로 렌더링된다. 일일이 사이트명을 붙일 필요가 없다.

generateMetadata: 페이지별 동적 메타데이터

블로그 상세 페이지는 글마다 제목, 설명, 이미지가 다르다. generateMetadata 함수로 동적으로 생성한다.

OG 이미지가 있으면 SNS 공유 시 카드에 이미지가 표시된다. type: 'article'을 지정하면 Facebook/Twitter가 블로그 글로 인식한다.

JSON-LD: 구조화된 데이터

JSON-LD는 검색 엔진에게 페이지의 의미를 알려주는 구조화된 데이터다. Google의 리치 결과(작성자, 발행일, 이미지 등이 검색 결과에 표시되는 것)를 위해 필요하다.

세 가지 스키마를 적용했다.

  • 홈페이지: Organization — 회사 정보, 로고, 연락처
  • 블로그 상세: Article — 제목, 작성자, 발행일, 이미지
  • 포트폴리오 상세: CreativeWork — 프로젝트 제목, 설명, 완료일

JSON-LD는 <script type="application/ld+json"> 태그로 페이지에 삽입한다. Next.js에서는 generateMetadata 안에 넣거나 컴포넌트에서 직접 렌더링할 수 있다.

삽질 기록

sitemap에서 빌드 타임 DB 호출 실패

sitemap.ts에서 PayloadCMS를 호출하는데, Docker 빌드 시에는 DB가 없다. try-catch로 감싸고 실패하면 정적 URL만 반환하는 패턴을 적용했다. 런타임에는 DB에서 동적으로 읽는다.

home 슬러그 중복

pages 컬렉션에 slug: 'home'인 문서가 홈페이지다. 사이트맵에 /home과 /이 둘 다 들어가면 중복이다. home 슬러그는 사이트맵에서 제외하고 루트 URL만 남겼다.

검색엔진 인증 태그

Google Search Console과 Naver Search Advisor의 소유권 확인 메타 태그를 루트 레이아웃의 verification 필드에 추가했다. 환경변수로 관리해서 코드에 인증 코드가 노출되지 않도록 했다.

결과

하루 작업으로 SEO 인프라가 갖춰졌다. robots.txt, sitemap.xml, 페이지별 메타데이터, OG 이미지, JSON-LD까지. Google Search Console에서 사이트맵 제출이 성공했고, 색인 요청이 가능해졌다.

Next.js App Router의 메타데이터 API가 이걸 간단하게 만들어줬다. 파일 하나 만들면 robots.txt가 나오고, 함수 하나 정의하면 페이지별 OG 태그가 생긴다. 프레임워크의 컨벤션을 따르면 보일러플레이트가 최소화된다.

자주 묻는 질문

Next.js App Router에서 sitemap.xml을 동적으로 생성하려면 어떻게 하나요?
src/app/sitemap.ts 파일에서 PayloadCMS API로 published 콘텐츠를 조회한 뒤 URL 배열을 반환합니다. Promise.all()로 여러 컬렉션을 병렬 조회하면 속도도 챙길 수 있습니다.
블로그 글에 어떤 JSON-LD 스키마를 적용해야 하나요?
Article 스키마가 적합합니다. headline, author, datePublished, image 등을 포함하면 Google 검색 결과에 리치 스니펫으로 표시될 수 있습니다.
museck 만들기(12/22)
Prev

블로그 시리즈 시스템 구축: PayloadCMS 컬렉션 설계부터 탭 필터링 UI까지

Next

Next.js 16 + Tailwind v4 + React Compiler 메가 업그레이드: 한 번에 세 개 올리기