링크 hover가 클릭 전에 URL을 보여줄 수 있는 이유
React render-time href evaluation
난이도: ★★☆☆☆
연관 노트: 모듈 상단 상수에 쿠키를 읽으면 왜 안 되는가
핵심 요약
buildExternalUrl("/")은 컴포넌트 렌더 시점에 실행되어 반환값이 <a> 태그의 href 속성에 문자열로 박힌다. hover는 클릭 전이지만 브라우저는 이미 DOM에 있는 href를 읽어 상태바에 보여줄 뿐이다.
왜 헷갈렸나
“hover인데 어떻게 URL을 미리 보여줄 수 있지?”라고 물었다.
hover 시점에 무언가 실행되는 게 있다고 생각했음. 하지만 브라우저 상태바 표시는 <a> 엘리먼트의 기본 동작으로, DOM에 이미 박힌 href를 읽는 것이다.
그리고 주소창과 상태바를 혼동했다. 둘 다 URL을 보여주지만 전혀 다른 것이다.
주소창 vs 상태바
| 주소창 (Address Bar) | 상태바 (Status Bar) | |
|---|---|---|
| 위치 | 브라우저 상단 | 브라우저 하단 (일시적) |
| 보여주는 것 | 현재 페이지 URL | hover 중인 링크의 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>엘리먼트 명세 —hrefattribute는 정적 문자열. 브라우저가 hover 시 이를 읽어 표시
→ 확장 개념 (여기서 더 나아가면)
- SSG hydration mismatch — SSG 빌드 시 서버에서
href가 결정되고, 클라이언트 hydration 시 값이 달라지면 React가 경고를 내고 DOM을 업데이트함 - 모듈 상단 상수에 쿠키를 읽으면 왜 안 되는가 — “언제 실행되느냐”가 결과를 결정한다는 동일한 원리
↔ 같은 원리가 적용되는 곳
- A/B 실험 노출이 버튼 클릭이 아닌 페이지 렌더에 발화되는 이유 — JSX 평가 시점에 side effect가 실행되는 동일한 패턴
- 이벤트 핸들러 vs 속성값 —
onClick={fn}(클릭 시 실행) vshref={fn()}(렌더 시 실행)의 구분