Next.js 개발하다 보면 <a/> 태그 대신 <Link/> 태그 많이 쓰시죠?
다들 명확한 이유를 아시나요?
구글링을 하다보면 “새로고침이 안 되니까”, “내부 라우팅이라서” 이렇게만 알고 넘어가는 경우가 많죠...그게 사실 저입니다..ㅎㅎ
그러다 회사에서 사이드바 리팩토링을 하면서 정말 코드 레벨로 정리할 필요가 생겼고,
Next.js 공식 GitHub로 들어가 Link 컴포넌트에 대해서 한번 정리해 봤습니다.
a태그 클릭 → 전체 페이지를 다시 요청 → React 상태 모두 초기화
Link 태그 클릭 → 클라이언트 라우팅 → 새로고침 없음 → 상태 유지
a 태그 → 외부 링크용
Link 태그 → Next.js 내부 페이지 이동용
Link 태그 → router.push/replace로 History API를 직접 다룸 → 즉, 앞/뒤 모두 정상 동작
a 태그 → 전체 리로드라 히스토리 구조도 달라진다.
제가 하는 프로젝트 사이드바는 2중 구조에 조건 처리, 열리고/닫히는 UI 로직이 많은 상황이었습니다.
그래서 리스트마다 단순하게 Link 태그만 씌우기가 애매했고,
아래처럼 버튼 + router.push() 방식으로 처리하고 있었죠.
<button onClick={() => router.push(child.href)}>
{child.title}
</button>
문제는…
Next.js는 dev 모드에서 페이지 이동할 때마다
전부 새로 컴파일 → 새로 빌드 → 트랜스폼이 일어나니까
router.push 로직으로는 페이지 전환이 너무 답답했어요.
그래서 “미리 관련 파일을 받아오자!” 라는 생각으로 리팩토링을 진행했습니다.

const prefetched = new Set<string>()
즉, 중복 요청 자동 방지 구조를 가지고 있습니다~
type PrefetchOptions = RouterPrefetchOptions & {
bypassPrefetchedCheck?: boolean
}
bypassPrefetchedCheck: true의 의미는
→ 기존 캐시와 무관하게 강제로 prefetch
→ hover 시 항상 최신 데이터를 위해 사용
function prefetch(router, href, as, options) {
if (typeof window === 'undefined') return
if (!isLocalURL(href)) return
router.prefetch(href, as, options).catch(err => {
if (process.env.NODE_ENV !== 'production') {
throw err
}
})
}
이 파트가 조금 재미있는 코드였습니다…(개인적인 생각)
정확히 두가지의 타이밍입니다.
const [setRef, visible] = useIntersection({ rootMargin: '200px' })
useEffect(() => {
if (visible && prefetch !== false) {
prefetch(...)
}
}, [visible])
화면에 해당 Link가 보이면 → 사용자가 곧 클릭할 가능성이 높기 때문
미리 JS 번들, SSG JSON을 받음
즉, 스크롤만 해도 필요한 페이지를 미리 받아 놓음
prefetch(router, href, as, {
priority: true,
bypassPrefetchedCheck: true
})
hover 시에는 무조건 작동할수있게 priority를 올려서 급하게 받습니다.
아닙니다!!!
prfetch는 아래와 같이
| 항목 | prefetch 유무 |
|---|---|
| JS 번들 | ✔️ |
SSG(getStaticProps) | ✔️ |
SSR(getServerSideProps) | ❌ |
SSR 데이터는 달라지는 요소가 많아 prefetch를 하지않는것 같습니다.
(event.metaKey || event.ctrlKey || event.shiftKey || ...)
(target !== '_self')
위에 상황은 아래와 같은 경우입니다.
이러면 next는 가로채지 않습니다. 그냥 a태그처럼 동작을 진행함
const handleClick = (e) => {
if (isModifiedEvent(e)) return
if (!isLocalURL(...)) return
e.preventDefault()
router.push(...)
}
쉽게 말해, browser navigation → Next.js 클라이언트 navigation으로 변환
Link태그는 단순 새로고침이 안일어나는 컴포넌트다~가 아닙니다!