몽땅뚝딱 개발자

[JAVA/Spring Boot] TodoList 만들기(5) - 스크립트 본문

Development/Spring Framework

[JAVA/Spring Boot] TodoList 만들기(5) - 스크립트

레오나르도 다빈츠 2021. 6. 10. 17:53

 

예전에 Vue.js를 사용하는 프로젝트 인터뷰에서 'ES5와 ES6의 차이점을 알고계신가요?'란 질문을 받았는데

그 때 나는 제이쿼리도 써본적이 없고 ES5나 ES6는 들어본적도 없다고 대답했다.

 

인터뷰가 끝나고 처음으로 둘의 차이를 찾아보며 매일 매일 주구장창 쓰고있던게 ES5란 사실을 알았다.

게다가 내가 자바스크립트라 믿으며 작성한 코드가 사실 자바스크립트와 제이쿼리를 모두 같이 사용한 문법이었다.

인터뷰 본 곳은 ES6 문법을 사용했는데, 일하게되면서 자연스럽게 그 차이를 한번 더 느끼게 됐다.

 

그때까지는 제이쿼리, 자바스크립트, ES5와 ES6의 개념이나 차이를 전혀 모른채로 사용하고 있었다.

await, async를 사용하는 것과 변수를 선언할 때 var가 아닌 let, const를 사용하는 방법도 ES6와 관련된 것이었고,

JSON을 지원하는 것도 ES5 부터였다.

 

그 질문을 계기로 몰라도 사용할 수는 있겠지만 알고쓰는 것과 모르고 쓰는 것은 정말 큰 차이가 있다는 걸 느꼈다.

이후로 전부는 아니더라도 적어도 원리정도는 파악해보려고 노력했고, 공식문서를 찾아보는 습관이 생겼다.

 

아래는 ES5와 ES6의 w3schools에 게시된 문서이다.

 

 

 

◽ ES5

 

JavaScript ES5

ECMAScript 2009 - ES5 ECMAScript 2009, also known as ES5, was the first major revision to JavaScript. This chapter describes the most important features of ES5. ES5 Features "use strict" String.trim() Array.isArray() Array.forEach() Array.map() Array.filte

www.w3schools.com

 

◽ ES6

 

JavaScript ES6

ECMAScript 2015 - ES6 ECMAScript 6 was the second major revision to JavaScript. ECMAScript 6 is also known as ES6 and ECMAScript 2015. This chapter describes the most important features of ES6. New Features in ES6 Browser Support for ES6 (ECMAScript 2015)

www.w3schools.com

 

 

 


 

 

 

제이쿼리 역시 자바스크립트이지만 차이점은 서술하는 방식에 있다.

제이쿼리는 자바스크립트보다 좀 더 간결하게 표현할 수 있으며  지원하는 기능이 많은 자바스크립트 라이브러리이다.

 

예를 들어 엘리먼트 하나를 찾을 때도 자바스크립트는 getElementById()나 getElementsByClassName() 등으로 찾지만

제이쿼리는 선택자인 $()로 깔끔하게 찾을 수 있다.

 

 

 

 


 

 

 

