링크 hover가 클릭 전에 URL을 보여줄 수 있는 이유

React render-time href evaluation

ReactBrowserRendering

난이도: ★★☆☆☆
연관 노트: 모듈 상단 상수에 쿠키를 읽으면 왜 안 되는가


핵심 요약

buildExternalUrl("/")은 컴포넌트 렌더 시점에 실행되어 반환값이 <a> 태그의 href 속성에 문자열로 박힌다. hover는 클릭 전이지만 브라우저는 이미 DOM에 있는 href를 읽어 상태바에 보여줄 뿐이다.


왜 헷갈렸나

“hover인데 어떻게 URL을 미리 보여줄 수 있지?”라고 물었다.
hover 시점에 무언가 실행되는 게 있다고 생각했음. 하지만 브라우저 상태바 표시는 <a> 엘리먼트의 기본 동작으로, DOM에 이미 박힌 href를 읽는 것이다.

그리고 주소창과 상태바를 혼동했다. 둘 다 URL을 보여주지만 전혀 다른 것이다.


주소창 vs 상태바

주소창 (Address Bar)상태바 (Status Bar)
위치브라우저 상단브라우저 하단 (일시적)
보여주는 것현재 페이지 URLhover 중인 링크의 href
언제 바뀌나실제 페이지 이동 시링크 hover 시
항상 보이는가항상hover 중에만
┌────────────────────────────────────────────────┐
│ 주소창: example.com/service/detail/             │  ← 지금 보고 있는 페이지
│                                                 │
│  < 링크에 마우스 올림 >                          │
│                                                 │
├────────────────────────────────────────────────┤
│ external.example.com/?utm_campaign=test         │  ← 상태바: href 미리보기
└────────────────────────────────────────────────┘

상태바의 역할: 클릭 전에 “이 링크를 누르면 어디로 가는지” 확인. 피싱 방어 목적이기도 하다 — 텍스트는 naver.com인데 href가 딴 곳을 가리키는 걸 사용자가 알아챌 수 있게.


메커니즘

컴포넌트 렌더
  └─ buildExternalUrl("/") 실행
       └─ 쿠키 읽기 → "https://external.example.com/?utm_campaign=test" 반환
  └─ <a href="https://external.example.com/?utm_campaign=test"> 로 DOM에 삽입

사용자 hover
  └─ 브라우저가 DOM의 href attribute를 읽음
  └─ 상태바에 표시 — 이 시점에 아무것도 실행되지 않음

onClick 핸들러는 클릭 시점에 실행되지만, href 속성값은 렌더 시점에 이미 결정된다.


해결 패턴

// href는 렌더 시 결정 → hover로 미리 확인 가능
<NextLink href={buildExternalUrl("/")}>프라임 이동</NextLink>

// router.push는 클릭 시 실행 → hover로 확인 불가
<button onClick={() => router.push(buildExternalUrl("/"))}>프라임 이동</button>

테스트 시 <a href> 방식이면 hover로 UTM 포함 여부를 미리 검증할 수 있다.
router.push 방식은 클릭 후 Network 탭에서 확인해야 한다.


다음에 이 상황을 만나면

“링크가 올바른 URL로 이동하는지 빠르게 검증하고 싶다”

  • <a href> 방식이면 hover로 상태바 확인
  • router.push 방식이면 DevTools → 해당 엘리먼트 우클릭 불가 → 클릭 후 Network에서 Navigation 요청 확인
  • DOM에서 직접 확인: document.querySelectorAll('a[href*="target"]')[0]?.href

커넥팅 닷

← 선행 개념 (이걸 알아야 이해된다)

  • React 렌더 사이클 — JSX 표현식은 렌더 시 평가된다. {fn()} 은 렌더마다 실행
  • HTML <a> 엘리먼트 명세href attribute는 정적 문자열. 브라우저가 hover 시 이를 읽어 표시

→ 확장 개념 (여기서 더 나아가면)

  • SSG hydration mismatch — SSG 빌드 시 서버에서 href가 결정되고, 클라이언트 hydration 시 값이 달라지면 React가 경고를 내고 DOM을 업데이트함
  • 모듈 상단 상수에 쿠키를 읽으면 왜 안 되는가 — “언제 실행되느냐”가 결과를 결정한다는 동일한 원리

↔ 같은 원리가 적용되는 곳