[스프링 부트 쇼핑몰 프로젝트 with JPA] 6장 정리
상품 등록, 수정, 관리, 메인 페이지 및 상세 페이지 구성 방법에 대한 내용을 다룹니다. 상품 등록 시 엔티티 객체 변환, 데이터 무결성 보장, 파일 전송, 이미지 업로드 경로 설정 등이 중요하며, 수정 시 트랜잭션 읽기 전용 설정, 예외 처리, 변경 감지 기능 활용이 필요합니다. 상품 관리에서는 복잡한 조회 조건에 Querydsl을 활용하며, 메인 페이지에서는 @QueryProjection을 사용하여 상품 조회 결과를 DTO 객체로 받습니다. 상품 상세 페이지에서는 th:each를 사용하여 구문을 반복하고, th:if를 사용하여 값이 null이거나 빈 문자열일 때 처리를 합니다.
Apr 05, 2024
🌼상품 등록하기
💡핵심 키워드
- 상품 이미지를 저장하는 엔티티는 이미지 파일명, 원본 이미지 파일명, 이미지 조회 경로, 대표 이미지 여부를 가진다.
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.prperties
의ddl-auto
속성을validate
로 변경하면 애플리케이션 실행 시점에 테이블을 삭제한 후 재생성하지 않으며 엔티티와 테이블이 매핑이 정상적으로 되어있는지만 확인한다.- 엔티티의 추가가 필요할 경우
create
와validate
를 번갈아 가면서 사용하면 편하게 개발을 진행할 수 있다.
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
을 생성해야 한다.
Querydsl
을Spring 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