Vue.js 미드레벨 트러블슈팅 면접

Vue.js 미드레벨 (3~7년) 트러블슈팅 7문항 조회수 28 · 2026-08-27 (목) 22:11:45
1 성능 최적화
Medium

Q. Vue 3 애플리케이션에서 사용자가 특정 페이지에 접속하면 브라우저가 멈추고 '응답 없음' 상태가 되는 문제가 프로덕션에서 발생했습니다. 로컬 환경에서는 문제가 없었지만, 실제 사용자 데이터(약 5만 건)를 사용하는 프로덕션에서만 발생합니다. 이 문제를 진단하고 해결하기 위한 단계별 접근 방법을 설명해주세요.

브라우저가 멈추는 것은 메인 스레드가 블로킹되었다는 의미이며, 대량의 데이터와 관련이 있습니다.

A. 모범답안

먼저 Chrome DevTools의 Performance 탭에서 프로파일링을 수행하여 어느 함수에서 시간이 오래 걸리는지 확인합니다. Long Task가 발생하는 지점을 식별하고, 해당 코드가 동기적으로 대량의 데이터를 처리하는지 확인합니다. 일반적으로 computed나 watch에서 5만 건의 배열을 필터링하거나 정렬하는 로직이 원인일 가능성이 높습니다. 해결 방법으로는 Virtual Scrolling을 적용하여 실제로 렌더링되는 항목을 제한하거나, Web Worker로 무거운 연산을 이동시키거나, requestIdleCallback을 활용하여 작업을 청크 단위로 분할하는 방법이 있습니다. 또한 shallowRef를 사용하여 불필요한 깊은 반응성을 제거하고, Object.freeze()로 읽기 전용 데이터의 반응성을 비활성화할 수 있습니다.

핵심 포인트
  • • Performance 탭을 통한 Long Task 식별
  • • 대량 데이터 처리 로직의 동기 실행이 메인 스레드 블로킹 원인
  • • Virtual Scrolling, Web Worker, 작업 분할로 해결
  • • shallowRef와 Object.freeze()를 통한 반응성 최적화
답변에 넣으면 좋은 키워드
Performance Profiling Long Task Virtual Scrolling Web Worker shallowRef Object.freeze
실무에서는

대시보드나 관리자 페이지에서 대량의 데이터를 테이블로 표시할 때 자주 발생하는 문제입니다.

Follow-up 질문

Virtual Scrolling을 적용할 수 없는 상황이라면 어떤 대안적인 최적화 방법을 사용하시겠습니까?

2 메모리 관리
Hard

Q. Vue SPA 애플리케이션에서 사용자가 여러 페이지를 탐색한 후 특정 페이지로 돌아오면 이전보다 렌더링 속도가 느려지고, 장시간 사용 후 브라우저 탭이 크래시되는 문제가 발생했습니다. Memory 탭에서 힙 스냅샷을 비교한 결과 Detached DOM nodes가 계속 증가하고 있습니다. 이 문제의 원인을 찾고 해결하는 과정을 설명해주세요.

Detached DOM nodes는 DOM에서 제거되었지만 JavaScript에서 여전히 참조되고 있는 노드들입니다.

A. 모범답안

Detached DOM nodes가 증가하는 것은 컴포넌트가 unmount되었음에도 이벤트 리스너, 타이머, Observer 등이 정리되지 않아 DOM 참조가 유지되는 메모리 누수 상황입니다. Memory 탭의 Comparison 뷰에서 페이지 이동 전후 스냅샷을 비교하여 어떤 객체가 증가했는지 확인합니다. 주요 원인으로는 onBeforeUnmount에서 removeEventListener를 호출하지 않은 경우, setInterval이나 setTimeout을 clearInterval/clearTimeout으로 정리하지 않은 경우, IntersectionObserver나 MutationObserver를 disconnect하지 않은 경우가 있습니다. 또한 전역 이벤트 버스나 window 객체에 직접 등록한 이벤트도 원인이 될 수 있습니다. 해결을 위해서는 Composable에서 onBeforeUnmount 훅을 활용하여 모든 구독과 리스너를 명시적으로 정리하고, AbortController를 사용하여 fetch 요청을 취소하며, effectScope를 활용하여 watch와 computed를 그룹으로 관리하고 한 번에 정리할 수 있습니다.

