-
6장. 실전 애플리케이션 만들기 - 5개발 공부/Do it! Vue.js 입문 2025. 5. 31. 09:00반응형
6장 - 5
06-5 기존 애플리케이션 구조의 문제점 해결하기
<현재 애플리케이션 구조의 문제점>
- 지금까지 구현한 애플리케이션의 문제점은 다음 2가지이다.
- 할 일을 입력했을 때 할 일 목록에 바로 반영되지 않는 점
- 할 일을 모두 삭제했을 때 할 일 목록에 바로 반영되지 않는 점
- 종합해 보면 현재 화면을 4개의 영역(컴포넌트)으로 분리해 놓았기 때문에 한 영역의 처리 결과를 다른 영역에서 잠지하지 못한다는 문제가 있다.
- 이 문제점을 해결하는 가장 간단한 방법은 컴포넌트를 4개로 분리하지 않고 한 컴포넌트 안에서 모든 것을 처리하는 것이다.
- 하지만 컴포넌트가 이렇게 간단한 화면만 있을 수는 없고 그러므로 컴포넌트가 복잡해지는 것은 불가피하다.
- 단순히 '컴포넌트의 단위가 작을 수록 재활용성이 높아진다' 라는 컴포넌트의 활용 측면 보다는 지금처럼 간단한 애플리케이션일 때 컴포넌트 통신을 할 수 있어야 향후에 스스로 애플리케이션을 설계하고 구현할 수 있다.
<문제 해결을 위한 애플리케이션 구조 개선>
- 현재 애플리케이션은 각각의 컴포넌트에서만 사용할 수 있는 뷰 데이터 속성(newTodoItem, todoItems)을 갖고 있다. 생각해 보면 로컬 스토리지의 todoItems, TodoInput의 newTodoItem, TodoList의 todoItems는 모두 '할 일'이라는 같은 성격의 데이터를 사용하고 있다.
- 만약 모든 컴포넌트가 '같은 데이터 속성(할일)'을 조작한다면 화면을 매번 새로 고침해야 하는 문제점은 해결할 수 있을 것이다.
- 같은 데이터 속성을 사용하기 위해 최상위(루트) 컴포넌트인 App 컴포넌트에 todoItems라는 데이터를 정의하고, 하위 컴포넌트 TodoList에 props로 전달한다.
변경된 애플리케이션의 구조 - 이전에는 할 일 데이터 추가, 삭제를 모두 하위 컴포넌트 TodoInput, TodoList에서 하였다.
- 이제는 뷰 데이터 속성 todoItems와 로컬 스토리지의 데이터 조회, 추가, 삭제를 모두 App 컴포넌트에서 한다.
- 그리고 하위 컴포넌트들은 그 데이터를 표현하거나 데이터 조작에 대한 요청(이벤트 발생)만 하는 것이다.
- 이러한 중앙 집중 관리 방식은 상태 관리 라이브러리인 뷰엑스와 비슷한 구조이다. 뷰엑스에 대한 내용은 7장을 참고하라.
<props와 이벤트 전달을 이용해 할 일 입력 기능 개선하기>
- 위에서 변경한 구조를 코드에 적용해 보자.
- 먼저 최상위 컴포넌트인 App 컴포넌트(App.vue)에 데이터 속성 todoItems를 선언한다. 그리고 뒤에서 사용할 addTodo( )메서드를 추가한다.
// App.vue export default { data() { // 데이터 속성 todoItems 선언 return { todoItems: [] } }, methods: { addTodo() { // 로컬 스토리지에 데이터를 추가하는 로직 } }, components: { 'TodoHeader': TodoHeader, 'TodoInput': TodoInput, 'TodoList': TodoList, 'TodoFooter': TodoFooter } }
- 선언한 todoItems 속성을 TodoList 컴포넌트에 props로 전달한다.
- TodoInput 컴포넌트 태그에는 할 일 추가 버튼을 클릭했을 때 App 컴포넌트로 이벤트를 전달할 수 있게 v-on 디렉티브를 추가한다.
// TodoList.vue export default { props: ['propsdata'], ...
<!-- App.vue --> <template> <div id="app"> <TodoHeader></TodoHeader> <TodoInput v-on:addTodo="addTodo"></TodoInput> <TodoList v-bind:propsdata="todoItems"></TodoList> <TodoFooter></TodoFooter> </div> </template>
- App.vue 파일에 todoItems 데이터 속성을 선언하고, TodoList 컴포넌트의 propsdata 속성에 props로 전달하였다.
- 그리고 TodoInput 컴포넌트 태그에서 버튼을 클릭했을 때 발생하는 이벤트 이름을 addTodo로 정한다.
- 해당 이벤트를 받아서 실행할 App 컴포넌트의 메서드도 addTodo( )로 한다.
반응형TodoInput 컴포넌트와 TodoList 컴포넌트 수정하기
- props와 이벤트 전달을 적용하기 위해 상위 컴포넌트의 코드를 바꿨으니 하위 컴포넌트 TodoInput과 TodoList를 수정하자.
- 첫 번째로, TodoInput 컴포넌트의 addTodo( ) 메서드에 this.$emit('addTodo', value);를 추가한다.
- 이벤트를 전달할 때 할 일 텍스트 값인 value 객체를 인자 값으로 전달한다. 그리고 로컬 스토리지에 데이터를 저장하는 기존 코드 localStorage.setItem(value, value);를 삭제한다.
// TodoInput.vue addTodo() { if (this.newTodoItem !== "") { var value = this.newTodoItem && this.newTodoItem.trim(); this.$emit('addTodo', value); this.clearInput(); } },
- 그리고 App 컴포넌트의 addTodo( ) 메서드에 아래와 같이 추가한다.
// App.vue addTodo(todoItem) { localStorage.setItem(todoItem, todoItem); this.todoItems.push(todoItem); }
- 이렇게 변경하고 나서 [+] 버튼을 클릭하면 TodoInput 컴포넌트에서 App 컴포넌트로 신호(이벤트)를 보내 App 컴포넌트의 addTodo( ) 메서드를 실행한다.
- addTodo( ) 메서드의 인자 값 todoItem은 TodoInput 컴포넌트에서 올려 보낸 할 일 텍스트 값이다. 이 값을 로컬 스토리지에 저장하고, App 컴포넌트의 todoItems 데이터 속성에도 추가한다.
- 두 번째로, TodoList 컴포넌트의 <template> 내용을 아래와 같이 수정한다.
<li v-for="(todoItem, index) in propsdata" v-bind:key="todoItem" class="shadow"> <i class="checkBtn fas fa-check" aria-hidden="true"></i> {{ todoItem }} <span class="removeBtn" type="button" @click="removeTodo(todoItem, index)"> <i class="far fa-trash-alt" aria-hidden="true"></i> </span> </li>
- <li> 태그에서 v-for 디렉티브의 반복 대상을 propsdata로 변경하였다.
- 기존에는 TodoList의 데이터 속성인 todoItems였지만, 이제는 App 컴포넌트의 todoItems 데이터의 개수만큼 목록 아이템을 생성한다.
- 위 코드를 저장하고 화면에서 할 일을 추가하면 새로 고침을 하지 않고도 목록이 갱신되는 것을 확인할 수 있다.
TodoList에서 불필요한 코드 제거하기
- 넘어가기 전에 TodoList.vue의 코드를 정리하고 App.vue에 코드를 옮기도록 하겠다.
<!-- TodoList.vue --> <script> export default { props: ['propsdata'], methods: { removeTodo(todoItem, index) { localStorage.removeItem(todoItem); this.todoItems.splice(index, 1); } } } </script>
// App.vue created() { // TodoList.vue에서 옮겨온 코드 if (localStorage.length) { for(var i = 0; i < localStorage.length; i++) { this.todoItems.push(localStorage.key(i)); } } },
- TodoList 컴포넌트에서 사용하던 데이터 속성 todoItems는 이제 불필요하므로 제거한다.
- 그리고 컴포넌트가 생성될 때 로컬 스토리지에 저장된 데이터를 모두 불러와 배열에 담아 주던 created( ) 로직도 App 컴포넌트로 옮긴다.
- 이렇게 해서 할 일을 추가했을 때 목록이 갱신되지 않던 문제는 해결하였다.
<이벤트 전달을 이용해 Clear All 버튼 기능 개선하기>
- 이번에는 [Clear All] 버튼을 울렀을 떄 자동으로 화면이 갱신될 수 있도록 코드를 변경해 보겠다.
- 앞에서 배운 이벤트 버스 방식과 이벤트 전달 방식 중 이벤트 전달 방식을 사용하자.
- 컴포넌트 간 이벤트 전달은 '하위 컴포넌트에서 발생시킨 이벤트를 상위 컴포넌트에서 받아 상위 컴포넌트의 메서드를 동작시키는 것'이라고 설명했다.
- 그러면 애플리케이션에서는 하위 컴포넌트인 TodoFooter에서 발생시킬 이벤트 이름을 removeAll로 하고, 상위 컴포넌트인 App에서 받아 실행시킬 메서드 이름을 clearAll( )로 정한다.
상위 컴포넌트 코드 수정하기
- 상위 컴포넌트인 App.vue 파일에 아래의 내용을 추가한다.
<!-- App.vue --> <TodoFooter v-on:removeAll="clearAll"></TodoFooter>
// App.vue methods: { clearAll() { localStorage.clear(); this.todoItems = []; },
- App 컴포넌트의 clearAll( ) 메서드는 TodoFooter 컴포넌트의 [Clear All] 버튼을 클릭했을 때 로컬 스토리지의 데이터를 모두 삭제하고 todoItems의 데이터를 비운다.
하위 컴포넌트 코드 수정하기
- 하위 컴포넌트(TodoFooter.vue)에 있는 [Clear All] 버튼의 클릭 이벤트 메서드 clearTodo( )를 아래와 같이 수정한다.
clearTodo() { this.$emit('removeAll'); }
- [Clear All] 버튼을 클릭하면 removeAll 이벤트를 발생시켜 상위 컴포넌트(App.vue)로 전달한다. 그러면 상위 컴포넌트에서 removeAll을 받아 상위 컴포넌트에 정의된 clearAll( ) 메서드를 실행한다.
- 여기까지 코드를 저장하고 화면의 [Clear All] 버튼을 클릭하여 모든 할 일이 정상적으로 삭제되는지 확인한다.
<이벤트 전달을 이용해 할 일 삭제 기능 개선하기>
- TodoList 컴포넌트의 각 할 일 아이템을 삭제하는 로직에도 이벤드 전달 방식을 적용한다.
- App.vue 파일과 TodoList.vue 파일의 코드를 다음과 같은 순서로 변경한다.
<!-- App.vue --> <TodoList v-bind:propsdata="todoItems" @removeTodo="removeTodo"></TodoList>
// TodoList.vue removeTodo(todoItem, index) { this.$emit('removeTodo', todoItem, index); }
// App.vue removeTodo(todoItem, index) { localStorage.removeItem(todoItem); this.todoItems.splice(index, 1); },
- <TodoList>의 @removeTodo는 앞에서 다뤘던 이벤트 전달 디렉티브인 v-on:removeTodo의 약식 문법이다.
- 할 일 목록에서 휴지통 아이콘을 클릭하면 TodoList 컴포넌트의 removeTodo( ) 메서드에서 removeTodo라는 이벤트를 발생시켜 App 컴포넌트로 전달한다.
- 이벤트를 전달할 때 선택한 할 일의 텍스트(todoItem)와 인덱스(index)를 같이 보낸다. 이런 방식으로 이벤트를 전달할 때 인자를 함게 전달 할 수 있다.
- 코드를 저장하고 화면에서 할 일을 삭제했을 때 정상적으로 동작하는지 확인한다.
- $emit( ) API 형식과 전달 인자의 규칙
- 하위 컴포넌트에서 이벤트를 발생시켜 상위 컴포넌트로 신호를 보낼 때 $emit( )를 사용한다. API의 기본 형식은 $emit('이벤트 이름')이지만 $emit('이벤트 이름', 인자1, 인자2, ...)와 같은 형식으로 하위 컴포넌트의 특정 데이터를 전달할 수 있다. 다만 전달받은 인자 값은 상위 컴포넌트에서 참고용으로만 활용하고, 데이터 값은 변경하지 말아야 한다. 컴포넌트는 각자 고유한 유효 범위를 갖기 때문에 상위 컴포넌트에서 전달받은 인자 값을 갱신하더라도 하위 컴포넌트에는 반영되지 않는다.
- 지금까지 컴포넌트가 분리되어 데이터를 공유하지 못했던 문제점은 컴포넌트 통신 방법(props 속성, 이벤트 전달)으로 해결하였다.
해당 글은 [Do it! Vue.js 입문] 책을 토대로 공부한 내용을 기록하기 위하여 작성됨.
반응형'개발 공부 > Do it! Vue.js 입문' 카테고리의 다른 글
7장. Vue.js 고급 개발자 되기 - 1 (1) 2025.06.02 6장. 실전 애플리케이션 만들기 - 6 (1) 2025.06.01 6장. 실전 애플리케이션 만들기 - 4 (1) 2025.05.30 6장. 실전 애플리케이션 만들기 - 3 (0) 2025.05.29 6장. 실전 애플리케이션 만들기 - 2 (0) 2025.05.28 - 지금까지 구현한 애플리케이션의 문제점은 다음 2가지이다.