이 내용은 스프링 부트 쇼핑몰 프로젝트 with JPA 책을 학습한 내용입니다.
- 메인화면에 출력할 데이터를 위한 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; |
| } |
| } |
- Entity 객체를 DTO 객체로 바로 반환하도록 지원하는 어노테이션
- @QueryProjection을 이용하여 상품 조회 시 DTO 객체로 결과 값을 받는 방법
- @QueryProjection을 이용하면 Item 객체로 값을 받은 후 DTO 클래스로 변환하는 과정 없이 바로 DTO 객체 뽑아낼 수 있음
- 기존의 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() 메소드 구현
| |
| 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 |
| |
| |
| |
| .select( |
| new QItemMainDto( |
| item.id, |
| item.itemNm, |
| item.itemDetail, |
| itemImg.imgUrl, |
| item.price) |
| ) |
| .from(itemImg) |
| |
| .join(itemImg.item, item) |
| |
| .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); |
| } |
- 화면 header Navbar 안에 있는 "search" 부분에서 검색 조건 입력QueryString 넘어오는 searchQuery 변수를 ItemSearchDto 객체의 멤버변수에 초기화
- QueryString 으로 넘어오는 "page" 변수를 page 파라미터에 넘김
| @Controller |
| @RequiredArgsConstructor |
| public class MainController { |
| |
| |
| 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"; |
| } |
| } |
- 배너는 BootStrap Carousel 컴포넌트 사용
- 상품은 BootStrap Card 컴포넌트 사용
- 상품 이미지를 요청하는 src URL 은 상품 이미지 정보에 담긴 imgUrl 로 지정
<img th:src="${item.imgUrl}" class="card-img-top" th:alt="${item.itemNm}" height="300">
- 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 { |
| |
| |
| @Value(value = "${uploadPath}") |
| private String uploadPath; |
| |
| @Override |
| |
| public void addResourceHandlers(ResourceHandlerRegistry registry) { |
| registry.addResourceHandler("/images/**") |
| .addResourceLocations(uploadPath); |
| } |
| } |
- SecurityConfig 클래스
- "/images/"로 시작하는 URL 패턴의 요청은 모두 허용
| |
| @Bean |
| |
| public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { |
| |
| http.authorizeHttpRequests() |
| |
| |
| .requestMatchers("/", "/member/**", "/item/**", "/images/**").permitAll() |
① src = "images/item/파일명"으로 이미지 파일 요청
② WebConfigurer 에 지정된 핸들러대로 uploadPath를 기준으로 요청 처리
③ "///D:/shop/item/상품명"을 수행하여 일치하는 파일 반환