๐ŸŽ [Vue TodoList] 3. ์ปดํฌ๋„ŒํŠธ ๋‚ด์šฉ ๊ตฌํ˜„ํ•˜๊ธฐ

sujinยท2021๋…„ 12์›” 28์ผ
0

Projects

๋ชฉ๋ก ๋ณด๊ธฐ
3/4
post-thumbnail

๐Ÿ“Œ์ปดํฌ๋„ŒํŠธ ๋ณ„ ๊ตฌํ˜„ํ•  ๊ธฐ๋Šฅ

   TodoHeader : ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ด๋ฆ„ ํ‘œ์‹œ 
   TodoInput :  ํ•  ์ผ ์ž…๋ ฅ ๋ฐ ์ถ”๊ฐ€
   TodoList :  ํ•  ์ผ ๋ชฉ๋ก ํ‘œ์‹œ ๋ฐ ํŠน์ • ํ•  ์ผ ์‚ญ์ œ
   TodoFooter: ํ•  ์ผ ๋ชจ๋‘ ์‚ญ์ œ

1. TodoHeader ์ปดํฌ๋„ŒํŠธ

<template>
  <header>
      <h1>TODO it!</h1>
  </header>
</template>

<script>
  export default{

  }
</script>

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


2. TodoInput ์ปดํฌ๋„ŒํŠธ

2-1. ์ธํ’‹ ๋ฐ•์Šค์™€ ๋ฒ„ํŠผ ์ถ”๊ฐ€ํ•˜๊ธฐ


<template>
    <div class="inputBox shadow">
       <!-- 
            * v-model  
            - ํผ์—์„œ ์ฃผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ์†์„ฑ, ํผ์— ์ž…๋ ฅํ•œ ๊ฐ’์„ ๋ทฐ ์ธ์Šคํ„ด์Šค์˜ ๋ฐ์ดํ„ฐ์™€ ์ฆ‰์‹œ ๋™๊ธฐํ™” ์‹œ์ผœ์ค€๋‹ค. 
            - ํ™”๋ฉด์— ์ž…๋ ฅ๋œ ๊ฐ’์„ ์ €์žฅํ•˜์—ฌ ์„œ๋ฒ„์— ๋ณด๋‚ด๊ฑฐ๋‚˜ ์ถ”๊ฐ€ ๋กœ์ง์„ ์ˆ˜ํ–‰ ํ•  ์ˆ˜ ์žˆ๋‹ค.
        -->    
        <input type="text" v-model= "newTodoItem">  
        <button>์ถ”๊ฐ€</button>
    </div>
</template>

<script>
    export default{
        data(){
            return{
                newTodoItem: ''
            }
        },
    }
</script>
...

  • ์ธํ’‹๋ฐ•์Šค์— ํ…์ŠคํŠธ๋ฅผ ์ž…๋ ฅํ•˜๋ฉด ๋ทฐ ๊ฐœ๋ฐœ์ž๋„๊ตฌ newTodoItem ๊ฐ’์ด ๊ฐ™์ด ๊ฐฑ์‹ ๋˜๋Š” ๊ฒƒ์„ ํ™•์ธ ํ•  ์ˆ˜ ์žˆ๋‹ค.

2-2. ํ…์ŠคํŠธ๋ฅผ ์ €์žฅํ•˜๊ธฐ ์œ„ํ•œ ๋ฒ„ํŠผ ์ด๋ฒคํŠธ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ธฐ


<template>
    <div class="inputBox shadow">  
        <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>
...

  • ์ถ”๊ฐ€ ๋ฒ„ํŠผ์„ ํด๋ฆญํ–ˆ์„ ๋•Œ console์ฐฝ์— ํ…์ŠคํŠธ ๊ฐ’์ด ํ‘œ์‹œ๋จ์œผ๋กœ, ์ด๋ฒคํŠธ๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ๋™์ž‘ํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

