Vue.js 체험
Dev

Vue.js 체험

2017. 3. 5. 23:59

그동안 React를 사용하다보니 새로운 프레임워크에 눈을 잘 못 돌렸는데 -아니 돌릴 필요가 없었는데- 요즘 Vue.js라는 것이 핫하다는 것을 들었다. 궁금해서 문서를 조금 보긴 했지만 구 angularjs와 비슷하다는 느낌만 받았을 뿐 특징같은 건 파악하지 못했다.

주말동안 혼자있는 시간이 많이 생겨서 이 기회에 Vue.js에 대해서 알아보기로 했다.

comment-sample

이전에 angularjshandlebar로 댓글 sample code를 만들었는데 지금도 그런 방식으로 react와 vuejs를 비교해서 댓글 sample code를 만들기로 했다. (사실 react로도 이미 만들었지만 그 코드는 react를 잘 모를때 만들어서 너무 보기가 어려워 다시 만들기로 했다.)

만들려고 하는 샘플은 아주 간단하다. 맨 위엔 count가 있고, 그 아래에는 댓글 리스트, 그 아래에는 댓글 입력 폼이다. 댓글 리스트는 2개만 보이는데 더보기를 클릭하면 4개가 더 보인다. 새로 입력한 댓글은 리스트 아래에 추가된다.

react version

먼저 비교적 잘 알고 있는 react버전을 만들어봤다. webpack 세팅하는게 번거로워서 그렇지 아주 쉽게 만든 편이다.

class App extends Component {

  constructor(props, context) {
    super(props, context)
    this.state = {
      comments: [
        {id: 5, message:'Comment 5', created: new Date(new Date().getTime() - 600000)},
        {id: 6, message:'Comment 6', created: new Date(new Date().getTime() - 500000)},
      ],
      count: 6,
      hasMoreComments: true
    }
  }

  render() {
    const { comments, count, hasMoreComments } = this.state
    return (
      <div className="app">
        <div>{count} Comments</div>
        <CommentList comments={comments} hasMore={hasMoreComments} onLoadMore={this.handleLoadMore.bind(this)} />
        <CommentForm onAdd={this.handleAdd.bind(this)} />
      </div>
    )
  }
}

redux 같은 것은 사용하지 않고 간단히 state로만 데이터를 처리했다. App 안에 count, CommentList, CommentForm 컴포넌트를 넣고 데이터를 넘겨준다. count는 따로 컴포넌트로 만들려다가 바로 넣어버렸다.

class CommentList extends Component {
  render() {
    const { comments, hasMore, onLoadMore } = this.props
    return (
      <div>
        {hasMore &&
          <button className="btn-more" onClick={onLoadMore}>more comment</button>
        }
        {comments.map(item =>
          <Comment key={item.id} comment={item} />
        )}
      </div>
    )
  }
}

CommentList는 더보기 버튼과 Comment 리스트를 보여준다.

class Comment extends Component {
  render() {
    const { comment } = this.props
    return (
      <div className="comment">
        <div className="info">{comment.created.toString()}</div>
        <div className="message">{comment.message}</div>
      </div>
    )
  }
}

Comment는 댓글 내용과 날짜를 보여준다.

class CommentForm extends Component {
  render() {
    const { ready } = this.state
    return (
      <form className="form" onSubmit={this.handleAddComment.bind(this)}>
          <label>message</label>
        <input className="text" type='text' ref='message' onChange={this.handleMessageChange.bind(this)} />
        <button type='submit' disabled={!ready}>submit</button>
      </form>
    )
  }
}

CommentForm은 텍스트를 입력받아서 onAdd에 넘겨준다. 여기까지 약 한시간 정도 걸렸다.

vuejs version

vuejs는 잘 모르는 상태다보니 문서를 보면서 하나하나 하기 시작했다. 먼저 어떻게 시작해야할지 감이 안잡혔다. 문서를 보다보니 vue-cli라는 것이 vuejs 프로젝트를 만들어줘서 이걸 따라하기로 했다.

The Installation page provides more options of installing Vue. Note that we do not recommend beginners to start with vue-cli, especially if you are not yet familiar with Node.js-based build tools.

나중에 안 사실이지만 Getting Started문서에 이런 내용이 있었다. 시작할때는 이걸로 하지말라고 ;; 어쨌든 난 이미 이걸 보고 따라한 상태이고 이 방식으로 만들었다.

module: {
    rules: [{
        test: /\.vue$/,
        use: ['vue-loader']
    }]
},
resolve: {
    extensions: ['.js', '.vue', '.json'],
    alias: {
        'vue$': 'vue/dist/vue.esm.js'
    }
}

vue-cli에서 사용하는 방법을 사용하려면 webpack에 이런 설정을 해야한다. .vue파일을 vuejs로 해석을 하는 것인데 .vue는 template, script, style 모두가 한 파일안에 들어간 컴포넌트다. 자세한 내용은 vue-loader 한글문서에 잘 설명이 되어있다.

<template>
<div>Hello, {{ name }}!</div>
</template>

<script>
export default {
    data() {
        return {
            name: "World"
        }
    }
}
</script>

