반응형

To-Do 리스트 애플리케이션에서 add 컴포넌트와 list 컴포넌트를 분리하는 작업을 하겠습니다.
컴포넌트를 분리하면 코드의 모듈화가 가능해지고, 각 컴포넌트가 자신의 역할에만 집중할 수 있게 됩니다.

이 과정에서 Vue.js의 컴포넌트 간 데이터 전달(propsevents)을 이해하는 데도 도움이 됩니다.

 

1. AddTodo.vue 컴포넌트 생성

 

1. AddTodo.vue 파일 생성:

src/components 디렉토리 안에 AddTodo.vue 파일을 생성합니다.

2. AddTodo.vue 초기 코드 작성:

이 컴포넌트는 새로운 할 일을 입력하고 추가하는 역할을 합니다.

 

<template>
  <div>
    <input v-model="newTodo" placeholder="Add a new to-do" @keyup.enter="submitTodo" />
    <button @click="submitTodo">Add</button>
  </div>
</template>

<script>
export default {
  name: 'AddTodo',
  data() {
    return {
      newTodo: ''
    };
  },
  methods: {
    submitTodo() {
      if (this.newTodo.trim()) {
        this.$emit('add-todo', this.newTodo);
        this.newTodo = ''; // 입력 필드 초기화
      }
    }
  }
};
</script>

3. 기능 설명:

newTodo라는 로컬 데이터 속성을 가지고 있으며, 사용자가 할 일 텍스트를 입력합니다.

사용자가 Enter 키를 누르거나 “Add” 버튼을 클릭하면 submitTodo 메서드가 호출됩니다.

submitTodo 메서드는 add-todo 이벤트를 상위 컴포넌트로 발송(emit)하여 입력된 할 일을 전달합니다.

 

2. TodoList.vue 컴포넌트 생성

 

1. TodoList.vue 파일 생성:

src/components 디렉토리 안에 TodoList.vue 파일을 생성합니다.

2. TodoList.vue 초기 코드 작성:

이 컴포넌트는 할 일 리스트를 렌더링하고, 할 일 항목을 삭제하는 역할을 합니다.

<template>
  <ul>
    <li v-for="(todo, index) in todos" :key="index">
      <input type="checkbox" v-model="todo.completed" @change="saveTodos" />
      <span :class="{ completed: todo.completed }">{{ todo.text }}</span>
      <button @click="removeTodo(index)">Delete</button>
    </li>
  </ul>
</template>

<script>
export default {
  name: 'ToDoList',
  props: {
    todos: {
      type: Array,
      required: true
    }
  },
  methods: {
    removeTodo(index) {
      this.$emit('remove-todo', index);
    },
    saveTodos() {
      this.$emit('save-todos');
    }
  }
};
</script>

<style>
.completed {
  text-decoration: line-through;
  color: grey;
}
</style>

3. 기능 설명:

todos 배열을 props로 받아와서 리스트를 렌더링합니다.

각 할 일 항목의 체크박스 상태를 변경하면 saveTodos 메서드가 호출되어, 상위 컴포넌트로 상태 저장 이벤트(save-todos)를 발송합니다.

“Delete” 버튼을 클릭하면 해당 항목을 삭제하기 위한 remove-todo 이벤트를 발송합니다.

 

3. App.vue 파일 수정

 

1. AddTodoTodoList 컴포넌트 가져오기:

App.vue 파일에서 새로 생성한 두 컴포넌트를 가져오고, 이를 사용해 To-Do 리스트를 완성합니다.

<template>
  <h1>Vue.js To-Do App</h1>
  <add-todo @add-todo="addTodo" />
  <to-do-list :todos="todos" @remove-todo="removeTodo" @save-todos="saveTodos" />
</template>

<script>
import AddTodo from './components/AddTodo.vue';
import ToDoList from './components/ToDoList.vue';


