Vue.js 미드레벨 코딩 면접

Vue.js 미드레벨 (3~7년) 코딩 · 알고리즘 5문항 조회수 120 · 2026-05-14 (목) 19:10:53
1 Composable 패턴
Medium

Q. Vue 3의 Composition API를 활용하여 무한 스크롤 기능을 재사용 가능한 Composable로 구현한다고 할 때, 어떤 상태와 로직을 포함해야 하는지 설계하고, IntersectionObserver API를 활용한 구현 방식과 메모리 누수를 방지하기 위한 cleanup 로직을 설명해주세요.

A. 모범답안

useInfiniteScroll composable은 isLoading, hasMore, items 등의 반응형 상태를 포함하고, target element를 감시하는 IntersectionObserver를 setup합니다. observer 콜백에서 isIntersecting이 true일 때 loadMore 함수를 호출하여 데이터를 fetch하고, onUnmounted 훅에서 observer.disconnect()를 호출하여 메모리 누수를 방지합니다. 또한 throttle이나 debounce를 적용하여 과도한 API 호출을 방지하고, error 상태와 retry 로직도 포함해야 합니다.

핵심 포인트
  • • IntersectionObserver를 활용한 viewport 진입 감지
  • • onUnmounted에서 observer.disconnect()로 리소스 정리
  • • loading, error, hasMore 등의 상태 관리
  • • throttle/debounce를 통한 API 호출 최적화
  • • 재사용 가능한 composable 인터페이스 설계
Follow-up 질문

만약 여러 개의 무한 스크롤 리스트가 같은 페이지에 있다면 각각의 IntersectionObserver 인스턴스를 어떻게 관리하시겠습니까?

2 성능 최적화
Hard

Q. 대규모 트리 구조 데이터(수천 개의 노드)를 렌더링하는 Vue 컴포넌트에서 노드 확장/축소 시 성능 문제가 발생합니다. v-show와 v-if의 선택 기준, 재귀 컴포넌트의 렌더링 최적화 방법, 그리고 shallowRef나 markRaw를 활용한 반응성 제어 전략을 설명해주세요.

A. 모범답안

자주 토글되는 노드는 v-show를 사용하여 DOM을 유지하고, 깊은 레벨의 노드는 v-if로 필요할 때만 렌더링합니다. 재귀 컴포넌트에는 key를 명시하고 KeepAlive로 감싸 캐싱하며, 각 노드 컴포넌트를 defineAsyncComponent로 lazy loading합니다. 트리 데이터 자체는 shallowRef로 감싸 중첩된 객체의 deep reactivity를 방지하고, 메타데이터는 markRaw로 표시하여 불필요한 프록시 생성을 막습니다. Virtual scrolling을 적용하여 보이는 영역의 노드만 렌더링하는 것도 효과적입니다.

핵심 포인트
  • • v-show와 v-if의 적절한 선택 (토글 빈도와 초기 렌더링 비용)
  • • shallowRef로 deep reactivity 방지
  • • markRaw로 메타데이터의 반응성 제거
  • • KeepAlive를 활용한 컴포넌트 캐싱
  • • Virtual scrolling 또는 windowing 기법 적용
Follow-up 질문

트리 노드가 동적으로 추가/삭제될 때 shallowRef를 사용하면 반응성이 작동하지 않는 문제를 어떻게 해결하시겠습니까?

3 상태 관리 패턴
Medium

Q. 여러 컴포넌트에서 공유되는 WebSocket 연결을 관리하는 로직을 구현해야 합니다. Composable 패턴을 사용할 때 싱글톤 인스턴스를 보장하는 방법, 연결 상태의 반응형 관리, 그리고 컴포넌트가 unmount될 때 연결을 유지할지 끊을지 판단하는 로직을 어떻게 설계하시겠습니까?

A. 모범답안

모듈 스코프에 WebSocket 인스턴스와 반응형 상태를 선언하여 싱글톤을 구현하고, useWebSocket composable은 이를 참조합니다. ref로 connectionStatus, lastMessage 등을 관리하고, 내부적으로 subscribers 카운터를 두어 여러 컴포넌트의 사용을 추적합니다. onMounted에서 카운터를 증가시키고 onUnmounted에서 감소시켜, 카운터가 0이 될 때만 연결을 종료합니다. reconnection 로직과 heartbeat를 포함하며, send 함수는 연결 상태를 확인 후 메시지를 전송하거나 큐에 저장합니다.

