본문 바로가기

React/React

왜 React.js는 Vue.js보다 인기가 많을까? (+ 내부 동작 비교, 분석)

들어가며

이번에 인턴 면접을 준비하며 Vue.js(Nuxt) 기반의 프로젝트를 경험하면서, Vue의 심플함에 매력을 느꼈습니다. html로 관심사가 분할되어 있는 부분은 마치 예전 Spring 기반 프로젝트의 Template Engine을 사용하는 것 같은 느낌을 받기도 했습니다. 그런데 현재 시장에서는 React가 압도적인 점유율을 보이고 있는데요. Vue는 러닝커브도 낮고, 내부 최적화도 훨씬 편리한데 왜 React가 더 인기를 끌게되었까요? 이 글에서 두 라이브러리의 특징을 비교하고, 분석해보려합니다.

 

React와 Vue의 시장 점유율

 

우선 npm trends로 다운로드 수를 비교해보았는데요, 2025년 1월 19일 기준으로 약 6배가량 차이가 나는 것을 확인할 수 있습니다. 수치상으로는 React가 압도적인 우위에 있다는 것을 체감할 수 있네요. 그럼 실제 기업 현장에서도 이렇게나 차이가 날까요? 카카오 테크글을 보면 약 3년 전 글이긴 하지만, 카카오 내부에서는 프로젝트가 반반으로 이루어지고 있음을 알 수 있습니다. 아마 작은 프로젝트를 Vue로 먼저 만든 후에 나중에 복잡한 요구사항이 많아지면 React로 변경하는 것 같은데요. 카카오 공식 홈페이지나 네이버의 Vibe는 Vue로 만든 사이트의 대표적인 예시입니다. 이렇게 보니 사용자별 데이터가 많이 필요없는 정적인 사이트(블로그, 홈페이지 등)에 Vue가 많이 사용되고 있는 것 같습니다. 그 이유에 대해서도 이어서 알아보겠습니다.

 

기술적인 특징, 내부 렌더링 방식은 어떻게 다를까?

React와 Vue 모두 가상 DOM을 사용하지만, 구체적인 구현 방식에서 차이가 있는데요.

 

React의 렌더링 프로세스

리액트의 렌더링 프로세스에는 두 단계가 있습니다. Render 단계와 Commit 단계입니다. state나 props가 변경되면 이 Render 단계가 시작됩니다. 가상 DOM 트리를 새로 생성하고, 이전 가상 DOM과 새로운 가상 DOM을 비교해서 Reconciliation 단계를 거칩니다. 그리고 변경이 필요한 부분을 표시하는데, 이걸보고 실제 DOM에 변경사항을 적용하는 단계가 바로 커밋 단계입니다.

 

Vue의 렌더링 프로세스

Vue는 Reactivity System이라는 반응형 시스템에 의해 모든 데이터의 의존성을 추적하고 있다가 해당 데이터를 사용하는 컴포넌트만 다시 렌더링하는 프로세스를 가지고 있습니다. React와 유사하게 Virtual DOM 트리를 생성하지만 최적화 방식이 달라지게 되는 것이죠.

 

최적화 방식의 주요 차이

// React - 명시적 최적화
const MemoizedComponent = React.memo(MyComponent)

 

리액트는 이렇게 개발자가 직접 명시적 최적화를 해주지 않는 경우, 부모 컴포넌트가 변경되면 자식 컴포넌트도 리렌더링이 발생하게 됩니다. 이럴 때 위의 코드 예시인 memo 또는 useMemo, useCallback을 적재적소에 써서 불필요한 리렌더링을 방지할 수 있습니다. 그리고 useTransition을 통해 렌더링 우선 순위도 제어가 가능합니다.

 

반면, Vue는 이런 작업들이 필요하지 않고 의존성 추적으로 자동으로 최적화가 적용됩니다. React는 불변성을 통해 명시적 상태 변경을 감지하지만, Vue는 Proxy를 통해 자동으로 상태를 감지합니다.

// Vue 내부적으로 이런 식으로 데이터를 Proxy로 감싸고 있음
const data = {
  count: 0,
  message: 'Hello'
}

const proxyData = new Proxy(data, {
  get(target, key) {
    track(target, key)  // 의존성 추적 시작
    return target[key]
  },
  set(target, key, value) {
    target[key] = value
    trigger(target, key)  // 변경 사항이 있는 부분만 업데이트 트리거
    return true
  }
})

 

