JavaScript 미드레벨 프레임워크 심화 면접
새 면접Q. React나 Vue 같은 프레임워크에서 사용하는 Virtual DOM의 동작 원리와 Reconciliation(재조정) 알고리즘을 설명하고, Virtual DOM이 실제 DOM 조작보다 빠른 이유와 오히려 느려질 수 있는 상황을 실무 관점에서 설명해주세요.
Virtual DOM은 실제 DOM의 가벼운 JavaScript 객체 표현으로, 상태 변경 시 새로운 Virtual DOM 트리를 생성하고 이전 트리와 비교(diffing)하여 변경된 부분만 실제 DOM에 반영합니다. Reconciliation은 key prop을 활용해 리스트 요소를 효율적으로 추적하고, 같은 타입의 컴포넌트는 재사용하며, 다른 타입이면 언마운트 후 새로 마운트합니다. Virtual DOM이 빠른 이유는 DOM 조작을 배치 처리하고 최소화하기 때문이지만, 매우 단순한 업데이트나 대량의 노드를 한 번에 변경할 때는 diffing 오버헤드로 인해 직접 DOM 조작보다 느려질 수 있습니다. 따라서 수천 개의 행을 렌더링하는 테이블 같은 경우 virtualization 기법이나 useMemo, React.memo를 활용한 최적화가 필요합니다.
- • Virtual DOM은 실제 DOM의 경량 복사본으로 diffing 알고리즘을 통해 변경 최소화
- • Reconciliation은 key를 사용한 효율적인 리스트 업데이트와 컴포넌트 타입 비교 수행
- • 배치 처리로 DOM 조작을 줄여 성능 향상하지만 대량 업데이트 시 diffing 오버헤드 발생 가능
- • 대규모 리스트는 virtualization이나 메모이제이션으로 최적화 필요
React 18의 Concurrent Rendering과 Fiber 아키텍처가 기존 Reconciliation과 어떻게 다른지, 그리고 이것이 사용자 경험에 어떤 개선을 가져오는지 설명해주세요.
Q. Server-Side Rendering(SSR)을 사용하는 Next.js나 Nuxt.js 애플리케이션에서 Hydration이 무엇인지 설명하고, Hydration Mismatch 에러가 발생하는 원인과 해결 방법, 그리고 Selective Hydration과 Progressive Hydration의 차이를 설명해주세요.
Hydration은 서버에서 렌더링된 정적 HTML에 클라이언트 측 JavaScript를 연결하여 인터랙티브하게 만드는 과정입니다. Hydration Mismatch는 서버와 클라이언트 렌더링 결과가 다를 때 발생하며, 주요 원인은 Date.now()나 Math.random() 같은 비결정적 값, localStorage 접근, 브라우저 전용 API 사용 등입니다. 해결 방법은 useEffect로 클라이언트 전용 로직 분리, suppressHydrationWarning 사용, 또는 두 단계 렌더링(초기 null 반환 후 useEffect에서 업데이트)입니다. Selective Hydration은 사용자 인터랙션에 따라 우선순위를 동적으로 조정하여 필요한 컴포넌트를 먼저 hydrate하고, Progressive Hydration은 컴포넌트를 점진적으로 hydrate하여 초기 인터랙티브 시간(TTI)을 개선하는 기법입니다.
- • Hydration은 SSR된 HTML에 JavaScript 이벤트 리스너와 상태를 연결하는 과정
- • Hydration Mismatch는 서버/클라이언트 렌더링 불일치로 발생하며 비결정적 값이 주요 원인
- • useEffect로 클라이언트 전용 로직 분리하거나 두 단계 렌더링으로 해결
- • Selective Hydration은 우선순위 기반, Progressive Hydration은 점진적 활성화로 TTI 개선
React Server Components와 기존 SSR의 차이점을 설명하고, Server Components가 Hydration 문제를 어떻게 해결하는지 설명해주세요.
Q. Vue.js의 Reactivity System에서 사용하는 Proxy 기반 반응성과 React의 상태 관리 방식의 근본적인 차이를 설명하고, Vue의 ref와 reactive의 차이, 그리고 Computed와 Watch의 적절한 사용 시나리오를 실무 예시와 함께 설명해주세요.
Vue의 Proxy 기반 반응성은 데이터 변경을 자동으로 추적하여 의존성 그래프를 구성하고 변경 시 자동으로 관련 컴포넌트를 업데이트하는 반면, React는 명시적인 setState나 상태 훅 호출을 통해 수동으로 리렌더링을 트리거합니다. ref는 원시값을 반응형으로 만들고 .value로 접근하며, reactive는 객체를 반응형으로 만들고 직접 접근합니다. Computed는 파생 상태를 캐싱하여 의존성 변경 시에만 재계산하므로 필터링된 리스트나 총합 계산에 적합하고, Watch는 비동기 작업이나 부수 효과(API 호출, 로깅)가 필요할 때 사용합니다. 예를 들어 검색어에 따른 결과 필터링은 Computed로, 검색어 변경 시 API 호출은 Watch로 구현하는 것이 적절합니다.
- • Vue는 Proxy로 자동 추적, React는 명시적 상태 업데이트로 수동 리렌더링
- • ref는 원시값용(.value 접근), reactive는 객체용(직접 접근)
- • Computed는 파생 상태 캐싱으로 성능 최적화, Watch는 부수 효과 처리용
- • 필터링/계산은 Computed, API 호출/로깅은 Watch 사용
Vue 3의 Composition API에서 watchEffect와 watch의 차이점을 설명하고, 각각 어떤 상황에서 사용하는 것이 적절한지 예시를 들어주세요.
Q. React 함수형 컴포넌트에서 useEffect의 cleanup 함수가 실행되는 시점과 역할을 설명하고, 실무에서 cleanup을 누락했을 때 발생할 수 있는 문제(메모리 누수, race condition)와 구체적인 해결 사례를 설명해주세요.
useEffect의 cleanup 함수는 컴포넌트 언마운트 시점과 다음 effect 실행 전에 호출되며, 이전 effect의 부수 효과를 정리하는 역할을 합니다. cleanup을 누락하면 setInterval/setTimeout이 계속 실행되거나, 이벤트 리스너가 중복 등록되거나, 구독이 해제되지 않아 메모리 누수가 발생합니다. race condition은 컴포넌트가 언마운트된 후 비동기 작업이 완료되어 setState를 호출할 때 발생하는데, cleanup에서 flag를 설정하거나 AbortController를 사용해 fetch를 취소하여 해결할 수 있습니다. 예를 들어 채팅방 컴포넌트에서 WebSocket 연결을 cleanup에서 닫지 않으면 다른 방으로 이동해도 이전 방의 메시지를 계속 수신하는 문제가 발생합니다.
- • cleanup은 언마운트 시와 다음 effect 실행 전에 호출되어 이전 부수 효과 정리
- • 누락 시 타이머, 이벤트 리스너, 구독이 계속 실행되어 메모리 누수 발생
- • race condition은 언마운트 후 비동기 작업 완료 시 발생, flag나 AbortController로 해결
- • WebSocket, EventSource, setInterval 등은 반드시 cleanup에서 정리 필요
useEffect의 의존성 배열에 객체나 함수를 넣을 때 발생하는 문제와 useCallback, useMemo를 활용한 해결 방법, 그리고 ESLint exhaustive-deps 규칙을 무시해야 하는 정당한 경우를 설명해주세요.
Q. Redux의 미들웨어 아키텍처와 동작 원리를 설명하고, redux-thunk와 redux-saga의 차이점을 비교한 뒤, 복잡한 비동기 플로우(재시도 로직, 폴링, 낙관적 업데이트)를 구현할 때 각각의 장단점과 어떤 상황에서 어떤 미들웨어를 선택해야 하는지 설명해주세요.
Redux 미들웨어는 action이 dispatch되고 reducer에 도달하기 전 중간에서 동작하며, store와 next를 받아 action을 가로채고 변형하거나 비동기 작업을 수행할 수 있습니다. redux-thunk는 action creator가 함수를 반환하여 dispatch와 getState를 받아 비동기 로직을 실행하는 간단한 방식이고, redux-saga는 Generator 함수와 Effect를 사용해 복잡한 비동기 플로우를 선언적으로 제어합니다. thunk는 간단한 API 호출에 적합하지만 재시도나 취소가 어렵고, saga는 takeLatest로 이전 요청 자동 취소, retry로 재시도, race로 타임아웃 처리가 가능하며 테스트도 용이합니다. 단순한 CRUD는 thunk, 복잡한 워크플로우(결제 프로세스, 실시간 동기화)나 여러 action 조합이 필요한 경우 saga를 선택하는 것이 적절합니다.
- • 미들웨어는 dispatch와 reducer 사이에서 action을 가로채 비동기 작업 수행
- • thunk는 함수 반환으로 간단하지만 복잡한 플로우 제어 어려움
- • saga는 Generator와 Effect로 취소, 재시도, race 같은 고급 제어 가능하고 테스트 용이
- • 단순 API 호출은 thunk, 복잡한 워크플로우와 여러 action 조합은 saga 선택
Redux Toolkit의 createAsyncThunk와 RTK Query가 기존 미들웨어 방식과 어떻게 다른지, 그리고 캐싱과 자동 리페칭 측면에서 어떤 이점이 있는지 설명해주세요.
아직 댓글이 없습니다. 첫 번째 댓글을 남겨보세요!