export default {
  name: 'App',
  components: {
    AddTodo,
    ToDoList
  },
  data() {
    return {
      todos: []
    };
  },
  mounted() {
    const savedTodos = localStorage.getItem('todos');
    if (savedTodos) {
      this.todos = JSON.parse(savedTodos);
    }
  },
  methods: {
    addTodo(newTodo) {
      this.todos.push({ text: newTodo, completed: false });
      this.saveTodos();
    },
    removeTodo(index) {
      this.todos.splice(index, 1);
      this.saveTodos();
    },
    saveTodos() {
      localStorage.setItem('todos', JSON.stringify(this.todos));
    }
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

 

2. 기능 설명:

AddTodo 컴포넌트에서 발송된 add-todo 이벤트를 받아 addTodo 메서드를 호출합니다.

TodoList 컴포넌트에서 발송된 remove-todosave-todos 이벤트를 처리하여 할 일 리스트를 관리하고 로컬 스토리지에 저장합니다.

로컬 스토리지에서 할 일 목록을 불러오고, 이를 todos 배열에 저장합니다.

 

 

요약

 

1. AddTodo.vue: 사용자가 할 일을 입력하고 추가하는 기능을 담당하는 컴포넌트.

2. TodoList.vue: 할 일 목록을 렌더링하고, 항목을 삭제하거나 완료 상태를 관리하는 컴포넌트.

3. App.vue: 상위 컴포넌트로서, AddTodoTodoList 컴포넌트를 포함하고, 할 일 데이터를 중앙에서 관리하는 역할을 수행.


이전블로그 
로컬스토리지 통한 to-do list 구현
https://juntcom.tistory.com/317

반응형
반응형

 

To-Do 리스트 항목을 로컬 스토리지에 저장하고, 페이지 새로고침 후에도 할 일 목록이 유지되도록 기능을 추가하겠습니다.

이를 위해 ToDoList.vue 컴포넌트의 코드를 수정할 것입니다.

 

수정된 ToDoList.vue 코드

<template>
  <div>
    <h2>To-Do List</h2>
    <input v-model="newTodo" placeholder="Add a new to-do" @keyup.enter="addTodo" />
    <button @click="addTodo">Add</button>

    <ul>
      <li v-for="(todo, index) in todos" :key="index">
        <input type="checkbox" v-model="todo.completed" @change="saveTodos" />
        <span :class="{ completed: todo.completed }">{{ todo.text }}</span>
        <button @click="removeTodo(index)">Delete</button>
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  name: 'ToDoList',
  data() {
    return {
      newTodo: '',
      todos: []
    };
  },
  mounted() {
    // 컴포넌트가 마운트될 때 로컬 스토리지에서 할 일 목록을 불러옵니다.
    const savedTodos = localStorage.getItem('todos');
    if (savedTodos) {
      this.todos = JSON.parse(savedTodos);
    }
  },
  methods: {
    addTodo() {
      if (this.newTodo.trim()) {
        this.todos.push({ text: this.newTodo, completed: false });
        this.newTodo = '';
        this.saveTodos(); // 새로운 할 일 추가 후 로컬 스토리지에 저장
      }
    },
    removeTodo(index) {
      this.todos.splice(index, 1);
      this.saveTodos(); // 할 일 삭제 후 로컬 스토리지에 저장
    },
    saveTodos() {
      // 현재 할 일 목록을 로컬 스토리지에 저장합니다.
      localStorage.setItem('todos', JSON.stringify(this.todos));
    }
  }
};
</script>

<style>
.completed {
  text-decoration: line-through;
  color: grey;
}
</style>

코드 설명

 

1. 로컬 스토리지에서 할 일 목록 불러오기:

mounted() 훅에서 컴포넌트가 마운트될 때 localStorage.getItem('todos')를 통해 로컬 스토리지에 저장된 할 일 목록을 불러옵니다.

만약 저장된 할 일 목록이 있으면, 이를 JSON으로 파싱하여 todos 배열에 할당합니다.

2. 할 일 목록 저장:

addTodoremoveTodo 메소드가 실행될 때, saveTodos() 메소드를 호출하여 현재 todos 배열을 로컬 스토리지에 저장합니다.

saveTodos() 메소드는 localStorage.setItem('todos', JSON.stringify(this.todos))를 통해 todos 배열을 문자열로 변환하여 로컬 스토리지에 저장합니다.

3. 할 일 완료 상태 변경 시 저장:

할 일의 체크박스를 클릭하여 완료 상태를 변경할 때도 saveTodos() 메소드를 호출하여 변경된 상태를 로컬 스토리지에 저장합니다.

 

 

 

개발자도구 > Application > Local storage > 도메인 > todos 에 데이터가 쌓인 걸 볼 수 있습니다. 


로컬스토리지란

로컬 스토리지는 웹 브라우저에서 제공하는 클라이언트 측 저장소입니다. 이를 통해 웹 애플리케이션은 사용자의 데이터를 클라이언트(브라우저) 측에 영구적으로 저장할 수 있습니다. 로컬 스토리지에 저장된 데이터는 브라우저를 닫거나 컴퓨터를 재부팅해도 유지됩니다.

 

로컬 스토리지의 특징

 

1. 키-값(Key-Value) 쌍으로 저장:

로컬 스토리지는 데이터를 키-값 쌍으로 저장합니다. 즉, 데이터에 접근할 때 특정 키를 사용하여 해당 값을 가져오거나 수정할 수 있습니다.

2. 영구 저장:

로컬 스토리지에 저장된 데이터는 사용자가 명시적으로 삭제하지 않는 한 영구적으로 저장됩니다. 쿠키와 달리 만료 시간이 없으며, 브라우저를 닫거나 컴퓨터를 재부팅해도 유지됩니다.

3. 도메인 별 저장:

로컬 스토리지는 도메인 단위로 데이터를 저장합니다. 이는 특정 웹사이트에서 저장된 데이터가 다른 도메인에서는 접근할 수 없음을 의미합니다.

4. 5MB 정도의 용량 제한:

대부분의 브라우저에서는 로컬 스토리지에 약 5MB 정도의 데이터를 저장할 수 있습니다. 이는 비교적 큰 용량으로, 텍스트 기반 데이터나 간단한 설정 값을 저장하는 데 충분합니다.

5. 동기적 접근:

로컬 스토리지는 동기적으로 동작합니다. 즉, 데이터를 저장하거나 불러올 때 해당 작업이 즉시 처리되며, 결과가 바로 반환됩니다.

 

로컬 스토리지 사용 방법

 

로컬 스토리지는 JavaScript를 통해 접근할 수 있으며, 다음과 같은 메서드를 사용합니다:




1. 데이터 저장:

localStorage.setItem(key, value)

예:

localStorage.setItem('username', 'JohnDoe');

2. 데이터 가져오기:

localStorage.getItem(key)

예:

const username = localStorage.getItem('username');
console.log(username); // 'JohnDoe'

3. 데이터 삭제:

특정 데이터 삭제: localStorage.removeItem(key)

모든 데이터 삭제: localStorage.clear()

예:

localStorage.removeItem('username');
localStorage.clear(); // 모든 데이터 삭제

4. 저장된 데이터 개수 확인:

localStorage.length

예:

const length = localStorage.length;
console.log(length);

 

로컬 스토리지의 활용 사례

 

사용자 설정 저장:

예를 들어, 웹사이트의 테마 설정이나 글꼴 크기 등의 개인 설정을 로컬 스토리지에 저장하여, 사용자가 브라우저를 닫았다가 다시 열어도 같은 설정을 유지할 수 있습니다.

간단한 데이터 캐싱:

네트워크 요청 결과를 로컬 스토리지에 저장하여, 동일한 데이터를 다시 요청할 필요 없이 빠르게 접근할 수 있습니다.

폼 데이터 임시 저장:

사용자가 폼을 작성하는 중에 브라우저를 닫거나 페이지를 떠났다가 돌아오더라도, 작성된 내용을 잃지 않도록 로컬 스토리지에 임시로 저장할 수 있습니다.

 

로컬 스토리지의 한계

 

보안 문제: 로컬 스토리지는 클라이언트 측에 저장되므로, 악의적인 사용자가 쉽게 접근할 수 있습니다. 따라서 민감한 정보(예: 비밀번호, 개인 정보)를 저장하는 데는 적합하지 않습니다.

용량 제한: 로컬 스토리지는 보통 5MB 정도의 용량 제한이 있습니다. 이 때문에 대용량 데이터를 저장하는 데는 적합하지 않습니다.

브라우저 호환성: 대부분의 현대 브라우저는 로컬 스토리지를 지원하지만, 매우 오래된 브라우저에서는 지원되지 않을 수 있습니다.




이전 블로그

https://juntcom.tistory.com/316

 

반응형
반응형

To-Do 리스트를 구현하기 위해 새로운 컴포넌트를 만드는 방법을 단계별로 설명드리겠습니다.

이 설명을 따라 새로운 컴포넌트를 만들고 App.vue에서 이를 사용해 To-Do 리스트를 구현할 수 있습니다.

 

1. 새로운 컴포넌트 생성

 

1. ToDoList.vue 파일 생성:

src/components 디렉토리 안에 ToDoList.vue 파일을 생성합니다.

2. ToDoList.vue 초기 코드 작성:

아래 코드를 ToDoList.vue 파일에 추가합니다.

이 코드는 To-Do 리스트 항목을 추가하고, 리스트를 렌더링하는 기본적인 구조입니다.

 

<template>
  <div>
    <h2>To-Do List</h2>
    <input v-model="newTodo" placeholder="Add a new to-do" @keyup.enter="addTodo" />
    <button @click="addTodo">Add</button>
    
    <ul>
      <li v-for="(todo, index) in todos" :key="index">
        <input type="checkbox" v-model="todo.completed" />
        <span :class="{ completed: todo.completed }">{{ todo.text }}</span>
        <button @click="removeTodo(index)">Delete</button>
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  name: 'ToDoList',
  data() {
    return {
      newTodo: '',
      todos: []
    };
  },
  methods: {
    addTodo() {
      if (this.newTodo.trim()) {
        this.todos.push({ text: this.newTodo, completed: false });
        this.newTodo = '';
      }
    },
    removeTodo(index) {
      this.todos.splice(index, 1);
    }
  }
};
</script>

