홈 화면과 레이아웃
•
tymeleaf를 사용하였다.
•
header,bodyHeader,footer를 thymeleaf의 replace기능을 이용하여 layout을 적용해줬다.
•
bootstrap과 jumbotron-narrow.css를 사용한다(jumbotron-narrow.css는 제공하는 소스를 사용)
home.html
Code Content
fragment::header
Code Content
fragment::bodyHeader
Code Content
fragment::footer
Code Content
jumbotron-narrow.css
Code Content
회원 등록
MemberController:: create
Code Content
members::createMemberForm
Code Content
Tips
•
BindingResult
→ Controller Parameter가 Valid체크가 실패했을경우 bindingResult.hasErrors()를 통해 에러를 담아서 되돌아갈 수 있다.
→ 이 때 form객체도 작성되있던 내용들이 같이 되돌아가서 html페이지에서는 이전 작성내용이 저장될 수 있다.
•
th:object="${memberForm}" / th:field="*{name}"
→ memberForm Object를 선언해준 태그안에서 th:field를 통해 memberForm의 attribute를 꺼내서 바로 사용할 수 있다.
회원 목록 조회
members::memberList
Code Content
MemberController::list
Code Content
Tips
•
thymeleaf에서 ? 을 사용하면 null을 무시한다 (ex: ${member.address?.city})
•
API를 만들 때는 절대로 엔티티를 반환해서는 안된다. (ex: Member일 경우 password 노출)
→ 템플릿엔진에서는 선택적으로 사용해도 된다(server side에서 돌기 때문에)
→ 그래도 DTO나 화면에 맞는 DataObject로 transform 해서 반환하는 것이 좋다.
상품 등록
items::createItemForm
Code Content
Controller::ItemController
Code Content
DTO::BookForm
Code Content
상품 목록
items::itemList
Code Content
ItemController::list
Code Content
상품 수정
items::updateItemForm
Code Content
ItemController::updateItemForm
Code Content
ItemController::updateItem
Code Content
변경 감지와 병합(merge)(Important!!)
준영속 엔티티란?
→영속성 컨텐츠가 더는 관리하지 않는 엔티티이다.
@PostMapping("/items/{itemId}/edit")
public String updateItem(@ModelAttribute("form") BookForm form, @PathVariable String itemId){
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";
}
Java
복사
위 코드를 보자. 위의 새로 생성 된 book객체는 new를 통해 새로 생성된 객체이고, 아직 영속성 컨텍스트에는 등록되지 않았지만, 식별자를 가지고 있다.
이처럼 임의로 엔티티 객체를 만들어 내도 식별자를 가지고 있으면 준영속 엔티티라 한다.
준영속 엔티티를 수정하는 방법
1.
변경 감지 기능
2.
병합(merge)기능
변경 감지 기능 사용
Code Content
병합 기능 사용
Code Content
병합 기능 로직
1.
merge() 실행
2.
파라미터로 넘어온 준영속 엔티티의 식별자 값으로 1차 캐시에서 엔티티 조회
→ 만약 1차 캐시에 엔티티가 없을 경우 DB에서 엔티티 조회 후, 1차 캐시에 저장.
3.
조회한 영속 엔티티에 member엔티티의 값을 채워 넣는다. (member의 모든 값을 mergeMember에 setting해준다.)
4.
영속 상태인 mergeMember를 반환한다.
5.
트랜잭션 커밋 시점에 해당 mergeMember로 update sql을 수행한다.
주의점: 병합(merge)의 경우 모든 값을 변경해주기 때문에 의도하지 않은 변경도 있을수가 있다.
(Ex: 따로 변경을 원하지 않아 null인 값들마저 모두 변경되버릴 수 있다.)
정리
1.
엔티티를 변경할 때는 항상 변경 감지를 사용하도록 하자.
2.
컨트롤러에서 어설프게 엔티티를 생성하지 마세요.
3.
트랜잭션이 있는 서비스 계층에 식별자와 변경할 데이터를 명확하게 전달하자.(파라미터 or DTO)
4.
트랜잭션 커밋 시점에 변경감지가 실행됩니다.
상품 주문
order::orderForm
Code Content
OrderController
Code Content
Tips
•
커맨드성 로직들은 Controller에서는 식별자만 넘겨주고, Service단에서 핵심 로직들을 수행하는게 좋다.
트랜잭션 내부에서 관리되고 엔티티들이 영속상태인데, Controller단에서 엔티티를 파라미터로 받으면, 해당 엔티티는 준영속 상태가 되기때문에 부작용(sideEffect)를 유도할 수 있다.
주문 목록 검색, 취소
OrderController::orderList+cancelOrder
Code Content
order::orderList
CodeContent