⚛️ 프레임워크 심화 개념: React & Vue 면접 대비
"라이브러리 사용법을 아는 것과, 왜 그렇게 설계되었는지 아는 것은 전혀 다른 레벨의 이해다."
React와 Vue를 단순히 "쓸 줄 아는" 수준을 넘어, 면접에서는 내부 동작 원리를 묻는 경우가 많습니다. 이 글에서는 두 프레임워크의 핵심 개념을 정리합니다.
📌 목차
- 가상 DOM과 재조정(Reconciliation)
- React: Hooks의 동작 원리
- React: 렌더링 최적화
- Vue: 반응성 시스템
- React vs Vue 비교
- 상태 관리의 흐름
1️⃣ 가상 DOM과 재조정(Reconciliation)
실제 DOM 조작은 비용이 큽니다. React/Vue는 **가상 DOM(Virtual DOM)**이라는 메모리상의 가벼운 객체를 먼저 비교한 뒤, 변경된 부분만 실제 DOM에 반영합니다.
상태 변경 발생
│
▼
새로운 Virtual DOM 트리 생성
│
▼
이전 Virtual DOM과 비교 (Diffing)
│
▼
변경된 부분만 실제 DOM에 반영 (Reconciliation)
Diffing 알고리즘의 핵심 규칙
- 서로 다른 타입의 엘리먼트는 트리 전체를 재생성 (<div> → <span>이면 하위 전부 재생성)
- 같은 타입이면 속성만 비교해 업데이트
- 리스트 렌더링 시 key로 각 항목을 식별해 불필요한 재생성 방지
// key가 없으면 배열 순서 변경 시 모든 항목을 재생성할 수 있음
{items.map((item) => (
<li key={item.id}>{item.name}</li>
))}
⚠️ 면접 단골 함정: key에 배열의 index를 사용하면, 리스트 순서가 바뀌거나 항목이 삭제될 때 잘못된 항목이 재사용되어 버그가 생길 수 있습니다. 가능하면 고유 ID를 사용해야 합니다.
2️⃣ React: Hooks의 동작 원리
useState
function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
useState는 클로저를 통해 상태를 기억합니다. 컴포넌트가 리렌더링될 때마다 함수 전체가 다시 실행되지만, React가 내부적으로 Fiber 노드에 상태를 저장해두고 있다가 다시 꺼내줍니다.
useEffect의 의존성 배열
useEffect(() => {
console.log('컴포넌트가 마운트되거나 count가 변경될 때 실행');
}, [count]); // 의존성 배열
의존성 배열동작
| 생략 | 매 렌더링마다 실행 |
| [] | 마운트 시 한 번만 실행 |
| [count] | count가 변경될 때만 실행 |
클린업 함수
useEffect(() => {
const timer = setInterval(() => console.log('tick'), 1000);
return () => clearInterval(timer); // 클린업: 다음 실행 전 또는 언마운트 시 호출
}, []);
💡 면접 포인트: "왜 Hooks는 조건문 안에서 사용하면 안 되는가?"에 대한 답 → React는 Hooks를 **호출 순서(인덱스)**로 구분해 관리하기 때문에, 조건문으로 순서가 바뀌면 상태가 엉뚱한 Hook에 매칭될 수 있습니다.
3️⃣ React: 렌더링 최적화
React.memo
const Child = React.memo(function Child({ value }) {
console.log('Child 렌더링');
return <div>{value}</div>;
});
부모가 리렌더링되어도 props가 바뀌지 않았다면 Child는 리렌더링을 건너뜁니다.
useMemo / useCallback
// 비싼 연산 결과를 재사용
const sortedList = useMemo(() => expensiveSort(list), [list]);
// 함수 참조 자체를 재사용 (자식에게 props로 넘길 때 유용)
const handleClick = useCallback(() => {
doSomething(id);
}, [id]);
주의: useCallback/useMemo는 무조건 좋은 것이 아닙니다. 의존성 비교 자체에도 비용이 들기 때문에, 정말 무거운 연산이거나 자식 컴포넌트의 불필요한 리렌더링을 막아야 할 때만 사용하는 것이 좋습니다.
4️⃣ Vue: 반응성 시스템
Vue의 가장 큰 특징은 **자동 반응성(Reactivity)**입니다. React처럼 setState를 명시적으로 호출하지 않아도, 데이터를 변경하면 화면이 자동으로 갱신됩니다.
Vue 2: Object.defineProperty
Object.defineProperty(obj, 'key', {
get() { /* 의존성 추적 */ return value; },
set(newValue) { value = newValue; /* 리렌더링 트리거 */ }
});
Vue 3: Proxy 기반
const state = reactive({ count: 0 });
// 내부적으로 Proxy가 get/set을 가로챔
state.count++; // 자동으로 관련 컴포넌트 리렌더링
Vue 3가 Proxy로 전환한 이유
- Object.defineProperty는 속성 추가/삭제, 배열 인덱스 변경을 감지하지 못하는 한계가 있었음
- Proxy는 객체 자체를 감싸기 때문에 이런 동적 변경도 모두 감지 가능
const state = reactive({ count: 0 });
state.newProp = 'hello'; // Vue 3에서는 이것도 반응형으로 감지됨
5️⃣ React vs Vue 비교
ReactVue
| 상태 변경 감지 | 명시적 (setState, useState) | 자동 반응성 (Proxy 기반) |
| 템플릿 | JSX (JS 안에 마크업) | Template 문법 (HTML에 가까움) |
| 학습 곡선 | 순수 JS 개념(클로저, 불변성) 이해 필요 | 상대적으로 완만 (템플릿이 HTML과 유사) |
| 생태계 | 매우 크고 자유도가 높음(선택의 폭이 넓음) | 공식 라이브러리(Router, Pinia)로 통일성 있음 |
💡 면접 포인트: "어떤 게 더 좋은가"보다 "각 설계가 어떤 트레이드오프를 가지는가"를 설명하는 것이 더 좋은 답변입니다. React는 명시적 상태 관리로 예측 가능성을, Vue는 자동 반응성으로 개발 편의성을 우선시합니다.
6️⃣ 상태 관리의 흐름
컴포넌트가 많아지면 상태를 어디에 둘지 고민하게 됩니다.
지역 상태 (useState/ref)
│ 여러 컴포넌트가 공유해야 하면
▼
상위 컴포넌트로 끌어올리기 (Lifting State Up)
│ Prop Drilling이 심해지면
▼
Context API / provide-inject
│ 상태 로직이 복잡해지면
▼
전역 상태 관리 라이브러리 (Redux, Zustand, Pinia)
⚠️ 흔한 실수: 모든 상태를 무조건 전역 상태 관리 라이브러리에 넣는 것. 컴포넌트 내부에서만 쓰이는 상태까지 전역으로 옮기면 오히려 복잡도만 늘어납니다. **"이 상태를 누가 알아야 하는가"**를 먼저 따져보세요.
🔚 마무리
프레임워크 면접 질문의 본질은 "이 도구가 왜 이렇게 설계되었는가"를 이해하고 있는지 확인하는 것입니다. 단순 API 암기보다 가상 DOM, 반응성, 렌더링 최적화의 원리를 설명할 수 있도록 준비하는 것이 훨씬 효과적입니다.
'Frontend' 카테고리의 다른 글
| [Frontend] 3D 재난 모니터링 플랫폼(feat. R&D 과제) (0) | 2026.07.04 |
|---|---|
| [Frontend] Web Fundamentals 정리 (1) | 2026.07.03 |
| [Frontend] 콜백 지옥에서 벗어나기 (0) | 2026.07.03 |
| [Frontend] React Hook - 함수 컴포넌트 이해하기 (0) | 2026.07.03 |
| [Frontend] Reflow는 왜 느릴까? (0) | 2026.07.03 |