이 글은 다음 영상의 튜토리얼을 글로 풀어쓴 내용을 담고 있습니다: Intro to Vue.js: Build a Todo App

 

 

 

1. 앞선 글.

 

 

이 글에선 위의 링크에서 안내되는 튜토리얼을 따라 해 볼 예정입니다. 이 영상에서 소개하는 내용은 가장 기초적인 내용으로 Vue JS의 기능을 이용해 Todo list를 관리하는 앱을 만드는 것입니다.

 

소개된 튜토리얼의 난이도는 초심자 급으로 VueJS를 갓 시작한 개발자를 위한 튜토리얼입니다. 영상의 길이는 약 19분으로 짧은 편이므로 영상을 직접 보시는 것을 추천드립니다.

 

또한 모든 소스코드는 다음 경로에서 확인할 수 있습니다: CodingGarden/vue-todo

 

CodingGarden/vue-todo

Intro to Vue.js: Build a Todo App. Contribute to CodingGarden/vue-todo development by creating an account on GitHub.

github.com

 

 

 

2. 목표 알아보기.

 

이 튜토리얼은 다음과 같은 순서로 진행됩니다. 

 

  • Index.html 생성.
  • Index.html에 Vue.js 추가하기.
  • app.js에 뷰 인스턴스 생성하기.
  • 페이지에 뷰 인스턴스로부터 메시지 표시하기.
  • 새로운 Todo 폼 생성하기.
  • 폼이 제출되었을 때 함수를 호출하기.
  • 유저가 입력한 값을 저장하기 위한 문자열 속성 생성하기.
  • Todo를 저장할 배열 속성 생성하기.
  • Todo항목을 Todos 배열에 넣고 완료 상태를 false로 설정하기.
  • Todos의 값을 화면에 보여주기.
  • Todo 완료 체크박스를 만들기.
  • Todo 완료 체크박스가 체크된 경우 Todo에 취소선을 추가하기.
  • Todo를 삭제하는 버튼을 추가하기.
  • 모든 Todo를 완료하는 버튼을 추가하기.

 

 

 

3. 뷰 프로젝트 생성.

 

뷰 프로젝트를 생성하는 방법은 여러 가지가 있습니다. 이 글에선 index.html이라는 정적 파일을 생성 한 뒤 vue.js를 script에 추가한 뒤 app.js에서 뷰 인스턴스를 생성해 사용하는 방법에 대해 알아보도록 합니다.

 

다음 명령어로 프로젝트 폴더를 만들고 index.html 파일을 생성해 주세요.

 

mkdir todo-app
cd todo-app
touch index.html

 

그리고 생성한 index.html을 다음과 같이 코딩해 주세요.

 

<!DOCTYPE html>
<html lang="en" dir="ltr">
    <head>
        <meta charset="utf-8">
        <title></title>
    </head>
    <body>
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    </body>
</html>

 

index.html을 생성하고 vue.js를 추가했습니다. 지금 우리는 vue.js의 개발 버전을 추가한 상황입니다. 추가할 vuejs에 대해서는 공식 홈페이지를 참고해 주세요: VueJS의 설치방법 - 직접 script에 추가.

 

 

 

4. 뷰 인스턴스 생성.

 

이제 뷰 인스턴스를 생성할 준비가 되었습니다. app.js 파일을 생성하고 다음 코드처럼 뷰 인스턴스를 생성해 보도록 합시다.

 

// app.js
const app = new Vue({
    el: '#app',
    data: {},
    methods: {},
});

 

위의 코드는 뷰 인스턴스를 생성해주는 코드입니다. el에 id를 정의해 주었으며 이 id를 가진 엘리먼트에 해당 뷰 인스턴스가 적용됩니다. 그러면 이제 app이라는 id를 가지는 엘리먼트를 index.html에 추가해 줍시다.

 

<!DOCTYPE html>
<html lang="en" dir="ltr">
    <head>
        <meta charset="utf-8">
        <title></title>
    </head>
    <body>
        <main id="app">
        </main>
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
        <script src="app.js"></script>
    </body>
</html>

 

main에 id를 app으로 지정해 주었으며 app.js에 대한 참조도 추가했습니다. 우리의 뷰가 제대로 동작하는지 한번 확인해 봅시다. app.js를 다음과 같이 수정해 주세요.

 

// app.js
const app = new Vue({
    el: '#app',
    data: {
        title: 'Hello VueJS',
    },
    methods: {},
});

 

data 객체에 title이란 문자열을 추가했습니다. 정상적으로 뷰 인스턴스를 생성했다면 html에서 해당 데이터를 가져다 사용할 수 있습니다. 이때 뷰 인스턴스에서 가져온 데이터라는 것을 표시하기 위해 중괄호 두 개를 사용합니다. {{title}}과 같이요. 다음과 같이 index.html을 수정 해 데이터를 가져와 표시해 보도록 합시다.

 

<!-- index.html -->
<!DOCTYPE html>
<html lang="en" dir="ltr">
    <head>
        <meta charset="utf-8">
        <title></title>
    </head>
    <body>
        <main id="app">
            <h3>{{title}}</h3>
        </main>
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
        <script src="app.js"></script>
    </body>