📄 common.js

	$(document).ready(function() {
		initCheckBoxClickEvent();
		initSubTab();
		showDeleteBtn();
		setTime();
		setInterval(setTime, 1000); // 1초마다 실행
		
	})
	
	function initSubTab() {
		let listEl = $(".subTab > li");
		listEl.click(function() {
			listEl.removeClass("active");
			$(this).addClass("active");
		})
	}
	
	// list에 마우스 오버 시 삭제버튼 노출
	function showDeleteBtn() {
		$(".list > li").each(function(index, item) {
			$(item).mouseover(function() {
				$(item).children("button").css("display", "inline");
			});
			$(item).mouseout(function() {
				$(item).children("button").css("display", "none");
			});
		});
	}
	
	// 삭제버튼 클릭 시 TODO 삭제
	function deleteTodo(todoIndex) {
		
		let subType = document.getElementsByClassName("active")[0].value;
		
		$.ajax({
			url: '/delete/todoList',
			method: 'POST',
			async: true,
			data: {
				"idx" : todoIndex,
				"searchType" : subType
			},
			success: function(data) {
				refreshList(data);
			}
		})
	}
	
	// 상단 시계에 시간 설정
	function setTime() {

		const time = new Date();
		let hour = time.getHours().toString();
		let minutes = time.getMinutes().toString();

		hour = hour.length == 1 ? "0" + hour : hour;
		minutes = minutes.length == 1 ? "0" + minutes : minutes;
	    
		document.getElementById("currentDate").value = new Date().toISOString().substring(0, 10);
		document.getElementById('box_hour1').innerHTML = hour.substring(0, 1);
		document.getElementById('box_hour2').innerHTML = hour.substring(1, 2);
		document.getElementById('box_minite1').innerHTML = minutes.substring(0, 1);
		document.getElementById('box_minite2').innerHTML = minutes.substring(1, 2);
	}
	
	
	// 체크박스 초기화
	function initCheckBoxClickEvent() {
		$("input[type='checkbox']").on('click', function() {
			// 체크여부
			let flag = $(this).prop("checked");
			
			let todoIndex = $(this).val();
			let subType = document.getElementsByClassName("active")[0].value;
			
			$.ajax({
				url: '/update/todoList',
				method: 'GET',
				async: true,
				data: {
					"idx" : todoIndex,
					"searchType" : subType
				},
				success: function(data) {
					if(flag) {
						$(".list" + todoIndex).addClass('checked');
					} else {
						$(".list" + todoIndex).removeClass('checked');
					}
					
					refreshList(data);
				}
			})
		});
	}
	
	// [전체/활성화/완료] 서브 영역 클릭 시 타입에 따라 리스트 refresh
	function changeType(type) {
		console.log("현재 타입 >>> ", type);
		$.ajax({
			url: '/select/todoList',
			method: 'POST',
			data: {
				"searchType": type
			},
			success: function(data) {
				refreshList(data);
			}
		})
	}
	
	// add 버튼 클릭 시 프레임을 추가하고 TODO가 작성된 input[type="text"] 검사
	function clickAddBtn() {
		// 이미 추가용 프레임이 존재하는 경우
		if($(".newTodoFrame").length) {
			// 텍스트 박스에 값이 존재할 때
			if($(".newTodoContents").val().length > 0) {
				let flag = confirm("이미 할 일이 존재합니다. 새로 작성하시겠습니까?");
				if(flag) {
					cancleAdd();
					addNewTodo();
				}
			// 텍스트에 값이 존재하지 않을 때
			} else {
				cancleAdd();
				addNewTodo();
			}
		
		// 프레임이 없는 경우
		} else {
			addNewTodo();
		}
	}
	
	// add 버튼 클릭 시 TODO 추가
	function addTodo() {
		let contents = $(".newTodoContents").val();
		let subType = document.getElementsByClassName('active')[0].value;
		
		if(contents.length == 0) {
			alert("값을 입력해주세요.");
		} else {
			$.ajax({
				url: "/insert/todoList",
				method: 'GET',
				async: true,
				data: {
					"contents" : contents,
					"searchType" : subType 
				},
				success : function(data) {
					if(data.isSuccess > 0) {
						alert("등록완료되었습니다.");
						refreshList(data);
						initCheckBoxClickEvent();
					}
				}
			})
		}
	}
	
	// 각 function에서 ajax 호출 후 TODO 목록 다시 그리기
	function refreshList(data) {
		let todoList = data.todoList;
		$(".list").empty();
		for(let i=0; i<todoList.length; i++) {
			let checked = data.todoList[i].complete_yn == 'Y' ? 'checked' : '';
			$(".list").append('<li class="list' + todoList[i].idx + ' ' + checked + '"></li>');
			$(".list" + todoList[i].idx).append('<input type="checkbox" value="' + todoList[i].idx + '" id="middle' + todoList[i].idx + '" ' + checked + '>');
			$(".list" + todoList[i].idx).append('<label for="middle' + todoList[i].idx + '">' + todoList[i].contents + '</label>');
			$(".list" + todoList[i].idx).append('<button class="delBtn" onclick="deleteTodo(' + todoList[i].idx + ')" style="display: none;">삭제</button>');
		}
		
		initCheckBoxClickEvent();
		showDeleteBtn();
	}
	
    // 새로운 TODO 리스트 추가 프레임에서 취소버튼 클릭 시
	function cancleAdd() {
		if($(".newTodoContents").val().length > 0) {
			let flag = confirm("이미 작성된 할 일이 존재합니다. 취소하시겠습니까?");
			if(flag) {
				$(".newTodoFrame").remove();
			}
		} else {
			$(".newTodoFrame").remove();
		}
	}
	
	// add 버튼 클릭 후 새로 추가할 TODO를 작성할 프레임을 추가
	function addNewTodo() {
		if($(".list").children().length > 0) {
			$(".list > li:first-child").before("<li class='newTodoFrame'></li>");
			$(".newTodoFrame").append("<input type='text' class='newTodoContents'>");
			$(".newTodoFrame").append("<div class='btn_wrap'><button onclick='addTodo()'>추가</button><button onclick='cancleAdd()'>취소</button></div>");
		} else {
			$(".list").append("<li class='newTodoFrame'></li>");
			$(".newTodoFrame").append("<input type='text' class='newTodoContents'>");
			$(".newTodoFrame").append("<div class='btn_wrap'><button onclick='addTodo()'>추가</button><button onclick='cancleAdd()'>취소</button></div>");
		}	
	}

 

 

 

 

 

