만족은 하되 안주하지는 말자

기록해야 기억한다

프로그래밍/frontend

[vue] 스스로 만들어보는 To Do List - 1

D36choi 2021. 10. 31. 17:15
728x90

vue.js 강의를 들었으니 스스로 만들어보자

우리 팀에서 사용하는 프론트엔드 프레임워크는 vue.js 다. 이 프레임워크를 이용해 모바일웹과 앱 뷰어를 통해, 우리 팀이 관리중인 영역에서의 사용자가 보는 화면을 만들고 있다.
나 또한, 이따금씩 들어오는 프로젝트들로 우리 팀의 vue 앱을 수정하거나 이해해야하는 일들이 생기고 있지만 vue를 제대로 다뤄본적 없는 내 입장에서 쉽지는 않았다. 하지만 조금만 시간을 들여서 이해한다면 충분히 그 데이터의 흐름이나 문법을 이해할 수 있을 정도로 vue는 백엔드 개발자에게 친절한 프레임워크였다.

vue is easy to learn (https://medium.com/pixance-studios/comparison-between-react-angular-and-vue-be488478cbfd)

그래도, 내가 직접 처음부터 시행착오를 겪어보는 것과, 이미 많은 개발자선배들이 구축해놓은 애플리케이션의 일부분을 건드리며 수정하는 것에는 큰 차이가 있다고 생각했다. 그래서 인프런강의도 듣고 책도 보면서 어느정도 vue에 필요한 명령어나 구조를 익혔다.

이론을 학습했으니 이제 스스로 만들어보자. 그래야 개발자니까~!

만드려는 심플 프로젝트는 To-Do List

가장 간단하면서, 즐겁게 입문해볼 수 있는 프로젝트는 todo list 가 아닌가 싶다. 간단한 데이터바인딩과 동작함수만으로 하나의 완성된 기능을 가진 앱을 만들 수 있다. 이 글은 vue의 기본 문법들인 v-지시자들을 구체적으로 설명하지 않는다.

UI 구상

UI 컴포넌트 구상

간단한 앱이니만큼 간단한 컴포넌트들로 화면을 구성하도록 할 계획이다. 최상단 Title.vue로 이 앱이 뭔지를 제목으로 알려주고

ToDoInput으로 텍스트 입력을 통해 간단한 todo를 추가할 수 있도록 한다.

하단 넓은 영역을 할일목록으로 두어 자동으로 추가될 수 있도록 한다.

프로젝트 세팅

npm, vue 설치등은 생략.

1. 내가 만들고자 하는 경로에서 vue create <app이름>
2. default3 (vue3+babel) 선택
3. component/HelloWorld.vue 내용을 지우고 title.vue, ToDoInput.vue, ToDoList.vue를 추가한다

디렉토리 상황과 App.vue

// components/Title.vue
<template>
  <div id="title">
    <h1>{{ name }}</h1>
  </div>
</template>

<script>
export default {
  name: 'Title',
  props: {
    name: String
  }
}
</script>

<style></style>

vue의 props를 활용해보기 위해 최상위 컴포넌트인 App.vue에서 msg 속성을 전달하도록 한다.

이로서 Title.vue의 h1 tag에는 name 문자열이 출력될 것이다.

npm run serve 명령어를 통해 확인해보자

localhost:8080 에 접속시 나오는 화면

4. 나머지를 App.vue의 컴포넌트로써 추가하자

// App.vue
<template>
  <img alt="Vue logo" src="./assets/logo.png" />
  <Title name="TO-DO" />
  <to-do-input />
  <to-do-list />
</template>

<script>
import Title from './components/Title.vue'
import ToDoInput from './components/ToDoInput.vue'
import ToDoList from './components/ToDoList.vue'

export default {
  name: 'App',
  components: {
    Title,
    ToDoInput,
    ToDoList
  },
  data() {
    return {
      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>
// components/ToDoInput.vue
<template>
  <div id="todoinput">
    <input type="text" />
    <button type="submit">제출</button>
  </div>
</template>

<script>
export default {}
</script>

<style></style>
// components/ToDoList.vue
<template><div id="todolist"></div></template>

<script>
export default {}
</script>

<style></style>

todo 추가 버튼과 인풋창이 추가된다. 할일 목록은 아예 기능 구현을 하지않았고.

5. 데이터의 흐름을 고민해보자

할일목록 데이터를 어떻게 관리하나

많은 데이터와 기능이 필요없으므로 위와같이 하면될 것 같다. 부모컴포넌트인 App.vue에서 할일목록 데이터를 가지고 있고

하위 컴포넌트인 TodoInput은 입력된 데이터를 todos에 추가될 수 있도록 전달해주고 ToDoList에서는 데이터의 변화를 감지해 리스트가 자동으로 추가되거나 제거될 수 있게 한다.

그런데 이론적으로 이게 올바른 방식인지는 잘 모르겠다. 누군가 짚어주면 고마울 것 같은데...

6. ToDoList.vue의 기본 구조를 세팅하자

//ToDoList.vue
<template>
  <div id="todolist">
    <div id="container" v-for="data in todos" :key="data.id">
      <li id="text">{{ data }}</li>
    </div>
  </div>
</template>

<script>
export default {
  props: {
    todos: []
  }
}
</script>

<style></style>

v-for 를 이용해 부모 컴포넌트로부터 전달받은 todos 리스트를 출력한다.

활용된 개념은 props, v-for, v-bind:key 등인데 v-bind의 생략형으로 ':' 를 사용가능하다.

key에 대한 이상적인 값은 각 항목을 식별할 수 있는 고유한 ID 이다.

App.vue의 to-do-list 컴포넌트에 아래와 같이 todos를 전달할 수 있도록 바꾼다.

<to-do-list :todos="todos" />

그리고 테스트할 겸, App.vue의 todos 데이터에 디폴트로 값을 하나 넣어본다.

  data() {
    return {
      todos: ['발 닦기']
    }
  }

어설프지만 값이 추가된 걸 볼 수 있다.

7. 할일을 추가하는 기능을 만들어보자

 

https://v3.ko.vuejs.org/guide/migration/emits-option.html#_3-x-%E1%84%83%E1%85%A9%E1%86%BC%E1%84%8C%E1%85%A1%E1%86%A8

 

emits 옵션 | Vue.js

emits 옵션 new 개요 Vue 3은 이제 기존 props옵션과 유사한 emits옵션을 제공합니다. 이 옵션은 컴포넌트가 부모에게 emit할 수 있는 이벤트를 정의하는데 사용할 수 있습니다. 2.x 동작 Vue 2에서는 컴포

v3.ko.vuejs.org

이번에는 '자식컴포넌트의 데이터와 이벤트를 부모컴포넌트에게 전달' 하는 방법을 이해하기 위함이다.

$emit을 이용해 자식컴포넌트의 이벤트와 데이터를 부모컴포넌트에게 전달한다

// components/ToDoInput.vue
<template>
  <div id="todoinput">
    <input type="text" v-model="content" />
    <button type="button" @click="addToDo">제출</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      content: ''
    }
  },
  methods: {
    addToDo() {
      // emit: 자식컴포넌트에서 부모컴포넌트의 특정 이벤트를 발생시키는 기능
      // 1st 파라미터는 지정된 이벤트의 이름
      // 2nd 파라미터는 전달하고자하는 데이터의 이름 명시
      alert('등록하였습니다')
      this.$emit('add-to-do', this.content)
      this.content = ''
    }
  }
}
</script>

<style></style>

입력값의 길이제한과 같은 validation은 생략, v-model을 통해 할일내용을 content에 바인딩하고, button을 클릭시에 addToDo() 메서드가 실행된다. $emit을 통해 부모컴포넌트에 'add-to-do'라는 이름의 이벤트를 전달한다.

주석처럼 두번째 파라미터부터는 전달할 데이터를 기입한다. 이로써 부모컴포넌트는 자식컴포넌트의 data인 content를 받아갈 수 있게 된다.

// App.vue
<template>
  <img alt="Vue logo" src="./assets/logo.png" />
  <Title name="TO-DO" />
  <!-- @<customEvent 이름>="실행되는 method이름" -->
  <to-do-input @add-to-do="addToDo" />
  <to-do-list :todos="todos" />
</template>

<script>
// ...
  data() {
    return {
      todos: ['발 닦기']
    }
  },
  methods: {
    addToDo(content) {
      this.todos.push(content)
    }
  }
}
</script>