핵심 포인트
  • • Detached DOM nodes는 메모리 누수의 신호
  • • 이벤트 리스너, 타이머, Observer의 미정리가 주요 원인
  • • onBeforeUnmount에서 명시적인 cleanup 필요
  • • effectScope와 AbortController를 활용한 체계적 정리
답변에 넣으면 좋은 키워드
Detached DOM 메모리 누수 onBeforeUnmount effectScope AbortController removeEventListener
실무에서는

지도 라이브러리, 차트 라이브러리, 리얼타임 데이터 구독 등을 사용하는 SPA에서 흔히 발생합니다.

Follow-up 질문

third-party 라이브러리가 메모리 누수를 일으키는 경우 어떻게 대응하시겠습니까?

3 상태 관리
Medium

Q. Pinia 스토어를 사용하는 Vue 3 애플리케이션에서 특정 액션을 실행하면 다른 컴포넌트의 데이터가 예상치 못하게 변경되는 문제가 발생했습니다. 개발자 도구에서 확인해보니 여러 컴포넌트가 같은 스토어 인스턴스를 참조하고 있지만, state의 일부 속성이 직접 수정되고 있었습니다. 이러한 상태 변이 추적과 디버깅을 어떻게 진행하시겠습니까?

Pinia는 개발 모드에서 상태 변경을 추적할 수 있는 devtools 통합 기능을 제공합니다.

A. 모범답안

먼저 Vue Devtools의 Pinia 탭에서 Timeline 기능을 활성화하여 모든 상태 변경을 시간순으로 추적합니다. 각 mutation의 before/after 스냅샷을 비교하여 어느 액션에서 예상치 못한 변경이 발생했는지 확인합니다. 만약 액션 외부에서 직접 state를 수정하는 코드가 있다면, Pinia의 strict mode를 활성화하여 개발 중 경고를 받을 수 있습니다. 또한 $subscribe 메서드를 사용하여 특정 스토어의 모든 변경사항을 로깅하고, storeToRefs를 사용하지 않고 직접 구조분해 할당을 하여 반응성을 잃은 경우도 확인합니다. 근본적인 해결을 위해서는 state를 직접 수정하지 않고 항상 actions를 통해서만 변경하도록 팀 컨벤션을 정립하고, TypeScript의 readonly 타입을 활용하여 컴파일 단계에서 직접 수정을 방지할 수 있습니다.

핵심 포인트
  • • Vue Devtools Timeline으로 상태 변경 추적
  • • $subscribe로 변경사항 로깅
  • • storeToRefs 미사용으로 인한 반응성 손실 확인
  • • actions를 통한 상태 변경 강제화
답변에 넣으면 좋은 키워드
Pinia Timeline $subscribe storeToRefs strict mode 상태 변이 추적
실무에서는

여러 개발자가 협업하는 프로젝트에서 상태 관리 규칙이 명확하지 않을 때 자주 발생합니다.

Follow-up 질문

여러 스토어가 서로 의존하면서 순환 참조가 발생하는 경우 어떻게 리팩토링하시겠습니까?

4 라우팅
Medium

Q. Vue Router를 사용하는 애플리케이션에서 사용자가 페이지를 이동할 때 간헐적으로 화면이 깜빡이거나 이전 페이지의 데이터가 잠깐 보이는 문제가 발생합니다. 특히 비동기로 데이터를 로드하는 페이지에서 자주 발생하며, 네트워크가 느린 환경에서 더 심합니다. 이 문제의 원인과 해결 방법을 설명해주세요.

라우트 전환 시점과 데이터 로딩 시점의 타이밍 문제를 고려해보세요.

A. 모범답안

이 문제는 라우트 전환이 먼저 일어나고 새 페이지 컴포넌트가 마운트된 후에 데이터를 로드하기 때문에 발생합니다. 컴포넌트가 마운트되면서 이전 상태나 빈 상태가 잠깐 렌더링되는 것입니다. 해결 방법으로는 먼저 Navigation Guard의 beforeEnter나 beforeResolve에서 필요한 데이터를 미리 로드하고 route.meta에 저장한 후 컴포넌트에서 사용하는 방법이 있습니다. 또는 Suspense 컴포넌트와 async setup을 활용하여 데이터 로딩이 완료될 때까지 fallback UI를 표시할 수 있습니다. KeepAlive를 사용하여 이전에 방문한 페이지의 상태를 캐싱하는 방법도 있지만, 이 경우 데이터 신선도 문제를 고려해야 합니다. 로딩 상태를 명시적으로 관리하는 전역 로딩 인디케이터를 구현하여 사용자 경험을 개선할 수도 있습니다.

