이전에 이 프로젝트를 진행했을 때는 localStorage를 사용해서 To do list를 구현했는데, 단국대부속소프트웨어고등학교 자율동아리 친구들의 수업을 준비하면서, 이 프로젝트를 통해 Firebase를 사용해보면 좋은 경험이 될 것 같아서 리팩토링 해보았다.
Firebase의 firestore에 To do list를 저장하고, javascript를 통해 웹과 연동하여 불러온다.
바닐라 js에서 firebase를 사용하려면 script에 주소를 추가해주어야 한다. require 등 다양한 방법을 써봤지만 버전 문제 때문에 제대로 작동하지 않았다.
<script src="https://www.gstatic.com/firebasejs/8.8.1/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.8.1/firebase-firestore.js"></script>
<script src="todo.js"></script>
todo.js 파일을 만들어 index.html과 연결하고 다음과 같이 프로젝트를 불러왔다. firebase에서 프로젝트를 생성하고 config를 가져오는 부분은 생략했다.
const firebaseConfig = {
apiKey: "apiKey",
authDomain: "authDomain",
projectId: "momentum-todo",
storageBucket: "storageBucket",
messagingSenderId: "messagingSenderId",
appId: "appId",
};
Firebase를 초기화하고 firestore를 불러온다.
// Firebase 초기화
// 인자로 위의 key들을 넣어주면 해당 프로젝트와 연결됨
firebase.initializeApp(firebaseConfig);
// firestore를 불러와서 dbService에 넣어줌
// dbService를 통해 firestore를 동작(수정/추가/삭제)할 수 있음
const dbService = firebase.firestore();
html에 to do를 입력하는 form과 input 요소를 넣는다. 불러온 to do list를 출력할 div 요소도 생성했다.
<form id="todo-form">
<input type="text" id="todo" />
<input type="submit" value="ADD" />
</form>
<div id="todos"></div>
to do를 입력하고 submit했을 때 연결된 firestore에 데이터를 넣어주는 코드이다.
// html의 todo-form, todo, todos 불러오기
const todoForm = document.querySelector("#todo-form");
const todoInput = document.querySelector("#todo");
const todos = document.querySelector("#todos");
// todo가 새로 추가됐을 때 호출되는 함수
const onSubmit = async (e) => {
// 새로고침 방지
e.preventDefault();
// 현재 시각을 아이디로 사용하기 위해서 Date 객체를 불러옴
const date = new Date();
// todo 생성 시각 불러옴
const createdAt = date.getTime();
// dbService에서 todos 컬렉션에 createdAt이라는 아이디를 가진 문서를 생성한다.
// 내용은 todo: todo 내용으로 한다. todo 내용은 input에 입력돼있는 내용이다.
// set함수를 통해서 생성하고, 내용을 넣어줄 수 있다.
// set함수는 같은 id의 문서가 있을 때는 문서의 내용을 덮어쓰기하고,
// 없을 때는 새로 추가해준다.
await dbService.collection("todos").doc(`${createdAt}`).set({
todo: todoInput.value,
});
// todoInput은 비워준다.
todoInput.value = "";
};
// todo form에 submit을 감지하는 이벤트리스너를 추가한다.
// submit되면 onSubmit 함수가 호출된다.
todoForm.addEventListener("submit", onSubmit);
onSnapshot을 통해 데이터가 변경될 때마다 함수를 실행할 수 있다.
// 빈 배열 생성
let todoArr = [];
// dbService(firestore)의 'todos' collection이 바뀔 때마다 감지
dbService.collection("todos").onSnapshot((snapshot) => {
// 변경되었을 때 코드 실행
// map 함수는 배열의 요소들을 하나하나 돌면서 코드를 실행한다.
// snapshot.docs.map은 documents를 배열로 받아온 것
// doc은 반복하고 있는 배열의 요소를 의미한다.
// 예를 들어 python에서 for a in arr: 라는 코드가 있을 때
// map == for
// snapshot.docs == arr
// doc == a
// todoArr에 문서의 데이터들과 id를 저장한다.
todoArr = snapshot.docs.map((doc) => ({
...doc.data(),
id: doc.id,
}));
// console.log(todoArr)
// to do list를 표시하는 함수 호출
paintingTodos();
});
todoArr에 넣어준 to do 데이터들을 화면에 띄운다.
// to do list를 표시하는 함수
const paintingTodos = () => {
// 밑의 내용을 먼저 보고 올라오도록
// preUl은 todos에 있는 ul을 불러온다.
const preUl = document.querySelector("#todos ul");
// 이미 한 번 todo가 그려져서 todos 안에 ul이 존재하는 상황이라면
// 해당 ul을 지워준다. 그래야 새로 ul이 추가됐을 때 또 불러와지지 않는다.
if (preUl) {
preUl.remove();
}
// ul(점으로 찍히는 리스트) 요소 생성
const ul = document.createElement("ul");
// map 함수를 이용해서 todoArr의 모든 요소를 반복해서 가져온다.
// 반복할 때마다 하나의 요소씩 t에 들어가게 된다.
todoArr.map((t) => {
// li(ul 안에 들어가는 리스트 요소)와 button 요소 생성(삭제 버튼)
const li = document.createElement("li");
const btn = document.createElement("button");
// li 태그의 안에는 todo의 내용이 들어간다.
li.innerText = `${t.todo}`;
// button 태그 안에는 delete라고 들어간다.
btn.innerText = "delete";
// btn이 클릭됐을 경우 함수를 발생시키기 위해 이벤트 리스너를 추가해주었다.
// btn이 클릭되면 onDeleteClick이 호출된다.
btn.addEventListener("click", onDeleteClick);
// btn의 아이디를 todo의 id로 설정해주었다.
// 삭제 작업을 하기 위해선 아이디가 필요하기 때문에
// 버튼의 아이디에 todo 아이디를 넣어주었다.
btn.id = `${t.id}`;
// li에 btn을 넣고, ul에 li를 넣어준다.
li.appendChild(btn);
ul.appendChild(li);
// 이 작업을 todoArr의 모든 요소에 반복한다.
});
// li 요소가 모두 추가된 ul을 todos에 추가한다.
// 이 때 최종적으로 화면에 todo가 표시되게 된다.
todos.appendChild(ul);
// 위에서 ul 삭제를 안한 경우 paintingTodos가 호출될 때마다
// 새로 todo를 그리기 때문에,
// 맨 앞쪽에 기존에 있던 것을 지워주는 코드를 추가한다.
};
Firestore에서 아이디를 통해 데이터를 지운다.
// 위에서 btn에 추가해주었던 click 이벤트 리스너가 호출하는 함수이다.
const onDeleteClick = (e) => {
// 클릭된 타겟이 가지고 있는 아이디를 불러온다.
const targetId = e.target.id;
// dbService의 todos 컬렉션에서 해당 아이디의 문서를 지운다.
dbService.collection("todos").doc(`${targetId}`).delete();
// 문서를 지웠으므로 다시 todo를 화면에 그린다.
paintingTodos();
};
Reference
좋은글 감사합니다!! 다음 내용도 기대되네요!