<자바스크립트> TO DO LIST 만들기

Woongbee Park·2022년 5월 13일
0
post-thumbnail

오늘은 흔해빠진 자바스크립트 프로젝트 TO DO LIST 를 만들어 보았다.
TO DO LIST에 필요한 기능들을 컴포넌트로 분리해서 만드는 방식,
리액트의 동작을 흉내내서 바닐라자바스크립트로 만들었다.
리액트를 공부할 땐 어려웠는데, 바닐라자바스크립트로 리액트의 동작을 흉내내보니까 리액트를 이해하게 되었다.

완성된 모습은 위와 같다. 할 일을 추가하면 아래에 순서대로 추가되고, 체크박스에 체크를 하면 빨간줄이 그어지고, delete를 누르면 삭제되는 심플한 todolist 이다.

폴더구조는 아래와 같다.

entry point, 즉 프로그램의 첫 시작 진입점은 index.html 이고
src 폴더 안에 index.html의 내용을 조작할 js 컴포넌트 파일들과 css 파일을 넣어두었다.

index.html 안에 기본적인 html을 써넣었다.
class="APP" 안에 JS를 통해 조작하는 HTML 요소들을 appendChild로 추가할거다. 날짜를 나타내는 부분은 그냥 html 파일에 스크립트 태그로 집어넣어버렸다. 쉬운 부분이니 코드는 생략.
여기서 주목해야할 부분은 line 76, 스크립트 파일을 삽입하는 부분에 type="module" 이라는 거다.

/
/

나는 3개의 컴포넌트를 만들거다.
1. 할일을 적으면 추가하는 기능 (Add.js)
2. 할일을 추가한 뒤, 삭제하는 기능 (Todo.js)
3. 위 2개의 컴포넌트가 직접 소통하지 않게, 상위 컴포넌트를 만들어
간접적으로 2개의 컴포넌트를 조작하기 (App.js)
/
/
/

여기부터 Add.js 코드
파라미터로 받는 $app 은,html에 미리 만들어둔
div class="App" 태그를 저장한 변수다. 저 태그 아래에 앞으로 만들 요소들을 child로 집어 넣을 거당

export default function Add({ $app,onClick }){

    this.target = document.createElement("div");
    this.target.className = "Add";
    $app.appendChild(this.target);



    this.onClick = onClick;

    this.render = () => {
        this.target.innerHTML = `<input id="write" type="text" placeholder="Add a task"></input><button id="addButton"type="button">+</button>`;
        const button = document.querySelector('#addButton');
        const write = document.querySelector('#write');
        button.addEventListener("click", (e) => {
            if (write.value) {
                this.onClick(write.value);
                write.value = '';
            }else(alert("Type what to do"))
        })
    }

    this.render();
}

위 코드에서 중요한 부분은 파라미터로 받는 onClick.
onClick이 어떤 동작을 하는지는 App.js 에서 정의해서 넘겨준다.
Add.js 컴포넌트에서는 input 창에 사용자가 입력한 값만 onClick 함수의 인자로 넘겨주면, 그 값을 가지고 어떤 동작을 할지는 App.js에서 미리 정의해서 넘겨줄거다. 이러면 코드의 재사용성이 높아지겠지...?
/
/
/

아래는 Todo.js 코드. Add.js 에서 넘겨받은 사용자 입력값을 목록에 추가하고, delete를 누르면 목록에서 할일을 삭제하는 기능을 한다.

export default function Todo({ $app, initialState,removeClick }) {
    this.state = initialState;
    this.target = document.createElement('div');
    this.target.className = "Todo";
    $app.appendChild(this.target)

    this.setState = (next) => {
        this.state = next;
        this.render()
    }    **Todo.js 컴포넌트의 setState 함수를 호출하면, 상태값을 인자로 받아 this.render()를 호출한다. **
    
    
    let count = 0;
    this.removeClick = removeClick;

    this.render = () => {  
        const lists = this.state.list.map(content => {
            count++;
            return `<div class="list"><input type="checkbox" id="${count}" class="checkbox1">
    <div id="one"><label for="${count}">${content}</label><button type="button" class="remove">delete</button></div></div>`      
        }).join('');

        this.target.innerHTML = lists;

        const remove = document.querySelectorAll('.remove');
        remove.forEach(v => {
            v.addEventListener("click", (e) => {
                const removeArr = Array.from(remove);
                const removingTarget = removeArr.findIndex(a => a.parentNode == e.target.parentNode)
                this.removeClick(removingTarget)
            })
        })
        
    }
    this.render();
}

여기서도 핵심은 할일을 삭제하는 removeClick 의 동작을
App.js 에서 정의하고 여기서는 지금 삭제하려는 요소가 무엇인지를 removeClick 함수의 인자로 넘겨주기만 했다.
/
/
/
/

다음은 Add.js 와 Todo.js를 조작하는 상위 컴포넌트 App.js

import Todo from './Todo.js'
import Add from './Add.js'

export default function App($app) {
    this.state = {
        list:[]          **여기에 사용자가 입력한 값이 추가된다.**
    }

    const add = new Add({
        $app,
        onClick: (writeValue) => {
            this.setState({
                list: [...this.state.list, writeValue]
            })
        }   
    })          **사용자 입력값을 넘겨받아, list에 추가한다.**


    const todo = new Todo({
        $app,

        initialState: {
            list:this.state.list
        },

        removeClick: (removingTarget) => {
            this.state.list.splice(removingTarget, 1);
            this.setState({
                list: [...this.state.list]
            })
        }** 사용자가 delete를 누른 요소의 index값을 넘겨 받아 list 배열에서 삭제. **
    })

  

    this.setState = (next) => {
        this.state = next;
        todo.setState({
            list:this.state.list
        });
    }
}      **this.setState 함수는 상태값(next)를 받아서,
       Todo.js의 setState 함수를 호출.**

마지막으로 index.js 파일

import App from './App.js'

new App(document.querySelector('.App'));
**html 파일에 미리 만들어준 class="App" 요소를 인자로 넘겨 App 컴포넌트 호출
App 컴포넌트에서 Todo, Add 파일들을 줄줄이 호출할 거다. **

완성하고 보니 아주 간단한 프로젝트 였는데,
기능들을 컴포넌트로 분리하여,
이 컴포넌트들을 조작하는 상위 컴포넌트로 html을 조작하는 방식을 이해할 수 있었던 좋은 프로젝트였다. 무엇보다 리액트가 동작하는 방식을 알게되어서 개운하다.
https://github.com/woongbeee/todolist

profile
나는야 개발자

0개의 댓글