Spring을 공부하고 있는 사람들은 TODO LIST를 만드는 것으로 구조나 코드 등을 연습하고 있다.
수 많은 사람들이 자료를 공유하고 있으며, 나도 그런 TODO 리스트를 만들어 본 게 한 10번 이상? 정도는 있는 것 같다.
하지만, 아에 Spring 자체를 이해하지 못할 때, 따라하는 식으로 제작한 TODO LIST가 도움이 되었을까 묻는다면 나는 아닌 것 같아서 한번 도움없이 만들어보기로 했다.
사실 만든지는 좀 됐고, 포스팅 시간에 공부하는 시간을 전부 쏟다보니... 너무 안올려서 한번 정리해서 올려보려고 한다.
1. Spring 프로젝트 생성
우선 IDE나 start.spring.io 등을 이용해서 Spring 프로젝트를 생성한다.
진짜 별거 안썼기 때문에 Spring Web, Thymeleaf 정도만 추가해주면 된다.
그러고 나서 딱 세 가지 파일을 만들었는데, Controller, DTO, Service 세 가지이다.
아 추가로 todo.html을 만들어서 사용했는데 우선 이것부터 살펴보자.
2. todo.html, TodoDto.java
📄 todo.html
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>ToDoList</title>
</head>
<body>
<form th:action="@{/todo/create}" method="post">
<h1>TODO List</h1>
<label for="todo-desc">새 할일: <input type="text" id="todo-desc" name="todo-desc"></label>
<input type="submit" value="생성">
</form>
<form>
<hr> <b style="font-size: 18px">ToDo</b>
<div th:each="todo: ${todoList}">
<div th:if="${todo.done} == false">
<span>[[${todo.content}]]</span> <br>
<div>
<form th:action="@{/todo/{id}/update (id=${todo.id})}" method="post" style="display:inline-block;">
<input type="submit" value="완료"></form>
<form th:action="@{/todo/{id}/delete (id=${todo.id})}" method="post" style="display:inline-block;">
<input type="submit" value="삭제"></form>
</div>
</div>
</div>
<hr> <b style="font-size: 18px">Done</b>
<div th:each="todo: ${todoList}">
<div th:if="${todo.done} == true">
<span>[[${todo.content}]]</span> <br>
<div>
<form th:action="@{/todo/{id}/update (id=${todo.id})}" method="post" style="display:inline-block;">
<input type="submit" value="취소"></form>
<form th:action="@{/todo/{id}/delete (id=${todo.id})}" method="post" style="display:inline-block;">
<input type="submit" value="삭제"></form>
</div>
</div>
</div>
</form>
</body>
</html>
우선 보다시피 한 html 파일에서 모든 것들이 작동하게끔 제작이 되어있다.
(보자마자 어지럽긴한데 html은 그냥 복사한다고 치고~~)
thymeleaf 문법은 대부분의 html 문법에서 `th:xxx` 가 붙는 것이 시작이다.
특히 [[${ ... }]]를 통해서 변수를 직접 출력할 수 있으며, 더 자세한 내용은 아래 표와 같다.
표현 | 설명 | 예제 |
@{ ... } | URL 링크 표현식 | th:href="@{/css/bootstrap.min.css}" th:href="@{/{itemId}/edit(itemId=${item.id})}" |
| ... | | 리터럴 대체 | th:text="|Hi ${user.name}!|" ( = th:text="'Hi '+${user.name}+'!'" ) |
${ ... } | 변수 | th:text=${user.name} |
[[${ … }]] | 변수 직접 출력 | [[${data}]] |
th:each | 반복 출력 | <tr th:each="item: ${items}"> <td th:text="${item.price}">100</td> </tr> |
*{ ... } | 선택 변수 | <tr th:object="${items}"> <td th:text="*{price}">100</td> </tr> |
#{ ... } | 메시지. properties같은 외부 자원에서 코드에 해당하는 문자열 get. |
th:text="#{member.register}" |
아무튼 요약하자면 내가 html에서 사용한 것은 if(조건), each(반복), [[${ ... }]] 이 세 가지를 이용하였다.
실행한 화면이 아니라 html만 보게 된다면 이런 식으로 출력된다.
todoDto는 아래와 같다.
📄 TodoDto.java
public class TodoDto {
private Long id;
private String content;
private Boolean done;
public TodoDto() {}
public TodoDto(Long id, String content, Boolean done) {
this.id = id;
this.content = content;
this.done = done;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Boolean getDone() {
return done;
}
public void setDone(Boolean done) {
this.done = done;
}
}
뭐 주절주절 길어보이지만 실상은 아래와 같다고 보면 된다.
private Long id;
private String content;
private Boolean done;
id, content, done에 대한 생성자와 getter, setter 추가인 것.
기본적으로 윈도우 기준 인텔리제이에서는 alt+insert 입력을 하게 되면 생성할 수도 있다.
3. TodoController, TodoService
📄 TodoController.java
@Controller
public class TodoController {
private final TodoService todoService;
public TodoController(TodoService todoService) {
this.todoService = todoService;
}
@RequestMapping("/todo")
public String todo(Model model) {
List<TodoDto> todoDtoList = this.todoService.readAll();
model.addAttribute("todoList", todoDtoList);
return "todo";
}
@RequestMapping("/")
public String home() {
return "redirect:/todo";
}
}
1. 'localhost:8080/todo'가 입력됨(localhost:8080만 입력해도 /todo로 Redirect를 해줌)
2. todoService의 readAll을 통해 불러온 todoDto의 List를 html에 넘겨서 값들을 보여주는 과정이다.
📄 TodoService.java
@Service
public class TodoService {
private final List<TodoDto> todoList = new ArrayList<>();
private Long nextId = 1L;
public List<TodoDto> readAll() {
return todoList;
}
}
우선 @Service를 통해 서비스를 지정해주고, Dto 형식대로 List를 만들어서 저장하게 된다.
조금 정확히는 객체를 Byte Array로 변환해서 저장하는 과정이다.
id에 대해서는 직렬화를 찾아보는 것이 좋을 것 같다.
@id나 @GeneratedValue에서 starategy를 GenerationType.IDENTITY 등으로 유사하게 사용할 수 있는 방법은 많은데, 지금 만드는 건 간단한 TODO List니까~ 그런거 안쓰고 나중에 Create할 때 nexId++할 예정이다.
readAll 메서드 같은 경우 List<TodoDto>형식의 todoList를 반환해주는 것
아무튼 이 상태에서 작동한다면, 저장된 값이 전혀 없기 때문에 화면은 이런 식으로 출력된다.
다른 사람 포스팅을 보면서 생각한건데, 아에 처음일때는 이정도도 버거웠던 기억이 나서 다음 게시물에서 마무리해보려고 한다. 다음은 생성, 완료, 삭제를 구현해보자.