# Vue 3.0

์ƒˆ๋กœ ๋“ฑ์žฅํ•œ Vue.js Composition API(๋ทฐ 3.0)์— ๋Œ€ํ•ด ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

# ๊ธฐ์กด์˜ ๋ฌธ์ œ์ 

  • ๋ณต์žกํ•œ ์•ฑ์—์„œ ์ฝ”๋“œ ์žฌ์‚ฌ์šฉ ๋ฐฉ๋ฒ•์˜ ํ•œ๊ณ„
    • HOC
    • ๋ฏน์Šค์ธ

# ๊ฐ€๊ณ ์ž ํ•˜๋Š” ๋ฐฉํ–ฅ

  • ์ธ์Šคํ„ด์Šค ์˜ต์…˜ ๋‹จ์œ„๊ฐ€ ์•„๋‹ˆ๋ผ ํŠน์ • ๊ธฐ๋Šฅ์ด๋‚˜ ๋…ผ๋ฆฌ์˜ ๋‹จ์œ„๋กœ ์ฝ”๋“œ๋ฅผ ๊ทธ๋ฃนํ™” ํ•˜๋Š” ๊ฒƒ. ๊ทธ๋ฆฌ๊ณ  ๊ทธ ๊ทธ๋ฃนํ™”๋œ ๋กœ์ง์„ ์—ฌ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ์—์„œ ์žฌ์‚ฌ์šฉํ•˜๊ธฐ

most of us opt to organize files by feature or responsibility

  • ss

# composition API์˜ ์žฅ์ 

  • ref, reactive API๋กœ ์ƒ์„ฑํ•œ reactive ๋ฐ์ดํ„ฐ๋“ค์€ ํ…œํ”Œ๋ฆฟ์—๋งŒ ๊ผญ ์“ฐ์ง€ ์•Š์•„๋„ ๋˜๋Š” ์žฅ์ ์ด ์ƒ๊ธด๋‹ค. ์ปดํฌ๋„ŒํŠธ์™€ ๋ณ„๊ฐœ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐ˜์‘์„ฑ ๋ฐ์ดํ„ฐ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Œ.
  • ์ปดํฌ๋„ŒํŠธ ์˜ต์…˜ ์†์„ฑ์ด ์กด์žฌํ•˜๊ธฐ ์ „์— setup API๊ฐ€ ์‹คํ–‰๋˜๋ฏ€๋กœ this๋กœ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ ‘๊ทผํ•  ์ˆ˜ ์—†๋‹ค. ๋Œ€์‹  context ์‚ฌ์šฉ. (๋งˆ์น˜ ๊ธฐ์กด์˜ functional component๋ฅผ ๋ณ€ํ˜•ํ•ด์„œ ์‚ฌ์šฉํ•˜๋Š” ๋Š๋‚Œ)
  • setup API์˜ ๋‘ ๋ฒˆ์งธ ์ธ์ž์— ์ œ๊ณต๋˜๋Š” ์˜ต์…˜๋“ค
setup(props, context) {
  context.attrs
  context.slots
  context.parent
  context.root
  context.emit
}

# composition API์— ๋Œ€ํ•œ ์ƒ๊ฐ

  • API ํ•จ์ˆ˜๋กœ ๋ถˆ๋Ÿฌ์˜จ ๋ฐ์ดํ„ฐ๋ฅผ ์ปดํฌ๋„ŒํŠธ data์— ์—ฐ๊ฒฐํ•˜๊ธฐ ์ „์— ๊ฐ€๊ณตํ•  ์ผ์ด ๋งŽ์€๋ฐ ์ด๋ฅผ ์ปดํฌ์ง€์…˜ API ํ•จ์ˆ˜ ๋ ˆ์ด์–ด์— ๋„ฃ์œผ๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™๋‹ค๋Š” ์ƒ๊ฐ์ด ๋“ฆ. ์ด๋ฅผ ์„œ๋น„์Šค ๋ ˆ์ด์–ด๋ผ๊ณ  ์น˜๋ฉด ๊ธฐ์กด์—๋Š” ์Šคํ† ์–ด๋ผ๋“ ๊ฐ€ ๋ฏน์Šค์ธ์— ๋ถ„์‚ฐ์‹œํ‚ค๊ฑฐ๋‚˜ ์•ต๊ทค๋Ÿฌ์ฒ˜๋Ÿผ ์„œ๋น„์Šค ํŒŒ์ผ์„ ๋งŒ๋“œ๋Š” ๊ฒฝํ–ฅ์ด ์žˆ๋Š” ๋“ฏ.

