본문 바로가기

주소 이동 (커스텀 리다이렉트) 연동

어드민(admin.roottale.com)의 설정 > 주소 이동에서 운영자가 정의한 임의 경로 이동 규칙(/old-event → /promo)을 사이트 Proxy로 적용합니다. 코드 수정 없이 고객이 직접 규칙을 추가·수정·삭제할 수 있습니다.

두 종류의 주소 이동이 있습니다.

  • 글 주소 변경 자동 301 — 블로그 글의 슬러그를 바꾸면 자동으로 옛 주소가 새 주소로 이어집니다. 글 라우트에서 처리되며 별도 설정이 필요 없습니다 (blog.md 의 postRedirectPath 참고).
  • 커스텀 리다이렉트(이 문서) — 글이 아닌 임의 경로를 옮깁니다. 라우팅 이전 단계인 Proxy에서만 가로챌 수 있어, 아래 설정이 필요합니다.

글의 공개 주소가 바뀌면(slug 변경·분류 이동·모델 규칙 변경) 플랫폼이 옛 경로 → 현재 경로 301 을 자동으로 이 목록에 더합니다(id 가 path-history: 로 시작). 운영자가 같은 출발 경로 규칙을 만들었으면 운영자 규칙이 이깁니다. Proxy를 쓰고 있다면 별도 작업 없이 옛 링크가 새 주소로 갑니다.

규칙은 GET /v1/cms/public/redirects 로 내려오며 활성 규칙만 포함됩니다 (api-reference.md). 출발 경로는 정규화된 사이트 내부 절대 경로, 도착지는 내부 경로 또는 절대 URL, 상태는 301(영구) 또는 302(임시)입니다.

Proxy 설정

@roottale/cms-renderer-next 의 createRedirectMiddleware 를 프로젝트 루트 proxy.ts 에 마운트합니다. 규칙을 자동 캐시(기본 60초)하며, API 실패 시 기존 캐시가 있으면 오래된 규칙을 우선 사용하고(stale-first), 캐시가 없으면 트래픽을 막지 않고 통과시킵니다(fail-soft).

// proxy.ts
import { NextResponse } from "next/server";
import { createRedirectMiddleware } from "@roottale/cms-renderer-next/routes";

const redirects = createRedirectMiddleware({
  apiKey: process.env.ROOTTALE_API_KEY!,
  // apiBase, siteId, cacheTtlMs 는 선택.
});

export async function proxy(req: Request) {
  return (await redirects(req)) ?? NextResponse.next();
}

// Next 내부·API·알려진 정적 자산만 제외합니다. 점 포함 경로를 전부 막으면
// `/old.html`·`/foo.php` 같은 레거시 마이그레이션 리다이렉트가 동작하지
// 않으므로, 자산 확장자만 명시적으로 제외합니다.
export const config = {
  matcher: [
    "/((?!_next/|api/|.*\\.(?:ico|png|jpg|jpeg|gif|svg|webp|css|js|txt|xml|json|woff2?|map)$).*)",
  ],
};
  • ROOTTALE_API_KEY 는 블로그 조회와 같은 키입니다. 서버 전용 — 절대 브라우저에 노출하지 마세요.
  • 매칭은 정확 경로 일치입니다(와일드카드 없음). 출발 경로의 앞/뒤 슬래시와 한글 percent-encoding 차이는 자동 정규화해 비교합니다.
  • 도착지가 내부 경로면 요청 origin 기준 절대 URL 로 변환해 리다이렉트합니다.
  • 내부 경로 체인은 최종 도착지로 평탄화하고, 순환이 발견되면 브라우저 왕복을 막기 위해 해당 요청을 통과시킵니다.
  • 매칭이 없으면 null 을 반환하므로 NextResponse.next() 로 통과시키세요.

Site Materializer로 새 사이트를 만들면 proxy.ts와 tests/redirect-proxy.test.ts가 필수 산출물로 포함됩니다. 두 파일은 내부 Materializer 영수증에도 기록되므로, 설치 여부를 추측하지 않고 실제 납품 산출물로 확인할 수 있습니다.

캐시와 즉시성

규칙은 Proxy가 TTL(기본 60초) 동안 캐시합니다. 운영자가 규칙을 바꾸면 최대 TTL 만큼 뒤 반영됩니다. 더 빠른 반영이 필요하면 cacheTtlMs 를 줄이세요 (요청당 API 호출이 늘어납니다).

const redirects = createRedirectMiddleware({
  apiKey: process.env.ROOTTALE_API_KEY!,
  cacheTtlMs: 10_000, // 10초
});

직접 호출 (Proxy 없이)

규칙 목록만 필요하면 fetchRedirects 로 직접 가져올 수 있습니다.

import { fetchRedirects } from "@roottale/cms-client/server";

const rules = await fetchRedirects({ apiKey: process.env.ROOTTALE_API_KEY! });
// [{ id, fromPath, toTarget, statusCode }]