◽ document.ready()

$(document).ready(function() {
	initCheckBoxClickEvent(); // 체크박스 클릭이벤트 초기화
	initSubTab(); // 서브탭 클릭이벤트 초기화
	showDeleteBtn(); // 마우스오버 시 삭제버튼 노출로직 초기화
	setTime(); // 시간을 출력하는 function
	setInterval(setTime, 1000); // 1초마다 실행
})

각 기능에 대한 초기화를 위해 관련 function들을 호출하였다.

setInterval()은 정해진 시간마다 function을 호출하는 기능으로 첫 호출 시 지정한 시간만큼 딜레이가 생기기때문에

setTime()이 1초 뒤에 실행되게 되어 처음에 시간이 초기값으로 출력되는 현상이 생겼다.

그래서 setTime()을 처음부터 호출한 뒤, setInterval()로 1초마다 호출하도록 했다.

 

 

 

 

 

◽ SubTab 클릭 시 .active 클래스를 주는 function

 

 

function initSubTab() {
	let listEl = $(".subTab > li");
	listEl.click(function() {
		listEl.removeClass("active");
		$(this).addClass("active");
	})
}

 

 

 

 

 

◽ TODO list에 마우스 오버 시 삭제버튼을 노출하는 function

 

function showDeleteBtn() {
	$(".list > li").each(function(index, item) {
		$(item).mouseover(function() {
			$(item).children("button").css("display", "inline");
		});
		$(item).mouseout(function() {
			$(item).children("button").css("display", "none");
		});
	});
}

 

 

 

 

 

◽ 삭제 버튼 클릭 시 TODO를 삭제하는 function

function deleteTodo(todoIndex) {
	
	// 헌재 선택되어있는 서브탭의 값 (0: 전체, 1: 활성화, 2: 완료)
	let subType = document.getElementsByClassName("active")[0].value;
	
	$.ajax({
		url: '/delete/todoList',
		method: 'POST',
		async: true,
		data: {
			"idx" : todoIndex,
			"searchType" : subType
		},
		success: function(data) {
			refreshList(data);
		}
	})
}