# Note Taking

  • ref๊ฐ€ ๊ฐ์ฒด์ธ ์ด์œ ๋Š” number, string ๋“ฑ์˜ ์›์‹œ ํƒ€์ž…์ด passed by value ์ด๊ธฐ ๋•Œ๋ฌธ์— computed ๋‚ด๋ถ€ ๋กœ์ง ํ๋ฆ„์ƒ passed by reference์ธ object ๋ชจ์–‘์ด ํ•„์š”ํ–ˆ์Œ
// won't work
function computed(getter) {
  let value
  watchEffect(() => {
    value = getter()
  })
  return value
}
// works like a charm
function computed(getter) {
  let ref = {
    value: null
  }
  watchEffect(() => {
    ref.value = getter()
  })
  return ref
}

# ref

๊ธฐ์กด ๋ทฐ ๋ฒ„์ „์—์„œ๋Š” ref๊ฐ€ ๋ทฐ ํ…œํ”Œ๋ฆฟ์˜ DOM ๋˜๋Š” ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” ์†์„ฑ์œผ๋กœ ์‚ฌ์šฉ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. Vue 3์—์„œ๋Š” ref๊ฐ€ reactive reference๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

Vue 3์˜ ref๋Š” ๋ฆฌ์•กํ‹ฐ๋ธŒํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€๋ฆฌํ‚ฌ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ํ…œํ”Œ๋ฆฟ ํ‘œํ˜„์‹๋„ ํ•จ๊ป˜ ๊ฐ€๋ฆฌํ‚ต๋‹ˆ๋‹ค.

TIP

TLDR

  • ref means reactive reference

reactive ์•ˆ์— ref ๊ฐ’์„ ์‚ฌ์šฉํ•˜๋ฉด .value๋กœ ์ ‘๊ทผํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค.

// ref
import { ref, watch } from 'vue'

const count = ref(0)

function increment() {
  count.value++
}

console.log(count.value) // 0

count.value++
console.log(count.value) // 1

// reactive
const state = reactive({
  count: 0,
  double: computed(() => state.count * 2)
})

// no need to use `state.double.value`
console.log(state.double)

# reactive์™€ ref์˜ ์ฐจ์ด์ ?

reactive๋Š” ๊ธฐ์กด ๋ทฐ ๋ฌธ๋ฒ•์˜ data ์†์„ฑ ๋Š๋‚Œ์ด๊ณ , ref๋Š” ์ข€ ๋” ๋ฆฌ์•กํ‹ฐ๋ธŒ ์†์„ฑ์„ ๊ฐœ๋ณ„์ ์œผ๋กœ ์„ ์–ธํ•˜๋Š” ๋Š๋‚Œ.

// reactive
const event = reactive({
  count: 3,
  doubled: computed(() => event.count * 2)
})

// ref
const count = ref(3)
const doubled = computed(() => count.value * 2)
  • ๋‘๊ฐœ ๋‹ค ์–ด์ฐจํ”ผ event.count๋กœ ์ ‘๊ทผํ•˜๋‚˜ count.value๋กœ ์ ‘๊ทผํ•˜๋‚˜ ์†์„ฑ์œผ๋กœ ํ•œ๋‹จ๊ณ„ ๋” ๋“ค์–ด๊ฐ€์„œ ์ ‘๊ทผํ•ด์•ผ ํ•˜๋Š”๊ฑด ๊ฐ™๋‹ค.

# reactive๊ฐ€ ๋” ์„ ์–ธํ•˜๊ธฐ ํŽธํ•œ ๊ฒƒ ๊ฐ™์€๋ฐ์š”?

์„ ์–ธํ•˜๋Š” ์ชฝ ์ฝ”๋“œ๋งŒ ๋ณด๋ฉด ๊ทธ๋ ‡์ง€๋งŒ ์‹ค์ œ๋กœ๋Š” ํ…œํ”Œ๋ฆฟ์—์„œ ์•„๋ž˜์™€ ๊ฐ™์€ ๋ถˆํŽธํ•จ์ด ๋ฐœ์ƒํ•œ๋‹ค. reactive์˜ ์ผ€์ด์Šค๋ฅผ ๋ณด์ž.

