[개발의 본질] 투두리스트 JS로 만들기 | 토이 프로젝트 02

2022. 4. 3. 15:04
728x90
반응형

 

 

 

 

01. TODO LIST 

두 번째 토이 프로젝트는 TODO 리스트로 선택하였습니다. 실제로 계산기, 투두 리스트는 개발의 첫 시작 프로젝트라고 말할 수도 있을 만큼 많이들 하는 것 같습니다. 이제야 시작하지만 오히려 좋아 


행맨 프로젝트를 해서 그런지 일단 막막함은 없었습니다. 그런데 제가 참고하려는 투두 프로젝트는 기본이지만 코드를 또다른 방식으로 작성해서 (클래스 형식) 그에 대한 공부를 또 하고 왔습니다. 아직 100% 이해를 한 것은 아니지만 클론 코딩하면서 이 개발자의 로직과 쓰는 방식을 이해하려고 했습니다. 

같은 자바스크립트 언어지만 작성하는 방식은 굉장히 많은 것 같습니다. (멀고도 험난한 자스의 세계) 

 

 

To do list - Plain JavaScript

I've seen alot of different to do list web apps and all used newer JS libraries which is fine but I've been working on bettering my understanding of pl...

codepen.io

 

02. 흐름파악

이번에도 플로우 차트를 기반으로 흐름을 파악하려고 했습니다. 그런데 투두에서는 조건이 들어오게 되는 경우가 있나?를 생각하다가 

만약에 조금 더 복잡하게 들어가서 기간을 사용자가 입력하게 만드는 투두의 경우 > 완료일과 날짜를 비교해서 처리 
하는 방법도 있겠지만, 

현재 참고하는 투두는 복잡한 로직은 아니었습니다. 다만, 간과한 것은 에러 처리에 대한 부분이었는데 
모든 입력 과정을 거칠 때 잘못 입력한다던가 하는 에러에 대한 부분을 항상 생각해 줘야 했습니다. (중요) 

현재 참고하는 투두 리스트를 보면서 짜 본 플로우 차트입니다. 

 

 

 

 

 

반응형

 

03. 코드 파악

TODO LIST를 만들어보면서 공부한 이론들은 이러합니다. 

 

✔ 즉시 실행 함수 
✔ 프로퍼티 상속 
✔ bind() 함수
✔ eval() 함수 -?? 

 

 

HTML

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>TODO</title>

    <link rel="stylesheet" href="/todoProject/main.css">


</head>
<body>
    <div id="tasker" class="tasker">
        <div id="error" class="error">Please enter a task</div>
        <div id="tasker-header" class="tasker-header">
            <input type="text" id="input-task" placeholder="Enter a task">
            <button id="add-task-btn"><i class="fa fa-fw fa-plus">+</i>
            </button>
        </div>
        <div class="tasker-body">
            <ul id="tasks"></ul>
        </div>
    </div>
    <!--즉시 실행함수를 사용하기 위해서는 script의 위치를 잘 선택해줘야한다. -->
    <script src="/todoProject/main.js"></script>
</body>
</html>

 

JS