<style>
.completed {
  text-decoration: line-through;
  color: grey;
}
</style>

2. App.vue 파일 수정

 

1. ToDoList 컴포넌트 가져오기:

App.vue 파일에서 ToDoList.vue 컴포넌트를 가져오고, 등록합니다.

<template>
  <div id="app">
    <h1>Vue.js To-Do App</h1>
    <ToDoList />
  </div>
</template>

<script>
import ToDoList from './components/ToDoList.vue';

export default {
  name: 'App',
  components: {
    ToDoList
  }
};
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

2. 컴포넌트 실행 및 테스트:

프로젝트를 실행하여 ToDoList.vue 컴포넌트가 정상적으로 렌더링되고, 할 일 항목을 추가, 삭제할 수 있는지 확인합니다.

 

 

3. 스타일 및 추가 기능

 

1. 스타일 개선:

ToDoList.vue 파일에 추가 스타일을 적용하여 UI를 더 예쁘게 꾸밀 수 있습니다.

예를 들어, 인풋 필드, 버튼, 리스트 항목 등에 스타일을 적용할 수 있습니다.

2. 추가 기능 구현:

할 일 목록을 로컬 스토리지에 저장하여 새로고침 후에도 유지되도록 하거나, 완료된 항목을 숨기거나 필터링하는 기능을 추가할 수 있습니다.




