작성
·
633
·
수정됨
0
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.
1. 강의 내용과 관련된 질문을 남겨주세요.
2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.
(자주 하는 질문 링크: https://bit.ly/3fX6ygx)
3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.
(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)
질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.
=========================================
[질문 템플릿]
1. 강의 내용과 관련된 질문인가요? (예/아니오)
2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)
3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)
[질문 내용]
아래 코드는 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">
<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>
th:onclick="|location.href='@{/basic/items}'|" type="button">취소</button>
</div> <!-- /container -->
</body>
</html>
package hello.itemservice.web.basic;
import hello.itemservice.domain.item.Item;
import hello.itemservice.domain.item.ItemRepository;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Controller
@RequestMapping("/basic/items")
/**
* basic/items로 get 방식으로 오면 @GetMapiing 메소드가 실행이 된다.
*/
@RequiredArgsConstructor
/**
* @RequiredArgsConstructor을 쓰면
* final이 붙은 멤버에게는
* @Autowired
* public BasicItemController(ItemRepository itemRepository) {
* this.itemRepository = itemRepository;
* }라는 생성자를 자동으로 만들어준다.
*/
public class BasicItemController {
/**
* 1.BasicItempContorller가 스프링 빈에 등록이 되면서
* 2.생성자 주입으로 스프링 빈으로 등록되어 있는 ItempRpository가 스프링 빈에서 주입이 된다.
*
* 1.Spring 컨테이너 초기화: 애플리케이션이 시작될 때 Spring 컨테이너가 초기화되고 구성 파일을 로드합니다.
* 이 때 @Autowired 어노테이션을 확인하고 의존성 주입을 준비합니다.
* 2.빈 객체 생성: Spring은 컨테이너에서 관리하는 빈(bean) 객체를 생성합니다.
* 빈 객체는 @Component, @Service, @Repository, @Controller 등과 같은 어노테이션으로 표시된 클래스들을 기반으로 생성됩니다.
* 3.의존성 주입: @Autowired 어노테이션이 적용된 생성자, 필드 또는 메서드를 확인하고 해당 의존성을 주입합니다.
* 이때, BasicItemController 클래스에 있는 @Autowired 어노테이션이 적용된 생성자가 호출되면서 ItemRepository의 인스턴스가 주입됩니다.
* @Autowired란 스프링 컨테이너에 등록한 빈에게 의존관계주입이 필요할 때,
* DI(의존성 주입)을 도와주는 어노테이션이다. 스프링 컨테이너에 빈들을 모두 등록한 후에, 의존성 주입 단계가 이루어진다.
* 4.애플리케이션 실행: 모든 초기화 작업이 완료되면 Spring은 애플리케이션을 실행합니다.
*/
private final ItemRepository itemRepository;
@GetMapping
public String items(Model model){
List<Item> items = itemRepository.findAll();
model.addAttribute("items",items);
return "basic/items"; // Return 되는 뷰 위치.
}
@GetMapping("/{itemId}")
public String item(@PathVariable long itemId,Model model)
{
Item item = itemRepository.findById(itemId);
model.addAttribute("item",item);
return "basic/item";
}
/**
* 실제로 데이터를 넣는 것이 아닌 form만 보여준다.
*
* form을 열때는 get
* 실제 저장을 할 떈 post 사용
* URL을 똑같게 지정한다. 이떄, 같은 URL로 오더라도 Get이나 post에 따라 addForm을 실행하거나 save를 실행한다.
*/
@GetMapping("/add")
public String addForm(){
return "basic/addForm";
}
/**
*
* addForm.html form에 있는 name과 변수명을 동일하게 해야한다.
* 이 코드에서 @ModelAttribute 가 html 의 name 속성과 Item class 를 매칭시켜주고
* 그래서 itemRepository에 저장 로직에서 사용된다는 거까지는 이해했습니다
*/
// @PostMapping("/add")
public String addItemV1(@RequestParam String itemName,
@RequestParam int price,
@RequestParam Integer 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")
public String addItemV2(@ModelAttribute("item") Item item,Model model){
itemRepository.save(item);
// model.addAttribute("item",item);
return "basic/item";
}
//@PostMapping("/add")
public String addItemV3(@ModelAttribute Item item){
itemRepository.save(item);
// model.addAttribute("item",item);
return "basic/item";
}
// @PostMapping("/add")
public String addItemV4(Item item){
itemRepository.save(item);
return "basic/addForm";
}
/**
* PRG - Post/Redirect/Get
*/
@PostMapping("/add")
public String addItemV5(Item item) {
itemRepository.save(item);
return "redirect:/basic/items/" + item.getId();
}
@GetMapping("/{itemId}/edit")
public String editForm(@PathVariable Long itemId,Model model){
Item item = itemRepository.findById(itemId);
model.addAttribute("item",item);
return "basic/editForm";
}
@PostMapping("/{itemId}/edit")
public String edit(@PathVariable Long itemId,@ModelAttribute Item item){
itemRepository.update(itemId,item); // @ModelAttribute Item item라고 작성을 하면 item객체를 가져옴
return "redirect:/basic/items/{itemId}"; // 이렇게 리다이렉트로하면 PathVariable에 있는 것을 사용할 수 있음.
//리다이렉션 부분 상품 수정 7분부터 다시 들어보기 이해안됨
/**
* 상품명을 itemD로 변경하면 edit이 호출되고 http 상태가 302가 된다
* 이때, location은 items/2로 되는데 이것은 eidt의 결과가 리다이렉트라서 웹 브라우저가 2번으로 실제 item 2번으로 간다.
*/
}
/**
* 테스트용 데이터 추가
*/
@PostConstruct
public void init(){
itemRepository.save(new Item("itemA",1000,20));
itemRepository.save(new Item("itemB",2000,40));
}
}
이것은 BasicItemController 코드입니다.
상품 등록 버튼을 누르면 위와 같은 오류가 발생합니다.
그리고2023-09-18T23:32:41.550+09:00 ERROR 29201 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: org.thymeleaf.exceptions.TemplateProcessingException: Could not parse as expression: "/basic/items/add" (template: "basic/addForm" - line 17, col 30)] with root cause
라는 오류가 발생합니다.
아무리 봐도 오류를 따라가서 수정을 해도 무엇이 문제인지 모르겠습니다.
답변 1
0
안녕하세요. blackhoild124님, 공식 서포터즈 코즈위버입니다.
addForm.html 파일에 아래와 같은 부분에 오탈자가 있는것 같습니다.
th:onclick="|location.href='@{/basic/items}'|" type="button">취소</button>
이 부분이 강의코드와 일치하는지 확인해주세요 :)
감사합니다.