본문 바로가기

Vue.js

[Udemy Vue 완벽가이드 Section3] 47. 목록 및 키 (key속성)

v-for에 대해서 알아두어야 할 점이 하나 있다.

모르고 넘어간다면 버그가 발생할 수도 있다.

 

# 이벤트 버블링

아래의 코드를 출력해보자.

<li v-for="(goal, index) in goals" @click="removeGoal(index)">
 <p>{{ goal }} - {{ index }}</p>
 <input type="text" />
</li>

 

goal을 추가하고, input창을 click → li요소를 클릭한 것으로 간주되어 추가한 goal이 삭제된다.

(input창을 click했음에도 불구하고, '이벤트 버블링'이 생겨 li요소의 removeGoal 메서드가 트리거된다.)

더보기

**이벤트 버블링(Event Bubbling)

: 특정 화면 요소에서 이벤트가 발생했을 때, 해당 이벤트가 상위 요소들로 전달되는 특성을 의미한다.

HTML요소는 기본적으로 트리구조를 가진다. 트리구조상, 한 단계 위에 있는 요소를 상위 요소라고 한다.

(위의 경우, p와 input의 상위요소가 li 요소이다.)

 

 

이럴 경우, input태그의 이벤트 리스너에 stop 수식어를 붙여 상위요소로의 이벤트 전파(event propagation)를 막을 수 있다.

<li v-for="(goal, index) in goals" @click="removeGoal(index)">
 <p>{{ goal }} - {{ index }}</p>
 <input type="text" @click.stop /> <!--event propagation이 목적이라면, @click.stop 뒤에 다른 코드를 구체적으로 지정할 필요는 없다.-->
</li>

 

stop 수식어가 event propagation을 멈추게 해서, input을 클릭해도 input에서만 click한 동작이 일어날 뿐, 이 click이벤트가 li요소의 removeGoal메서드를 트리거하지 않는다.

 

 

# key 추가

goal을 두 개 작성해보자.

먼저 생성한 첫번째 goal에 'something'을 입력하고, 첫번째 goal을 클릭하여 삭제시켜보자.

goal이 삭제되어 두번째 goal이 첫번째 goal이 되었지만, 첫번째 goal의 input창에 적은 'something'은 그대로 남아있다.

이건 사실 버그이다.

삭제 전

첫번째 goal 삭제 후

 

 

반대로 두 번째 goal에 'something'을 입력하고 첫 번째 goal을 삭제해보자. 첫 번째 goal이 삭제되기 전까지는 'something'이 있었는데, 삭제 후 input에 있던 'something'이 사라졌다.

 

 

 

항목을 추가 및 삭제 시, Vue가 목록을 업데이트하는데 실제 DOM에 list를 렌더링하고, 필요한 업데이트를 수행하는 방식. 이때 Vue는 성능을 최적화하는 방식으로 이를 수행하는데, 다시 말하면 DOM 요소를 재사용한다는 의미.

 

지금까지 두 개의 goal, 즉 두 개의 DOM요소가 있을 때 첫 번째 goal을 지웠을 때 Vue가 전체 list를 다시 렌더링하지 않는다. 첫 번째 DOM요소를 삭제한 후, 두번째 요소로 넘어가는게 아니다. 대신, 두 번째 요소의 컨텐츠를 그대로 가지고 첫 번째 DOM요소로 넘긴다.

 

이런 이유로 두 번째 goal의 input에 text를 입력했을 때, 첫 번째 요소를 삭제하면 text까지 사라진다. 

 

실제로는 Vue상에서 첫 번째 DOM요소는 삭제되지 않았고, 두 번째 goal의 동적 컨텐츠만, 즉 코드의 {{ }} 이중 중괄호 속 부분만 첫 번째 DOM 요소로 옮겨간 것.

첫번째 DOM요소에서의 input요소는 여전히 예전 것이고, li요소도 여전히 첫 번째 DOM element이다. 단지 동적 컨텐츠만 이동했을 뿐.

그래서 두 번째 요소에 있던 input필드의 값이 사라진 것. 두 번째 요소의 content들이 첫 번째 DOM element로 옮겨져갔기 때문.

 

이는 Vue가 요소를 재사용해서 생긴 문제.

하지만 요소를 구분해야 하는 경우가 있다. 모두 동일한 요소를 갖고 있는 경우, 즉 모두 li요소로 동일한 DOM 요소인 경우, 렌더링된 모든 DOM요소에 대한 고유한 식별 기준이 없다.

 

이 경우, 사용할 수 있는 추가 속성이 있다. 기본 HTML 속성이 아니고, v-for를 사용하는 요소에 사용할 수 있다.  key 속성

Vue가 인식 및 감지할 수 있는 속성. 

key는 출력하는 모든 항목에 대해 고유 식별 기준으로 작용한다. v-bind를 통해 동적값에 바인딩해보자.   간단히 :key로 사용 가능.

각각의 goal을 고유하게 식별할 수 있는 것이 필요하다.

 

<li v-for="(goal, index) in goals" :key="goal"></li>

 

모든 goal에 index가 있으니 index를 사용하면 된다고 생각할 수 있지만, 두 개의 goal이 있을 때 첫 번째 goal을 지우면 두 번째 goal의 index가 0이 된다. (즉, index가 계속 변경된다.)

index는 goal에 속해있지 않다. 첫 번째 항목은 항상 index를 0으로 가진다. 그래서 Index는 배열의 요소값과 연결되어있다고 보기 어렵다. 그래서 이 경우에는 key를 index를 사용하기에 적합하지 않다.

 

 

대신, goal을 그대로 사용할 수 있다. (동일한 goal을 쓰지 않는다는 가정 하에)

좀 더 현실적인 예시로는 고유 id를 사용하는 것. id가 이미 데이터베이세 있는 경우 사용 가능하다.

 

 

두 번째 goal에 'something'을 입력하고 첫 번째 goal을 삭제해도 두 번째 goal에 작성한 'something'이 그대로 남아있다.

Vue가 다른 두 DOM요소를 구별하고, 두 번째 DOM요소에 있는 어떠한 data도 잃지 않기 위해 첫 번째 DOM요소를 삭제해야한다는걸 감지했다.

 

# 핵심 내용

v-for를 사용할 때는 고유 식별 기준이 있는 key를 추가해야한다.

이렇게 해야 Vue가 요소를 구별할 수 있고, 입력값 등의 상태가 유실되는 일을 막을 수 있다.

 

 

** 출처: 모든 내용은 Udemy Vue-완벽가이드 강의를 기반으로 작성하였습니다.