이전 블로그 

Vue.js 프로젝트 설정 - vue cli 시작하기

https://juntcom.tistory.com/284

반응형
반응형

Vue.js로 개발할 때 유용한 레퍼런스와 리소스를 정리해드리겠습니다. 이들은 Vue.js의 기본 개념부터 심화된 주제, 그리고 개발 도구와 관련된 다양한 정보들을 포함합니다.

 

공식 문서 및 가이드

 

1. Vue.js 공식 문서:

Vue.js 공식 문서

Vue.js의 모든 기능과 개념에 대한 공식 가이드입니다. 기초부터 고급 주제까지 잘 정리되어 있습니다.

2. Vue.js 스타일 가이드:

Vue.js 공식 스타일 가이드

Vue.js 프로젝트에서 일관된 코딩 스타일을 유지하기 위한 권장사항이 담긴 가이드입니다.

3. Vue.js API 레퍼런스:

Vue.js API 레퍼런스

Vue.js의 모든 API와 관련된 설명을 제공합니다. 특정 기능이나 메서드를 찾을 때 유용합니다.

4. Vue.js 3 공식 문서:

Vue 3 공식 문서

Vue.js 3의 새로운 기능들과 변경된 사항들을 포함한 공식 가이드입니다.

 

튜토리얼 및 학습 리소스

 

1. Vue Mastery:

Vue Mastery

Vue.js 공식 학습 파트너로, 초급부터 고급까지 다양한 튜토리얼 동영상을 제공합니다. 일부 강의는 무료로 제공됩니다.

2. Vue School:

Vue School

Vue.js 학습을 위한 온라인 교육 플랫폼으로, 심화된 Vue.js 주제들을 다루는 동영상 강의를 제공합니다.

3. Scrimba: Learn Vue.js for free:

Scrimba: Learn Vue.js

무료로 제공되는 Vue.js 강의 시리즈로, 인터랙티브한 코딩 환경에서 학습할 수 있습니다.

4. Laracasts Vue.js 시리즈:

Laracasts Vue.js 시리즈

Vue.js와 관련된 강좌를 제공하는 사이트로, 특히 Laravel과 함께 사용하는 방법을 다룹니다. 일부는 무료로 제공됩니다.

 

유용한 도구와 라이브러리

 

1. Vue CLI:

Vue CLI

Vue.js 프로젝트 생성을 위한 강력한 CLI 도구입니다. 다양한 설정을 자동으로 처리해주며, 플러그인 시스템을 통해 확장이 가능합니다.

2. Vue Router:

Vue Router 공식 문서

Vue.js에서 SPA(Single Page Application)의 라우팅을 담당하는 라이브러리로, 공식 문서를 통해 설정과 사용법을 자세히 배울 수 있습니다.

3. Vuex:

Vuex 공식 문서

Vue.js에서 상태 관리를 위한 중앙 집중식 저장소 역할을 하는 라이브러리입니다. 특히 대규모 애플리케이션에서 유용합니다.

4. Axios:

Axios 공식 문서

Vue.js와 함께 자주 사용되는 HTTP 클라이언트 라이브러리로, API 통신을 쉽게 처리할 수 있습니다.

