[스프링 부트 쇼핑몰 프로젝트 with JPA] 6장 정리

상품 등록, 수정, 관리, 메인 페이지 및 상세 페이지 구성 방법에 대한 내용을 다룹니다. 상품 등록 시 엔티티 객체 변환, 데이터 무결성 보장, 파일 전송, 이미지 업로드 경로 설정 등이 중요하며, 수정 시 트랜잭션 읽기 전용 설정, 예외 처리, 변경 감지 기능 활용이 필요합니다. 상품 관리에서는 복잡한 조회 조건에 Querydsl을 활용하며, 메인 페이지에서는 @QueryProjection을 사용하여 상품 조회 결과를 DTO 객체로 받습니다. 상품 상세 페이지에서는 th:each를 사용하여 구문을 반복하고, th:if를 사용하여 값이 null이거나 빈 문자열일 때 처리를 합니다.
DriedPollack's avatar
Apr 05, 2024
[스프링 부트 쇼핑몰 프로젝트 with JPA] 6장 정리

🌼상품 등록하기

💡핵심 키워드

  • 상품 이미지를 저장하는 엔티티는 이미지 파일명, 원본 이미지 파일명, 이미지 조회 경로, 대표 이미지 여부를 가진다.
item
item_id (PK)
item_nm
price
stock_number
item_detail
item_sell_status
item_img
item_img_id (PK)
item_id (FK)
img_name
ori_img_name
img_url
rep_img_yn
  • 상품을 등록할 때는 화면으로부터 전달받은 DTO 객체를 엔티티 객체로 변환하는 작업을 해야 하고, 상품을 조회할 때는 엔티티 객체를 DTO 객체로 바꿔주는 작업을 해야 한다.
    • 이를 도와주는 라이브러리로 modelmapper 라이브러리가 있다.
    • 이 라이브러리는 서로 다른 클래스의 값을 필드의 이름과 자료형이 같으면 getter, setter를 통해 값을 복사해서 객체를 반환해준다.
  • 실제로 쇼핑몰은 엄청난 양의 데이터를 입력해야 한다.
  • 상품 등록 같은 관리자 페이지에서 중요한 것은데이터의 무결성을 보장해야 한다는 것이다.
    • 데이터가 의도와 다르게 저장된다거나, 잘못된 값이 저장되지 않도록 validation을 해야 한다.
    • 보통 데이터를 검증할 때는 스크립트에서 validation 을 한 번 하고, 스크립트는 사용자가 변경이 가능하므로 서버에서 한 번 더 validation을 한다.
      • 스크립트에서 validation을 하는 이유는 서버쪽으로 요청을 하면 네트워크를 통해 서버에 요청이 도착하고 다시 그 결과를 클라이언트에 반환하는 등 리소스를 소모하기 때문이다.
  • 파일을 전송할 때는 form 태그에 enctype 값으로 multipart/form-data 를 입력한다.
    • 이 속성은 method 속성값이 post인 경우에만 사용할 수 있다.
  • 타임리프의 유틸리티 객체 #numbers.secquence(start, end) 를 이용하면 start부터 end까지 반복 처리를 할 수 있다.
  • 타임리프의 유틸리티 객체인 #string.isEmpty(string) 을 이용해서 저장된 이미지 정보의 여부를 확인할 수 있다.
  • application.prpertiesddl-auto 속성을 validate로 변경하면 애플리케이션 실행 시점에 테이블을 삭제한 후 재생성하지 않으며 엔티티와 테이블이 매핑이 정상적으로 되어있는지만 확인한다.
    • 엔티티의 추가가 필요할 경우 createvalidate를 번갈아 가면서 사용하면 편하게 개발을 진행할 수 있다.
  • application.properties 파일에서 이미지와 관련된 다음과 같은 설정을 추가할 수 있다.
    • #파일 한 개당 최대 사이즈 spring.servlet.multipart.maxFileSize=20MB #요청당 최대 파일 크기 spring.servlet.multipart.maxRequestSize=100MB #상품 이미지 업로드 경로 itemImgLocation=C:/shop/item #리소스 업로드 경로 uploadPath=file:///C:/shop/
  • WebMvcConfigurer 인터페이스를 구현하는 클래스를 통해 자신의 로컬 컴퓨터에 업로드한 파일을 찾을 위치를 설정할 수 있다.
  • UUID(Universally Unique Identifier)는 서로 다른 개체들을 구별하기 위해서 이름을 부여할 때 사용한다.
  • FileOutputStream 클래스는 바이트 단위의 출력을 내보내는 클래스다. 생성자로 파일이 저장될 위치와 파일의 이름을 넘겨 파일에 쓸 파일 출력 스트림을 만든다.
  • @Value 어노테이션을 통해 application.properties 파일에 등록한 상품 이미지 업로드 경로 값을 변수에 지정할 수 있다.
  • 서버를 운영하는데 화면이 너무 복잡하고 서로 얽혀있는 데이터가 많으면 Vue.js 프레임워크를 도입하는 것도 좋은 방법이다.
 

🌼상품 수정하기

