React 미드레벨 코딩 면접
새 면접Q. React 컴포넌트에서 조건부 렌더링을 구현하는 다양한 방법들(삼항 연산자, && 연산자, if문, switch문, 즉시 실행 함수)을 설명하고, 각 방법의 적절한 사용 시나리오와 주의사항을 말씀해주세요. 특히 && 연산자 사용 시 0이나 NaN 같은 falsy 값이 렌더링되는 문제를 어떻게 방지할 수 있나요?
&& 연산자는 간단한 조건부 렌더링에 적합하지만, 좌측 값이 0이나 NaN일 경우 그 값 자체가 렌더링되므로 명시적으로 boolean으로 변환해야 합니다. 삼항 연산자는 else 케이스가 있을 때 사용하며, 복잡한 조건이 3개 이상일 때는 즉시 실행 함수나 별도 함수로 분리하는 것이 가독성에 좋습니다. null을 반환하면 아무것도 렌더링되지 않으므로 else 케이스에서 활용할 수 있습니다.
- • && 연산자는 falsy 값(0, NaN, '')이 렌더링될 수 있으므로 !!value 또는 value > 0 같은 명시적 boolean 변환 필요
- • 삼항 연산자는 if-else 구조에 적합하며, 중첩 시 가독성이 떨어지므로 2단계 이상은 피해야 함
- • 복잡한 조건은 컴포넌트 외부에서 함수로 분리하거나 early return 패턴 사용
- • switch문이나 객체 매핑은 상태에 따른 다중 컴포넌트 렌더링에 유용
조건부 렌더링이 많아져서 컴포넌트가 복잡해질 때, 이를 리팩토링하는 전략과 패턴은 무엇인가요? Polymorphic Component 패턴을 적용할 수 있을까요?
Q. React에서 대량의 DOM 노드를 한 번에 추가하거나 제거할 때 브라우저의 리플로우(reflow)와 리페인트(repaint)가 성능에 미치는 영향을 설명하고, DocumentFragment나 CSS transform을 활용한 최적화 방법을 제시해주세요. React의 batch update와 어떻게 연계되는지도 설명해주세요.
리플로우는 레이아웃 계산이 필요한 DOM 변경 시 발생하며, 리페인트는 시각적 변경만 있을 때 발생합니다. React는 상태 변경을 batch 처리하여 한 번에 DOM을 업데이트하지만, 직접 DOM 조작 시에는 DocumentFragment로 노드를 메모리에서 조립한 후 한 번에 삽입해야 합니다. 애니메이션은 transform과 opacity만 사용하면 GPU 가속을 받아 리플로우 없이 처리됩니다. will-change CSS 속성으로 브라우저에 힌트를 줄 수 있습니다.
- • 리플로우는 레이아웃 계산이 필요한 속성(width, height, position) 변경 시 발생하며 비용이 큼
- • React 18의 Automatic Batching은 이벤트 핸들러뿐 아니라 Promise, setTimeout 내부에서도 동작
- • 대량의 DOM 조작은 가상화(react-window, react-virtualized) 또는 DocumentFragment 사용
- • transform, opacity는 합성 레이어에서 처리되어 리플로우 없이 GPU 가속 가능
React에서 CSS-in-JS 라이브러리(styled-components, emotion)를 사용할 때 동적 스타일 변경이 성능에 미치는 영향과, CSS 변수를 활용한 최적화 방법을 설명해주세요.
Q. React 애플리케이션에서 사용자 입력 자동완성 기능을 구현할 때, Trie(트라이) 자료구조를 사용하는 것이 단순 배열 필터링보다 유리한 이유를 시간 복잡도와 함께 설명하고, 메모리 사용량 측면에서의 트레이드오프를 분석해주세요. 수만 개의 단어를 다룰 때 어느 방식이 더 효율적인가요?
Trie는 prefix 검색에 최적화된 트리 구조로, 검색 시간 복잡도가 O(m)(m은 검색어 길이)이며 데이터 개수 n에 영향받지 않습니다. 배열 필터링은 O(n*m)이므로 데이터가 많을수록 느립니다. 하지만 Trie는 각 문자마다 노드를 생성하므로 메모리 사용량이 크며, 공통 접두사가 적으면 메모리 낭비가 심합니다. 수만 개 이상의 단어에서는 Trie가 유리하지만, 천 개 미만이면 배열 필터링도 충분히 빠릅니다.
- • Trie는 prefix 검색에 O(m) 시간 복잡도로 데이터 크기와 무관하게 일정한 성능
- • 배열 필터링은 O(n*m)이지만 구현이 간단하고 메모리 효율적
- • 공통 접두사가 많은 데이터셋에서 Trie의 메모리 효율이 높아짐
- • 실무에서는 데이터 크기, 검색 빈도, 메모리 제약을 고려하여 선택
자동완성 결과를 인기도 순으로 정렬해야 한다면, Trie 구조를 어떻게 확장하시겠습니까? 각 노드에 빈도 정보를 저장하는 방식의 장단점을 설명해주세요.
Q. 함수형 프로그래밍 원칙을 React 컴포넌트에 적용할 때, 순수 함수(Pure Function)의 개념과 중요성을 설명하고, 사이드 이펙트가 있는 로직을 어디에 배치해야 하는지 설명해주세요. map, filter, reduce를 체이닝할 때 성능 문제가 발생할 수 있는 이유와 해결 방법도 함께 설명해주세요.
순수 함수는 동일한 입력에 항상 동일한 출력을 반환하고 사이드 이펙트가 없는 함수로, React 컴포넌트 렌더링 로직은 순수해야 예측 가능하고 테스트하기 쉽습니다. 사이드 이펙트(API 호출, DOM 조작, 타이머)는 useEffect나 이벤트 핸들러에 배치해야 합니다. map, filter, reduce 체이닝은 각각 배열을 순회하므로 O(3n)이며, 대량 데이터에서는 단일 reduce로 통합하거나 for 루프로 변환하는 것이 효율적입니다.
- • 순수 함수는 테스트 가능성, 예측 가능성, 메모이제이션 가능성을 제공
- • React 컴포넌트의 렌더링 로직은 순수해야 하며, 사이드 이펙트는 useEffect로 분리
- • map().filter().reduce() 체이닝은 가독성은 좋지만 n번의 순회가 발생
- • 성능이 중요한 경우 단일 reduce나 for 루프로 통합하거나, 지연 평가(lazy evaluation) 라이브러리 사용
Immutability(불변성)를 유지하면서 깊게 중첩된 객체를 업데이트할 때, 성능과 가독성을 모두 고려한 접근 방법은 무엇인가요? Immutable.js와 Immer의 차이점도 설명해주세요.
Q. React 컴포넌트에서 여러 개의 독립적인 비동기 작업을 병렬로 실행하되, 그중 하나라도 실패하면 전체를 롤백해야 하는 트랜잭션 같은 로직을 구현해야 합니다. Promise.all, Promise.allSettled, Promise.race의 차이를 설명하고, 부분 성공 시 보상 트랜잭션(compensating transaction) 패턴을 어떻게 구현할 수 있는지 설명해주세요.
Promise.all은 모든 Promise가 성공해야 resolve되고 하나라도 실패하면 즉시 reject되므로, 전체 성공/실패가 중요한 트랜잭션에 적합합니다. Promise.allSettled는 모든 Promise의 성공/실패와 관계없이 결과를 배열로 반환하므로 부분 성공 처리에 유용합니다. Promise.race는 가장 먼저 완료된 Promise의 결과를 반환합니다. 보상 트랜잭션은 성공한 작업들을 역순으로 되돌리는 rollback 함수를 각 작업마다 정의하고, 실패 시 실행하는 패턴입니다.
- • Promise.all은 하나라도 실패하면 즉시 reject, 나머지 Promise는 계속 실행됨
- • Promise.allSettled는 모든 결과를 {status, value/reason} 형태로 반환
- • 보상 트랜잭션은 각 작업에 대응하는 rollback 함수를 정의하여 실패 시 역순 실행
- • Saga 패턴이나 분산 트랜잭션에서 사용되는 개념으로, 프론트엔드에서도 복잡한 폼 제출 등에 적용 가능
React에서 낙관적 업데이트(Optimistic Update)를 구현할 때, 서버 요청이 실패했을 경우 UI를 이전 상태로 되돌리는 로직을 어떻게 설계하시겠습니까? 에러 처리와 사용자 피드백 전략도 함께 설명해주세요.
아직 댓글이 없습니다. 첫 번째 댓글을 남겨보세요!