-
Notifications
You must be signed in to change notification settings - Fork 122
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주차 과제(2단계) #168
base: main
Are you sure you want to change the base?
Changes from all commits
577b80d
526556b
6e6515e
41ac09c
278b54d
ba85a3c
557c978
05b42f1
b9b54e3
5eaa188
6cd18a3
ce558e9
731b7e6
bfe7fd9
d72b62b
4da5702
824a714
0c7924e
a09d9e6
28459d6
f730d4d
3045378
0e2855d
387afa4
1643f6e
dc50ce1
34aa0a2
8e60069
daf36c1
7f3f239
a75b803
98cfa59
869af5d
b70ec2f
5991652
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,23 @@ | ||
# spring-gift-product | ||
# spring-gift-product | ||
|
||
구현할 기능 목록 | ||
|
||
## step1 | ||
|
||
상품 정보 DTO - record로 구현 (gift.web.dto.Product) | ||
|
||
HTTP API - ProductController (gift.web.ProductController) | ||
|
||
- POST | ||
- GET | ||
- PUT / PATCH | ||
- DELETE | ||
|
||
## step2 | ||
|
||
상품을 조회, 추가, 수정, 삭제할 수 있는 관리자 화면을 구현한다. | ||
|
||
Thymeleaf를 사용해, HTML 폼 전송을 이용한 페이지 이동을 기반으로 구현 | ||
|
||
* ProductController로 view를 반환하도록 재작성 | ||
* index(product) html, create update html 작성 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package gift.web; | ||
|
||
import gift.web.dto.Product; | ||
import java.util.List; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.*; | ||
|
||
@RestController //컨트롤러를 JSON을 반환하는 컨트롤러로 만들어줌 | ||
@RequestMapping("/api/products") | ||
public class ProductController { | ||
|
||
private final ProductService productService; | ||
|
||
public ProductController(ProductService productService) { | ||
this.productService = productService; | ||
} | ||
|
||
@GetMapping | ||
public ResponseEntity<List<Product>> getProducts() { | ||
return new ResponseEntity<>(productService.getProducts(), HttpStatus.OK); | ||
} | ||
|
||
// products/{상품번호}의 GetMapping | ||
@GetMapping("/{id}") //Query Param과 Path Variable 사용의 차이점 알아보기 | ||
public ResponseEntity<Product> getProductById(@PathVariable Long id) { | ||
Product product = productService.getProductById(id); | ||
if(product != null) { | ||
return new ResponseEntity<>(product, HttpStatus.OK); | ||
} | ||
return new ResponseEntity<>(HttpStatus.NOT_FOUND); | ||
} | ||
|
||
@PostMapping | ||
public ResponseEntity<?> createProduct(@RequestBody Product product) { | ||
return new ResponseEntity<>(productService.createProduct(product), HttpStatus.CREATED); | ||
} | ||
|
||
// PUT은 업데이트시 존재하지 않는다면, 생성을 하게 되는데, POST와의 차이점?이 뭔 지 알아보기 | ||
// PUT 구현 | ||
@PutMapping("/{id}") | ||
public ResponseEntity<?> updateProduct(@PathVariable Long id, @RequestBody Product product) { | ||
boolean exists = productService.getProductById(id) != null; | ||
productService.updateProduct(id, product); | ||
if(exists) { | ||
return ResponseEntity.ok(product); | ||
} | ||
return ResponseEntity.status(HttpStatus.CREATED).body(product); | ||
} | ||
|
||
@DeleteMapping("/{id}") | ||
public ResponseEntity<?> deleteProduct(@PathVariable Long id) { | ||
if(productService.deleteProduct(id)) { | ||
return ResponseEntity.ok("Delete Success"); | ||
} | ||
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Product Not Found"); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package gift.web; | ||
|
||
import gift.web.dto.Product; | ||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.concurrent.atomic.AtomicLong; | ||
import org.springframework.stereotype.Service; | ||
|
||
@Service | ||
public class ProductService { | ||
private final Map<Long, Product> products = new HashMap<>(); | ||
private final AtomicLong incrementCounter = new AtomicLong(1); // ID를 관리할 변수 | ||
|
||
public List<Product> getProducts() { | ||
return List.copyOf(products.values()); | ||
} | ||
|
||
public Product getProductById(Long id) { | ||
return products.get(id); | ||
} | ||
|
||
public Product createProduct(Product product) { | ||
Long id = incrementCounter.getAndIncrement(); // 1씩 증가하는 id | ||
Product newProduct = new Product(id, product.name(), product.price(), product.imageUrl()); | ||
products.put(id, newProduct); | ||
|
||
return newProduct; | ||
} | ||
|
||
public Product updateProduct(Long id, Product product) { | ||
products.put(id, product); | ||
return product; | ||
} | ||
|
||
public boolean deleteProduct(Long id) { | ||
return products.remove(id) != null; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
package gift.web.dto; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. AdminController 클래스가 dto 패키지 하위에 있네요! |
||
|
||
import gift.web.ProductService; | ||
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; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
|
||
@Controller | ||
@RequestMapping("/admin/products") | ||
public class AdminController { | ||
private final ProductService productService; | ||
|
||
public AdminController(ProductService productService) { | ||
this.productService = productService; | ||
} | ||
|
||
@GetMapping | ||
public String getProducts(Model model) { | ||
model.addAttribute("products", productService.getProducts()); | ||
return "products"; | ||
} | ||
|
||
@GetMapping("/{id}") | ||
public String getProductById(@PathVariable Long id, Model model) { | ||
Product product = productService.getProductById(id); | ||
if (product != null) { | ||
model.addAttribute("product", product); | ||
} | ||
return "products"; | ||
} | ||
|
||
@GetMapping("/create") | ||
public String createProductForm(Model model) { | ||
model.addAttribute("product", new Product(1L, "name", 0L, "image.url")); | ||
return "create"; | ||
} | ||
Comment on lines
+36
to
+40
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
라고 질문주신 코드 인 것 같아요. 덕분에 테스트하기 수월했습니다 👍 기본값을 설정해주는 곳으로는 서버, 클라이언트가 있습니다. |
||
|
||
@PostMapping("/create") | ||
public String createProduct(@ModelAttribute Product product) { | ||
productService.createProduct(product); | ||
return "redirect:/admin/products"; | ||
} | ||
|
||
@GetMapping("/edit/{id}") | ||
public String editProductForm(@PathVariable Long id, Model model) { | ||
Product product = productService.getProductById(id); | ||
if (product != null) { | ||
model.addAttribute("product", product); | ||
} | ||
return "edit"; | ||
} | ||
|
||
@PostMapping("/edit/{id}") | ||
public String editProduct(@PathVariable Long id, @ModelAttribute Product product) { | ||
productService.updateProduct(id, product); | ||
return "redirect:/admin/products"; | ||
} | ||
|
||
@PostMapping("/delete/{id}") | ||
public String deleteProduct(@PathVariable Long id) { | ||
productService.deleteProduct(id); | ||
return "redirect:/admin/products"; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
package gift.web.dto; | ||
|
||
public record Product(Long id, String name, Long price, String imageUrl) { | ||
} | ||
Comment on lines
+1
to
+4
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Product 클래스가 dto 패키지 하위에 있네요! |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
<!DOCTYPE html> | ||
|
||
<html> | ||
<head> | ||
<title>Product Create</title> | ||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> | ||
</head> | ||
<body> | ||
<h1>상품 생성</h1> | ||
<form th:action="@{/admin/products/create}" th:object="${product}" method="post"> | ||
<div> | ||
<label for="name">상품명:</label> | ||
<input type="text" th:field="*{name}" id="name"></input> | ||
</div> | ||
<div> | ||
<label for="price">가격:</label> | ||
<input type="text" th:field="*{price}" id="price"></input> | ||
</div> | ||
<div> | ||
<label for="imageUrl">사진주소:</label> | ||
<input type="text" th:field="*{imageUrl}" id="imageUrl"></input> | ||
</div> | ||
<div> | ||
<button type="submit">Create</button> | ||
</div> | ||
</form> | ||
</body> | ||
</html> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
<!DOCTYPE html> | ||
|
||
<html> | ||
<head> | ||
<title>Product Create</title> | ||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> | ||
</head> | ||
<body> | ||
<h1>상품 수정</h1> | ||
<form th:action="@{/admin/products/edit/{id}(id=${product.id()})}" th:object="${product}" method="post"> | ||
<div> | ||
<label for="name">상품명:</label> | ||
<input type="text" th:field="*{name}" id="name"></input> | ||
</div> | ||
<div> | ||
<label for="price">가격:</label> | ||
<input type="text" th:field="*{price}" id="price"></input> | ||
</div> | ||
<div> | ||
<label for="imageUrl">사진주소:</label> | ||
<input type="text" th:field="*{imageUrl}" id="imageUrl"></input> | ||
</div> | ||
<div> | ||
<button type="submit">Update</button> | ||
</div> | ||
</form> | ||
</body> | ||
</html> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
<!DOCTYPE html> | ||
|
||
<html> | ||
<head> | ||
<title>Products</title> | ||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> | ||
</head> | ||
<body> | ||
<h1>상품 목록</h1> | ||
<a href="/admin/products/create">새 상품 만들기</a> | ||
<table> | ||
<tr> | ||
<th>ID</th> | ||
<th>Name</th> | ||
<th>Price</th> | ||
<th>Actions</th> | ||
</tr> | ||
<tr th:each="product : ${products}"> | ||
<td th:text="${product.id()}">1</td> | ||
<td th:text="${product.name()}">상품 이름예시</td> | ||
<td th:text="${product.price()}">5000</td> | ||
<td th:text="${product.imageUrl()}">https://image.jpg</td> | ||
<td> | ||
<a th:href="@{/admin/products/edit/{id}(id=${product.id()})}">Edit</a> | ||
<form th:action="@{/admin/products/delete/{id}(id=${product.id()})}" method="post" style="display:inline;"> | ||
<button type="submit">Delete</button> | ||
</form> | ||
</td> | ||
</tr> | ||
</table> | ||
</body> | ||
</html> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
List.copyOf(products.values());
를 이용해 컬렉션을 복사하여 반환하고 있군요!불변을 만들기 위해 컬렉션을 복사하는 방법에는 어떤 것들이 있고, 많은 방법 중
List.copyOf
를 사용하신 이유는 무엇일까요?