<style>
//...
</style>

이제 부모컴포넌트에서는 자식컴포넌트에서 발생하는 add-to-do 이벤트를 받아 addToDo(content) 메서드를 실행할 수 있다. 이 결과로 todos 배열에 content를 추가한다.

그러면 ToDoList에서는 content배열의 변화가 감지될테니 자연스럽게 리스트가 추가될 것으로 예상해볼 수 있다.

결과 확인

입력한 값들을 리스트에 등록할 수 있다

8. 원모어띵. 뭔가 불편하다?

웹 이용이 익숙한 사람들은 텍스트를 입력하고 버튼클릭보다는 엔터키를 먼저 누르는 경향이 있다(뇌피셜).

나 또한 그런데,, 지금 여기서는 그게 안된다. 굳이 마우스로 제출 버튼을 눌러야만 등록이 되어야하는걸까?

지금 이 상태로는 쓰기 어렵다. 할일 목록이라기엔 할일 완료나 삭제기능도 없고, UI도 상당히 조잡하다.

그리고 버튼 클릭보다는 엔터키가 동작하는게 더 효율적일 것 같은데

todo list 구실을 하기 위해서는 갈길이 멀다. 그래도 단순히 강의만 보는거보다 스스로 만들어가다보면 더 기억도 오래남고 재밌다.

// ToDoInput.vue 
<template>
  <div id="todoinput">
    <input type="text" v-model="content" @keyup.enter="addToDo" />
    <button type="button" @click="addToDo">제출</button>
  </div>
</template>

@keyup.enter="addToDo" 가 input 태그에 추가되었다.

위는 v-on:keyup.enter와 같고 엔터키가 떼지는 순간 이벤트가 동작한다.

이런 간단한 명령 하나 추가하는 것만으로 사용자 경험을 더 높일 수 있을 것 같다.

(정작 내가 일할 때는 이런걸 신경 못쓴다... 기능 제대로 구현하는 것만으로도 힘듬;)

좀 더 완성도가 있게끔 발전시켜나가보자.

다음은 할일 달성 / 삭제 및 날짜 기능을 추가해보자.