728x90
반응형
웹페이지 만들어 보기
배운내용으로 실제 웹페이지를 만들어 본다.
들어가기전
타임리프 핵심을 공부하고 가보자
핵심1
- th:xxx 가 붙은 부분은 서버사이드에선 렌더링이 된다;
- 즉 원래 정의되어있는 값을 대체 한다.
@{...}
- url 링크표현식이라고 한다.
- th:href="@{/css/bootstrap,min.css}"
- URL 링크 표현식을 사용하면 서블릿 컨텍스트를 자동으로 포함한다.
- 경로변수에 값을 넣을수 있고 쿼리 파라미터도 지원한다.
- th:href="@{/basic/items/{itemId}(itemId=${item.id}, query='test')}"
- http://localhost:8080/basic/items/1?query=test
th:onclick
- onclock="location.href='addForm.html'"
- th:onclick="|location.href='@{/basic/items/add}'|"
|...|
- 리터럴 대체
- 타임리브에서 문자와 표현식은 분리되어 있다.
- <span th:text="'Welcome to our application, ' + ${user.name} + '!'"> - 하지만 공백이 들어가면 오류가 발생하는데 다음과 같이 쉽게 쓸수 있다.
- <span th:text="|Welcome to our application, ${user.name}!|">
th:each
- 반복표현식
- 반복할때 사용한다. for 문이라고 생각하면 되겠다.
${...}
- 변수표현식
- <td th:text="${item.price}">10000</td>
- 모델에 포함된 값이다, 타임리프 변수로 선언한 값을 조회할 수 있다.
- 프로퍼티 접근법을 사용한다.
th:text
- 내용변경을 할 수 있다.
- <td th:text="${item.price}">10000</td>
- 10000 값을 item.price 로 변경한다.
상품목록
View
- th:onclick="|location.href='@{/basic/items/add}'|"
- 클릭시 해당 URL 이동 및 컨트롤러 호출 - th:href="@{/basic/items/{itemId}(itemId=${item.id})}"
- {itemId}(itemId=${item.id}) itemId 에 item 객체의 id 값을 넣어준다
- /basic/items/1 이렇게 변하게 된다.
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<!--th 를 쓰게 되면 해당 내용으로 변경된다.-->
<link th:href="@{/css/bootstrap.min.css}"
href="../css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container" style="max-width: 600px">
<div class="py-5 text-center">
<h2>상품 목록</h2>
</div>
<div class="row">
<div class="col">
<!-- th:onclick 쓴다. @{} 를 쓰면 참조가 된다.-->
<button class="btn btn-primary float-end"
onclick="location.href='addForm.html'"
th:onclick="|location.href='@{/basic/items/add}'|"
type="button">상품 등록</button>
</div>
</div>
<hr class="my-4">
<div>
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>상품명</th>
<th>가격</th>
<th>수량</th>
</tr>
</thead>
<tbody>
<!-- each 를 써서 반복을 해준다.-->
<tr th:each = "item : ${items}">
<!-- {itemId}(itemId=${item.id}) 문법 itemId 에 값을 넣는다.-->
<td><a href="item.html" th:href="@{/basic/items/{itemId}(itemId=${item.id})}" th:text="${item.id}">회원id</a></td>
<td><a href="item.html" th:href="@{/basic/items/{itemId}(itemId=${item.id})}" th:text="${item.itemName}">상품명</a></td>
<td th:text="${item.price}">10000</td>
<td th:text="${item.quantity}">10</td>
</tr>
</tbody>
</table>
</div>
</div> <!-- /container -->
</body>
</html>
Controller
- @RequestMapping("/basic/items")
- 클래스 위에 선언해줌으로써 기본 URI 값을 정의한다.
- 뒤에 부터는 class 는 생략 하겠습니다. - @PostConstruct
- 빈이 생성되고 난 후 해당연산을 수행하여 기본데이터를 넣어준다.
@Controller
@RequestMapping("/basic/items")
@RequiredArgsConstructor
public class BasicItemController {
private final ItemRepository itemRepository;
@GetMapping
public String items(Model model) {
List<Item> items = itemRepository.findAll();
model.addAttribute("items", items);
return "basic/items";
}
/**
* Test 용 데이터 추가.
*/
@PostConstruct
public void init() {
itemRepository.save(new Item("ItemA",10000,10));
itemRepository.save(new Item("ItemB",20000,20));
}
}
상품상세
View
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<link th:href="@{/css/bootstrap.min.css}"
href="../css/bootstrap.min.css" rel="stylesheet">
<style>
.container {
max-width: 560px;
}
</style>
</head>
<body>
<div class="container">
<div class="py-5 text-center">
<h2>상품 상세</h2>
</div>
<div>
<label for="itemId">상품 ID</label>
<input type="text" id="itemId" name="itemId" class="form-control"
value="1" th:value="${item.id}" readonly>
</div>
<div>
<label for="itemName">상품명</label>
<input type="text" id="itemName" name="itemName" class="form-control"
value="상품A" th:value="${item.itemName}" readonly>
</div>
<div>
<label for="price">가격</label>
<input type="text" id="price" name="price" class="form-control"
value="10000" th:value="${item.price}" readonly>
</div>
<div>
<label for="quantity">수량</label>
<input type="text" id="quantity" name="quantity" class="form-control"
value="10" th:value="${item.quantity}" readonly>
</div>
<hr class="my-4">
<div class="row">
<div class="col">
<button class="w-100 btn btn-primary btn-lg"
onclick="location.href='editForm.html'"
th:onclick="|location.href='@{/basic/items/{itemId}/edit(itemId=${item.id})}'|" type="button">상품 수정</button>
</div>
<div class="col">
<button class="w-100 btn btn-secondary btn-lg"
onclick="location.href='items.html'"
th:onclick="|location.href='@{/basic/items}'|" type="button">목록으로</button>
</div>
</div>
</div> <!-- /container -->
</body>
</html>
Controller
- class 생략
@GetMapping("/{itemId}")
public String items(@PathVariable long itemId, Model model) {
Item item = itemRepository.findById(itemId);
model.addAttribute("item", item);
return "basic/item";
}
상품등록(너무너무 중요하다)
View
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<link th:href="@{/css/bootstrap.min.css}"
href="../css/bootstrap.min.css" rel="stylesheet">
<style>
.container {
max-width: 560px;
}
</style>
</head>
<body>
<div class="container">
<div class="py-5 text-center">
<h2>상품 등록 폼</h2>
</div>
<h4 class="mb-3">상품 입력</h4>
<form action="item.html" th:action="@{/basic/items/add}" method="post">
<div>
<label for="itemName">상품명</label>
<input type="text" id="itemName" name="itemName" class="form-control" placeholder="이름을 입력하세요">
</div>
<div>
<label for="price">가격</label>
<input type="text" id="price" name="price" class="form-control"
placeholder="가격을 입력하세요">
</div>
<div>
<label for="quantity">수량</label>
<input type="text" id="quantity" name="quantity" class="form-control" placeholder="수량을 입력하세요">
</div>
<hr class="my-4">
<div class="row">
<div class="col">
<button class="w-100 btn btn-primary btn-lg" type="submit">상품 등록</button>
</div>
<div class="col">
<button class="w-100 btn btn-secondary btn-lg"
onclick="location.href='items.html'"
th:onclick="|location.href='@{/basic/items}'|"
type="button">취소</button>
</div>
</div>
</form>
</div> <!-- /container -->
</body>
</html>
Controller
여기가 중요하다.
단계적으로 addItem 함수를 개선했다.
addItemV1
- @RequestParam 으로 요청 파라미터를 받는다.
- Model 을 이용하여 View 에게 넘겨줄 객체를 정의해준다.
addItemV2
- ModelAttribute 를 이용하여 객체로 파라미터를 받는다.
- ModelAttribute 는 요청파라미터의 값을 프로퍼티 접급법 setXxx 으로 입력해준다.
- ModelAttribute Model에 지정한 객체를 자동으로 넣어준다.
코드가 간결해 졌다.
addItemV3
- ModelAttribute 의 name 생략
addItemV4
- ModelAttribute 생략
addItemV5
- redirect 사용
- redirect를 사용하는 이유는 다음과 같다.
- redirect를 사용하지 않으면 아이템 을 추가 할때 다음과 같은 일이 발생한다. - 새로 고침을 할 때마다 새로운 아이템이 계속 추가 된다.
- 이유는 아이템을 추가후 새로고침을 할경우 이전요청을 다시 수행한다.
- 그렇기 때문에 아이템들이 계속 추가된다. - redirect 를 이용하여 아예 상세 페이지로 새로운 요청을 하도록 한다.
//@PostMapping("/add")
public String addItemV1(@RequestParam String itemName ,
@RequestParam int price,
@RequestParam int quantity,
Model model) {
Item item = new Item();
item.setItemName(itemName);
item.setPrice(price);
item.setQuantity(quantity);
itemRepository.save(item);
model.addAttribute("item", item);
return "basic/item";
}
// @PostMapping("/add")
// ModelAttribute 의 item 은 뭘까?
// 아래 처럼 알아서 된다.
// model.addAttribute("item", item);
public String addItemV2(@ModelAttribute("item") Item item) {
itemRepository.save(item);
return "basic/item";
}
// @PostMapping("/add")
// ModelAttribute 의 item 즉 name 을 빼면?
// 아래 처럼 알아서 된다.
// Item -> item
// HelloData -> helloData 가 된다.
public String addItemV3(@ModelAttribute Item item) {
itemRepository.save(item);
return "basic/item";
}
//@PostMapping("/add")
// 아예 생략도 가능하다.
// 상품을 저장하고 뷰를 보여 줬다.
// 상품 저장을 하면 상품상세로 넘어간다.
// 하지만 url 은 여전히 저장을 나타내기 때문에
// 새로고침을 하면 계속 저장된다. 계속 아이템이 저장된다.
public String addItemV4(Item item) {
itemRepository.save(item);
return "basic/item";
}
@PostMapping("/add")
public String addItemV5(Item item) {
itemRepository.save(item);
return "redirect:basic/item"+item.getId();
}
전체코드
https://github.com/rnrl1215/spring-mvc-first-toyproject
참고
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-1/dashboard
728x90
반응형
'Spring > spring mvc 1 스터디' 카테고리의 다른 글
[Spring] PRG(Post Redirect Get) (0) | 2021.12.03 |
---|---|
[Spring] @ModelAttribute 사용하기 (0) | 2021.11.27 |
[Spring] Message Converter (0) | 2021.11.25 |
[Spring] HTTP 응답 (0) | 2021.11.21 |
[Spring] HTTP 요청메세지 TEXT, JSON (0) | 2021.11.16 |
댓글