Vue Composition api - 반응형 API 기초

h.Im·2024년 9월 9일
0

Vue 기초

목록 보기
26/28
post-thumbnail

반응형 상태 선언하기

javascript 객체에서 반응형 상태를 생성하기 위해서는 reactive() 함수를 사용할 수 있습니다.

<template>
	<div>
		<button v-on:click="increment">Click {{ state.count }}</button>
		<button v-on:click="increment">Deep Click {{ state.deep.count }}</button>
	</div>
</template>
<script>
import { reactive } from 'vue'

export default {
	setup() {
		const state = reactive({
			count: 0,
			deep: {
				count: 0,
			},
		})
		const increment = () => {
			state.count++
			state.deep.count++
		}
		return {
			state,
			increment,
		}
	},
}
</script>
<style lang=""></style>

state를 reactive 함수를 통해 선언 후, Click과 Deep Click 버튼을 클릭하면 화면에 노출되는 state.count 값이 1씩 올라가는 것을 확인할 수 있습니다.
그렇다면 다른 예시도 살펴 보겠습니다.

<template>
	<div>
		<p>{{ message }}</p>
		<button v-on:click="addMessage">add click</button>
		<hr />
	</div>
</template>

<script>
import { reactive } from 'vue'
export default {
	setup() {
		let message = reactive('message')
		const addMessage = () => {
			message = message + '!'
		}
		return {
			message,
			addMessage,
		}
	},
}
</script>

<style lang=""></style>

위 예시에서는 add click 버튼을 클릭해도 화면에 노출되는 message에 !가 추가되지 않습니다. 왜 그럴까요?
Vue는 Javascipt를 기반으로 만들어졌기 때문에 Javascipt의 기본 특징을 따르기 때문입니다. 앞선 예시에서 state는 Object 타입으로 선언한 반면 message는 String 타입으로 선언했습니다. 이것은 Primitive와 Reference Type의 차이로, 잘 정리된 글이 있어 링크를 달아두도록 하겠습니다.
https://velog.io/@surim014/JavaScript-Primitive-Type-vs-Reference-Type

그렇다면 primitive type으로 reactive를 선언할 수는 없는 것일까요? 위 보기에서 반응성을 갖기 위해 message를 object로 감싸야 할까요?

ref 함수

reactive 함수가 reference type에만 동작하기 때문에, ref함수를 사용하여 primitive type으로 반응형을 만들 수 있습니다. ref 메서드는 metable한 객체를 반환합니다. 이 객체는 value라는 하나의 속성만을 포함합니다.

<template>
	<div>
		<p>{{ refMessage }}</p>
		<button v-on:click="addRefMessage">add click</button>
		<hr />
	</div>
</template>

<script>
import { ref } from 'vue'
export default {
	setup() {
		let refMessage = ref('message')
		const addRefMessage = () => {
			refMessage.value += '!'
		}
		return {
			refMessage,
			addRefMessage,
		}
	},
}
</script>

<style lang=""></style>

위 예시에서는 add click 버튼을 클릭하면 화면에 !가 append 되어 노출되는 것을 확인할 수 있습니다. ref 함수에 대해 배웠듯, refMessage.value에 !를 추가하고 있습니다.
그런데 template 태그 내에서 {{ refMessage.value }}가 아닌, {{ refMessage }}로 작성했음에도 정상적으로 노출되는 이유는 무엇일까요?

자동 언래핑이 일어나는 경우

  • 템플릿 내부
    템플릿 컴파일러는 ref로 선언된 값들을 자동으로 언래핑합니다. 따라서 템플릿 안에서 ref를 사용할 때는 .value를 명시할 필요가 없습니다.
  • 컴포넌트 props로 전달
    다른 컴포넌트에 ref를 props로 전달할 때도 자동으로 언래핑이 이루어집니다.
  • 디렉티브 내부
    v-bind나 v-model과 같은 디렉티브에서도 ref가 자동으로 언래핑됩니다.
  • 반응형 객체 내부
    반응형 객체 내부 예제는 코드로 살펴보겠습니다.
<template>
	<div></div>
</template>

<script>
import { reactive, ref } from 'vue'
export default {
	setup() {
		const count = ref(0)
		const state = reactive({
			count,
		})

		console.log(count.value) // 0
		console.log(state.count) // 0
		console.log(state.count.value) // undefined
		return {}
	},
}
</script>

<style lang=""></style>

반응형 상태 구조 분해하기

ref 함수를 이용해 primitive type으로 반응형 상태를 만드는 것은 확인해 보았는데, 구조 분해 할당을 이용하면서 동시에 반응성을 잃지 않게하는 좋은 방법도 존재합니다.

const book = reactive({
	author: 'Vue Team',
	year: '2020',
	price: 'free',
})
const { author, price } = book

위 코드에서 author과 price는 반응성을 잃지만,

const book = reactive({
	author: 'Vue Team',
	year: '2020',
	price: 'free',
})
const { author, price } = toRefs(book)

toRefs를 사용하면 반응성이 유지됩니다.

const book = reactive({
	author: 'Vue Team',
	year: '2020',
	price: 'free',
})
const author = toRef(book, 'author')
const price = toRef(book, 'price')

toRefs는 위처럼 사용할 수도 있습니다.


readonly를 이용하여 반응형 객체의 변경 방지

import { reactive, readonly } from 'vue'
const original = reactive({ count: 0 })
const copy = readonly(original)

original.count++ // 원본은 변이 가능, 의존하는 watch도 트리거 됩니다.
copy.count++ // 복사본은 readonly로 선언하였으므로, 경고와 함께 변경이 실패합니다.

반응형 데이터를 선언해두고, 변경 불가한 복사본을 만들어두고 싶을 수도 있습니다. 그럴 때는 readonly를 사용하면 됩니다.

0개의 댓글