Options API의 장단점
Vue에서 기본적으로 사용하고 있는 패턴은 객체 내에 다양한 속성을 사용하고 있는 형태이다. 라이프사이클을 제어하기 위해 사용하는 mounted, updated 그리고 컴포넌트 내에서 여러 메서드들을 정의하고 데이터들을 참조할 때 정해진 속성에 정의해서 사용하였다. 이를 Options API라고 한다.
아래는 페이지네이션 기능 컴포넌트를 구현할 때 작성했던 코드인데, 컴포지션 API를 적용하지 않은 options api
방식으로 작성하였다.
export default {
components: {
//컴포넌트 정의
ChildComponent
},
data() {
//참조할 데이터 정의
return {
newsItem: {},
showModal: false,
page: 1,
records: 20,
listItems: [],
options: {
hideCount: true
}
};
},
mounted: function () {
//마운트 시점 제어
this.fetchData();
},
computed: {
//속성 참조
totalLength() {
return this.records;
}
},
methods: {
//컴포넌트 내 사용 메서드 정의
fetchData: function () {
fetch(`https://api.hnpwa.com/v0/news/${this.page}.json`)
.then((response) => response.json())
.then((items) => {
this.records = items.length * 10;
this.listItems = items;
});
},
changePage: function (page) {
this.page = page;
return this.fetchData();
},
viewNewsContent: function (newsItem) {
this.newsItem = newsItem;
this.showModal = !this.showModal;
},
close: function () {
this.showModal = false;
},
formatLocaleTime: function (value) {
const fullMs = value.toString().padEnd(13, 0);
return new Date(Number(fullMs)).toLocaleDateString();
}
}
};
장점은 명확한 구성이다. 메서드를 수정하거나 추가하려면 methods
속성을 손보면되고, 참조 데이터를 수정하려면 data, computed 속성을 보면된다. 이렇게 각각의 역할이 명확하면 유지보수가 용이할 수 있으나 소스코드가 복잡한 컴포넌트라면 상황은 달라진다.
하나의 data
를 수정하려면 이를 사용하는 메서드까지 수정해야하는데 코드가 길어질수록 가독성이 점점 떨어질 수 밖에 없다. 로직적인 부분에서 관심사가 서로 분리되어있기 때문에 추적하기 불편할것이다.
Composition API 소개
공식 문서에서는 Composition API의 중요한 특징 중 하나로 관심사의 구분이라고 설명하고 있다. 옵션 API는 역할별로 구분을 하였다면 컴포지션 API 패턴에서는 논리적 관심사끼리 구분을 하기 편하게 설계되어있다. 무슨말이냐하면..
기존 옵션 API 패턴에서 관심사를 구분하면 이리저리 흩어져 있다. 같은 색상 영역 코드가 서로 참조되어 있어 같이 수정을 해야한다. 이를 컴포지션 API 형태로 변환하면 아래와 같이 관심사가 한 부분에 모여질 수 있어 가독성이 향상된다.
import axios from 'axios';
import Pagination from 'v-pagination-3';
import Modal from './common/Modal.vue';
import { ref, onMounted } from 'vue';
onMounted(function () {
getPost();
});
/** 모달 제어 */
const showModal = ref(false);
const edit = ref(false);
function openModal(id) {
edit.value = id > 0;
postId.value = id;
showModal.value = !showModal.value;
}
function closeModal(isRefresh) {
showModal.value = false;
return isRefresh ? getPost() : false;
}
/** API 통신 */
const page = ref(1);
const records = ref(0);
const listItems = ref([]);
const postId = ref(0);
function getPost() {
axios.get(`http://localhost:8088/board?_page=${page.value}&_limit=10`).then(function (res) {
if (res.status === 200) {
records.value = res.headers['x-total-count'] || 0;
listItems.value = res.data;
}
});
}
/** 페이징 처리 */
function changePage(tobePage) {
page.value = tobePage;
getPost();
}
Composition API 사용법
setup
setup
메서드를 통해 Composition API를 사용할 수 있다.
변수 선언은 ref
함수를 사용하고 변수를 사용하려면 반환을 해주어야한다.
<template>
<h2>{{ message }}</h2>
</tempate>
import { ref } from 'vue';
export default {
setup() {
const message = ref('message');
return {
message
};
}
};
함수 선언
함수를 선언하려면 setup
메서드안에 사용 할 함수를 작성해주면된다. 참고로 변수값에 접근 하려면 value
속성에 접근하면 된다.
<template>
<div>
<button type="button" @click="changeMessage">click!</button>
<p>{{ message }}</p>
</div>
</tempate>
export default {
setup() {
const message = ref('message');
function changeMessage() {
message.value = 'change message!!'; //message 변수 값 변경
}
return {
message
};
}
};
라이프사이클 hook
컴포넌트 라이프사이클을 제어하려면 hook을 import시키면 된다.
- onMounted,onUnmounted
- onUpdated, onBeforeUpdate
- onBeforeMount, onBeforeUnmount
import { ref, onMounted } from 'vue'
// 컴포넌트 내부
setup () {
const page = ref(1)
function getPost() {
axios.get(`http://localhost:8088/board?_page=${page.value}&_limit=10`).then(function (res) {
console.log(JSON.stringify(res));
});
}
onMounted(getPost); //mounted 단계에서 'getPost' 호출
return {
repositories,
getUserRepositories
}
}
Computed, Watch
computed
속성과 watch
속성은 import시켜서 사용할 수 있다.
<template>
<div>
<h2>{{ message }}</h2> <!-- hello -->
<h2>{{ reverseMessage }}</h2> <!-- olleh -->
</div>
</tempate>
//computed 방식
import {ref, computed, watch } from 'vue'
setup () {
const message = ref('hello');
const reverseMessage = computed(function(){
return message.value.split('').reverse().join('');
});
}
//watch 방식
import {ref, watch } from 'vue'
setup () {
const message = ref('hello');
const reverseMessage = ref('');
//참조대상, 콜백함수(바뀌는 값, 기존 값)
watch(message, function(newValue, oldValue){
reverseMessage.value = oldValue.value.split('').reverse().join('');
});
}
script setup
<script setup>
을 통해 스크립트를 선언하면 코드를 더욱 간결하게 줄일 수 있다. 변수 선언 시 return
문을 생략할 수 있으며 props, emit등을 선언할 때 defineProps
와 defineEmits
를 사용하는데 TypeScript도 지원한다!
<script setup>
import {ref, defineProps, defineEmits} from 'vue';
//emit event 정의
const emit = defineEmits(['close']);
//props 정의
const props = defineProps({
isEdit: String,
postId: String
});
const message = ref('');
</script>
예제로 작성한 코드는 깃허브 에서 확인하실 수 있습니다.:smiley:
참고자료
- Vue 3 공식문서 - 컴포지션 API가 필요한 이유
- Vue 3 공식문서 - Script Setup.