이번에는 좀 다른 이야기를 해볼까 합니다. VueJS 프로젝트를 vue-cli를 사용해 Typescript를 사용하도록 생성하면 기본으로 들어가 있는 vue-property-decorator 를 사용하면 VueJS를 Typescript와 클래스 형태로 사용하기 좋지만, 그대로 사용하게 되면 코드들이 완전히 클래스 형태로 사용한다는 느낌은 좀 덜 받게 됩니다.
그러나 여전히 마음에 안드는 부분이 있습니다. filters 부분인데요 얘네들도 결국엔 function 들인데 클래스 안쪽에서 정의할 수는 없을까? 하는 생각이 들었습니다. (Filter에 대한 설명은 vue공식페이지에서 참조하시기 바랍니다.)
물론 이를 해결해주는 라이브러리가 당연히 있습니다. vue-ts-decorate라는 녀석인데요, 사용하려고 고려해 보았으나, last publish가 2년 전입니다. 시시각각 변하는 frontend 생태계에서 마지막 업데이트가 2년전이라면 죽은 프로젝트라고 보아도 될 것 같다는 판단이네요. 그리고 또 한가지 마음에 들지 않는 점은, Filter뿐만 아니라 기존에 vue-property-decorator 가 제공하는 데코레이터들(Prop, Watch 등)도 중복해서 지원하고, 이를 적용하기 위해선 기존 코드들도 vue-ts-decorate를 적용하도록 변경해야 하는 것 처럼 보였습니다.
새로 정의한 Filter 데코레이터를 import 해주고, import { Filter } from '@/utils/Decorators'; 클래스 내부에 @Filter() 데코레이터와 함께 필터의 내용을 작성해 줍니다. (여기서 주의할 점은 필터는 순수함수로만 만들어져야 합니다. this 키워드를 통해 함수 외부의 변수는 사용할 수 없습니다.)
state, getter, mutation, action 들은 위와 같은 형식으로 적용하여 사용합니다. 컴포넌트에서 incr 액션함수를 호출하면, 함수가 return 하는 delta라는 값으로 increment mutation을 commit 하게 됩니다. 설명이 약간 어설픈데요, 결국 위에 선언한 incr 이라는 이름의 Action 은 아래 코드와 동일합니다. (namespace를 사용하지 않으려면 @Module 데코레이터의 내용을 빼고 작성합니다.)
과 같이 선언하며, ‘change’와같이 emit되는 이벤트 명을 넣지 않을 경우, 함수 명을 이벤트 명으로 사용하게 됩니다. (단, 이벤트명을 넣지 않아 자동으로 이벤트 emit이 선언된 경우에는 카멜 케이스의 함수명이 케밥 케이스의 함수명으로 변경되어 적용됩니다. 예: onChange -> on-change)
<template> <div class="home"> <img alt="Vue logo" src="../assets/logo.png"> <HelloWorld msg="Welcome to Your Vue.js + TypeScript App" @onClick="clickEventHandler" /> </div> </template>
<script lang="ts"> import { Component, Vue } from 'vue-property-decorator'; import HelloWorld from '@/components/HelloWorld.vue'; // @ is an alias to /src
@Component({ components: { HelloWorld, }, }) export default class Home extends Vue {
.hello 클래스를 가진 div에 클릭을 할 경우 onClick함수를 실행시키는 버튼을 추가 했습니다. 그리고 onClick() 함수 앞에 Emit(‘onClick’) 이라고 Emit 데코레이터를 선언 했습니다. 이제 이 onClick() 함수가 실행되면 부모 클래스로 onClick 이벤트를 Emit 합니다.
이 Emit 데코레이터는 @Model 데코레이터와 함께 많이 사용되며, 이벤트 바인딩을 할때 필수기 때문에 잘 봐두어야 합니다.
<template> <div class="home"> <img alt="Vue logo" src="../assets/logo.png"> <HelloWorld msg="Welcome to Your Vue.js + TypeScript App" @onClick="clickEventHandler" /> </div> </template>
<script lang="ts"> import { Component, Vue } from 'vue-property-decorator'; import HelloWorld from '@/components/HelloWorld.vue'; // @ is an alias to /src
@Component({ components: { HelloWorld, }, }) export default class Home extends Vue {
<script lang="ts"> import { Component, Vue, Watch } from 'vue-property-decorator'; import HelloWorld from '@/components/HelloWorld.vue'; // @ is an alias to /src
@Component({ components: { HelloWorld, }, }) export default class Home extends Vue {
private count: number = 0;
} </script>
count 변수를 새로 넣었고, 이를 화면에 출력하도록 했습니다. 그리고 HelloWorld 컴포넌트에는 count 변수를 v-model로 양방향 바인딩을 했습니다.
저는 vscode를 주로 사용하는데요, Typescript 문법을 완벽지원할뿐 아니라 플러그인을 통해 vuejs와의 궁합도 매우 좋아 추천하는 에디터입니다. 아톰 에디터나 intelliJ를 사용해도 문제 없으니 익숙한 도구로 사용하시면 좋을 것 같아요.
추천하는 플러그인 vscode의 plugin 영역에서 vue.js extension pack 로 검색하면 나오는 플러그인팩을 추천합니다. 이거 하나 설치하면 한번에 여러개(Auto Close Tag, Vetur 등등)가 설치가 되는데요, 같이 설치되는 플러그인 중 eslint는 제거하고, tslint로 사용하고 있어요.
에디터 셋팅이 끝났다면, 해당 에디터를 사용해서 생성된 project 폴더를 여시기 바랍니다.
물론 폴더 구조는 입맛에 따라 바꿔서 사용하기도 합니다.
1 2 3 4 5 6 7 8 9 10
src - 소스 폴더입니다. 주로 다루게 될 폴더죠. -assets - static한 이미지 등의 파일이 위치합니다. -components - 컴포넌트들이 위치합니다. 화면을 구성하는 작은 조각들이라고 생각하시면 됩니다. -views - 컴포넌트들을 모아 구성되는 화면들의 view라고 보면 됩니다. vuex를 사용하여 컴포넌트들에게 데이터를 바인딩하거나 액션을 보내기도 합니다. -App.vue - 앱의 기본 컴포넌트입니다. 엔트리 포인트인 main.ts에서 호출하는 최초의 Vue 컴포넌트. -main.ts - 앱이 호출되면 실행되는 엔트리 포인트입니다. 이 ts 파일에서 router, store, App.vue 파일들을 불러옵니다. -router.ts - 앱의 라우팅 설정읻 들어간 파일입니다. 라우터에 대해서는 추후 설명할 예정입니다. -store.ts - 앱의 상태관리를 담당하는 store 입니다. package.json - 패키지매니저(npm 또는 yarn)에서 관리하는, dependency 관련 설정 파일입니다. tsconfig.json - typescript 컴파일 관련 설정파일입니다.
이정도로만 간단히 필요한 파일들만 설명하고 넘어가도록 하겠습니다.
그럼, 이 프로젝트의 메인 컴포넌트인 App.vue를 열어보도록 하겠습니다.
상단의 <template> 태그로 html 형태로 구선된 템플릿 영역이 보이고, 하단에는 scss로 작성된 <style>이 보입니다. template 내부에 HOME, ABOUT으로 라우터 링크가 걸려 있네요. 그리고 <router-view/> 태그도 보입니다. router에 대해서는 추후 설명하겠지만, router-view 태그는 router에서 설정된 대로 화면을 그려준다고 일단 설명 드리겠습니다.
/views/Home.vue 파일을 열어봅시다.
최상단에는 App.vue파일과 마찬가지로 <template>영역이 있습니다. 그리고 바로 하단에 <script lang=”ts”>영역이 있습니다. 이곳에 component를 구성하는 Typescript로 작성된 class들이 들어갑니다.
1
import { Component, Vue } from 'vue-property-decorator';
우리는 클래스 스타일 컴포넌트를 좀 더 편하게 사용하기 위해 vue-property-decorator를 사욯합니다. @Component 데코레이터 외에 우리가 자주 사용하게 될 @Prop, @Emit 등의 데코레이터를 제공합니다.
1
import HelloWorld from '@/components/HelloWorld.vue'; // @ is an alias to /src
그 바로 아랫줄엔 HelloWorld 컴포넌트를 import 하는 구문이 들어가 있습니다. vue 파일을 import 할 때, ‘@/component/‘ 형식으로 절대경로 참조가 가능합니다. 혹은 ‘../components/HelloWorld.vue’와 같은 형식으로 상대경로 참조도 가능합니다.
1 2 3 4 5 6 7 8
@Component({ components: { HelloWorld, }, }) export default class Home extends Vue {
}
그리고 그 아래엔 @Component 데코레이터 안에, 이 컴포넌트에서 사용하는 다른 컴포넌트들을 components: 라는 값으로 나열해 줍니다. 이렇게 components 항목에 HelloWorld 컴포넌트를 추가해 주었기 때문에, 상단 템플릿 영역에서 <HelloWorld>와 같이 컴포넌트를 사용할 수 있게 됩니다.
1
<HelloWorld msg="Welcome to Your Vue.js + TypeScript App"/>
그렇다면 우리가 msg property로 넘긴 Welcom to Your Vue.js ~~~ 는 어떻게 HelloWorld 컴포넌트에서 불러올 수 있게 될까요?
/src/components/HelloWorld.vue 파일을 열어 봅시다.
line: 12 를 보면 msg라고 선언한 변수 앞에 @Prop() 이라는 데코레이터를 볼 수 있습니다. 바로 Prop이라는 데코레이터와 함께 변수를 선언 하면, 해당 변수 명의 prop을 바인딩 해서 받아올 수 있습니다.