## Options API
Vue 2 에서는 아래와 같이 작성하는 Options API가 주류였다.
```javascript
<script>
export default {
props: {
// 부모로부터 넘겨받는 값들
},
methods: {
// 메소드들
},
computed: {
// 계산된 값들
},
data: () => {
// 데이터
},
mounted() {
},
}
</script>
```
여기에는 다음과 같은 단점들이 있었다.
- 컴포넌트 내에 특정 비즈니스 로직을 구현하는 경우, 이 로직들이 각각 `methods`, `computed`, `data` 등 각 속성별로 분리되어, 덩어리째 재사용이 어려움.
![](https://d1085v6s0hknp1.cloudfront.net/boards/coinsect_blog/ea321963-4a64-45f1-80aa-84183bf26c73_image.png)
이 그림은 Options API의 단점을 명확하게 보여준다.
- `this`의 사용. 나는 코딩을 할 때 this나 self 등의 개념을 사용하지 않는 것이 좋다고 생각한다. Python, Java, Ruby 등 이것이 혼란을 줄 여지가 없는 언어들의 경우는 모르겠으나, JS의 경우는 그것이 호출되는 컨텍스트에 따라 가리키는 대상이 바뀔 수 있기 때문에 (e.g. [화살표 함수는 컨텍스트를 만들지 않음](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Functions/Arrow_functions)) 객체 내에서 자기 자신을 인용할지라도 인스턴스의 이름을 항상 적는 것이 좋다고 생각한다. 이것은 추후 코드를 리팩토링하며 코드를 이리저리 옮기는 과정에서 this가 무엇을 가리키는지 신경 쓸 필요가 없도록 만들어준다. 또, computed같은 경우 정의할 때는 함수로 정의하나 호출할 때는 그냥 `this.someVal`과 같이 '값'을 호출하는 방식으로 사용하게 되니, 사실 초심자 입장에서는 혼란스럽다.
Options API는 Composition API가 등장하기 전의 과도기적 형태였다고 생각한다.
## Composition API
Vue 3부터는 Composition API가 공식적으로 권장사항이 되었다.
```javascript
<script>
import { ref, computed, onMounted } from 'vue'
export default {
setup() {
const myData = ref()
const myComputedVal = computed(() => { ... })
const init = () => {}
onMounted(init)
},
}
```
Composition API는 이처럼 'vue' 패키지에서 반응형 변수를 선언할 수 있도록 해주는 `ref`, `computed`나 `onMounted`와 같은 lifecycle hook을 import하여 `setup()` 안에 구현하는 방식이다.
이것의 가장 큰 장점은 `ref`, `computed`, `onMounted`, `watch` 등의 기능들이 `vue` 패키지를 통해 제공되기 때문에, `.vue` 파일 뿐 아니라 일반적인 `.js` 파일들에서도 사용할 수 있게 되었다는 것이다! 이처럼 재사용 가능한 로직들의 구현을 `.vue` 파일 외부로 빼낼 수 있게 되었기 때문에, `.vue` 파일들은 좀 더 `<template>`과 `<style>` 등 눈에 보이는 부분에 집중하고, 로직들은 외부로 묶는 형태의 프로젝트 구성이 용이해졌다.
누가봐도 리액트의 훅에서 영감을 받은 것이 명확해보이는 이 스펙은 Vue에서는 [composable](https://v3-docs.vuejs-korea.org/guide/reusability/composables.html)이라 불린다.
```javascript
// my-hook.js
import { computed } from 'vue'
export const useMyHook = () => {
const todayISO8601 = computed(() =>
(new Date()).toISOString().split('T')[0]
)
return {
todayISO8601,
}
}
// MyComponent.vue
import useMyHook from '@/hooks/my-hook'
<template>
<div>{{ todayISO8601 }}</div>
</template>
<script>
export default {
setup() {
const { todayISO8601 } = useMyHook()
return {
todayISO8601,
}
},
}
</script>
```
상태관리나 생명주기 등과 무관한 단순 기능에 해당하는 함수들은 보통 helper라고 구분하면 되겠고, `ref`, `computed` 또는 lifecycle 함수나 상태 등이 엮이는 함수들은 composable(= hook)로 구분할 수 있다.
## [`<script setup>`](https://vuejs.org/api/sfc-script-setup)
그런데 `<script>` 내부에서 매번 `export default { setup() {} }`을 작성하고, 템플릿에 노출할 값들을 명시적으로 `return`하는 것도 사실 귀찮은 보일러플레이트이다. 그래서 그것에 대한 설탕으로, `<script setup>`이라는 것이 존재한다.
```javascript
<template>
<div>{{ todayISO8601 }}</div>
</template>
<script setup>
import useMyHook from '@/hooks/my-hook'
const props = defineProps(...)
const emit = defineEmits([...])
const { todayISO8601 } = useMyHook()
</script>
```
위 코드에서 알 수 있듯이, 번거로운 보일러플레이트가 꽤나 간소화된다. 또한 선언한 변수나 함수들은 별도의 `return` 없이 자동으로 템플릿에 노출되어 사용할 수 있게 된다. 그리고 prop, emit은 defineProps, defineEmits를 통해 정의하는데, 이런 함수들은 딱히 명시적으로 import하지 않아도 된다.
## Typescript
처음 프로젝트를 생성할 때부터 Typescript 기반으로 생성했다면 바로 사용할 수 있고, 기존 프로젝트에서 추가하고 싶은 경우라면 터미널에서 간단히
```
vue add typescript
```
를 하면 된다. (`vue-cli`가 글로벌로 설치되어 있는 경우 가정) 타입스크립트를 설치했다면 이후부터는
`<script lang="ts">`과 같이 `lang="ts"`만 붙이면 된다. 물론 당연하게도 `<script setup lang="ts">`도 가능하다.
## 내가 새로 Vue 프로젝트를 만든다면?
- Vite
- Typescript
- script setup
- Vuex (X) [Pinia (O)](https://pinia.vuejs.kr/)
요렇게 scaffolding 할 것 같다. 현 회사에서의 3년 넘은 Vue 3 프로젝트 역시 최근에 script setup + typescript로의 전환을 마쳤다. 너무나 작업량이 많아 엄두를 내지 못 하고 있었는데 github copilot의 도움으로 비교적 빠르게 마칠 수 있었다.