Separating Landing Performance from Auth Logic with a Dedicated Redirect Route

랜딩 페이지 설계: 빠른 첫 화면과 정확한 분기, 둘 다 잡기

랜딩 페이지는 사용자가 서비스를 처음 만나는 지점이다.
그래서 두 가지 요구를 동시에 만족해야 한다.

  1. 최대한 빠르게 보여야 한다.
  2. CTA 클릭 시 사용자 상태에 맞는 정확한 목적지로 이동해야 한다.

문제는 이 두 요구가 자주 충돌한다는 점이다.
랜딩에서 바로 세션을 조회해 분기하면 목적지 결정은 쉬워지지만, 랜딩을 정적으로 유지하기 어려워진다.
반대로 랜딩을 완전 정적으로 두면 CTA 이후 분기 로직을 별도로 설계해야 한다.

해결 아이디어: CTA는 무조건 /me로 보낸다

핵심은 책임 분리다.

  • 랜딩(/)은 정적 UI만 담당
  • /me는 서버 리다이렉트 전용 엔드포인트로 동작

이렇게 하면 랜딩은 끝까지 빠르게 유지하면서, 사용자 상태 기반 분기는 /me 한 곳에서 일관되게 처리할 수 있다.

tsx
"use client";

import Link from "next/link";
import { useRouter } from "next/navigation";

export default function CTAButton() {
  const router = useRouter();

  return (
    <Link
      href="/me"
      prefetch={false}
      onMouseEnter={() => router.prefetch("/me")}
    >
      Start for free
    </Link>
  );
}

/me에서 목적지를 단일 책임으로 처리

사용자가 CTA를 누르면 항상 /me로 이동하고, /me에서 세션/온보딩/핸들을 순차 판별해 최종 리다이렉트한다.

  • 세션 없음 → /sign-in
  • 세션 있음 + 온보딩 미완료 → /onboarding
  • 온보딩 완료 + 정상 핸들 → /${handle}
  • 핸들 없음/비정상 → /onboarding
tsx
import { redirect } from "next/navigation";

export default async function MePage() {
  const session = await getSession();

  if (!session) redirect("/sign-in");

  const onboardingComplete = isOnboardingComplete(session.user.userMetadata);
  if (!onboardingComplete) redirect("/onboarding");

  const primaryPage = await findPrimaryPageHandleByUserId(session.user.id);

  if (!primaryPage?.handle?.startsWith("@")) {
    redirect("/onboarding");
  }

  redirect(`/${primaryPage.handle}`);
}

이 구조의 핵심은 명확하다.
랜딩 렌더링인증 기반 목적지 결정을 분리해, 각각의 관심사를 섞지 않는다.

프리패치 전략 개선

Next.js Link는 기본 자동 prefetch를 수행한다.
하지만 /me는 단순 정적 라우트가 아니라 세션 조회와 분기 판단이 있는 서버 페이지다.
자동 prefetch를 그대로 두면 사용자가 CTA를 누르지 않아도 인증 관련 선행 요청이 발생할 수 있다.

그래서 다음 전략이 더 합리적이다.

  • 기본 prefetch 비활성화 (prefetch={false})
  • 클릭 가능성이 높아지는 시점(예: hover)에만 수동 prefetch

이렇게 하면 불필요한 선행 요청을 줄이면서도, 실제 클릭 직전 체감 속도는 유지할 수 있다.

트레이드오프와 운영 포인트

이 패턴에는 트레이드오프도 있다.

  • 라우팅 홉 증가: / → /me → 최종 경로
  • 인증/온보딩 분기 로직이 /me에 집중
  • 따라서 /me의 안정성과 테스트가 중요

그럼에도 랜딩 성능(정적 유지)과 정책 관리 일관성(분기 단일화)을 함께 확보한다는 점에서 충분히 합리적인 선택이다.

결론

CTA → /me 패턴은 “빠른 랜딩”과 “정확한 목적지 분기”의 충돌을 풀기 위한 실용적인 구조다.
랜딩은 정적으로 가볍게 유지하고, 상태 기반 의사결정은 /me에서 중앙집중 처리한다.
결과적으로 성능, 유지보수성, 정책 일관성을 동시에 가져갈 수 있다.