track, trigger 메서드가 어떻게 최적화를 담당하는지 좀 더 자세히 살펴보겠습니다. 

// Vue 내부 동작 예시
let activeEffect // 현재 실행 중인 효과(렌더링이나 computed)

function track(target, key) {
  if (activeEffect) {
    // key가 변경될 때 실행할 효과를 저장
    let depsMap = targetMap.get(target)
    if (!depsMap) {
      targetMap.set(target, (depsMap = new Map()))
    }
    let dep = depsMap.get(key)
    if (!dep) {
      depsMap.set(key, (dep = new Set()))
    }
    dep.add(activeEffect)
  }
}

function trigger(target, key) {
  const depsMap = targetMap.get(target)
  if (!depsMap) return

  const effects = depsMap.get(key)
  if (effects) {
    effects.forEach(effect => effect()) // 관련된 효과만 실행
  }
}

 

이렇기에 Vue는 개발자가 직접 최적화 코드를 작성할 필요가 없습니다. Proxy 기반의 반응형 시스템이 자동으로 변경된 데이터에 의존하는 부분만을 업데이트하고, 불필요한 리렌더링을 방지해주기 때문입니다. React처럼 useMemo나 useCallback 같은 메모이제이션 로직을 직접 작성할 필요가 없죠.

 

Vue의 이런 특징을 알게 되고 예전에 스타트업에 입사한 친구가 들려준 시니어 개발자의 조언이 바로 떠올랐습니다. "자바스크립트를 제대로 모르는 개발자에게 React를 맡길 수 없다. 너는 Vue로 개발해라." 이렇게 말씀하셔서 Vue를 하고 있다고 했었는데, Vue를 몰랐을 때는 이해하기 어려웠던 이 말이 바로 이해가 되는 순간이었습니다.

 

왜냐하면 하나의 예시로 React에서는 상태 업데이트와 렌더링을 일으키는 위치가 매우 유동적입니다.

  • JSX를 반환하는 컴포넌트 로직 내부
  • useEffect나 다른 훅을 사용하는 부분
  • 이벤트 핸들러
  • 외부에서 전달받은 콜백 함수

이러한 높은 자유도는 곧 성능 최적화의 책임도 개발자에게 있다고 생각합니다. 언제 어디서 상태를 업데이트하고, 어떻게 불필요한 리렌더링을 방지할지를 개발자가 직접 고려해야 하기 때문에, JavaScript와 React에 대한 깊은 이해가 필수적입니다. 결과적으로 개발자의 숙련도가 애플리케이션의 성능을 크게 좌우하게 되기에, Vue를 사용하는 것이 숙련도가 낮은 신입 개발자와 협업을 해야한다면 더 나은 선택이 될 수 있었을 것 같습니다.

 

React가 더 나은 선택이 되는 경우: 실시간 데이터 처리

위에서 Vue가 주로 정적인 사이트나 공식 홈페이지에 사용된다고 언급했었는데 그 이유에 대해 알아보겠습니다. Vue의 반응형 시스템은 데이터 변화를 세밀하게 감지하고 필요한 부분만 업데이트하는 것에 최적화되어 있습니다. 하지만 이 시스템은 데이터가 동적으로 자주 변하는 경우 오히려 부담이 될 수 있습니다. 주식 거래 플랫폼을 예로 들어보겠습니다.

 

function StockTrading() {
  const [stockData, setStockData] = useState({});
  const [userPositions, setUserPositions] = useState([]);
  const [orderBook, setOrderBook] = useState([]);
  
  // WebSocket을 통한 실시간 데이터 업데이트
  useEffect(() => {
    const ws = new WebSocket('wss://trading-api.example.com');
    
    ws.onmessage = (event) => {
      const data = JSON.parse(event.data);
      
      // 각각의 데이터 업데이트를 개별적으로 제어
      if (data.type === 'STOCK_UPDATE') {
        setStockData(prev => ({
          ...prev,
          [data.symbol]: data.price
        }));
      } else if (data.type === 'POSITION_UPDATE') {
        setUserPositions(prev => {
          // 복잡한 포지션 업데이트 로직
          return updatePositions(prev, data);
        });
      }
    };
  }, []);

  return (
    <div className="trading-dashboard">
      <PriceChart 
        data={stockData} 
        // 차트 업데이트 최적화
        shouldUpdate={price => checkSignificantChange(price)} 
      />
      <OrderBook orders={orderBook} />
      <TradingPositions positions={userPositions} />
    </div>
  );
}

 

