티스토리 뷰

 

지난 웹 페이지 만들기 - 1 (복습)에 이어서 상품 상세 페이지와 상품 등록(add) 그리고 상품 수정(edit) 페이지를 만들어봤다. 코드를 다시 들여다보면서 강의를 제대로 이해했는지(?) 나름대로 어떤 과정이 되는지 이해한대로 작성했다.. ^^.. 

 

 

6. 상품 상세 페이지 상세 등록 폼의 컨트롤러 뷰 템플릿을 작성한다.

 

컨트롤러 전체 코드

 web/item/ItemController.java

더보기
package magazink.re.web.item;

import lombok.RequiredArgsConstructor;

import magazink.re.domain.item.Item;
import magazink.re.domain.item.ItemRepository;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import java.util.*;

@Controller
@RequiredArgsConstructor
@RequestMapping("/items")
public class ItemController {

    private final ItemRepository itemRepository;
    
    // 아이템 리스트 화면
    @GetMapping
    public String items(Model model) {
        List<Item> items = itemRepository.findAll();
        model.addAttribute("items", items);
        return "items/items";
    }
    // 복습 2 - 코드 추가
    // 아이템 상세 페이지
    @GetMapping("/{itemId}")
    public String item(@PathVariable Long itemId, Model model) {
        Item item = itemRepository.findById(itemId);
        model.addAttribute("item", item);
        return "items/item";
    }

    // 상품 등록 폼
    @GetMapping("/add")
    public String addForm(Model model) {
        //로그인 여부 체크
        model.addAttribute("item", new Item());
        return "items/addForm";
    }

    // 상품 추가 후 저장
    @PostMapping("/add")
    public String addItem(@ModelAttribute Item item, RedirectAttributes redirectAttributes){

        Item savedItem  = itemRepository.save(item);

        redirectAttributes.addAttribute("itemId", savedItem.getId());
        redirectAttributes.addAttribute("status", true);
        return "redirect:/items/{itemId}";

    }
 
}

 

각 컨트롤러를 자세히 살펴보자..(크게 자세히 볼 건 없지만 ^^..)

 

상품(Item) 상세 페이지

- @GetMapping 사용하여 itemId 값으로 지정

- 자원 자체를 나타낼 때@PathVariable으로 itemId을 받아오고, 모델(Model)에 담아둔다.

- 받아온 itemId값으로 itemRepository에서 item 값을 찾아와 변수에 저장한다.

 

- addAttribute("key", "value") 메서드를 이용해 view에 전달할 데이터를 "item" (key) item (value)로 전달한다.

    // 아이템 상세 페이지
    @GetMapping("/{itemId}")
    public String item(@PathVariable Long itemId, Model model) {
        Item item = itemRepository.findById(itemId);
        model.addAttribute("item", item);
        return "items/item";
    }

 

상품 등록 폼

- 상품 등록 폼으로 이동할 수 있게 @GetMapping 사용하여 ulr 경로를 지정

- 등록할 상품을 정하고 저장할 수 있게 @PostMapping 사용하여 동일한 경로 지정

- 기존에 Model model로 객체를 담아두고 addAttribute()를 사용해서 전달했다면, @ModelAttribute을 사용하면 addAttribute() 메서드를 사용하지 않고도 객체를 전달할 수 있다.

