정의
서버에서 렌더링한 화면에서 클릭·제출 같은 상호작용이 여러 개 동시에 안 먹히면, 그건 보통 버그 여러 개가 아니라 클라이언트 JavaScript가 실행되지 못해 하이드레이션이 통째로 불발된 하나의 근본원인이다.
하이드레이션(hydration): 서버가 만든 정적 HTML에 브라우저에서 JS가 나중에 이벤트 리스너·상태를 붙여 "살아 움직이게" 만드는 과정. 이게 끝나기 전엔 버튼이 그림일 뿐이다. 번들이 파싱/실행 단계에서 죽으면 하이드레이션 자체가 시작·완료되지 못한다.
왜 필요한가
버그 리포트는 "토글이 안 돼요", "로그인 버튼이 안 돼요", "제출이 안 돼요"처럼 증상 여러 개로 온다. 이걸 버그 3개로 보고 각각 파고들면 시간을 낭비한다. 서버 렌더링(SSR) 앱에서 이 증상들의 공통점은 전부 이벤트 핸들러라는 것이다. 서버가 보낸 HTML은 멀쩡히 보이지만, 그 위에 이벤트를 붙이는 클라이언트 JS가 죽으면 화면의 모든 인터랙션이 한꺼번에 사라진다. 화면은 정상으로 보여서 더 헷갈린다.
동작 원리
번들 실행이 죽는 두 갈래:
- 파싱 실패: 옛 브라우저가 새 문법을 못 읽어
SyntaxError로 파일 전체가 중단된다. 첫 오류 이후 한 줄도 실행되지 않는다. - 런타임 실패: 없는 API를 호출해
TypeError가 나며 하이드레이션 도중 중단된다. 그 코드가 초기 실행 경로에 있으면 화면 전체 인터랙션이 죽는다.
이는 하이드레이션 불일치(서버·클라 마크업이 달라 React가 트리를 버리는 별개 사례)와 다르다. 불일치는 "실행은 되나 결과가 어긋남"이고, 여기서 말하는 것은 "실행 자체가 안 됨"이다.
실무 적용
- 증상 여러 개를 "버그 N개"가 아니라 "하나의 실행 실패"로 가설을 먼저 세운다.
- 콘솔 첫 에러의 종류로 가른다:
SyntaxError→ 문법(트랜스파일),TypeError: is not a function→ 없는 API(폴리필). - 원인 층을 고친다(문법은 다운레벨, 없는 API는 폴리필).
- 특정 브라우저/버전에서만 재현되면 호환성(문법·API) 원인일 확률이 높다.
트레이드오프
| 진단 접근 | 장점 | 비용 |
|---|---|---|
| 증상별 개별 디버깅 | 즉시 착수 | 공통 원인 놓치면 시간 낭비 |
| 공통 근본원인 가설 우선 | 한 번에 해결 | 초기 가설이 틀리면 우회 필요 |
사용하면 안 되는 경우
- 단 하나의 기능만 안 되는 경우 — 그건 진짜 개별 버그일 수 있으니 공통 원인 가설을 강요하지 않는다.
- 서버/클라 마크업 불일치 경고가 명확한 경우 — 이건 불발이 아니라 불일치이므로 처방이 다르다.
흔한 실수
- 화면이 보이니 "렌더는 됐다"며 하이드레이션을 배제하기.
- 증상마다 별도 티켓을 파서 같은 원인을 세 번 조사하기.
- 콘솔 첫 에러를 안 보고 UI만 만지기.
관련 개념
- syntax-transpilation-vs-runtime-polyfill — 번들을 죽이는 두 원인(문법·API)
- ssr-hydration-mismatch — 실행은 되지만 서버/클라가 어긋나는 별개 사례
- browser-compat-interaction-smoke — 죽은 핸들러를 실제로 잡는 테스트