Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

부산대 BE_원윤서 2주차 과제(Step1) #194

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
Open
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,9 @@
# spring-gift-wishlist
# spring-gift-wishlist
구현할 기능 목록
1. Product DTO에 조건 추가
2. 컨트롤러 수정
- 상품 추가 시 검증
- 상품 수정 시 검증
3. 뷰 수정
- 상품 추가 뷰 : 필드값 에러 시 오류 메시지 출력
- 삼품 수정 뷰 : 필드값 에러 시 오류 메시지 출력
6 changes: 4 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
plugins {
id 'java'
id 'org.springframework.boot' version '3.3.1'
id 'io.spring.dependency-management' version '1.1.5'
id 'io.spring.dependency-management' version '1.1.4'
}

group = 'camp.nextstep.edu'
version = '0.0.1-SNAPSHOT'

java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
languageVersion = JavaLanguageVersion.of(17)
}
}

Expand All @@ -21,6 +21,8 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-validation'

runtimeOnly 'com.h2database:h2'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
Expand Down
37 changes: 37 additions & 0 deletions src/main/java/gift/Product.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package gift;

import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;

public class Product {
Long id;
String name;
Long price;
String imageUrl;

public Product() {
}

public Long getId() {
return id;
}

public String getName() {
return name;
}

public Long getPrice() {
return price;
}

public String getImageUrl() {
return imageUrl;
}

public Product(Long id, String name, Long price, String imageUrl) {
this.id = id;
this.name = name;
this.price = price;
this.imageUrl = imageUrl;
}
}
79 changes: 79 additions & 0 deletions src/main/java/gift/ProductController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package gift;

import jakarta.validation.Valid;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

@Controller
@RequestMapping("/manager")
public class ProductController {
private final ProductRepository productRepository;

public ProductController(ProductRepository productRepository) {
this.productRepository = productRepository;
}

@PostMapping("/products/add")
public String addProduct(@Valid @ModelAttribute("newProduct") ProductDTO newProduct, BindingResult bindingResult, RedirectAttributes redirectAttributes){
if (bindingResult.hasErrors()) {
return "AddProduct";
}

System.out.println("add");
Product product = productRepository.insertProduct(newProduct);
redirectAttributes.addAttribute("id", product.getId());
System.out.println(product.id);
return "redirect:/manager/products/{id}";
}

@PutMapping("/products/update/{id}")
public String updateProduct(@PathVariable Long id, @Valid @ModelAttribute("product") ProductDTO product, BindingResult bindingResult, RedirectAttributes redirectAttributes){
if (bindingResult.hasErrors()) {
return "UpdateProduct";
}

System.out.println("update");
productRepository.updateProduct(id, product);
redirectAttributes.addAttribute("id", id);
return "redirect:/manager/products/{id}";
}

@DeleteMapping("/products/delete/{id}")
public String deleteProduct(@PathVariable Long id){
System.out.println("delete");
Product product = productRepository.selectProduct(id);
if(product != null){
productRepository.deleteProduct(id);
}
return "redirect:/manager/products";
}

@GetMapping("/products")
public String getProductsView(Model model){
model.addAttribute("products", productRepository.selectProducts());
return "ManageProduct";
}

@GetMapping("/products/add")
public String addProductView(Model model){
model.addAttribute("newProduct", new Product());
return "AddProduct";
}

@GetMapping("/products/update/{id}")
public String updateProductView(@PathVariable Long id, Model model){
model.addAttribute("product", new ProductDTO(productRepository.selectProduct(id)));
return "UpdateProduct";
}

@GetMapping("/products/{id}")
public String getProduct(@PathVariable long id, Model model) {
Product product = productRepository.selectProduct(id);
model.addAttribute("product", product);
return "ProductInfo";
}
}
60 changes: 60 additions & 0 deletions src/main/java/gift/ProductDTO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package gift;

import jakarta.validation.constraints.AssertTrue;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;

