React 미드레벨 코딩 면접
새 면접Q. React 애플리케이션에서 10,000개 이상의 아이템을 가진 드롭다운 셀렉트 컴포넌트를 구현해야 합니다. 사용자가 타이핑할 때마다 필터링이 발생하는데, 입력 지연(input lag)이 심각하게 발생합니다. 이 문제를 해결하기 위한 최적화 전략들을 제시하고, 각 방법의 구현 방식과 트레이드오프를 설명해주세요.
먼저 디바운싱(debouncing)을 적용하여 연속된 입력에 대해 필터링 횟수를 줄입니다. 필터링 로직 자체는 useMemo로 메모이제이션하여 불필요한 재계산을 방지합니다. 가상 스크롤링(react-window, react-virtualized)을 도입하여 실제로 보이는 항목만 렌더링합니다. Web Worker를 활용하여 필터링 작업을 메인 스레드에서 분리할 수 있습니다. 또한 Trie 자료구조나 인덱싱을 통해 검색 알고리즘 자체의 시간 복잡도를 O(n)에서 O(log n) 수준으로 개선할 수 있습니다.
- • 디바운싱으로 필터링 호출 횟수 감소 (lodash.debounce 또는 커스텀 훅)
- • useMemo로 필터링 결과 메모이제이션
- • 가상 스크롤링으로 DOM 노드 수 최소화
- • Web Worker로 무거운 연산 오프로딩
- • Trie 또는 인덱싱으로 검색 알고리즘 최적화
디바운싱과 쓰로틀링(throttling)의 차이는 무엇이며, 각각 어떤 상황에서 사용해야 하나요? 또한 Web Worker를 사용할 때 데이터 직렬화 오버헤드는 어떻게 처리하나요?
Q. 여러 컴포넌트에서 공유되는 폼 상태를 관리하는 복잡한 위저드(다단계 폼) 기능을 구현해야 합니다. 각 단계는 독립적인 검증 로직을 가지며, 뒤로 가기/앞으로 가기, 중간 저장, 특정 단계로 점프 기능이 필요합니다. 이런 복잡한 상태 관리를 어떤 패턴과 자료구조로 설계하시겠습니까? 상태 머신(State Machine) 패턴을 적용한다면 어떻게 구현할 수 있나요?
상태 머신 패턴을 적용하여 각 단계를 명시적인 상태로 정의하고, 가능한 전이(transition)만 허용하도록 설계합니다. XState 같은 라이브러리를 사용하거나 useReducer로 직접 구현할 수 있습니다. 각 단계의 데이터는 Map 자료구조로 관리하여 O(1) 접근을 보장하고, 검증 상태는 별도의 객체로 분리합니다. 히스토리 스택을 유지하여 뒤로가기를 구현하고, sessionStorage를 활용해 중간 저장 기능을 제공합니다. 각 단계는 독립적인 컴포넌트로 분리하고 lazy loading을 적용하여 초기 번들 사이즈를 최적화합니다.
- • 상태 머신 패턴으로 명시적 상태 전이 관리 (XState 또는 useReducer)
- • Map 자료구조로 각 단계 데이터 관리 (빠른 조회)
- • 히스토리 스택으로 네비게이션 구현
- • sessionStorage로 중간 저장 및 복구
- • 단계별 컴포넌트 lazy loading
상태 머신 패턴 대신 일반적인 상태 관리를 사용했을 때 발생할 수 있는 문제점은 무엇인가요? 또한 폼 데이터가 매우 클 경우 sessionStorage의 용량 제한(5MB)을 어떻게 해결하시겠습니까?
Q. 사용자가 검색어를 입력할 때마다 API를 호출하는 자동완성 기능을 구현했는데, 네트워크 응답 순서가 요청 순서와 다를 경우 이전 검색 결과가 나중에 도착한 결과를 덮어쓰는 race condition 문제가 발생합니다. 이 문제를 해결하기 위한 여러 전략을 제시하고, 각각의 구현 방법과 장단점을 설명해주세요.
가장 간단한 방법은 AbortController를 사용하여 새로운 요청 시 이전 요청을 취소하는 것입니다. 또는 각 요청에 시퀀스 번호나 타임스탬프를 부여하고, 응답 도착 시 최신 요청인지 확인하여 오래된 응답은 무시합니다. RxJS의 switchMap이나 lodash의 debounce와 결합하여 처리할 수도 있습니다. React Query나 SWR을 사용하면 이런 race condition을 라이브러리 레벨에서 자동으로 처리해줍니다. useEffect cleanup 함수에서 플래그를 설정하여 컴포넌트 언마운트 또는 재실행 시 이전 응답을 무시하는 방법도 있습니다.
- • AbortController로 이전 요청 취소
- • 시퀀스 번호/타임스탬프로 최신 응답만 처리
- • useEffect cleanup 함수에서 플래그 관리
- • React Query/SWR의 자동 race condition 처리
- • 디바운싱과 결합하여 요청 횟수 자체를 줄임
AbortController를 사용할 때 서버 측에서는 실제로 요청 처리가 중단되나요? 또한 GraphQL 구독(subscription)이나 WebSocket 같은 실시간 연결에서는 race condition을 어떻게 처리해야 하나요?
Q. 드래그 앤 드롭으로 리스트 아이템의 순서를 변경할 수 있는 기능을 구현해야 합니다. 아이템이 1000개 이상일 때, 드래그 중 실시간으로 다른 아이템들의 위치를 계산하고 애니메이션을 적용해야 하는데 성능 문제가 발생합니다. 효율적인 위치 계산 알고리즘과 React에서의 구현 전략을 제시해주세요. 특히 불필요한 리렌더링을 최소화하는 방법을 설명해주세요.
드래그되는 아이템만 실제로 DOM 위치를 변경하고, 나머지 아이템들은 transform CSS 속성으로 시각적 위치만 변경하여 리플로우를 최소화합니다. 아이템의 위치 정보를 useRef로 관리하여 상태 변경 없이 실시간 계산을 수행하고, 드롭 시점에만 실제 상태를 업데이트합니다. 가상 스크롤링을 적용하여 화면에 보이는 아이템만 렌더링하고, Intersection Observer로 뷰포트 내 아이템만 추적합니다. requestAnimationFrame으로 위치 계산을 프레임 단위로 배치 처리하여 메인 스레드 부하를 분산시킵니다. 리스트 재정렬 알고리즘은 splice 대신 새 배열을 생성하되, 변경되지 않은 아이템은 동일한 참조를 유지하여 React.memo 최적화를 활용합니다.
- • transform CSS로 시각적 위치 변경 (리플로우 방지)
- • useRef로 위치 정보 관리하여 불필요한 리렌더링 방지
- • 드롭 시점에만 실제 상태 업데이트
- • requestAnimationFrame으로 배치 처리
- • 불변성 유지하되 변경되지 않은 아이템은 참조 유지
react-beautiful-dnd 같은 라이브러리를 사용하지 않고 직접 구현하는 이유는 무엇일 수 있나요? 또한 터치 디바이스에서 드래그 앤 드롭을 구현할 때 추가로 고려해야 할 사항은 무엇인가요?
Q. 프로덕션 환경에서 특정 사용자들이 버튼 클릭 시 아무 반응이 없다는 버그 리포트를 받았습니다. 개발 환경에서는 재현되지 않고, 에러 로그도 잡히지 않습니다. React 이벤트 핸들러가 실행되지 않는 원인을 체계적으로 디버깅하는 방법과, 이런 상황에서 문제를 추적하기 위해 구현해야 할 로깅 및 모니터링 전략을 설명해주세요.
먼저 Sentry나 LogRocket 같은 에러 추적 도구로 사용자 세션을 리플레이하여 실제 상황을 재현합니다. 이벤트 핸들러 내부에 커스텀 로깅을 추가하여 핸들러가 실행되는지, 특정 조건문에서 early return되는지 확인합니다. z-index나 pointer-events CSS 속성으로 인해 다른 요소가 클릭을 가로채는지 확인하고, React DevTools Profiler로 해당 컴포넌트가 실제로 마운트되어 있는지 검증합니다. 브라우저별, 디바이스별 차이를 확인하기 위해 User-Agent 정보와 함께 로그를 수집하고, 특정 상태나 props 조합에서만 발생하는지 확인하기 위해 상태 스냅샷을 로깅합니다. 또한 번들 크기나 코드 스플리팅 문제로 핸들러 코드가 로드되지 않았을 가능성도 확인합니다.
- • 세션 리플레이 도구로 사용자 행동 재현 (Sentry, LogRocket)
- • 이벤트 핸들러 내부에 커스텀 로깅 추가
- • CSS 레이어링 문제 확인 (z-index, pointer-events)
- • 상태/props 스냅샷과 환경 정보 로깅
- • 코드 스플리팅 및 번들 로드 이슈 확인
이벤트 핸들러가 클로저로 오래된 상태를 참조하는 stale closure 문제일 가능성도 있을까요? 이를 어떻게 진단하고 해결할 수 있나요?
아직 댓글이 없습니다. 첫 번째 댓글을 남겨보세요!