5. Vuetify:

Vuetify 공식 문서

Vue.js 기반의 Material Design 컴포넌트 프레임워크로, 아름답고 반응형인 UI를 쉽게 구축할 수 있습니다.

6. Quasar Framework:

Quasar Framework 공식 문서

Vue.js 기반의 강력한 프레임워크로, 웹, 모바일, 데스크탑 애플리케이션을 한 번에 개발할 수 있는 도구를 제공합니다.

 

커뮤니티 및 포럼

 

1. Vue.js 공식 포럼:

Vue.js 공식 포럼

Vue.js 커뮤니티가 활발하게 활동하는 공식 포럼입니다. 질문과 답변, 다양한 토론이 이루어집니다.

2. Stack Overflow:

Stack Overflow Vue.js 태그

Vue.js 관련 질문과 답변을 찾을 수 있는 커뮤니티입니다. 개발 중 발생하는 문제를 해결할 때 유용합니다.

3. Reddit:

Reddit Vue.js 커뮤니티

Vue.js 관련 토론과 정보 공유가 활발히 이루어지는 Reddit 커뮤니티입니다.

4. Discord Vue.js 커뮤니티:

Vue Land (Discord 서버)

Vue.js 개발자들이 실시간으로 소통할 수 있는 공식 Discord 커뮤니티입니다.

 

추가 리소스

 

1. Awesome Vue:

Awesome Vue

Vue.js 관련 리소스와 라이브러리를 모아놓은 깃허브 리포지토리입니다. 다양한 플러그인, 튜토리얼, 프로젝트 예제를 찾을 수 있습니다.

2. Vue.js Examples:

Vue.js Examples

다양한 Vue.js 애플리케이션 예제와 코드를 제공하는 사이트입니다. 실제 프로젝트에 응용할 수 있는 샘플 코드들이 많습니다.

반응형
반응형

1. Visual Studio Code 설치

 

먼저, Visual Studio Code를 설치합니다. VS Code는 무료로 제공되며, 다양한 운영 체제(Windows, macOS, Linux)에서 사용할 수 있습니다.

 

설치 방법: 공식 웹사이트에서 운영 체제에 맞는 설치 파일을 다운로드한 후, 설치 프로그램을 실행하여 설치합니다.

 

2. Node.js 설치

 

Vue CLI와 관련된 도구들을 사용하려면 Node.js가 설치되어 있어야 합니다. Node.js 설치 시 npm(Node Package Manager)이 함께 설치됩니다.

 

설치 방법:

1. Node.js 공식 웹사이트에서 최신 LTS(Long Term Support) 버전을 다운로드합니다.

2. 설치 프로그램을 실행하여 Node.js와 npm을 설치합니다.

3. 설치 완료 후, 터미널에서 Node.js와 npm의 버전을 확인합니다.

 

node -v
npm -v

3. Vue CLI 설치

 

Vue.js 프로젝트를 쉽게 생성하고 관리하기 위해 Vue CLI(Command Line Interface)를 설치합니다.

 

설치 방법:

1. 터미널을 열고 다음 명령어를 입력하여 Vue CLI를 전역으로 설치합니다.

npm install -g @vue/cli

2. 설치가 완료되면, Vue CLI의 버전을 확인합니다.

vue --version

4. Visual Studio Code 플러그인 설치

vue 라는 플러그인 설치. 그 외에 다른 확장도구 설치해도 무방

 

5. Debugger for Chrome:
https://chromewebstore.google.com/search/vue?hl=ko 

브라우저에서 vue 개발을 도와주는 디버그 툴을 설치해주셔야 합니다.  

 

반응형
반응형

Flutter는 다양한 디자인 패턴을 사용하여 애플리케이션 개발을 더욱 구조적이고 유지보수가 쉽게 만들 수 있습니다. 주요 패턴들을 정리해보겠습니다.

 

1. Provider 패턴

 

설명: Flutter 애플리케이션에서 상태 관리를 간단하고 효율적으로 수행하기 위해 사용됩니다. InheritedWidget을 기반으로 하여, 상위 위젯 트리에서 상태를 하위 위젯 트리에 전달할 수 있습니다.

사용법: provider 패키지를 사용하여 구현합니다.

예제:

class Counter with ChangeNotifier {
  int _count = 0;
  int get count => _count;

  void increment() {
    _count++;
    notifyListeners();
  }
}

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => Counter(),
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Provider Example')),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Text(
                'You have pushed the button this many times:',
              ),
              Consumer<Counter>(
                builder: (context, counter, _) {
                  return Text(
                    '${counter.count}',
                    style: Theme.of(context).textTheme.headline4,
                  );
                },
              ),
            ],
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            Provider.of<Counter>(context, listen: false).increment();
          },
          tooltip: 'Increment',
          child: Icon(Icons.add),
        ),
      ),
    );
  }
}

