Typescript 가 프론트엔드를 구현할 때 매우 유용하다고 생각합니다. Javascript 를 사용하며 발생하는 암묵적인 형식으로 인한 실수를 최소화 할 수 있고 명시적 타입을 통한 IDEA 지원이 개발 시에 매우 편리합니다.

Vue 에서 편리하게 Universal Application 을 개발할 수 있는 NUXT(https://ko.nuxtjs.org/) 프레임워크와 Typescript 함께 사용하는 방법은 다음과 같습니다. 본 방법은 참고1 https://nuxtjs.org/guide/installation 와 참고2 https://nuxtjs.org/guide/typescript 에 작성된 가이드에 따르며 작성하였습니다. 워낙 간단하게만 기록이 되어 위 방법에 누락된 상세한 과정을 기록하였습니다.

환경 구성

NUXT와 Typscript를 함께 사용하는 환경을 설치합니다.

NUXT 프로젝트 생성

Nuxt 프로젝트를 먼저 생성합니다.

$ yarn create nuxt-app peak-spa

// 다음과 같은 구성으로 프로젝트 생성하였습니다.
? Project name: peak-spa
? Project description: PEAK with SPA
? Author name: lubang
? Choose the package manager: Yarn
? Choose UI framework: Buefy
? Choose custom server framework: None (Recommended)
? Choose Nuxt.js modules: Axios, Progressive Web App (PWA) Support
? Choose linting tools: Prettier
? Choose test framework: Jest
? Choose rendering mode: Universal (SSR)

Package manager 는 속도 때문에 Yarn을 주로 사용하고 CSS Framework는 JQuery 가 없는 Buefy를 선택하였습니다. 문법은 Prettier 를 이용하여 자동화된 포매팅을 제공하고 테스트는 Jest 기반으로 구현하도록 설정하였습니다.

Typscript 빌드 환경 추가

NUXT 기본 프로젝트 구성에 Typescript 모듈을 추가합니다.

$ yarn add -D @nuxt/typescript
$ yarn add ts-node

// Typescript 설정 파일 추가
$ vi tsconfig.json

Typescript 빌드 환경 설정

nuxt.config.js 을 nuxt.config.ts 로 파일명을 변경하고 다음과 같이 수정합니다. 다음의 수정 내역을 참고해주세요.

// nuxt.config.js 원본
export default {
mode: 'universal',
/*
** Headers of the page
*/
head: {
...
}

// nuxt.config.ts 수정본
import NuxtConfiguration from '@nuxt/config'

const config: NuxtConfiguration = {
    mode: 'universal',
    /*
    ** Headers of the page
    */
    head: {
    ...
    }
}

export default config

nuxt.config.js 에 정의된 export 의 값을 NuxtConfiguration 객체 값으로 초기화하여 export 합니다.

Typescript 컴포넌트로 수정

타입스크립트를 이용한 Vue 컴포넌트의 최대 장점은 ‘클래스 기반의 컴포넌트’ 개념으로 설계/구현이 가능하다는 점입니다. 이러한 접근을 도와주는 Annotation 을 vue-property-decorator 모듈을 이용하여 사용합니다.

$ yarn add vue-property-decorator

클래스 형태로 컴포넌트를 정의하는 방법은 다음과 같이 @Component 어노테이션과 Vue를 상속받는 클래스를 선언합니다.

// 기본 컴포넌트 정의
<script>    
import Card from '~/components/Card.vue'

export default {
    name: 'HomePage',

    components: {
        Card
    }
}
</script>

// TS의 클래스 컴포넌트 정의
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
import Card from '~/components/Card.vue'

@Component({
    components: {
        Card
    }
})
export default class HomePage extends Vue {}
</script>

위와 같이 수정하고 $ yarn dev 명령어를 입력하면 다음과 같이 실행된 화면이 실행됩니다.

NUXT 실행화면

Component 지시어: data, computed, method

컴포넌트를 클래스 기반으로 사용 시에는 자주 사용하는 data, computed, methods 를 클래스 멤버변수, 메소드 형식으로 지원할 수 있습니다.

@Component
export default class HelloBanner extends Vue {
    // data 정의
    private title: String = 'Lubang HomePage'

    // computed 정의
    private get friendlyTitle() {
        return 'Hello ' + this.title
    }

    // method 정의
    private handleClicked() {
        console.log('hello')
    }
}

Component 지시어: prop, watch, emit

컴포넌트에서 자주 사용되는 기능으로 prop과 watch는 다음과 같이 쉽게 사용이 가능합니다.

import { Vue, Component, Prop, Watch } from 'vue-property-decorator'

@Component
export default class YourComponent extends Vue {
    @Prop(Number) readonly propA: number | undefined

    @Watch('child')
    private onChildChanged(val: string, oldVal: string) {
        ...
    }

    @Emit('apply-hello-event')
    private applyHelloEvent() {
        return 'lubang'
    }
}

그리고 부모, 자식 컴포넌트 간의 통신 및 이벤트 기반의 처리를 구현할 때 꼭 필요한 emit 을 위의 예제와 같이 쉽게 구현할 수 있습니다. 필요한 컴포넌트에서 on 구독만 추가하면 됩니다.

Component 생명주기

컴포넌트의 생명주기 훅은 다음의 메소드를 정의하면 자동으로 등록됩니다.

// Lifecycle hooks  
private created() { }
private mounted() { }
private updated() { }
private destroyed() { }

개발

JEST 기반의 테스트 코드

import { mount } from '@vue/test-utils'
import Logo from '@/components/Logo.vue'

describe('Logo', () => {
    test('is a Vue instance', () => {
        const wrapper = mount(Logo)
        expect(wrapper.isVueInstance()).toBeTruthy()
    })
})

테스트 실행하면 수행 여부와 커버리지를 확인할 수 있습니다.

➜ $ yarn test
yarn run v1.16.0
$ jest
PASS  test/Logo.spec.js
Logo
    ✓ is a Vue instance (12ms)

-----------|----------|----------|----------|----------|-------------------|
File       |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
-----------|----------|----------|----------|----------|-------------------|
All files  |        0 |      100 |        0 |        0 |                   |
index.vue  |        0 |      100 |        0 |        0 | 26,27,34,36,40,45 |
-----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        2.596s
Ran all test suites.
✨  Done in 3.79s.

개발 시 참고 사항

Vuex 를 이용한 상태관리는 vuex-class 모듈을 이용하여 어노테이션으로 쉽게 사용할 수 있습니다. Typescript 를 이용하여 Vuex를 구성하는 방법은 참고4 https://codeburst.io/vuex-and-typescript-3427ba78cfa8 을 참고하여 구현합니다.

참고5 Why I use vue-class-component 글에는 위에서 언급된 것 외에도 Vue 클래스 컴포넌트의 장점이 정리되어 있습니다.