</html>

 

그리고 index.html 파일을 열면 정상적으로 title에 입력한 문자열을 출력하는 것을 확인할 수 있습니다.

 

 

 

5. Todo를 위한 폼 생성하기.

 

이제 본격적으로 TodoList를 위한 작업을 시작하도록 합시다. 가장 먼저 Todo 작업을 입력받을 텍스트 박스와 제출 버튼을 추가해 보도록 합시다. index.html을 다음과 같이 수정해 주세요.

 

<!-- index.html -->
<!DOCTYPE html>
<html lang="en" dir="ltr">
    <head>
        <meta charset="utf-8">
        <title></title>
        <link rel="stylesheet" href="https://ez-css.now.sh">
    </head>
    <body>
        <main id="app">
            <h3>{{title}}</h3>
            <form>
                <label for="newTodo">New Todo</label>
                <input type="text" name="newTodo" value="">
                <button type="submit" name="button">Add</button>
            </form>
        </main>
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
        <script src="app.js"></script>
    </body>
</html>

 

기본적인 스타일 시트를 제공해주는 사이트를 추가했습니다. 또한 h3아래에 form을 추가했습니다. 이제 제출 버튼이 클릭되었을 때 호출되는 함수를 정의해 주도록 합시다. app.js를 다음과 같이 수정해 주세요.

 

// app.js
const app = new Vue({
    el: '#app',
    data: {
        title: 'Hello VueJS',
    },
    methods: {
        addTodo: function(){
            console.log(`Todo is submitted.`)
        },
    },
});

 

앞으로 뷰에서 사용할 함수는 methods 객체에 추가하면 사용할 수 있습니다. addTodo함수를 추가했으며 해당 함수가 호출되면 콘솔 창에 메시지를 출력하도록 했습니다. 이제 이 함수를 폼에 바인드 해보도록 합시다. index.js의 폼을 다음과 같이 수정해 주세요.

 

<!-- index.html -->
...
            <form @submit.prevent="addTodo">
                <label for="newTodo">New Todo</label>
                <input type="text" name="newTodo" value="">
                <button type="submit" name="button">Add</button>
            </form>
...

 

@를 이용해 이벤트를 바인드 할 수 있습니다. .prevent는 제출 이벤트가 페이지를 다시 로드하는 것을 방지해줍니다. 폼이 제출되면 addTodo가 호출되고 페이지가 다시 로드되지 않도록 작성되었습니다. 페이지로 이동해 폼을 제출해 보세요.

 

 

우리가 작성한 메시지가 정상적으로 보입니다.

 

 

 

6. Todo 저장하기.

 

이제 함수가 호출되는 것을 확인했으니 Todo를 추가해 보도록 하겠습니다. app.js를 다음과 같이 수정해 주세요.

 

// app.js
const app = new Vue({
    el: '#app',
    data: {
        title: 'Hello VueJS',
        newTodo: '',
        todos: [],
    },
    methods: {
        addTodo: function(){
            this.todos.push({
                title: this.newTodo,
                done: false,
            });
            this.newTodo = '';
        },
    },
});

 

data에 newTodo와 todos가 추가되었습니다. newTodo는 사용자가 입력할 새 작업입니다. 텍스트 박스에 입력될 것입니다. todos는 현재 사용자가 추가한 todo의 배열입니다. 사용자가 todo를 추가하면 이 배열에 저장됩니다.

 

methods에 addTodo도 같이 수정되었습니다. 해당 인스턴스의 todos에 새로운 객체를 생성해 push 하게 됩니다. 해당 객체에는 title과 done을 가지며 title에 사용자가 입력한 작업이 저장되며 done은 해당 작업이 완료되었는지에 대한 플래그가 저장됩니다. 새로운 todo가 추가되면 기존 텍스트 박스의 값을 비워주는 로직도 추가하였습니다. 

 

이제 우리가 구현한 기능이 적용될 수 있도록 index.html을 수정해 줍시다.

 

<!-- index.html -->
<!DOCTYPE html>
<html lang="en" dir="ltr">
    <head>
        <meta charset="utf-8">
        <title></title>
        <link rel="stylesheet" href="https://ez-css.now.sh">
    </head>
    <body>
        <main id="app">
            <h3>{{title}}</h3>
            <form @submit.prevent="addTodo">
                <label for="newTodo">New Todo</label>
                <input v-model="newTodo" type="text" name="newTodo" value="">
                <button type="submit" name="button">Add</button>
            </form>
            <ul>
                <li v-for="todo in todos">
                    <input type="checkbox" v-model="todo.done">
                    <span>{{todo.title}}</span>
                </li>
            </ul>
        </main>
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
        <script src="app.js"></script>
    </body>
</html>

 

form 내부의 input에 v-model이 추가된 것을 확인할 수 있습니다. 이 명령어를 통해 우린 이 input의 값을 newTodo에 바인딩 할 수 있습니다.

 