<!-- reactive -->
<div>count: {{ event.count }}</div>
<div>doubled: {{ event.doubled }}</div>
// reactive
const event = reactive({
  count: 3,
  doubled: computed(() => state.count * 2)
})

์˜์™ธ๋กœ ref ์†์„ฑ์ด ์„ ์–ธํ•  ๋•Œ ์กฐ๊ธˆ ๋ถˆํŽธํ•˜์ง€๋งŒ ํ…œํ”Œ๋ฆฟ์—์„œ๋Š” ์ ‘๊ทผํ•˜๊ธฐ๊ฐ€ ํŽธํ•˜๋‹ค.

<!-- ref -->
<div>count: {{ count }}</div>
<div>doubled: {{ doubled }}</div>
// ref
const count = ref(3)
const doubled = computed(() => count.value * 2)

# ๊ทธ๋ž˜๋„ reactive๊ฐ€ ์ข€ ๋” ํŽธํ•  ๊ฒƒ ๊ฐ™์€๋ฐ ์–ด๋–ป๊ฒŒ ๋ฐฉ๋ฒ•์ด ์—†์„๊นŒ์š”?

ES6์˜ ๋””์ŠคํŠธ๋Ÿญ์ฒ˜๋ง ๋ฌธ๋ฒ•์„ ์ƒ๊ฐํ•ด๋ณด๋ฉด ์™ ์ง€ ์•„๋ž˜์™€ ๊ฐ™์€ ์‹œ๋„๊ฐ€ ๊ฐ€๋Šฅํ•  ๊ฒƒ ๊ฐ™๋‹ค.

<!-- reactive -->
<div>count: {{ event.count }}</div>
<div>doubled: {{ event.doubled }}</div>
// reactive
function setup() {
  const event = reactive({
    count: 3,
    doubled: computed(() => state.count * 2)
  })

  // ์ •์ƒ ๋ฌธ๋ฒ•
  return { event }
}

๊ธฐ๋ณธ์ ์œผ๋กœ setup API์—์„œ ๋ฐ˜์‘์„ฑ์ด ์ฃผ์ž…๋œ ๊ฐ’(reactive ๋˜๋Š” ref)์„ ๋ฐ˜ํ™˜ํ•ด์ค˜์•ผ ํ•˜๋Š”๋ฐ ์ด ๋•Œ ๋ญ”๊ฐ€ ์•„๋ž˜์™€ ๊ฐ™์ด ํ•ด๋ณผ ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™๋‹ค.

<!-- reactive -->
<div>count: {{ count }}</div>
<div>doubled: {{ doubled }}</div>
// reactive
function setup() {
  // ...
  return { ...event } // X
}
// reactive
function setup() {
  // ...
  return { event.count, event. doubled } // X
}

์œ„์™€ ๊ฐ™์€ ํ˜•ํƒœ๋กœ๋Š” reactive ์•ˆ์— ์„ ์–ธ๋œ ์†์„ฑ๋“ค์„ ๋ฐ”๋กœ ํ…œํ”Œ๋ฆฟ์—์„œ ์ ‘๊ทผํ•  ์ˆ˜๊ฐ€ ์—†๋‹ค. ์ด ๋•Œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š”๊ฒŒ toRef API๋‹ค

<!-- reactive -->
<div>count: {{ count }}</div>
<div>doubled: {{ doubled }}</div>
import { toRefs } from 'vue';

// reactive
function setup() {
  // ...
  return { ...toRefs(event) } // O
  return toRefs(event); // O
}

# ๋ผ์ดํ”„ ์‚ฌ์ดํด ํ›…