이런 경우 React에서는 각 데이터의 변경을 개별적으로 제어할 수 있습니다. 특정 조건에서만 리렌더링이 일어나도록 최적화를 적용할 수도 있고, 불필요한 업데이트를 방지하는 로직을 커스텀할 수 있습니다. 예를 들어, 가격 변동이 특정 임계값을 넘을 때만 업데이트를 시킬 수 있겠죠. 반면, Vue의 경우에는 이런 데이터 변경의 세밀한 제어가 어렵습니다.

<template>
  <div class="trading-dashboard">
    <price-chart :data="stockData"/>
    <order-book :orders="orderBook"/>
    <trading-positions :positions="userPositions"/>
  </div>
</template>

<script>
export default {
  data() {
    return {
      stockData: {},
      orderBook: [],
      userPositions: []
    }
  },
  created() {
    const ws = new WebSocket('wss://trading-api.example.com');
    ws.onmessage = (event) => {
      const data = JSON.parse(event.data);
      // 반응형 시스템이 자동으로 모든 의존성을 추적
      // 때로는 이 자동화가 오히려 제약이 될 수 있음
      this.stockData[data.symbol] = data.price;
    }
  }
}
</script>

이렇게 자동 반응형 시스템이 오히려 제약이 되는 상황이 발생하기도 합니다. 특정 조건에서만 업데이트하도록 최적화하기가 복잡하고, 깊은 중첩 구조의 데이터가 실시간으로 변경되는 경우, 성능 저하의 가능성도 있습니다. 물론 이를 해결하는 방법으로는 shallowRef, shallowReactive와 같은 메서드를 사용해 최상위 레벨의 변경만 추적하는 등의 방법들이 있습니다만, React의 메모이제이션을 활용하는 방법만큼 직관적이지 않고, Vue의 기본 철학인 '심플함'과는 거리가 멀다고 생각합니다. 그래서 이처럼 실시간으로 빈번한 데이터 업데이트가 필요한 어플리케이션의 경우 React의 명시적인 상태 관리와 세밀한 제어가 가능한 특성이 큰 장점이 되는 것 같습니다.

 

결론

위에서 살펴보았듯이 React의 높은 자유도는 양날의 검과 같기에 숙련된 개발자의 손에서는 강력한 무기가 될 수 있습니다! 복잡한 상태 관리나 세밀한 성능 최적화가 필요한 대규모 어플리케이션에서 개발자가 원하는대로 제어가 가능하기 때문입니다. 그리고 React의 인기에는 인스타그램, 에어비앤비, 넷플릭스와 같은 유저가 많은 큰 기업들이 React를 사용하면서 검증된 기술이라는 신뢰를 얻게된 것도 한 몫했다고 생각합니다. 그러다보니 많은 기업들이 React를 사용하게 되고, 리액트 개발자의 수요가 늘어나고 취업 시장에서도 더 큰 우위를 차지하다보니 이런 인기의 선순환?을 만들어낸 것 같습니다.

 

React와 Vue는 각각의 철학과 접근 방식이 뚜렷이 구분됩니다. Vue는 직관적인 문법과 자동 최적화를 통해 개발자가 비즈니스 로직에 집중할 수 있게 해주는 반면, React는 높은 자유도와 세밀한 제어를 통해 개발자가 원하는 방식대로 애플리케이션을 구축할 수 있게 해줍니다. Vue는 정적 콘텐츠 중심의 웹사이트나 중소규모 프로젝트에서 강점을 보이며, React는 복잡한 상태 관리와 실시간 데이터 처리가 필요한 대규모 애플리케이션에서 빛을 발합니다. 단순히 기술적 차이뿐만 아니라, 각 프레임워크가 추구하는 가치와도 연관이 있다고 생각합니다. 각각의 장단점을 이해하고, 프로젝트와 팀의 상황에 맞는 기술을 선택하면 현명한 접근이 될 것 같습니다!

반응형