2. BLoC (Business Logic Component) 패턴

 

설명: BLoC 패턴은 비즈니스 로직을 UI에서 분리하여 유지보수성과 테스트 용이성을 높입니다. 이벤트와 상태 스트림을 사용하여 비동기 데이터 흐름을 관리합니다.

사용법: flutter_bloc 패키지를 사용하여 구현합니다.

예제:

class CounterEvent {}

class CounterState {
  final int counter;
  CounterState(this.counter);
}

class CounterBloc extends Bloc<CounterEvent, CounterState> {
  CounterBloc() : super(CounterState(0));

  @override
  Stream<CounterState> mapEventToState(CounterEvent event) async* {
    if (event is IncrementEvent) {
      yield CounterState(state.counter + 1);
    }
  }
}

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: BlocProvider(
        create: (context) => CounterBloc(),
        child: MyHomePage(),
      ),
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('BLoC Example')),
      body: Center(
        child: BlocBuilder<CounterBloc, CounterState>(
          builder: (context, state) {
            return Text(
              '${state.counter}',
              style: Theme.of(context).textTheme.headline4,
            );
          },
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          BlocProvider.of<CounterBloc>(context).add(IncrementEvent());
        },
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

3. Redux 패턴

 

설명: 상태 관리 라이브러리로, 애플리케이션의 상태를 하나의 중앙 저장소에서 관리합니다. 액션을 통해 상태를 업데이트하고 리듀서를 통해 새로운 상태를 반환합니다.

사용법: flutter_redux 패키지를 사용하여 구현합니다.

예제:

// State
class AppState {
  final int counter;
  AppState(this.counter);
}

// Actions
class IncrementAction {}

// Reducer
AppState counterReducer(AppState state, action) {
  if (action is IncrementAction) {
    return AppState(state.counter + 1);
  }
  return state;
}

// Store
final store = Store<AppState>(
  counterReducer,
  initialState: AppState(0),
);

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return StoreProvider<AppState>(
      store: store,
      child: MaterialApp(
        home: MyHomePage(),
      ),
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Redux Example')),
      body: Center(
        child: StoreConnector<AppState, String>(
          converter: (store) => store.state.counter.toString(),
          builder: (context, counter) {
            return Text(
              counter,
              style: Theme.of(context).textTheme.headline4,
            );
          },
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          StoreProvider.of<AppState>(context).dispatch(IncrementAction());
        },
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

4. MVVM (Model-View-ViewModel) 패턴

 

설명: 애플리케이션의 UI를 모델과 분리하여 유지보수성과 테스트 용이성을 높입니다. ViewModel은 모델 데이터를 처리하고 이를 뷰에 바인딩합니다.

사용법: providerChangeNotifier를 사용하여 구현합니다.

예제:

class CounterViewModel extends ChangeNotifier {
  int _counter = 0;
  int get counter => _counter;

  void increment() {
    _counter++;
    notifyListeners();
  }
}

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context) => CounterViewModel(),
      child: MaterialApp(
        home: MyHomePage(),
      ),
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('MVVM Example')),
      body: Center(
        child: Consumer<CounterViewModel>(
          builder: (context, viewModel, _) {
            return Text(
              '${viewModel.counter}',
              style: Theme.of(context).textTheme.headline4,
            );
          },
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          Provider.of<CounterViewModel>(context, listen: false).increment();
        },
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

참고 자료

 

Flutter Documentation - Provider

Flutter Documentation - BLoC

Redux in Flutter

MVVM in Flutter

반응형
반응형

Dart의 Futureasync/await는 비동기 프로그래밍을 지원하는 핵심 개념입니다.

이들 개념을 사용하면 비동기 작업을 더 쉽게 작성하고 관리할 수 있습니다.

 

Future

 

Future는 비동기 작업의 결과를 나타내는 객체입니다. 이 객체는 작업이 완료되었을 때 결과를 제공하거나 오류를 전달합니다. Future는 JavaScript의 Promise와 유사합니다.

 

Future 사용 예제

 

1. Future 생성

Future 사용 예제

 

1. Future 생성

Future<String> fetchUserOrder() {
  // 2초 후에 'Order Complete' 반환
  return Future.delayed(Duration(seconds: 2), () => 'Order Complete');
}

 

2. then() 사용

void main() {
  fetchUserOrder().then((order) {
    print(order); // 'Order Complete' 출력
  }).catchError((error) {
    print('Error: $error');
  });
}

async 및 await

 

asyncawait 키워드는 비동기 코드를 더 읽기 쉽게 작성할 수 있도록 도와줍니다. async 함수는 Future를 반환하며, await 키워드는 Future가 완료될 때까지 기다린 후 결과를 반환합니다.

 

async 및 await 사용 예제

 

1. async 함수 정의

 

Future<void> fetchUserOrder() async {
  try {
    String order = await Future.delayed(Duration(seconds: 2), () => 'Order Complete');
    print(order); // 'Order Complete' 출력
  } catch (e) {
    print('Error: $e');
  }
}

2. async 함수 호출

void main() {
  fetchUserOrder();
  print('Fetching user order...'); // 먼저 출력됨
}

 

자세한 설명

 

1. Future

비동기 작업을 표현: Future는 비동기 작업의 완료(성공/실패)를 표현합니다.

메서드:

then: Future가 완료될 때 호출될 콜백을 등록합니다.

catchError: Future가 실패할 때 호출될 콜백을 등록합니다.

whenComplete: Future가 완료되면 성공 여부와 상관없이 호출될 콜백을 등록합니다.

2. async

함수를 비동기로 표시: 함수에 async를 붙이면 해당 함수는 Future를 반환합니다.

비동기 코드 작성: async 키워드를 사용하면 코드가 비동기로 실행되며, 함수는 즉시 Future를 반환합니다.

3. await

Future가 완료될 때까지 기다림: await 키워드를 사용하면 Future가 완료될 때까지 기다리고, 완료 후 결과를 반환합니다.

try-catch와 함께 사용: 비동기 작업에서 발생하는 오류를 처리하기 위해 try-catch 블록과 함께 사용할 수 있습니다.

 

 

참고

https://dart-ko.dev/codelabs/async-await

반응형
반응형

 

Firestore를 사용하여 To-Do 앱을 만드는 방법을 설명하겠습니다. 이 예제에서는 Firebase Firestore를 사용하여 할 일 데이터를 저장하고, 읽고, 업데이트하고, 삭제하는 기능을 구현합니다.

 

1. firebase 내 데이터베이스 만들기

위와 같이 firestore 를 생성해줍니다. 

 

1. 프로젝트 설정

 

pubspec.yaml 파일 업데이트

 

최신 Firebase 패키지를 pubspec.yaml 파일에 추가합니다.

dependencies:
  flutter:
    sdk: flutter
  firebase_core: 
  firebase_auth: 
  cloud_firestore:

2. Firebase 설정

 

google-services.json 파일을 다운로드하여 android/app 디렉토리에 복사합니다. Firebase 콘솔에서 프로젝트를 설정하고 앱을 등록한 후 이 파일을 받을 수 있습니다.

 

android/build.gradle 파일 업데이트

buildscript {
    repositories {
        google()
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:8.0.0'
        classpath 'com.google.gms:google-services:4.4.2'
    }
}

allprojects {
    repositories {
        google()
        mavenCentral()
    }
}

android/app/build.gradle 파일 업데이트

apply plugin: 'com.android.application'
apply plugin: 'com.google.gms.google-services'

android {
    compileSdkVersion 34

    defaultConfig {
        applicationId "com.example.flutter_todo"
        minSdkVersion 21
        targetSdkVersion 34
        versionCode 1
        versionName "1.0"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation platform('com.google.firebase:firebase-bom:32.2.2')
}

apply plugin: 'com.google.gms.google-services'

 

3. Firebase 초기화

 

main.dart

import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'screens/todo_list_screen.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Firebase Todo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: TodoListScreen(),
    );
  }
}

 