<style>
div { background-color:#f00 }
</style>

이런 식으로 한 파일 안에 다 넣으면 'Hello, World!'라는 문자가 들어간 빨간 박스를 만들 수 있다. 내가 만든 것들은 다 이걸로 만들었다. style은 없는데 react버전을 만들면서 css파일로 만들어두었기 때문이다.

<template>
    <div class="app">
        <div>{{count}} Comments</div>
        <CommentList :comments="comments" :hasMore="hasMore" @onLoadMore="onLoadMore" />
        <CommentForm @onAdd="onAdd" />
    </div>
</template>

<script>
import CommentList from './components/CommentList'
import CommentForm from './components/CommentForm'

let data = {
    comments: [
        {id:5, message:"Comment 5", created:new Date(new Date().getTime() - 60000)},
        {id:6, message:"Comment 6", created:new Date(new Date().getTime() - 50000)}
    ],
    count: 6,
    hasMore: true
}

export default {
    data() {
        return data
    },
    methods: {
        onAdd: function(message) { ... },
        onLoadMore: function() { ... }
    },
    components: { CommentList, CommentForm }
}
</script>

vuejs에서는 property를 :name에 넣고 method는 @name에 넣는다. 구성은 react버전과 동일하다.

<template>
    <div>
        <button v-if="hasMore" class="btn-more" v-on:click="onLoadMore">more comment</button>
        <Comment v-for="comment in comments" :key="comment.id" :comment="comment" />
    </div>
</template>

<script>
import Comment from './Comment'

export default {
    props: [
        'comments',
        'hasMore'
    ],
    model: {
        event: "onLoadMore"
    },
    methods: {
        onLoadMore: function(e) {
            this.$emit('onLoadMore')
        }
    },
    components: { Comment }
}
</script>

CommentList도 template만 보면 react버전과 큰 차이는 없다. 단지 이벤트와 loop가 angularjs를 생각나게 하는 방식이다. v-if, v-for, v-on정도만 알면 ui를 만드는데 큰 무리는 없다. 컴포넌트도 props, model, methods, components만 알면 된다. 아! 상위에서 할당한 method를 호출하려면 this.$emit('name')을 사용한다.

좋았던 점은 class를 react처럼 className으로 안써도 된다는 것이고, 나쁜 점은 반대로 이벤트나 루프가 vuejs만의 문법이라는 점이다. react는 이해하기 편했던 것이 javascript를 그대로 사용한다는 것이었다. 물론 초반에 JSX는 이해하기 힘들었다.

<template>
<div class="comment">
  <div class="info">{{comment.created.toString()}}</div>
  <div class="message">{{comment.message}}</div>
</div>
</template>

<script>
export default {
    props: [ "comment" ]
}
</script>

Comment 컴포넌트는 꽤 단순하다고 생각했는데 react를 생각하니 딱히 그런 것 같지는 않다.

<template>
    <form class="form" v-on:submit.prevent="onAdd">
      <label>message</label>
      <input ref="message" class="text" type='text' />
      <button type='submit'>submit</button>
    </form>
</template>

<script>
export default {
    model: {
        event: "onAdd"
    },
    methods: {
        onAdd: function(e) {
            this.$emit('onAdd', this.$refs.message.value)
            this.$refs.message.value = ""
        }
    }
}
</script>

CommentForm도 특별한 것은 없는데 여기서 조금 어려웠던 것은 변하는 값에 따라서 button의 상태를 변경하는 것이다. 이건 아직도 해결이 안됐는데 input 값이 변경될때 button의 상태도 변하게 하려면 어떻게 해야할 지 모르겠다. disabled="validate()? '' : 'disabled'" 같은 방법을 써봤는데 값이 바뀔때마다 변경되지는 않았다. 아마도 watch를 위해선 다른 방법을 써야하는 것 같다. 여기까지도 너무 오래 걸려서 지쳤기때문에 vuejs버전은 여기서 중단했다. 하루를 꼬박 사용한 것 같다.

결론

이렇게 react와 vuejs를 비교해가며 체험을 해봤다. 당연하게도 익숙한 react가 모든 부분에서 더 좋게 느껴진다. 하지만 이건 내가 익숙해져서 그런 것일테니 잠시 vuejs를 반드시 사용해야하고 앞으로도 계속 사용해야한다는 입장에 서서 장점을 찾아보기로 했다.

  1. 저용량

    react.js  739 kB
    vuejs.js  273 kB
    

    vuejs의 가장 큰 장점 중 하나가 경량화된 모듈이라는 점이다. 거의 같은 기능을 하는 두개의 프로젝트가 2-3배의 용량 차이가 난다.

  2. 빠른속도
    너무 간단한 프로그램이라 별로 느끼진 못했지만 vuejs는 react에 비해서 2-3배 가량 빠르다고 한다.

이 정도다. 사실 장점이 많지만 react와 비교를 해서 장점을 찾으려니 더 찾기가 어렵다. 오히려 단점이 눈에 더 보인다.

  1. 컴포넌트의 event 처리가 직관적이지 않다.
    v-on, v-if, v-for등의 별도 이벤트를 먼저 익혀야 한다.
  2. 컴포넌트의 인터페이스를 알기 어렵다. prop, model, method로 나눠지고 template에 바인딩되는 것이 자연스러워 보이지 않는다. 이해안되지만 동작이 되는 그런 느낌이다.

종합해보면 어렵다 라는 한마디로 정리가 될 것 같다. 심지어 vue 컴포넌트 같은 것을 활용하기 위해서는 설정도 복잡하다. 가볍게 쓰고 싶지만 배워야 할 것이 너무 많고 제대로 쓰고 싶지만 react가 너무 매력적이다.

이쯤에서 이 비교는 끝! 다음엔 angularjs 2.0을 해봐야겠다. (언제 할지는 모르겠지만...)

반응형