ksundev 님의 블로그

[TODO App][Refactoring] App 컴포넌트를 컨테이너로 만들기 4 본문

[개발] Vue.js/중급

[TODO App][Refactoring] App 컴포넌트를 컨테이너로 만들기 4

ksundev 2025. 7. 9. 15:06

할일 전체 초기화 기능 구현하기

이번 포스트에서는 할 일 목록을 전체적으로 초기화하는 기능을 구현해보겠습니다. 특히 Vue.js에서 자식 컴포넌트가 부모 컴포넌트의 데이터를 변경하는 방법과 이벤트 기반 통신 패턴에 대해 알아보겠습니다.

1. 현재 상황과 해결해야 할 문제

컴포넌트 구조 개선: UI와 비즈니스 로직 분리

이전 포스트들과 마찬가지로, 이번에도 컴포넌트의 책임을 명확히 분리했습니다:

  • TodoFooter 컴포넌트: UI 구현만 담당 (프레젠테이션 컴포넌트)
  • App 컴포넌트: 비즈니스 로직과 데이터 관리 담당 (컨테이너 컴포넌트)

이러한 분리를 통해 코드의 재사용성과 유지보수성을 크게 향상시킬 수 있습니다.

현재 TodoFooter 컴포넌트의 상태

현재 TodoFooter.vue는 프레젠테이션 컴포넌트로 구현되어 있으며, "전체 초기화" 버튼을 통해 모든 할 일을 삭제하는 기능을 제공합니다. 하지만 실제 데이터 삭제 로직은 App 컴포넌트에서 처리합니다.

해결해야 할 문제

  • 전체 초기화 버튼 클릭 시 모든 할 일 삭제
  • localStorage에서 모든 데이터 제거
  • UI에서 할 일 목록 완전히 비우기

2. TodoFooter 컴포넌트 구현

TodoFooter.vue 구조

<template>
  <div class="clearAllContainer">
    <span class="clearAllBtn" @click="clearTodo">전체 초기화</span>
  </div>
</template>

<script>
export default {
  methods: {
    clearTodo() {
      this.$emit("clearTodo");
    },
  },
};
</script>

프레젠테이션 컴포넌트로서의 역할

TodoFooter는 다음과 같은 UI 관련 책임만 가집니다:

  1. 버튼 렌더링: "전체 초기화" 버튼 표시
  2. 클릭 이벤트 처리: 사용자 클릭 시 이벤트 발생
  3. 스타일링: 버튼의 시각적 표현

실제 데이터 삭제 로직은 부모 컴포넌트에 위임합니다.

이벤트 발생 구조 분석

// 자식 컴포넌트에서 부모에게 이벤트 발생
methods: {
  clearTodo() {
    this.$emit("clearTodo"); // 커스텀 이벤트 발생
  },
}

3. App 컴포넌트에서 이벤트 처리

App.vue에서 clearAllItems 메서드 구현

<template>
  <div id="app">
    <TodoHeader />
    <TodoInput @addTodo="addOneItem" />
    <TodoList
      :todoItems="todoItems"
      @removeItem="removeOneItem"
      @toggleComplete="toggleOneItem"
    />
    <TodoFooter @clearTodo="clearAllItems" />
  </div>
</template>

<script>
export default {
  // ... 기존 코드 ...
  methods: {
    // ... 기존 메서드들 ...
    clearAllItems() {
      localStorage.clear();
      this.todoItems = [];
    },
  },
};
</script>

컨테이너 컴포넌트로서의 역할

App 컴포넌트는 다음과 같은 비즈니스 로직을 담당합니다:

  1. 데이터 관리: todoItems 배열 상태 관리
  2. localStorage 동기화: 브라우저 저장소와 데이터 동기화
  3. 이벤트 처리: 자식 컴포넌트로부터 받은 이벤트 처리

clearAllItems 메서드 분석

clearAllItems() {
  localStorage.clear();  // 브라우저 저장소 완전 삭제
  this.todoItems = [];   // 반응형 데이터 초기화
}

4. 이벤트 기반 통신 패턴

부모-자식 컴포넌트 통신 원리

자식 컴포넌트 (TodoFooter) - UI 담당
    ↓ $emit("clearTodo")
부모 컴포넌트 (App) - 비즈니스 로직 담당
    ↓ @clearTodo="clearAllItems"
메서드 실행 및 데이터 변경

Props Down, Events Up 패턴

Vue.js에서는 다음과 같은 단방향 데이터 흐름을 권장합니다:

  1. Props Down: 부모에서 자식으로 데이터 전달
  2. Events Up: 자식에서 부모로 이벤트 발생
// Props Down 예시
<TodoList :todoItems="todoItems" />

// Events Up 예시
<TodoFooter @clearTodo="clearAllItems" />

5. localStorage와 반응형 데이터 동기화

데이터 초기화 과정

clearAllItems() {
  // 1. 브라우저 저장소 완전 삭제
  localStorage.clear();

  // 2. Vue 반응형 데이터 초기화
  this.todoItems = [];
}

왜 두 단계로 나누어 처리하는가?

  1. localStorage.clear(): 브라우저에 저장된 영구 데이터 삭제
  2. this.todoItems = []: Vue의 반응형 시스템이 감지하여 UI 업데이트

6. 실제 동작 과정

전체 초기화 플로우

1. 사용자 액션: "전체 초기화" 버튼 클릭
   ↓
2. 이벤트 발생: TodoFooter에서 clearTodo 이벤트 발생
   ↓
3. 메서드 호출: App 컴포넌트의 clearAllItems 메서드 실행
   ↓
4. 데이터 삭제: localStorage에서 모든 데이터 제거
   ↓
5. 상태 초기화: todoItems 배열을 빈 배열로 설정
   ↓
6. UI 업데이트: Vue의 반응형 시스템이 자동으로 화면 업데이트

시각적 결과

  • 할 일 목록이 완전히 비워짐
  • 체크박스와 텍스트가 모두 사라짐
  • 앱이 초기 상태로 돌아감

7. 컴포넌트 책임 분리

TodoFooter의 책임 (프레젠테이션 컴포넌트)

// UI 구현만 담당
export default {
  methods: {
    clearTodo() {
      // 데이터 변경 로직은 부모에게 위임
      this.$emit("clearTodo");
    },
  },
};

App 컴포넌트의 책임 (컨테이너 컴포넌트)

// 비즈니스 로직과 데이터 관리 담당
methods: {
  clearAllItems() {
    // 실제 데이터 변경 로직
    localStorage.clear();
    this.todoItems = [];
  },
}

8. 결론

이번 포스트에서는 Vue.js의 이벤트 기반 통신 패턴을 활용하여 전체 초기화 기능을 구현했습니다.

핵심 학습 포인트

  1. 컴포넌트 책임 분리: UI와 비즈니스 로직의 명확한 분리
  2. 이벤트 기반 통신: $emit을 통한 자식→부모 통신
  3. 단방향 데이터 흐름: Props Down, Events Up 패턴
  4. localStorage 동기화: 브라우저 저장소와 Vue 상태의 일관성 유지

완성된 Todo 앱의 기능

  • ✅ 할 일 추가
  • ✅ 할 일 삭제
  • ✅ 할 일 완료 상태 토글
  • ✅ 전체 초기화

이제 완전히 동작하는 Todo 앱이 완성되었습니다! 다음 포스트에서는 복습하는 의미에서 퀴즈 몇가지만 풀어볼까요?