1. 간단한 HTML 제작하기
크게 toast-container
과 container-full
로 분리했다.
toast-container
에는 toast
가 실행될 div
를 만들어 두었다.
container-full
가 실제 실행코드들로 보면 된다.
container-full
에는 Head 부분
과 Btn 부분
과 Data 부분
으로 구성되어 있다.
Todo와 Done 을 구분하기 위해 다른 웹 페이지 test.html 을 만들었다. test.html도 아래의 html과 유사해 생략하였다.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ToDoApp</title>
<link rel="stylesheet" href="/css/index.css">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/js/bootstrap.bundle.min.js"></script>
<script type="module" src="/js/index.js"></script>
</head>
<body>
<!--Toast Message-->
<div id="toast-container"></div>
<!--실제 화면-->
<div class="container-full">
<!--Head 부분: 제목과 Input-->
<div class="container-head">
<!--제목과 Fetch(온도)-->
<div id="title">
<h1>† To Do App †</h1>
<p>오늘 서울의 온도는 <span style="color:brown;" id="tempInfo"></span>°C 이며, 날씨는 <span style="color:brown;"id="weatherInfo"></span> 입니다.</p>
</div>
<!--일정추가란-->
<div class="todo_Input">
<div class="input-group mb-3">
<input type="text" class="form-control" id="todoText" placeholder="할일을 적어주세요.">
<button type="button" class="btn btn-outline-dark" id="todoBtn">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
class="bi bi-plus-square-fill" viewBox="0 0 16 16">
<path
d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0" />
</svg>
일정 추가
</button>
</div>
</div>
</div>
<div id="line"></div>
<div id="line2"></div>
<!--Btn 부분: Todo와 Done 이동 부분-->
<div id="todoDonerow">
<a href="/index.html"><button id="todohref" class="btn hrefthis">To Do</button></a>
<a href="/test.html"><button id="donehref" class="btn">Done</button></a>
</div>
<!--Data 부분: 실제 데이터 쌓는 부분-->
<div class="container-stack" >
<ul class="list-group" id="text-stack">
<!--여기에 데이터 쌓임.-->
</ul>
</div>
</div>
</body>
</html>
- css 는 생략하겠다.
2. 데이터 처리 (Firebase)
1) 데이터 연동
- 프론트를 만든 후, Firebase와 프론트를 연결했다.
- API 키 보호를 위해 비공개로 비워두었다.
// Firebase SDK 라이브러리 가져오기
import { initializeApp } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-app.js";
import { getFirestore } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-firestore.js";
import { collection, addDoc } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-firestore.js";
import { getDocs } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-firestore.js";
// Firebase 구성 정보 설정
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
const firebaseConfig = {
//비공개
};
// Firebase 인스턴스 초기화
const app = initializeApp(firebaseConfig);
const db = getFirestore(app);
2) 데이터 전달하기
- 데이터 전달에 앞서, 데이터는 크게 4가지로 구성되어 있다. ID
와 text
, complete
, date
이렇게 있다. 각 데이터를 설명하자면,
ID
: 일정 추가를 한 시간을 밀리초로 환산한string
값text
: 일정complete
: 일정 완료 여부date
: 일정 추가/수정한 '년-월-일 시간:분'
- todoBtn
을 클릭시 해당 값이 DB 로 전달될 수 있게 Jquery 로 코드를 작성하였다.
- Firebase
의 DATA
에 고유한 ID
값을 주기 위하여 현재 시간을 밀리초로 환산한 값을 string
으로 저장하였다.
- getTodayDate()
라고 오늘 시간을 년-월-일 시간:분
으로 바꾸어주는 함수로 date
값을 저장하였다.
- showToast
를 통해 일정이 추가되었다는 메시지를 Toast
로 보여주었다.
- 일정이 추가되고 난 후에는 widow.location.reload()
를 해주었다.
$('#todoBtn').click(async function () {
let taskText = $('#todoText').val();
let customId = Date.now().toString();
let complete = false;
let date = getTodayDate();
// customId 로 id 지정해서 추가하기.
const docRef = doc(db, "todos", customId);
try {
await setDoc(docRef, {
id: customId,
text: taskText,
complete: complete,
date: date
});
showToast("[ " + taskText.slice(0, 8) + "... ] ", "일정을 추가 하였습니다.");
window.location.reload();
} catch (error) {
console.error("에러가 발생했습니다. ", error);
}
})
// 날씨 환산 함수
function getTodayDate() {
let today = new Date();
let year = today.getFullYear();
let month = today.getMonth() + 1;
let day = today.getDate();
let hour = today.getHours();
let minute = today.getMinutes();
return `${year}-${month < 10 ? '0' + month : month}-${day < 10 ? '0' + day : day} ${hour < 10 ? '0' + hour : hour}:${minute < 10 ? '0' + minute : minute}`;
}
3) 데이터 띄우기
- todos
에 db를 저장해두고, 저장한 db 값을 forEach
를 통해 화면에 띄운다.
- complete
여부에 따라 하나는 'Todo' 로 가고 하나는 'Done'으로 간다.
let todos = await getDocs(collection(db, "todos"))
todos.forEach((todo) => {
let text = todo.data()['text'];
let complete = todo.data()['complete'];
let id = todo.data()['id'];
let date = todo.data()['date'];
let temp_html = ``
if (complete === true) {
temp_html = `
<li class="list-group-item" id="${id}">
<div>
<input class="form-check-input" type="checkbox" id="checkbox" checked>
<input type="text" id="eidt_text" value="${text}">
<p class="date-p">${date}</p>
</div>
<button type="button" class="btn btn-secondary float-end deleteBtn" id="deleteBtn">삭제</button>
<button type="button" class="btn btn-secondary float-end editBtn" id="editBtn">수정</button>
</li>`;
$('#text-stack-done').append(temp_html);
} else {
temp_html = `
<li class="list-group-item" id="${id}">
<div>
<input class="form-check-input" type="checkbox" id="checkbox">
<input type="text" id="eidt_text" value="${text}">
<p class="date-p">${date}</p>
</div>
<button type="button" class="btn btn-secondary float-end deleteBtn" id="deleteBtn">삭제</button>
<button type="button" class="btn btn-secondary float-end editBtn" id="editBtn">수정</button>
</li>`;
$('#text-stack').append(temp_html);
}
});
4) complete
와 checkbox
- checkbox
가 변경될 때 마다 이를 DB에 반영해주는 코드이다.
- checkbox
의 change
이벤트는 JS
가 각각 독립으로 처리하기 때문에 $(document)
로 처리하지 않아도 된다.
$('.form-check-input').change(async function () {
let check_id = $(this).closest('li').attr('id');
let check_text = $(this).closest('li').find('input[type="text"]').val().trim();
const docRef = doc(db, "todos", check_id);
console.error("에러가 발생했습니다. ", '1');
if ($(this).is(':checked')) {
console.error("에러가 발생했습니다. ", '2');
showToast("[ " + check_text.slice(0, 8) + "... ] ", "일정을 완료 하였습니다.");
await updateDoc(docRef, {
complete: true,
});
console.error("에러가 발생했습니다. ", '3')
} else {
showToast("[ " + check_text.slice(0, 8) + "... ] ", "일정을 미완료 하였습니다.");
await updateDoc(docRef, {
complete: false,
});
}
});
5) Delete Btn 작동
- DeleteBtn
클릭 시, 데이터가 DB에서 삭제될 수 있도록 하는 코드이다.
- 여기서 중요한 포인트가 $(documnet).on
으로 처리했다는 것이다. 만약 바로 click
클래스로 접근하였으면 첫번째 값만 삭제 이벤트가 작동하고, 나머지 아이들은 삭제이벤트가 작동하지 않을 것이다.
- click
이벤트의 경우, 이벤트 위임을 사용하지 않으면 페이지가 처음 로드될 때 존재하는 요소에만 이벤트가 등록됩니다. 그래서 동적으로 생성된 요소에는 이벤트가 적용되지 않아서 첫 번째 요소만 작동하는 것처럼 보일 수 있다.
- JS 코드 실행 시점에 존재하는 .editBtn 요소들에 대해서만 click
이벤트를 등록한다. 따라서 코드 실행 이후에 동적으로 추가된 .editBtn
버튼은 이 이벤트를 인식하지 못하게 된다. 따라서 이벤트 위임을 사용하여 페이지 전체(document)에 click
이벤트를 걸고, 이벤트가 발생하면 .editBtn
요소에서만 실행되도록 위임하는 방식으로 접근해야한다.
- 체크박스의 change
이벤트는 각 요소마다 독립적으로 동작한다. 체크박스는 보통 동적으로 생성되지 않거나, 생성되더라도 jQuery가 기존 요소에 대해 이벤트를 등록하는 방식이 잘 적용된다. 따라서 change
이벤트는 여러 개의 체크박스에서도 잘 동작한다.
$(document).on('click', '.deleteBtn', async function () {
let check_id = $(this).closest('li').attr('id');
let task_text = $(this).closest('li').find('input[type="text"]').val().trim();
actionModal("이 일정을 삭제하시겠습니까?", "[ " + task_text + " ]", "삭제하기");
$(document).one('click', '#modal-actionBtn', async function () {
const docRef = doc(db, 'todos', check_id);
try {
await deleteDoc(docRef);
$("#dynamicModal").remove()
showToast("[ " + task_text.slice(0, 8) + "... ] 삭제여부", "삭제되었습니다.");
} catch (error) {
console.error("삭제 실패: ", error);
}
})
$(document).on('click','#modal-closeBtn', async function (){
showToast("[ " + task_text.slice(0, 8) + "... ] 삭제여부", "수정 취소 하였습니다.");
})
});
6) Edit Btn 작동
- 수정 버튼을 누를 때 마다, 수정된 일정과 새로운 날짜로 바꾸어주는 코드이다.
$(document).on('click', '.editBtn', async function () {
let task_text = $(this).closest('li').find('input[type="text"]').val().trim();
let check_id = $(this).closest('li').attr('id');
let date = getTodayDate();
console.log(task_text,check_id);
actionModal("해당 일정으로 수정하실건가요?", "[ " + task_text + " ]", "수정하기");
// 실제로 변경할건지 아닌지 물어보기.
$(document).on('click', '#modal-actionBtn', async function () {
const docRef = doc(db, "todos", check_id);
updateDoc(docRef, {
text: task_text,
date: date
});
$("#dynamicModal").remove()
showToast("[ " + task_text.slice(0, 8) + "... ] 수정여부", "수정되었습니다.");
setTimeout(() => {
window.location.reload();
}, 3000); // 3초 후 새로고침
})
$(document).on('click','#modal-closeBtn', async function (){
showToast("[ " + task_text.slice(0, 8) + "... ] 수정여부", "수정 취소 하였습니다.");
})
});
3. Fetch 로 날씨 온도 가져오기 (온도 API) 가져오기
- 날씨 온도 API 를 가져오는 방법을 적어보겠다.
1) https://openweathermap.org/api 회원가입하기
"https://openweathermap.org/api"
- a. 위 사이트에 들어가서 회원가입을 해준다.
- b. 이메일 인증한다.
- c. 메일 인증 하면 아래의 이메일이 한 번 더 날아온다. 빨간색으로 칠해진 부분이 API 키 인데, 이를 복사해두면 된다.
2) 날씨 데이터 접근해보기
- a. 아래의 링크 {} 안에 복사해둔 자신의 키를 넣어준다. ({}을 포함하면 안된다.)
"http://api.openweathermap.org/data/2.5/weather?id=1835848&APPID={**자신의API키**}&lang=kr&units=metric"
id=1835848
한국 id 임lang=kr
한국어로 번역units=metric
섭씨온도로 변환
- b. 링크에 들어가 데이터를 확인해본다. 아래와 같은 JSON 형태로 나오면 성공이다.
- 온도에 접근하고 싶으면
data.main.temp
- 날씨에 접근하고 싶으면
data.weather[0].description
- c. 다른 데이터로 링크를 바꾸어서 데이터 접근도 가능하다. 아래의 사이트에 가서 직접 확인하면 된다.
"https://openweathermap.org/api/one-call-3"
3) Fetch 연동
- 아래의 코드에 넣으면 된다.
var apiUrl = "http://api.openweathermap.org/data/2.5/weather?id=1835848&APPID={**자신의API키**}&lang=kr&units=metric"
fetch(apiUrl)
.then(res => res.json())
.then(data => {
var temperature = data.main.temp;
var description = data.weather[0].description;
$('#tempInfo').text(temperature);
$('#weatherInfo').text(description);
})
4) 결과
'프로젝트 > ToDoApp프로젝트-FireBase' 카테고리의 다른 글
[Bootstrap Toast, Modal] 동적 삽입 (0) | 2025.02.09 |
---|---|
[ToDoApp-Firebase] 프로젝트 기획 및 설계 (1) | 2025.02.08 |