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_권도윤_1주차 과제(1~3단계) #200

Open
wants to merge 10 commits into
base: doyooon
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,13 @@
# spring-gift-product
# spring-gift-product
- [x] 상품을 조회, 추가, 수정, 삭제할 수 있는 HTTP API를 구현한다.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

요구사항 정리한 부분 좋네요 👍

- [x] 조회
- [x] 추가
- [x] 수정
- [x] 삭제
- [x] Thymeleaf를 사용하여 상품을 조회, 추가, 수정, 삭제할 수 있는 관리자 화면을 구현한다.
- [x] 관리자 페이지 구현
- [x] 페이지와 상호작용 할 수 있도록 api 수정
- [x] 상품 정보를 데이터베이스에 저장한다.
- [x] H2 데이터베이스를 사용 설정한다.
- [x] 데이터베이스와 통신할 repository를 생성한다.
- [x] repository를 통해서 DB에 요청을 보내도록 api 수정
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ version = '0.0.1-SNAPSHOT'

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

Expand Down
3 changes: 3 additions & 0 deletions src/main/java/gift/Application.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.filter.HiddenHttpMethodFilter;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

불필요한 import 문은 제거해도 좋을 것 같습니다 🙏


@SpringBootApplication
public class Application {
public static void main(String[] args) {

SpringApplication.run(Application.class, args);
}
}
58 changes: 58 additions & 0 deletions src/main/java/gift/Product.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package gift;