삭제버튼의 onClick에 deleteTodo()를 달고 파라미터로 삭제할 Todo의 index 값을 받아온다.

새로고침 없이 화면이 리로드가 되어야하기 때문에 ajax를 사용하여 갱신된 리스트를 다시 받아와 화면에 출력하는데

이 때 선택되어있는 서브탭(0: 전체, 1: 활성화, 2: 완료)의 값을 함께 보내어 탭에 맞는 리스트를 가져온다.

 

 

 

 

 

◽ 상단 시계에 시간을 설정하는 function

 

function setTime() {
	const time = new Date();
	let hour = time.getHours().toString();
	let minutes = time.getMinutes().toString();

	// 10보다 작을 경우 앞에 0을 붙여준다.
	hour = hour.length == 1 ? "0" + hour : hour;
	minutes = minutes.length == 1 ? "0" + minutes : minutes;
	
	document.getElementById('currentDate').value = new Date().toISOString().substring(0, 10);
	document.getElementById('box_hour1').innerHTML = hour.substring(0, 1);
	document.getElementById('box_hour2').innerHTML = hour.substring(1, 2);
	document.getElementById('box_minite1').innerHTML = minutes.substring(0, 1);
	document.getElementById('box_minite2').innerHTML = minutes.substring(1, 2);
}

 

현재 날짜와 시/분으로 구성되어 있는 시계가 있는 부분이다.

시간을 출력하는 부분은 시 2개, 분 2개로 div가 각각 하나씩 구성되어 한 글자씩 잘라서 출력해야 했다.

앞에 0이 붙는 오전 시간대나 10분보다 작은 분의 경우에는 앞에 0이 붙도록 삼항연산자를 사용했다.

setTime() 1초마다 실행되어 시간이 바뀌도록 document.ready()에서 setInterval() 함수를 사용했다.

 

// 이렇게 사용하는 방법도 있다.

변수 = new Date().toISOString().slice(start, end);

 

 

 

 

 

◽ 체크박스를 초기화하는 function

function initCheckBoxClickEvent() {
	$("input[type='checkbox']").on('click', function() {
		// 체크여부
		let flag = $(this).prop("checked");
		
		let todoIndex = $(this).val();
		let subType = document.getElementsByClassName("active")[0].value;
		
		$.ajax({
			url: '/update/todoList',
			method: 'GET',
			async: true,
			data: {
				"idx" : todoIndex,
				"searchType" : subType
			},
			success: function(data) {
				if(flag) {
					$(".list" + todoIndex).addClass('checked');
				} else {
					$(".list" + todoIndex).removeClass('checked');
				}
				
				refreshList(data);
			}
		})
	});
}

ajax로 다시 그려진 엘리먼트의 경우 기존의 클릭이벤트가 적용되지 않는 현상이 있었다.

새로 만들어진 엘리먼트에 클릭이벤트를 달아주기 위해 function을 따로 분리하였다.

 

아래 2가지 상황에서 호출한다.

- $(document).ready()

- 리스트를 다시 그린 후

 

 

 

 

 

서브탭 클릭 시 TODO list를 다시 그리는 function

function changeType(type) {
	$.ajax({
		url: '/select/todoList',
		method: 'POST',
		data: {
			"searchType": type
		},
		success: function(data) {
			refreshList(data);
		}
	})
}

서브탭 클릭 시 조회값(0: 전체, 1: 활성화, 2: 완료)에 따라 탭에 맞는 리스트를 가져와 화면에 다시 그린다.

 

 

 

 

 

◽ add 버튼 클릭 시 유효성 검사 후 새로운 프레임을 추가하는 function

 

