소스는 A 머신에, 저는 B 머신에 있습니다. tailnet으로 이어져 있어서 A에서 npm run dev 를 띄우고 B 브라우저로 http://<A의 IP>:3000 을 엽니다. 몇 달째 이렇게 일해 왔습니다.
새 페이지를 만들었습니다. 서버 컴포넌트가 데이터를 넘기고, 클라이언트 컴포넌트가 캔버스에 애니메이션을 그립니다. 열어봤더니 완전히 빈 화면이었습니다.
조용한 증상들
하나씩 확인했습니다. 전부 정상이었습니다.
SSR HTML <canvas width="1080" height="1920" …> 있음
스토어 링크 2개도 있음
콘솔 에러 0건. React DevTools 안내 로그만 정상 출력
네트워크 페이지 200
서버 로그 GET /t/… 200 in 347ms요소는 DOM에 있습니다. 접근성 트리로 읽어도 나옵니다. 에러는 없습니다. 그런데 화면은 비어 있습니다.
여기서 당신이라면 어디를 의심하겠습니까?
제가 의심한 것들
순서대로 이렇게 갔습니다.
캔버스가 배경색과 같아서 안 보이는 건가? 배경이 #0B0B14 이고 캔버스 기본색도 같았습니다. 그럴듯했지만, 그러면 스토어 배지는 왜 안 보이나. (배지는 검은색이라 진짜로 어두운 배경에 묻혀 있었습니다. 두 개의 다른 원인이 같은 증상을 만들고 있었습니다.)
비동기 자산 로딩이 안 끝나는 건가? 지도 이미지를 기다리는 코드가 있었습니다. 확인해보니 503을 0.5초 만에 돌려주고 있었습니다. 아니었습니다.
requestAnimationFrame 이 백그라운드 탭에서 스로틀되나? 이건 실제로 참이었지만, 그것만으로는 첫 프레임조차 안 그려지는 걸 설명 못 합니다.
계속 브라우저만 봤습니다. 증상이 브라우저에 있었으니까요.
증거가 있던 곳
포기하고 dev 서버 로그를 봤습니다. 거기 이렇게 적혀 있었습니다.
⚠ Blocked cross-origin request to Next.js dev resource
/_next/webpack-hmr from "100.x.y.z".
Cross-origin access to Next.js dev resources is blocked by default for safety.
To allow this host in development, add it to "allowedDevOrigins"…Next 16 의 dev 서버는 다른 호스트에서 오는 /_next/* 요청을 기본으로 막습니다. 클라이언트 번들이 안 내려옵니다. 그래서 하이드레이션이 통째로 건너뛰어집니다.
서버 컴포넌트는 서버에서 렌더되니 HTML은 멀쩡합니다. 클라이언트 컴포넌트도 SSR 결과는 나옵니다. 다만 마운트가 안 되니 useEffect 가 영원히 안 돕니다. 캔버스에 그리는 코드가 전부 거기 있었습니다.
고치는 건 세 줄입니다.
const nextConfig: NextConfig = {
reactStrictMode: true,
allowedDevOrigins: ["100.x.y.z", "192.168.x.y"],
};왜 이렇게 오래 걸렸나
이 실패의 성질이 고약합니다.
정상 신호가 너무 많습니다. HTML도 있고, 200도 뜨고, React 도 로드됩니다. "React 가 로드됐다"는 사실이 "React 가 마운트했다"로 자동 번역되는데, 그게 틀렸습니다.
에러가 우리 쪽에 없습니다. 차단은 dev 서버가 하고, 그 결정을 브라우저에 알려주지 않습니다. 브라우저 입장에서는 그냥 스크립트 몇 개를 못 받은 것이고, 그건 콘솔 에러가 아닙니다.
증상과 원인이 다른 프로세스에 있습니다. 저는 증상이 보이는 쪽만 계속 봤습니다. 답이 서버 로그에만 있던 건 전에도 있었습니다 — 서치콘솔이 404 메일을 퍼부었다. 로그를 보니 범인은 내 코드가 아니었다.
자가진단 3가지
- "화면이 비었다"를 만나면 브라우저 콘솔보다 dev 서버 로그를 먼저 보세요. 특히 원격 호스트로 접속하는 개발 환경이라면.
- 하이드레이션이 실제로 됐는지 확인할 방법이 있습니까?
console.log를 이펙트 첫 줄에 한 줄 넣으면 5초면 갈립니다. SSR HTML 의 존재는 마운트의 증거가 아닙니다. localhost가 아닌 주소로 개발 서버를 열고 있습니까? 그렇다면 보안 컨텍스트도 함께 잃습니다 — 저는 같은 환경에서 WebCodecs 가 통째로 사라진 것도 겪었습니다. 두 문제 다localhost로 터널링하면 사라집니다.
솔직한 부분
이건 제 코드 버그가 아니었습니다. 그래서 더 오래 걸렸습니다 — 저는 계속 제가 뭘 잘못 썼는지 찾고 있었으니까요.
결국 갈라준 건 이펙트 첫 줄에 넣은 console.log 한 줄이었습니다. 로그가 안 찍히는 걸 보고서야 "코드가 틀린 게 아니라 아예 안 돈다"로 방향이 바뀌었습니다. 진작 했으면 30분은 아꼈을 겁니다.
당신의 "안 되는데 에러가 없다"는, 정말 에러가 없는 겁니까 아니면 에러를 다른 프로세스가 삼킨 겁니까?