2-3. ์ž…๋ ฅ๋ฐ›์€ ํ…์ŠคํŠธ๋ฅผ ๋กœ์ปฌ ์Šคํ† ๋ฆฌ์ง€์— ์ €์žฅํ•˜๊ธฐ

  • ์ž…๋ ฅ๋ฐ›์€ ํ…์ŠคํŠธ๋ฅผ ๋กœ์ปฌ ์Šคํ† ๋ฆฌ์ง€์˜ setItem() API๋ฅผ ์ด์šฉํ•˜์—ฌ ์ €์žฅํ•œ๋‹ค.
  • setItem()์€ ๋กœ์ปฌ ์Šคํ† ๋ฆฌ์ง€์— ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” API๋กœ, APIํ˜•์‹์€ ํ‚ค, ๊ฐ’ ํ˜•ํƒœ์ด๋‹ค.
  • ๋กœ์ปฌ ์Šคํ† ๋ฆฌ์ง€์— ์ €์žฅ๋œ ๊ฒƒ์€ [๊ฐœ๋ฐœ์ž๋„๊ตฌ] -> [Aplication] -> [Local Storage] -> http://localhost:8080] ์„ ํด๋ฆญํ•˜์—ฌ ํ™•์ธ ํ•  ์ˆ˜ ์žˆ๋‹ค.
<template>
    <div class="inputBox shadow">  
        <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>
...


2-4. addTodo() ์•ˆ์— ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ์ฝ”๋“œ ๋„ฃ๊ธฐ

  • ์ธํ’‹ ๋ฐ•์Šค์— ์ž…๋ ฅ๋œ ํ…์ŠคํŠธ๊ฐ€ ์—†์„ ๊ฒฝ์šฐ ๋กœ์ปฌ ์Šคํ† ๋ฆฌ์ง€์— ๋ฐ์ดํ„ฐ๊ฐ€ ์ €์žฅ๋˜์ง€ ์•Š๋„๋ก ์ฒ˜๋ฆฌํ•ด์ค€๋‹ค.
<template>
    <div class="inputBox shadow">  
        <input type="text" v-model= "newTodoItem">  
        <button v-on:click = "addTodo">์ถ”๊ฐ€</button>
    </div>
</template>

<script>
    export default{
        data(){
            return{
                newTodoItem: ''
            }
        },
        methods: {
            addTodo(){
              // 1. ์ธํ’‹ ๋ฐ•์Šค์˜ ์ž…๋ ฅ ๊ฐ’์ด ์žˆ์„ ๋•Œ๋งŒ ์ €์žฅ 
              if(this.newTodoItem !== ""){
                 // 2. ์ธํ’‹๋ฐ•์Šค์— ์ž…๋ ฅ๋œ ํ…์ŠคํŠธ์˜ ์•ž๋’ค ๊ณต๋ฐฑ ๋ฌธ์ž์—ด ์ œ๊ฑฐ 
                 var value = this.newTodoItem && this.newTodoItem.trim();
                 localStorage.setItem(value, value);
                 //3. ์ธํ’‹ ๋ฐ•์Šค์˜ ์ž…๋ ฅ ๊ฐ’ ์ดˆ๊ธฐํ™”
                 this.clearInput();
              } 
            },
            clearInput() {
              this.newTodoItem = '';
            }
        }
    }
</script>
...

2-5. ๋ฒ„ํŠผ ์Šคํƒ€์ผ ์ ์šฉํ•˜๊ธฐ

<template>
    <div class="inputBox shadow">  
        <input type="text" v-model= "newTodoItem" placeholder="type what your have to do" @keyup.enter= "addTodo">  
        <span class="addContainer" v-on:click = "addTodo">
            <i class="addBtn fa fa-plus"></i>
        </span>
    </div>
</template>

<script>
    export default{
        data(){
            return{
                newTodoItem: ''
            }
        },
        methods: {
            addTodo(){
              // 1. ์ธํ’‹ ๋ฐ•์Šค์˜ ์ž…๋ ฅ ๊ฐ’์ด ์žˆ์„ ๋•Œ๋งŒ ์ €์žฅ 
              if(this.newTodoItem !== ""){
                 // 2. ์ธํ’‹๋ฐ•์Šค์— ์ž…๋ ฅ๋œ ํ…์ŠคํŠธ์˜ ์•ž๋’ค ๊ณต๋ฐฑ ๋ฌธ์ž์—ด ์ œ๊ฑฐ 
                 var value = this.newTodoItem && this.newTodoItem.trim();
                 localStorage.setItem(this.newTodoItem, this.newTodoItem);
                 //3. ์ธํ’‹ ๋ฐ•์Šค์˜ ์ž…๋ ฅ ๊ฐ’ ์ดˆ๊ธฐํ™”
                 this.clearInput();
              } 
            },
            clearInput() {
              this.newTodoItem = '';
            }
        }
    }
