React 주니어 프레임워크 면접
새 면접Q. React의 Virtual DOM과 실제 DOM의 차이점을 설명하고, Virtual DOM을 사용하는 이유와 성능상 이점을 말씀해주세요.
브라우저의 실제 DOM 조작이 왜 비용이 큰지, 그리고 React가 변경사항을 어떻게 효율적으로 감지하고 적용하는지 생각해보세요.
실제 DOM은 브라우저가 HTML 문서를 트리 구조로 표현한 객체이며, DOM 조작 시 리플로우와 리페인트가 발생해 성능 비용이 큽니다. Virtual DOM은 실제 DOM의 가벼운 JavaScript 객체 복사본으로, 메모리 상에서만 존재합니다. React는 상태 변경 시 새로운 Virtual DOM 트리를 생성하고, 이전 트리와 비교(Diffing)하여 실제로 변경된 부분만 찾아냅니다. 이렇게 찾아낸 최소한의 변경사항만 실제 DOM에 일괄 적용(Batch Update)하여 불필요한 DOM 조작을 줄이고 성능을 최적화합니다. 특히 복잡한 UI에서 여러 상태가 동시에 변경될 때 효율성이 극대화됩니다.
- • Virtual DOM은 실제 DOM의 경량 JavaScript 복사본
- • Diffing 알고리즘으로 변경사항만 감지
- • Batch Update로 최소한의 DOM 조작만 수행
- • 리플로우/리페인트 최소화로 성능 향상
대시보드처럼 실시간으로 여러 데이터가 동시에 업데이트되는 화면에서 Virtual DOM이 성능 최적화에 기여합니다.
React Fiber 아키텍처가 기존 Reconciliation 과정을 어떻게 개선했는지 설명해주세요.
Q. 함수형 컴포넌트에서 useEffect의 cleanup 함수가 실행되는 시점과 역할을 설명하고, 어떤 상황에서 cleanup 함수를 반드시 작성해야 하는지 예를 들어주세요.
컴포넌트가 언마운트되거나 effect가 재실행되기 전에 어떤 정리 작업이 필요한지 생각해보세요.
cleanup 함수는 컴포넌트가 언마운트되기 직전 또는 effect가 재실행되기 전에 실행됩니다. useEffect 내부에서 return문으로 함수를 반환하면 이것이 cleanup 함수가 됩니다. 이벤트 리스너를 등록했다면 메모리 누수를 방지하기 위해 cleanup에서 제거해야 하며, setInterval이나 setTimeout을 사용했다면 clearInterval이나 clearTimeout으로 정리해야 합니다. WebSocket 연결이나 API 구독을 설정했다면 cleanup에서 연결을 해제하고, 비동기 작업이 진행 중일 때는 취소 플래그를 설정해 불필요한 상태 업데이트를 방지해야 합니다. cleanup 함수를 작성하지 않으면 메모리 누수, 중복 이벤트 실행, 언마운트된 컴포넌트의 상태 업데이트 에러 등이 발생할 수 있습니다.
- • 언마운트 직전과 effect 재실행 전에 실행
- • 이벤트 리스너 제거로 메모리 누수 방지
- • 타이머와 구독 정리 필수
- • 비동기 작업 취소로 불필요한 상태 업데이트 방지
채팅 애플리케이션에서 WebSocket 연결을 설정할 때 컴포넌트 언마운트 시 연결을 정리하지 않으면 메모리 누수가 발생합니다.
useEffect의 의존성 배열에 객체나 배열을 넣을 때 주의해야 할 점은 무엇인가요?
Q. React 컴포넌트가 리렌더링되는 조건들을 모두 설명하고, 불필요한 리렌더링을 방지하기 위한 최적화 기법들을 말씀해주세요.
state, props, context 변경과 부모 컴포넌트의 영향, 그리고 React가 제공하는 메모이제이션 도구들을 생각해보세요.
React 컴포넌트는 자신의 state가 변경되거나, 부모로부터 받은 props가 변경되거나, 사용 중인 Context 값이 변경되거나, 부모 컴포넌트가 리렌더링될 때 함께 리렌더링됩니다. 불필요한 리렌더링을 방지하기 위해 React.memo를 사용하여 props가 실제로 변경되지 않았다면 리렌더링을 건너뛸 수 있습니다. useMemo는 계산 비용이 큰 값을 메모이제이션하여 의존성이 변경될 때만 재계산하고, useCallback은 함수를 메모이제이션하여 자식 컴포넌트에 props로 전달할 때 불필요한 리렌더링을 방지합니다. 또한 상태를 적절히 분리하고 컴포넌트 구조를 최적화하여 변경 영향 범위를 최소화하는 것도 중요합니다.
- • state, props, Context 변경 시 리렌더링
- • 부모 리렌더링 시 자식도 리렌더링
- • React.memo로 props 비교 후 리렌더링 방지
- • useMemo와 useCallback으로 메모이제이션
대용량 테이블이나 복잡한 폼에서 일부 필드만 변경되었을 때 전체가 리렌더링되면 성능 저하가 발생합니다.
React.memo의 두 번째 인자로 커스텀 비교 함수를 전달하는 경우는 언제인가요?
Q. useState와 useReducer의 차이점을 설명하고, 어떤 상황에서 useReducer를 사용하는 것이 더 적합한지 말씀해주세요.
상태 업데이트 로직의 복잡도와 여러 상태 간의 연관성을 고려해보세요.
useState는 간단한 상태 관리에 적합하며, 상태 값과 업데이트 함수를 반환합니다. useReducer는 Redux와 유사한 패턴으로 상태와 dispatch 함수를 반환하며, reducer 함수에서 action 타입에 따라 상태 변경 로직을 처리합니다. 여러 개의 연관된 상태를 함께 관리하거나, 상태 업데이트 로직이 복잡하거나, 다음 상태가 이전 상태에 의존하는 경우 useReducer가 더 적합합니다. 또한 상태 업데이트 로직을 컴포넌트 외부로 분리하여 테스트하기 쉽고, action 객체를 통해 상태 변경 의도를 명확하게 표현할 수 있습니다.
- • useState는 간단한 상태, useReducer는 복잡한 상태 관리
- • useReducer는 연관된 여러 상태를 함께 관리
- • reducer 함수로 상태 업데이트 로직 분리
- • action으로 상태 변경 의도 명확화
쇼핑 카트처럼 상품 추가, 삭제, 수량 변경 등 여러 종류의 상태 업데이트가 필요한 경우 useReducer가 코드를 더 명확하게 만듭니다.
useReducer를 사용할 때 불변성을 유지해야 하는 이유는 무엇인가요?
Q. React Context API의 동작 원리를 설명하고, Context를 사용할 때 발생할 수 있는 성능 문제와 해결 방법을 말씀해주세요.
Context 값이 변경되면 해당 Context를 구독하는 모든 컴포넌트에 어떤 일이 발생하는지 생각해보세요.
Context API는 createContext로 Context 객체를 생성하고, Provider 컴포넌트로 값을 제공하며, useContext Hook으로 하위 컴포넌트에서 값을 구독하는 방식으로 동작합니다. props drilling 없이 전역 상태를 공유할 수 있지만, Context 값이 변경되면 해당 Context를 사용하는 모든 컴포넌트가 리렌더링되는 성능 문제가 있습니다. 이를 해결하기 위해 Context를 목적별로 분리하여 변경 빈도가 다른 값들을 다른 Context에 담거나, useMemo로 Context value를 메모이제이션하여 불필요한 참조 변경을 방지할 수 있습니다. 또한 Context 소비 컴포넌트를 React.memo로 감싸거나, 상태와 업데이트 함수를 별도 Context로 분리하는 방법도 효과적입니다.
- • Provider로 값 제공, useContext로 구독
- • Context 값 변경 시 모든 구독 컴포넌트 리렌더링
- • Context 분리로 불필요한 리렌더링 방지
- • value 메모이제이션과 Context 분리 전략
다크모드 테마나 사용자 인증 정보처럼 앱 전역에서 필요한 데이터를 Context로 관리하면 props drilling을 피할 수 있습니다.
Context API와 상태 관리 라이브러리(Redux, Zustand 등)의 차이점은 무엇인가요?
Q. 제어 컴포넌트(Controlled Component)와 비제어 컴포넌트(Uncontrolled Component)의 차이점을 설명하고, 각각의 장단점과 사용 사례를 말씀해주세요.
폼 입력값의 상태를 React가 관리하는지, DOM이 직접 관리하는지를 기준으로 생각해보세요.
제어 컴포넌트는 input의 value를 React state로 관리하고 onChange 이벤트로 상태를 업데이트하는 방식으로, React가 단일 진실 공급원이 됩니다. 비제어 컴포넌트는 DOM이 직접 입력값을 관리하며, ref를 사용해 필요할 때만 값을 가져옵니다. 제어 컴포넌트는 실시간 유효성 검사, 조건부 입력 제한, 즉각적인 피드백이 가능하지만 모든 입력마다 리렌더링이 발생합니다. 비제어 컴포넌트는 불필요한 리렌더링이 없어 성능상 유리하지만, 실시간 검증이 어렵고 React 외부에서 상태가 관리되어 예측 가능성이 낮습니다. 일반적으로 React 애플리케이션에서는 제어 컴포넌트를 권장하며, 파일 업로드나 대용량 폼에서는 비제어 컴포넌트를 고려할 수 있습니다.
- • 제어 컴포넌트는 React state로 값 관리
- • 비제어 컴포넌트는 DOM이 직접 관리, ref로 접근
- • 제어 컴포넌트는 실시간 검증 가능, 리렌더링 발생
- • 비제어 컴포넌트는 성능 유리, 예측성 낮음
회원가입 폼에서 비밀번호 강도를 실시간으로 표시하려면 제어 컴포넌트로 구현해야 입력할 때마다 검증할 수 있습니다.
React Hook Form 같은 라이브러리가 비제어 컴포넌트 방식을 채택하는 이유는 무엇인가요?
Q. React의 합성 이벤트(Synthetic Event)가 무엇인지 설명하고, 브라우저 네이티브 이벤트와의 차이점 및 React가 합성 이벤트를 사용하는 이유를 말씀해주세요.
브라우저마다 이벤트 처리 방식이 다를 때 React가 어떻게 일관성을 제공하는지 생각해보세요.
합성 이벤트는 React가 브라우저 네이티브 이벤트를 감싸서 제공하는 크로스 브라우저 래퍼 객체입니다. 모든 브라우저에서 동일한 인터페이스와 동작을 보장하여 브라우저 간 호환성 문제를 해결합니다. React는 이벤트를 실제 DOM 요소가 아닌 루트 레벨에서 위임(Event Delegation)하여 처리하므로 메모리 효율성이 높고 성능이 향상됩니다. 또한 합성 이벤트는 이벤트 풀링을 통해 재사용되므로 가비지 컬렉션 부담이 줄어들지만, React 17부터는 이벤트 풀링이 제거되었습니다. stopPropagation이나 preventDefault 같은 메서드는 네이티브 이벤트와 동일하게 사용할 수 있으며, nativeEvent 속성으로 원본 브라우저 이벤트에 접근할 수도 있습니다.
- • 브라우저 네이티브 이벤트를 감싼 래퍼 객체
- • 크로스 브라우저 호환성 보장
- • 이벤트 위임으로 메모리 효율성 향상
- • nativeEvent로 원본 이벤트 접근 가능
모달 외부 클릭 감지 같은 기능을 구현할 때 합성 이벤트의 stopPropagation과 네이티브 이벤트의 동작 차이를 이해해야 합니다.
React 17에서 이벤트 위임 방식이 어떻게 변경되었고, 왜 그런 변경이 필요했나요?
아직 댓글이 없습니다. 첫 번째 댓글을 남겨보세요!