4. Firestore 데이터 모델

 

models/todo.dart

class Todo {
  final String id;
  final String title;
  final bool isDone;

  Todo({
    required this.id,
    required this.title,
    this.isDone = false,
  });

  factory Todo.fromMap(Map<String, dynamic> data, String documentId) {
    return Todo(
      id: documentId,
      title: data['title'] ?? '',
      isDone: data['isDone'] ?? false,
    );
  }

  Map<String, dynamic> toMap() {
    return {
      'title': title,
      'isDone': isDone,
    };
  }
}

5. Firestore 서비스

 

services/firestore_service.dart

import 'package:cloud_firestore/cloud_firestore.dart';
import '../models/todo.dart';

class FirestoreService {
  final FirebaseFirestore _db = FirebaseFirestore.instance;

  Stream<List<Todo>> getTodos() {
    return _db.collection('todos').snapshots().map((snapshot) =>
        snapshot.docs.map((doc) => Todo.fromMap(doc.data(), doc.id)).toList());
  }

  Future<void> addTodo(Todo todo) {
    return _db.collection('todos').add(todo.toMap());
  }

  Future<void> updateTodo(Todo todo) {
    return _db.collection('todos').doc(todo.id).update(todo.toMap());
  }

  Future<void> deleteTodo(String id) {
    return _db.collection('todos').doc(id).delete();
  }
}

