본문으로 건너뛰기
개발 머꼬
개발 노트Vue
hohyeon.dev15

v-for와 v-if를 같이 썼더니 property is not defined 오류가 난 이유

  • #Common Pitfall
  • #Engineering Note
  • #Vue

문제 발생

완료되지 않은 할 일만 그리려고 두 디렉티브를 한 요소에 붙였습니다.

<li v-for="todo in todos" v-if="!todo.isComplete">
  {{ todo.name }}
</li>

화면에는 아무것도 나오지 않고 콘솔에 오류가 찍혔습니다.

Property "todo" was accessed during render but is not defined on instance.

원인 분석

평가 순서 때문입니다. Vue 문서가 그대로 적어둡니다 — 같은 노드에 있을 때 v-ifv-for보다 우선순위가 높습니다. 즉 v-if 조건은 v-for 스코프의 변수에 접근할 수 없습니다.

v-if가 먼저 실행되는 시점에는 아직 반복이 시작되지 않아 todo라는 변수가 없습니다. 컴포넌트 인스턴스에 그런 이름이 없으니 위 오류가 납니다. Vue 2에서는 반대 순서였기 때문에 예전 코드를 옮겨오면 이 자리에서 걸립니다.

문서의 결론도 명시적입니다 — 암묵적인 우선순위 때문에 v-ifv-for를 같은 요소에 쓰는 것은 권장되지 않습니다.

해결 방안

  1. 필터링이 목적이면 computed로 걸러진 목록을 만듭니다. 문서가 첫 번째로 제시하는 방법이고, 대개 이게 맞습니다.
<script setup>
const activeTodos = computed(() => todos.value.filter((todo) => !todo.isComplete));
</script>

<template>
  <li v-for="todo in activeTodos" :key="todo.id">{{ todo.name }}</li>
</template>

렌더 함수가 매번 filter를 돌지 않고 캐시된다는 이득도 함께 얻습니다.

  1. 정말 반복 안에서 조건이 필요하면 <template v-for>로 감쌉니다. 문서가 제시하는 다른 방법입니다.
<template v-for="todo in todos" :key="todo.id">
  <li v-if="!todo.isComplete">{{ todo.name }}</li>
</template>

v-for가 바깥으로 나가서 todo가 안쪽 v-if 스코프에 들어옵니다. key<template> 쪽에 둡니다.

  1. "목록 전체를 보일지 말지"라면 컨테이너에 v-if를 답니다. 항목마다 조건을 평가할 이유가 없습니다.
<ul v-if="todos.length">
  <li v-for="todo in todos" :key="todo.id">{{ todo.name }}</li>
</ul>
  1. 오류 메시지를 그대로 믿지 않습니다. "not defined on instance"는 오타를 의심하게 만들지만, 이 경우 원인은 이름이 아니라 평가 순서입니다. v-forv-if가 한 줄에 같이 있으면 그것부터 봅니다.

공식 문서

마지막 수정

좋아요북마크

댓글0

아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.