Vue보다 React를 선호하는 이유

둘 중 나에게 선택권이 있다면, 무조건 React

만약 제게 두가지 중 선택권이 있다면 “무조건 React를 사용하겠다.”고 할 것 같습니다.

최근  프로젝트를 시작할 때의 기술 선택에 대해 직간접적으로 조언을 요청받는데, 매번 위와 같이 대답을 했습니다. 그런데 비슷한 대답을 세네번 쯤 하고 있자니 이런 입장을 갖게 된 이유를 한 번 글로 정리해두면 편하기도 하고, 좀 더 도움이 될 수 있겠다는 생각이 들었습니다.

글에 들어가기 앞서 이 글의 제목이 “React가 Vue.js보다 우월하고 모두가 React를 사용해야 하는 이유”가 아니란 점을 강조하고 싶습니다. 모든 Vue.js 사용자가 React를 사용하도록 만드는 건 이 글의 목표가 아닙니다. 오류에 대한 지적, 또 미처 생각하지 못한 점에 대한 의견은 환영합니다.

제가 React를 Vue보다 선호하는 이유는 크게 세 가지 정도로 정리할 수 있습니다.

  • 타입스크립트의 지원
  • 단순한 컴포넌트 정의의 용의함
  • 더 빠르고 담대한 개선

하나씩 살펴보도록 하겠습니다.

타입스크립트 지원

이 항목은 상대적으로 다른 항목에 비해 React의 비교 우위가 명백합니다. React와 타입스크립트(이하 TS)의 결합은 아주 매끄럽습니다. 큰 보일러 플레이트 없이도 SFC와 클래스 기반 컴포넌트 둘 모두의 타입을 정확하게 기술 할 수 있고, redux를 비롯한 컴패니언 툴도 대부분 훌륭한 타입 지원을 제공합니다.

반면 Vue는 2.5 버전 업데이트 당시 TS 지원의 큰 개선을 홍보했지만 아직까지도 많이 미흡합니다. 예를 들어 computed나 methods 내의  this 타입은 여전히 제대로 추론되지 않고 있습니다. vue-class-component를 사용하면 상황이 좀 나아지지만 이는 아직 실험 기능인 데코레이터에 의존하며, 주류 문법과 이질적입니다. vuex의 타입 지원 또한 redux의 그것과 비슷한 수준이 되기는 요원해 보입니다.

TS의 필요성에 공감하지 못하거나 도입이 너무 어렵고 귀찮게 느껴져 사용하지 않는 사람들을 꽤 보았는데, 그런 이들에겐 이 항목은 별로 해당 사항이 없습니다. 개인적으론 TS 도입을 권하는 발표까지 할 정도로 기술의 팬이고, 그 지원 수준에 따라 생산성이 극명하게 달라질 정도로 의존성이 큽니다. 따라서 Vue의 TS 지원이 눈에 띄게 개선되기 전까지는 이 점 하나만 봐도 제겐 React를 선택할 충분한 이유가 될 듯 합니다.

단순한 컴포넌트 정의의 용이함

Vue는 컴포넌트에 관한 템플릿, 스타일과 스크립트를 .vue 확장자를 갖는 한 파일 내에 모두 작성할 수 있는 단일 파일 컴포넌트(Single File Component)를 제공합니다. 이를 사용해 허구의 UserList 컴포넌트를 작성하는 경우를 생각해보면.

<template>
  <ul :id="$style.userList">
    <li v-for="user in users" :key="user.id" :class="{
        [$style.userItem]: true,
        [$style.selected]: user.id === selectedUserId
      }">
      {{ user.name }}
    </li>
  </ul>
</template>

<script>
export default {
  data() {
    return { selectedUserId: undefined }
  },
  props: ['users']
}
</script>

<style module="">
/* style definition */
</style>

React를 사용한다면 이 컴포넌트를 대략 아래와 같이 작성할 것입니다.

import React, { Component } from 'react'
import classNames from 'classnames'
import * as styles from './UserList.css'

const UserItem = ({ user, selected }) => (
  <li className={classNames(style.userItem, { [style.selected]: selected })}>
    { user.name }
  </li>
)

