다크 모드
트러블슈팅
이 페이지는 @platform/vue-debugger를 이식할 때 가장 자주 마주치는 문제를 증상 → 원인 → 해결 순서로 정리합니다. 대부분의 증상은 "호출 순서"나 "환경 플래그"가 원인이며, 잘못된 동작이 아니라 켜야 할 스위치가 꺼져 있는 것입니다. 디버거는 모호한 상황을 침묵하지 않고 정보 탭에 정직하게 고지하도록 설계됐으니, 막히면 먼저 정보 탭을 확인하세요.
각 항목의 해결책에서 언급하는 옵션의 전체 명세는 옵션 레퍼런스에, 환경 플래그의 배경은 환경별 설정에 있습니다.
연동 단계의 문제
같은 코드라도 어디서·언제 호출했는지에 따라 결과가 갈립니다. 호출 순서만 맞추면 대부분 해결됩니다.
FAB(⚙)가 안 보입니다
우측 하단 톱니(⚙) 버튼이 나타나지 않으면, 디버거가 아예 붙지 않은 것입니다.
원인
attachDebugger를app.mount()뒤에 호출했습니다. mount 이후엔 디버거가 호스트 앱에 끼어들 자리를 놓칩니다.- 활성 분기(
import.meta.env.DEV || (import.meta.env.MODE === 'staging' && import.meta.env.VITE_DEBUGGER === 'on'))가 현재 환경에서true가 아닙니다.
해결
attachDebugger(app)을app.use(...)등록 뒤,app.mount('#app')앞에서 호출했는지 확인합니다.- 로컬이라면
vite dev로 띄웠는지(=import.meta.env.DEV가true인지) 확인합니다.
ts
import { createApp } from 'vue'
import App from './App.vue'
async function bootstrap() {
const app = createApp(App)
// app.use(createPinia()) / app.use(router) ... (있다면 먼저)
if (import.meta.env.DEV || (import.meta.env.MODE === 'staging' && import.meta.env.VITE_DEBUGGER === 'on')) {
const { attachDebugger } = await import('@platform/vue-debugger')
attachDebugger(app)
}
app.mount('#app') // ← attachDebugger는 반드시 이 줄 '앞'에서
}
bootstrap()동적 키 접근은 프로덕션 제거를 무력화합니다
활성 분기는 반드시 import.meta.env.VITE_DEBUGGER처럼 정적으로 써야 합니다. const f = 'VITE_DEBUGGER'; import.meta.env[f]처럼 동적으로 접근하면 Vite가 값을 정적 치환하지 못해, 디버거가 프로덕션 번들에 그대로 남습니다. FAB가 "프로덕션에서도 보이는" 반대 증상이 나타나면 이 패턴을 의심하세요.
단축키로도 토글됩니다
FAB를 가렸거나 못 찾겠다면 Alt+Shift+D로 패널을 토글하세요. 콘솔에서 window.__VUE_DEBUGGER__가 핸들을 반환하는지 확인하면 "안 붙음 vs 안 보임"을 빠르게 구분할 수 있습니다(null이면 비브라우저 환경이거나 attach 자체가 안 된 것입니다).
Pinia / Router 패널이 비어 있습니다
FAB는 보이는데 Pinia 또는 Router 탭에 아무것도 없으면, 디버거가 attach될 시점에 그 플러그인이 아직 앱에 등록되지 않았던 것입니다.
원인 — 디버거는 pinia/vue-router를 import하지 않고 런타임에 app에서 구조적으로 발견합니다(없어도 동작). attachDebugger를 app.use(pinia) / app.use(router) 앞에서 부르면 발견 시점에 대상이 없어 빈 상태가 됩니다.
해결 — app.use(...) 등록을 모두 마친 뒤에 attachDebugger를 호출하세요.
ts
async function bootstrap() {
const app = createApp(App)
app.use(createPinia()) // 먼저
app.use(router) // 먼저
attachDebugger(app) // 그다음 (자동 발견)
app.mount('#app')
}자동 발견이 끝내 실패한다면 (드묾)
비표준 부트스트랩 등으로 자동 발견이 안 되면 옵션으로 직접 주입할 수 있습니다. 보통은 불필요합니다.
ts
attachDebugger(app, { pinia, router })각 옵션의 구조적 타입(PiniaLike/RouterLike)은 옵션 레퍼런스를 보세요.
전역 라우터 가드가 Router 패널에 안 잡히는 건 별개 문제입니다
라우트별 beforeEnter는 자동으로 귀속되지만, **전역 beforeEach/beforeResolve**는 디버거 attach 이전에 등록되면 못 잡습니다. 이건 패널이 "비는" 게 아니라 "그 가드만 누락"되는 경우로, 라우터 가드 추적의 traceGuards(router)로 해결합니다.
배포(빌드) 환경의 문제
검증계·WebView 같은 "배포됐지만 디버깅이 필요한" 빌드에서는 로컬과 달리 두 개의 스위치(__VUE_PROD_DEVTOOLS__, sourcemap)를 명시적으로 켜야 합니다. 켜지 않으면 디버거는 붙지만 일부 데이터가 비어 보입니다.
컴포넌트 트리는 보이는데 값이 다 비거나 undefined입니다
컴포넌트 탭에 트리는 멀쩡히 뜨는데 State 값·출처 칩·식 평가 결과가 전부 비어 있다면, 거의 항상 컴파일 타임 바인딩 노출이 꺼진 빌드입니다.
원인 — __VUE_PROD_DEVTOOLS__를 켜지 않은 vite build 빌드에서는 @vitejs/plugin-vue가 <script setup>을 인라인 템플릿 ON으로 컴파일합니다. 그러면 setup이 렌더 함수만 반환하고 변수들이 클로저 안에 갇혀, 디버거가 바인딩을 들여다볼 수 없습니다. (vite dev는 이 플래그 없이도 바인딩이 노출되므로 로컬에선 멀쩡합니다.)
해결 — 검증계 모드에서 __VUE_PROD_DEVTOOLS__를 'true'로 정의합니다. 이 스위치가 인라인 템플릿을 끄고 setup이 바인딩을 __returned__로 노출하게 합니다.
ts
// vite.config.ts
export default defineConfig(({ mode }) => ({
define: mode === 'staging' ? { __VUE_PROD_DEVTOOLS__: 'true' } : {},
}))자세한 동작 원리와 진짜 프로덕션에 절대 켜면 안 되는 이유는 환경별 설정의 __VUE_PROD_DEVTOOLS__ 절을 보세요.
스택이 chunk-abc.js:1:2345처럼만 보입니다
에러·네트워크·스토리지·라우터의 호출 위치가 원본(LessonShell.vue:33)이 아니라 번들 위치로만 표시되면, 디버거가 원본을 복원할 소스맵을 찾지 못한 것입니다.
원인 — 디버거는 빌드 위치를 소스맵(.map)으로 원본 위치로 복원합니다. .map이 없으면 복원할 수 없어 chunk:line만 보입니다. 흔한 두 경우입니다.
build.sourcemap: false(기본) →.map이 아예 생성되지 않음.- 보안 정책으로 빌드 산출물에서
.map을 빼고 배포 → 디버거가 fetch할 대상이 없음.
해결 — 검증계 빌드에서 sourcemap: 'hidden'을 켜고, .map 파일을 (외부 공개망이 아닌) 검증계에 함께 둡니다.
ts
// vite.config.ts
export default defineConfig(({ mode }) => ({
build: { sourcemap: mode === 'staging' ? 'hidden' : false },
}))'hidden'은 .map을 생성하되 번들에 //# sourceMappingURL 주석을 남기지 않습니다. 디버거는 .map을 명시적으로 fetch해 쓰므로 정상 복원되고, 일반 사용자의 devtools엔 소스가 자동 노출되지 않습니다.
함수명은 종종 소실됩니다 — 정상입니다
minify된 빌드에서는 소스맵이 파일:줄은 줘도 함수명 매핑이 불완전한 경우가 많습니다. 디버거는 파일:줄 위주로 표시하고, 이 한계를 정보 탭의 '알려진 한계'에 고지합니다. 함수명이 빠져 보여도 위치만 맞으면 정상입니다.
.map은 외부에 공개 배포하지 마세요
소스맵은 원본 코드를 그대로 복원합니다. .map은 폐쇄망/사내 검증계에만 두고, 외부 공개망 배포에는 절대 포함하지 마세요. 진짜 프로덕션엔 __VUE_PROD_DEVTOOLS__·소스맵 공개를 함께 끕니다.
네트워크 / 래핑 충돌
페이지 안에서 동작하는 도구라 다른 관측 도구와 같은 표면(fetch·console)을 공유합니다. 노이즈와 충돌을 가려내면 "진짜 요청"만 남길 수 있습니다.
네트워크 탭이 APM·텔레메트리 비콘으로 가득합니다
요청 목록이 비즈니스 로직과 무관한 주기적 전송으로 도배되면, 사내 APM/모니터링 비콘이 섞인 것입니다.
원인 — 폐쇄망 APM(Datadog/New Relic/Sentry/자체 텔레메트리)은 보통 3~5초마다 폴링·비콘을 보내, 비즈니스 요청을 묻어 버립니다.
해결 — ignoreUrls로 그 패턴을 추적에서 제외합니다. 문자열은 부분일치, RegExp는 정규식으로 매칭합니다.
ts
attachDebugger(app, {
ignoreUrls: ['/apm', /datadog|newrelic|sentry/, '/telemetry', '/heartbeat'],
})리포트 수집 URL은 자동 제외됩니다
report.endpoint를 설정한 경우, 그 수집 URL은 네트워크 추적에서 자동으로 제외되므로 ignoreUrls에 따로 넣지 않아도 됩니다.
요청의 '호출 위치'가 비어 있습니다 (개발자 코드 프레임 못 찾음)
네트워크 행의 "이 요청을 보낸 코드 위치"에 개발자 프레임이 안 보이면, 보통 비동기 경계 너머에서 요청이 나간 것입니다.
원인 — .then()·setTimeout·이벤트 리스너 콜백에서 요청을 보내면, V8이 그 호출자를 스택에 남기지 않습니다(엔진 한계 — 어떤 도구도 사후 복원 불가). await 체인은 복원되지만 순수 .then/타이머는 끊깁니다.
해결 — 같은 행의 맥락(breadcrumb) 을 보세요. 콜스택이 끊겨도 "이 요청 직전에 무엇을 클릭/이동했나"가 시간순으로 남습니다. 어떤 경계가 복원되고 안 되는지는 8443 데모 /#/lab/callstack 에서 코드+버튼으로 직접 비교할 수 있습니다.
컴포넌트가 수백 개라 트리에서 못 찾습니다
컴포넌트 패널 트리가 너무 길어 원하는 컴포넌트를 못 찾을 때.
해결 — 트리 상단 🔍 검색으로 이름을 좁히고(매치 강조 + 조상 경로만), ▸/▾·**⊟/⊞**로 가지를 접으세요. 트리는 가상화되어 수천 개도 부드럽습니다. 데모: /#/lab/tree.
콘솔에 끌 수 없는 디버거 경고가 보입니다
⚠ @platform/vue-debugger ACTIVE는 의도된 트립와이어입니다. dev/검증계에선 정상이지만, production 콘솔에서 보이면 누출이니 즉시 보안·누출 차단의 게이트를 확인하세요.
호출 위치·타이밍이 어긋나 보입니다 (래핑 충돌)
콘솔이나 네트워크 항목의 호출 스택 위치가 실제와 미묘하게 어긋나면, 다른 도구가 같은 전역을 이미 감싼 상태입니다.
원인 — APM이나 폴리필 같은 다른 도구가 console·fetch를 먼저 래핑하면, 디버거가 보는 호출 위치·타이밍이 그 래퍼를 거치며 어긋날 수 있습니다.
해결 — 이 상황을 디버거는 숨기지 않고 정보 탭에 '래핑 충돌'로 정직하게 고지합니다. 정보 탭에 이 고지가 떠 있다면, 표시된 호출 위치를 절대값이 아니라 참고값으로 해석하세요. 가능하면 디버거를 다른 관측 도구의 초기화 이후에 붙여 충돌 표면을 줄일 수 있습니다.
위 항목으로 해결되지 않으면, 연동 절차 전체를 연동 가이드에서, 설치 정상 여부 점검은 설치 검증에서 다시 확인하세요. 환경 플래그 전반은 환경별 설정에 모여 있습니다.