💡핵심 키워드

  • @Transactional(readonly = true) 어노테이션을 통해 트랜잭션을 읽기 전용으로 설정할 수 있다.
    • 이럴 경우 JPA가 더티체킹(변경감지)을 수행하지 않아서 성능읗 향상시킬 수 있다.
  • repository에서 객체를 조회할 때 .orElseThrow(EntityNotFoundException::new)를 사용하면 객체가 존재하지 않을 때 예외처리를 할 수 있다.
  • 변경감지 기능을 이용하면 .save() 로직을 호출하지 않았는데도 트랜잭션이 끝날 때 update 쿼리가 실행된다.
    • 이 경우 중요한 것은 엔티티가 영속 상태여야 한다는 것이다.
 

🌼상품 관리하기

💡핵심 키워드

  • 조회 조건으로 설정할 값은 다음과 같다.
    • 상품 등록일
    • 상품 판매 상태
    • 상품명 또는 상품 등록자 아이디
  • 이런 식으로 조회 조건이 복잡한 화면은 Querydsl을 이용해 조건에 맞는 쿼리를 동적으로 쉽게 생성할 수 있다.
  • Querydsl을 사용하기 위해서는 QDomain을 생성해야 한다.
  • QuerydslSpring Data Jpa와 함께 사용하기 위해서는 사용자 정의 리포지토리를 정의해야 한다. 총 3단계의 과정으로 구현한다.
    • 사용자 정의 인터페이스 작성
    • 사용자 정의 인터페이스 구현
    • Spring Data Jpa 리포지토리에서 사용자 정의 인터페이스 상속
  • 사용자 정의 인터페이스를 구현하는 클래스는 클래스명 끝에 Impl을 붙여주어야 정상적으로 동작한다.
  • Querydsl에서는 BooleanExpression이라는 where 절에서 사용할 수 있는 값을 지원한다.
  • 동적으로 쿼리를 생성하기 위해서 JPAQueryFactory 클래스를 사용한다.
    • selectForm(QItem.item) : 상품 데이터를 조회하기 위해 QItem의 Item을 지정한다.
    • where 조건절 : BooleanExpression을 반환하는 조건문들을 넣어준다. , 단위로 넣어줄 경우 and 조건으로 인식한다.
    • offset : 데이터를 가지고 올 시작 인덱스를 지정한다.
    • limit : 한 번에 사지고 올 최대 개수를 지정한다.
    • fetchResult() : 조회한 리스트 및 전체 개수를 포함하는 QueryResults를 반환한다.상품 데이터 리스트 조회 및 상품 데이터 전체 개수를 조회하는 2번의 쿼리문이 실행된다.
  • BooleanExpression 을 리턴할 때 null을 리턴하면 where절에서 해당 조건은 무시된다.
  • dateTime.minus기간() 메소드를 이용해서 dateTime의 시간을 특정 식단 이전으로 설정할 수 있다.
  • Querydsl 의 결과 조회 메소드는 여러 가지가 있다.
    • 메소드
      기능
      QueryResults<T> fetchResults()
      조회 대상 리스트 및 전체 개수를 포함하는 QueryResults 반환
      List<T> fetch()
      조회 대상 리스트 반환
      T fetchOne()
      조회 대상이 1건이면 해당 타입 반환. 조회 대상이 1건 이상이면 에러 발생
      T fetchFirst()
      조회 대상이 1건 또는 1건 이상이면 1건만 반환
      long fetchCount()
      해당 데이터 전체 개수 반환. count 쿼리 실행
  • PageRequest.of 메소드를 이용해서 Pageable 객체를 생성할 수 있다.
    • 첫 번째 파라미터로는 조회할 페이지 번호, 두 번째 파라미터로는 한 번에 가지고 올 데이터 수를 넣어준다.
  • th:with 은 변수값을 정의할 때 사용한다. 페이지 시작 번호와 페이지 끝 번호를 구해서 저장한다.
    • 페이지 시작 번호 = (현재 페이지 번호 / 보여줄 페이지 수) + 1
    • 페이지 끝 번호 = 페이지 시작 번호 + (보여줄 페이지 수 - 1)
 

🌼메인 화면

💡핵심 키워드

  • @QueryProjection 을 이용하여 상품 조회 시 DTO객체로 결과값을 받을 수 있다.
  • QMainItemDto 의 생성자에 반환할 값들을 넣어주면 @QueryProjection 을 사용했을 때 DTO로 바로 조회가 가능하다.
  • .join(itemImg, item) 메소드를 통해 itemImg와 item을 내부 조인할 수 있다.
  • 부트스트랩의 슬라이드를 보여주는 Carousel 컴포넌트를 이용해서 배너를 만들 수 있다.
  • <img> 태그의 src 속성에는 웹상에 존재하는 이미지 경로를 넣어주면 해당 이미지를 보여줄 수 있다.
 

🌼상품 상세 페이지

💡핵심 키워드

  • th:each를 통해서 구문을 반복할 수 있다.
  • <div th:if="${not #strings.isEmpty(examData)}"> ... </div> 를 이용하면 들어온 값이 null 이거나 빈 문자열일 때 true 를 반환한다.
 

🏁결론

해당 내용을 정리하면서 상품 등록 및 수정 페이지와 등록한 상품을 조회하는 관리 페이지, 고객이 상품을 볼 수 있는 메인 페이지, 상품 상세페이지를 구성하는 법을 이해할 수 있었다.
Share article

More articles

See more posts

👨🏻‍💻DriedPollack's Blog