이번에는 vue.js에서 Components를 사용하여, 코드를 분리하는 방법에 대해서 알아보겠습니다. vue.js가 SPA를 가능하게 하는 Frontend 프레임워크 입니다. 하지만 엄청난 코드를 한개의 vue 파일로 만드는 것은 상상하기 힘듭니다. 이에, vue에서는 각 기능이나 화면에 대해서 부분 부분을 분리하여 각 조각별로 개발을 진행하게 됩니다.
이를 Components라고 하는데, 당장 생각해 봐도... 이렇게 기능을 혹은 화면을 분리해서 개발한다고 하면 필요할때 해당 기능을 조립해서 적용할 수도 있고, 결국 코드의 재사용가 가능해 집니다.
이때, Components에도 관계가 형성되는데 보통은 Parent / Child로 구분하게 됩니다. 물론 이 둘 사이에 데이터의 이동도 가능합니다. 간단하게 관계를 살펴보면 아래와 같습니다.
Parent Components는 Child Components를 import하고 Props를 설정하여 데이터를 보낼 수 있으며, Child Components는 Emit Event를 통해서 Parent Components에 데이터를 전달할 수 있습니다. 바로 전에 ChartTest를 진행할 때에도... 이 Components를 사용해서 구현했었는데요.
이번에는 App.vue를 Components화 해서 App Bar와 Footer를 분리하는 것으로 이해를 해보도록 하겠습니다.
1. App.vue의 코드 분리
우선 vue project내부에 src > components에 폴더를 통해서 분리구조를 생성합니다.
저는 layout과 관련된 코드는 components폴더 하위에 layout폴더를 생성하여 관리하고자 합니다. 우선 기존코드를 확인해 보겠습니다.
[App.vue - Components 분리전]
<template>
<v-app id="App">
<v-app-bar app dense color="#689F38" dark>
<v-app-bar-nav-icon @click.stop="drawer = !drawer"></v-app-bar-nav-icon>
<v-app-bar-title>AT Project</v-app-bar-title>
<v-spacer></v-spacer>
<v-btn icon color="white" to="GridTest">
<v-icon>mdi-battery-charging</v-icon>
</v-btn>
<v-btn icon color="white" to="ChartTest">
<v-icon>mdi-account</v-icon>
</v-btn>
<v-menu offset-y>
<template v-slot:activator="{ on, attrs }">
<v-btn icon color="yellow" v-bind="attrs" v-on="on">
<v-icon>mdi-dots-vertical</v-icon>
</v-btn>
</template>
<v-list>
<v-list-item v-for="n in 5" :key="n" @click="() => {}">
<v-list-item-title>Option {{ n }}</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
</v-app-bar>
<v-navigation-drawer v-model="drawer" absolute temporary>
</v-navigation-drawer>
<v-content>
<router-view/>
</v-content>
<v-footer dark padless tile>
<v-card tile color="#689F38" dark center class="white--text text-center" width="100%">
<v-card-text>
<v-btn v-for="icon in icons" :key="icon" class="mx-4 white--text" icon>
<v-icon size="24px">{{ icon }}</v-icon>
</v-btn>
</v-card-text>
<v-card-text class="white--text pt-0">
This is the Ayotera Lab's AT Project test vue.js web page.
</v-card-text>
<v-divider></v-divider>
<v-card-text class="white--text">
{{ new Date().getFullYear() }} <strong>Ayotera Lab</strong>
</v-card-text>
</v-card>
</v-footer>
</v-app>
</template>
코드는 이렇습니다. 이제... 상단의 App Bar와 하단의 Footer를 component로 분리하겠습니다. 분리하는 방법은 간단합니다. 왜냐하면 우선은 단순하게 분리하고자 하는 코드부분을 잘라서 신규 vue 파일에 <template/> 안에다가 넣어주면 되기 때문입니다.
물론 그 코드에서 사용하는 data( )는 함께 옮겨 줘야 합니다.
[AppBar.vue]
<template>
<div>
<v-app-bar app dense color="#689F38" dark>
<v-app-bar-nav-icon @click.stop="drawer = !drawer"></v-app-bar-nav-icon>
<v-app-bar-title>AT Project</v-app-bar-title>
<v-spacer></v-spacer>
<v-btn icon color="white" to="GridTest">
<v-icon>mdi-battery-charging</v-icon>
</v-btn>
<v-btn icon color="white" to="ChartTest">
<v-icon>mdi-account</v-icon>
</v-btn>
<v-menu offset-y>
<template v-slot:activator="{ on, attrs }">
<v-btn icon color="yellow" v-bind="attrs" v-on="on">
<v-icon>mdi-dots-vertical</v-icon>
</v-btn>
</template>
<v-list>
<v-list-item v-for="n in 5" :key="n" @click="() => {}">
<v-list-item-title>Option {{ n }}</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
</v-app-bar>
<v-navigation-drawer v-model="drawer" absolute temporary>
</v-navigation-drawer>
</div>
</template>
우선 AppBar에 해당하는 <v-app-bar/> 및 그 이하 tag를 모두 옮겼고, <v-navigation-drawer/>를 옮겼습니다. 그런데... 위에서 설명했던 것과 사뭇 다르게... 단순히 잘라서 옮기는 것이 아닌 <div/>사이에 넣어줬습니다. 그 이유는 아래와 같이 에러를 발생하기 때문입니다.
발생한 Error
Component template should contain exactly one root element.
If you are using v-if on multiple elements, use v-else-if to chain them instead.
<template/>은 단 한개의 root element로 구성되어야 합니다. 위의 코드에서 <div/>가 없다면... root element는 <v-app-bar/>와 <v-navigation-drawer/>이렇게 2개가 존재하게 됩니다. 이럴경우에는 v-if를 통해서 분기하여 구성하라는 것 입니다. 우리는 두개가 모두 필요하기 때문에 강제로 root element를 구성해 줘야 합니다.
[Footer.vue]
<template>
<v-footer dark padless tile>
<v-card tile color="#689F38" dark center class="white--text text-center" width="100%">
<v-card-text>
<v-btn v-for="icon in icons" :key="icon" class="mx-4 white--text" icon>
<v-icon size="24px">{{ icon }}</v-icon>
</v-btn>
</v-card-text>
<v-card-text class="white--text pt-0">
This is the Ayotera Lab's AT Project test vue.js web page.
</v-card-text>
<v-divider></v-divider>
<v-card-text class="white--text">
{{ new Date().getFullYear() }} — <strong>Ayotera Lab</strong>
</v-card-text>
</v-card>
</v-footer>
</template>
이 코드가 진심으로 잘라다가 붙여서 구성한 코드입니다. 이렇게 Components로 구성하기 위한 준비를 마쳤습니다.
2. Components 사용하기
위에서 기존의 App.vue의 코드를 App Bar / Footer 로 분리하였습니다. 이렇게 분리된 단위 vue 코드를 Components라고 합니다. 그렇다면, 분리된 vue Components를 어떻게 사용을 해야 할까요?? 사용하는 방법은 간단합니다.
우선 Components를 호출하고자 하는 vue에서 등록을 하고, 등록된 Components 명으로 tagging하여 호출합니다. 이때, 기존에 지정된 props가 있으면 해당 props명으로 data를 전달합니다.
[Components import]
<script>
import MainAppBar from '@/components/layout/AppBar';
import MainFooter from '@/components/layout/Footer';
export default {
name: 'App',
components: {
MainAppBar,
MainFooter,
},
Components를 사용하기 위해서는 해당 vue파일에서 어떤 vue Component를 사용할지를 알게해줘야 합니다. 이를 가능하게 하기 위해서는 <script> 내부에 import를 사용해서 명시적으로 파일의 위치를 지정하고 자신이 원하는 alias로 매핑해 줍니다. 그리고 지역 Component로 변수화하여 등록해 줍니다.
이제는 등록된 변수명으로 사용이 가능해 졌습니다.
[Components tagging]
<template>
<v-app id="App">
<main-app-bar></main-app-bar>
<v-content>
<router-view/>
</v-content>
<main-footer></main-footer>
</v-app>
</template>
지역 Components로 등록된 대상은 아래의 규칙으로 사용이 가능합니다.
- 첫 대문자는 소문자로 변경
- 중간에 CamelCase처럼 대문자로 구성되어 있는 경우는 '-' 가 앞에 붙고 소문자로 변경
따라서, 지역 Components로 등록된 변수 중 MainAppBar는 main-app-bar로 변경후 tagging하여 사용합니다.
<main-app-bar></main-app-bar>
<main-footer></main-footer>
어떤가요?? App.vue의 코드가 엄청나게 간단해 졌네요. 이렇게 모듈화는 코드의 재사용성을 극대화하고 추후 코드의 유지보수도 용이하게 해줍니다.
또한, 해당 예제에서는 대상이 없지만, Send Props도 이 부분에서 일어나게 됩니다.
<chart-test :chart-data="dataCollection"></chart-test>
기존에 알아봤던 vue-chartjs 부분에서 구현된 내용입니다. 해당방식으로 데이터가 이동하게 됩니다. 참조를 위해서 링크를 걸어두겠습니다.
2021/03/08 - [Vue.js] - [Vue.js] 14. use Vue chart plugin
0. 단순 Error 처리
최초에 vue project의 생성을 위해서 vue-cli를 사용했습니다. App.vue도 이때 최초 구성되는 코드입니다. 하지만 개발을 진행하면서 웹브라우저의 개발자도구를 통해서 확인해 보면, 아래의 알람이 발생합니다.
[Vuetify] [UPGRADE] 'v-content' is deprecated, use 'v-main' instead.
본문에 해당하는 <v-content/>를 비추천하고, <v-main/>를 사용하라고 합니다.
<template>
<v-app id="App">
<main-app-bar></main-app-bar>
<v-main>
<router-view/>
</v-main>
<main-footer></main-footer>
</v-app>
</template>
고민없이 바꾸어 주면, 알람이 사라집니다.
- Ayotera Lab -
댓글