프론트엔드 개발 챌린지 3일차의 미션은 'TODO list' 제작하기였다.
조건은 투두리스트에 항목을 추가하고 각 항목마다 체크 표시를 할 수 있으며 체크된 항목만 보거나 미완료된 항목만 보거나 전체 보기 등 필터링을 할 수 있어야한다.
또한, 각 항목을 추가할 때 우선순위를 지정할 수 있어야한다. 이 우선순위는 투두리스트에 표시해야 한다.
깃헙 배포링크 : https://aengzu.github.io/Todolist/
깃헙 소스코드 : https://github.com/aengzu/Todolist
우선 피그마를 사용하여 어떤 디자인으로 제작할 지 틀을 잡아보았다.
스마트한 느낌을 내고 싶어서 민트, 보라, 연두를 사용했다.
디자인은 어려운 것 같다..
디자인을 토대로 먼저 화면 구성을 진행하였다. 그 후 기능 구현을 하였다.
완성 화면은 다음과 같다.
아래 링크에서 실행해볼 수 있다.
https://aengzu.github.io/Todolist/
코드는 다음과 같다. 코드 설명은 시간적 여유가 있을 때 추가할 예정이다.
<!DOCTYPE html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="./style.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">
<title>ToDo List</title>
</head>
<body>
<div class="box">
<div class="row">
<div class="col-4">
<div id="logo"></div>
</div>
<div class="col-3"></div>
<div class="col-5">
<div class="row">
<button type="button" class="btn filter mb-2" style="background-color: white;" id="all">전체</button>
<button type="button" class="btn filter mb-2" style="background-color: white;" id="completed">완료</button>
<button type="button" class="btn filter mb-2" style="background-color: white;" id="pending">미완료</button>
</div>
</div>
</div>
<div class="row-3 mt-3 mb-3">
<input id="todo" class="col" style="border:none; background: none;" placeholder=" 할 일을 작성해주세요." ></input>
<select id="priority" class="col">
<option value="" disabled selected style="color: gray;">우선순위</option>
<option value="low">낮음</option>
<option value="medium">보통</option>
<option value="high">높음</option>
<option value="very-high">아주 높음</option>
</select>
<button type="button" id = "addButton" class="col btn-sm btn" style="background-color: #5193F5;">입력</button>
<div class="mt-1" id="line"></div>
</div>
<div class="row-9">
<div class="memopad">
<!-- 할 일 목록이 여기에 추가됩니다 -->
</div>
</div>
</div>
</body>
<script src="./main.js"></script>
</html>
.box {
margin-top: 100px;
margin-left: auto;
margin-right: auto;
width: 440px;
height: auto; /* 높이를 자동으로 조정하여 내용이 많아질 때 박스가 확장되도록 변경 */
border-radius: 10px;
padding: 30px;
background: linear-gradient( to top, #8EFBE1, #B2BFFF );
box-shadow: 4px 4px 3px grey;
}
.memopad {
background-color: #FFF8EB;
width: 380px;
height: auto; /* 내용에 따라 높이가 조정되도록 변경 */
min-height: 226px; /* 최소 높이 설정 */
border-radius: 10px;
box-shadow: 3px 3px 3px grey;
overflow-y: auto; /* 내용이 많을 경우 스크롤 가능 */
padding: 10px;
}
/* 할 일 목록 스타일 */
.task {
background-color: white;
border-radius: 5px;
padding: 5px 10px;
margin-bottom: 5px;
display: flex;
align-items: center;
justify-content: space-between;
}
/* 우선순위별 배경색 */
.priority-low { background-color: #F2F2FF; } /* 낮음 */
.priority-medium { background-color: #CED0FF; } /* 보통 */
.priority-high { background-color: #B4B7FF; } /* 높음 */
.priority-very-high { background-color: #8489F9; } /* 아주 높음 */
/* 체크박스와 할 일 텍스트 스타일 */
.task-checkbox {
margin-right: 10px;
}
.task-text {
flex-grow: 1;
}
/* 수정 및 삭제 버튼 스타일 */
.edit, .delete {
background-color: #E7DFDF;
border: none;
border-radius: 3px;
padding: 5px 10px;
margin-left: 5px;
cursor: pointer;
}
/* 필터 버튼 스타일 */
.filter {
background-color: #E7DFDF;
box-shadow: 3px 3px 3px #AFB2B5;
border: none;
border-radius: 3px;
padding: 5px 10px;
margin: 0 5px;
cursor: pointer;
}
/* 할 일 추가 및 우선순위 드롭다운 스타일 */
#addButton, #priority {
border: 1px solid #ccc;
border-radius: 3px;
padding: 5px 10px;
}
.filter-selected {
background-color: #C9FFBB !important; /* 선택된 필터 버튼의 배경색 */
}
#logo {
width: 149px;
height: 158px;
background-repeat: no-repeat;
background-image: url(./res/TODO.png);
}
#line {
width: 375.01px;
height: 2px;
background-color: white;
}
document.addEventListener('DOMContentLoaded', () => {
const addButton = document.getElementById('addButton'); // 할 일 추가 버튼
const todoInput = document.getElementById('todo'); // 할 일 입력 필드
const prioritySelect = document.getElementById('priority'); // 우선순위 선택 필드
const taskList = document.querySelector('.memopad'); // 할 일 목록을 표시할 요소
const filterButtons = document.querySelectorAll('.filter'); // 필터 버튼
// 할 일 추가
addButton.addEventListener('click', () => {
const task = todoInput.value;
const priority = prioritySelect.value;
addTask(task, priority);
todoInput.value = '';
});
// 우선순위 변경에 따른 클래스 변경
prioritySelect.addEventListener('change', () => {
switch(prioritySelect.value) {
case 'low':
prioritySelect.style.backgroundColor = '#F2F2FF'; // 낮음에 대한 색상
break;
case 'medium':
prioritySelect.style.backgroundColor = '#CED0FF'; // 보통에 대한 색상
break;
case 'high':
prioritySelect.style.backgroundColor = '#B4B7FF'; // 높음에 대한 색상
break;
case 'very-high':
prioritySelect.style.backgroundColor = '#8489F9'; // 아주 높음에 대한 색상
break;
default:
prioritySelect.style.backgroundColor = 'white'; // 기본 색상
}
});
// 할 일 추가 함수
function addTask(task, priority) {
const taskItem = document.createElement('div');
taskItem.className = `task priority-${priority}`;
taskItem.innerHTML = `
<input type="checkbox" class="task-checkbox">
<span class="task-text">${task}</span>
<button class="edit">수정</button>
<button class="delete">삭제</button>
`;
taskItem.querySelector('.delete').addEventListener('click', () => {
taskItem.remove();
});
taskItem.querySelector('.edit').addEventListener('click', () => {
const newText = prompt('할 일 수정:', taskItem.querySelector('.task-text').textContent);
if (newText) {
taskItem.querySelector('.task-text').textContent = newText;
}
});
taskList.appendChild(taskItem);
}
// 필터링 함수
function filterTasks(filter) {
const tasks = document.querySelectorAll('.task');
tasks.forEach(task => {
const isChecked = task.querySelector('.task-checkbox').checked;
switch(filter) {
case 'all':
task.style.display = 'flex';
break;
case 'completed':
if (isChecked) {
task.style.display = 'flex';
} else {
task.style.display = 'none';
}
break;
case 'pending':
if (!isChecked) {
task.style.display = 'flex';
} else {
task.style.display = 'none';
}
break;
}
});
}
// 필터 버튼 이벤트 리스너
filterButtons.forEach(button => {
button.addEventListener('click', () => {
filterButtons.forEach(btn => btn.classList.remove('filter-selected'));
button.classList.add('filter-selected');
filterTasks(button.id);
});
});
});
처음에는 전체 챌린지 리포지토리 내부의 폴더에서 작업을 해서 배포를 하려고 했으나 404 error 가 떴다.
페이지 배포는 해당 리포지토리에서 Settings 를 누르고, Pages 를 누르고, branch 를 main 으로 경로는 root 로 하고 save 를 누르면 몇 분 뒤에 생성된다.
처음에 404 에러가 뜬 것은 아마 루트 경로에 index.html 이 없어서라고 생각된다.
원래 리포지토리 : https://github.com/aengzu/FE-Challenge
그래서 새로운 리포지토리를 만들어서 루트 경로에 index.html 이 위치할 수 있도록 하였다.
새로 만든 리포지토리 : https://github.com/aengzu/Todolist
++ 그리고 새로운 리포지토리에서 배포하려니 갑자기 흰 화면으로만 떠서 당황했는데, index.html 에 이 태그를 까먹어서인 것 같다. 주의하자!