0. 기본 페이지 View (home.html)
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Task App</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<p class="lead">TASK 기능</p>
<p>
<a href="tasks/new">TASK 저장</a>
<a href="tasks">TASK 목록</a>
</p>
<p class="lead">회원 기능</p>
<p>
<a href="members/new">회원 저장</a>
<a href="members">회원 목록</a>
</p>
</body>
</html>
1. Member 관련 View
* createMemberForm.html (회원 가입 View)
* memberList.html (회원 목록 View)
2. Task 관련 View
* createTaskForm.html (Task 등록 View)
* updateTaskForm.html (Task 수정 View)
* taskList.html (Task 목록 View)
<div class="container">
<table>
<thead>
<tr>
<th>#</th>
<th>Title</th>
<th>Description</th>
<th>writer</th>
<th>Created Date</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr th:each="task : ${tasks}">
<td th:text="${task.id}"></td>
<td th:text="${task.title}"></td>
<td th:text="${task.desc}"></td>
<td th:text="${task.memberName}"></td>
<td th:text="${task.createdDate}"></td>
<td>
<a th:href="@{/tasks/{id}/edit (id=${task.id})}"
role="button">수정</a>
</td>
<td>
<form id="delete_form" th:action="@{/tasks/{id}/delete (id=${task.id})}" method="post">
<input type="hidden" name="_method" value="delete"/>
<a onclick="if (confirm('정말로 삭제하시겠습니까?')) document.getElementById('delete_form').submit();" class="btn btn-danger">삭제</a>
</form>
</td>
</tr>
</tbody>
</table>
</div>
"Task 삭제" 에 대한 자세한 설명
<form id="delete_form" th:action="@{/tasks/{id}/delete (id=${task.id})}" method="post">
<input type="hidden" name="_method" value="delete"/>
<a onclick="if (confirm('정말로 삭제하시겠습니까?')) document.getElementById('delete_form').submit();" class="btn btn-danger">삭제</a>
</form>
위와 같이 작성 하였다. 여기서 중요한 부분은 form 태그의 method 속성을 "post" 로 설정하였다는 것인데,
이를 "delete" 로 설정하여, delete 요청을 보내면 컨트롤러에서 처리할 것 같지만, 가능하지 않았다.
해결 방법
1. form 태그의 method 속성을 "post" 로 설정
2. 내부 input 태그에 type을 "hidden" 으로 설정하고, name="_method" value="delete" 로 설정하는 것이 중요하다.
이렇게 설정하게 되면, 비로소 컨트롤러로 delete 요청을 보내게 된다.
@DeleteMapping을 사용하기 위해, 메인 애플리케이션에 HiddenHttpMethodFilter를 빈으로 등록해주어야 한다.
+) 수정 사항
"taskList.html" 에 '삭제' 버튼을 만들어 해당 id 를 가진 task 를 개별적으로 삭제하는 기능이 정상 작동 하지 않았다.
<!--기존 삭제 버튼-->
<td>
<form id="backup_delete_form" th:action="@{/tasks/{id}/delete (id=${task.id})}" method="post">
<input type="hidden" name="_method" value="delete"/>
<a onclick="if (confirm('정말로 삭제하시겠습니까?')) document.getElementById('backup_delete_form').submit();" class="btn btn-danger">기존 삭제</a>
</form>
</td>
기존 방식의 삭제 버튼을 누르게 되면, 어떤 Task의 삭제 버튼을 누르더라도, 가장 작은 id를 가진 Task 가 삭제되는 문제가 발생했다.
[문제 발생 후보 1. 기능 구현]
먼저 기능이 제대로 구현 됐는가 를 판단하기 위해 TaskService.java 의 기능의 Test 코드를 작성하였다.
@Test
@DisplayName("Task Delete 기능 테스트")
public void Task_Delete_Test() throws Exception {
//given
Member member = new Member("name");
em.persist(member);
//1~10 Task 10개 생성
for (int i = 1; i < 11; i++) {
Task task = new Task("title" + i, "desc" + i, member);
em.persist(task);
}
System.out.println("입력된 데이터 - TASK 데이터");
List<Task> beforeTasks = taskService.findTasks();
for (Task task : beforeTasks) {
System.out.println("task.getId() = " + task.getId());
}
em.flush();
em.clear();
//1,3,5,7,9 홀수 Task 삭제
for (int i = 1; i < 10; i += 2) {
taskService.deleteTask( (long) i);
}
List<Task> afterTasks = taskService.findTasks();
for (Task task : afterTasks) {
System.out.println("task.getId() = " + task.getId());
}
//남은 Task 의 개수 == 5
Assertions.assertEquals(afterTasks.size(), 5);
}
/*
task.getId() = 1
task.getId() = 2
task.getId() = 3
task.getId() = 4
task.getId() = 5
task.getId() = 6
task.getId() = 7
task.getId() = 8
task.getId() = 9
task.getId() = 10
*/
/*
task.getId() = 2
task.getId() = 4
task.getId() = 6
task.getId() = 8
task.getId() = 10
*/
=> TaskService의 delete 기능에는 문제가 없다.
[문제 발생 후보 2. VIEW]
View 단에서 삭제하고자 하는 Task 의 id를 특정해서 Controller로 보내주는 부분에서 문제가 있다고 생각하였다.
위와 같이 변경 후 정상작동 하였다.
'PROJECT > TASK APP (TO DO LIST)' 카테고리의 다른 글
[TaskApp] 3. Controller 만들기 (1) | 2024.02.16 |
---|---|
[TaskApp] 2. Back-end 구축 (Entity + Repository + Service) (2) | 2024.02.15 |
[Task App] 1. 개발 환경 설정하기 (0) | 2024.02.15 |
[TaskApp] 0. TaskApp 개요 / 기능 설명 (0) | 2024.02.15 |