본문 바로가기

Vue.js

[Udemy Vue 완벽가이드 Section6] 70. 문제 이해하기

<section id="app">
  <ul>
    <li>
      <h2>Manuel Lorenz</h2>
      <button>Show Details</button>
       <ul>
         <li><strong>Phone:</strong> 01234 5678 991</li>
         <li><strong>Email:</strong>manuel@localhost.com</li>
       </ul>
     </li>
     <li>
       <h2>Julie Jones</h2>
       <button>Show Details</button>
       <ul>
         <li><strong>Phone:</strong> 09876 543 221</li>
         <li><strong>Email:</strong>julie@localhost.com</li>
       </ul>
      </li>
    </ul>
</section>

 

위 예제에 Manuel, Julie 두 친구의 list 항목이 들어있는 ul이 있다.

현재는 Vue앱이 없다. 이를 하나의 Vue 앱으로 관리할 수 있다.

친구 데이터를 HTML 코드에 하드코딩하는 대신, Vue 코드로 Vue 애플리케이션에 보관해보자.

즉, 위 코드처럼 li 항목들을 HTML 코드로 하드코딩하지않고, Vue.js를 사용해 동적으로 렌더링해보자.

 

Vue앱의 data 프로퍼티에 friends로 저장해보자.

 

app.js

const app = Vue.createApp({
  data() {
    return {
      friends: [
        {
          id: "manuel",
          name: "Manuel Lorenz",
          phone: "01234 5678 991",
          email: "manuel@localhost.com",
        },
        {
          id: "julie",
          name: "Julie Jones",
          phone: "09876 543 221",
          email: "julie@localhost.com",
        },
      ],
    };
  },
  methods: {},
});

 

index.html

<section id="app">
  <ul>
    <li v-for="friend in friends" :key="friend.id">
      <h2>{{ friend.name }}</h2>
      <button>Show Details</button>
      <ul>
        <li><strong>Phone:</strong> {{ friend.phone }}</li>
        <li><strong>Email:</strong> {{ friend.email }}</li>
      </ul>
    </li>
  </ul>
</section>

이제 데이터는 JS로 관리한다.

실제 data는 데이터베이스에서 가져올 수도 있고, 사용자가 입력한 것일 수도 있다.

 

v-for를 이용하면 v-for 이용 전과 화면은 같지만, Vue를 활용하면서 HTML 코드가 줄었다.

하지만 'Show Details'버튼은 작동하지 않는다.  ← 이 부분이 까다롭다.

 

 

 

toggleDetails 메서드를 추가하고 호출하여 버튼에 연결하고, 버튼을 클릭해서 details를 숨기고 표시할 수 있다.

 

detailsAreVisible이라는 data 프로퍼티를 추가하고, 초기값은 false로 설정해 처음에는 세부정보를 표시하지 않는다.

const app = Vue.createApp({
  data() {
    return {
      friends: [
        {
          id: "manuel",
          name: "Manuel Lorenz",
          phone: "01234 5678 991",
          email: "manuel@localhost.com",
        },
        {
          id: "julie",
          name: "Julie Jones",
          phone: "09876 543 221",
          email: "julie@localhost.com",
        },
      ],
      detailsAreVisible: false,
    };
  },
  methods: {
   toggleDetails(){
    this.detailsAreVisible = !this.detailsAreVisible;
   },
  },
});

 

그리고 button 클릭 시, toggleDetails가 트리거 되도록 이벤트바인딩을 한다.

세부정보가 들어있는 ul 태그에는 v-if값을 설정해 DOM에 추가여부를 제어한다.

버튼의 라벨도 동적으로 변경가능하다. (computed 프로퍼티로 사용 가능.)

 

 

index.html

<ul>
  <li v-for="friend in friends" :key="friend.id">
   <h2>{{ friend.name }}</h2>
    <button @click="toggleDetails">
      {{ detailsAreVisible ? 'Hide' : 'Show' }}Details <!--버튼 라벨 동적으로-->
    </button>
    <ul v-if="detailsAreVisible"> <!--v-if값을 설정해 DOM에 추가여부를 제어-->
      <li><strong>Phone:</strong> {{ friend.phone }}</li>
      <li><strong>Email:</strong> {{ friend.email }}</li>
    </ul>
  </li>
</ul>

 

 

새로고침 시, 아래와 같이 버튼이 표시된다.

 

 

# 문제점

문제는, 어떤 친구 항목에서 버튼을 누르는지와는 관계 없이, 어떤 버튼을 누르든 일괄적으로 세부정보가 같이 사라지고 같이 보여진다.

 

그 이유는 분명하다.

v-for에서 여러 개의 li항목을 출력했다. 하지만 모든 li항목의 button은 항상 같은 method인 toggleDetails를 가리키고 있다.

즉, 어떤 친구의 button을 누르든지 항상 같은 메서드를 호출하고,

또한 두 친구의 세부정보 표시제어를 같은 detailsAreVisible로 하고있기 때문.

 

 

# 해결책 ?

해결책을 생각해보자.

toggleDetails 메서드에 id값을 전달해보자.

<ul>
  <li v-for="friend in friends" :key="friend.id">
   <h2>{{ friend.name }}</h2>
    <button @click="toggleDetails(friend.id)">
      {{ detailsAreVisible ? 'Hide' : 'Show' }}Details
    </button>
    <ul v-if="detailsAreVisible">
      <li><strong>Phone:</strong> {{ friend.phone }}</li>
      <li><strong>Email:</strong> {{ friend.email }}</li>
    </ul>
  </li>
</ul>

 

그런 다음, app.js파일에서 toggleDetails메서드에 id를 전달하여

data에 detailsAreVisibleA, detailsAreVisibleB 두 개의 프로퍼티로 나눠서 작동하게 설정할 수 있다.

But, 많이 번거롭다. 이렇게 하는건 궁극적인 방법이 아니다.

 

만약 유저가 새 친구를 추가할 수 있게 한다면, data의 friends 프로퍼티의 리스트는 동적으로 증가하므로 detailsAreVisibleA, B, C 이렇게 하는 것도 좋지 않다. 프로퍼티의 개수를 예상하는 것도 불가능하다.

 

 

# 컴포넌트

대신, 이 문제는 컴포넌트를 활용하면 쉽게 해결할 수 있다!

 

 

 

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