export default class UserList extends Component {
  constructor(props) {
    super(props)
    this.state = { selectedUserId: undefined }
  }

  render() {
    const { users } = this.props
    const { selectedUserId } = this.state

    return (
      <ul className={styles.userList}>
        {users.map(user => (
          <li className={classNames(styles.userItem, { [styles.selected]: user.id === selectedUserId })}>
            { user.name }
          </li>
        )}
      </ul>
    )
  }
}

이 시점에서는 비교해보면, Vue에 비해 React의 문법이 갖는 몇 가지 단점이 눈에 뜁니다.

  • styled-components등을 사용하지 않는 이상 컴포넌트의 스타일시트를 별도의 파일에 작성해야 한다.
  • 특정 조건에 따라 엘리먼트의 클래스 명을 다르게 주고 싶을 때 classnames등의 써드파티에 의존해야 한다.
  • 상태를 갖는 컴포넌트를 정의할 때 필요한 보일러플레이트가 Vue에 비해 크다.

하지만 이 파일 내에서만 사용 될 UserItem 컴포넌트를 별도의 컴포넌트로 빼는 경우를 생각해 보면.

Vue에서는 두 가지 선택지가 있습니다. 하나는 별도의 UserItem.vue를 만드는 것입니다.

<template>
  <ul :id="$style.userList">
    <user-item
      v-for="user in users"
      :key="user.id"
      :user="user"
      :selected="user.id === selectedUserId"
    />
  </ul>
</template>

<script>
import UserItem from './UserItem.vue'

export default {
  components() {
    UserItem
  },
  data() {
    return { selectedUserId: undefined }
  },
  props: ['users']
}
</script>

<style module>
/* style definition */
</style>

또는 파일 내에서  ComponentOption 객체를 정의 할 수 있습니다.

<template>
  <ul :id="$style.userList">
    <user-item
      v-for="user in users"
      :key="user.id"
      :user="user"
      :selected="user.id === selectedUserId"
    />
  </ul>
</template>

<script>
const UserItem = {
  template: '<li :class="{ [styles.userItem]: true, [styles.selected]: selected }">{{ user.name }}</li>',
  props: ['user', 'selected']
}

export default {
  components() {
    UserItem
  },
  data() {
    return { selectedUserId: undefined }
  },
  props: {
    users: {
      type: Array,
      default: []
    }
  }
}
</script>

<style module>
/* style definition */
</style>

이러한 두 가지 방법은 각각의 단점을 갖고 있습니다.

먼저 단일 파일 컴포넌트를 사용한 접근의 경우, 한 군데에서만 사용될 작은 컴포넌트를 정의할 때에도 무조건 새 파일을 만들어야 한다는 점입니다. 이는 보일러플레이트의 증가로 이어집니다.

한 편, ComponentOptions를 사용한 접근의 경우, 템플릿을 플레인 문자열로 표현하는 탓에 많은 정보를 잃게 됩니다. template 대신 render 함수를 사용하고 그 안에서 JSX를 사용할 수 있지만, Vue가 권장하는 방식은 아니라는 인상을 받았습니다.

React의 무상태 함수 컴포넌트(Stateless Functional Component)를 사용하면 같은 작업을 아래와 같이 우아하게 해낼 수 있습니다.

import React, { Component } from 'react'
import classNames from 'classnames'
import * as styles from './UserList.css'

const UserItem = ({ user, selected }) => (
  <li className={classNames(style.userItem, { [style.selected]: selected })}>
    { user.name }
  </li>
)

export default class UserList extends Component {
  render() {
    const { users } = this.props
    const { selectedUserId } = this.state

    return (
      <ul className={styles.userList}>
        {users.map(user => (
          <UserItem user={user} selected={this.selectedUserId === user.id} />
        )}
      </ul>
    )
  }
}

보일러플레이트가 훨씬 적을 뿐더러, UserItem 컴포넌트가 부모가 던져준 데이터에 의존하는 함수라는 점이 코드 자체에서 아주 명백하게 드러납니다.

컴포넌트를 잘게 쪼갤 때 React는 Vue에 비해 다음과 같은 강점을 있습니다.