public class Product {
Long id;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

private과 같은 적절한 접근제어자를 사용하면 접근을 제한할 수 있습니다ㅎㅎ
자바가 제공하는 접근제어자에 대해서도 알아보시면 좋을 것 같아요!

Suggested change
Long id;
private Long id;

String name;
int price;
String imageUrl;

public Product(){

}

public Product(Long id, String name, int price, String imageUrl){
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

불필요한 set 관련 메서드는 최소화하는 것이 좋을 것 같습니다

this.id = id;
this.name = name;
this.price = price;
this.imageUrl = imageUrl;
}

public Long getId(){
return this.id;
}

public void setId(Long id){
this.id = id;
}

public String getName(){
return this.name;
}

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

public int getPrice(){
return this.price;
}

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

public String getImageUrl(){
return this.imageUrl;
}

public void setImageUrl(String imageUrl){
this.imageUrl = imageUrl;
}

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

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;


@Controller
public class ProductController {

@Autowired
private ProductRepository productRepository;

@GetMapping("/")
public String listProducts(Model model) {
model.addAttribute("products", productRepository.findAll());
model.addAttribute("newProduct", new Product()); // 새 상품 객체
model.addAttribute("product", new Product()); // 편집을 위한 빈 객체*/
return "home"; // Thymeleaf 템플릿 이름
}

@PostMapping("/post")
public String createProduct(@ModelAttribute Product newProduct){
productRepository.save(newProduct);
return "redirect:/";
}

@PostMapping ("/update")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수정을 위한 HTTP Method로는 PUT, PATCH 그리고 삭제를 위해서는 DELETE가 존재합니다.
따라서 용도에 맞는 HTTP Method를 활용하는 것이 바람직할 것 같습니다ㅎㅎ

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그리고 API 요청의 prefix로 /products 와 같이 구분을 두면 좋을 것 같습니다!
이후에 상품 외에 다른 추가/삭제/수정 요청이 생기면 문제가 생길 수 있습니다ㅎㅎ

public String updateProduct(@ModelAttribute Product changeProduct){
productRepository.update(changeProduct);

return "redirect:/";
}

@GetMapping ("/delete/{id}")
public String deleteProduct(@PathVariable Long id){
productRepository.deleteById(id);

return "redirect:/";
}

}
50 changes: 50 additions & 0 deletions src/main/java/gift/ProductRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package gift;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
import org.springframework.stereotype.Repository;

@Repository
public class ProductRepository {
private final JdbcTemplate jdbcTemplate;
private final SimpleJdbcInsert insertProduct;

@Autowired
public ProductRepository(JdbcTemplate jdbcTemplate, DataSource dataSource) {
this.jdbcTemplate = jdbcTemplate;
this.insertProduct = new SimpleJdbcInsert(dataSource)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SimpleJdbcInsert를 활용한 부분 좋네요! 👍

.withTableName("products")
.usingGeneratedKeyColumns("id");
}

public List<Product> findAll() {
return jdbcTemplate.query("SELECT * FROM products", new BeanPropertyRowMapper<>(Product.class));
}

public Product findById(Long id) {
return jdbcTemplate.queryForObject("SELECT * FROM products WHERE id = ?", new BeanPropertyRowMapper<>(Product.class), id);
}

public long save(Product product) {
Map<String, Object> parameters = new HashMap<>();
parameters.put("name", product.getName());
parameters.put("price", product.getPrice());
parameters.put("image_url", product.getImageUrl());
return insertProduct.executeAndReturnKey(parameters).longValue();
}

public int update(Product product) {
return jdbcTemplate.update("UPDATE products SET name = ?, price = ?, image_url = ? WHERE id = ?",
product.getName(), product.getPrice(), product.getImageUrl(), product.getId());
}

public int deleteById(Long id) {
return jdbcTemplate.update("DELETE FROM products WHERE id = ?", id);
}
}
7 changes: 7 additions & 0 deletions src/main/resources/application.properties
Original file line number Diff line number Diff line change
@@ -1 +1,8 @@
spring.application.name=spring-gift
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.h2.console.enabled=true
spring.sql.init.mode=always
6 changes: 6 additions & 0 deletions src/main/resources/schema.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
CREATE TABLE IF NOT EXISTS products (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
price DECIMAL(10, 2) NOT NULL,
image_url VARCHAR(255)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image url이 null이 될 수 없다면, 해당 부분에도 not-null 제약 조건을 걸어주는 것이 좋을 것 같습니다ㅎㅎ

);
93 changes: 93 additions & 0 deletions src/main/resources/templates/home.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
table, th, td {
border: 1px solid black;
border-collapse: collapse;
}
th, td {
padding: 10px;
}
form {
margin-bottom: 20px;
}
</style>
</head>
<body>
<h1>Product Management</h1>

<h2>Product List</h2>
<table>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Price</th>
<th>Image</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr th:each="product : ${products}">
<td th:text="${product.id}">1</td>
<td th:text="${product.name}">Product Name</td>
<td th:text="${product.price}">100.00</td>
<td><img th:src="@{product.imageUrl}" alt="no image" /></td>
<td>
<a th:href="@{/delete/{id}(id=${product.id})}">Delete</a>
</td>
</tr>
</tbody>
</table>

<h2>Create New Product</h2>
<form th:action="@{/post}" th:object="${newProduct}" method="post">
<div>
<label>Id:</label>
<input type="text" th:field="*{id}" />
</div>
<div>
<label>Name:</label>
<input type="text" th:field="*{name}" />
</div>
<div>
<label>Price:</label>
<input type="text" th:field="*{price}" />
</div>
<div>
<label>ImageUrl:</label>
<input type="text" th:field="*{imageUrl}" />
</div>
<div>
<button type="submit">Save</button>
</div>
</form>

<h2>Update Product</h2>
<form th:action="@{/update}" th:object="${product}" method="post">
<div>
<label>Id:</label>
<input type="text" th:field="*{id}" />
</div>
<div>
<label>Name:</label>
<input type="text" th:field="*{name}" />
</div>
<div>
<label>Price:</label>
<input type="text" th:field="*{price}" />
</div>
<div>
<label>Image:</label>
<input type="text" th:field="*{imageUrl}" />
</div>
<div>
<button type="submit">Update</button>
</div>
</form>
</body>
</html>