💡 프로젝트 내에 새로운 페이지를 만들어야 했었는데, 만들고 나서 보니, 기존에 있던 페이지와 거의 동일한 UI를 가지고 있음을 깨달았다. 페이지 구조나 안에 들어가는 버튼들이 모두 동일했고, 두 페이지의 다른 점은 글씨나 테이블 필드명 정도 였다. 그래서 공통화를 하기로 결심!! 했다. 공통화를 하면서 겪었던 과정이 담긴 이야기를 해보려고 한다.
Vue.js (version 2) + TypeScript (+ UI는 Element UI 라이브러리 사용)
<script lang="ts">
// Component
import { Component, Mixins } from 'vue-mixin-decorator';
// Mixins
import Table from '경로';
export default class ListView extends Mixins<Table>(Table) {}
</script>
💡 Mixin은 여러 컴포넌트 간에 공통으로 사용하고 있는 로직, 기능들을 정의해두고 필요한 곳에 Vue 컴포넌트와 Mixin 파일을 결합해서 사용한다.
💡 Mixin과 컴포넌트의 차이!
- Mixin은 하나의 파일 처럼 동작 + 믹스인 파일에 작성한 변수들을 사용 가능 (인식함)
- 컴포넌트는 독립적인 파일로 동작 + 작성한 변수들 사용 불가능 (인식 못함)
<template>
<el-custom-main :title="titleObj.title" :isWhite="false">
<template slot="content">
<el-custom-card>
<template slot="content">
<el-row>
<el-col>
<span>
<i class="fa fa-align-justify"></i>
{{ titleObj.listTitle }} 목록
({{contentObj.totalCount}}건)
</span>
</el-col>
<el-col>
<el-button type="primary" @click="clickRegister">
{{ titleObj.registerName }} 등록
</el-button>
</el-col>
</el-row>
<el-custom-table
:content="contentObj.items"
:columns="fields"
>
<template slot="fundName" slot-scope="data">
<span @click="clickName(data.row)">
{{ data.row.fundName }}
</span>
</template>
<div slot="management" slot-scope="data">
<el-button
type="default"
plain
@click="clickEdit(data.row)"
>수정</el-button>
</div>
</el-custom-table>
</template>
</el-custom-card>
</template>
</el-custom-main>
</template>
나의 경우는 위와 같은 템플릿을 사용한 것이고, 각자가 원하는 UI 템플릿을 적어주면 된다.
프로젝트 내에서 Element UI를 사용했기 때문에, 태그들은 라이브러리에서 제공해주는 태그들이다.
@Prop(Object) titleObj: Title; /** title: 화면 전체 타이틀, listTitle: 목록 타이틀, registerName: 등록 버튼 이름 */
@Prop(Array) fields: Fields[]; /** 테이블 필드 */
@Prop(Object) contentObj; /** 테이블 데이터 */
mounted() {
this.loadData();
}
loadData() {
this.$emit('load-data', this.currentPage - 1, this.limit);
}
clickRegister() {
this.$emit('click-register');
}
clickName(data) {
this.$emit('click-name', data);
}
clickEdit(data) {
this.$emit('click-edit', data);
}
하위 컴포넌트에서 상위 컴포넌트의 메서드를 호출할 때는 emit을 활용한다.
메서드에 대해 정의한 명칭을 적어주고, 두번째로 들어가는 인자는 상위 컴포넌트에 존재하는 메서드에 전달할 값을 의미한다.
즉, 메서드를 호출함과 동시에 메서드에 필요한 값도 전달하고 싶다면 두번째 인자에 넣어주기만 하면 된다!
/** 등록 버튼 클릭 시 */
clickRegister() {
// 원하는 동작 (ex. 페이지 이동)
}
/** 이름 클릭 시 */
clickName(data) {
// 원하는 동작 (ex. 페이지 이동)
}
/** 수정 버튼 클릭 시 */
clickEdit(data) {
// 원하는 동작 (ex. 페이지 이동)
}
<template>
<w-list-view
:titleObj="{ title: '제목', listTitle: '제목', registerName: '이름' }"
:fields="fields"
:contentObj="content"
@load-data="loadData"
@click-register="clickRegister"
@click-name="clickName"
@click-edit="clickEdit"
></w-management-view>
</template>
// Components
import ListView from '경로';
@Component({
components: {
ListView,
},
})
컴포넌트에 사용한 ":"은 v-bind의 줄임말, "@"는 v-on의 줄임말이다.
v-bind는 한 마디로 데이터를 연결하겠다는 의미이고, v-on은 이벤트와 관련된 동작을 위해 사용한다.
이벤트명은 공통 컴포넌트에서 emit을 사용할 때 적어주었던 이벤트명을 그대로 사용해주어야 한다. (이벤트 발생 시 실행되는 메서드 이름은 아무거나 상관없다.)
💡 이렇게 해서 공통회 작업을 마쳤다. 공통화 작업을 진행하면서 어떻게 해야 좀 더 효율적이고 여러 파일에서 사용이 가능할까를 고민하면서 했던 것 같다. 지금 내가 한 작업이 별거 아닐지 모르지만, 이 공통화 작업을 통해 불필요한 코드를 줄이고 코드의 일관성과 통일성은 높였다는 생각이든다!!! 한번 해봤으니 앞으로도 이런 작업을 두려워하지말고 해나아가자!!