그동안 React를 사용하다보니 새로운 프레임워크에 눈을 잘 못 돌렸는데 -아니 돌릴 필요가 없었는데- 요즘 Vue.js라는 것이 핫하다는 것을 들었다. 궁금해서 문서를 조금 보긴 했지만 구 angularjs와 비슷하다는 느낌만 받았을 뿐 특징같은 건 파악하지 못했다.
주말동안 혼자있는 시간이 많이 생겨서 이 기회에 Vue.js에 대해서 알아보기로 했다.
comment-sample
이전에 angularjs와 handlebar로 댓글 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를 반드시 사용해야하고 앞으로도 계속 사용해야한다는 입장에 서서 장점을 찾아보기로 했다.
저용량
react.js 739 kB vuejs.js 273 kB
vuejs의 가장 큰 장점 중 하나가 경량화된 모듈이라는 점이다. 거의 같은 기능을 하는 두개의 프로젝트가 2-3배의 용량 차이가 난다.
- 빠른속도
너무 간단한 프로그램이라 별로 느끼진 못했지만 vuejs는 react에 비해서 2-3배 가량 빠르다고 한다.
이 정도다. 사실 장점이 많지만 react와 비교를 해서 장점을 찾으려니 더 찾기가 어렵다. 오히려 단점이 눈에 더 보인다.
- 컴포넌트의 event 처리가 직관적이지 않다.
v-on
,v-if
,v-for
등의 별도 이벤트를 먼저 익혀야 한다. - 컴포넌트의 인터페이스를 알기 어렵다. prop, model, method로 나눠지고 template에 바인딩되는 것이 자연스러워 보이지 않는다. 이해안되지만 동작이 되는 그런 느낌이다.
종합해보면 어렵다 라는 한마디로 정리가 될 것 같다. 심지어 vue 컴포넌트 같은 것을 활용하기 위해서는 설정도 복잡하다. 가볍게 쓰고 싶지만 배워야 할 것이 너무 많고 제대로 쓰고 싶지만 react가 너무 매력적이다.
이쯤에서 이 비교는 끝! 다음엔 angularjs 2.0을 해봐야겠다. (언제 할지는 모르겠지만...)