웹 계층을 개발하기 위해서 필요한 것은 Contoller
Controller가 뭔데??
URL Mapping 해주고 POST 방법 지정하는 친구 ➡️ model과 view 사이의 징검다리
웹 계층 개발의 전체적인 로직은 회원 관리 예제 포스팅에서 공부했었다.
간단하게 그 로직을 복습해보자면
- 컨트롤러 등록
- Mapping & return
- return 파일(.html) 등록
추가로 이번 예제에서는 뷰 템플릿을 최대한 간단하게 하기 위해 header, footer와 같은 템플릿 파일을 반복해서 포함한다.
이러한 레이아웃을 Hierarchical-style layout
이라고 하며 thymeleaf의 fragments기능을 이용한다 th:replace="fragments/header :: header"
마지막으로 이쁜 화면을 위해 bootstrap의 css,js 와 jumbotron-narrow.css을 추가해준다
resource/static : css, js 추가
resource/static/css : jumbotron-narrow.css 추가
SpringBoot에서 VIEW를 잘 다룬다는 것은 thymleaf를 얼마나 잘 다룬다는 것을 의미하는 것 같다.
추후에 thymeleaf를 이해하고 활용하는 공부를 시작해보자!!
이제 본격적인 웹 계층 개발을 시작해보자
회원 등록
1. 회원 등록 폼 객체
2. 회원 등록 컨트롤러
3. 회원 등록 폼 화면
package jpabook.jpashop.controller;
import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.NotEmpty;
@Getter @Setter
public class MemberForm {
@NotEmpty(message = "회원 이름은 필수 입니다.")
private String name;
private String city;
private String street;
private String zipcode;
}
@Controller
@RequiredArgsConstructor
public class MemberController {
private final MemberService memberService;
@GetMapping("/members/new")
public String createForm(Model model) {
model.addAttribute("memberForm", new MemberForm());
return "members/createMemberForm";
}
@PostMapping("/members/new")
public String create(@Valid MemberForm form, BindingResult result) {
if (result.hasErrors()) {
return "members/createMemberForm";
}
Address address = new Address(form.getCity(), form.getStreet(), form.getZipcode());
Member member = new Member();
member.setName(form.getName());
member.setAddress(address);
memberService.join(member);
return "redirect:/";
}
@GetMapping("/members")
public String list(Model model) {
List<Member> members = memberService.findMembers();
model.addAttribute("members", members);
return "members/memberList";
}
}
placeholder="도시를 입력하세요"> </div>
<div class="form-group">
<label th:for="street">거리</label>
<input type="text" th:field="*{street}" class="form-control" placeholder="거리를 입력하세요">
</div>
<div class="form-group">
<label th:for="zipcode">우편번호</label>
<input type="text" th:field="*{zipcode}" class="form-control"
placeholder="우편번호를 입력하세요"> </div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
<br/>
<div th:replace="fragments/footer :: footer" />
</div> <!-- /container -->
</body>
</html>
html에 코드는 여기에서만 작성하고 이후부터는 생략하겠다.
회원 목록 조회
회원 목록 컨트롤러
회원 목록 뷰
상품 등록
상품 등록 폼
상품 등록 컨트롤러
상품 등록 뷰
상품 목록
상품 목록 컨트롤러
상품 목록 뷰
상품 수정
상품 수정 컨트롤러
상품 수정 폼 화면
이번 강의중 가장 중요한 내용이다!!
매우매우 중요하니 꼭 완벽하게 이해하고 넘어가자
영속성에 관에서는 여기에서 자세히 살펴보도록 하자
준영속 엔티티를 수정하는 2가지 방법
1. 변경 감지
2. 병합(merge)
// 준영속 데이터 수정 - 1.변경감지
@Transactional
public void updateItem(Long itemId, String name, int price, int stockQuantity) {
Item findItem = itemRepository.findOne(itemId);
findItem.setName(name);
findItem.setPrice(price);
findItem.setStockQuantity(stockQuantity);
}
@Transactional
void update(Item itemParam) { //itemParam: 파리미터로 넘어온 준영속 상태의 엔티티
Item mergeItem = em.merge(item);
}
병합을 사용하는 방법이 정말 간단하고 쉬워 보이지만 문제가 있다.
변경감지는 원하는 속성만 바꿀 수 있지만 병합은 모든 속성을 바꿔야한다.
만약 입력값 안넣은 채로 병합시키면 null로 채워진다.
결론, merge쓰지 말고 변경감지를 쓰자!!
추가 팁, Setter 없이 해라.. —> 추적하기 어려워진다
상품주문
- 상품 주문 컨트롤러
- 상품 주문 폼
주문목록 검색, 취소
- 주문 목록 검색/취소 컨트롤러
* 뷰 템플릿 변경사항을 서버 재시작 없이 즉식 변경하기
1. build.gradle : spring-boot-devtools 추가하기
2. html 파일 수정 후 build-> Recompile