07 웹 계층 개발 - 상품 등록/조회/수정

shin·2023년 11월 26일
0

1. 상품 등록

1) BookForm

package jpabook.jpashop.web;

import lombok.Getter;
import lombok.Setter;

@Getter @Setter
public class BookForm {

    private Long id;

    private String name;
    private int price;
    private int stockQuantity;

    private String author;
    private String isbn;

}
  • 상품은 수정 기능도 수행을 해야하기 때문에 id 값이 있어야 함

2) ItemController

package jpabook.jpashop.web;

import jpabook.jpashop.domain.item.Book;
import jpabook.jpashop.service.ItemService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

@Controller
@RequiredArgsConstructor
public class ItemController {

    private final ItemService itemService;

    @GetMapping(value = "/items/new")
    public String createForm(Model model){

        model.addAttribute("form", new BookForm());
        return "items/createItemForm";

    }

    @PostMapping(value = "/items/new")
    public String create(BookForm form){

        Book book = new Book();
        book.setName(form.getName());
        book.setPrice(form.getPrice());
        book.setStockQuantity(form.getStockQuantity());
        book.setAuthor(form.getAuthor());
        book.setIsbn(form.getIsbn());

        itemService.saveItem(book);
        return "redirect:/items";

    }

}
  • setter를 생략하는 방향으로 개발하는 것이 더 좋지만, 예제 수행을 위해서 일단 setter로 값을 세팅해줌

3) View : createItemForm

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head th:replace="fragments/header :: header" />
<body>
<div class="container">
    <div th:replace="fragments/bodyHeader :: bodyHeader"/>
    <form th:action="@{/items/new}" th:object="${form}" method="post">
        <div class="form-group">
            <label th:for="name">상품명</label>
            <input type="text" th:field="*{name}" class="form-control"
                   placeholder="이름을 입력하세요">
        </div>
        <div class="form-group">
            <label th:for="price">가격</label>
            <input type="number" th:field="*{price}" class="form-control"
                   placeholder="가격을 입력하세요">
        </div>
        <div class="form-group">
            <label th:for="stockQuantity">수량</label>
            <input type="number" th:field="*{stockQuantity}" class="formcontrol" placeholder="수량을 입력하세요">
        </div>
        <div class="form-group">
            <label th:for="author">저자</label>
            <input type="text" th:field="*{author}" class="form-control"
                   placeholder="저자를 입력하세요">
        </div>
        <div class="form-group">
            <label th:for="isbn">ISBN</label>
            <input type="text" th:field="*{isbn}" class="form-control"
                   placeholder="ISBN을 입력하세요">
        </div>
        <button type="submit" class="btn btn-primary">Submit</button>
    </form>
    <br/>
    <div th:replace="fragments/footer :: footer" />
</div> <!-- /container -->
</body>
</html>
  • 만약 이름이 다르게 설정되어 있으면 컴파일 단계에서 오류가 발생함
    • form 뿐만 아니라, 내부 필드의 이름도 오류 확인 가능
    • = createForm에서 빈 폼이라도 넘겨주는 이유
  • 상품 등록 폼에서 데이터를 입력하고 submit 버튼을 클릭하면 /items/new를 POST 방식으로 요청
  • 상품 저장이 끝나면 상품 목록 화면(redirect:/items)으로 리다이렉트


2. 상품 목록 조회

1) ItemController

...
public class ItemController {

    ...
    
    /**
    * 상품 목록
     */
    @GetMapping(value = "items")
    public String list(Model model){
        
        List<Item> items = itemService.findItems();
        model.addAttribute("items", items);
        return "items/itemList";
        
    }

}

2) View : itemList

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head th:replace="fragments/header :: header" />
<body>
<div class="container">
    <div th:replace="fragments/bodyHeader :: bodyHeader"/>
    <div>
        <table class="table table-striped">
            <thead>
            <tr>
                <th>#</th>
                <th>상품명</th>
                <th>가격</th>
                <th>재고수량</th>
                <th></th>
            </tr>
            </thead>
            <tbody>
            <tr th:each="item : ${items}">
                <td th:text="${item.id}"></td>
                <td th:text="${item.name}"></td>
                <td th:text="${item.price}"></td>
                <td th:text="${item.stockQuantity}"></td>
                <td>
                    <a href="#" th:href="@{/items/{id}/edit (id=${item.id})}"
                       class="btn btn-primary" role="button">수정</a>
                </td>
            </tr>
            </tbody>
        </table>
    </div>
    <div th:replace="fragments/footer :: footer"/>
</div> <!-- /container -->
</body>
</html>
  • model에 담아둔 상품 목록인 items를 꺼내서 상품 정보를 출력


3. 상품 수정

1) itemController

...
@Controller
@RequiredArgsConstructor
public class ItemController {
...

