프로그래밍/Spring

[Spring boot / JPA] 7. 웹 계층 개발

daykim 2023. 7. 4. 17:58
아래 강의 정리
 

실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발 - 인프런 | 강의

실무에 가까운 예제로, 스프링 부트와 JPA를 활용해서 웹 애플리케이션을 설계하고 개발합니다. 이 과정을 통해 스프링 부트와 JPA를 실무에서 어떻게 활용해야 하는지 이해할 수 있습니다., 스프

www.inflearn.com

 

목차

  • 홈 화면과 레이아웃
  • 회원 등록
  • 회원 목록 조회
  • 상품 등록
  • 상품 목록
  • 상품 수정
  • 변경 감지와 병합(merge)
  • 상품 주문
  • 주문 목록 검색, 취소

 

 

홈 화면과 레이아웃


Log

아래 코드 둘이 같은 의미다.

@Slf4j

Logger log = LoggerFactory.getLogger(getClass());

 

HTML

<html xmlns:th="http://www.thymeleaf.org">
<head th:replace="fragments/header :: header">
<div th:replace="fragments/bodyHeader :: bodyHeader" />
<div th:replace="fragments/footer :: footer" />
  • 관례상 th라고 많이 쓴다.
  • th:replace와 같은 속성들을 사용할 수 있다.
  • fragments/~로 replace 한다.

 

Hierarchical-style layouts

  • 예제에서는 뷰 템플릿을 최대한 간단하게 설명하려고 header, footer 같은 템플릿 파일을 반복해서 포함한다.
  • 다음 링크의 Hierarchical-style layouts을 참고하면 이런 부분도 중복을 제거할 수 있다.
  • https://www.thymeleaf.org/doc/articles/layouts.html

 

 

회원 등록


  • @NotEmpty(message = "- 필수입니다.")
    • @Valid
  • BindingResult : 오류가 있을 때, 오류가 여기 담긴다.
    • ~.html 파일 보면, fields.hasError('name')

 

 

회원 목록 조회


  • 타임리프에서 ?를 사용하면 null 을 무시한다.
  • *** API를 만들 때, 엔티티를 절대 외부에 반환해선 안 된다.
    • 만약 멤버 엔티티를 반환한다면, 예를 들어 패스워드 필드를 추가한다면, 패스워드가 그대로 노출될 수 있고, API 스펙이 바뀌게 된다. 그럼 불안전한 API 스펙이 되고, 사용하기 괴롭게 된다..?
    • 템플릿엔진에선, 선택적으로 사용해도 괜찮다.

 

참고: 폼 객체 vs 엔티티 직접 사용
요구사항이 정말 단순할 때는 폼 객체( MemberForm ) 없이 엔티티( Member )를 직접 등록과 수정 화면 에서 사용해도 된다.
하지만, 화면 요구사항이 복잡해지기 시작하면, 엔티티에 화면을 처리하기 위한 기능이 점점 증가한다. 결과적으로 엔티티는 점점 화면에 종속적으로 변하고, 이렇게 화면 기능 때문에 지저분해진 엔티티는 결국 유지보수하기 어려워진다.
실무에서 엔티티는 핵심 비즈니스 로직만 가지고 있고, 화면을 위한 로직은 없어야 한다. 화면이나 API에 맞는 폼 객체나 DTO를 사용하자. 그래서 화면이나 API 요구사항을 이것들로 처리하고, 엔티티는 최대한 순수 하게 유지하자.

 

 

상품 등록


  • ItemConroller의 create를 보면, setter를 사용하기 보단, 생성자 메서드를 가지고 하는 것이 더 좋은 방법이다.

 

 

상품 목록


  • model 에 담아둔 상품 목록인 items 를 꺼내서 상품 정보를 출력

 

 

상품 수정


  • 예제 단순화를 위해 Book만 사용할 것이다.
  • @PathVariable
  • Id를 조심히 사용해야한다.
    • 누군가 조작해서 넘길 수 있기 때문에, 다른 사람의 데이터가 수정될 수 있다.
    • 유저가 해당 아이템에 대한 권한이 있는지 확인하는 과정이 있어야 한다.

 

 

*** 변경 감지와 병합(merge) ***


준영속 엔티티

  • 영속성 컨텍스트가 더는 관리하지 않는 엔티티를 말한다.
  • 예시로 앞의 상품 수정에서 PostMapping의 itemService.saveItem(book)에서 수정을 시도하는 Book 객체가 있다.
  • Book 객체는 이미 DB에 한 번 저장되어서 식별자가 존재한다.
  • 이렇게 임의로 만들어낸 엔티티도 기존 식별자를 가지고 있으면, 준영속 엔티티로 볼 수 있다.

 

준영속 엔티티를 수정하는 2가지 방법

  • 변경 감지 기능 사용
  • 병합(merge) 사용

 

변경 감지 기능

  • 영속성 컨텍스트에서 엔티티를 다시 조회한 후에 데이터를 수정하는 방법
  • 트랜잭션 안에서 엔티티를 다시 조회, 변경할 값 선택 -> 트랜잭션 커밋 시점에 변경 감지(Dirty Checking)
  • 이 동작해서 데이터베이스에 UPDATE SQL 실행


병합(merge)

  • 준영속 상태의 엔티티를 영속 상태로 변경할 때 사용하는 기능이다.
public void save(Item item) {
    if (item.getId() == null) {
        em.persist(item);
    } else {
        em.merge(item); // 요거
    }
}

 

  • 파라미터 너무 많으면, DTO 만들어서 적용해라. (23: 10)

 

 

상품 주문


  • @RequestParam : form submit 방
  • controller에서 엔티티를 찾아서 넘겨도 되지 않나?
    • 된다. 그렇지만, service에서 찾게끔 하면, JPA가 더 깔끔하게 동작할 수 있다.
    • 할 수 있는게 더 많아진다.
    • 엔티티도 영속 상태로 흘러가기 때문에, 훨씬 깔끔하게 해결된다.
  • 강사님은 주로 커맨드성, 주문은 외부에서 컨트롤러에선 식별자만 넘기고, 찾는건 서비스에서 한다.
    • 조회는 상관없지만, 이외의 비즈니스 로직은 식별자만 넘기고 서비스에서 처리하는 것이 더 좋다.
    • 더티체킹 같은 것이 자연스럽게 되는데, 밖에서 가지고 넘어오면 이러한 것들이 적용이 안 된다.
      => 밖에서 트랜잭션 없이 조회해서 넘어온 것이기 때문이다. 그럼 JPA, 영속성 컨텍스트와 관계 없는 것이 넘어온 것이다.

 

 

주문 목록 검색, 취소


  • findOrders를 보면 단순히 레파지토리에 위임해준다.
    이런 단순한 위임 처럼 단순한 경우엔, Controller에서 바로 repository를 불러도 괜찮다.