- RedirectAttributes를 사용하여 상품이 저장되었을 때 화면에 메시지를 출력할 수 있게 설정했다.

    // 상품 등록 폼
    @GetMapping("/add")
    public String addForm(Model model) {
        return "items/addForm";
    }

    // 상품 추가 후 저장
    @PostMapping("/add")
    public String addItem(@ModelAttribute Item item, RedirectAttributes redirectAttributes){

        Item savedItem  = itemRepository.save(item);

        redirectAttributes.addAttribute("itemId", savedItem.getId());
        redirectAttributes.addAttribute("status", true);
        return "redirect:/items/{itemId}";

 

 

상품 상세 페이지 뷰 템플릿

 resources/templates/items/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='@{/items/{itemId}/edit(itemId=${item.id})}'|"
                    type="button" th:text="#{page.updateItem}">상품 수정</button>
        </div>

        <div class="col">
            <button class="w-100 btn btn-secondary btn-lg"
                    onclick="location.href='items.html'"
                    th:onclick="|location.href='@{/items}'|"
                    type="button" th:text="#{page.items}">목록으로</button>
        </div>
    </div>
</div> <!-- /container -->
</body>
</html>

 

뷰 템플릿에서 th:if=조건이 참(true) 이면 실행된다. 

<h2 th:if="${param.status}" th:text="'저장 완료!'"></h2>

상품 등록 컨트롤러에서 사용한 RedirectAttributes의 "status" (key) 값이 true 일 때 위 조건이 실행되므로 상품 등록이 완료되면 화면에 "저장완료"로 표기가 된다. 

redirectAttributes.addAttribute("itemId", savedItem.getId());
redirectAttributes.addAttribute("status", true);

 

상품 등록 후 나오는 뷰를 살펴보면 url에 status=true 상태값과 페이지에 " 저장 완료! " 가 나오는 것을 볼 수 있다.

 

 

 

 

상품 등록 폼 뷰 템플릿

 resources/templates/items/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 th:text="#{page.addItem}">상품 등록</h2>
  </div>

  <h4 class="mb-3">상품 입력</h4>

  <form action="item.html" th:action th:object="${item}" method="post">

    <div>
      <label for="itemName" th:text="#{label.item.itemName}">상품명</label>
      <input type="text" id="itemName" th:field="*{itemName}" class="form-control" 
      		placeholder="이름을 입력하세요">
    </div>

    <div>
      <label for="price" th:text="#{label.item.price}">가격</label>
      <input type="text" id="price" th:field="*{price}" class=" form-control"
             placeholder="가격을 입력하세요">
    </div>

    <div>
      <label for="quantity" th:text="#{label.item.quantity}">수량</label>
      <input type="text" id="quantity" th:field="*{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" th:text="#{button.save}">
        상품 등록</button>

      </div>

      <div class="col">
        <button class="w-100 btn btn-secondary btn-lg"
                onclick="location.href='items.html'"
                th:onclick="|location.href='@{/items}'|"
                type="button" th:text="#{button.cancel}">취소</button>

      </div>

    </div>

  </form>

</div> <!-- /container -->
</body>
</html>

 

form 태그를 살펴보면 th:action과 th:object 가 있는데,

th:action은 HTML form에서 acition 값이 없으면 현재 URL에 데이터를 전송한다.

상품 등록 폼과 등록을 하는 URL을 /basic/items/add로 동일하게 사용하여 HTTP 메서드로 기능을 구분하게 했다.

- GET  /basic/items/add

- POST  /basic/itmes/add 

이렇게 동일한 메서드로 보다 깔끔하게 URL을 처리했다.

 <form action="item.html" th:action th:object="${item}" method="post">

 

th:object은 form 태그에서 받은 데이터가 th:object로 설정해준 {item} 객체로 받아진다.

 

 

서버 실행 후 상품 상세 페이지상품 등록 페이지로 접속하면 아래와 같이 위에서 설정한 URL대로 화면이 나오는 것을 확인할 수 있다.

 

     상품 상세 페이지                                                                              상품 등록 페이지

http://localhost:8080/items/1                                                         http://localhost:8080/items/add

 

7. 등록한 상품을 수정하는 페이지컨트롤러뷰 템플릿을 작성한다.

 

컨트롤러 전체 코드

 web/item/ItemController.java

더보기
package magazink.re.web.item;

import lombok.RequiredArgsConstructor;

import magazink.re.domain.item.Item;
import magazink.re.domain.item.ItemRepository;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import java.util.*;

@Controller
@RequiredArgsConstructor
@RequestMapping("/items")
public class ItemController {

    private final ItemRepository itemRepository;
    
    // 아이템 리스트 화면
    @GetMapping
    public String items(Model model) {
        List<Item> items = itemRepository.findAll();
        model.addAttribute("items", items);
        return "items/items";
    }

    // 아이템 상세 페이지
    @GetMapping("/{itemId}")
    public String item(@PathVariable Long itemId, Model model) {
        Item item = itemRepository.findById(itemId);
        model.addAttribute("item", item);
        return "items/item";
    }

    // 상품 등록 폼
    @GetMapping("/add")
    public String addForm(Model model) {
        //로그인 여부 체크
        model.addAttribute("item", new Item());
        return "items/addForm";
    }

    // 상품 추가 후 저장
    @PostMapping("/add")
    public String addItem(@ModelAttribute Item item, RedirectAttributes redirectAttributes){

        Item savedItem  = itemRepository.save(item);

        redirectAttributes.addAttribute("itemId", savedItem.getId());
        redirectAttributes.addAttribute("status", true);
        return "redirect:/items/{itemId}";
    }

    // 상품 수정 폼
    @GetMapping("/{itemId}/edit")
    public String editForm(@PathVariable Long itemId, Model model) {
        Item item = itemRepository.findById(itemId);
        model.addAttribute("item", item);
        return "items/editForm";
    }

    // 상품 수정 처리
    @PostMapping("/{itemId}/edit")               // @ModelAttribute("item") 도 동일
    public String edit(@PathVariable Long itemId, @ModelAttribute Item item){
        itemRepository.update(itemId, item);
        return"redirect:/items/{itemId}";
    }
}

 

상품 수정 폼

- 상품 등록 폼과 마찬가지로 동일한 URL주소로 받고, HTTP메서드 구분을 통해 클라이언트 요청을 처리한다.

- 수정할 상품을 @PathVariable으로itemId을 받아오고, 모델(Model)에 담아둔다.

- 받아온 itemId로 itemRepository에서 찾아 model에 담고, addAttribute("key", "value") 메서드를 이용해 view에 전달할 데이터를 "item" (key) item (value)로 전달한다.

    // 상품 수정 폼
    @GetMapping("/{itemId}/edit")
    public String editForm(@PathVariable Long itemId, Model model) {
        Item item = itemRepository.findById(itemId);
        model.addAttribute("item", item);
        return "items/editForm";
    }

 

상품 수정 처리 폼

- @PostMapping 사용하여 수정폼과 동일한 URL경로 지정

- @ModelAttribute을 사용하여 addAttribute() 메서드를 사용하지 않고 객체를 전달한다.

- @PathVariable으로itemId itemRepository에 업데이트한다. 

- 스프링에서 리다이렉트를 지원하기 때문에 redirect:/로 저장 후 경로를 지정해 주었다.

※ itemRepository.update(itemId, Item) itemId는 @PathVariable으로itemId이고, item 은 model에 저장된 객체다.

    // 상품 수정 처리
    @PostMapping("/{itemId}/edit")  // @ModelAttribute("item") 도 동일
    public String edit(@PathVariable Long itemId, @ModelAttribute Item item){
        itemRepository.update(itemId, item);
        return"redirect:/items/{itemId}";
    }

 

 

상품 수정 폼 뷰 템플릿

상품 등록 폼 뷰 템플릿과 동일하다.

resources/templates/items/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  th:text="#{page.updateItem}">상품 수정 폼</h2>
  </div>

  <form action="item.html" th:action th:object="${item}" method="post">

    <div>
      <label for="id" th:text="#{label.item.id}">상품 ID</label>
      <input type="text" id="id" th:field="*{id}" class="form-control" readonly>
    </div>

    <div>
      <label for="itemName" th:text="#{label.item.itemName}">상품명</label>
      <input type="text" id="itemName" th:field="*{itemName}" class="form-control">
    </div>

    <div>
      <div>
        <label for="price" th:text="#{label.item.price}">가격</label>
        <input type="text" id="price" th:field="*{price}" class="form-control">
    </div>

      <div>
        <label for="quantity" th:text="#{label.item.quantity}">수량</label>
        <input type="text" id="quantity" th:field="*{quantity}" class="form-control">
      </div>

    <hr class="my-4">

    <div class="row">

      <div class="col">
        <button class="w-100 btn btn-primary btn-lg" type="submit" th:text="#{button.save}">
        저장</button>
      </div>

      <div class="col">
        <button class="w-100 btn btn-secondary btn-lg"
                onclick="location.href='item.html'"
                th:onclick="|location.href='@{/items/{itemId}(itemId=${item.id})}'|"
                type="button" th:text="#{button.cancel}">>취소</button>
      </div>

    </div>

  </form>

</div> <!-- /container -->

</body>

</html>

 

 

서버 실행 후 상품 수정을 누르면 아래와 같이 위에서 설정한 URL대로 화면이 나오는 것을 확인할 수 있다.

 

상품 수정 페이지

 http://localhost:8080/items/1/edit       

'study > Spring' 카테고리의 다른 글

웹 페이지 만들기 - 1 (복습)  (0) 2023.03.28
chap 14 - @PathVariable, 익셉션 처리  (0) 2023.02.27
chap 13 - Cookie  (0) 2023.02.23
chap 13 - HandlerInterceptor  (0) 2023.02.23
chap 13 - HttpSession  (0) 2023.02.21
댓글