핵심 포인트
  • • 라우트 전환과 데이터 로딩 타이밍 불일치가 원인
  • • Navigation Guard에서 데이터 선로딩
  • • Suspense와 async setup 활용
  • • KeepAlive로 상태 캐싱하되 신선도 관리 필요
답변에 넣으면 좋은 키워드
Navigation Guard beforeResolve Suspense async setup KeepAlive 데이터 선로딩
실무에서는

상세 페이지나 대시보드처럼 API 호출이 필요한 페이지로 이동할 때 자주 발생합니다.

Follow-up 질문

여러 페이지에서 공통으로 사용하는 데이터를 효율적으로 프리로드하는 전략은 무엇입니까?

5 반응성 시스템
Hard

Q. Vue 3 애플리케이션에서 외부 라이브러리(예: Chart.js)를 reactive 객체로 감싼 후 사용하고 있는데, 차트 업데이트 시 성능이 급격히 저하되고 콘솔에 Maximum call stack size exceeded 에러가 간헐적으로 발생합니다. 이 문제의 원인을 분석하고 해결 방법을 제시해주세요.

외부 라이브러리 객체를 reactive로 감싸는 것이 적절한지 고려해보세요.

A. 모범답안

이 문제는 Chart.js와 같은 외부 라이브러리 인스턴스를 reactive로 감쌀 때 발생하는 전형적인 문제입니다. reactive는 객체의 모든 중첩된 속성을 Proxy로 감싸기 때문에, 복잡한 내부 구조를 가진 라이브러리 객체에 적용하면 엄청난 수의 Proxy가 생성되어 성능 저하를 일으킵니다. 또한 라이브러리 내부에서 순환 참조 구조가 있을 경우 Vue의 반응성 시스템이 무한 루프에 빠질 수 있습니다. 해결 방법으로는 markRaw를 사용하여 해당 객체를 반응성 추적에서 제외하거나, shallowRef를 사용하여 객체 참조만 추적하고 내부 속성은 추적하지 않도록 합니다. 차트 인스턴스는 ref나 shallowRef에 저장하되 markRaw로 감싸고, 차트 업데이트는 명시적으로 .value를 통해 접근하여 수행합니다. 또한 차트 데이터만 별도의 reactive 상태로 관리하고, 데이터 변경 시 watch를 통해 차트 인스턴스의 update 메서드를 호출하는 패턴이 안전합니다.

핵심 포인트
  • • 외부 라이브러리 객체를 reactive로 감싸면 과도한 Proxy 생성
  • • 순환 참조로 인한 무한 루프 가능성
  • • markRaw나 shallowRef로 반응성 추적 제한
  • • 데이터와 인스턴스를 분리하여 관리
답변에 넣으면 좋은 키워드
markRaw shallowRef reactive Proxy 외부 라이브러리 순환 참조
실무에서는

Chart.js, D3.js, Three.js 같은 복잡한 외부 라이브러리를 Vue 컴포넌트에 통합할 때 발생합니다.

Follow-up 질문

DOM 엘리먼트를 ref에 저장할 때도 markRaw를 사용해야 하는 이유는 무엇입니까?

6 비동기 처리
Medium

Q. Vue 컴포넌트에서 사용자가 검색어를 입력하면 API를 호출하여 자동완성 결과를 표시하는 기능을 구현했습니다. 그런데 사용자가 빠르게 타이핑하면 이전 요청의 응답이 나중에 도착하여 최신 검색어와 맞지 않는 결과가 표시되는 Race Condition 문제가 발생합니다. 이 문제를 해결하는 여러 방법을 설명해주세요.

요청의 순서와 응답의 순서가 다를 수 있다는 점을 고려하세요.

A. 모범답안

