Spring(5)

9mond·2023년 9월 26일
0
post-thumbnail

1. 기본 객체들

  • ${#request}
  • ${#response}
  • ${#session}
  • ${#servletContext}
  • ${#locale}
  • spring boot 2.* 모든 객체 지원 가능
  • spring boot 3.* 는 ${#locale}만 지원 가능

2. Spring Boot static 폴더와 template 폴더의 차이

  • 의미론적으로 나눠지는 것. 서로에 것에 못두는건 아니다.

2-1. template

  • thymeleaf의 파일을 두는 곳
  • 동적 페이지를 둔다.

2-2. static

  • 보통 css나 js를 둔다.

3. @PostConstruct

  • 객체의 초기화 부분
  • 객체가 생성된 후에 별도의 초기화 작업을 위해 실행하는 메서드를 선언한다.
  • @PostConstruct를 설정해놓은 init 메서드(메서드명은 상관없음)는 WAS가 띄워질 때 실행된다.

4. @PreDestroy

  • 마지막 소멸 단계
  • 스프링 컨테이너에서 객체(빈)를 제거하기 전에 해야할 작업이 있다면 메서드 위에 사용하는 어노테이션이다.(메서드명은 상관없음)

5. redirect 시 사용되는 속성

  • RedirectAttribute : 화면에 "저장되었습니다." 라는 메세지 출력

💻코드

dto

item.java

package com.codingbox.item.domain.web.dto;

import lombok.Getter;
import lombok.Setter;

@Getter @Setter
public class Item {
	private Long id;
	private String itemName;
	private Integer price;		// null일 수 있기 때문에 Integer
	private Integer quantity;	// null일 수 있기 때문에 Integer
	
	public Item() {
		super();
	}
	
	public Item(String itemName, Integer price, Integer quantity) {
		super();
		this.itemName = itemName;
		this.price = price;
		this.quantity = quantity;
	}
}

Controller

BasicItemController.java

package com.codingbox.item.domain.web.controller;

import java.util.List;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import com.codingbox.item.domain.web.dto.Item;
import com.codingbox.item.domain.web.repository.ItemRepository;

import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import lombok.RequiredArgsConstructor;

@Controller
@RequestMapping("/basic/items")
@RequiredArgsConstructor
// @RequiredArgsConstructor : 
// final이 붙은 멤버변수만 사용해서 생성자를 자동으로 만들어준다.
public class BasicItemController {
	
	private final ItemRepository itemRepository;
	
//	@Autowired
	// 이렇게 생성자가 딱 1개만 있으면 스프링이 해당 생성자에게
	// @Autowired로 의존관계(DI)를 주입해준다.
//	public BasicItemController(ItemRepository itemRepository) {
//		this.itemRepository = itemRepository;
//	}
	
	@GetMapping
	public String items(Model model) {
		List<Item> items = itemRepository.findAll();
		model.addAttribute("items", items);
		return "basic/items";
	}
	
	@GetMapping("/{itemId}")
    // {itemId} : 경로변수, @PathVariable : 경로변수의 값을 추출하려면 이거 사용
	public String item(@PathVariable long itemId, Model model) {
		
		Item item = itemRepository.findById(itemId);
		model.addAttribute("item", item);
		return "basic/item";
		
	}
	
	@GetMapping("/add")
	public String addForm() {
		return "basic/addForm";
	}
	
	/*
	 * post방식
	 * /basic/items/add url mapping
	 * 파라미터 받아오기
	 * save()
	 * 상세페이지 조회 -> basic/item.html
	 */
//	@PostMapping("/add")
	public String saveItemV1(Model model, 
			@RequestParam String itemName,
			@RequestParam int price,
			@RequestParam Integer quantity) {
		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")
	public String saveItemV2(Model model, 
			@ModelAttribute("item") Item item) {
		// @ModelAttribute가 대신 해주는 역할
//		Item item = new Item();
//		item.setItemName(itemName);
//		item.setPrice(price);
//		item.setQuantity(quantity);
		
		itemRepository.save(item);
		
		model.addAttribute("item", item);
		return "basic/item";
	}
	
	/*
	 * @ModelAttribute의 name도 생략 가능
	 * 즉, model.addAttribute("item", item); 생략 가능
	 * 생략 시 model에 저장되는 name은 클래스명 첫 글자만 소문자로 등록된다. Item -> item
	 * ----------------------------------------------------------------
	 * @ModelAttribute("hello") Item item
	 * 	-> model.addAttribute("hello", item);
	 * 	-> ("hello")도 생략 가능
	 * Model model도 생략 가능
	 */
//	@PostMapping("/add")
	public String saveItemV3( // Model model, 
			@ModelAttribute Item item) {
		itemRepository.save(item);
//		model.addAttribute("item", item);
		return "basic/item";
	}
	
	/*
	 * @ModelAttribute 자체 생략 가능하나 권장하진 않음
	 */
//	@PostMapping("/add")
	public String saveItemV4(Item item) {
		itemRepository.save(item);
		return "basic/item";
	}
	
//	@PostMapping("/add")
	public String saveItemV5(Item item) {
		itemRepository.save(item);
		return "redirect:/basic/items/" + item.getId();
	}
	
	@PostMapping("/add")
	public String saveItemV6(Item item, RedirectAttributes redirectAttributes) {
		Item savedItem = itemRepository.save(item);
//		redirectAttributes.addAttribute("itemId", savedItem);
		redirectAttributes.addAttribute("status", true);
		return "redirect:/basic/items/" + item.getId();
	}
	
	
	@GetMapping("/{itemId}/edit")
	public String editForm(@PathVariable Long itemId, Model model) {
		// 조회
		// model에 담아서 editForm.html에 요청 보내기
		Item item = itemRepository.findById(itemId);
		model.addAttribute("item", item);
		return "basic/editForm";
	}
	
	/*
	 * update
	 * update 후에는 상세페이지로 이동
	 */
	@PostMapping("/{itemId}/edit")
	public String edit(@PathVariable Long itemId, @ModelAttribute Item item) {
		itemRepository.update(itemId, item);
		return "redirect:/basic/items/{itemId}";	// forward 방식이 아니고 redirect로
	}
	
	
	// 테스트 데이터 추가
	@PostConstruct
	public void init() {
		System.out.println("초기화 메서드 실행");
		itemRepository.save(new Item("testA", 10000, 10));
		itemRepository.save(new Item("testB", 20000, 20));
	}
	
	// 종료 메서드
	@PreDestroy
	public void destroy() {
		System.out.println("종료 메서드 호출");
	}
}

Repository

ItemRepository.java

package com.codingbox.item.domain.web.repository;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.stereotype.Repository;

import com.codingbox.item.domain.web.dto.Item;

@Repository
public class ItemRepository {
	private static final Map<Long, Item> store = new HashMap<>();
	private static long sequence = 0L;
	
	// 저장
	public Item save(Item item) {
		item.setId(++sequence);
		store.put(item.getId(), item);
		return item;
	}
	
	// 아이템 하나 검색
	public Item findById(Long id) {
		return store.get(id);
	}
	
	// 전체항목
	public List<Item> findAll(){
		return new ArrayList<Item>(store.values());
	}
	
	// 아이템 수정
	public void update(Long itemId, Item updateParam) {
		
		//item 먼저 찾는다.
		Item findItem = findById(itemId);
		findItem.setItemName(updateParam.getItemName());
		findItem.setPrice(updateParam.getPrice());
		findItem.setQuantity(updateParam.getQuantity());
		
	}
	
	// store clear
	public void clearStore() {
		store.clear();
	}
}

templates.basic

items.html

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<link href="../css/bootstrap.min.css" 
	th: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">
				<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>
					<tr th:each="item : ${items}">
						<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/${item.id}|}"
							th:text="${item.itemName}">상품명</a>
						</td>
						<td th:text="${item.price}">1000</td>
						<td th:text="${item.quantity}">10</td>
					</tr>
					
				</tbody>
			</table>
		</div>
	</div>
	<!-- /container -->
</body>
</html>

item.html

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<link href="../css/bootstrap.min.css"
	th: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>
		
		<!--추가-->
		<h2 th:if="${param.status}" th:text="'저장 성공!'"></h2>
		<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 -->
	<script th:inline="javascript">
		if([[${param.status}]]){
			alert("저장완료");
		}
/*		else{
			alert("저장실패");
		}*/
	</script>
</body>
</html>

editForm.html

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<link href="../css/bootstrap.min.css" 
	th: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>
		<form action="item.html" th:action method="post">	<!--th:action 메서드 방식만 변경-->
			<div>
				<label for="id">상품 ID</label> 
				<input type="text" id="id" name="id" 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}|">
			</div>
			<div>
				<label for="price">가격</label> 
				<input type="text" id="price" name="price" class="form-control"
				 value="10000" th:value="|${item.price}|">
			</div>
			<div>
				<label for="quantity">수량</label> 
				<input type="text" id="quantity" name="quantity" class="form-control"
				 value="10" th:value="|${item.quantity}|">
			</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='item.html'" type="button">
						취소
					</button>
				</div>
			</div>
		</form>
	</div>
	<!-- /container -->
</body>
</html>

addForm.html

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<link href="../css/bootstrap.min.css" 
	th: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 method="post">	<!--/ 동일한 url을 가지고 있을 경우 ="@{/basic/items/add}" 생략 가능-->
			<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>
profile
개발자

0개의 댓글

관련 채용 정보