</script>
<style>
    input:focus {
        outline: none;
    }
    .inputBox {
        background: #fff;
        height: 50px;
        line-height: 50px;
        border-radius: 5px;
    }
    .inputBox input {
        border-style: none;
        font-size: 0.9rem;
    }
    .addContainer {
        float:right;
        background: linear-gradient(to right, #6478fb, #8763fb);
        display: block;
        width: 3rem;
        border-radius: 0 5px 5px 0;
    }
    .addBtn{
        color: #fff;
        vertical-align: middle;
    }
</style>


3. TodoList ์ปดํฌ๋„ŒํŠธ

3-1. ๋กœ์ปฌ ์Šคํ† ๋ฆฌ์ง€ ๋ฐ์ดํ„ฐ๋ฅผ ๋ทฐ ๋ฐ์ดํ„ฐ์— ์ €์žฅํ•˜๊ธฐ

...
<script>
export default{
  //1. todoItems ๋ฐ์ดํ„ฐ ์†์„ฑ์„ ๋นˆ ๋ฐฐ์—ด๋กœ ์„ ์–ธํ•œ๋‹ค.
  data() {
    return {
      todoItems : [] 
    }
  },
  // 2. created ๋ผ์ดํ”„์‚ฌ์ดํด ํ›…์— for ๋ฐ˜๋ณต๋ฌธ๊ณผ push()๋กœ ๋กœ์ปฌ ์Šคํ† ๋ฆฌ์ง€์˜ ๋ฐ์ดํ„ฐ๋ฅผ todoItems์— ๋‹ด์•„์ค€๋‹ค. 
  // created
  // - ์ธ์Šคํ„ด์Šค๊ฐ€ ํ™”๋ฉด์— ๋ถ€์ฐฉ๋˜๊ธฐ ์ „ ๋‹จ๊ณ„
  // - data์†์„ฑ๊ณผ methods์†์„ฑ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ์ฒซ ๋ผ์ดํ”„ ์‚ฌ์ดํด ๋‹จ๊ณ„
  // - ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ƒ์„ฑ๋˜๊ณ  ๋‚˜์„œ ์‹คํ–‰๋˜๋Š” ๋‹จ๊ณ„๋กœ, ์„œ๋ฒ„์— ๋ฐ์ดํ„ฐ๋ฅผ ์š”์ฒญํ•˜์—ฌ ๋ฐ›์•„์˜ค๋Š” ๋กœ์ง์„ ์ˆ˜ํ–‰ํ•œ๋‹ค. 
  created() {
    if(localStorage.length > 0){
      for(var i = 0, i < localStorage.length; i++){
        this.todoItems.push(localStorage.key(i));
      }
    }
  }
}
</script>
...
  • ๋ทฐ์˜ ์ธ์Šคํ„ด์Šค๊ฐ€ ์ƒ์„ฑ๋˜์ž๋งˆ์ž ๋ทฐ ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก created() ๋ผ์ดํ”„ ์‚ฌ์ดํด์—์„œ ๋กœ์ปฌ ์Šคํ† ๋ฆฌ์ง€์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ทฐ ๋ฐ์ดํ„ฐ๋กœ ์˜ฎ๊ธด๋‹ค.

3-2. ๋ทฐ ๋ฐ์ดํ„ฐ์˜ ์•„์ดํ…œ ๊ฐœ์ˆ˜๋งŒํผ ํ™”๋ฉด์— ํ‘œ์‹œํ•˜๊ธฐ

<template>
    <section>
        <ul>
                <!-- 
                v-for = "์•„์ดํ…œ๋ช… in array"
                : ์ง€์ •ํ•œ ๋ฐ์ดํ„ฐ์˜ ๊ฐฏ์ˆ˜๋งŒํผ html์„ ์ถœ๋ ฅ  
                --> 
            <li v-for= "todoItem in todoItems">{{ todoItem }}</li>
        </ul>
    </section>
</templat>

  • v-for ๋Š” ๋ทฐ ๋ฐ์ดํ„ฐ ์†์„ฑ todoItems์˜ ๋‚ด์šฉ๋ฌผ ๊ฐœ์ˆ˜๋งŒํผ ๋ฐ˜๋ณตํ•ด์„œ <li> ํƒœ๊ทธ๋ฅผ ์ถœ๋ ฅํ•ด ์ค€๋‹ค.
  • todoItems์˜ ํƒ€์ž…์ด ๋ฐฐ์—ด์ด๊ธฐ ๋•Œ๋ฌธ์— ๋ฐฐ์—ด์˜ ์š”์†Œ ์ˆซ์ž๋งŒํผ ๋ฐ˜๋ณตํ•ด์„œ ์œ„์™€ ๊ฐ™์ด ์ถœ๋ ฅํ•œ๋‹ค.

โ— ๋ฌธ์ œ์ : ํ•  ์ผ์„ ์ถ”๊ฐ€ํ•ด๋„ ํ™”๋ฉด์ด ๋ฐ”๋กœ ๊ฐฑ์‹ ๋˜์ง€ ์•Š๊ณ  ๋ธŒ๋ผ์šฐ์ € ์ƒˆ๋กœ๊ณ ์นจ์„ ํ•ด์•ผํ•œ๋‹ค.


3-3. ํ•  ์ผ ๋ชฉ๋ก & ์‚ญ์ œ ๋ฒ„ํŠผ ๋งˆํฌ์—… ์ž‘์—…ํ•˜๊ธฐ

  • ์‚ญ์ œ ๊ธฐ๋Šฅ ๊ตฌํ˜„ ์ „์— ๋จผ์ € ๋งˆํฌ์—… ์ž‘์—…(HTML, CSS)์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.
<template>
    <section>
        <ul>
            <li v-for= v-for= "todoItem in todoItems" :key="todoItem" class="shadow">
                <i class="checkBtn fa fa-check"></i>
                {{ todoItem }}
                <span class="removeBtn" type="button">
                    <i class="fa fa-trash"></i>
                </span>
            </li>
        </ul>
    </section>
</template>
 
 ...

<style>
    ul{
        list-style-type: none;
        padding-left: 0px;
        margin-top: 0;
        text-align: left;
    }
    li{
        display: flex;
        min-height: 50px;
        height: 50px;
        line-height: 50px;
        margin: 0.5rem 0;
        padding: 0 0.9rem;
        background: #fff;
        border-radius: 5px;
    }
    .checkBtn{
        line-height: 45px;
        color: #62acde;
        margin-right:5px;
        cursor: pointer;
    }
    .removeBtn{
        line-height: 45px;
        margin-left: auto;
        color: #de4343;
        cursor: pointer;
    } 
</style>
 

3-4. ํ•  ์ผ ์‚ญ์ œ ๋ฒ„ํŠผ์— ํด๋ฆญ ์ด๋ฒคํŠธ ์ถ”๊ฐ€ํ•˜๊ธฐ

  • ์‚ญ์ œ ๋ฒ„ํŠผ์„ ํด๋ฆญํ–ˆ์„ ๋•Œ, ์‚ญ์ œ ๊ธฐ๋Šฅ์ด ์‹คํ–‰๋˜๋„๋ก <span> ํƒœ๊ทธ์— ํด๋ฆญ ์ด๋ฒคํŠธ๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค.
<template>
    <section>
        <ul>
            <li v-for= "todoItem in todoItems" :key="todoItem" class="shadow">
                <i class="checkBtn fa fa-check"></i>
                {{ todoItem }}
                <span class="removeBtn" type="button" v-on:click = "removeTodo">
                    <i class="fa fa-trash"></i>
                </span>
            </li>
        </ul>
    </section>
</template>

<script>
export default{
  ... 
  
  methods: {
    removeTodo() {
      console.log('clicked');
    }
  }
}
</script>
...


3-5. ์„ ํƒํ•œ ํ•  ์ผ์„ ๋ทฐ์—์„œ ์ธ์‹ํ•˜๋„๋ก ๋งŒ๋“ค๊ธฐ

  • ์‚ญ์ œ ๋ฒ„ํŠผ์„ ํด๋ฆญํ–ˆ์„ ๋•Œ, ์„ ํƒํ•œ ํ•  ์ผ์˜ ํ…์ŠคํŠธ ๊ฐ’๊ณผ ์ธ๋ฐ์Šค๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค.
  • ์—ฌ๊ธฐ์„œ ํ•  ์ผ ๋ชฉ๋ก์˜ ์ธ๋ฑ์Šค๋ฅผ ๋ทฐ์—์„œ ๋‚ด๋ถ€์ ์œผ๋กœ ๊ด€๋ฆฌํ•˜๊ณ  ์žˆ๋‹ค.
  • ํ…์ŠคํŠธ ๊ฐ’๊ณผ ์ธ๋ฑ์Šค(๋ชฉ๋ก์—์„œ ์ˆœ์„œ,๋ฐฐ์—ด ์ธ๋ฑ์Šค์™€ ๋™์ผ)๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
  • index๋Š” ์ž„์˜๋กœ ์ •์˜ํ•œ ๋ณ€์ˆ˜๊ฐ€ ์•„๋‹ˆ๋ผ v-for ๋””๋ ‰ํ‹ฐ๋ธŒ์—์„œ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ œ๊ณตํ•˜๋Š” ๋ณ€์ˆ˜์ด๋‹ค.
  • v-for ๋””๋ ‰ํ‹ฐ๋ธŒ๋กœ ๋ฐ˜๋ณตํ•œ ์š”์†Œ๋Š” ๋ชจ๋‘ ๋ทฐ์—์„œ ๋‚ด๋ถ€์ ์œผ๋กœ ์ธ๋ฑ์Šค๋ฅผ ๋ถ€์—ฌํ•œ๋‹ค.
<template>
    <section>
        <ul>
            <li v-for= "(todoItem, index) in todoItems" :key="todoItem" class="shadow">
                <i class="checkBtn fa fa-check"></i>
                {{ todoItem }}
                <span class="removeBtn" type="button" v-on:click = "removeTodo(todoItem, index)">
                    <i class="fa fa-trash"></i>
                </span>
            </li>
        </ul>
    </section>
</template>

<script>
export default{
  ... 
  
  methods: {
    removeTodo(todoItem, index) {
      console.log(todoItem, index);
    }
  }
}
</script>
...


3-6. ์„ ํƒํ•œ ํ•  ์ผ์„ ๋กœ์ปฌ ์Šคํ† ๋ฆฌ์ง€์™€ ๋ทฐ ๋ฐ์ดํ„ฐ์—์„œ ์‚ญ์ œํ•˜๊ธฐ

<script>
export default{
  ... 
  
  methods: {
    removeTodo(todoItem, index) {
      localStorage.removeItem(todoItem);
      this.todoItems.splice(index, 1); // splice() : ๋ฐฐ์—ด์˜ ํŠน์ • ์ธ๋ฑ์Šค์—์„œ ๋ถ€์—ฌํ•œ ์ˆซ์ž๋งŒํผ์˜ ์ธ๋ฑ์Šค๋ฅผ ์‚ญ์ œํ•œ๋‹ค.
    }
  }
}
</script>

  • removeItem() API๋Š” todoItem ์ธ์ž๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋กœ์ปฌ ์Šคํ† ๋ฆฌ์ง€์—์„œ ํ•  ์ผ ํ…์ŠคํŠธ๋ฅผ ์‚ญ์ œํ•œ๋‹ค.
  • splice() API๋Š” ์ธ์ž๋กœ ๋ฐ›์€ index๋ฅผ ์ด์šฉํ•˜์—ฌ ๋ฐฐ์—ด์˜ ํ•ด๋‹น ์ธ๋ฑ์Šค์—์„œ 1๋งŒํผ ์‚ญ์ œํ•œ๋‹ค.

4. TodoFooter ์ปดํฌ๋„ŒํŠธ

4-1. ๋ชจ๋‘ ์‚ญ์ œํ•˜๊ธฐ ๋ฒ„ํŠผ ์ถ”๊ฐ€ํ•˜๊ธฐ

<template>
    <div class="clearAllContainer">
        <span class="clearAllBtn" v-on: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: #fff;
        border-radius: 5px;
        margin: 40px auto 0;
    }
    .clearAllBtn{
        display: block;
        color: #e20303;
        cursor: pointer;
    }
</style>

โ— ๋ฌธ์ œ์ : Clear All ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด ๋ธŒ๋ผ์šฐ์ €๋ฅผ ์ƒˆ๋กœ ๊ณ ์นจํ•ด์•ผ๋งŒ ๋กœ์ปฌ ์Šคํ† ๋ฆฌ์ง€์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ฐ˜์˜๋œ๋‹ค

profile
๊ฐœ๋ฐœ๋Œ•๋ฐœ

0๊ฐœ์˜ ๋Œ“๊ธ€