프로젝트 소개 및 목표
🔗 관련 에러 해결 가이드
이 30분만에 만드는 Todo App 완성 가이드는 프론트엔드 개발을 시작하는 분들을 위한 실전 프로젝트입니다. Todo 애플리케이션은 CRUD(Create, Read, Update, Delete) 기능을 모두 포함하고 있어 웹 개발의 핵심 개념을 익히기에 완벽한 프로젝트입니다. 이 가이드를 따라하면 할 일 추가, 완료 체크, 삭제, 필터링 기능을 가진 완전한 Todo 앱을 만들 수 있습니다. 로컬 스토리지를 활용하여 데이터를 저장하므로 백엔드 없이도 실용적인 애플리케이션을 완성할 수 있습니다. 초보자도 쉽게 따라할 수 있도록 단계별로 상세히 설명하며, 완성 후에는 포트폴리오에 추가할 수 있는 수준의 프로젝트가 됩니다.
필요한 기술 스택
이 프로젝트를 완성하기 위해 필요한 기술은 다음과 같습니다:
- HTML5: 애플리케이션의 구조를 만듭니다
- CSS3: 스타일링과 반응형 디자인을 구현합니다
- JavaScript (ES6+): 동적인 기능과 상호작용을 처리합니다
- Local Storage API: 브라우저에 데이터를 저장합니다
별도의 프레임워크나 라이브러리 없이 순수 JavaScript(Vanilla JS)로 구현하므로, 기본기를 탄탄히 다질 수 있습니다. 텍스트 에디터(VS Code 추천)와 최신 웹 브라우저만 있으면 시작할 수 있습니다.
프로젝트 셋업
먼저 프로젝트 폴더를 생성하고 필요한 파일을 준비합니다:
mkdir todo-app
cd todo-app
touch index.html style.css app.js
프로젝트 구조는 다음과 같이 간단합니다:
todo-app/
├── index.html
├── style.css
└── app.js
모든 파일을 같은 폴더에 배치하여 관리의 편의성을 높입니다. VS Code를 사용한다면 Live Server 확장 프로그램을 설치하여 실시간으로 변경사항을 확인할 수 있습니다. 이제 각 파일을 단계별로 작성해보겠습니다.
단계별 구현 과정
1단계: HTML 구조 만들기
index.html 파일에 다음 코드를 작성합니다:
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>30분 Todo App</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container">
<h1>나의 할 일 목록</h1>
<div class="input-section">
<input type="text" id="todoInput" placeholder="할 일을 입력하세요...">
<button id="addBtn">추가</button>
</div>
<div class="filter-section">
<button class="filter-btn active" data-filter="all">전체</button>
<button class="filter-btn" data-filter="active">진행중</button>
<button class="filter-btn" data-filter="completed">완료</button>
</div>
<ul id="todoList"></ul>
<div class="stats">
<span id="totalCount">전체: 0</span>
<span id="activeCount">진행중: 0</span>
</div>
</div>
<script src="app.js"></script>
</body>
</html>
HTML 구조는 입력 섹션, 필터 버튼, 할 일 목록, 통계 정보로 구성됩니다.
2단계: CSS 스타일링
style.css 파일에 다음 스타일을 추가합니다:
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
}
.container {
background: white;
border-radius: 15px;
padding: 30px;
width: 100%;
max-width: 500px;
box-shadow: 0 10px 40px rgba(0,0,0,0.2);
}
h1 {
color: #333;
text-align: center;
margin-bottom: 30px;
}
.input-section {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
#todoInput {
flex: 1;
padding: 12px;
border: 2px solid #e0e0e0;
border-radius: 8px;
font-size: 16px;
outline: none;
transition: border-color 0.3s;
}
#todoInput:focus {
border-color: #667eea;
}
#addBtn {
padding: 12px 24px;
background: #667eea;
color: white;
border: none;
border-radius: 8px;
cursor: pointer;
font-weight: bold;
transition: background 0.3s;
}
#addBtn:hover {
background: #5568d3;
}
.filter-section {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
.filter-btn {
flex: 1;
padding: 8px;
background: #f5f5f5;
border: none;
border-radius: 6px;
cursor: pointer;
transition: all 0.3s;
}
.filter-btn.active {
background: #667eea;
color: white;
}
#todoList {
list-style: none;
margin-bottom: 20px;
}
.todo-item {
background: #f9f9f9;
padding: 15px;
border-radius: 8px;
margin-bottom: 10px;
display: flex;
align-items: center;
gap: 10px;
transition: transform 0.2s;
}
.todo-item:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}
.todo-item.completed .todo-text {
text-decoration: line-through;
color: #999;
}
.todo-checkbox {
width: 20px;
height: 20px;
cursor: pointer;
}
.todo-text {
flex: 1;
font-size: 16px;
}
.delete-btn {
background: #ff6b6b;
color: white;
border: none;
padding: 6px 12px;
border-radius: 5px;
cursor: pointer;
transition: background 0.3s;
}
.delete-btn:hover {
background: #ee5a52;
}
.stats {
display: flex;
justify-content: space-around;
padding: 15px;
background: #f5f5f5;
border-radius: 8px;
font-weight: bold;
color: #555;
}
3단계: JavaScript 기능 구현
app.js 파일에 핵심 로직을 작성합니다:
// 전역 변수
let todos = JSON.parse(localStorage.getItem('todos')) || [];
let currentFilter = 'all';
// DOM 요소
const todoInput = document.getElementById('todoInput');
const addBtn = document.getElementById('addBtn');
const todoList = document.getElementById('todoList');
const filterBtns = document.querySelectorAll('.filter-btn');
const totalCount = document.getElementById('totalCount');
const activeCount = document.getElementById('activeCount');
// 초기화
document.addEventListener('DOMContentLoaded', () => {
renderTodos();
updateStats();
});
// 할 일 추가
addBtn.addEventListener('click', addTodo);
todoInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') addTodo();
});
function addTodo() {
const text = todoInput.value.trim();
if (text === '') {
alert('할 일을 입력해주세요!');
return;
}
const todo = {
id: Date.now(),
text: text,
completed: false,
createdAt: new Date().toISOString()
};
todos.push(todo);
saveTodos();
todoInput.value = '';
renderTodos();
updateStats();
}
// 할 일 렌더링
function renderTodos() {
const filteredTodos = getFilteredTodos();
todoList.innerHTML = '';
filteredTodos.forEach(todo => {
const li = document.createElement('li');
li.className = `todo-item ${todo.completed ? 'completed' : ''}`;
li.innerHTML = `
${todo.text}
`;
todoList.appendChild(li);
});
}
// 할 일 토글
function toggleTodo(id) {
todos = todos.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
);
saveTodos();
renderTodos();
updateStats();
}
// 할 일 삭제
function deleteTodo(id) {
if (confirm('정말 삭제하시겠습니까?')) {
todos = todos.filter(todo => todo.id !== id);
saveTodos();
renderTodos();
updateStats();
}
}
// 필터링
filterBtns.forEach(btn => {
btn.addEventListener('click', () => {
filterBtns.forEach(b => b.classList.remove('active'));
btn.classList.add('active');
currentFilter = btn.dataset.filter;
renderTodos();
});
});
function getFilteredTodos() {
switch(currentFilter) {
case 'active':
return todos.filter(todo => !todo.completed);
case 'completed':
return todos.filter(todo => todo.completed);
default:
return todos;
}
}
// 통계 업데이트
function updateStats() {
const total = todos.length;
const active = todos.filter(todo => !todo.completed).length;
totalCount.textContent = `전체: ${total}`;
activeCount.textContent = `진행중: ${active}`;
}
// 로컬 스토리지 저장
function saveTodos() {
localStorage.setItem('todos', JSON.stringify(todos));
}
이 코드는 할 일 추가, 완료 체크, 삭제, 필터링, 통계 표시 기능을 모두 포함합니다. Local Storage를 활용하여 페이지를 새로고침해도 데이터가 유지됩니다.
4단계: 고급 기능 추가 (선택사항)
시간이 남는다면 다음 기능을 추가해보세요:
- 수정 기능: 더블클릭으로 할 일 수정
- 드래그 앤 드롭: 순서 변경
- 다크모드: 테마 전환 버튼
- 우선순위: 중요도 표시
테스트 및 배포
로컬 테스트
브라우저에서 index.html 파일을 열어 다음 항목을 테스트합니다:
- 할 일 추가가 정상적으로 작동하는지 확인
- 체크박스 클릭 시 완료 상태가 토글되는지 확인
- 삭제 버튼이 정상적으로 작동하는지 확인
- 필터 버튼(전체/진행중/완료)이 올바르게 작동하는지 확인
- 페이지 새로고침 후에도 데이터가 유지되는지 확인
- 통계가 정확하게 표시되는지 확인
배포하기
GitHub Pages를 이용한 무료 배포:
git init
git add .
git commit -m "Todo App 완성"
git branch -M main
git remote add origin https://github.com/username/todo-app.git
git push -u origin main
GitHub 저장소 설정에서 Pages를 활성화하면 몇 분 내에 앱이 배포됩니다. Netlify나 Vercel을 사용하면 더욱 간편하게 배포할 수 있으며, 드래그 앤 드롭만으로 배포가 완료됩니다.
마무리 및 확장 아이디어
축하합니다! 30분만에 만드는 Todo App 완성 가이드를 따라 완전한 Todo 애플리케이션을 만들었습니다. 이 프로젝트를 통해 DOM 조작, 이벤트 처리, Local Storage 활용 등 실무에서 필요한 핵심 기술을 익혔습니다.
확장 아이디어:
- 백엔드 API 연동 (Node.js + Express + MongoDB)
- 사용자 인증 기능 추가
- React나 Vue.js로 리팩토링
- PWA(Progressive Web App)로 변환
- 마감일과 알림 기능 추가
- 카테고리별 분류 기능
이 프로젝트는 포트폴리오에 추가하기 좋으며, 다양한 방향으로 확장하여 더 복잡한 애플리케이션으로 발전시킬 수 있습니다. 계속해서 새로운 기능을 추가하며 실력을 향상시켜보세요!
📚 함께 읽으면 좋은 글
30분만에 만드는 Todo App 완성 가이드 – 완성까지 한번에!
📅 2025. 10. 21.
🎯 30분만에 만드는 Todo App 완성 가이드
30분만에 만드는 Todo App 완성 가이드 – 완성까지 한번에!
📅 2025. 10. 20.
🎯 30분만에 만드는 Todo App 완성 가이드
30분만에 만드는 Todo App 완성 가이드 – 완성까지 한번에!
📅 2025. 10. 15.
🎯 30분만에 만드는 Todo App 완성 가이드
30분만에 만드는 Todo App 완성 가이드 – 완성까지 한번에!
📅 2025. 10. 10.
🎯 30분만에 만드는 Todo App 완성 가이드
30분만에 만드는 Todo App 완성 가이드 – 완성까지 한번에!
📅 2025. 10. 5.
🎯 30분만에 만드는 Todo App 완성 가이드
💡 위 글들을 통해 더 깊이 있는 정보를 얻어보세요!
📢 이 글이 도움되셨나요? 공유해주세요!
여러분의 공유 한 번이 더 많은 사람들에게 도움이 됩니다 ✨
🔥 공유할 때마다 블로그 성장에 큰 힘이 됩니다! 감사합니다 🙏
💬 여러분의 소중한 의견을 들려주세요!
30분만에 만드는 Todo App 완성 가이드 관련해서 궁금한 점이 더 있으시다면 언제든 물어보세요!
⭐ 모든 댓글은 24시간 내에 답변드리며, 여러분의 의견이 다른 독자들에게 큰 도움이 됩니다!
🎯 건설적인 의견과 경험 공유를 환영합니다 ✨
🔔 블로그 구독하고 최신 글을 받아보세요!
🌟 프로젝트 아이디어부터 다양한 실생활 정보까지!
매일 새로운 유용한 콘텐츠를 만나보세요 ✨
📧 RSS 구독 | 🔖 북마크 추가 | 📱 모바일 앱 알림 설정
지금 구독하고 놓치는 정보 없이 업데이트 받아보세요!