요즘 회사에서 레거시 시스템의 재개발을 추진하고 있어서, 평소 관심있게 가지고 놀던 Vue.js를 본격적으로 파기 시작했다. 새로운 레거시의 탄생을 막기 위해 고려해야할 점들 안에서, 강타입 컴파일 언어가 요구 사항으로 거론되었다. 문제는 현재 시점(Vue.js의 최신 버전 2.6.8)에서 Vue.js의 공식 문서는 모두 JavaScript의 사용을 전제로 쓰여져 있다는 점이다.
하지만 Vue.js에서도 TypeScript에 대한 지원을 강화하려는 조짐이 있고, 핵심 멤버들에 의해 관리되고 있는 Vue CLI가 버전 3.0부터 완벽하게 TypeScript 개발 환경을 지원하고 있기 때문에 공식 문서가 JavaScript를 기준으로 쓰여져 있음에도 불구하고 TypeScript를 사용하는 Vue를 소개하려 한다.
왜 TypeScript를 사용해야 하는가에 대한 짧으면서도 좋은 비디오가 있어 소개한다.
vue-class-component
Vue를 TypeScript와 함께 사용하려 할 때 한 가지 더 고려해야 하는 것이 있다. 바로 클래스 스타일 컴포넌트이다. vue-class-component는 클래스 스타일의 Vue 컴포넌트를 사용할 수 있게 해주는 라이브러리로, Vue CLI와 마찬가지로 Vue.js의 핵심 멤버에 의해 관리되고 있다. 클래스 스타일 컴포넌트를 왜 고려해야 하는지 본격적으로 알아보기 전에, JavaScript를 이용한 기본 Vue.js 스타일의 컴포넌트와 TypeScript를 이용한 클래스 스타일의 컴포넌트를 직접 비교해보자.
The component of Vanilla Vue
1 | <script> |
2 | export default { |
3 | /** component name */ |
4 | name: 'hello-vue', |
5 | |
6 | /** props */ |
7 | props: ['val'], |
8 | |
9 | /** data */ |
10 | data() { |
11 | return { |
12 | value: this.val, |
13 | inputValue: '', |
14 | }; |
15 | }, |
16 | |
17 | /** lifycycle hooks */ |
18 | mounted() { |
19 | console.log('mounted'); |
20 | }, |
21 | /** computed */ |
22 | computed: { |
23 | isDisabled() { |
24 | return this.inputValue === ''; |
25 | }, |
26 | }, |
27 | |
28 | /** methods */ |
29 | methods: { |
30 | handleInput($event) { |
31 | this.inputValue = $event.target.value; |
32 | }, |
33 | handleClick() { |
34 | if (this.inputValue === '') { |
35 | return; |
36 | } |
37 | this.value = this.inputValue; |
38 | this.inputValue = ''; |
39 | |
40 | /** emit custom event */ |
41 | this.$emit('handle-click', this.value); |
42 | }, |
43 | }, |
44 | |
45 | /** watch */ |
46 | watch: { |
47 | value(newValue, oldValue) { |
48 | console.log(`watch: ${newValue}, ${oldValue}`); |
49 | }, |
50 | }, |
51 | |
52 | /** filters */ |
53 | filters: { |
54 | convertUpperCase(value) { |
55 | if (!value) return; |
56 | return value.toUpperCase(); |
57 | }, |
58 | }, |
59 | }; |
60 | </script> |
Class style Vue component with TypeScript
1 | <script lang="ts"> |
2 | import { Component, Prop, Emit, Watch, Vue } from 'vue-property-decorator'; |
3 | |
4 | @Component({ |
5 | /** filters */ |
6 | filters: { |
7 | convertUpperCase(value: string): string | null { |
8 | if (!value) { |
9 | return null; |
10 | } |
11 | return value.toUpperCase(); |
12 | }, |
13 | }, |
14 | }) |
15 | export default class HelloVue extends Vue { |
16 | /** props */ |
17 | @Prop() val!: string; |
18 | |
19 | /** data */ |
20 | value: string = this.val; |
21 | inputValue: string = ''; |
22 | |
23 | /** emit */ |
24 | @Emit('handle-click') |
25 | clickButton(val: string): void {} |
26 | |
27 | /** watch */ |
28 | @Watch('value') |
29 | onValueChange(newValue: string, oldValue: string): void { |
30 | console.log(`watch: ${newValue}, ${oldValue}`); |
31 | } |
32 | |
33 | /** computed */ |
34 | get isDisabled(): boolean { |
35 | return this.inputValue === ''; |
36 | } |
37 | |
38 | /** lifecycle hook */ |
39 | mounted(): void { |
40 | console.log('mounted'); |
41 | } |
42 | |
43 | /** methods */ |
44 | handleInput($event: Event): void { |
45 | this.inputValue = (($event.target as any) as HTMLInputElement).value; |
46 | } |
47 | handleClick(): void { |
48 | if (this.inputValue === '') { |
49 | return; |
50 | } |
51 | this.value = this.inputValue; |
52 | this.inputValue = ''; |
53 | this.clickButton(this.value); |
54 | } |
55 | } |
56 | </script> |
어느 쪽이 더 가독성이 좋은가? 클래스 스타일 컴포넌트를 이용하면 하위 컴포넌트와 프로퍼티, 데이터 등을 우리가 익숙한 클래스 스타일로 관리하는 것이 가능해진다.
Create the project
node나 Vue CLI의 설치는 공식 문서에 자세히 설명되어 있기 때문에 생략한다.
Vue CLI 버전 3이 정상적으로 설치된 상태라면 아래 명령어를 통해 Vue 프로젝트를 생성할 수 있다.
1 | vue create vue-ts-front |
그러면 새로운 Vue 프로젝트에 사용할 기술 스택 옵션을 선택할 수 있다. 본 튜토리얼에서는 다음과 같은 설정으로 진행하려 한다.
프로젝트의 생성이 완료되면 다음과 같은 파일 구조가 완성될 것이다.
1 | . |
2 | ├── dist // Compiled output will come here |
3 | ├── node_modules // Libraries |
4 | ├── public |
5 | │ └── index.html // The main app file |
6 | ├── src |
7 | │ ├── assets |
8 | │ ├── components |
9 | │ ├── views |
10 | │ ├── App.vue |
11 | │ ├── main.ts // TypeScript file that drives the app |
12 | │ ├── router.ts // Router for SPA |
13 | │ └── store.ts |
14 | └── tests |
프로젝트 디렉토리로 이동한 뒤 개발 모드로 로컬 웹서버를 올려 확인해보자.
1 | npm run serve |
브라우저에서 localhost:8080
으로 접속하면 기본적으로 만들어진 웹 어플리케이션이 정상적으로 동작하고 있는 것을 확인할 수 있다.