홍준혁

Vue.js-Do It! 강의 To do List 클론 코딩 본문

Vue.js

Vue.js-Do It! 강의 To do List 클론 코딩

홍준혁 [Hong-JunHyeok] 2020. 9. 24. 10:27
728x90

시작합니다`

 

 

먼저 각 컴포넌트를 만들어서 App.vue에 넣어서 만들었다.

<template>
    <div id="app">
        <TodoHeader></TodoHeader>
        <TodoInput></TodoInput>
        <TodoList></TodoList>
        <TodoFooter></TodoFooter>
    </div>
</template>

<script>
import TodoHeader from "./components/TodoHeader";
import TodoInput from "./components/TodoInput";
import TodoList from "./components/TodoList";
import TodoFooter from "./components/TodoFooter";
export default {
    components: {
        TodoHeader: TodoHeader,
        TodoInput: TodoInput,
        TodoList: TodoList,
        TodoFooter: TodoFooter,
    },
};
</script>

<style></style>

 

이렇게 하면 components에서 만든 Vue 컴포넌트들을 App이라는 곳에 다 넣어줬다.

이렇게 구현을 하고 F12를 눌러 개발자 모드를 들어가 보면 성공적으로 만들어진 것을 볼 수 있다.

 

이제 각 기능들을 정의하고 구현할 것이다.

 

  • TodoHeader : 애플리케이션 이름 표시
  • TodoInput : 할 일 입력 및 추가
  • TodoList : 할 일 표시 및 특정 할 일 삭제 
  • TodoFooter : 할 일 모두 삭제 

대충 이런식으로 구현을 할 것이다.

 

이제 하나하나 스타일 및 기능 구현을 해보도록 해보겠다.

 

App.vue의 style을 조금 꾸며주도록 하자 

<style>
body {
    text-align: center;
    background-color: #f6f6f6;
}
input {
    border-style: groove;
    width: 200px;
}
buton {
    border-style: groove;
}
.shadow {
    box-shadow: 5px 10px 10px rgba(0, 0, 0, 0.93);
}
</style>

 

그다음 TodoHeader를 꾸며보자 

<style scopred>
h1 {
    color: #2f3b52;
    font-weight: 900;
    margin: 2.5rem 0 1.5rem;
}
</style>

 

여기서 style에 scoped속성은 해당 컴포넌트에만 적용하겠다는 의미이다.

 

그렇게 해서 만들어진 웹을 보면

 

이정도 완성이 되었다.

이제 할 일을 입력하는 TodoInput을 꾸미기 및 기능 구현을 해보겠다.

 

TodoInput에

<div>
        <input type="text" v-model="newTodoItem" />
        <button>추가</button>
    </div>

인풋 박스와 버튼을 만들어 놓고 

 

<script>
export default {
    data() {
        return {
            newTodoItem: "",
        };
    },
};
</script>

 

여기서 script를 조금 살펴보도록 하겠다.

 

v-model이 연결되어 있는 input에 값이 입력되면 newTodoItem이 갱신되게 하는 것이다. 

그래서 추가를 누르면 newTodoItem의 값을 출력하도록 코드를 작성해보도록 하겠다.

 

<template>
    <div>
        <input type="text" v-model="newTodoItem" />
        <button v-on:click="addTodo()">추가</button>
    </div>
</template>

<script>
export default {
    data() {
        return {
            newTodoItem: "",
        };
    },
    methods: {
        addTodo() {
            console.log(this.newTodoItem);
        },
    },
};
</script>

<style></style>

 

이렇게 해서 실제 웹사이트에서 추가를 누르고 console창을 보면 엄청난 결과가 나오는 것을 알 수 있다.

 

이런 식으로 나오는 것을 알 수 있다.

뭔가 진행되는것을 알 수 있다...!

 

계속해보겠다.

 

여기서 f5를 누르면 값은 초기화되는데 이제는 로컬 스토리지에 저장하는 방법을 배워보도록 하겠다.

정말 간단히 

localStorage.setItem()을 사용하면 구현할 수 있다.

여기서 이 메서드는 키 : 값의 형태로 값을 받게 되는데 그래서 로직을 작성해보면 

 

<template>
    <div>
        <input type="text" v-model="newTodoItem" />
        <button v-on:click="addTodo()">추가</button>
    </div>
</template>

<script>
export default {
    data() {
        return {
            newTodoItem: "",
        };
    },
    methods: {
        addTodo() {
            localStorage.setItem(this.newTodoItem, this.newTodoItem);
        },
    },
};
</script>

<style></style>

 

이렇게 되는데 여기서 개발자 도구의 local storage를 확인해 보면 

저장이 된 것을 알 수 있다.

 

이 값은 새로고침 해도 그래도 남아 있는 것을 알 수 있다.

 

여기서 addTodo에서 예외를 조금 작성해보도록 하겠다.

  1. 인풋 박스의 입력값이 있을 때만 저장 
  2. 앞뒤의 공백 문자열 제거 
  3. 인풋 박스의 입력 값을 초기화 

로직을 보면 

<template>
    <div>
        <input type="text" v-model="newTodoItem" />
        <button v-on:click="addTodo()">추가</button>
    </div>
</template>

<script>
export default {
    data() {
        return {
            newTodoItem: "",
        };
    },
    methods: {
        addTodo() {
            if (this.newTodoItem !== "") {
                //공백이 아닌 경우
                var value = this.newTodoItem && this.newTodoItem.trim();
                console.log(value);
                localStorage.setItem(this.newTodoItem, this.newTodoItem);
                this.clearInput();
            }
        },
        clearInput() {
            this.newTodoItem = "";
        },
    },
};
</script>

<style></style>

 

이렇게 했는데 공백이 아닌 경우에만 localStorage에 저장하는 로직을 작성했다.

 

이제 멋진 css코드를 작성해 보도록 하겠다.

조금 책이랑 다르게 꾸며보았다.

이제 저장하는 법을 배웠으니 할 일들을 리스트에 저장시키는 기능을 구현해 보도록 하겠다.

 

html에서 리스트 아이템을 표현하는 데 사용하는 것은 리스트이다.

리스트를 사용해서 먼저 프로토타입으로 html에 추가를 해보겠다.

 

 

이런 식으로 된다

<template>
    <div>
        <ul>
            <li>할일 1</li>
            <li>할일 2</li>
            <li>할일 3</li>
        </ul>
    </div>
</template>

<script>
export default {};
</script>

<style></style>

 

그럼 이제 input에서 입력했을 때 리스트를 추가하는 기능을 구현해보도록 하겠다.

 

리스트를 표시하기 위해서는 두 가지의 방법을 사용해야 하는데

 

로컬 스토리지 데이터를 뷰 데이터에 저장 -> 뷰 데이터의 아이템 개수만큼 리스트 아이템 표시 

 

이런 식으로 작성을 하면 된다.(물론 다른 방법도 있다.)

 

로직을 먼저 짜고 설명을 해보겠다.

<template>
    <section>
        <ul>
            <li v-for="(item, key) in todoItems" :key="key">
                {{ item }}
            </li>
        </ul>
    </section>
</template>

<script>
export default {
    data() {
        return {
            todoItems: [],
        };
    },
    created() {
        if (localStorage.length > 0) {
            for (var i = 0; i < localStorage.length; i++) {
                this.todoItems.push(localStorage.key(i));
            }
        }
    },
};
</script>

<style></style>

 

이렇게 구현을 했는데 음... 사실 이 코드는 굉장히 비효율적이다.

입력을 하면 바로 뜨는 것이 아니라 입력을 하고 새로고침을 해야 리스트에 뜨는 형식이라서 추가된 할 일을 확인하려면 브라우저를 새로고침 해야 하는데 이는 다음에 구현해보도록 하고 할 일 삭제 기능을 추가해보도록 하겠다.

 

<template>
    <section>
        <ul>
            <li v-for="(todoItem, index) in todoItems" :key="index">
                <span class="check_button">✔</span>
                {{ todoItem }}
                <span
                    class="remove_button"
                    type="button"
                    @click="removeTodo(todoItem, index)"
                >
                    <span aria-hidden="true">🗑</span>
                </span>
            </li>
        </ul>
    </section>
</template>

<script>
export default {
    data() {
        return {
            todoItems: [],
        };
    },
    created() {
        if (localStorage.length > 0) {
            for (var i = 0; i < localStorage.length; i++) {
                if (localStorage.key(i) != "loglevel:webpack-dev-server")
                    this.todoItems.push(localStorage.key(i));
            }
        }
    },
    methods: {
        removeTodo(todoItem, index) {
            localStorage.removeItem(todoItem);
            this.todoItems.splice(index, 1);
        },
    },
};
</script>

<style scoped>
ul {
    list-style: none;
    padding-left: 0px;
    margin-top: 0px;
    text-align: center;
}

li {
    display: flex;
    min-height: 50px;
    height: 50px;
    line-height: 50px;
    margin: 0.5rem 0;
    padding: 0 0.9rem;
    background: white;
    border-radius: 5px;
}
</style>

 

 

다음 코드처럼 작성을 하면 성공적으로 값이 지워지는 것을 알 수 있다.

removeTodo에서 해당 값을 삭제하고 자동으로 값이 경신되면서 반영되는 것을 볼 수 있는데 ,

이는 데이터의 속성이 변하면 즉시 반영하는 뷰의 반응성 때문이다.

 

이제 모든 값을 삭제하는 clear All을 구현해보도로 하겠다.

<template>
    <div class="clearAllContainer">
        <span class="clearAllBtn" @click="clearTodo()">
            Clear ALl
        </span>
    </div>
</template>

<script>
export default {
    methods: {
        clearTodo() {
            localStorage.clear();
        },
    },
};
</script>

<style>
.clearAllContainer {
    width: 8.5rem;
    height: 50px;
    line-height: 50px;
    background-color: white;
    border-radius: 5px;
    margin: 0 auto;
}
.clearAllBtn {
    color: #e20303;
    display: black;
}
</style>

 

하지만 이렇게 작성을 하면 즉결 삭제가 되지 않는다.

 

우리가 원하는 것은 바로바로 데이터가 반영되었으면 하는 것인데 그것을 구현하기 위해서 어떻게 해야 하는지 

알아보도록 하겠다.

 

바로바로 구현이 안됐었던 이유는 다른 컴포넌트에서 일어난 이벤트이기 때문에 데이터 변동을 감지하지 못하고 바로 변화가 일어나지 않았던 것이다.

정말 단순하고 간단하게 생각하면 컴포넌트를 분리시키지 않고 한 컴포넌트 안에서 저장, 조회, 삭제를 모두 구현하는 것인데, 프로젝트가 작은 경우에는 이 방법이 단순하고 좋겠지만 프로젝트가 커질수록 힘드니까 컴포넌트별로 기능 구현을 다르게 하는 연습을 해놓는 것이 좋다.

 

그리고 컴포넌트가 새부히 나누어져 있을수록 유지보수가 좋아지는것이 사실이다. 

 

기존에 데이터 추가.삭제를 로컬 스토리지에 다이렉트로 연결해서 했다면 이번에는 최상위 루트에 todoItems를 정의하고 하위 컴포넌트에 전달하는 방식을 사용할것이다.

 

여기까지 하도록 하겠다...

나머지 구현은 <<Do it! Vue.js입문>> 에 있으니까 참고하길 바란다.

 

728x90

'Vue.js' 카테고리의 다른 글

Vue.js-뷰 템플릿  (0) 2020.09.22
Vue.js-HTTP 통신  (0) 2020.09.22
Vue.js-네임드 뷰  (0) 2020.09.22
Vue.js-네스티드 라우터  (0) 2020.09.21
Vue.js-Router(뷰 라우터)  (0) 2020.09.21
Comments