// document.addEventListener("DOMContentLoaded",function(){
(function(){
    'use strict';
    var tasker = {
      init: function(){
        this.cacheDom();
        this.bindEvents();
        this.evalTasklist();
      },
      cacheDom: function(){
        this.taskInput = document.getElementById("input-task");
        this.addBtn = document.getElementById("add-task-btn");
        this.tasklist = document.getElementById("tasks");
        this.tasklistChildren = this.tasklist.children; 
        this.errorMessage = document.getElementById("error");
      },
      bindEvents : function(){
        this.addBtn.onclick = this.addTask.bind(this);
        this.taskInput.onkeypress = this.enterKey.bind(this);
      },
      evalTasklist: function(){
        var i, chkBox, delBtn;
        for(i = 0; i<this.tasklistChildren.length; i+=1){
          chkBox = this.tasklistChildren[i].getElementsByTagName("input")[0];
          chkBox.onclick = this.completeTask.bind(this, this.tasklistChildren[i], chkBox);
  
          delBtn = this.tasklistChildren[i].getElementsByTagName("button")[0];
          delBtn.onclick = this.delTask.bind(this,i);
        }
      },
      render : function(){
        var taskLi, taskChkbk, taskVal, taskBtn, taskTrash;
        // build HTMl
        taskLi = document.createElement("li");
        taskLi.setAttribute("class","task");
        // chkeckbox
        taskChkbk = document.createElement("input");
        taskChkbk.setAttribute("type", "checkbox");
        // user task
        taskVal = document.createTextNode(this.taskInput.value);
        // del button
        taskBtn = document.createElement("button");
        // trash icon
        taskTrash = document.createElement("i");
        taskTrash.setAttribute("class","fa fa-trash");
        taskTrash.textContent = '-' 
        console.log(taskTrash.textContent)
        // insert trash can into button
        taskBtn.appendChild(taskTrash);
  
  
        // append elements to taskLi
        taskLi.appendChild(taskChkbk);
        taskLi.appendChild(taskVal);
        taskLi.appendChild(taskBtn);
  
        // add task to task list
        this.tasklist.appendChild(taskLi);
  
      },
      completeTask: function(i, chkBox){
        if(chkBox.checked){
          i.className = "task completed";
        }else{
          this.incompleteTask(i);
        }
      },
      incompleteTask: function(i){
        i.className = "task";
      },
      enterKey: function(event){
        if(event.keyCode ===13  || event.which === 13){
          this.addTask();
        }
      },
      addTask: function(){
        var value = this.taskInput.value;
        this.errorMessage.style.display = "none";
  
        if(value ===""){
          this.error();
        } else{
          this.render();
          this.taskInput.value ="";
          this.evalTasklist();
        }
      },
      delTask : function(i){
        this.tasklist.children[i].remove();
        this.evalTasklist();
      },
      error: function(){
        this.errorMessage.style.display = "block";
      }
  
    };
  
    tasker.init();
  
  }());
// }());
// })

 

💥에러 발생

 

참고했던 코드랑 똑같이 썼는데 자꾸 오류가 발생했습니다. 

알고 보니 구조 차이였습니다. 즉시 실행 함수를 사용하고 있고, script를 head안에 넣어 놨던 것.

그렇게 되면 돔에서 js를 실행하는 과정에서 돔을 읽기 전에 js를 불러옵니다. 그렇게 되면 document에 있는 코드를 읽어 들이지 못합니다. 

 

 

✔ 해결 방법은 main.html 에서 script의 위치를 head에 놓는 게 아니라 코드가 그려지고 난 다음인 </body> 바로 위에 넣거나, [html 파일의 코드 수정] 

 

✔ head에 스크립트를 넣고 싶다면, js에서 즉시 실행 함수가 아니라 document가 그려지고 난 후에 쓸 수 있도록 해주어야 합니다. [js 파일의 코드 수정]

 

 

 

04. 결과

See the Pen todo list - project02 by bok-world (@bok-world) on CodePen.

 

 

05. 마치며

이번에 본 코드는 좀 신선했습니다. 하나의 코드에서 상속을 하면서 함수 형태로 쓰고 있다는 점이었습니다. 
사실 이런 식으로 작성되어 있는 코드는 많이 봤지만 직접 코드를 작성해 본 적은 없어서 많이 공부해야 했습니다. 상속에 대한 개념이라던가 클래스처럼 쓰이는 자스의 개념이라던가 등등.. 

그리고 투두를 생각했을 때 데이터를 저장한다는 느낌이 있어서 배열을 쓰겠거니 생각했는데, 배열의 개념은 하나도 들어가지 않았던 것이 좀 놀라웠습니다. (어쩌면 그냥 기본적인 출력이어서 그럴 수도 있겠습니다만) 

단위 단위 기능대로 묶은 함수가 여기저기서 쓰이는 것을 보니 코드가 아주 간편하고 깔끔했습니다. 함수를 쓰는 이유를 다시 한번 느끼며 다음 프로젝트를 향해 달려가 보겠습니다. 

 

 

 

 

 

 

 

참고문헌

📌 모던 자바스크립트 입문 책 

 

 

728x90
반응형