[DMD] 6-3. 웹 개발 - Item, 변경 감지와 병합

sorzzzzy·2021년 12월 19일
0

Spring Project

목록 보기
8/18
post-thumbnail

이번 시간에는 상품 수정에 관련된 부분과 변경감지와 병합에 대해 다뤄보겠다.


🏷 상품 수정

✔️ 상품 수정 컨트롤러 - ItemController

    // 상품 수정
    // itemId는 변경될 수 있으므로 @PathVariable 사용
    @GetMapping("items/{itemId}/edit")
    public String updateItemForm(@PathVariable("itemId") Long itemId, Model model) {
        // 캐스팅을 하는 것이 좋은 방법은 아니나, 여기서는 예제 단순화를 위해 사용
        Food item = (Food) itemService.findOne(itemId);

        // 폼을 업데이트 할 건데, 엔티티가 아닌 BookForm 을 보냄
        FoodForm form = new FoodForm();
        form.setId(item.getId());
        form.setName(item.getName());
        form.setPrice(item.getPrice());
        form.setStockQuantity(item.getStockQuantity());
        form.setAllowance(item.getAllowance());
        form.setIngredient(item.getIngredient());

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

    @PostMapping("items/{itemId}/edit")
    // @PathVariable Long itemId : 폼에서 그대로 넘어오기 때문에 생략해도 됨
    // @ModelAttribute("form") : 폼에서 오브젝트 이름으로 설정한 부분이 그대로 넘어옴
    public String updateItem(@PathVariable Long itemId, @ModelAttribute("form") FoodForm form) {

        itemService.updateItem(itemId, form.getName(), form.getPrice(), form.getStockQuantity());

        return "redirect:/items";
    }

✔️ 상품 수정 폼 화면 - updateItemForm.html

<!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="allowance">권장량</label>
            <input type="text" th:field="*{allowance}" class="form-control"  placeholder="하루 권장량을 입력하세요" />
        </div>
        <div class="form-group">
            <label th:for="ingredient">주재료</label>
            <input type="text" th:field="*{ingredient}" class="form-control"  placeholder="들어가는 주재료를 입력하세요" />
        </div>
        <button type="submit" class="btn btn-primary">Submit</button>
    </form>

    <div th:replace="fragments/footer :: footer" />

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

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

🏷 변경 감지와 병합

변경 감지와 병합은 정말 중요한 내용으로, 이전에 따로 포스팅 한 적이 있으므로 이번 시간에는 핵심만 간단히 짚고 넘어가겠다!

📌 변경 감지와 병합 포스팅 참고

준영속 엔티티란🤔?
➡️ 영속성 컨텍스트가 더는 관리하지 않는 엔티티를 말한다.

준영속 엔티티를 수정하는 2가지 방법
1. 변경 감지 기능 사용

  • 영속성 컨텍스트에서 엔티티를 다시 조회한 후에 데이터를 수정하는 방법
  • 트랜잭션 안에서 엔티티를 다시 조회, 변경할 값 선택 트랜잭션 커밋 시점에 변경 감지(Dirty Checking) 이 동작해서 데이터베이스에 UPDATE SQL 실행
  1. 병합( merge ) 사용
  • 병합은 준영속 상태의 엔티티를 영속 상태로 변경할 때 사용하는 기능

    ➡️ merge()를 실행한다.
    ➡️ 파라미터로 넘어온 준영속 엔티티의 식별자 값으로 1차 캐시에서 엔티티를 조회한다.
    ➡️ 만약 1차 캐시에 엔티티가 없으면 데이터베이스에서 엔티티를 조회하고, 1차 캐시에 저장한다.
    ➡️ 조회한 영속 엔티티에 member 엔티티의 값을 채워 넣는다.
    (member 엔티티의 모든 값을 mergeMember에 밀어 넣는다.
    이때 mergeMember의 “회원1”이라는 이름이 “회원명변경”으로 바뀐다.)
    ➡️ 영속 상태인 mergeMember를 반환한다.

가장 좋은 방법은, 엔티티를 변경할 때는 항상 변경 감지를 사용하는 것!

  • 컨트롤러에서 어설프게 엔티티를 생성하지 말 것.
  • 트랜잭션이 있는 서비스 계층에 식별자(id)와 변경할 데이터를 명확하게 전달 (파라미터 or dto)
  • 트랜잭션이 있는 서비스 계층에서 영속 상태의 엔티티를 조회하고, 엔티티의 데이터를 직접 변경
  • 트랜잭션 커밋 시점에 변경 감지가 실행됨

📌 권장 코드

@PostMapping(value = "/items/{itemId}/edit")
public String updateItem(@ModelAttribute("form") BookForm form) {
    itemService.updateItem(form.getId(), form.getName(), form.getPrice());
    return "redirect:/items";
            
profile
Backend Developer

0개의 댓글