React 신입 코딩·알고리즘 면접
새 면접Q. React에서 배열 데이터를 map 함수로 렌더링할 때 key 속성을 부여하는 이유와, key 값으로 배열의 인덱스를 사용하면 안 되는 경우를 알고리즘 관점에서 설명해주세요.
React가 가상 DOM을 비교하는 방식(재조정 알고리즘)을 생각해보세요.
React는 재조정(Reconciliation) 알고리즘을 통해 가상 DOM을 비교할 때 key를 식별자로 사용합니다. key가 있으면 O(n) 시간 복잡도로 어떤 요소가 변경, 추가, 삭제되었는지 효율적으로 판단할 수 있습니다. 배열의 순서가 바뀌거나 중간에 요소가 삽입/삭제되는 경우 인덱스를 key로 사용하면 React가 잘못된 요소를 재사용하거나 불필요한 리렌더링을 발생시킬 수 있습니다. 따라서 고유하고 안정적인 값(예: 데이터의 ID)을 key로 사용해야 정확한 비교와 최적화가 가능합니다.
- • 재조정 알고리즘에서 key는 요소 식별자로 사용됨
- • key가 있으면 O(n) 복잡도로 효율적인 비교 가능
- • 인덱스를 key로 사용하면 배열 순서 변경 시 오작동 발생
- • 고유하고 안정적인 값을 key로 사용해야 함
동적인 목록(게시판, 장바구니 등)에서 아이템 순서 변경이나 필터링 시 올바른 key 사용이 필수적입니다.
만약 데이터에 고유한 ID가 없다면 어떻게 key를 생성하는 것이 좋을까요?
Q. React 컴포넌트 내에서 이벤트 핸들러 함수를 정의할 때, 매 렌더링마다 새로운 함수가 생성되는 것이 성능에 미치는 영향과 이를 방지하기 위한 방법들을 설명해주세요.
함수가 새로 생성되면 참조 동등성 비교와 메모이제이션에 어떤 영향을 주는지 생각해보세요.
함수형 컴포넌트에서 일반 함수로 이벤트 핸들러를 정의하면 매 렌더링마다 새로운 함수 인스턴스가 생성됩니다. 이는 참조 동등성 비교에 실패하게 만들어 React.memo나 useMemo로 최적화된 자식 컴포넌트가 불필요하게 리렌더링될 수 있습니다. useCallback 훅을 사용하면 의존성 배열이 변경되지 않는 한 동일한 함수 참조를 유지할 수 있습니다. 또는 클래스 컴포넌트의 경우 생성자에서 바인딩하거나 클래스 필드로 화살표 함수를 정의하는 방법도 있습니다. 단, 간단한 컴포넌트에서는 함수 재생성 비용이 미미하므로 과도한 최적화는 오히려 코드 복잡도만 높일 수 있습니다.
- • 매 렌더링마다 새 함수 생성 시 참조 동등성 비교 실패
- • 메모이제이션된 자식 컴포넌트의 불필요한 리렌더링 유발
- • useCallback으로 함수 참조 안정화 가능
- • 과도한 최적화보다 실제 성능 병목 지점 파악이 중요
대규모 리스트에서 각 아이템에 이벤트 핸들러를 전달할 때 useCallback 사용으로 성능을 개선할 수 있습니다.
useCallback을 사용할 때 의존성 배열을 잘못 설정하면 어떤 문제가 발생할 수 있나요?
Q. React에서 조건부 렌더링을 구현하는 여러 방법(삼항 연산자, 논리 AND 연산자, if문 등)의 차이점과 각각의 시간 복잡도, 그리고 어떤 상황에서 어떤 방법을 선택해야 하는지 설명해주세요.
각 방법의 가독성과 조건의 복잡도에 따른 적절성을 고려해보세요.
모든 조건부 렌더링 방법은 기본적으로 O(1) 시간 복잡도를 가집니다. 삼항 연산자는 참/거짓 두 가지 경우를 모두 처리할 때 적합하며 JSX 내부에서 인라인으로 사용 가능합니다. 논리 AND 연산자는 조건이 참일 때만 렌더링하는 단순한 경우에 간결하지만, 0이나 빈 문자열 등 falsy 값에 주의해야 합니다. if문이나 switch문은 조건이 복잡하거나 여러 분기가 있을 때 가독성이 좋으며 컴포넌트 반환 전에 사용합니다. 즉시 실행 함수(IIFE)는 복잡한 로직을 JSX 내부에서 처리할 때 사용할 수 있지만 가독성이 떨어질 수 있습니다.
- • 모든 방법이 O(1) 시간 복잡도
- • 삼항 연산자는 양방향 조건에 적합
- • 논리 AND는 단순 조건에 간결하나 falsy 값 주의
- • 복잡한 조건은 if문으로 가독성 확보
사용자 권한에 따라 UI를 다르게 보여주거나 로딩 상태를 처리할 때 조건부 렌더링을 사용합니다.
논리 AND 연산자로 조건부 렌더링할 때 count가 0인 경우 어떤 문제가 발생할 수 있나요?
Q. React에서 setState를 연속으로 여러 번 호출할 때 배치 업데이트가 일어나는 원리와, 함수형 업데이트를 사용해야 하는 이유를 알고리즘 관점에서 설명해주세요.
React가 상태 업데이트를 큐에 저장하고 처리하는 방식을 생각해보세요.
React는 성능 최적화를 위해 상태 업데이트를 즉시 반영하지 않고 큐에 쌓아두었다가 배치로 처리합니다. 같은 이벤트 핸들러 내에서 setState를 여러 번 호출하면 각 호출이 큐에 추가되고, 이후 한 번의 리렌더링으로 모든 업데이트를 반영합니다. 일반 값으로 setState를 호출하면 현재 상태를 기준으로 하므로 이전 업데이트가 반영되지 않은 상태 값을 참조할 수 있습니다. 함수형 업데이트(prevState => prevState + 1)를 사용하면 큐에 저장된 업데이트 함수들이 순차적으로 실행되어 이전 업데이트 결과를 정확히 반영할 수 있습니다. React 18부터는 자동 배칭이 더 확장되어 setTimeout, Promise 등 비동기 콜백에서도 배치 업데이트가 적용됩니다.
- • 상태 업데이트는 큐에 저장되어 배치로 처리됨
- • 일반 값 업데이트는 현재 상태를 참조해 누락 가능
- • 함수형 업데이트는 이전 업데이트 결과를 순차적으로 반영
- • 배치 업데이트로 불필요한 리렌더링 방지
카운터나 장바구니 수량 조절처럼 연속적인 상태 변경이 필요한 경우 함수형 업데이트를 사용합니다.
React 18의 자동 배칭을 의도적으로 비활성화하고 싶다면 어떤 방법을 사용할 수 있나요?
Q. 사용자가 입력하는 검색어로 대용량 데이터 배열을 필터링하여 React 컴포넌트에 표시할 때, 성능을 최적화하기 위한 알고리즘적 접근 방법들을 설명해주세요.
디바운싱, 메모이제이션, 가상 스크롤링 등 여러 기법을 조합할 수 있습니다.
먼저 디바운싱(debouncing)을 적용하여 사용자가 타이핑을 멈춘 후 일정 시간(예: 300ms) 후에만 필터링을 실행하면 불필요한 연산을 줄일 수 있습니다. 필터링 결과는 useMemo로 메모이제이션하여 검색어가 변경되지 않으면 재계산하지 않도록 합니다. 필터링 자체는 O(n) 복잡도이므로 데이터가 매우 크다면 백엔드에서 검색을 처리하거나 인덱싱된 자료구조를 사용하는 것이 좋습니다. 결과가 많을 경우 react-window나 react-virtualized 같은 가상 스크롤링 라이브러리로 실제 화면에 보이는 항목만 렌더링하여 DOM 노드 수를 줄입니다. 추가로 검색어를 소문자로 변환하여 대소문자 구분 없이 비교하면 사용자 경험이 개선됩니다.
- • 디바운싱으로 불필요한 연산 횟수 감소
- • useMemo로 필터링 결과 메모이제이션
- • 필터링은 O(n) 복잡도이므로 대용량은 백엔드 처리 고려
- • 가상 스크롤링으로 렌더링되는 DOM 노드 수 최소화
상품 검색, 사용자 목록 필터링, 자동완성 기능 등에서 이러한 최적화 기법이 필수적입니다.
디바운싱과 쓰로틀링(throttling)의 차이점과 각각 어떤 상황에 적합한지 설명해주세요.
Q. 트리 구조의 데이터(예: 폴더/파일 시스템, 댓글 대댓글)를 재귀적으로 렌더링하는 React 컴포넌트를 구현할 때 고려해야 할 알고리즘적 문제점과 해결 방법을 설명해주세요.
재귀 호출의 깊이, 스택 오버플로우, 렌더링 성능 등을 고려해보세요.
재귀 컴포넌트는 트리 구조를 순회하므로 최악의 경우 O(n) 시간 복잡도를 가지며, 각 노드마다 컴포넌트 인스턴스가 생성됩니다. 트리 깊이가 매우 깊으면 이론적으로 스택 오버플로우가 발생할 수 있지만 실제로는 브라우저 메모리 한계가 먼저 도달하는 경우가 많습니다. 각 재귀 노드에 고유한 key를 부여해야 하며, 보통 ID와 깊이를 조합하여 생성합니다. 성능 최적화를 위해 각 노드 컴포넌트를 React.memo로 감싸고, 변경되지 않은 하위 트리는 리렌더링하지 않도록 합니다. 매우 큰 트리라면 초기에는 일정 깊이까지만 렌더링하고 나머지는 지연 로딩하거나, 가상화 기법을 적용하는 것이 좋습니다. 또한 트리 순회를 재귀 대신 스택 기반 반복문으로 구현하는 방법도 고려할 수 있습니다.
- • 트리 순회는 O(n) 시간 복잡도
- • 깊은 트리에서 스택 오버플로우 가능성
- • 각 노드에 고유 key 부여 필수
- • React.memo와 지연 로딩으로 성능 최적화
- • 매우 깊은 트리는 반복문 기반 순회 고려
파일 탐색기, 조직도, 중첩 댓글 시스템 등에서 재귀 컴포넌트 패턴이 사용됩니다.
재귀 대신 스택이나 큐를 사용한 반복문으로 트리를 순회하는 방법의 장단점은 무엇인가요?
Q. React 컴포넌트에서 배열 데이터를 정렬하여 표시할 때, 원본 배열을 직접 수정하지 않고 정렬하는 방법과 그 이유를 불변성 원칙과 알고리즘 관점에서 설명해주세요.
JavaScript의 sort 메서드 특성과 React의 상태 관리 원칙을 함께 고려해보세요.
JavaScript의 Array.prototype.sort는 원본 배열을 직접 수정하는(in-place) 메서드로, 평균 O(n log n) 시간 복잡도를 가집니다. React에서는 불변성 원칙에 따라 상태를 직접 수정하면 안 되므로, 먼저 배열을 복사한 후 정렬해야 합니다. 스프레드 연산자나 slice 메서드로 얕은 복사를 만든 뒤 sort를 호출하면 원본은 유지되고 새 배열이 반환됩니다. 정렬 기준이 자주 바뀐다면 useMemo로 정렬 결과를 메모이제이션하여 불필요한 재정렬을 방지할 수 있습니다. toSorted 같은 불변 메서드를 사용할 수도 있지만 브라우저 지원 범위를 확인해야 합니다. 불변성을 지키지 않으면 React가 변경을 감지하지 못해 컴포넌트가 리렌더링되지 않는 문제가 발생합니다.
- • sort는 원본 배열을 수정하는 in-place 메서드
- • React 불변성 원칙을 위해 복사 후 정렬 필요
- • 스프레드 연산자나 slice로 얕은 복사 생성
- • useMemo로 정렬 결과 메모이제이션 가능
- • 불변성 위반 시 리렌더링 감지 실패
테이블의 컬럼 헤더를 클릭하여 데이터를 정렬하는 기능을 구현할 때 이 원칙을 적용합니다.
얕은 복사와 깊은 복사의 차이점은 무엇이며, 언제 깊은 복사가 필요한가요?
아직 댓글이 없습니다. 첫 번째 댓글을 남겨보세요!