설명
상품 클릭 시 상품의 상세한 내용을 보여주는 기능을 구현하도록 하겠습니다.
Database
data.sql
테스트를 위해서 discount 값을 수정합니다.
...
-- 청바지
INSERT INTO product
(name, price, description, image_url, color, size, category_id, discount, create_timestamp, update_timestamp)
VALUES('Blue Jeans', 39800, '일자 청바지입니다.', '/images/pants-2.png', 'Blue,Black', '28,29,30,31,32,33,34', 103, 10, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);
...
Dependencies
build.gradle
DTO 와 Entity 변환을 수월하게 하기 위해서 modelMapper 를 추가합니다.
dependencies {
...
compile group: 'org.modelmapper', name: 'modelmapper', version: '2.3.5'
...
}
Back-End
BeanConfiguration.java
Bean 정의를 모아놓을 클래스를 작성합니다.
package happygram.ecommerce.config;
import org.modelmapper.ModelMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class BeanConfiguration {
@Bean
public ModelMapper modelMapper() {
return new ModelMapper();
}
}
ProductDto.java
Entity 변수와 대부분 동일하고, 서비스 레이어에서 특정 값을 계산 후 Front-End 에 전달하기 위해서 discountPrice 변수를 추가 하였습니다.
package happygram.ecommerce.dto;
import java.time.LocalDateTime;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@ToString
@Setter
@Getter
public class ProductDto {
private String name;
private Long price;
private String description;
private String imageUrl;
private String color;
private String size;
private Long discount;
private Long categoryId;
private LocalDateTime createTimestamp;
private LocalDateTime updateTimestamp;
private Long discountPrice;
}
ProductService.java
modelMapper 오브젝트를 이용하여 데이터베이스에서 가져온 값을 그대로 DTO 클래스에 매핑합니다.
discountPrice 값은 계산하여 적용합니다.
@Service
public class ProductService {
@Autowired
private ModelMapper modelMapper;
@Autowired
private ProductRepository productRepository;
/**
* 상품 상세 조회
* @param categoryId
* @return
*/
public ProductDto getProduct(Long categoryId){
ProductDto productDto = modelMapper.map(productRepository.findById(categoryId).get(), ProductDto.class);
productDto.setDiscountPrice(productDto.getPrice() * (100 - productDto.getDiscount())/100);
return productDto;
}
...
}
ProductController.java
@Controller
@RequestMapping("/product")
public class ProductController {
@Autowired
private ProductService productService;
/**
* 상품 상세 조회
*/
@RequestMapping(value = "/view/detail/{id}")
public String viewProductDetail(@PathVariable Long id, Model model) {
// data
ProductDto productDto = productService.getProduct(id);
model.addAttribute("product", productDto);
// view
model.addAttribute("template", "fragments/content/product/detail");
return "index";
}
...
}
Front-End
list.html
th:href 부분을 다음처럼 product.id 값을 받을 수 있게 수정합니다.
...
<!-- image -->
<div class="text-center">
<a th:href="@{'/product/view/detail/' + ${product.id}}">
<img class="img-fluid" th:src="@{'/static/' + ${product.imageUrl}}" alt="Product picture">
</a>
</div>
...
detail.html
Back-End 에서 받은 product 값을 이용하여 값을 채웁니다.
(설명, 리뷰, 평가 등 부가적인 내용은 추후 완성 예정입니다)
<!-- Content Wrapper. Contains page content -->
<div class="content-wrapper">
<!-- Content Header (Page header) -->
<section class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<h1>티셔츠</h1>
</div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="#">Home</a></li>
<li class="breadcrumb-item active">티셔츠</li>
</ol>
</div>
</div>
</div><!-- /.container-fluid -->
</section>
<!-- Main content -->
<section class="content">
<!-- Default box -->
<div class="card card-solid">
<div class="card-body">
<div class="row">
<div class="col-12 col-sm-6">
<h3 class="d-inline-block d-sm-none" th:text="${product.name}"></h3>
<div class="col-12">
<img th:src="@{'/static/' + ${product.imageUrl}}" class="product-image" alt="Product Image">
</div>
<div class="col-12 product-image-thumbs">
<div class="product-image-thumb active"><img th:src="@{'/static/' + ${product.imageUrl}}" alt="Product Image"></div>
<div class="product-image-thumb" ><img th:src="@{'/static/' + ${product.imageUrl}}" alt="Product Image"></div>
<div class="product-image-thumb" ><img th:src="@{'/static/' + ${product.imageUrl}}" alt="Product Image"></div>
<div class="product-image-thumb" ><img th:src="@{'/static/' + ${product.imageUrl}}" alt="Product Image"></div>
<div class="product-image-thumb" ><img th:src="@{'/static/' + ${product.imageUrl}}" alt="Product Image"></div>
</div>
</div>
<div class="col-12 col-sm-6">
<h3 class="my-3" th:text="${product.name}"></h3>
<p th:text="${product.description}">
</p>
<hr>
<h4>색상</h4>
<div class="btn-group btn-group-toggle" data-toggle="buttons">
<label class="btn btn-default text-center active" th:each="color,status : ${#strings.arraySplit(product.color, ',')}">
<input type="radio" name="color_option" th:id="'color_option' + ${status.index}" autocomplete="off" checked="" th:text="${color}">
<br>
<i class="fas fa-circle fa-2x" th:classappend="'text-' + ${#strings.toLowerCase(color)}"></i>
</label>
</div>
<h4 class="mt-3">사이즈</h4>
<div class="btn-group btn-group-toggle" data-toggle="buttons">
<label class="btn btn-default text-center" th:each="size,status : ${#strings.arraySplit(product.size, ',')}">
<input type="radio" name="size_option" th:id="'size_option' + ${status.index}" autocomplete="off">
<span class="text-xl" th:text="${size}"></span>
</label>
</div>
<div class="bg-gray py-2 px-3 mt-4">
<h4 class="mt-0">
<small><del th:text="${product.price}"></del>원</small>
</h4>
<h2 class="mb-0" th:text="${product.discountPrice + '원'}">
</h2>
</div>
<div class="mt-4">
<div class="btn btn-primary btn-lg btn-flat">
<i class="fas fa-cart-plus fa-lg mr-2"></i>
Add to Cart
</div>
<div class="btn btn-default btn-lg btn-flat">
<i class="fas fa-heart fa-lg mr-2"></i>
Add to Wishlist
</div>
</div>
<div class="mt-4 product-share">
<a href="#" class="text-gray">
<i class="fab fa-facebook-square fa-2x"></i>
</a>
<a href="#" class="text-gray">
<i class="fab fa-twitter-square fa-2x"></i>
</a>
<a href="#" class="text-gray">
<i class="fas fa-envelope-square fa-2x"></i>
</a>
<a href="#" class="text-gray">
<i class="fas fa-rss-square fa-2x"></i>
</a>
</div>
</div>
</div>
<div class="row mt-4">
<nav class="w-100">
<div class="nav nav-tabs" id="product-tab" role="tablist">
<a class="nav-item nav-link active" id="product-desc-tab" data-toggle="tab" href="#product-desc" role="tab" aria-controls="product-desc" aria-selected="true">설명</a>
<a class="nav-item nav-link" id="product-comments-tab" data-toggle="tab" href="#product-comments" role="tab" aria-controls="product-comments" aria-selected="false">리뷰</a>
<a class="nav-item nav-link" id="product-rating-tab" data-toggle="tab" href="#product-rating" role="tab" aria-controls="product-rating" aria-selected="false">평가</a>
</div>
</nav>
<div class="tab-content p-3" id="nav-tabContent">
<div class="tab-pane fade show active" id="product-desc" role="tabpanel" aria-labelledby="product-desc-tab">상세 설명</div>
<div class="tab-pane fade" id="product-comments" role="tabpanel" aria-labelledby="product-comments-tab">리뷰</div>
<div class="tab-pane fade" id="product-rating" role="tabpanel" aria-labelledby="product-rating-tab">평가</div>
</div>
</div>
</div>
<!-- /.card-body -->
</div>
<!-- /.card -->
</section>
<!-- /.content -->
</div>
<!-- /.content-wrapper -->
'IT 프로젝트 > 쇼핑몰 만들기' 카테고리의 다른 글
[Spring Boot] 스프링 부트 프로젝트/쇼핑몰 만들기 - 상품 목록 조회 (0) | 2020.01.25 |
---|---|
[Spring Boot] 스프링 부트 프로젝트/쇼핑몰 만들기 - 메뉴 (6) | 2020.01.18 |
[Spring Boot] 스프링 부트 프로젝트/쇼핑몰 만들기 - 화면 동적 로딩 (0) | 2020.01.07 |
[Spring Boot] 스프링 부트 프로젝트/쇼핑몰 만들기 - 화면 구성 (0) | 2020.01.05 |
[Spring Boot] 스프링 부트 프로젝트/쇼핑몰 만들기 - 개발 환경 구성 (Visual Studio Code) (2) | 2020.01.02 |