function clickAddBtn() {
	// 이미 추가용 프레임이 존재하는 경우
	if($(".newTodoFrame").length) {
		// 텍스트 박스에 값이 존재할 때
		if($(".newTodoContents").val().length > 0) {
			let flag = confirm("이미 할 일이 존재합니다. 새로 작성하시겠습니까?");
			if(flag) {
				cancleAdd();
				addNewTodo();
			}
		// 텍스트에 값이 존재하지 않을 때
		} else {
			cancleAdd();
			addNewTodo();
		}
	
	// 프레임이 없는 경우
	} else {
		addNewTodo();
	}
}

function addNewTodo() {
	if($(".list").children().length > 0) {
		$(".list > li:first-child").before("<li class='newTodoFrame'></li>");
		$(".newTodoFrame").append("<input type='text' class='newTodoContents'>");
		$(".newTodoFrame").append("<div class='btn_wrap'><button onclick='addTodo()'>추가</button><button onclick='cancleAdd()'>취소</button></div>");
	} else {
		$(".list").append("<li class='newTodoFrame'></li>");
		$(".newTodoFrame").append("<input type='text' class='newTodoContents'>");
		$(".newTodoFrame").append("<div class='btn_wrap'><button onclick='addTodo()'>추가</button><button onclick='cancleAdd()'>취소</button></div>");
	}	
}

 

 

 

 

 

◽ ajax 호출 후 리스트를 그리는  function

function refreshList(data) {
	let todoList = data.todoList;
	$(".list").empty();
	for(let i=0; i<todoList.length; i++) {
		let checked = data.todoList[i].complete_yn == 'Y' ? 'checked' : '';
		$(".list").append('<li class="list' + todoList[i].idx + ' ' + checked + '"></li>');
		$(".list" + todoList[i].idx).append('<input type="checkbox" value="' + todoList[i].idx + '" id="middle' + todoList[i].idx + '" ' + checked + '>');
		$(".list" + todoList[i].idx).append('<label for="middle' + todoList[i].idx + '">' + todoList[i].contents + '</label>');
		$(".list" + todoList[i].idx).append('<button class="delBtn" onclick="deleteTodo(' + todoList[i].idx + ')" style="display: none;">삭제</button>');
	}
	
	initCheckBoxClickEvent();
	showDeleteBtn();
}

ajax 호출 후 넘어온 list를 변수로 받아 리스트를 새로 그리는 function이다.새로 그려진 element는 클릭이벤트가 적용되지 않은 상태이기 때문에 클릭이벤트를 초기화하는 함수를 호출했다.

 

 

 

 

 

 Todo를 새로 추가하는 프레임에서 추가 버튼을 눌렀을 때, 유효성 검사 후 추가 처리하는 function

function addTodo() {
	let contents = $(".newTodoContents").val();
	let subType = document.getElementsByClassName('active')[0].value;
	
	if(contents.length == 0) {
		alert("값을 입력해주세요.");
	} else {
		$.ajax({
			url: "/insert/todoList",
			method: 'GET',
			async: true,
			data: {
				"contents" : contents,
				"searchType" : subType 
			},
			success : function(data) {
				if(data.isSuccess > 0) {
					alert("등록완료되었습니다.");
					refreshList(data);
					initCheckBoxClickEvent();
				}
			}
		})
	}
}

 

 

 

 

 

◽ Todo를 새로 추가하는 프레임에서 취소 버튼을 눌렀을 때, 유효성 검사 후 취소 처리하는 function

function cancleAdd() {
	if($(".newTodoContents").val().length > 0) {
		let flag = confirm("이미 작성된 할 일이 존재합니다. 취소하시겠습니까?");
		if(flag) {
			$(".newTodoFrame").remove();
		}
	} else {
		$(".newTodoFrame").remove();
	}
}

 

 

 

 

 

GIT

🚩 Github | https://github.com/hvsundev/Spring

 

 

 

 

 

 


개인적으로 공부한 내용을 정리하는 블로그로
잘못된 개념을 게시하지않도록 주의하고 있으나 오류가 있을 수 있습니다.

 

 

 

 

Comments