과제 step2에서 Thymeleaf로 관리자페이지를 만들라는 과제가 나왔다. 나는 Thymeleaf를 다뤄본 적이 전혀 없기 때문에 이거부터 공부해야겠다.
타임리프는 스프링부트에서 공식적으로 지원하는 View 템플릿이다. JSP와는 달리 타임리프 문서는 html확장자를 가지고 있어서 JSP처럼 서블렛이 문서를 표현하는 방식이 아니기 때문에 서버 없이도 동작이 가능하다.
<타임리프에서 사용되는 문법>
- th:text : 문자열 생성 -> th:text="${data}"
- th:each : 반복문 -> th:each="article:${alticleList}"
- th:if : 조건문 -> th:if=${data != null}
- th:herf : 이동경로 -> th:herf="@{/article/list(id=${data})}"
<a> 태그는 링크를 받아서 이동하는 태그이다.
<table> : 표를 만드는 태그
<th> : 표의 제목
<tr> : 표의 줄( 한 줄을 tr로 감싼다)
<td> : 표의 데이터
<구현 방법>
우선 의존성을 추가한다.
build.gradle에
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
를 추가한다.
앞서 강의에서의 예시를 봤듯이
package com.example.servingwebcontent;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class GreetingController {
@GetMapping("/greeting")
public String greeting(@RequestParam(name="name", required=false, defaultValue="World") String name, Model model) {
model.addAttribute("name", name);
return "greeting";
}
}
위와 같이 코드를 짠 후 실행하고, http://localhost:8080/greeting를 실행하면 인삿말이 나오고, http://localhost:8080/greeting?name=User를 실행하면 user 부분에 이름도 삽입되어 출력된다.
이제 이를 thymeleaf를 사용해서 구현하려면
package com.example.servingwebcontent;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class GreetingController {
@GetMapping("/greeting")
public String greeting(@RequestParam(name="name", required=false, defaultValue="World") String name, Model model) {
model.addAttribute("name", name);
return "greeting";
}
}
위의 클래스는 위와 같이 바꿔야한다. 모델을 파라미터로 받고, 이를 name에 바인딩하여 addAttribute로 보낸다.
이 때 위에 @RestController가 아니라 @Controller을 사용해야한다.
@RestController은 @Controller + @ResponseBody이다.
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Getting Started: Serving Web Content</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<p th:text="|Hello, ${name}!|" />
</body>
</html>
html코드는 위와 같다.
이 때 html코드는 resources에 넣어야한다.
<Controller 에서 html에 데이터 전달>
@GetMapping
public String read(Model model){
List<Menu> menus = menuService.findall();
model.addAttribute("menus", menus);
return "Menu";
}
위와 같이 model.addAttribute를 통해서 html에 전달해야 하는 데이터를 보낸다.
model을 사용하기 위해서는 Model을 주입받아야한다.
그 다음 String형태로 html 파일명을 리턴하여 해당 html을 화면에 띄운다.
<tr th:each="menu : ${menus}">
<td th:text="${menu.name}">Menu Name</td>
<td th:text="${menu.price}">Menu Price</td>
<td><img th:src="${menu.imageUrl}" alt="Menu Image"></td>
<td>
<a th:href="@{/menu/edit/{id}(id=${menu.id})}">Edit</a>
</td>
<td>
<a th:href="@{/menu/delete/{id}(id=${menu.id})}">Delete</a>
</td>
위의 리스트를 받아서 화면에 리스트로 출력하는 부분이다. th:each는 menus리스트에 있는 값을 각각 빼온다는 의미이다. 각 객체는 menu에 저장된다.
th:text를 통해서 해당 메뉴에서의 속성을 가져오고, 만약 그 부분이 비었다면 menu name을 출력한다.
<html에서 Controller에 데이터 전달>
html에서 controller에 데이터를 전달할 때에는 form을 사용한다.
<h1>Edit Menu</h1>
<form th:action="@{/menu/update/{id}(id=${menu.id})}" method="post">
<ul>
<li>
<label for="name">Name:</label>
<input type="text" id="name" name="name" th:value="${menu.name}" />
</li>
<li>
<label for="price">Price:</label>
<input type="number" id="price" name="price" th:value="${menu.price}" />
</li>
<li>
<label for="imageUrl">ImageUrl:</label>
<input type="url" id="imageUrl" name="imageUrl" th:value="${menu.imageUrl}" />
</li>
<li class="button">
<button type="submit">Update</button>
</li>
</ul>
</form>
위에는 메뉴 수정사항을 받는 부분이다. controller부분에 post로 메세지를 보내는데 이 폼에 있는 부분이 body로 들어간다.
label for 부분이 어떤 데이터를 입력받을건지를 나타내고, 이는 input의 id와 연결되어 서로를 식별한다.
Type는 어떤 타입으로 받을건지를 나타내고,
name부분은 body부분의 key가 된다. 또한 th:value는 해당 부분의 초기값이 된다.
위 폼은 button이 눌리면 전송된다. 버튼의 타입은 submit이다.
@PostMapping("/update/{id}")
public String update(
@PathVariable("id") Long id,
@ModelAttribute MenuRequest request,
Model model
){
menuService.update(
id,
request.name(),
request.price(),
request.imageUrl()
);
List<Menu> menus = menuService.findall();
model.addAttribute("menus", menus);
return "Menu";
}
그 다음 아래와 같이 컨트롤러가 받는데, 이 때 @ModelAttribute를 통해서 받는다.
<화면 전환>
사용자가 특정 객체의 수정버튼을 누르면 이를 수행할 수 있는 화면으로 이동하고, 수정이 완료되었으면 다시 메인화면으로 돌아가는 부분을 구현하고자 한다.
우선 Menu.html부분에서 시작한다.
<tr th:each="menu : ${menus}">
<td th:text="${menu.name}">Menu Name</td>
<td th:text="${menu.price}">Menu Price</td>
<td><img th:src="${menu.imageUrl}" alt="Menu Image"></td>
<td>
<a th:href="@{/menu/edit/{id}(id=${menu.id})}">Edit</a>
</td>
<td>
<a th:href="@{/menu/delete/{id}(id=${menu.id})}">Delete</a>
</td>
위 코드는 Edit를 누르면 menu/edit/{id}(id=${menu.id})를 보낸다.
이와 같이 하면 내가 수정을 원하는 객체의 아이디 값이 매핑되어 전송된다.
그럼 이를 컨트롤러가 받는다.
@GetMapping("/edit/{id}")
public String edit(@PathVariable("id") Long id, Model model) {
Menu menu = menuService.findById(id);
model.addAttribute("menu", menu);
return "update_menu";
}
컨트롤러 클래스 앞에
@RequestMapping("/menu")
를 붙였으니 edit메서드가 실행이 된다. 위 메서드는 받은 id에 해당하는 객체 값을 서비스를 통해서 받고, 이를 model.addAttribute를 통해서 매핑해준다.
그 다음 return "update_menu"를 해서 update_menu.html을 실행한다.
<h1>Edit Menu</h1>
<form th:action="@{/menu/update/{id}(id=${menu.id})}" method="post">
<ul>
<li>
<label for="name">Name:</label>
<input type="text" id="name" name="name" th:value="${menu.name}" />
</li>
<li>
<label for="price">Price:</label>
<input type="number" id="price" name="price" th:value="${menu.price}" />
</li>
<li>
<label for="imageUrl">ImageUrl:</label>
<input type="url" id="imageUrl" name="imageUrl" th:value="${menu.imageUrl}" />
</li>
<li class="button">
<button type="submit">Update</button>
</li>
</ul>
update_menu는 위와 같다. 사용자가 데이터를 모두 수정하고 버튼을 누르면 /menu/update/{id} 메세지가 전송된다.
@PostMapping("/update/{id}")
public String update(
@PathVariable("id") Long id,
@ModelAttribute MenuRequest request,
Model model
){
menuService.update(
id,
request.name(),
request.price(),
request.imageUrl()
);
List<Menu> menus = menuService.findall();
model.addAttribute("menus", menus);
return "Menu";
}
그러면 컨트롤러 부분이 위와 같이 데이터를 받게된다. html부분에서 데이터를 받을 때에는 @ModelAttribute를 통해 받는다.
여기서 서비스를 통해서 받은 데이터를 업데이트 한 후 다시 Menu.html에 새로운 데이터 리스트를 전달한다.
'Back-end > 카카오테크캠퍼스' 카테고리의 다른 글
[카카오테크캠퍼스] 2주차 과제 코드리뷰 정리 (0) | 2024.07.11 |
---|---|
[카카오테크캠퍼스] 1주차 과제 코드리뷰 정리 (1) | 2024.06.30 |
[카카오테크캠퍼스] 데이터베이스 적용, JDBC (0) | 2024.06.30 |
[카카오테크캠퍼스] 스프링이란, 의존성, http로 정보 주고받기 (0) | 2024.06.25 |
[카카오테크캠퍼스] 카카오테크캠퍼스 백엔드 2기 합격! (1) | 2024.04.04 |