기능 계획, 화면 설계, 동작 추가, 데이터 저장, 테스트, 배포까지 단계별로 설명하는 간단한 투두(할일) 앱 만들기 가이드입니다.

이 가이드에서 말하는 “앱”은 작은 웹 앱을 의미합니다: 브라우저에서 열 수 있는 단일 페이지로, 클릭과 입력에 반응합니다. 설치나 계정, 복잡한 설정은 필요 없습니다 — 로컬에서 바로 실행할 수 있는 간단한 프로젝트입니다.
완료하면 다음 기능을 가진 투두 앱을 갖게 됩니다:
localStorage 사용)완벽하거나 기업용 수준일 필요는 없습니다. 이 프로젝트는 초보자가 많은 도구 없이 기본을 배우도록 설계되었습니다.
단계별로 앱을 만들며 프론트엔드 웹 앱의 핵심 요소를 배웁니다:
간단합니다. 필요한 것:
폴더를 만들고 몇 개의 파일을 편집할 수 있다면 준비 완료입니다.
코드를 쓰기 전에 "성공"이 무엇인지 정하세요. 이 튜토리얼은 하나의 명확한 목적을 가진 작은 앱을 만듭니다: 당신이 해낼 작업을 기록하도록 돕는 것.
빌드하는 동안 앞에 두고 볼 수 있는 한 문장 목표를 적으세요:
“이 앱은 작업을 목록에 추가해서 잊지 않게 도와준다.”
그게 전부입니다. 달력, 알림, 태그, 계정 같은 기능은 나중으로 미루세요.
두 개의 짧은 목록을 만드세요:
필수(이번 프로젝트):
localStorage 사용)있으면 좋은 기능(오늘은 불필요): 마감일, 우선순위, 카테고리, 검색, 드래그 앤 드롭, 클라우드 동기화.
필수 기능을 작게 유지하면 실제로 끝낼 가능성이 커집니다.
앱은 한 페이지로 구성할 수 있습니다:
막히지 않도록 구체적으로 정하세요:
이제 프로젝트 파일을 설정할 준비가 되었습니다.
코드를 쓰기 전에 앱의 "집"이 될 깔끔한 폴더를 만듭니다. 처음부터 파일을 정리하면 다음 단계가 편해집니다.
컴퓨터에 todo-app 같은 새 폴더를 만드세요. 이 폴더가 프로젝트 전체를 담습니다.
폴더 안에 세 파일을 만드세요:
index.html(페이지 구조)styles.css(보기와 레이아웃)app.js(동작과 상호작용)파일 확장자가 보이지 않는 경우(.html 등) 실제 파일로 생성되었는지 확인하세요. 초보자가 흔히 겪는 실수는 index.html.txt처럼 저장하는 것입니다.
todo-app 폴더를 코드 편집기(VS Code, Sublime 등)로 열고 index.html을 브라우저에서 엽니다.
지금은 페이지가 비어 있을 수 있는데 괜찮습니다. 다음 단계에서 내용을 추가합니다.
파일을 편집해도(특별한 도구가 없다면) 브라우저는 자동으로 업데이트되지 않습니다.
기본 작업 흐름은:
문제가 생기면 먼저 새로고침을 시도하세요.
더블클릭으로 index.html을 열어도 되지만, 로컬 서버를 쓰면 나중에 파일 로드나 저장 관련 이상한 문제를 예방할 수 있습니다.
초보자에게 친근한 옵션:
python -m http.server
그 후 터미널에 표시되는 주소(보통 http://localhost:8000)를 브라우저에서 엽니다.
이제 앱의 깔끔한 골격을 만들겠습니다. 이 HTML은 아직 상호작용을 제공하지 않지만(그건 다음 단계), JavaScript가 읽고 쓸 명확한 위치를 제공합니다.
다음 요소를 포함합니다:
간단하고 알아보기 쉬운 이름을 쓰세요. 잘 정한 ID/클래스는 JavaScript에서 요소를 쉽게 찾게 해줍니다.
index.html에 붙여넣으세요<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>To‑Do App</title>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<main class="app" id="app">
<h1 class="app__title" id="appTitle">My To‑Do List</h1>
<form class="task-form" id="taskForm">
<label class="task-form__label" for="taskInput">New task</label>
<div =>
Add
이것으로 구조는 끝났습니다. id="taskInput"와 id="taskList"를 주목하세요 — JavaScript에서 가장 많이 사용할 요소들입니다.
지금 페이지는 문서처럼 보일 겁니다. 약간의 CSS로 간격을 정리하고 읽기 쉽게 만들면 사용하기 편합니다.
가운데 정렬된 박스는 화면을 집중시키고 넓은 화면에서 내용이 늘어지는 것을 막습니다.
/* Basic page setup */
body {
font-family: Arial, sans-serif;
background: #f6f7fb;
margin: 0;
padding: 24px;
}
/* Centered app container */
.container {
max-width: 520px;
margin: 0 auto;
background: #ffffff;
padding: 16px;
border-radius: 10px;
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.08);
}
각 작업은 편안한 간격의 "행"처럼 보이게 만드세요.
ul { list-style: none; padding: 0; margin: 16px 0 0; }
li {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
padding: 10px 12px;
border: 1px solid #e7e7ee;
border-radius: 8px;
margin-bottom: 10px;
}
.task-text { flex: 1; }
작업이 완료되면 시각적으로 달라져야 한눈에 알아볼 수 있습니다.
.done .task-text {
text-decoration: line-through;
color: #777;
opacity: 0.85;
}
버튼을 같은 크기와 스타일로 유지하면 앱의 일관성이 좋아집니다.
button {
border: none;
border-radius: 8px;
padding: 8px 10px;
cursor: pointer;
}
button:hover { filter: brightness(0.95); }
여기까지면 깔끔한 UI를 만들기 충분합니다. 다음은 JavaScript로 동작을 연결하는 단계입니다.
이제 입력창, 버튼, 리스트가 있으니 실제로 동작하게 만들어야 합니다. 목표는 간단합니다: 사용자가 작업을 입력하고 추가 버튼을 누르거나 Enter를 치면 새로운 항목이 리스트에 나타나도록 합니다.
JavaScript 파일에서 필요한 요소를 잡고, 버튼 클릭과 입력창에서의 Enter 키를 듣는 리스너를 추가합니다.
const taskInput = document.querySelector('#taskInput');
const addBtn = document.querySelector('#addBtn');
const taskList = document.querySelector('#taskList');
function addTask() {
const text = taskInput.value.trim();
// Block empty tasks (including ones that are just spaces)
if (text === '') return;
const li = document.createElement('li');
li.textContent = text;
taskList.appendChild(li);
// Clear the input and put the cursor back
taskInput.value = '';
taskInput.focus();
}
addBtn.addEventListener('click', addTask);
taskInput.addEventListener('keydown', (event) => {
if (event.key === 'Enter') {
addTask();
}
});
trim()으로 앞뒤 공백을 제거합니다.li를 만들어 텍스트를 넣고 리스트에 추가합니다.아무런 동작이 없다면 HTML의 ID와 JavaScript의 선택자가 정확히 일치하는지 먼저 확인하세요(초보자에게 가장 흔한 문제입니다).
이제 작업을 추가할 수 있으니, 작업을 완료로 표시하고 삭제할 수 있게 만듭니다.
문자열 대신 객체로 작업을 저장하세요. 이렇게 하면 각 작업에 고유한 정체성을 부여하고 "완료" 상태를 추적할 수 있습니다:
text: 작업 내용done: true 또는 falseid: 특정 작업을 찾거나 삭제할 때 쓸 고유 번호예시:
let tasks = [
{ id: 1, text: "Buy milk", done: false },
{ id: 2, text: "Email Sam", done: true }
];
각 작업을 렌더링할 때 체크박스 또는 "완료" 버튼과 "삭제" 버튼을 함께 넣으세요.
이벤트 리스너는 클릭에 반응하는 방법입니다. 버튼(또는 전체 리스트)에 리스너를 달고 사용자가 클릭하면 코드가 실행됩니다.
초보자에게 친절한 패턴은 이벤트 위임(event delegation) 입니다: 리스트 컨테이너 하나에 클릭 리스너를 달고, 클릭된 요소를 확인합니다.
function toggleDone(id) {
tasks = tasks.map(t => t.id === id ? { ...t, done: !t.done } : t);
renderTasks();
}
function deleteTask(id) {
tasks = tasks.filter(t => t.id !== id);
renderTasks();
}
document.querySelector("#taskList").addEventListener("click", (e) => {
const id = Number(e.target.dataset.id);
if (e.target.matches(".toggle")) toggleDone(id);
if (e.target.matches(".delete")) deleteTask(id);
});
renderTasks() 함수에서는:
data-id="${task.id}"를 추가하세요..done 클래스를 추가하여 시각적으로 표시하세요.지금은 새로고침하면 모든 작업이 사라집니다. 이는 작업이 JavaScript 메모리에만 저장되기 때문입니다. 페이지를 새로고침하면 메모리가 초기화됩니다.
localStorage는 브라우저에 내장되어 있습니다. 키(이름) 아래에 텍스트를 저장하는 작은 상자라고 생각하세요. 서버나 계정 없이도 쓸 수 있어 초보자 프로젝트에 적합합니다.
작업 전체 리스트를 JSON 문자열로 저장하고, 페이지가 열릴 때 다시 불러옵니다.
작업을 추가하거나 완료 표시를 바꾸거나 삭제할 때마다 saveTasks()를 호출하세요.
const STORAGE_KEY = "todo.tasks";
function saveTasks(tasks) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(tasks));
}
앱이 tasks 배열을 업데이트할 때마다 다음을 실행하세요:
saveTasks(tasks);
renderTasks(tasks);
페이지가 열릴 때 저장된 값을 읽습니다. 저장된 것이 없으면 빈 리스트로 초기화하세요.
function loadTasks() {
const saved = localStorage.getItem(STORAGE_KEY);
return saved ? JSON.parse(saved) : [];
}
let tasks = loadTasks();
renderTasks(tasks);
이제 앱이 새로고침 후에도 작업을 기억합니다.
팁: localStorage는 문자열만 저장하므로 JSON.stringify()로 배열을 텍스트로 바꾸고, 불러올 때 JSON.parse()로 다시 배열로 만듭니다.
테스트는 지루해 보이지만, 앱을 "내 머신에서 동작"에서 "항상 동작"으로 만드는 가장 빠른 방법입니다. 작은 변경 후에 빠르게 한 번씩 점검하세요.
주요 흐름을 다음 순서로 실행하세요:
어떤 단계가 실패하면 새로운 기능을 추가하기 전에 고치세요. 작은 앱은 문제를 쌓아두면 복잡해집니다.
설계하지 않았던 입력을 사람들이 실제로 하게 됩니다:
빈 항목을 막는 일반적인 해결책:
const text = input.value.trim();
addButton.disabled = text === "";
(이 코드를 input 이벤트마다 실행하고, 추가하기 직전에도 다시 확인하면 좋습니다.)
동작하지 않을 때 흔한 원인:
id나 클래스 이름 불일치)app.js를 찾지 못함)무작위로 보이는 문제는 로그로 확인하세요:
console.log("Adding:", text);
console.log("Current tasks:", tasks);
브라우저 콘솔에서 오류(빨간 텍스트)를 확인하세요. 문제를 고친 후에는 로그를 지워 프로젝트가 지저분해지지 않게 하세요.
투두 앱은 실제 사람이 편하게 쓸 수 있을 때 "완성"입니다 — 휴대폰, 키보드, 화면 읽기 도구에서 모두 편해야 합니다.
작은 버튼은 모바일에서 짜증납니다. 클릭 가능한 요소에 충분한 공간을 주세요:
CSS에서 padding, font-size, gap을 키우면 큰 차이가 납니다.
화면 읽기 도구에는 명확한 이름이 필요합니다.
label을 사용하세요(가장 좋음). 표시하고 싶지 않으면 CSS로 시각적으로 숨기되 HTML에는 남겨두세요.aria-label="Delete task"처럼 읽어줄 수 있는 문자열을 포함하세요.이렇게 하면 추측 없이 어떤 컨트롤인지 이해할 수 있습니다.
마우스 없이도 앱을 사용할 수 있게 하세요:
form을 사용하면 Enter가 자연스럽게 동작합니다).기본 글꼴 크기는 16px을 권장하고, 색상 대비는 충분히 강하게(어두운 텍스트/밝은 배경 등) 유지하세요. 색상만으로 "완료"를 표시하지 말고 취소선 같은 명확한 스타일을 함께 사용하세요.
모든 기능이 동작하면 10–15분 정도 코드를 정리하세요. 이렇게 하면 나중에 수정하기 쉽고 프로젝트를 다시 볼 때 이해하기 쉽습니다.
작게 그리고 예측 가능하게 유지하세요:
/index.html — 페이지 구조/styles.css — 앱의 스타일/app.js — 동작(추가, 완료 토글, 삭제, 저장/로드)/README.md — 나중의 당신을 위한 간단한 메모서브폴더를 선호하면:
/css/styles.css/js/app.js사용하는 경우 link와 script 경로가 정확한지 확인하세요.
몇 가지 작은 규칙으로 가독성을 높일 수 있습니다:
taskInput, taskList, saveTasks()예를 들어 읽기 쉬운 함수들:
renderTasks(tasks)addTask(text)toggleTask(id)deleteTask(id)README.md는 간단하면 됩니다:
index.html 열기)완료 마일스톤(예: "localStorage 작동")마다 폴더를 압축해 두세요. 버전 관리를 원하면 Git을 쓰는 걸 권장하지만 선택 사항입니다. 한 번의 백업도 실수로 파일을 잃는 것을 막아줍니다.
배포는 앱 파일(HTML, CSS, JS)을 인터넷에 공개해 다른 사람이 링크로 열어 쓸 수 있게 하는 것입니다. 이 투두 앱은 정적 사이트이므로 무료로 호스팅할 수 있는 곳이 많습니다.
대략적인 단계:
/) 선택별도 파일을 쓴 경우 파일명이 링크와 정확히 일치하는지 확인하세요(예: styles.css vs style.css).
가장 쉬운 업로드 방법:
localStorage 작동)통과하면 친구에게 링크를 보내어 테스트해 달라고 하세요 — 제3자의 사용에서 문제를 쉽게 발견할 수 있습니다.
작동하는 투두 앱을 만들었습니다. 다음 단계로 넘어가지 않고도 배움을 이어갈 수 있는 업그레이드입니다. 가치도 있고 유용한 패턴을 가르쳐줍니다.
각 작업 옆에 "편집" 버튼을 추가하세요. 클릭하면 라벨을 작은 입력 필드로 바꾸고(값 미리 채우기) "저장"과 "취소"를 표시합니다.
팁: 작업 데이터를 id와 text를 가진 객체 배열로 유지하세요. 편집은 id로 작업을 찾아 text를 바꾸고 다시 렌더링한 뒤 저장하면 됩니다.
상단에 All, Active, Done 세 개 버튼을 추가하세요.
현재 필터를 currentFilter = 'all' 같은 변수에 저장합니다. 렌더링 시:
가볍게 추가하세요:
YYYY-MM-DD)을 추가해 작업 옆에 보여주기한 개의 필드만 추가해도 데이터 모델과 UI를 함께 업데이트하는 법을 배울 수 있습니다.
준비되면 큰 아이디어는: localStorage 대신 API(서버)에 작업을 전송하는 것입니다. 서버는 데이터베이스에 저장하므로 기기 간 동기화가 가능합니다.
그 다음 단계로 바로 건너뛰지 않고 싶다면, Koder.ai 같은 프로토타이핑 도구로 기능(엔드포인트, DB 테이블, UI 변경)을 채팅으로 설명하고 빠르게 시제품을 만들며 React/Go/PostgreSQL 같은 스택을 내보낼 수 있습니다.
검색 기능이 있는 노트 앱이나 습관 추적기(매일 체크) 같은 프로젝트를 시도해 보세요. 리스트 렌더링, 편집, 저장, UI 설계 같은 동일한 기술을 재사용합니다.