Vue 3.0/02.Essentials

[vue 3] v-for와 key

  • -

Vue에서 v-for를 사용할 때는 key 속성을 바인딩하도록 가이드 한다. 이번 포스트에서는 그 이유에 대해서 살펴보자.

https://vuejs.org/guide/essentials/list.html#maintaining-state-with-key

 

List Rendering | Vue.js

 

 

 

vuejs.org

 

v-for 사용 시 key를 바인딩하는 이유

 

in place patch 전략

Vue는 메모리를 아끼기 위해서 DOM 최적화를 통해 DOM을 재사용 한다.  이 과정에서 in-place patch 전략이라는 것을 사용한다. patch는 일종의 "땜빵" 을 의미하고 in-place는 현재 지점에서를 의미하니까 in-place patch 전략이란 현재 DOM에서 바뀌어야할 부분만 땜빵해서 처리하고 DOM을 재사용한다는 내용이다.

patch: 부분 땜빵(ex bug patch)

이 전략은 당연히 DOM 재사용 측면에서 매우 우수한데 가끔 문제를 일으키기도 한다.

 

언제가 문제인고 하니..

예를 들어 유니크한 과일의 목록을 리스트로 관리하고 과일의 개수를 지정해서 구매한다고 생각해보자.

<body>
    <div id="app">
        <ul>
            <li v-for="item in list">
                {{item}} : <input type="number" /> 개
            </li>
        </ul>
    </div>
</body>

<script src="https://unpkg.com/vue@3"></script>
<script>
    const app = Vue.createApp({
        setup(props) {
            // 과일의 목록은 중복되지 않는다.
            const list = Vue.ref(["apple", "banana", "orange"]);
            return {
                list,
            };
        },
    }).mount("#app");
</script>

위 코드는 잘 동작하며 apple 1개, banana 2개, orange 3개를 구매할 수 있다.

여기서 눈여겨볼 내용은 반복되는 <li>태그의 구성이다. <li>에는 {{item}} 이라는 표현식과 함께 일반 태그인 <input>이 있고 여기에 과일의 개수가 저장된다.

 

여기서 배열의 순서가 바뀌면?

이 상태에서 배열의 순서를 apple, banana, orange에서 banana, orange, apple로 변경하면 어떻게 되어야 할까? 당연히 우리의 기준으로는 apple 1개라는 <li>태그 자체가 이동하는게 맞지만 vue는 in-place patch로 처리하기 때문에 {{item}} 부분만 살짝 patch 하고 만다.

 

<body>
	<div id="app">
		<ul>
			<li v-for="item in list">
				{{item}} : <input type="number" /> 개
			</li>
		</ul>
		<button v-on:click="shift">shift</button>
	</div>
</body>

<script src="https://unpkg.com/vue@3"></script>
<script>
	const app = Vue.createApp({
		setup(props) {
			// 과일의 목록은 중복되지 않는다.
			const list = Vue.ref(["apple", "banana", "orange"]);
			const shift = () => {
				list.value.push(list.value.shift());
			};
			return {
				list,
				shift,
			};
		},
	}).mount("#app");
</script>
초기 상태 1번 shift 후(input의 내용은 변경되지 않아서 개수가 달라져버림)
 

만약 <li>의 내용이 {{item}}으로만 구성되어있었다면 전혀 문제가 되지 않지만 다른 내용과 함께 사용된다면 문제가 될 수 있는 것이다.

 

반복되는 내용을 하나로 관리하기 위해서 key 필요

위와 같은 문제를 방지하기 위해 반복되는 내용을 하나로 관리할 필요가 있고 이때 사용되는 것이 key이다. key에는 요소를 구별할 수 있는 unique 한 내용을 사용해야 한다.

특히 list의 요소를 index로 구분한다고 해서 index를 사용하면 안된다. index는 요소에 속한 값이 아니고 그때 그때 바뀌는 값이기 때문이다. 요소 자체로 구별될 수 있는 값이어야 하기 때문에 [{id:1, name:'apple'}, {id:2, name:'banana'}] 형태로 작성하고 id를 key로 사용하거나 이 예제에서 처럼 list 안의 요소들을 unique 하게 설정할 수도 있다.

이제 코드를 아래와 같이 key에 item이 바인딩되도록 수정해보자.

<body>
	<div id="app">
		<ul>
			<li v-for="item in list" :key="item">
				{{item}} : <input type="number" /> 개
			</li>
		</ul>
		<button v-on:click="shift">shift</button>
	</div>
</body>
<script src="https://unpkg.com/vue@3"></script>
<script>
	const app = Vue.createApp({
		setup(props) {
			// 과일의 목록은 중복되지 않는다.
			const list = Vue.ref(["apple", "banana", "orange"]);
			const shift = () => {
				list.value.push(list.value.shift());
			};
			return {
				list,
				shift,
			};
		},
	}).mount("#app");
</script>

 

초기 상태 1번 shift 후(input의 내용은 함께 이동함)

이제 생각한대로 완벽하게 동작하는 것을 확인할 수 있다. 

'Vue 3.0 > 02.Essentials' 카테고리의 다른 글

[vue 3] 14.Try Yourself!!  (0) 2023.08.22
[vue 3] 13.app의 config 객체  (0) 2022.07.05
[vue 3] 12.Vue 객체의 라이프 사이클  (0) 2022.07.04
[vue 3] 11.watch  (0) 2022.07.03
[vue 3] 10.computed  (0) 2022.07.02
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.