이 내용은 스프링 부트 쇼핑몰 프로젝트 with JPA 책을 학습한 내용입니다.
1. MainItemDto
- 메인화면에 출력할 데이터를 위한 DTO 객체
- 사용자에게 보여질 내용만 포함하고 있음 (등록날짜, 수정날짜, 등록자 등 제외)
import com.querydsl.core.annotations.QueryProjection;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class ItemMainDto {
private Long id;
private String itemNm;
private String itemDetail;
private String imgUrl;
private Integer price;
@QueryProjection
public ItemMainDto(Long id, String itemNm, String itemDetail, String imgUrl, Integer price) {
this.id = id;
this.itemNm = itemNm;
this.itemDetail = itemDetail;
this.imgUrl = imgUrl;
this.price = price;
}
}
2. @QueryProjection
- Entity 객체를 DTO 객체로 바로 반환하도록 지원하는 어노테이션
- @QueryProjection을 이용하여 상품 조회 시 DTO 객체로 결과 값을 받는 방법
- @QueryProjection을 이용하면 Item 객체로 값을 받은 후 DTO 클래스로 변환하는 과정 없이 바로 DTO 객체 뽑아낼 수 있음
3. 사용자 정의 인터페이스 리포지토리 생성
- 기존의 ItemRepositoryCustom 클래스에 조회문 추가
import kr.spring.item.dto.ItemMainDto;
import kr.spring.item.dto.ItemSearchDto;
import kr.spring.item.entity.Item;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
public interface ItemRepositoryCustom {
// 메인 페이지에 보여줄 상품 리스트를 가져오는 메소드
Page<ItemMainDto> getItemMainPage(ItemSearchDto itemSearchDto, Pageable pageable);
}
- ItemRepositoryCustomImpl 클래스에서 getMainItemPage() 메소드 구현
// 검색어가 포함된 상품 조회 조건 BooleanExpression
private BooleanExpression itemNmLike(String searchQuery) {
return StringUtils.isEmpty(searchQuery) ? null : item.itemNm.like("%" + searchQuery + "%");
}
@Override
public Page<ItemMainDto> getItemMainPage(ItemSearchDto itemSearchDto, Pageable pageable) {
QItem item = QItem.item;
QItemImg itemImg = QItemImg.itemImg;
QueryResults<ItemMainDto> results = queryFactory
// MainItemDto 객체를 반환
// 멤버변수 초기화는 조회된 결과값에서 MainItemDto 객체 생성자를 통해 지정
// 즉, db 조회 결과는 itemImg-item 조인된 결과가 반환되지만 그 중 일부만 사용
.select(
new QItemMainDto(
item.id,
item.itemNm,
item.itemDetail,
itemImg.imgUrl,
item.price)
)
.from(itemImg)
// itemImg 테이블의 item 필드가 참조하는 item 테이블 조인
.join(itemImg.item, item)
// 5개의 이미지중에서 대표사진만을 조회
.where(itemImg.repimgYn.eq("Y"))
.where(itemNmLike(itemSearchDto.getSearchQuery()))
.orderBy(item.id.desc())
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetchResults();
List<ItemMainDto> content = results.getResults();
long total = results.getTotal();
return new PageImpl<>(content, pageable, total);
}
- ItemService 에 getMainItemPage() 메소드 수행 로직 추가
@Transactional(readOnly = true)
public Page<ItemMainDto> getItemMainDto(ItemSearchDto itemSearchDto, Pageable pageable) {
return itemRepository.getItemMainPage(itemSearchDto, pageable);
}
4. Main Controller
- 화면 header Navbar 안에 있는 "search" 부분에서 검색 조건 입력QueryString 넘어오는 searchQuery 변수를 ItemSearchDto 객체의 멤버변수에 초기화
- QueryString 으로 넘어오는 "page" 변수를 page 파라미터에 넘김
@Controller
@RequiredArgsConstructor // @Autowired로도 쓸 수 있음
public class MainController {
// @Autowired -> @RequiredArgsConstructor를 써도 됨 (final 붙여야함)
private final ItemService itemService;
@GetMapping("/")
public String main(ItemSearchDto itemSearchDto, Optional<Integer> page, Model model) {
Pageable pageable = PageRequest.of(page.isPresent() ? page.get() : 0, 6);
Page<ItemMainDto> items = itemService.getItemMainDto(itemSearchDto, pageable);
model.addAttribute("items", items);
model.addAttribute("itemSearchDto", itemSearchDto);
model.addAttribute("maxPage", 5);
return "main";
}
}
5. Main Page
- 배너는 BootStrap Carousel 컴포넌트 사용
- 상품은 BootStrap Card 컴포넌트 사용
- 상품 이미지를 요청하는 src URL 은 상품 이미지 정보에 담긴 imgUrl 로 지정
<img th:src="${item.imgUrl}" class="card-img-top" th:alt="${item.itemNm}" height="300">
6. 상품 이미지 파일 불러오기 동작 원리
- application.properties 파일
- 로컬 내에 파일이 저장된 경로를 uploadPath로 지정
# 리소스 업로드 경로
uploadPath=file:///D:/shop/
- WebMvcConfigurer 클래스
- "/images/"로 시작하는 URL 패턴의 요청이 들어오면 uploadPath를 기준으로 파일 탐색
// 이미지 업로드 파일 경로
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
//파일 올리고 이미지 올리고 이럴 때 웹이 로컬을 막 건들면 안되니까 설정 파일을 하나 만들어줌
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
// 이미지/파일을 업로드하는 진짜 경로를 application properties에 uploadPath로 저장해놨는데 그걸 가져오게 하기 위함
@Value(value = "${uploadPath}")
private String uploadPath; // 해당 경로를 사용할 수 있음
@Override
// registry를 등록해서 쓸 수 있게 함
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/images/**") // 접근을 이 주소로 하게 함
.addResourceLocations(uploadPath); // 실제로는 여기인데 (위에 처럼함)
}
}
- SecurityConfig 클래스
- "/images/"로 시작하는 URL 패턴의 요청은 모두 허용
// 메모리를 미리 올려놔야 하기 때문에 bean 붙이기
@Bean
// http 요청에 대한 보안 설정. 페이지 권한, 로그인 페이지, 로그아웃 메소드 설정 예정
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests() // 인증 여부 확인 -> 스프링 3.0 이하 버전은 authorizeRequests()로 설정
// 스프링 3.0 이하 버전은 antMatchers(), mvcMatchers(), regexMatchers()으로 사용
.requestMatchers("/", "/member/**", "/item/**", "/images/**").permitAll() // 아무나 페이지에 들어올 수 있고, member, item 밑에 있는 애들은 모두 permit 허용
- 동작 순서
① src = "images/item/파일명"으로 이미지 파일 요청
② WebConfigurer 에 지정된 핸들러대로 uploadPath를 기준으로 요청 처리
③ "///D:/shop/item/상품명"을 수행하여 일치하는 파일 반환
'Web & Android > 스프링 부트 쇼핑몰 프로젝트 with JPA' 카테고리의 다른 글
[스프링 부트 쇼핑몰 프로젝트 with JPA] 11. 상품 주문 (1) | 2023.10.16 |
---|---|
[스프링 부트 쇼핑몰 프로젝트 with JPA] 10. 상세페이지 (0) | 2023.10.16 |
[스프링 부트 쇼핑몰 프로젝트 with JPA] 8-2. [상품 관리] 상품 목록 페이지 (0) | 2023.10.16 |
[스프링 부트 쇼핑몰 프로젝트 with JPA] 8-1. [상품 관리] 상품 목록 조회 Querydsl (0) | 2023.10.16 |
[스프링 부트 쇼핑몰 프로젝트 with JPA] 7. 상품 수정 (0) | 2023.10.16 |