클릭했는데 반응이 없는 것처럼 보이는 문제
동적 route의 RSC 응답이나 아직 끝나지 않은 prefetch를 기다리면 URL과 화면이 바로 바뀌지 않을 수 있습니다. 이때 page 전체 spinner를 먼저 띄우면 어떤 link를 눌렀는지 알기 어렵고, 빠른 이동에서도 짧게 깜빡일 수 있습니다. useLinkStatus 이동 상태는 실제로 선택한 link 가까이에서만 피드백을 주는 방법입니다.
useLinkStatus는 <Link>의 descendant component에서 해당 link의 pending 상태를 제공하며, destination이 이미 prefetch됐다면 pending feedback이 생략될 수 있습니다.
Link 안에서 pending을 읽는 흐름
useLinkStatus는 Link를 렌더링하는 component에서 바로 호출하는 hook이 아닙니다. 반드시 Link 아래의 별도 descendant component에서 호출해야 해당 link의 context를 읽습니다. 표시 영역의 폭을 미리 확보하고 100ms 정도 늦게 보이게 하면 빠른 이동에서는 문구가 나타나지 않아 layout shift와 깜빡임을 줄일 수 있습니다.
"use client";
import Link, { useLinkStatus } from "next/link";
function PendingHint() {
const { pending } = useLinkStatus();
return (
<span className={`link-status ${pending ? "is-pending" : ""}`} role="status">
{pending ? "여는 중" : ""}
</span>
);
}
export function NoteLink({ slug, title }: { slug: string; title: string }) {
return (
<Link href={`/notes/${slug}`} prefetch={false}>
<span>{title}</span>
<PendingHint />
</Link>
);
}
.link-status { display: inline-block; min-inline-size: 3.5rem; opacity: 0; }
.link-status.is-pending { animation: reveal 160ms 100ms forwards; }
@keyframes reveal { to { opacity: 1; } }
loading.tsx와 역할을 나누는 기준
useLinkStatus는 route loading UI를 대체하지 않습니다. 클릭한 control의 즉시 피드백은 link status가 맡고, destination의 실제 구조를 유지하는 fallback은 loading.tsx가 맡습니다. 이미 prefetch된 route에서는 pending이 건너뛰어질 수 있으므로 이를 analytics나 저장 완료 판정에 사용하면 안 됩니다. production build에서 빠른 cache hit, 느린 network, 연속 link 클릭을 각각 확인하고 마지막으로 선택한 link만 pending인지 검증합니다. motion을 줄이도록 설정한 환경에서는 pulse animation을 제거하되 상태 문구는 유지합니다.
공식 문서
useLinkStatus는 Next.js 15.3.0에 추가됐으므로 현재 설치 version과 App Router 사용 여부를 확인합니다.