setup API์—์„œ ๊ธฐ์กด ๋ผ์ดํ”„ ์‚ฌ์ดํด ๊ฐœ๋…๊ณผ ํ›…์€ ๋Œ€๋ถ€๋ถ„ ์œ ์ง€๊ฐ€ ๋˜์—ˆ๊ณ  ์•„๋ž˜ 4๊ฐ€์ง€์˜ ๋ณ€ํ™”๋งŒ ์žˆ๋‹ค.

  1. beforeCreate, created๋Š” setup ํ•จ์ˆ˜ ์•ˆ์—์„œ์˜ ๋กœ์ง ์‹คํ–‰์œผ๋กœ ๋™์ผํ•œ ํšจ๊ณผ๋ฅผ ๋ฐœํœ˜ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ œ๊ฑฐ๋จ
  2. beforeDestroy, destroyed๋Š” ๋” ๋ช…์‹œ์ ์ธ ์ด๋ฆ„์œผ๋กœ ๋ณ€๊ฒฝ -> beforeUnmount, unmounted
  3. onRenderTracked : ํ™”๋ฉด์„ ๋‹ค์‹œ ๊ทธ๋ฆฌ๊ธฐ ์œ„ํ•œ ๋ฆฌ์•กํ‹ฐ๋ธŒ ๋””ํŽœ๋˜์‹œ(๋ฐ˜์‘์„ฑ์ด ์ฃผ์ž…๋œ ๋ฐ์ดํ„ฐ ๋ณ€ํ™” ์ •๋„๋กœ ์ดํ•ด)๊ฐ€ ์ฒ˜์Œ์œผ๋กœ ๊ฐ์ง€๋˜์—ˆ์„ ๋•Œ. ๋””๋ฒ„๊น… ์šฉ๋„
  4. onRenderTriggered : ์ฒซ ๋ฒˆ์งธ ๋ Œ๋”๋ง์ด ์ผ์–ด๋‚ฌ์„ ๋•Œ. ์–ด๋–ค ์ด์œ ๋กœ ํ™”๋ฉด์ด ๋‹ค์‹œ ๊ทธ๋ ค์ง„๊ฑด์ง€ ๋””๋ฒ„๊น…ํ•˜๊ธฐ ์œ„ํ•œ ์šฉ๋„.

๋ฌธ๋ฒ•์€ ์•„๋ž˜์™€ ๊ฐ™์ด ๋ณ€๊ฒฝ๋˜์—ˆ๋‹ค.

// ๊ธฐ์กด
new Vue({
  created() {
    
  },
  beforeMount() {

  }
})

// setup
setup() {
  onBeforeMount(() => {
    console.log("Before Mount!");
  });
  onMounted(() => {
    console.log("Mounted!");
  });
}

# ๋ผ์ดํ”„ ์‚ฌ์ดํด ํ›…

  1. ๊ธฐ์กด์˜ ๋ผ์ดํ”„ ์‚ฌ์ดํด ํ›…์˜ ์•ž์— on ์ ‘๋‘์‚ฌ๊ฐ€ ๋ถ™๋Š”๋‹ค.
  • beforeMount -> onBeforeMount
  • mounted -> onMounted
  1. ์ด์™ธ์—๋„ beforeCreate์™€ created ๋ผ์ดํ”„ ์‚ฌ์ดํด์ด ์ œ๊ฑฐ๋จ
  2. ์ƒˆ๋กœ ์ถ”๊ฐ€๋œ ๋ผ์ดํ”„ ์‚ฌ์ดํด ํ›…์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค.
  • onRenderTracked : ๋ Œ๋” ํ•จ์ˆ˜ ์•ˆ์— ์กด์žฌํ•˜๋Š” ๋ฆฌ์•กํ‹ฐ๋ธŒ ๋””ํŽœ๋˜์‹œ๊ฐ€ ์ตœ์ดˆ๋กœ ์ ‘๊ทผ๋˜์—ˆ์„ ๋•Œ ํ˜ธ์ถœ๋œ๋‹ค. ๋””๋ฒ„๊น…์— ์œ ์šฉํ•œ ํ›…
  • onRenderTriggered : ๋ Œ๋”๋ง์ด ์ƒˆ๋กœ ๋˜์—ˆ์„ ๋•Œ ํ˜ธ์ถœ๋œ๋‹ค. ํ™”๋ฉด์„ ๋‹ค์‹œ ๊ทธ๋ฆฐ ์ด์œ ๊ฐ€ ์–ด๋–ค๊ฑด์ง€ ๋””๋ฒ„๊น…ํ•˜๊ธฐ ์ข‹๋‹ค.

# VSCode ํ™•์žฅ ํ”Œ๋Ÿฌ๊ทธ์ธ

  • Vue VSCode Snippets (opens new window) : vbc(vbase-css)๋ฅผ ์น˜๊ณ  ํƒญ์„ ๋ˆ„๋ฅด๋ฉด ์ปดํฌ๋„ŒํŠธ ๊ธฐ๋ณธ ์ฝ”๋“œ๋กœ ์ž๋™ ์™„์„ฑ