핵심 포인트
  • • 모듈 레벨 변수로 싱글톤 패턴 구현
  • • subscribers 카운팅으로 참조 관리
  • • 반응형 상태로 연결 상태와 메시지 관리
  • • 자동 재연결 및 heartbeat 로직
  • • 메시지 큐를 통한 연결 전 메시지 버퍼링
Follow-up 질문

같은 앱에서 서로 다른 WebSocket 엔드포인트에 연결해야 한다면 이 패턴을 어떻게 확장하시겠습니까?

4 동적 컴포넌트
Hard

Q. 사용자 권한과 설정에 따라 다른 컴포넌트를 동적으로 로드하고 렌더링하는 플러그인 시스템을 구현해야 합니다. component :is를 사용한 동적 컴포넌트 렌더링, defineAsyncComponent의 에러 처리, 그리고 플러그인 간 통신을 위한 이벤트 버스나 provide/inject 패턴의 설계 방법을 설명해주세요.

A. 모범답안

플러그인 레지스트리를 Map으로 관리하고, 각 플러그인은 defineAsyncComponent로 lazy loading하며 loading/error 컴포넌트를 지정합니다. component :is에 동적으로 선택된 컴포넌트를 바인딩하고, KeepAlive로 감싸 상태를 유지합니다. 플러그인 간 통신은 provide/inject로 플러그인 컨텍스트를 제공하고, mitt 같은 경량 이벤트 에미터를 inject하여 사용합니다. 각 플러그인은 manifest 객체로 메타데이터(권한, 의존성)를 정의하고, 플러그인 로더가 이를 검증한 후 로드합니다.

핵심 포인트
  • • defineAsyncComponent로 플러그인 lazy loading
  • • loading/error 상태 처리를 위한 fallback 컴포넌트
  • • Map 기반 플러그인 레지스트리 관리
  • • provide/inject로 플러그인 컨텍스트 공유
  • • 이벤트 에미터를 통한 플러그인 간 통신
  • • manifest 기반 권한 및 의존성 검증
Follow-up 질문

플러그인이 로드에 실패했을 때 fallback 플러그인을 자동으로 로드하는 체인 메커니즘을 어떻게 구현하시겠습니까?

5 리팩토링
Medium

Q. 레거시 Vue 2 프로젝트에서 Vuex를 사용하고 있고, mutations에 비즈니스 로직이 섞여있으며, actions에서 다른 모듈의 state를 직접 참조하는 안티패턴이 있습니다. Vue 3와 Pinia로 마이그레이션하면서 이를 개선하는 리팩토링 전략을 단계별로 설명해주세요.

A. 모범답안

먼저 Vuex mutations의 비즈니스 로직을 actions로 추출하고, state 변경만 mutations에 남깁니다. 모듈 간 의존성은 actions에서 다른 모듈의 actions를 dispatch하도록 변경합니다. Pinia 마이그레이션 시 각 Vuex 모듈을 독립적인 store로 변환하고, mutations와 actions를 하나의 actions로 통합합니다. 다른 store 참조는 useOtherStore()를 호출하여 명시적으로 가져오고, computed getters는 Pinia의 getters로 변환합니다. 공통 로직은 별도의 service 레이어나 composable로 분리하여 재사용성을 높입니다.

핵심 포인트
  • • 비즈니스 로직을 mutations에서 actions로 추출
  • • 모듈 간 의존성을 명시적 store 참조로 변경
  • • Vuex 모듈을 독립적인 Pinia store로 변환
  • • mutations와 actions를 단일 actions로 통합
  • • 공통 로직을 service 레이어로 분리
Follow-up 질문

마이그레이션 중에 Vuex와 Pinia를 동시에 사용해야 한다면, 두 상태 관리 시스템 간 데이터 동기화를 어떻게 처리하시겠습니까?

댓글 0

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

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