diff --git a/index.html b/index.html
new file mode 100644
index 0000000..4fa389e
--- /dev/null
+++ b/index.html
@@ -0,0 +1,41 @@
+
+
+
+
+
+ Vanilla Todo
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/main.js b/main.js
new file mode 100644
index 0000000..bd216fe
--- /dev/null
+++ b/main.js
@@ -0,0 +1,277 @@
+document.addEventListener ("DOMContentLoaded", function() {
+ //DOM 요소 가져오기
+ const input = document.querySelector(".input");
+ const enterButton = document.querySelector(".enter");
+ const todoContainer = document.querySelector(".container");
+ const currentDateSpan = document.getElementById("currentDate");
+ const prevButton = document.getElementById("prev");
+ const nextButton = document.getElementById("next");
+ const datePicker = document.getElementById("datePicker");
+ const themeBtn = document.querySelector(".theme");
+ const body = document.body;
+ const homeButton = document.querySelector("h2");
+ const sidebar = document.querySelector(".sidebar");
+ const hamburger = document.querySelector(".hamburger");
+ const closeBtn = document.querySelector(".close");
+ const todoCountSpan = document.getElementById("todo-count");
+
+ // Date 객체를 YYYY-MM-DD 형식의 문자열로 변환
+ function formatDateToYYYYMMDD(date) {
+ return [
+ date.getFullYear(),
+ String(date.getMonth() + 1).padStart(2, "0"),
+ String(date.getDate()).padStart(2, "0")
+ ].join("-");
+ }
+
+ // 현재 선택된 날짜 (YYYY-MM-DD 형식)
+ let currentDate = formatDateToYYYYMMDD(new Date());
+
+ // YYYY-MM-DD 문자열을 "YYYY년 M월 D일" 형식으로 변환
+ function formatDate(dateString) {
+ const [y, m, d] = dateString.split("-").map(Number);
+ return `${y}년 ${m}월 ${d}일`;
+ }
+
+ // 현재 날짜 상태를 UI에 반영
+ function updateDateUI() {
+ currentDateSpan.textContent = formatDate(currentDate);
+ datePicker.value = currentDate;
+ updateTodoCount();
+ }
+
+ // 할 일 리스트 불러오기 (로컬 스토리지)
+ function loadTodos() {
+ todoContainer.innerHTML = "";
+ const todos = JSON.parse(localStorage.getItem(currentDate)) || [];
+ todos.forEach(({ id, text, completed }) =>
+ addTodoElement(id, text, completed)
+ );
+ updateTodoCount();
+ }
+
+ // 새로운 할 일 요소 추가
+ function addTodoElement(id, text, completed = false) {
+ const todoDiv = document.createElement("div");
+ todoDiv.classList.add("todo");
+ todoDiv.setAttribute("draggable", true); /*드래그 가능 설정*/
+ todoDiv.dataset.id = id; /*순서 저장용*/
+
+ // 체크박스
+ const checkbox = document.createElement("input");
+ checkbox.type = "checkbox";
+ checkbox.checked = completed;
+ checkbox.classList.add("checkbox");
+
+ checkbox.addEventListener("change", () => {
+ // UI 클래스 토글 먼저
+ if (checkbox.checked) {
+ todoText.classList.add("completed");
+ } else {
+ todoText.classList.remove("completed");
+ }
+
+ // localStorage Update
+ toggleComplete(id, checkbox.checked);
+
+ /*console 확인용
+ console.log(
+ `Todo "${todoText.textContent}" completed:`,
+ todoText.classList.contains("completed")
+ );*/
+ });
+
+ // Todo 텍스트 요소 생성 (완료 시 CSS 클래스 적용)
+ const todoText = document.createElement("span");
+ todoText.textContent = text;
+ if (completed) {
+ todoText.classList.add("completed");
+ }
+
+ // 삭제버튼
+ const deleteButton = document.createElement("button");
+ deleteButton.textContent = "삭제";
+ deleteButton.classList.add("delete");
+ deleteButton.addEventListener("click", () => removeTodo(id));
+
+ todoDiv.append(checkbox, todoText, deleteButton);
+ todoContainer.appendChild(todoDiv);
+
+ // 드래그 이벤트 추가
+ addDragAndDropListeners(todoDiv);
+ }
+
+ // 드래그 이벤트 함수
+ let placeholder = null;
+
+ function addDragAndDropListeners(item) {
+ item.draggable = true;
+
+ item.addEventListener("dragstart", () => {
+ item.classList.add("dragging");
+ placeholder = document.createElement("div");
+ placeholder.classList.add("todo", "placeholder");
+ item.parentNode.insertBefore(placeholder, item.nextSibling);
+ });
+
+ item.addEventListener("dragend", () => {
+ item.classList.remove("dragging");
+ if (placeholder) {
+ placeholder.parentNode.replaceChild(item, placeholder);
+ placeholder = null;
+ saveNewOrder(); // localStorage에 순서 저장
+ }
+ });
+
+ item.addEventListener("dragover", (e) => {
+ e.preventDefault();
+ const container = item.parentNode;
+ const draggingItem = document.querySelector(".dragging");
+ if (draggingItem === item) return;
+
+ const rect = item.getBoundingClientRect();
+ const offset = e.clientY - rect.top;
+ const middle = rect.height / 2;
+
+ if (offset > middle) {
+ container.insertBefore(placeholder, item.nextSibling);
+ } else {
+ container.insertBefore(placeholder, item);
+ }
+ });
+ }
+
+ // 순서 변경 후 LocalStorage에 저장
+ function saveNewOrder() {
+ const todos = [];
+ todoContainer.querySelectorAll(".todo").forEach((todoDiv) => {
+ const id = todoDiv.dataset.id;
+ const text = todoDiv.querySelector("span").textContent;
+ const completed = todoDiv.querySelector("input").checked;
+ todos.push({id, text, completed});
+ });
+ localStorage.setItem(currentDate, JSON.stringify(todos));
+ }
+
+ // 할 일 추가
+ function addTodo() {
+ const text = input.value.trim();
+ if (!text) return;
+
+ const todos = JSON.parse(localStorage.getItem(currentDate)) || [];
+ const newTodo = { id: crypto.randomUUID(), text, completed: false };
+
+ todos.push(newTodo);
+ localStorage.setItem(currentDate, JSON.stringify(todos));
+ addTodoElement(newTodo.id, text);
+ input.value = "";
+ updateTodoCount();
+ }
+
+ // 할 일 삭제
+ function removeTodo(id) {
+ let todos = JSON.parse(localStorage.getItem(currentDate)) || [];
+ todos = todos.filter((todo) => todo.id !== id);
+ localStorage.setItem(currentDate, JSON.stringify(todos));
+ loadTodos();
+ }
+
+ // 완료 상태 체크박스
+ function toggleComplete(id, isCompleted) {
+ let todos = JSON.parse(localStorage.getItem(currentDate)) || [];
+ todos = todos.map((todo) =>
+ todo.id === id ? { ...todo, completed: isCompleted } : todo
+ );
+ localStorage.setItem(currentDate, JSON.stringify(todos));
+ loadTodos();
+ }
+
+ // Todo 개수 업데이트 함수 (체크된 항목 제외)
+ function updateTodoCount() {
+ const todos = JSON.parse(localStorage.getItem(currentDate)) || [];
+ const activeTodos = todos.filter((todo) => !todo.completed).length;
+ todoCountSpan.textContent = activeTodos;
+ }
+
+ // 현재 날짜를 기준으로 days 만큼 이동
+ function changeDate(days) {
+ const [y, m, d] = currentDate.split("-").map(Number);
+ const newDate = new Date(y, m - 1, d);
+
+ newDate.setDate(newDate.getDate() + days);
+ currentDate = formatDateToYYYYMMDD(newDate);
+
+ updateDateUI();
+ loadTodos();
+ }
+
+ // 다크모드 설정
+ function loadTheme() {
+ const isDarkMode = JSON.parse(localStorage.getItem("darkMode"));
+ body.classList.toggle("dark-mode", isDarkMode);
+ themeBtn.textContent = isDarkMode ? "☀️다크모드 해제" : "다크모드 설정🌙";
+ }
+
+ function toggleTheme() {
+ const isDarkMode = body.classList.toggle("dark-mode");
+ themeBtn.textContent = isDarkMode ? "☀️다크모드 해제" : "다크모드 설정🌙";
+ localStorage.setItem("darkMode", isDarkMode);
+ }
+
+ // 사이드바 열기/닫기 기능
+ function openSidebar() {
+ sidebar.style.left = "0";
+ }
+
+ function closeSidebar() {
+ sidebar.style.left = "-250px";
+ }
+
+ // 사이드바 외부 클릭 처리 - Early Return 패턴 적용
+ function handleOutsideClick(event) {
+ if (sidebar.contains(event.target) || hamburger.contains(event.target)) return;
+ closeSidebar();
+ }
+
+ // Enter 입력 시 Todo 추가 (한글 IME 조합 중 입력 방지 <-- React에서 수정필요!)
+ enterButton.addEventListener("click", addTodo);
+
+ input.addEventListener("keydown", (event) => {
+ if (event.key === "Enter") {
+ addTodo();
+ }
+ });
+
+ prevButton.addEventListener("click", () => changeDate(-1));
+ nextButton.addEventListener("click", () => changeDate(1));
+
+ datePicker.addEventListener("change", function () {
+ currentDate = this.value;
+ updateDateUI();
+ loadTodos();
+ });
+
+ document.querySelectorAll(".weekBtn").forEach((button) => {
+ button.addEventListener("click", () =>
+ changeDate(parseInt(button.dataset.days))
+ );
+ });
+
+ // 홈 버튼 클릭 시 오늘 날짜로 이동
+ homeButton.addEventListener("click", () => {
+ currentDate = formatDateToYYYYMMDD(new Date());
+ updateDateUI();
+ loadTodos();
+ });
+
+ themeBtn.addEventListener("click", toggleTheme);
+ hamburger.addEventListener("click", openSidebar);
+ closeBtn.addEventListener("click", closeSidebar);
+ document.addEventListener("click", handleOutsideClick);
+
+ // 초기 실행
+ updateDateUI();
+ loadTodos();
+ loadTheme();
+});
+
diff --git a/styles.css b/styles.css
new file mode 100644
index 0000000..eab7ad7
--- /dev/null
+++ b/styles.css
@@ -0,0 +1,427 @@
+:root {
+ --bg: #cbdef0;
+ --bg_inputContainer: #ffffff;
+ --bg_enter: #ffc933;
+ --bg_todoitem: #f9c254;
+
+ --bg_checkbox: #f9f9f9;
+ --checkbox: #ff4d4d;
+
+ --text: #000000;
+ --text_nav: #ffffff;
+ --text_todoitem: #000000;
+ --text_todocount: #b2b0b0;
+ --text_countitem: #b2b0b0;
+ --dateButton: #000000;
+ /*할 일 목록*/
+ --deleteButton: #f3f4f6;
+ --hover_deleteButton: #e5e7eb;
+ --text_deleteButton: #000000;
+
+ /*사이드 바*/
+ --hamburger: #000000;
+ --bg_sidebar: #fbf0d6;
+ --text_sidebarButton: #000000;
+ --text_datePicker: #a5a3a3;
+ --border_datePicker: #ccc;
+ --closeButton: #a5a3a3;
+
+ /*다크모드 설정 시*/
+ --darkmode_hamburger:#ffffff;
+ --bg_darkmode: #3d3c3c;
+ --text_darkmode_header: #ffffff;
+ --bg_inputContainer_darkmode: #2c2c2c;
+ --bg_todo_darkmode: #d6d7d8;
+ --text_darkmode: #000000;
+ --text_darkmode_sidebar: #a5a3a3;
+ --text_darkmode_enter: #ffffff;
+ --bg_sidebar_darkmode: #ffffff;
+ --text_darkmode_todo: #000000;
+}
+
+/*기본 스타일 설정*/
+* {
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
+}
+
+html,
+body {
+ font-family: 'Pretendard', sans-serif;
+}
+
+/* body 스타일*/
+body {
+ background-color: var(--bg);
+ color: var(--text);
+}
+
+body.dark-mode {
+ background-color: var(--bg_darkmode);
+ color: var(--text_darkmode);
+}
+
+/* 헤더 스타일*/
+header {
+ display: flex;
+ position:relative;
+ align-items: center;
+ justify-content: center;
+ color: var(--text_header);
+ width: 100%;
+ height: 4rem;
+ cursor: default;
+}
+
+body.dark-mode header {
+ color: var(--text_darkmode_header);
+}
+
+/*햄버거 메뉴 버튼 스타일*/
+.hamburger {
+ position: absolute;
+ left: 1rem;
+ padding: 0;
+ margin: 0;
+ cursor: pointer;
+ color: var(--hamburger);
+ border: none;
+ background-color: transparent;
+ font-size: 2rem;
+}
+
+body.dark-mode .hamburger {
+ color: var(--darkmode_hamburger); /*피드백 수정; color값 오타*/
+}
+
+/*내비게이션 바 스타일*/
+nav {
+ display: flex;
+ gap: 10px;
+ margin-top: 1rem;
+ justify-content: center;
+ align-items: center;
+}
+
+@media (max-width: 500px) {
+ .inputContainer, .todo {
+ width: 90%;
+ }
+
+ nav {
+ flex-wrap: wrap;
+ gap: 5px;
+ }
+}
+
+.calendarWrapper {
+ position: relative;
+ width: 22px;
+ height: 22px;
+}
+
+.calendarWrapper:hover {
+ transform: scale(1.1);
+}
+
+#datePicker {
+ position: absolute;
+ background: transparent;
+ inset: 0;
+ border: none;
+ opacity: 100;
+ cursor: pointer;
+}
+
+#datePicker::-webkit-datetime-edit {
+ display: none; /*날짜 텍스트 숨기기*/
+}
+
+#datePicker::-webkit-calendar-picker-indicator {
+ cursor: pointer;
+ width: 18px;
+ height: 18px;
+ transform: scale(1.2);
+}
+
+body.dark-mode nav {
+ color: var(--text_darkmode);
+}
+
+/*main 스타일*/
+main {
+ display: flex;
+ align-items: center;
+ flex-direction: column;
+}
+
+/*화살표 버튼 스타일*/
+.dateButton {
+ border: none;
+ cursor: pointer;
+ background-color: transparent;
+ color: var(--dateButton);
+}
+
+body.dark-mode .dateButton {
+ color: var(--text_darkmode);
+}
+
+/*카운트 숫자*/
+#todo-count {
+ margin-left: 10px;
+ font-size: 14px;
+ color: var(--text_todocount);
+}
+
+/*카운트 item*/
+.count {
+ color: var(--text_countitem);
+ font-size: 14px;
+}
+
+/*입력창 스타일*/
+.inputContainer {
+ box-shadow: 0 3px 10px rgba(0, 0, 0, 0.08);
+ display: flex;
+ border-radius: 1rem;
+ width: 20rem;
+ padding: 10px;
+ margin: 2rem 0 1rem 0;
+ background-color: var(--bg_inputContainer);
+ align-items: center;
+ justify-content: center;
+ position: relative;
+}
+
+.input {
+ border: none;
+ outline: none;
+ text-align: left;
+ flex-grow: 1;
+}
+
+.enter {
+ display: flex;
+ cursor: pointer;
+ border: none;
+ padding: 5px 10px;
+ margin-left: 10px;
+ border-radius: 5px;
+ background-color: var(--bg_enter);
+ font-weight: 500;
+ font-size: 14px;
+}
+
+.enter:active {
+ transform: scale(0.95);
+}
+
+body.dark-mode .inputContainer,
+body.dark-mode .enter,
+body.dark-mode .input {
+ background: var(--bg_inputContainer_darkmode);
+ color: var(--text_darkmode_enter);
+}
+
+/*할 일 리스트 스타일*/
+.container {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-end;
+ gap: 10px;
+ height: 100%;
+ max-height: 500px;
+ overflow-y: scroll;
+ margin-bottom: 100px;
+}
+
+/*할 일 item*/
+.todo {
+ transition: transform 0.2s ease, box-shadow 0.2s ease;
+ box-shadow: 0 2px 6px rgba(0,0,0,0.05);
+ animation: fadeIn 0.2s ease;
+ display: flex;
+ align-items: center;
+ background-color: var(--bg_todoitem);
+ color: var(--text_todoitem);
+ font-weight: 500;
+ font-size: 14px;
+ border-radius: 1rem;
+ width: 20rem;
+ padding: 10px;
+ gap: 10px;
+}
+
+@keyframes fadeIn {
+ from {
+ opacity: 0;
+ transform: translateY(10px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0)
+ }
+}
+
+.todo:hover {
+ transform: translateY(-1px);
+}
+
+.todo.dragging {
+ opacity: 0.3;
+ transform: scale(1.05);
+ box-shadow: 0 5px 15px rgba(0,0,0,0.2);
+}
+
+.todo.placeholder {
+ height: 50px;
+ margin: 10px 0;
+ border: 2px dashed #ccc;
+ border-radius: 1rem;
+ transition: height 0.2s ease;
+}
+
+body.dark-mode .todo {
+ background: var(--bg_todo_darkmode);
+ color: var(--text_darkmode_todo);
+}
+
+/*체크박스 스타일*/
+.todo input[type="checkbox"] {
+ appearance: none;
+ flex-shrink: 0; /*피드백 수정; 왼쪽 체크박스 찌그러짐*/
+ width: 18px;
+ height: 18px;
+ border-radius: 5px;
+ border: 2px solid var(--bg_checkbox);
+ /*display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: all 0.2s ease-in-out;*/
+ cursor: pointer;
+ position: relative;
+ background-color: var(--bg_checkbox);
+}
+
+.todo input[type="checkbox"]:checked {
+ background-color: var(--bg_checkbox);
+}
+
+.todo input[type="checkbox"]:checked::after {
+ content: "✓";
+ position: absolute;
+ color: var(--checkbox);
+ font-size: 14px;
+ left: 3px;
+ top: -2px;
+}
+
+/*할 일 텍스트 스타일*/
+.todo span {
+ flex-grow: 1;
+ font-size: 14px;
+ text-align: left;
+ word-break: break-all; /*피드백 수정; 영문, 숫자의 줄바꿈 안됨*/
+}
+
+/*완료된 할 일 텍스트 스타일*/
+.completed {
+ text-decoration: line-through;
+ color: gray;
+}
+
+/*할 일 삭제 버튼 스타일*/
+.delete {
+ flex-shrink: 0;
+ background: var(--deleteButton);
+ color: var(--text_deleteButton);
+ border: none;
+ padding: 5px 10px;
+ border-radius: 5px;
+ cursor: pointer;
+ transition: background 0.2s;
+}
+
+.delete:hover {
+ background: var(--hover_deleteButton);
+}
+
+/*사이드바 스타일*/
+.sidebar {
+ position: fixed;
+ top: 0;
+ left: -250px;
+ width: 250px;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ background: var(--bg_sidebar);
+ transition: left 0.3s ease-in-out;
+ padding: 0.2rem;
+}
+
+body.dark-mode .sidebar {
+ background: var(--bg_sidebar_darkmode);
+ color: var(--text_darkmode_sidebar);
+}
+
+.sidebar button:not(.close) {
+ display: block;
+ width: 100%;
+ margin: 10px 0;
+ padding: 10px;
+ font-size: 15px;
+ font-weight: 500;
+ background: transparent;
+ color: var(--text_sidebarButton);
+ border: none;
+ cursor: pointer;
+}
+
+/*사이드바 닫기 버튼*/
+.close {
+ width: auto;
+ margin: 20px 0 0 0;
+ align-self: flex-start;
+ border:none;
+ font-size: 19px;
+ cursor: pointer;
+ background-color: transparent;
+ color: var(--closeButton);
+}
+
+.close:hover {
+ transform: scale(1.1);
+}
+
+body.dark-mode .close {
+ color: var(--text_darkmode);
+}
+
+/*주간 버튼 스타일*/
+.weekBtn {
+ display: block;
+ width: 100%;
+ padding: 10px;
+ margin: 5px 0;
+ background: white;
+ color: var(--text);
+ border: none;
+ cursor: pointer;
+}
+
+body.dark-mode .weekBtn {
+ color: var(--text_darkmode);
+}
+
+/*테마 스타일*/
+.theme {
+ width:100%;
+}
+
+body.dark-mode #currentDate {
+ color: var(--text_darkmode);
+}
\ No newline at end of file