    /**
     * 상품 수정 폼
     */
    @GetMapping(value = "/items/{itemId}/edit")
    public String updateItemForm(@PathVariable("itemId") Long itemId, Model model){

        Book item = (Book) itemService.findOne(itemId);

        BookForm form = new BookForm();
        form.setId(item.getId());
        form.setName(item.getName());
        form.setPrice(item.getPrice());
        form.setStockQuantity(item.getStockQuantity());
        form.setAuthor(item.getAuthor());
        form.setIsbn(item.getIsbn());

        model.addAttribute("form", form);
        return "items/updateItemForm";

    }

    /**
     * 상품 수정
     */
    @PostMapping(value = "/items/{itemId}/edit")
    public String updateItem(@ModelAttribute("form") BookForm form){

        Book book = new Book();
        book.setId(form.getId());
        book.setName(form.getName());
        book.setPrice(form.getPrice());
        book.setStockQuantity(form.getStockQuantity());
        book.setAuthor(form.getAuthor());
        book.setIsbn(form.getIsbn());

        itemService.saveItem(book);
        return "redirect:/items";

    }

}

2) View : updateItemForm

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head th:replace="fragments/header :: header" />
<body>
<div class="container">
    <div th:replace="fragments/bodyHeader :: bodyHeader"/>
    <form th:object="${form}" method="post">
        <!-- id -->
        <input type="hidden" th:field="*{id}" />
        <div class="form-group">
            <label th:for="name">상품명</label>
            <input type="text" th:field="*{name}" class="form-control"
                   placeholder="이름을 입력하세요" />
        </div>
        <div class="form-group">
            <label th:for="price">가격</label>
            <input type="number" th:field="*{price}" class="form-control"
                   placeholder="가격을 입력하세요" />
        </div>
        <div class="form-group">
            <label th:for="stockQuantity">수량</label>
            <input type="number" th:field="*{stockQuantity}" class="form-
control" placeholder="수량을 입력하세요" />
        </div>
        <div class="form-group">
            <label th:for="author">저자</label>
            <input type="text" th:field="*{author}" class="form-control"
                   placeholder="저자를 입력하세요" />
        </div>
        <div class="form-group">
            <label th:for="isbn">ISBN</label>
            <input type="text" th:field="*{isbn}" class="form-control"
                   placeholder="ISBN을 입력하세요" />
        </div>
        <button type="submit" class="btn btn-primary">Submit</button>
    </form>
    <div th:replace="fragments/footer :: footer" />
</div> <!-- /container -->
</body>
</html>

상품 수정 폼 이동

  • 수정 버튼을 선택하면 /items/{itemId}/edit URL을 GET 방식으로 요청
  • 그 결과로 updateItemForm() 메서드를 실행하는데, 해당 메서드는 temService.findOne(itemId)를 호출해서 수정할 상품을 조회함
  • 조회 결과를 모델 객체에 담아서 뷰(items/updateItemForm)에 전달

상품 수정 실행

  • 상품 수정 폼 HTML에는 상품의 id(hidden), 상품명, 가격, 수량 정보가 있음

  • 상품 수정 폼에서 정보를 수정하고 Submit 버튼을 선택

  • /items/{itemId}/edit URL을 POST 방식으로 요청하고 updateItem()메서드를 실행

  • 이때 컨트롤러에 파라미터로 넘어온 item 엔티티 인스턴스는 현재 준영속 상태임

    • 따라서 영속성 컨텍스트의 지원을 받을 수 없고 데이터를 수정해도 변경 감지 기능은 동작하지 않음
  • 준영속 엔티티는 영속성 엔티티가 관리하지 않는 엔티티를 말함

    • 준영속 엔티티를 수정하는 2가지 방법 : 변경 감지 기능 사용, 병합 사용
    • 자세한 내용은 다음 강의를 통해 공부할 예정


상품 등록/조회/수정 테스트😊

  • 상품 등록 버튼 선택

  • 상품명, 가격, 수량, 저자, ISBN을 입력

  • 입력 후 Submit 버튼을 선택하면 상품 목록 화면으로 자동 리다이렉트가 됨

  • 테스트용 데이터베이스에도 값이 저장된 것을 확인할 수 있음
package jpabook.jpashop.domain.item;
...
@Entity
@DiscriminatorValue("B")
@Getter @Setter
public class Book extends Item {

    private String author;
    private String isbn;

}
  • Book Entity를 저장하면 type으로 B가 저장이 되도록 @DiscriminatorValue("B") 어노테이션을 활용하여 구현했기 때문에 Dtype 값으로 B가 자동 저장이 됨

  • 상품 목록에서 수정 버튼을 선택한 후 상품 정보를 위와 같이 수정함

  • 수정된 정보로 조회되는 것을 확인할 수 있음

  • 데이터베이스에도 값이 업데이트됨


강의 : 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발

profile
Backend development

0개의 댓글