Link는 단순한 링크보다 더 많은 일을 해
<Link>는 주소를 바꾸는 데서 끝나지 않아. 클라이언트 전환을 사용하고, 목적 라우트를 미리 가져오며, 공유 레이아웃의 상태를 보존해. 앱 내부 이동에는 기본적으로 Link를 써. 평범한 <a>를 쓰면 문서 전체를 다시 읽으면서 지금까지 유지하던 상태도 사라져.
코드로 이동할 때는 실행 위치를 구분해
클라이언트 컴포넌트에서는 next/navigation의 useRouter()를 사용해 push, replace, refresh를 호출할 수 있어. 서버 컴포넌트나 서버 액션에서는 hook을 쓸 수 없으므로 redirect()로 응답 흐름을 끝내.
| 도구 | 돌려주는 값 | 사용 위치 |
|---|---|---|
useRouter() | push, replace, refresh 같은 메서드 | 클라이언트 |
usePathname() | 현재 pathname 문자열 | 클라이언트 |
useSearchParams() | URLSearchParams | 클라이언트 |
useSelectedLayoutSegment() | 현재 레이아웃의 활성 자식 구간 | 클라이언트 |
버튼보다 링크가 맞는 이동도 있어
사용자가 새 탭으로 열거나 주소를 복사할 수 있어야 한다면 이동의 의미는 링크야. 클릭 처리기에서 무조건 router.push()를 호출하면 브라우저의 기본 기능과 접근성을 잃기 쉬워. 명령의 결과로 이동해야 할 때만 프로그래밍 방식의 탐색을 골라.
이동 요소가 브라우저의 링크 의미를 가져야 하는지 먼저 판단해. 새 탭·복사·검색 노출이 필요하면 Link, 제출 성공처럼 명령의 결과라면 redirect()나 router 메서드가 맞아. 앱 안의 모든 <a>를 기계적으로 바꾸기 전에 외부 URL과 다운로드 링크는 별도 규칙으로 남겨. prefetch가 빠르다고 모든 링크를 미리 읽으면 데이터와 네트워크 예산을 낭비할 수 있어. 긴 목록이나 민감한 동적 라우트에서는 실제 hover·viewport 조건과 캐시 정책을 확인해. 내비게이션 성능은 클릭 뒤 속도뿐 아니라 클릭하지 않은 경로에 쓴 비용까지 포함해.
내부 Link, 일반 <a>, router.push, 서버 redirect를 같은 목적지에 적용해 network와 layout mount를 비교해. 새 탭·주소 복사·뒤로 가기가 필요한 요소가 버튼으로 구현되지 않았는지 접근성 트리도 확인하고, 긴 목록의 prefetch 요청 수를 측정해.