Frontend

[Frontend] Framwork 정리

teddy bear 2026. 7. 3. 19:01
반응형

⚛️ 프레임워크 심화 개념: React & Vue 면접 대비

"라이브러리 사용법을 아는 것과, 왜 그렇게 설계되었는지 아는 것은 전혀 다른 레벨의 이해다."

React와 Vue를 단순히 "쓸 줄 아는" 수준을 넘어, 면접에서는 내부 동작 원리를 묻는 경우가 많습니다. 이 글에서는 두 프레임워크의 핵심 개념을 정리합니다.


📌 목차

  1. 가상 DOM과 재조정(Reconciliation)
  2. React: Hooks의 동작 원리
  3. React: 렌더링 최적화
  4. Vue: 반응성 시스템
  5. React vs Vue 비교
  6. 상태 관리의 흐름

1️⃣ 가상 DOM과 재조정(Reconciliation)

실제 DOM 조작은 비용이 큽니다. React/Vue는 **가상 DOM(Virtual DOM)**이라는 메모리상의 가벼운 객체를 먼저 비교한 뒤, 변경된 부분만 실제 DOM에 반영합니다.

상태 변경 발생
     │
     ▼
새로운 Virtual DOM 트리 생성
     │
     ▼
이전 Virtual DOM과 비교 (Diffing)
     │
     ▼
변경된 부분만 실제 DOM에 반영 (Reconciliation)

Diffing 알고리즘의 핵심 규칙

  1. 서로 다른 타입의 엘리먼트는 트리 전체를 재생성 (<div> → <span>이면 하위 전부 재생성)
  2. 같은 타입이면 속성만 비교해 업데이트
  3. 리스트 렌더링 시 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, 반응성, 렌더링 최적화의 원리를 설명할 수 있도록 준비하는 것이 훨씬 효과적입니다.