웹 계층 개발

이가현·2022년 10월 28일
0

홈 화면과 레이아웃

홈 컨트롤러 등록

  • SLF4J (Simple Logging Facade for Java) 란?
    java.util.logging, logback 및 log4j와 같은 다양한 로깅 프레임 워크에 대한 추상화(인터페이스) 역할을 하는 라이브러리

최종 사용자가 배포시 원하는 로깅 프레임워크를 결정하고 사용해도 SLF4J가 인터페이스화 되어있기 때문에, SLF4J를 의존하는 클라이언트 코드에서는 실제 구현을 몰라도 됨.

(의존관계 역전 법칙)


회원

회원 등록

  • 폼 객체를 사용해서 화면 계층서비스 계층을 명확하게 분리하기 !!

    📍 참고: 폼 객체 (MemberForm) vs 엔티티 (Member) 직접 사용
    요구사항이 정말 단순할 때를 제외하고 엔티티 (Member)를 직접 등록하면 유지보수가 어려워진다.
    실무에서 엔티티는 핵심 비즈니스 로직만 가지고 있고, 화면을 위한 로직은 없어야 한다.
    화면이나 API 요구사항을 폼 객체나 DTO로 처리하고, 엔티티는 최대한 순수 하게 유지하자.

회원 등록 폼 객체

package jpabook.jpashop.web;
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(value = "/members/new")
    public String createForm(Model model) {
        model.addAttribute("memberForm", new MemberForm());
        return "members/createMemberForm";
    }
    @PostMapping(value = "/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:/";
      }
}

회원 목록 조회

package jpabook.jpashop.web;

@Controller
@RequiredArgsConstructor
public class MemberController {

	//추가
	@GetMapping(value = "/members") public String list(Model model) {
    List<Member> members = memberService.findMembers();
    model.addAttribute("members", members); // 조회한 상품을 뷰에 전달하기 위해 모델 객체에 보관
    
    return "members/memberList"; // 실행할 뷰 이름 반환
    
    }
}

상품

상품 등록

상품 등록 폼은 회원 등록의 경우와 매우 유사하므로 생략.

상품 등록 컨트롤러

@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") // POST 요청
    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"; // 상품 목록 화면으로 리다이렉트
    }
 }
 

상품 수정

@Controller
@RequiredArgsConstructor
public class ItemController {
    /**
    * 상품 수정 폼
    */
    @GetMapping(value = "/items/{itemId}/edit") // GET 요청
    public String updateItemForm(@PathVariable("itemId") Long itemId, Model
model) {
        Book item = (Book) itemService.findOne(itemId); // 준영속 상태 ( book 객체에 해당 ), 수정할 상품 조회
        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")  // POST 요청
    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)

1) ⭐️ ⭐️ ⭐️ 변경 감지 기능 사용 ⭐️ ⭐️ ⭐️

@Transactional
void update(Item itemParam) { //itemParam: 파리미터로 넘어온 준영속 상태의 엔티티
	Item findItem = em.find(Item.class, itemParam.getId()); //같은 엔티티를 조회한 다.
	findItem.setPrice(itemParam.getPrice()); //데이터를 수정한다. 
}

영속성 컨텍스트에서 엔티티를 다시 조회** -> 변경할 값 선택 트랜잭션 커밋 시점에 변경 감지(Dirty Checking) -> 데이터베이스에 UPDATE SQL 실행
-> 데이터 수정

권장 코드

@Controller
@RequiredArgsConstructor
	public class ItemController {
        private final ItemService itemService;
        /**
        *상품 수정,권장 코드
        */
        @PostMapping(value = "/items/{itemId}/edit")
        public String updateItem(@ModelAttribute("form") BookForm form) {
            itemService.updateItem(form.getId(), form.getName(), form.getPrice());
            return "redirect:/items";
        }
	}
@Service
@RequiredArgsConstructor
   
public class ItemService {
	 private final ItemRepository itemRepository;
      /**
      * 영속성 컨텍스트가 자동 변경
      */
      @Transactional
      public void updateItem(Long id, String name, int price) {
          Item item = itemRepository.findOne(id);
          item.setName(name);
          item.setPrice(price);
	}
}

2) 병합 사용

  1. 준영속 엔티티의 식별자 값으로 영속 엔티티 조회
  2. 영속 엔티티의 값을 준영속 엔티티의 값으로 모두 교체 ( = 병합 )
  3. 트랜잭션 커밋 시점에 변경 감지 기능이 동작해서 DB에 UPDATE SQL이 실행됨.
@Transactional
	void update(Item itemParam) { //itemParam: 파리미터로 넘어온 준영속 상태의 엔티티 Item mergeItem = em.merge(item);
}

상품 주문

위의 과정과 매우 유사하므로 생략.

0개의 댓글