아래의 li에 보면 v-for이 추가되어 있습니다. 이는 C#의 foreach와 매우 유사하게 동작합니다. todo in todos에서 우리는 해당 루프 문을 수행할 때 뷰 인스턴스의 todos에 들어 있는 내용을 토대로 루프문을 실행합니다. 즉 todos의 위치에 있는 내용은 뷰 인스턴스에 동일하게 정의가 되어 있어야 함을 의미합니다. todo는 우리가 html내에서 정의하면 됩니다. 이 값을 내부에서 사용하도록 합니다.

 

내부의 체크박스는 todo의 done 속성을 반영하고 있으며 span에선 유저가 입력한 newTodo(todos에 저장되면 title) 값을 보여줍니다.

 

 

아름답진 않지만 정상적으로 보이고 있습니다.

 

 

 

7. 완료 및 삭제 처리 하기.

 

이제 체크박스를 선택하면 타이틀에 취소선이 추가되는 기능과 Todo를 삭제하는 기능, 현재 추가된 모든 Todo를 완료하는 기능을 추가해 보도록 하겠습니다.

 

app.js를 열고 todo를 삭제하는 로직을 추가해 주세요.

 

// app.js
const app = new Vue({
...
    methods: {
        addTodo: function(){
            this.todos.push({
                title: this.newTodo,
                done: false,
            });
            this.newTodo = '';
        },
        removeTodo: function(todo) {
            const todoIdx = this.todos.indexOf(todo);
            this.todos.splice(todoIdx, 1);
        },
    },
});

 

로직은 간단합니다. 매개변수로 받아온 todo가 todos에서 어디 있는지 찾아낸 후 그 하나를 없애버리는 것이 전부입니다. 이제 이 함수를 index.html에 반영해 봅시다.

 

<!-- index.html -->
...
                <li v-for="todo in todos">
                    <input type="checkbox" v-model="todo.done">
                    <span>{{todo.title}}</span>
                    <button @click="removeTodo(todo)" type="button">Remove</button>
                </li>
...

 

@click을 추가해 해당 버튼이 클릭되었을 때 removeTodo(todo)가 호출되게 만들었습니다. 이제 페이지를 실행시킨 뒤 새 todo를 입력해보면 다음과 같이 보입니다.

 

 

Todo에 추가된 remove버튼을 클릭하면 해당 Todo가 삭제됩니다. 

 

이제 완료 기능을 만들어 보도록 하겠습니다. 체크박스를 클릭하면 Todo가 완료되었다고 판단하고 유저가 입력한 Todo에 취소선을 추가해 완료가 되었다는 표시를 남기도록 해보겠습니다. 또한 이와 함께 모든 작업을 한 번에 완료하는 버튼도 같이 추가해 보도록 하겠습니다.

 

app.js를 다음과 같이 수정해 주세요. 이제 마지막 코드 수정입니다!

 

// app.js
const app = new Vue({
    el: '#app',
    data: {
        title: 'Hello VueJS',
        newTodo: '',
        todos: [],
    },
    methods: {
        addTodo: function(){
            this.todos.push({
                title: this.newTodo,
                done: false,
            });
            this.newTodo = '';
        },
        removeTodo: function(todo) {
            const todoIdx = this.todos.indexOf(todo);
            this.todos.splice(todoIdx, 1);
        },
        allDone: function(){
            this.todos.forEach((todo) => {
                todo.done = true;
            });
        },
    },
});

 

마지막으로 allDone 함수를 추가했습니다. 모든 todos를 순회하면서 done값을 true로 변경합니다.

 

이제 index.html을 수정합시다.

 

<!-- index.html -->
<!DOCTYPE html>
<html lang="en" dir="ltr">
    <head>
        <meta charset="utf-8">
        <title></title>
        <link rel="stylesheet" href="https://ez-css.now.sh">
        <style>
            .done{
                text-decoration: line-through;
            }
        </style>
    </head>
    <body>
        <main id="app">
            <h3>{{title}}</h3>
            <form @submit.prevent="addTodo">
                <label for="newTodo">New Todo</label>
                <input v-model="newTodo" type="text" name="newTodo" value="">
                <button type="submit" name="button">Add</button>
            </form>
            <button @click="allDone" type="button" name="button">All Done</button>
            <ul>
                <li v-for="todo in todos">
                    <input type="checkbox" v-model="todo.done">
                    <span :class="{done: todo.done}">{{todo.title}}</span>
                    <button @click="removeTodo(todo)" type="button">Remove</button>
                </li>
            </ul>
        </main>
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
        <script src="app.js"></script>
    </body>
</html>

 

style이 추가되었습니다. .done 클래스가 추가된 항목은 취소선을 추가하도록 하였습니다. 

 

새 버튼이 추가되었습니다. 버튼을 클릭하면 앞서 구현한 allDone함수가 호출됩니다.

 

li의 span에 클래스 바인딩이 추가되었습니다. 만약 내부 조건(:이후의 조건)이 true라면 done이라는 클래스가 추가되도록 하는 논리입니다. 즉 todo.done이 true인 경우 해당 span에 done라는 클래스가 추가됩니다.

 

이제 페이지를 열고 한번 테스트해보세요!

 

 

구현한 기능이 모두 정상적으로 동작하는 것을 확인할 수 있습니다!

 

 

 

 

 

반응형

+ Recent posts