Race Condition 문제는 비동기 요청의 완료 순서가 발생 순서와 다를 때 발생합니다. 첫 번째 해결 방법은 각 요청마다 고유한 토큰이나 타임스탬프를 부여하고, 응답이 도착했을 때 가장 최신 요청인지 확인하는 것입니다. 두 번째는 AbortController를 사용하여 새 요청이 발생하면 이전 요청을 취소하는 방법입니다. watch에서 검색어가 변경될 때마다 이전 controller.abort()를 호출하고 새 AbortController를 생성합니다. 세 번째는 debounce를 적용하여 사용자가 타이핑을 멈춘 후 일정 시간이 지나야 요청을 보내도록 하여 불필요한 요청 자체를 줄이는 방법입니다. 가장 견고한 방법은 AbortController와 debounce를 함께 사용하는 것이며, VueUse의 useDebounceFn과 useAsyncState를 조합하면 쉽게 구현할 수 있습니다. 또한 요청 시퀀스 번호를 관리하여 응답이 도착했을 때 최신 시퀀스인지 확인하는 방법도 있습니다.

핵심 포인트
  • • 비동기 응답 순서 불일치로 인한 Race Condition
  • • AbortController로 이전 요청 취소
  • • debounce로 불필요한 요청 감소
  • • 요청 시퀀스 번호나 타임스탬프로 최신성 검증
답변에 넣으면 좋은 키워드
Race Condition AbortController debounce 시퀀스 번호 useDebounceFn
실무에서는

검색 자동완성, 실시간 필터링, 무한 스크롤 등에서 자주 발생하는 문제입니다.

Follow-up 질문

여러 개의 독립적인 API를 병렬로 호출하면서 하나라도 실패하면 전체를 롤백해야 하는 경우 어떻게 처리하시겠습니까?

7 SSR/Hydration
Hard

Q. Nuxt 3 애플리케이션을 배포한 후 특정 페이지에서 Hydration mismatch 경고가 발생하고, 클라이언트에서 전체 컴포넌트가 다시 렌더링되면서 화면이 깜빡이는 문제가 발생했습니다. 서버 렌더링된 HTML과 클라이언트 렌더링 결과가 다른 원인을 찾고 해결하는 과정을 설명해주세요.

서버와 클라이언트 환경의 차이가 렌더링 결과에 영향을 줄 수 있습니다.

A. 모범답안

Hydration mismatch는 서버에서 생성한 HTML과 클라이언트에서 생성한 Virtual DOM이 일치하지 않을 때 발생합니다. 주요 원인으로는 첫째, 현재 시간이나 랜덤 값처럼 서버와 클라이언트에서 다른 값을 생성하는 경우입니다. 이는 서버에서 생성한 값을 상태로 저장하고 클라이언트에서 재사용해야 합니다. 둘째, localStorage나 document 같은 브라우저 전용 API를 서버에서 접근하려고 하는 경우로, onMounted나 ClientOnly 컴포넌트를 사용하여 클라이언트에서만 실행되도록 해야 합니다. 셋째, 외부 라이브러리가 서버와 클라이언트에서 다르게 동작하는 경우입니다. 디버깅을 위해서는 브라우저 개발자 도구의 콘솔에서 정확히 어느 엘리먼트에서 mismatch가 발생했는지 확인하고, 해당 컴포넌트의 조건부 렌더링 로직을 점검합니다. suppressHydrationWarning 속성으로 경고를 숨기는 것은 근본적인 해결책이 아니며, 반드시 원인을 찾아 수정해야 합니다. Nuxt의 경우 process.server와 process.client를 활용하여 환경별로 다른 로직을 실행할 수 있습니다.

핵심 포인트
  • • 서버와 클라이언트의 렌더링 결과 불일치가 원인
  • • 시간, 랜덤값, 브라우저 API 사용이 주요 원인
  • • ClientOnly 컴포넌트나 onMounted로 클라이언트 전용 로직 분리
  • • suppressHydrationWarning은 임시방편일 뿐 근본 해결 아님
답변에 넣으면 좋은 키워드
Hydration mismatch SSR ClientOnly process.server onMounted Virtual DOM
실무에서는

Nuxt나 SSR을 사용하는 프로젝트에서 날짜/시간 표시, 사용자 맞춤 콘텐츠, A/B 테스트 등을 구현할 때 발생합니다.

Follow-up 질문

third-party 스크립트(광고, 분석 도구)가 DOM을 직접 수정하여 Hydration 문제를 일으키는 경우 어떻게 대응하시겠습니까?

댓글 0

로그인 후 댓글을 작성할 수 있습니다.

아직 댓글이 없습니다. 첫 번째 댓글을 남겨보세요!