-
HTML+Javascript+CSS ๊ธฐ๋ณธ ์ค์ตIT๊ธฐ์ 2025. 3. 24. 08:24
๐ง๐ป ์ฝ๋ ๋ฆฌ๋ทฐ – To-Do List ํ๋ก์ ํธ ์ ์ฒด ์ ๊ฒ โ ๐
๊ตฌ์ฑ: HTML + CSS + JS
๊ธฐ๋ฅ: ๊ธฐ๋ณธ์ ์ธ ์ฒดํฌ๋ฐ์ค ๊ธฐ๋ฐ ํ ์ผ ๋ฆฌ์คํธ + ์ ํ ์ญ์ + ์ ์ฒด ์ญ์
1๏ธโฃ HTML ๋ฆฌ๋ทฐ
<!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8"> <title>To-Do List</title> <link rel="stylesheet" href="style.css"> </head> <body> <h1>๐ ํ ์ผ ๋ฆฌ์คํธ</h1> <div class="input-area"> <input type="text" id="todoInput" placeholder="ํ ์ผ์ ์ ๋ ฅํ์ธ์"> <button onclick="addItem()">โ ์ถ๊ฐ</button> <!-- <button onclick="addItem()">โ ์์ดํ ์ถ๊ฐ</button> --> <button onclick="removeItem()">โ ์ญ์ </button> <button onclick="removeAllItem()">โ ์ ์ฒด์ญ์ </button> </div> <ul id="todoList"></ul> <script src="script.js"></script> </body> </html>
โ ์ฅ์
- ๊ตฌ์กฐ ๊ฐ๊ฒฐ, ์๋งจํฑ ํ๊ทธ ์ฌ์ฉ ์ ์ (<input>, <button>, <ul>)
- lang="ko" ๋ฐ charset="UTF-8" ์ธํ ์๋ฒฝ
- ๋ฒํผ ํ ์คํธ + ์ด๋ชจ์ง ์ฌ์ฉ์ UX ์ธก๋ฉด์์ ์ง๊ด์ ๐
๐ง ๊ฐ์ ์ ์
์์น ๊ฐ์ ํฌ์ธํธ ์ด์
<button onclick="..."> onclick ๋์ addEventListener๋ฅผ JS์์ ์ฌ์ฉ ์ ์ง๋ณด์์ฑ ํฅ์, HTML ๋ถ๋ฆฌ ์์น <script src="script.js"> defer ์์ฑ ์ถ๊ฐ ์ถ์ฒ DOM ๋ก๋ ์ดํ ์คํ๋๋๋ก ๋ณด์ฅ (<script defer src="...">)
2๏ธโฃ JS ๋ฆฌ๋ทฐ
function addItem() { const input = document.getElementById("todoInput"); const text = input.value.trim(); if (text === "") { alert("ํ ์ผ์ ์ ๋ ฅํด ์ฃผ์ธ์!"); return; } //์ฒดํฌ๋ฐ์ค ์ ์ธ const checkbox = document.createElement("input"); checkbox.type = "checkbox"; //๋ฆฌ์คํธ const list = document.getElementById("todoList"); //๋ฆฌ์คํธ ์์ดํ const item = document.createElement("li"); //item.textContent = text; //์์ดํ ํ ์คํธ const textNode = document.createTextNode(" " + text); // // ํด๋ฆญํ๋ฉด ์ญ์ // item.addEventListener("click", function () { // list.removeChild(item); // }); item.appendChild(checkbox); item.appendChild(textNode); list.appendChild(item); input.value = ""; // ์ ๋ ฅ์ฐฝ ์ด๊ธฐํ } function removeItem() { console.log("์ญ์ ํจ์ ์คํ๋จ"); const list = document.getElementById("todoList"); const items = list.querySelectorAll("li"); items.forEach(item => { console.log("์ญ์ for each๋ฌธ ์คํ์ค"); const checkbox = item.querySelector("input[type='checkbox']"); if (checkbox && checkbox.checked) { list.removeChild(item); } }); } function removeAllItem() { console.log("์ ์ฒด์ญ์ ํจ์ ์คํ๋จ"); // 1. UL ๊ฐ์ ธ์ค๊ธฐ const list = document.getElementById("todoList"); // ์ ์ฒด Item ์ญ์ while (list.firstChild) { list.removeChild(list.firstChild); } // // 2. ๋ชจ๋ ํ ์ผ ํญ๋ชฉ(li) ๊ฐ์ ธ์ค๊ธฐ // const items = list.querySelectorAll("li"); // // 3. ํญ๋ชฉ๋ค ํ๋์ฉ ํ์ธ // items.forEach(item => { // console.log("์ ์ฒด์ญ์ for each๋ฌธ ์คํ ์ค.") // const checkbox = item.querySelector("input[type='checkbox']"); // // 4. ์ฒดํฌ๋์ด ์์ผ๋ฉด ์ญ์ // if (checkbox && checkbox.checked) { // list.removeChild(item); // } // }); } // // ํด๋ฆญํ๋ฉด ์ญ์ // item.addEventListener("click", function () { // list.removeChild(item); // });
โ ๊ธฐ๋ฅ ๊ตฌํ ํ๋ฅญํ ์
- ๊ณต๋ฐฑ ์ ๋ ฅ ๋ฐฉ์ง ๋ก์ง (trim())
- ์ฒดํฌ๋ฐ์ค ์์ฑ ๋ฐ li์ ๋์ ์ถ๊ฐ
- ์ ํ ์ญ์ ์ ์ ์ฒด ์ญ์ ๋ชจ๋ ๋ณ๋ ํจ์๋ก ๋ถ๋ฆฌ ๐ก
- console.log()๋ก ๋๋ฒ๊น ํฌ์ธํธ ํ์ธ ๊ฐ๋ฅ
๐ง ๊ฐ์ ์ ์
์์น ๊ฐ์ ํฌ์ธํธ ์ด์
onclick="addItem()" ๋ฑ JS ๋ด๋ถ์์ addEventListener๋ฅผ ํตํด ๋ฐ์ธ๋ฉํ๋ ๋ฐฉ์ ๊ถ์ฅ HTML๊ณผ JS ๋ถ๋ฆฌ, ์ฌ์ฌ์ฉ์ฑ ์ฆ๊ฐ list.removeChild(item) forEach๋ก ๋ฃจํ ๋๋ฉฐ ์ญ์ ํ ๊ฒฝ์ฐ, ์ญ์์ผ๋ก ์ ๊ฑฐํด์ผ ์์ ํ์ฌ๋ ์ ์ ์๋ํ๋ DOM ๋ณ๊ฒฝ ์ค ์ํ๋ ๋ฒ๊ทธ ์ ๋ฐ ๊ฐ๋ฅ์ฑ checkbox ํ๊ทธ ๋ถ๋ฆฌ ์ ์ธ ๋ณ๋ ํจ์๋ก ๋นผ์ ์ถํ UI ํ์ฅ ๋๋น ์: createTodoItem(text) ๋ฑ
๐ง ๋ฆฌํฉํ ๋ง ์์ (์ด๋ฒคํธ ์ฐ๊ฒฐ)
document.addEventListener('DOMContentLoaded', () => { document.querySelector('button[onclick="addItem()"]').onclick = addItem; document.querySelector('button[onclick="removeItem()"]').onclick = removeItem; document.querySelector('button[onclick="removeAllItem()"]').onclick = removeAllItem; });
๋๋ ์์ querySelectorAll('button')๋ก ์ํํ๋ฉฐ data-action ๊ฐ์ ์ปค์คํ ์์ฑ์ผ๋ก ์ฒ๋ฆฌ๋ ๊ฐ๋ฅํด์.
3๏ธโฃ CSS ๋ฆฌ๋ทฐ
body { font-family: sans-serif; background-color: #f0f0f0; padding: 20px; } .input-area { margin-bottom: 20px; } input { padding: 10px; font-size: 16px; } button { padding: 10px; font-size: 16px; margin-left: 5px; background-color: #4CAF50; color: white; border: none; } button:hover{ background: #66BB6A; cursor: pointer; } ul { list-style: none; padding: 0; } li { background: white; padding: 10px; margin-bottom: 5px; cursor: pointer; transition: 0.2s; display: flex; align-items: center; gap: 8px; } li:hover { background: #ddd; }
โ ๋์์ธ ์ฅ์
- hover, transition, gap, display: flex ํ์ฉ์ผ๋ก UI ๊น๋ํจ ๐จ
- ๋ฒํผ ์คํ์ผ์ด ๋์ ์ ๋ (์์ ์ผ๊ด์ฑ OK)
- li ํญ๋ชฉ๋ ์ฌ์ฉ์ ํผ๋๋ฐฑ ๋ฐ์ ์ข์ (hover ์์ ๋ณ๊ฒฝ)
๐ง ๊ฐ์ ์ ์
์ ํ์ ๊ฐ์ ํฌ์ธํธ ์ด์
input, button ๊ณตํต์ ์ธ ํจ๋ฉ, ํฐํธ ๋ฑ์ .input-area * ๋๋ ๊ณตํต ํด๋์คํ ์ถ์ฒ CSS ์ฝ๋ ์ฌ์ฌ์ฉ์ฑ ํฅ์ ๋ฐฐ๊ฒฝ์ #f0f0f0 ๋คํฌ ๋ชจ๋ ๋๋น ์ :root์ ๋ณ์๋ก ์ฒ๋ฆฌ ๊ณ ๋ ค ํ์ฅ์ฑ / ํ ๋ง ์ ์ฐ์ฑ ํ๋ณด li:hover ์ปค์ ๊ธฐ๋ณธ ์ค์ OK์ง๋ง, ํด๋ฆญ ์ด๋ฒคํธ๊ฐ ์์ผ๋ฏ๋ก pointer ๋ถํ์ UX ๋ช ํ์ฑ
๐ฆ ์ข ํฉ ์ ๋ฆฌ
ํญ๋ชฉ ํ๊ฐ
๊ธฐ๋ฅ ๊ตฌํ โ ๋งค์ฐ ์ถฉ์ค UI/UX โ ์ง๊ด์ ์ด๊ณ ๊น๋ ์ ์ง๋ณด์์ฑ ๐ก ๊ฐ์ ์ฌ์ง ์์ (์ด๋ฒคํธ ๋ฐ์ธ๋ฉ ๋ฐฉ์, ๊ตฌ์กฐ ๋ถ๋ฆฌ) ์ฑ๋ฅ/์์ ์ฑ โ ํ์ฌ ๊ตฌ์กฐ์์๋ ์ถฉ๋ถ ํ์ฅ์ฑ ๐ก ์ปดํฌ๋ํธํ๋ ํ ํ๋ฆฟํ ์๋ ๊ฐ๋ฅ
๐ก ๋ค์ ๋จ๊ณ ์ถ์ฒ
- โ ๊ฐ๋ณ ์์ดํ ์ ์ญ์ ๋ฒํผ ์ถ๊ฐ
- โ localStorage๋ก ์ ์ฅํด์ ์๋ก๊ณ ์นจ ํ์๋ ์ ์ง๋๋๋ก
- โ ์๋ฃ ํญ๋ชฉ ์คํ์ผ(์ทจ์์ , ์์ ๋ณ๊ฒฝ ๋ฑ) ์ถ๊ฐ
- ๐งฉ Vue/React๋ก ๋ฆฌํฉํ ๋งํด๋ณด๊ธฐ
ํ์ํ๋ฉด ์ ์ถ์ฒ ๊ธฐ๋ฅ๋ค๋ ๋น ๋ฅด๊ฒ ๊ตฌํ ๋์์ค๊ฒ์ ๐ง๐ป
์ํ๋ ๊ธฐ๋ฅ ํค์๋๋ง ๋งํด์ค!'IT๊ธฐ์ ' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
๋ฆฌํฐ๋ด ๋ฌธ๋ฒ ์ค๋ฅ (๋ฐฑํฑ) (0) 2025.03.24 Javacript ์์น (์ธ๋ผ์ธ ์คํฌ๋ฆฝํธ vs ์ธ๋ถ ์คํฌ๋ฆฝํธ) (0) 2025.03.24 DB Migration (MariaDB to MSSQL) (0) 2025.03.14 vlan๊ณผ Trunc (0) 2022.05.23 Putty ํ๋ฉด์ค์ (0) 2022.05.18