  • 작은 컴포넌트를 정의하는 문법이 직관적이고 간결하다.
  • 컴포넌트 정의하는 두 문법(SFC, 클래스 기반 컴포넌트)이 상대적으로 더 일관적이다.
  • 템플릿을 문자열로 표현하지 않으므로 여러 정보를 잃어버리지 않는다.

커다란 로직을 함수로 쪼개 의도를 명시적으로 인코딩하고, 프로그램을 보다 쉽게 소화할 수 있는 의미 덩어리로 나누듯, 컴포넌트를 쪼개는 작업도 비슷한 효과를 갖습니다. React는 Vue에 비해 UI를 조그마한 컴포넌트들의 조합으로 표현 하는데에 더 적합한 문법을 갖고 있습니다. 저는 이런 장점이 React의 단점을 상쇄하고 남을 정도라 생각합니다.

더 빠르고 담대한 개선

마지막 항목은 두 라이브러리의 현재를 비교한 앞선 두 항목과 달리 미래에 대한 이야기인데, 어느 정도 믿음의 영역이라 사람에 따라 의견 차이가 클 수 있을거 같습니다. 저는 React가 Vue에 비해 앞으로 더 빠르고 담대한 개선을 이루어낼 것이라 기대하고 있습니다.

최근 길지 않은 시간 동안 React에는 Fiber를 비롯해 Fragment, Portal 등 굵직한 변화 내지는 기능 추가가 있었습니다. 또한 새로운 Context API, 라이플 사이클 메소드, time slicing과 suspense 등 다양한 변경사항이 예고되어 있다. 모든 변경사항이 좋은 변경사항이라고 할 순 없겠으나, 그와 별개로 React 팀이 빠르게 움직이고 있다는 사실 자체는 이견의 여지가 없습니다.

반면 Vue의 릴리즈는 상대적으로 느리게 이루어지고, 주로 마이너한 변경사항 위주인 경우가 많습니다. 이런 속도의 차이는 결국 현존하는 두 라이브러리 사이의 간극이 더 벌어지는 결과를 낳을 것입니다. 빠르게 변화하고, 배우고, 그리고 발전해 결국은 앞서 나갈 React에 베팅하는게 맞다고 생각하는 이유입니다.

맺으며

이상 제가 React를 Vue보다 더 선호하는 이유를 정리해 보았습니다. 끝으로 덧붙이자면, 사실 위 내용 중 많은 부분이 두 라이브러리의 보다 근본적인 철학 차이에서 파생되었다 생각합니다. 두 라이브러리가 추구하는 바에 대해 제가 받은 인상은 아래와 같습니다.

  • React에는 Vue에 비해 매직이 덜하다. Vue는 사용자에게 쉽게 느껴지는 API를 제공하기 위해 라이브러리가 직접 헤비 리프팅을 하는 경우가 많다.
  • React는 Vue에 비해 플레인 자바스크립트에 더 가깝다. 새로운 문법 내지는 컨벤션을 정의하는 대신 자바스크립트의 그것을 활용하는데 무리가 없다면 그리 한다.
  • React는 Vue에 비해 사용자 및 사용처에 대해 더 적은 가정을 하고, 컴포넌트 기반의 선언적 UI 렌더링이라는 가장 핵심적인 기능과 관련된 부분만 코어에 포함한다.

두 가지 다른 방향 중 나는 React의 방향에 더 공감합니다. 미래에 두 라이브러리가 어떻게 변할지, 그리고 어떤 새로운 라이브러리가 등장할 지 모릅니다. 하지만 React가 지금 보여주는 철학을 앞으로도 고수하며 꾸준히 발전해 나간다면 앞으로도 저는 React를 즐겁게 사용할 것입니다.

글의 목적 상 Vue의 단점을 더 많이 언급했지만 Vue 역시 분명 좋은 도구입니다. 만약 상대적으로 자바스크립트에 익숙하지 않은 팀원이 많이 참여하고 소규모의, 오래 유지 보수하지 않을 프로젝트라면 Vue도 좋은 선택이 될 수 있다고 생각합니다. 두 라이브러리가 긍정적인 경쟁을 통해 함께 발전하길 바랍니다.

0 Comments

Cancel