public class ProductDTO {
@NotBlank(message = "상품명은 공백으로 둘 수 없습니다.")
@Pattern(regexp = "^[a-zA-Zㄱ-ㅎ가-힣0-9\\(\\)\\[\\]\\+\\-&/\\_ ]{1,16}$", message = "상품명은 공백을 포함하여 최대 15자까지 입력할 수 있습니다. 특수문자는 ( ) [ ] + - & / _ 만 사용 가능합니다.")
String name;
@NotNull(message = "가격은 공백으로 둘 수 없습니다.")
Long price;
@NotBlank(message = "이미지 URL은 공백으로 둘 수 없습니다.")
String imageUrl;

@AssertTrue(message = "\"카카오\"가 포함된 문구는 담당 MD와 협의한 경우에만 사용할 수 있습니다.")
public boolean isNameNotContainingKakao() {
return name == null || !name.contains("카카오");
}

public ProductDTO() {
}

public ProductDTO(String name, Long price, String imageUrl) {
this.name = name;
this.price = price;
this.imageUrl = imageUrl;
}

public ProductDTO(Product product) {
this.name = product.getName();
this.price = product.getPrice();
this.imageUrl = product.getImageUrl();
}

public String getName() {
return name;
}

public Long getPrice() {
return price;
}

public String getImageUrl() {
return imageUrl;
}

public void setName(String name) {
this.name = name;
}

public void setPrice(Long price) {
this.price = price;
}

public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
}
71 changes: 71 additions & 0 deletions src/main/java/gift/ProductRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package gift;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
import org.springframework.stereotype.Repository;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Repository
public class ProductRepository {
private final JdbcTemplate jdbcTemplate;
private SimpleJdbcInsert simpleJdbcInsert;
public ProductRepository(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
this.simpleJdbcInsert = new SimpleJdbcInsert(jdbcTemplate)
.withTableName("Products")
.usingGeneratedKeyColumns("id");
}

public Product insertProduct(ProductDTO product){
Map<String, Object> parameters = new HashMap<String, Object>();
parameters.put("name", product.getName());
parameters.put("price", product.getPrice());
parameters.put("imageUrl", product.getImageUrl());
Long id = simpleJdbcInsert.executeAndReturnKey(parameters).longValue();
return new Product(id, product.getName(), product.getPrice(), product.getImageUrl());
}
public Product selectProduct(Long id) {
var sql = "select id, name, price, imageUrl from Products where id = ?";
return jdbcTemplate.queryForObject(
sql,
getProductRowMapper(),
id
);
}

public List<Product> selectProducts(){
var sql = "select id, name, price, imageUrl from Products";
return jdbcTemplate.query(
sql,
getProductRowMapper()
);
}

private static RowMapper<Product> getProductRowMapper() {
return (resultSet, rowNum) -> new Product(
resultSet.getLong("id"),
resultSet.getString("name"),
resultSet.getLong("price"),
resultSet.getString("imageUrl")
);
}

public Product updateProduct(Long id, ProductDTO updateParam){
var sql = "update Products set name=?, price=?, imageUrl=? where id = ?";
jdbcTemplate.update(sql,
updateParam.getName(),
updateParam.getPrice(),
updateParam.getImageUrl(),
id);
return new Product(id, updateParam.getName(), updateParam.getPrice(), updateParam.getImageUrl());
}

public void deleteProduct(Long id){
var sql = "delete from Products where id = ?";
jdbcTemplate.update(sql, id);
}
}
2 changes: 2 additions & 0 deletions src/main/java/gift/ProductViewController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
package gift;public class ProductViewController {
}
4 changes: 4 additions & 0 deletions src/main/resources/application.properties
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
spring.application.name=spring-gift
spring.mvc.hiddenmethod.filter.enabled=true
spring.h2.console.enabled=true
spring.datasource.url=jdbc:h2:mem:test
spring.datasource.username=sa
9 changes: 9 additions & 0 deletions src/main/resources/schema.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
DROP TABLE IF EXISTS PRODUCTS;

CREATE TABLE PRODUCTS (
id long NOT NULL AUTO_INCREMENT,
name varchar(255) NOT NULL,
price long NOT NULL,
imageurl varchar(255),
primary key (id)
);
49 changes: 49 additions & 0 deletions src/main/resources/templates/AddOrUpdateProduct.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>상품 등록/수정</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
</head>
<body>
<div class="p-5 mb-5 text-center</> bg-light">
<h1 class="mb-3" th:align="center"><b>상품 관리</b></h1>
<h4 class="mb-3" th:align="center">관리자 전용 상품 관리 페이지</h4>
</div>
<div class="container">
<form action="ProductInfo.html" th:action th:object="${product}" method="post">

<div>
<label for="id">ID</label>
<input type="text" th:if="${product.id} != null" disabled id="id" th:field="*{id}" class="form-control" placeholder="ID를 입력하세요">
</div>
<div>
<label for="name">상품명</label>
<input type="text" id="name" th:field="*{name}" class="form-control" placeholder="이름을 입력하세요">
</div>
<div>
<label for="price">가격</label>
<input type="text" id="price" th:field="*{price}" class="form-control" placeholder="가격을 입력하세요">
</div>
<div>
<label for="imageUrl">이미지 URL</label>
<input type="text" id="imageUrl" th:field="*{imageUrl}" class="form-control" placeholder="이미지 URL을 입력하세요">
</div>

<div class="row">
<div class="col">
<button th:if="${product.id} == null" class="w-100 btn btn-primary btn-lg" type="submit">상품 등록</button>
<button th:if="${product.id}!=null" class="w-100 btn btn-primary btn-lg" type="submit">상품 수정</button>
</div>
<div class="col">
<button class="w-100 btn btn-secondary btn-lg"
onclick="location.href='ManageProduct.html'"
th:onclick="|location.href='@{/manager/products}'|"
type="button">취소</button>
</div>
</div>
<input type="hidden" th:if="${product.id} != null" name="_method" value="PUT"/>
</form>

</div>
</body>
Loading