6. 프로바이더 설정

 

providers/todo_provider.dart

import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import '../models/todo.dart';
import '../services/firestore_service.dart';

class TodoProvider with ChangeNotifier {
  final FirestoreService _firestoreService = FirestoreService();

  late Stream<List<Todo>> _todos;
  Stream<List<Todo>> get todos => _todos;

  TodoProvider() {
    _todos = _firestoreService.getTodos();
  }

  Future<void> addTodo(Todo todo) async {
    await _firestoreService.addTodo(todo);
  }

  Future<void> updateTodo(Todo todo) async {
    await _firestoreService.updateTodo(todo);
  }

  Future<void> deleteTodo(String id) async {
    await _firestoreService.deleteTodo(id);
  }
}

7. UI 작성

 

screens/todo_list_screen.dart

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../models/todo.dart';
import '../providers/todo_provider.dart';
import 'edit_todo_screen.dart';

class TodoListScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('To-Do List'),
      ),
      body: StreamBuilder<List<Todo>>(
        stream: Provider.of<TodoProvider>(context).todos,
        builder: (context, snapshot) {
          if (!snapshot.hasData) {
            return Center(child: CircularProgressIndicator());
          }
          final todos = snapshot.data!;
          return ListView.builder(
            itemCount: todos.length,
            itemBuilder: (ctx, i) => ListTile(
              title: Text(todos[i].title),
              trailing: Checkbox(
                value: todos[i].isDone,
                onChanged: (value) {
                  Provider.of<TodoProvider>(context, listen: false).updateTodo(
                    Todo(
                      id: todos[i].id,
                      title: todos[i].title,
                      isDone: value!,
                    ),
                  );
                },
              ),
              onLongPress: () {
                Provider.of<TodoProvider>(context, listen: false)
                    .deleteTodo(todos[i].id);
              },
              onTap: () {
                Navigator.of(context).push(
                  MaterialPageRoute(
                    builder: (context) => EditTodoScreen(todo: todos[i]),
                  ),
                );
              },
            ),
          );
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          showDialog(
            context: context,
            builder: (ctx) {
              TextEditingController controller = TextEditingController();
              return AlertDialog(
                title: Text('Add Todo'),
                content: TextField(
                  controller: controller,
                  decoration: InputDecoration(labelText: 'Title'),
                ),
                actions: [
                  TextButton(
                    onPressed: () {
                      if (controller.text.isNotEmpty) {
                        final newTodo = Todo(
                          id: '',
                          title: controller.text,
                        );
                        Provider.of<TodoProvider>(context, listen: false)
                            .addTodo(newTodo);
                        Navigator.of(ctx).pop();
                      }
                    },
                    child: Text('Add'),
                  ),
                ],
              );
            },
          );
        },
        child: Icon(Icons.add),
      ),
    );
  }
}

screens/edit_todo_screen.dart

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../models/todo.dart';
import '../providers/todo_provider.dart';

class EditTodoScreen extends StatelessWidget {
  final Todo todo;

  EditTodoScreen({required this.todo});

  @override
  Widget build(BuildContext context) {
    TextEditingController titleController = TextEditingController(text: todo.title);

    return Scaffold(
      appBar: AppBar(
        title: Text('Edit Todo'),
      ),
      body: Padding(
        padding: EdgeInsets.all(16.0),
        child: Column(
          children: [
            TextField(
              controller: titleController,
              decoration: InputDecoration(labelText: 'Title'),
            ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: () {
                if (titleController.text.isNotEmpty) {
                  final updatedTodo = Todo(
                    id: todo.id,
                    title: titleController.text,
                    isDone: todo.isDone,
                  );
                  Provider.of<TodoProvider>(context, listen: false)
                      .updateTodo(updatedTodo);
                  Navigator.of(context).pop();
                }
              },
              child: Text('Update'),
            ),
          ],
        ),
      ),
    );
  }
}

요약

 

1. Firestore 데이터 모델(Todo)을 정의합니다.

2. Firestore 서비스(FirestoreService)를 작성하여 Firestore와 상호작용합니다.

3. 프로바이더(TodoProvider)를 작성하여 상태 관리를 합니다.

4. UI를 작성하여 할 일 목록을 표시하고 추가, 수정, 삭제 기능을 구현합니다.

5. Firebase를 초기화하고 앱을 실행합니다.

 

이제 Firestore를 사용하여 할 일 데이터를 관리하는 간단한 To-Do 앱이 완성되었습니다. 필요에 따라 추가 기능을 구현하거나 UI를 개선할 수 있습니다.

반응형

+ Recent posts