From 98f3a17bced12efd88d539a7406b06d893ff5f4d Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Wed, 26 Jun 2024 15:33:12 +0900 Subject: [PATCH 01/56] feat(global): create global controller --- src/main/java/gift/GlobalController.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/main/java/gift/GlobalController.java diff --git a/src/main/java/gift/GlobalController.java b/src/main/java/gift/GlobalController.java new file mode 100644 index 000000000..b1f8ebf84 --- /dev/null +++ b/src/main/java/gift/GlobalController.java @@ -0,0 +1,12 @@ +package gift; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; + +@Controller +public class GlobalController { + @GetMapping("/") + public String index() { + return "index"; + } +} From e114c0ebe1d374f4c1576c5d18d5bcaa22157d87 Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Tue, 25 Jun 2024 18:19:22 +0900 Subject: [PATCH 02/56] docs(global): summerize list of features --- README.md | 47 ++++++++++++++++++- .../product/controller/ProductController.java | 7 +++ .../java/gift/product/entity/Product.java | 38 +++++++++++++++ .../java/gift/product/entity/ProductInfo.java | 25 ++++++++++ 4 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 src/main/java/gift/product/controller/ProductController.java create mode 100644 src/main/java/gift/product/entity/Product.java create mode 100644 src/main/java/gift/product/entity/ProductInfo.java diff --git a/README.md b/README.md index cafde8a2c..10c0c5c6c 100644 --- a/README.md +++ b/README.md @@ -1 +1,46 @@ -# spring-gift-product \ No newline at end of file +# spring-gift-product + +--- + +## Step1 + +**요구 사항**
+아래와 같이 http 메세지를 받도록 구현한다 + +```http request +GET /api/products HTTP/1.1 +``` + +```http request +HTTP/1.1 200 +Content-Type: application/json + +[ + { + "id": 8146027, + "name": "아이스 카페 아메리카노 T", + "price": 4500, + "imageUrl": "https://st.kakaocdn.net/product/gift/product/20231010111814_9a667f9eccc943648797925498bdd8a3.jpg" + } +] +``` + + +**필요 조건**
+상품 데이터 관리 +현재는 별도의 데이터베이스가 없으므로 적절한 컬렉션을 이용하여 메모리에 저장한다. +```java +public class ProductController { + private final Map productMap = new HashMap<>(); +} +``` +---- +## 구현 기능 +- product 조회 +- product 추가 +- product 수정 +- product 삭제 + + + + diff --git a/src/main/java/gift/product/controller/ProductController.java b/src/main/java/gift/product/controller/ProductController.java new file mode 100644 index 000000000..78fd78ed1 --- /dev/null +++ b/src/main/java/gift/product/controller/ProductController.java @@ -0,0 +1,7 @@ +package gift.product.controller; + +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class ProductController { +} diff --git a/src/main/java/gift/product/entity/Product.java b/src/main/java/gift/product/entity/Product.java new file mode 100644 index 000000000..4ee94b567 --- /dev/null +++ b/src/main/java/gift/product/entity/Product.java @@ -0,0 +1,38 @@ +package gift.product.entity; + +public class Product { + private Long id; + private String name; + private int price; + private String imageUrl; + + public Product(Long id, String name, int price, String imageUrl) { + this.id = id; + this.name = name; + this.price = price; + this.imageUrl = imageUrl; + } + + public Product(Long id, ProductInfo productInfo) { + this.id = id; + this.name = productInfo.getName(); + this.price = productInfo.getPrice(); + this.imageUrl = productInfo.getImageUrl(); + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public int getPrice() { + return price; + } + + public String getImageUrl() { + return imageUrl; + } +} diff --git a/src/main/java/gift/product/entity/ProductInfo.java b/src/main/java/gift/product/entity/ProductInfo.java new file mode 100644 index 000000000..538581711 --- /dev/null +++ b/src/main/java/gift/product/entity/ProductInfo.java @@ -0,0 +1,25 @@ +package gift.product.entity; + +public class ProductInfo { + private String name; + private int price; + private String imageUrl; + + public ProductInfo(String name, int price, String imageUrl) { + this.name = name; + this.price = price; + this.imageUrl = imageUrl; + } + + public String getName() { + return name; + } + + public int getPrice() { + return price; + } + + public String getImageUrl() { + return imageUrl; + } +} From 5b377756dea992d7cfbcbfddf83e831fa44bcdd3 Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Tue, 25 Jun 2024 18:20:04 +0900 Subject: [PATCH 03/56] feat(product): create product controller --- src/main/java/gift/product/controller/ProductController.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/gift/product/controller/ProductController.java b/src/main/java/gift/product/controller/ProductController.java index 78fd78ed1..4e0047a2e 100644 --- a/src/main/java/gift/product/controller/ProductController.java +++ b/src/main/java/gift/product/controller/ProductController.java @@ -1,7 +1,8 @@ package gift.product.controller; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; -@RestController +@RestController("/api") public class ProductController { } From 2b52d04de7514e70a851fc602b152d0df1ff4373 Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Tue, 25 Jun 2024 18:21:00 +0900 Subject: [PATCH 04/56] feat(product): create product entity --- .../java/gift/product/entity/Product.java | 15 +---------- .../java/gift/product/entity/ProductInfo.java | 25 ------------------- 2 files changed, 1 insertion(+), 39 deletions(-) delete mode 100644 src/main/java/gift/product/entity/ProductInfo.java diff --git a/src/main/java/gift/product/entity/Product.java b/src/main/java/gift/product/entity/Product.java index 4ee94b567..699e456ca 100644 --- a/src/main/java/gift/product/entity/Product.java +++ b/src/main/java/gift/product/entity/Product.java @@ -1,29 +1,16 @@ package gift.product.entity; public class Product { - private Long id; private String name; private int price; private String imageUrl; - public Product(Long id, String name, int price, String imageUrl) { - this.id = id; + public Product(String name, int price, String imageUrl) { this.name = name; this.price = price; this.imageUrl = imageUrl; } - public Product(Long id, ProductInfo productInfo) { - this.id = id; - this.name = productInfo.getName(); - this.price = productInfo.getPrice(); - this.imageUrl = productInfo.getImageUrl(); - } - - public Long getId() { - return id; - } - public String getName() { return name; } diff --git a/src/main/java/gift/product/entity/ProductInfo.java b/src/main/java/gift/product/entity/ProductInfo.java deleted file mode 100644 index 538581711..000000000 --- a/src/main/java/gift/product/entity/ProductInfo.java +++ /dev/null @@ -1,25 +0,0 @@ -package gift.product.entity; - -public class ProductInfo { - private String name; - private int price; - private String imageUrl; - - public ProductInfo(String name, int price, String imageUrl) { - this.name = name; - this.price = price; - this.imageUrl = imageUrl; - } - - public String getName() { - return name; - } - - public int getPrice() { - return price; - } - - public String getImageUrl() { - return imageUrl; - } -} From f54b33a13a05222a42334197b3be842fa9683410 Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Tue, 25 Jun 2024 18:25:53 +0900 Subject: [PATCH 05/56] fix(product): add column id into product entity --- src/main/java/gift/product/entity/Product.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/gift/product/entity/Product.java b/src/main/java/gift/product/entity/Product.java index 699e456ca..72b0b58d6 100644 --- a/src/main/java/gift/product/entity/Product.java +++ b/src/main/java/gift/product/entity/Product.java @@ -1,15 +1,20 @@ package gift.product.entity; public class Product { + private Long id; private String name; private int price; private String imageUrl; - public Product(String name, int price, String imageUrl) { + public Product(Long id, String name, int price, String imageUrl) { + this.id = id; this.name = name; this.price = price; this.imageUrl = imageUrl; } + public Long getId() { + return id; + } public String getName() { return name; @@ -22,4 +27,5 @@ public int getPrice() { public String getImageUrl() { return imageUrl; } + } From f376876bae07a856acea70afe751e627bbb8d372 Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Tue, 25 Jun 2024 18:26:28 +0900 Subject: [PATCH 06/56] feat(product): add method get product by id in controller --- .../gift/product/controller/ProductController.java | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/main/java/gift/product/controller/ProductController.java b/src/main/java/gift/product/controller/ProductController.java index 4e0047a2e..859e52de6 100644 --- a/src/main/java/gift/product/controller/ProductController.java +++ b/src/main/java/gift/product/controller/ProductController.java @@ -1,8 +1,17 @@ package gift.product.controller; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RestController; +import gift.product.entity.Product; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.Map; @RestController("/api") public class ProductController { + private final Map productMap = new HashMap<>(); + + @GetMapping("/product/{id}") + public Product getProductById(@PathVariable Long id) { + return productMap.get(id); + } } From 0ded7d37054e6ea77c57557178d3da0310892e94 Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Tue, 25 Jun 2024 18:37:20 +0900 Subject: [PATCH 07/56] feat(product): create product request record --- src/main/java/gift/product/dto/ProductRequest.java | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 src/main/java/gift/product/dto/ProductRequest.java diff --git a/src/main/java/gift/product/dto/ProductRequest.java b/src/main/java/gift/product/dto/ProductRequest.java new file mode 100644 index 000000000..7dfba414f --- /dev/null +++ b/src/main/java/gift/product/dto/ProductRequest.java @@ -0,0 +1,10 @@ +package gift.product.dto; + +import java.util.Objects; + +public record ProductRequest(String name, int price, String imageUrl) { + public ProductRequest { + Objects.requireNonNull(name); + Objects.requireNonNull(imageUrl); + } +} From dc565f44d4d7c6fea916271825878f6a91f3a7de Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Tue, 25 Jun 2024 18:44:25 +0900 Subject: [PATCH 08/56] feat(product): add constructor to initialize Product from ProductRequest --- src/main/java/gift/product/entity/Product.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/java/gift/product/entity/Product.java b/src/main/java/gift/product/entity/Product.java index 72b0b58d6..f31d5a6a9 100644 --- a/src/main/java/gift/product/entity/Product.java +++ b/src/main/java/gift/product/entity/Product.java @@ -1,5 +1,7 @@ package gift.product.entity; +import gift.product.dto.ProductRequest; + public class Product { private Long id; private String name; @@ -12,6 +14,15 @@ public Product(Long id, String name, int price, String imageUrl) { this.price = price; this.imageUrl = imageUrl; } + + public Product(Long id, ProductRequest productRequest) { + this.id = id; + this.name = productRequest.name(); + this.price = productRequest.price(); + this.imageUrl = productRequest.imageUrl(); + } + + public Long getId() { return id; } From 5507db1a93e28fe6a1845ca32699f9d79dd72526 Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Tue, 25 Jun 2024 18:54:46 +0900 Subject: [PATCH 09/56] fix(product): change RequestMapping url error --- src/main/java/gift/product/controller/ProductController.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/gift/product/controller/ProductController.java b/src/main/java/gift/product/controller/ProductController.java index 859e52de6..f69814490 100644 --- a/src/main/java/gift/product/controller/ProductController.java +++ b/src/main/java/gift/product/controller/ProductController.java @@ -6,7 +6,8 @@ import java.util.HashMap; import java.util.Map; -@RestController("/api") +@RestController +@RequestMapping("/api") public class ProductController { private final Map productMap = new HashMap<>(); From fa94538ae5d35dc33b0dec412a609f783e88a960 Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Tue, 25 Jun 2024 18:58:21 +0900 Subject: [PATCH 10/56] feat(product): add create product method --- .../product/controller/ProductController.java | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/main/java/gift/product/controller/ProductController.java b/src/main/java/gift/product/controller/ProductController.java index f69814490..ea9a1ba5d 100644 --- a/src/main/java/gift/product/controller/ProductController.java +++ b/src/main/java/gift/product/controller/ProductController.java @@ -1,5 +1,6 @@ package gift.product.controller; +import gift.product.dto.ProductRequest; import gift.product.entity.Product; import org.springframework.web.bind.annotation.*; @@ -12,7 +13,25 @@ public class ProductController { private final Map productMap = new HashMap<>(); @GetMapping("/product/{id}") - public Product getProductById(@PathVariable Long id) { + public Product getProductById(@PathVariable(name = "id") Long id) { return productMap.get(id); } + + @PostMapping("/product") + public Product createProduct(@RequestBody ProductRequest productRequest) { + Long id = addProduct(productRequest); + return productMap.get(id); + } + + private Long addProduct(ProductRequest productRequest) { + Long id = getMaxKey(); + productMap.put(id, new Product(id, productRequest)); + return id; + } + + private Long getMaxKey() { + return 1L + productMap.keySet().stream() + .max(Long::compare) + .orElse(0L); + } } From 30282134214efd77bff4533d2a1617f753622711 Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Tue, 25 Jun 2024 18:59:31 +0900 Subject: [PATCH 11/56] fix(product): change endpoint product to products --- src/main/java/gift/product/controller/ProductController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/gift/product/controller/ProductController.java b/src/main/java/gift/product/controller/ProductController.java index ea9a1ba5d..d1eb169a9 100644 --- a/src/main/java/gift/product/controller/ProductController.java +++ b/src/main/java/gift/product/controller/ProductController.java @@ -12,12 +12,12 @@ public class ProductController { private final Map productMap = new HashMap<>(); - @GetMapping("/product/{id}") + @GetMapping("/products/{id}") public Product getProductById(@PathVariable(name = "id") Long id) { return productMap.get(id); } - @PostMapping("/product") + @PostMapping("/products") public Product createProduct(@RequestBody ProductRequest productRequest) { Long id = addProduct(productRequest); return productMap.get(id); From eedb420cf533ab5e459d2826576c155bfc55d148 Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Tue, 25 Jun 2024 19:04:25 +0900 Subject: [PATCH 12/56] feat(product): add update product method --- .../java/gift/product/controller/ProductController.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/gift/product/controller/ProductController.java b/src/main/java/gift/product/controller/ProductController.java index d1eb169a9..16046ff0a 100644 --- a/src/main/java/gift/product/controller/ProductController.java +++ b/src/main/java/gift/product/controller/ProductController.java @@ -23,6 +23,12 @@ public Product createProduct(@RequestBody ProductRequest productRequest) { return productMap.get(id); } + @PutMapping("/products/{id}") + public Product updateProduct(@PathVariable(name = "id") Long id, @RequestBody ProductRequest productRequest) { + productMap.replace(id, new Product(id, productRequest)); + return productMap.get(id); + } + private Long addProduct(ProductRequest productRequest) { Long id = getMaxKey(); productMap.put(id, new Product(id, productRequest)); From c3c5908589673e4f75c1a324879f94dd0dcdb83d Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Tue, 25 Jun 2024 19:09:00 +0900 Subject: [PATCH 13/56] feat(product): add delete product method --- .../java/gift/product/controller/ProductController.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/gift/product/controller/ProductController.java b/src/main/java/gift/product/controller/ProductController.java index 16046ff0a..f1d90f513 100644 --- a/src/main/java/gift/product/controller/ProductController.java +++ b/src/main/java/gift/product/controller/ProductController.java @@ -29,6 +29,12 @@ public Product updateProduct(@PathVariable(name = "id") Long id, @RequestBody Pr return productMap.get(id); } + @DeleteMapping("/products/{id}") + public String deleteProduct(@PathVariable(name = "id") Long id) { + productMap.remove(id); + return "product " + id + " is deleted"; + } + private Long addProduct(ProductRequest productRequest) { Long id = getMaxKey(); productMap.put(id, new Product(id, productRequest)); From 317a4bab495355c45d3c51d13f5b5affae756158 Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Tue, 25 Jun 2024 19:10:19 +0900 Subject: [PATCH 14/56] feat(product): create http request file --- src/main/http/product/Product.http | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 src/main/http/product/Product.http diff --git a/src/main/http/product/Product.http b/src/main/http/product/Product.http new file mode 100644 index 000000000..73fdd75e1 --- /dev/null +++ b/src/main/http/product/Product.http @@ -0,0 +1,27 @@ +### get product +GET http://localhost:8080/api/products/1 +Content-Type: application/json + +### create product +POST http://localhost:8080/api/products +Content-Type: application/json + +{ + "name": "아메리카노", + "price": "4500", + "imageUrl": "http://hello" +} + +### update product +PUT http://localhost:8080/api/products/1 +Content-Type: application/json + +{ + "name": "아메리카노2", + "price": "5000", + "imageUrl": "http://hello" +} + +### delete product +DELETE http://localhost:8080/api/products/1 +Content-Type: application/json \ No newline at end of file From 5d21ad18fde656c16a4ee7da158f6d3f15da2448 Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Wed, 26 Jun 2024 15:49:17 +0900 Subject: [PATCH 15/56] refactor(global): combine common parts of API URLs --- .../gift/product/controller/ProductController.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/gift/product/controller/ProductController.java b/src/main/java/gift/product/controller/ProductController.java index f1d90f513..27a109ad6 100644 --- a/src/main/java/gift/product/controller/ProductController.java +++ b/src/main/java/gift/product/controller/ProductController.java @@ -8,28 +8,28 @@ import java.util.Map; @RestController -@RequestMapping("/api") +@RequestMapping("/api/products") public class ProductController { private final Map productMap = new HashMap<>(); - @GetMapping("/products/{id}") + @GetMapping("/{id}") public Product getProductById(@PathVariable(name = "id") Long id) { return productMap.get(id); } - @PostMapping("/products") + @PostMapping("") public Product createProduct(@RequestBody ProductRequest productRequest) { Long id = addProduct(productRequest); return productMap.get(id); } - @PutMapping("/products/{id}") + @PutMapping("/{id}") public Product updateProduct(@PathVariable(name = "id") Long id, @RequestBody ProductRequest productRequest) { productMap.replace(id, new Product(id, productRequest)); return productMap.get(id); } - @DeleteMapping("/products/{id}") + @DeleteMapping("/{id}") public String deleteProduct(@PathVariable(name = "id") Long id) { productMap.remove(id); return "product " + id + " is deleted"; From c42e11782e4b20d64ea84a1c45ee29fe325b0630 Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Wed, 26 Jun 2024 15:51:29 +0900 Subject: [PATCH 16/56] feat(product): add get all product method --- src/main/http/product/Product.http | 4 ++++ .../java/gift/product/controller/ProductController.java | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/src/main/http/product/Product.http b/src/main/http/product/Product.http index 73fdd75e1..c465f395d 100644 --- a/src/main/http/product/Product.http +++ b/src/main/http/product/Product.http @@ -1,3 +1,7 @@ +### get product list +GET http://localhost:8080/api/products/list +Content-Type: application/json + ### get product GET http://localhost:8080/api/products/1 Content-Type: application/json diff --git a/src/main/java/gift/product/controller/ProductController.java b/src/main/java/gift/product/controller/ProductController.java index 27a109ad6..dae648724 100644 --- a/src/main/java/gift/product/controller/ProductController.java +++ b/src/main/java/gift/product/controller/ProductController.java @@ -5,6 +5,7 @@ import org.springframework.web.bind.annotation.*; import java.util.HashMap; +import java.util.List; import java.util.Map; @RestController @@ -12,6 +13,11 @@ public class ProductController { private final Map productMap = new HashMap<>(); + @GetMapping("/list") + public List getAllProducts() { + return productMap.values().stream().toList(); + } + @GetMapping("/{id}") public Product getProductById(@PathVariable(name = "id") Long id) { return productMap.get(id); From 225aaf7bf375942303a06b245685fc6f86d612f2 Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Wed, 26 Jun 2024 15:56:27 +0900 Subject: [PATCH 17/56] chore(product): rename global controller to view controller --- .../{GlobalController.java => view/ViewController.java} | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) rename src/main/java/gift/{GlobalController.java => view/ViewController.java} (58%) diff --git a/src/main/java/gift/GlobalController.java b/src/main/java/gift/view/ViewController.java similarity index 58% rename from src/main/java/gift/GlobalController.java rename to src/main/java/gift/view/ViewController.java index b1f8ebf84..dfff0dff4 100644 --- a/src/main/java/gift/GlobalController.java +++ b/src/main/java/gift/view/ViewController.java @@ -1,12 +1,13 @@ -package gift; +package gift.view; import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; @Controller -public class GlobalController { +public class ViewController { @GetMapping("/") - public String index() { + public String index(Model model) { return "index"; } } From ebc23b24578391d3dfa409fc829329e9d9c06b9a Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Wed, 26 Jun 2024 16:29:56 +0900 Subject: [PATCH 18/56] fix(global): change get products list api url --- src/main/java/gift/product/controller/ProductController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/gift/product/controller/ProductController.java b/src/main/java/gift/product/controller/ProductController.java index dae648724..2881b7836 100644 --- a/src/main/java/gift/product/controller/ProductController.java +++ b/src/main/java/gift/product/controller/ProductController.java @@ -13,7 +13,7 @@ public class ProductController { private final Map productMap = new HashMap<>(); - @GetMapping("/list") + @GetMapping("") public List getAllProducts() { return productMap.values().stream().toList(); } From bf7afcd32ead59e8d707d95aa3bcce1eebf6cfed Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Thu, 27 Jun 2024 14:56:47 +0900 Subject: [PATCH 19/56] chore(view): delete unnecssary parameter --- src/main/java/gift/view/ViewController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/gift/view/ViewController.java b/src/main/java/gift/view/ViewController.java index dfff0dff4..06b16be34 100644 --- a/src/main/java/gift/view/ViewController.java +++ b/src/main/java/gift/view/ViewController.java @@ -1,13 +1,13 @@ package gift.view; import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; @Controller public class ViewController { + @GetMapping("/") - public String index(Model model) { + public String index() { return "index"; } } From ecc4ff9685a265f5252cd18d211e183d3c951b8f Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Thu, 27 Jun 2024 14:58:47 +0900 Subject: [PATCH 20/56] feat(view): create product management UI --- src/main/resources/templates/index.html | 213 ++++++++++++++++++++++++ 1 file changed, 213 insertions(+) create mode 100644 src/main/resources/templates/index.html diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html new file mode 100644 index 000000000..93586bc56 --- /dev/null +++ b/src/main/resources/templates/index.html @@ -0,0 +1,213 @@ + + + + + Manage Products + + + + + + +
+
+

Manage Products

+
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
NamePriceImage URLActions
+ + +
+
+ + + + + + + From 199c89413f2c2ec11bdd9d8bcacda34f902e6360 Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Thu, 27 Jun 2024 15:58:41 +0900 Subject: [PATCH 21/56] feat(product): create ServiceDto --- .../product/controller/ProductController.java | 19 +++++++++++-------- .../java/gift/product/dto/ServiceDto.java | 4 ++++ 2 files changed, 15 insertions(+), 8 deletions(-) create mode 100644 src/main/java/gift/product/dto/ServiceDto.java diff --git a/src/main/java/gift/product/controller/ProductController.java b/src/main/java/gift/product/controller/ProductController.java index 2881b7836..bfb271b75 100644 --- a/src/main/java/gift/product/controller/ProductController.java +++ b/src/main/java/gift/product/controller/ProductController.java @@ -1,32 +1,35 @@ -package gift.product.controller; +package gift.product.presentation; -import gift.product.dto.ProductRequest; +import gift.product.presentation.dto.ProductRequest; import gift.product.entity.Product; +import gift.product.service.ProductService; import org.springframework.web.bind.annotation.*; -import java.util.HashMap; import java.util.List; -import java.util.Map; @RestController @RequestMapping("/api/products") public class ProductController { - private final Map productMap = new HashMap<>(); + private final ProductService productService; + + public ProductController(ProductService productService) { + this.productService = productService; + } @GetMapping("") public List getAllProducts() { - return productMap.values().stream().toList(); + return productService.getAllProducts(); } @GetMapping("/{id}") public Product getProductById(@PathVariable(name = "id") Long id) { - return productMap.get(id); + return productService.getProductById(id); } @PostMapping("") public Product createProduct(@RequestBody ProductRequest productRequest) { Long id = addProduct(productRequest); - return productMap.get(id); + return productService.createProduct(); } @PutMapping("/{id}") diff --git a/src/main/java/gift/product/dto/ServiceDto.java b/src/main/java/gift/product/dto/ServiceDto.java new file mode 100644 index 000000000..9e41b8e5a --- /dev/null +++ b/src/main/java/gift/product/dto/ServiceDto.java @@ -0,0 +1,4 @@ +package gift.product.dto; + +public record ServiceDto(Long id, String name, int price, String imageUrl) { +} From a38cc6f031b1675feb2a9974f1efb6fea03237ae Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Thu, 27 Jun 2024 16:01:15 +0900 Subject: [PATCH 22/56] feat(product): add toServiceDto method to convert ProductRequest --- src/main/java/gift/product/dto/ProductRequest.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/gift/product/dto/ProductRequest.java b/src/main/java/gift/product/dto/ProductRequest.java index 7dfba414f..0e2c70f23 100644 --- a/src/main/java/gift/product/dto/ProductRequest.java +++ b/src/main/java/gift/product/dto/ProductRequest.java @@ -1,5 +1,7 @@ package gift.product.dto; +import gift.product.service.ProductService; + import java.util.Objects; public record ProductRequest(String name, int price, String imageUrl) { @@ -7,4 +9,8 @@ public record ProductRequest(String name, int price, String imageUrl) { Objects.requireNonNull(name); Objects.requireNonNull(imageUrl); } + + public ServiceDto toServiceDto() { + return new ServiceDto(null, this.name, this.price, this.imageUrl); + } } From 4c57bf17d3122f93dc861b89e38b97724d5172fa Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Thu, 27 Jun 2024 17:29:36 +0900 Subject: [PATCH 23/56] feat(product): change all method to use productService --- .../product/controller/ProductController.java | 26 +++++-------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/src/main/java/gift/product/controller/ProductController.java b/src/main/java/gift/product/controller/ProductController.java index bfb271b75..a1810ad95 100644 --- a/src/main/java/gift/product/controller/ProductController.java +++ b/src/main/java/gift/product/controller/ProductController.java @@ -1,11 +1,13 @@ -package gift.product.presentation; +package gift.product.controller; -import gift.product.presentation.dto.ProductRequest; +import gift.product.dto.ProductRequest; import gift.product.entity.Product; import gift.product.service.ProductService; import org.springframework.web.bind.annotation.*; +import java.util.HashMap; import java.util.List; +import java.util.Map; @RestController @RequestMapping("/api/products") @@ -28,31 +30,17 @@ public Product getProductById(@PathVariable(name = "id") Long id) { @PostMapping("") public Product createProduct(@RequestBody ProductRequest productRequest) { - Long id = addProduct(productRequest); - return productService.createProduct(); + return productService.createProduct(productRequest.toServiceDto()); } @PutMapping("/{id}") public Product updateProduct(@PathVariable(name = "id") Long id, @RequestBody ProductRequest productRequest) { - productMap.replace(id, new Product(id, productRequest)); - return productMap.get(id); + return productService.updateProduct(productRequest.toServiceDto(id)); } @DeleteMapping("/{id}") public String deleteProduct(@PathVariable(name = "id") Long id) { - productMap.remove(id); + productService.deleteProduct(id); return "product " + id + " is deleted"; } - - private Long addProduct(ProductRequest productRequest) { - Long id = getMaxKey(); - productMap.put(id, new Product(id, productRequest)); - return id; - } - - private Long getMaxKey() { - return 1L + productMap.keySet().stream() - .max(Long::compare) - .orElse(0L); - } } From 20d14b73ad0280050badb42049f89fdbb39f41c0 Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Thu, 27 Jun 2024 17:51:43 +0900 Subject: [PATCH 24/56] feat(product): create Product Not Found Exception --- .../gift/product/exception/ProductNotFoundException.java | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/main/java/gift/product/exception/ProductNotFoundException.java diff --git a/src/main/java/gift/product/exception/ProductNotFoundException.java b/src/main/java/gift/product/exception/ProductNotFoundException.java new file mode 100644 index 000000000..2ff1af342 --- /dev/null +++ b/src/main/java/gift/product/exception/ProductNotFoundException.java @@ -0,0 +1,7 @@ +package gift.product.exception; + +public class ProductNotFoundException extends Exception{ + public ProductNotFoundException() { + super("Product Not Found Exception"); + } +} From dd3d0e5fc14f61f5086d446c8f61f7f63075ff12 Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Thu, 27 Jun 2024 17:52:33 +0900 Subject: [PATCH 25/56] feat(product): create Service Dto --- src/main/java/gift/product/dto/ProductRequest.java | 4 ++++ src/main/java/gift/product/dto/ServiceDto.java | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/src/main/java/gift/product/dto/ProductRequest.java b/src/main/java/gift/product/dto/ProductRequest.java index 0e2c70f23..2a5d42f58 100644 --- a/src/main/java/gift/product/dto/ProductRequest.java +++ b/src/main/java/gift/product/dto/ProductRequest.java @@ -13,4 +13,8 @@ public record ProductRequest(String name, int price, String imageUrl) { public ServiceDto toServiceDto() { return new ServiceDto(null, this.name, this.price, this.imageUrl); } + + public ServiceDto toServiceDto(Long id) { + return new ServiceDto(id, this.name, this.price, this.imageUrl); + } } diff --git a/src/main/java/gift/product/dto/ServiceDto.java b/src/main/java/gift/product/dto/ServiceDto.java index 9e41b8e5a..9df4cd2dd 100644 --- a/src/main/java/gift/product/dto/ServiceDto.java +++ b/src/main/java/gift/product/dto/ServiceDto.java @@ -1,4 +1,9 @@ package gift.product.dto; +import gift.product.entity.Product; + public record ServiceDto(Long id, String name, int price, String imageUrl) { + public Product toProduct() { + return new Product(id, name, price, imageUrl); + } } From fc49319a1efcb8fa78011cd2d4e0e22b009fb275 Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Thu, 27 Jun 2024 17:53:56 +0900 Subject: [PATCH 26/56] feat(product): create Product Service and methods --- .../gift/product/service/ProductService.java | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 src/main/java/gift/product/service/ProductService.java diff --git a/src/main/java/gift/product/service/ProductService.java b/src/main/java/gift/product/service/ProductService.java new file mode 100644 index 000000000..f193564ad --- /dev/null +++ b/src/main/java/gift/product/service/ProductService.java @@ -0,0 +1,47 @@ +package gift.product.service; + +import gift.product.dto.ServiceDto; +import gift.product.entity.Product; +import gift.product.exception.ProductNotFoundException; +import gift.product.repository.ProductRepository; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class ProductService { + private final ProductRepository productRepository; + + public ProductService(ProductRepository productRepository) { + this.productRepository = productRepository; + } + + public List getAllProducts() { + return productRepository.findAll(); + } + + public Product getProductById(Long id) { + return productRepository.findById(id) + .orElseThrow(ProductNotFoundException::new); + } + + public Product createProduct(ServiceDto serviceDto) { + return productRepository.save(serviceDto.toProduct()); + } + + public Product updateProduct(ServiceDto serviceDto) { + validateProductExists(serviceDto.id()); + return productRepository.save(serviceDto.toProduct()); + } + + public void deleteProduct(Long id) { + validateProductExists(id); + productRepository.deleteById(id); + } + + private void validateProductExists(Long id) { + if (!productRepository.existsById(id)) { + throw new ProductNotFoundException(); + } + } +} From a94447576330fb4c2952a1d36f9861fbf6dcfb9c Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Fri, 28 Jun 2024 09:12:17 +0900 Subject: [PATCH 27/56] feat(product): add isNew method in product entity --- src/main/java/gift/product/entity/Product.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/gift/product/entity/Product.java b/src/main/java/gift/product/entity/Product.java index f31d5a6a9..981f14a03 100644 --- a/src/main/java/gift/product/entity/Product.java +++ b/src/main/java/gift/product/entity/Product.java @@ -39,4 +39,7 @@ public String getImageUrl() { return imageUrl; } + public boolean isNew() { + return id == null; + } } From 56f5f57b88ef4d4207831cb4361f8337d001e624 Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Fri, 28 Jun 2024 09:32:29 +0900 Subject: [PATCH 28/56] chore(global): add data jdbc dependency --- build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/build.gradle b/build.gradle index df7db9334..c13eda0cf 100644 --- a/build.gradle +++ b/build.gradle @@ -19,6 +19,7 @@ repositories { dependencies { implementation 'org.springframework.boot:spring-boot-starter-jdbc' + implementation 'org.springframework.boot:spring-boot-starter-data-jdbc' implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' implementation 'org.springframework.boot:spring-boot-starter-web' runtimeOnly 'com.h2database:h2' From 29cf8c42641a63be955354c3753b79132b799565 Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Fri, 28 Jun 2024 12:19:31 +0900 Subject: [PATCH 29/56] feat(product): create product repository and methods --- .../product/repository/ProductRepository.java | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 src/main/java/gift/product/repository/ProductRepository.java diff --git a/src/main/java/gift/product/repository/ProductRepository.java b/src/main/java/gift/product/repository/ProductRepository.java new file mode 100644 index 000000000..76c1ae34e --- /dev/null +++ b/src/main/java/gift/product/repository/ProductRepository.java @@ -0,0 +1,63 @@ +package gift.product.repository; + +import gift.product.entity.Product; +import org.springframework.jdbc.core.simple.JdbcClient; +import org.springframework.stereotype.Repository; +import org.springframework.util.Assert; + +import java.util.List; +import java.util.Optional; + +@Repository +public class ProductRepository { + private final JdbcClient jdbcClient; + + public ProductRepository(JdbcClient jdbcClient) { + this.jdbcClient = jdbcClient; + } + + public List findAll() { + String sql = "select * from products"; + return jdbcClient.sql(sql) + .query(Product.class) + .list(); + } + + public Optional findById(Long id) { + String sql = "select * from products where id = ?"; + return jdbcClient.sql(sql) + .param(id) + .query(Product.class) + .optional(); + } + + public Product save(Product product) { + Assert.notNull(product, "Product must not be null"); + if (product.isNew()) { + String sql = "INSERT INTO products (name, price, image_url) VALUES (?, ?, ?)"; + jdbcClient.sql(sql) + .param(product.getName()) + .param(product.getPrice()) + .param(product.getImageUrl()) + .update(); + + } + if (!product.isNew()) { + String sql = "UPDATE products SET name = ?, price = ?, image_url = ? WHERE id = ?"; + jdbcClient.sql(sql) + .param(product.getName()) + .param(product.getPrice()) + .param(product.getImageUrl()) + .param(product.getId()) + .update(); + } + return product; + } + + public void deleteById(Long id) { + String sql = "delete from products where id = ?"; + jdbcClient.sql(sql) + .param(id) + .update(); + } +} From 714baf2256f9a4d06727af5904267ea1356c415e Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Fri, 28 Jun 2024 12:25:56 +0900 Subject: [PATCH 30/56] feat(global): setting global exception handler --- .../gift/global/exception/BusinessException.java | 7 +++++++ .../global/response/GlobalExceptionHandler.java | 14 ++++++++++++++ .../exception/ProductNotFoundException.java | 4 +++- 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 src/main/java/gift/global/exception/BusinessException.java create mode 100644 src/main/java/gift/global/response/GlobalExceptionHandler.java diff --git a/src/main/java/gift/global/exception/BusinessException.java b/src/main/java/gift/global/exception/BusinessException.java new file mode 100644 index 000000000..f0dac8c6a --- /dev/null +++ b/src/main/java/gift/global/exception/BusinessException.java @@ -0,0 +1,7 @@ +package gift.global.exception; + +public class BusinessException extends RuntimeException { + public BusinessException(String message) { + super(message); + } +} diff --git a/src/main/java/gift/global/response/GlobalExceptionHandler.java b/src/main/java/gift/global/response/GlobalExceptionHandler.java new file mode 100644 index 000000000..010b34ba9 --- /dev/null +++ b/src/main/java/gift/global/response/GlobalExceptionHandler.java @@ -0,0 +1,14 @@ +package gift.global.response; + +import gift.global.exception.BusinessException; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@RestControllerAdvice +public class GlobalExceptionHandler { + @ExceptionHandler(BusinessException.class) + public ResponseEntity handleBusinessException(BusinessException e) { + return ResponseEntity.status(400).body(e.getMessage()); + } +} diff --git a/src/main/java/gift/product/exception/ProductNotFoundException.java b/src/main/java/gift/product/exception/ProductNotFoundException.java index 2ff1af342..08446c981 100644 --- a/src/main/java/gift/product/exception/ProductNotFoundException.java +++ b/src/main/java/gift/product/exception/ProductNotFoundException.java @@ -1,6 +1,8 @@ package gift.product.exception; -public class ProductNotFoundException extends Exception{ +import gift.global.exception.BusinessException; + +public class ProductNotFoundException extends BusinessException { public ProductNotFoundException() { super("Product Not Found Exception"); } From 902f597fddd813e1a83a83d3619ae5ef257ea7bc Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Fri, 28 Jun 2024 12:43:36 +0900 Subject: [PATCH 31/56] feat(global): setting db properties --- src/main/resources/application.properties | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 3d16b65f4..763017f2c 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1 +1,15 @@ 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=1234 + +# H2 Console Configuration +spring.h2.console.enabled=true +spring.h2.console.path=/h2-console + +# SQL Script Initialization +spring.sql.init.mode=always +spring.sql.init.schema-locations=classpath:schema.sql +spring.sql.init.data-locations=classpath:data.sql \ No newline at end of file From 71fb6edbaa90f0dd902ad7cf5cc627cafc6d6043 Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Fri, 28 Jun 2024 12:44:25 +0900 Subject: [PATCH 32/56] feat(product): create input initial data sql --- src/main/resources/data.sql | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 src/main/resources/data.sql diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql new file mode 100644 index 000000000..594dd9348 --- /dev/null +++ b/src/main/resources/data.sql @@ -0,0 +1,2 @@ +INSERT INTO products (name, price, image_url) VALUES ('test1', 10000, 'http://'); +INSERT INTO products (name, price, image_url) VALUES ('test2', 20000, 'http://'); From b00ee6f67c33f685b69d640181bc32bdaf0e36f5 Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Fri, 28 Jun 2024 12:44:54 +0900 Subject: [PATCH 33/56] feat(product): create schema sql to make table products --- src/main/resources/schema.sql | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 src/main/resources/schema.sql diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql new file mode 100644 index 000000000..e38d50c96 --- /dev/null +++ b/src/main/resources/schema.sql @@ -0,0 +1,6 @@ +CREATE TABLE IF NOT EXISTS products ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(255) NOT NULL, + price BIGINT NOT NULL, + image_url VARCHAR(255) +); \ No newline at end of file From 1d4f2bbad9bd7b3a557731ab713b89fa71114531 Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Fri, 28 Jun 2024 12:47:41 +0900 Subject: [PATCH 34/56] fix(product): add default constructor and change directory entity to domain --- .../java/gift/product/controller/ProductController.java | 4 +--- .../java/gift/product/{entity => domain}/Product.java | 9 ++++++++- src/main/java/gift/product/dto/ServiceDto.java | 2 +- .../java/gift/product/repository/ProductRepository.java | 2 +- src/main/java/gift/product/service/ProductService.java | 7 +++---- 5 files changed, 14 insertions(+), 10 deletions(-) rename src/main/java/gift/product/{entity => domain}/Product.java (73%) diff --git a/src/main/java/gift/product/controller/ProductController.java b/src/main/java/gift/product/controller/ProductController.java index a1810ad95..3a1ac212d 100644 --- a/src/main/java/gift/product/controller/ProductController.java +++ b/src/main/java/gift/product/controller/ProductController.java @@ -1,13 +1,11 @@ package gift.product.controller; import gift.product.dto.ProductRequest; -import gift.product.entity.Product; +import gift.product.domain.Product; import gift.product.service.ProductService; import org.springframework.web.bind.annotation.*; -import java.util.HashMap; import java.util.List; -import java.util.Map; @RestController @RequestMapping("/api/products") diff --git a/src/main/java/gift/product/entity/Product.java b/src/main/java/gift/product/domain/Product.java similarity index 73% rename from src/main/java/gift/product/entity/Product.java rename to src/main/java/gift/product/domain/Product.java index 981f14a03..b062d7fd7 100644 --- a/src/main/java/gift/product/entity/Product.java +++ b/src/main/java/gift/product/domain/Product.java @@ -1,13 +1,20 @@ -package gift.product.entity; +package gift.product.domain; import gift.product.dto.ProductRequest; +import org.springframework.data.annotation.Id; +import org.springframework.data.relational.core.mapping.Table; +@Table("products") public class Product { + @Id private Long id; private String name; private int price; private String imageUrl; + // JDBC 에서 엔티티 클래스를 인스턴스화할 때 반드시 기본 생성자와 파라미터 생성자가 필요하다 + public Product() {} + public Product(Long id, String name, int price, String imageUrl) { this.id = id; this.name = name; diff --git a/src/main/java/gift/product/dto/ServiceDto.java b/src/main/java/gift/product/dto/ServiceDto.java index 9df4cd2dd..019e1842a 100644 --- a/src/main/java/gift/product/dto/ServiceDto.java +++ b/src/main/java/gift/product/dto/ServiceDto.java @@ -1,6 +1,6 @@ package gift.product.dto; -import gift.product.entity.Product; +import gift.product.domain.Product; public record ServiceDto(Long id, String name, int price, String imageUrl) { public Product toProduct() { diff --git a/src/main/java/gift/product/repository/ProductRepository.java b/src/main/java/gift/product/repository/ProductRepository.java index 76c1ae34e..66efae5aa 100644 --- a/src/main/java/gift/product/repository/ProductRepository.java +++ b/src/main/java/gift/product/repository/ProductRepository.java @@ -1,6 +1,6 @@ package gift.product.repository; -import gift.product.entity.Product; +import gift.product.domain.Product; import org.springframework.jdbc.core.simple.JdbcClient; import org.springframework.stereotype.Repository; import org.springframework.util.Assert; diff --git a/src/main/java/gift/product/service/ProductService.java b/src/main/java/gift/product/service/ProductService.java index f193564ad..34c6b377a 100644 --- a/src/main/java/gift/product/service/ProductService.java +++ b/src/main/java/gift/product/service/ProductService.java @@ -1,7 +1,7 @@ package gift.product.service; import gift.product.dto.ServiceDto; -import gift.product.entity.Product; +import gift.product.domain.Product; import gift.product.exception.ProductNotFoundException; import gift.product.repository.ProductRepository; import org.springframework.stereotype.Service; @@ -40,8 +40,7 @@ public void deleteProduct(Long id) { } private void validateProductExists(Long id) { - if (!productRepository.existsById(id)) { - throw new ProductNotFoundException(); - } + productRepository.findById(id) + .orElseThrow(ProductNotFoundException::new); } } From 199a7ff9fb89d2e504c489270f7dfb295aab6afd Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Fri, 28 Jun 2024 12:57:47 +0900 Subject: [PATCH 35/56] feat(global): create result code enum --- .../java/gift/global/response/ResultCode.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/main/java/gift/global/response/ResultCode.java diff --git a/src/main/java/gift/global/response/ResultCode.java b/src/main/java/gift/global/response/ResultCode.java new file mode 100644 index 000000000..0bfc77f09 --- /dev/null +++ b/src/main/java/gift/global/response/ResultCode.java @@ -0,0 +1,20 @@ +package gift.global.response; + +import org.springframework.http.HttpStatusCode; + +public enum ResultCode { + // Product + + + ; + // HttpStatusCode 로 관리하는 것이 좋을까, 아니면 String으로 관리하는 것이 좋을까? + private final HttpStatusCode status; + private final String resultCode; + private final String message; + + ResultCode(HttpStatusCode status, String resultCode, String message) { + this.status = status; + this.resultCode = resultCode; + this.message = message; + } +} From b388095bdffbdc129d0985828661b88fe99c1734 Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Fri, 28 Jun 2024 13:05:10 +0900 Subject: [PATCH 36/56] feat(product): set result code of product api --- .../java/gift/global/response/ResultCode.java | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/main/java/gift/global/response/ResultCode.java b/src/main/java/gift/global/response/ResultCode.java index 0bfc77f09..f2af50d2d 100644 --- a/src/main/java/gift/global/response/ResultCode.java +++ b/src/main/java/gift/global/response/ResultCode.java @@ -1,20 +1,35 @@ package gift.global.response; -import org.springframework.http.HttpStatusCode; - public enum ResultCode { // Product - + GET_ALL_PRODUCTS_SUCCESS("200", "P001", "모든 제품 조회 성공"), + GET_PRODUCT_BY_ID_SUCCESS("200", "P002", "단일 제품 조회 성공"), + CREATE_PRODUCT_SUCCESS("200", "P003", "제품 추가 성공"), + UPDATE_PRODUCT_SUCCESS("200", "P002", "제품 수정 성공"), + DELETE_PRODUCT_SUCCESS("200", "P002", "제품 삭제 성공"), ; - // HttpStatusCode 로 관리하는 것이 좋을까, 아니면 String으로 관리하는 것이 좋을까? - private final HttpStatusCode status; + + // status 를 HttpStatus 로 관리하는 것이 좋을까, 아니면 String으로 관리하는 것이 좋을까? + private final String status; private final String resultCode; private final String message; - ResultCode(HttpStatusCode status, String resultCode, String message) { + ResultCode(String status, String resultCode, String message) { this.status = status; this.resultCode = resultCode; this.message = message; } + + public String getStatus() { + return status; + } + + public String getResultCode() { + return resultCode; + } + + public String getMessage() { + return message; + } } From 8a22e261089440ab172b395deac2e78baae525ae Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Fri, 28 Jun 2024 13:11:15 +0900 Subject: [PATCH 37/56] feat(global): create result response --- src/main/java/gift/global/response/ResultResponse.java | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/main/java/gift/global/response/ResultResponse.java diff --git a/src/main/java/gift/global/response/ResultResponse.java b/src/main/java/gift/global/response/ResultResponse.java new file mode 100644 index 000000000..24bb682a3 --- /dev/null +++ b/src/main/java/gift/global/response/ResultResponse.java @@ -0,0 +1,8 @@ +package gift.global.response; + +public record ResultResponse(String code, String message, T data) { + // 자바 record 에서 생성자를 만들 경우, 반드시 canonical(표준) 생성자를 사용해야 한다!! + ResultResponse(ResultCode resultCode, T data) { + this(resultCode.getResultCode(), resultCode.getMessage(), data); + } +} From 9410cc5b3146fd7ffcf4f95bca9a78e28b6e407e Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Fri, 28 Jun 2024 13:14:16 +0900 Subject: [PATCH 38/56] fix(global): change result code status type String to int --- .../java/gift/global/response/ResultCode.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/java/gift/global/response/ResultCode.java b/src/main/java/gift/global/response/ResultCode.java index f2af50d2d..b7e87fae2 100644 --- a/src/main/java/gift/global/response/ResultCode.java +++ b/src/main/java/gift/global/response/ResultCode.java @@ -2,26 +2,26 @@ public enum ResultCode { // Product - GET_ALL_PRODUCTS_SUCCESS("200", "P001", "모든 제품 조회 성공"), - GET_PRODUCT_BY_ID_SUCCESS("200", "P002", "단일 제품 조회 성공"), - CREATE_PRODUCT_SUCCESS("200", "P003", "제품 추가 성공"), - UPDATE_PRODUCT_SUCCESS("200", "P002", "제품 수정 성공"), - DELETE_PRODUCT_SUCCESS("200", "P002", "제품 삭제 성공"), + GET_ALL_PRODUCTS_SUCCESS(200, "P001", "모든 제품 조회 성공"), + GET_PRODUCT_BY_ID_SUCCESS(200, "P002", "단일 제품 조회 성공"), + CREATE_PRODUCT_SUCCESS(200, "P003", "제품 추가 성공"), + UPDATE_PRODUCT_SUCCESS(200, "P002", "제품 수정 성공"), + DELETE_PRODUCT_SUCCESS(200, "P002", "제품 삭제 성공"), ; - // status 를 HttpStatus 로 관리하는 것이 좋을까, 아니면 String으로 관리하는 것이 좋을까? - private final String status; + // status 를 HttpStatus 로 관리하는 것이 좋을까, 아니면 int로 관리하는 것이 좋을까? + private final int status; private final String resultCode; private final String message; - ResultCode(String status, String resultCode, String message) { + ResultCode(int status, String resultCode, String message) { this.status = status; this.resultCode = resultCode; this.message = message; } - public String getStatus() { + public int getStatus() { return status; } From c3736c505f233c8a072e2281588f1f7bf13aea58 Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Fri, 28 Jun 2024 13:17:01 +0900 Subject: [PATCH 39/56] fix(global): change result response private to public --- src/main/java/gift/global/response/ResultResponse.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/gift/global/response/ResultResponse.java b/src/main/java/gift/global/response/ResultResponse.java index 24bb682a3..50ed06353 100644 --- a/src/main/java/gift/global/response/ResultResponse.java +++ b/src/main/java/gift/global/response/ResultResponse.java @@ -2,7 +2,7 @@ public record ResultResponse(String code, String message, T data) { // 자바 record 에서 생성자를 만들 경우, 반드시 canonical(표준) 생성자를 사용해야 한다!! - ResultResponse(ResultCode resultCode, T data) { + public ResultResponse(ResultCode resultCode, T data) { this(resultCode.getResultCode(), resultCode.getMessage(), data); } } From 7bac451fdbcc97d298a1058aa4303560af030133 Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Fri, 28 Jun 2024 13:22:12 +0900 Subject: [PATCH 40/56] chore(global): change ResultResponse name to ResultResponseDto --- .../response/{ResultResponse.java => ResultResponseDto.java} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename src/main/java/gift/global/response/{ResultResponse.java => ResultResponseDto.java} (62%) diff --git a/src/main/java/gift/global/response/ResultResponse.java b/src/main/java/gift/global/response/ResultResponseDto.java similarity index 62% rename from src/main/java/gift/global/response/ResultResponse.java rename to src/main/java/gift/global/response/ResultResponseDto.java index 50ed06353..1829813c0 100644 --- a/src/main/java/gift/global/response/ResultResponse.java +++ b/src/main/java/gift/global/response/ResultResponseDto.java @@ -1,8 +1,8 @@ package gift.global.response; -public record ResultResponse(String code, String message, T data) { +public record ResultResponseDto(String code, String message, T data) { // 자바 record 에서 생성자를 만들 경우, 반드시 canonical(표준) 생성자를 사용해야 한다!! - public ResultResponse(ResultCode resultCode, T data) { + public ResultResponseDto(ResultCode resultCode, T data) { this(resultCode.getResultCode(), resultCode.getMessage(), data); } } From 0759818c10d891b8f3b7757b1bd8a779eb452b2e Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Fri, 28 Jun 2024 13:24:46 +0900 Subject: [PATCH 41/56] fix(http): update url for get all products --- src/main/http/product/Product.http | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/http/product/Product.http b/src/main/http/product/Product.http index c465f395d..e5f6a467f 100644 --- a/src/main/http/product/Product.http +++ b/src/main/http/product/Product.http @@ -1,5 +1,5 @@ ### get product list -GET http://localhost:8080/api/products/list +GET http://localhost:8080/api/products Content-Type: application/json ### get product From 7e61e4199adb38c0a5f8ca26e9c9ae64256a9591 Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Fri, 28 Jun 2024 13:25:24 +0900 Subject: [PATCH 42/56] feat(global): create simple result response dto --- .../java/gift/global/response/SimpleResultResponseDto.java | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/main/java/gift/global/response/SimpleResultResponseDto.java diff --git a/src/main/java/gift/global/response/SimpleResultResponseDto.java b/src/main/java/gift/global/response/SimpleResultResponseDto.java new file mode 100644 index 000000000..2abcd9905 --- /dev/null +++ b/src/main/java/gift/global/response/SimpleResultResponseDto.java @@ -0,0 +1,7 @@ +package gift.global.response; + +public record SimpleResultResponseDto(String code, String message) { + public SimpleResultResponseDto(ResultCode resultCode) { + this(resultCode.getResultCode(), resultCode.getMessage()); + } +} From 3ef0273a54848efc442976f2cdfcde3d7fc5f309 Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Fri, 28 Jun 2024 14:38:46 +0900 Subject: [PATCH 43/56] chore(global): move global exception handler package response to handler --- .../global/{response => handler}/GlobalExceptionHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/main/java/gift/global/{response => handler}/GlobalExceptionHandler.java (94%) diff --git a/src/main/java/gift/global/response/GlobalExceptionHandler.java b/src/main/java/gift/global/handler/GlobalExceptionHandler.java similarity index 94% rename from src/main/java/gift/global/response/GlobalExceptionHandler.java rename to src/main/java/gift/global/handler/GlobalExceptionHandler.java index 010b34ba9..2f64b1248 100644 --- a/src/main/java/gift/global/response/GlobalExceptionHandler.java +++ b/src/main/java/gift/global/handler/GlobalExceptionHandler.java @@ -1,4 +1,4 @@ -package gift.global.response; +package gift.global.handler; import gift.global.exception.BusinessException; import org.springframework.http.ResponseEntity; From 1d83a5799eee9a2469b4afa0bd4c1f0461417771 Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Fri, 28 Jun 2024 14:40:15 +0900 Subject: [PATCH 44/56] feat(product): change all method response to use ResponseEntity --- .../product/controller/ProductController.java | 42 ++++++++++++++----- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/src/main/java/gift/product/controller/ProductController.java b/src/main/java/gift/product/controller/ProductController.java index 3a1ac212d..a95d5edb7 100644 --- a/src/main/java/gift/product/controller/ProductController.java +++ b/src/main/java/gift/product/controller/ProductController.java @@ -1,8 +1,12 @@ package gift.product.controller; +import gift.global.response.ResultCode; +import gift.global.response.ResultResponseDto; +import gift.global.response.SimpleResultResponseDto; import gift.product.dto.ProductRequest; import gift.product.domain.Product; import gift.product.service.ProductService; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.util.List; @@ -17,28 +21,44 @@ public ProductController(ProductService productService) { } @GetMapping("") - public List getAllProducts() { - return productService.getAllProducts(); + public ResponseEntity>> getAllProducts() { + List products = productService.getAllProducts(); + return createResponse(ResultCode.GET_ALL_PRODUCTS_SUCCESS, products); } @GetMapping("/{id}") - public Product getProductById(@PathVariable(name = "id") Long id) { - return productService.getProductById(id); + public ResponseEntity> getProductById(@PathVariable(name = "id") Long id) { + Product product = productService.getProductById(id); + return createResponse(ResultCode.GET_PRODUCT_BY_ID_SUCCESS, product); } @PostMapping("") - public Product createProduct(@RequestBody ProductRequest productRequest) { - return productService.createProduct(productRequest.toServiceDto()); + public ResponseEntity createProduct(@RequestBody ProductRequest productRequest) { + productService.createProduct(productRequest.toServiceDto()); + return createSimpleResponse(ResultCode.CREATE_PRODUCT_SUCCESS); } @PutMapping("/{id}") - public Product updateProduct(@PathVariable(name = "id") Long id, @RequestBody ProductRequest productRequest) { - return productService.updateProduct(productRequest.toServiceDto(id)); + public ResponseEntity updateProduct(@PathVariable(name = "id") Long id, @RequestBody ProductRequest productRequest) { + productService.updateProduct(productRequest.toServiceDto(id)); + return createSimpleResponse(ResultCode.UPDATE_PRODUCT_SUCCESS); } @DeleteMapping("/{id}") - public String deleteProduct(@PathVariable(name = "id") Long id) { + public ResponseEntity deleteProduct(@PathVariable(name = "id") Long id) { productService.deleteProduct(id); - return "product " + id + " is deleted"; + return createSimpleResponse(ResultCode.DELETE_PRODUCT_SUCCESS); } -} + + private ResponseEntity> createResponse(ResultCode resultCode, T data) { + ResultResponseDto resultResponseDto = new ResultResponseDto<>(resultCode, data); + return ResponseEntity.status(resultCode.getStatus()) + .body(resultResponseDto); + } + + private ResponseEntity createSimpleResponse(ResultCode resultCode) { + var resultResponseDto = new SimpleResultResponseDto(resultCode); + return ResponseEntity.status(resultCode.getStatus()) + .body(resultResponseDto); + } +} \ No newline at end of file From c3005316188c55283db9689dde7e56b963922eba Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Fri, 28 Jun 2024 14:44:15 +0900 Subject: [PATCH 45/56] fix(product): remove return value from create and update methods --- .../java/gift/product/repository/ProductRepository.java | 3 +-- src/main/java/gift/product/service/ProductService.java | 8 ++++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main/java/gift/product/repository/ProductRepository.java b/src/main/java/gift/product/repository/ProductRepository.java index 66efae5aa..aa48a117e 100644 --- a/src/main/java/gift/product/repository/ProductRepository.java +++ b/src/main/java/gift/product/repository/ProductRepository.java @@ -31,7 +31,7 @@ public Optional findById(Long id) { .optional(); } - public Product save(Product product) { + public void save(Product product) { Assert.notNull(product, "Product must not be null"); if (product.isNew()) { String sql = "INSERT INTO products (name, price, image_url) VALUES (?, ?, ?)"; @@ -51,7 +51,6 @@ public Product save(Product product) { .param(product.getId()) .update(); } - return product; } public void deleteById(Long id) { diff --git a/src/main/java/gift/product/service/ProductService.java b/src/main/java/gift/product/service/ProductService.java index 34c6b377a..1a2dda1f0 100644 --- a/src/main/java/gift/product/service/ProductService.java +++ b/src/main/java/gift/product/service/ProductService.java @@ -25,13 +25,13 @@ public Product getProductById(Long id) { .orElseThrow(ProductNotFoundException::new); } - public Product createProduct(ServiceDto serviceDto) { - return productRepository.save(serviceDto.toProduct()); + public void createProduct(ServiceDto serviceDto) { + productRepository.save(serviceDto.toProduct()); } - public Product updateProduct(ServiceDto serviceDto) { + public void updateProduct(ServiceDto serviceDto) { validateProductExists(serviceDto.id()); - return productRepository.save(serviceDto.toProduct()); + productRepository.save(serviceDto.toProduct()); } public void deleteProduct(Long id) { From 77076beddfd3b85c5b78b8fa9eb4c299833af336 Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Fri, 28 Jun 2024 14:51:42 +0900 Subject: [PATCH 46/56] feat(global): create response helper util --- .../gift/global/utils/ResponseHelper.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/main/java/gift/global/utils/ResponseHelper.java diff --git a/src/main/java/gift/global/utils/ResponseHelper.java b/src/main/java/gift/global/utils/ResponseHelper.java new file mode 100644 index 000000000..22ddedd4c --- /dev/null +++ b/src/main/java/gift/global/utils/ResponseHelper.java @@ -0,0 +1,20 @@ +package gift.global.utils; + +import gift.global.response.ResultCode; +import gift.global.response.ResultResponseDto; +import gift.global.response.SimpleResultResponseDto; +import org.springframework.http.ResponseEntity; + +public class ResponseHelper { + public static ResponseEntity> createResponse(ResultCode resultCode, T data) { + ResultResponseDto resultResponseDto = new ResultResponseDto<>(resultCode, data); + return org.springframework.http.ResponseEntity.status(resultCode.getStatus()) + .body(resultResponseDto); + } + + public static ResponseEntity createSimpleResponse(ResultCode resultCode) { + var resultResponseDto = new SimpleResultResponseDto(resultCode); + return ResponseEntity.status(resultCode.getStatus()) + .body(resultResponseDto); + } +} From fd8961965d7987d0a0c3a5a301a54172dd902f90 Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Fri, 28 Jun 2024 14:53:46 +0900 Subject: [PATCH 47/56] refactor(product): apply ResponseHelper for response creation in ProductController --- .../product/controller/ProductController.java | 23 +++++-------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/src/main/java/gift/product/controller/ProductController.java b/src/main/java/gift/product/controller/ProductController.java index a95d5edb7..296856a96 100644 --- a/src/main/java/gift/product/controller/ProductController.java +++ b/src/main/java/gift/product/controller/ProductController.java @@ -3,6 +3,7 @@ import gift.global.response.ResultCode; import gift.global.response.ResultResponseDto; import gift.global.response.SimpleResultResponseDto; +import gift.global.utils.ResponseHelper; import gift.product.dto.ProductRequest; import gift.product.domain.Product; import gift.product.service.ProductService; @@ -23,42 +24,30 @@ public ProductController(ProductService productService) { @GetMapping("") public ResponseEntity>> getAllProducts() { List products = productService.getAllProducts(); - return createResponse(ResultCode.GET_ALL_PRODUCTS_SUCCESS, products); + return ResponseHelper.createResponse(ResultCode.GET_ALL_PRODUCTS_SUCCESS, products); } @GetMapping("/{id}") public ResponseEntity> getProductById(@PathVariable(name = "id") Long id) { Product product = productService.getProductById(id); - return createResponse(ResultCode.GET_PRODUCT_BY_ID_SUCCESS, product); + return ResponseHelper.createResponse(ResultCode.GET_PRODUCT_BY_ID_SUCCESS, product); } @PostMapping("") public ResponseEntity createProduct(@RequestBody ProductRequest productRequest) { productService.createProduct(productRequest.toServiceDto()); - return createSimpleResponse(ResultCode.CREATE_PRODUCT_SUCCESS); + return ResponseHelper.createSimpleResponse(ResultCode.CREATE_PRODUCT_SUCCESS); } @PutMapping("/{id}") public ResponseEntity updateProduct(@PathVariable(name = "id") Long id, @RequestBody ProductRequest productRequest) { productService.updateProduct(productRequest.toServiceDto(id)); - return createSimpleResponse(ResultCode.UPDATE_PRODUCT_SUCCESS); + return ResponseHelper.createSimpleResponse(ResultCode.UPDATE_PRODUCT_SUCCESS); } @DeleteMapping("/{id}") public ResponseEntity deleteProduct(@PathVariable(name = "id") Long id) { productService.deleteProduct(id); - return createSimpleResponse(ResultCode.DELETE_PRODUCT_SUCCESS); - } - - private ResponseEntity> createResponse(ResultCode resultCode, T data) { - ResultResponseDto resultResponseDto = new ResultResponseDto<>(resultCode, data); - return ResponseEntity.status(resultCode.getStatus()) - .body(resultResponseDto); - } - - private ResponseEntity createSimpleResponse(ResultCode resultCode) { - var resultResponseDto = new SimpleResultResponseDto(resultCode); - return ResponseEntity.status(resultCode.getStatus()) - .body(resultResponseDto); + return ResponseHelper.createSimpleResponse(ResultCode.DELETE_PRODUCT_SUCCESS); } } \ No newline at end of file From 8fdd8ea2a4a4690e89e25efc09bade9747ac1935 Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Fri, 28 Jun 2024 15:04:50 +0900 Subject: [PATCH 48/56] fix(product): change method name from isNew to checkNew to fix JSON serialization --- src/main/java/gift/product/domain/Product.java | 2 +- src/main/java/gift/product/repository/ProductRepository.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/gift/product/domain/Product.java b/src/main/java/gift/product/domain/Product.java index b062d7fd7..d0d473e73 100644 --- a/src/main/java/gift/product/domain/Product.java +++ b/src/main/java/gift/product/domain/Product.java @@ -46,7 +46,7 @@ public String getImageUrl() { return imageUrl; } - public boolean isNew() { + public boolean checkNew() { return id == null; } } diff --git a/src/main/java/gift/product/repository/ProductRepository.java b/src/main/java/gift/product/repository/ProductRepository.java index aa48a117e..72df257f5 100644 --- a/src/main/java/gift/product/repository/ProductRepository.java +++ b/src/main/java/gift/product/repository/ProductRepository.java @@ -33,7 +33,7 @@ public Optional findById(Long id) { public void save(Product product) { Assert.notNull(product, "Product must not be null"); - if (product.isNew()) { + if (product.checkNew()) { String sql = "INSERT INTO products (name, price, image_url) VALUES (?, ?, ?)"; jdbcClient.sql(sql) .param(product.getName()) @@ -42,7 +42,7 @@ public void save(Product product) { .update(); } - if (!product.isNew()) { + if (!product.checkNew()) { String sql = "UPDATE products SET name = ?, price = ?, image_url = ? WHERE id = ?"; jdbcClient.sql(sql) .param(product.getName()) From ebee7f4600a2d0444fd03bb24d4d7cd31bfd4a50 Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Fri, 28 Jun 2024 15:16:07 +0900 Subject: [PATCH 49/56] fix(view): update index.html to use response.data for request parameter --- src/main/resources/templates/index.html | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html index 93586bc56..6b8985ecd 100644 --- a/src/main/resources/templates/index.html +++ b/src/main/resources/templates/index.html @@ -90,12 +90,12 @@ // 제품 목록을 서버에서 가져와서 테이블에 표시하는 함수 function loadProducts() { - $.get(apiBaseUrl, function (data) { + $.get(apiBaseUrl, function (response) { // 테이블 본문을 비우기 $('#productTableBody').empty(); // products 데이터로 테이블 행 생성 - data.forEach(product => { + response.data.forEach(product => { $('#productTableBody').append(` @@ -164,12 +164,12 @@ // "Edit" 버튼 클릭 시 해당 제품 데이터를 가져와 모달에 표시 $(document).on('click', '.editProduct', function () { const productId = $(this).closest('tr').data('id'); - $.get(apiBaseUrl + '/' + productId, function (data) { + $.get(apiBaseUrl + '/' + productId, function (response) { $('#productModalLabel').text('Edit Product'); - $('#productId').val(data.id); - $('#productName').val(data.name); - $('#productPrice').val(data.price); - $('#productImageUrl').val(data.imageUrl); + $('#productId').val(response.data.id); + $('#productName').val(response.data.name); + $('#productPrice').val(response.data.price); + $('#productImageUrl').val(response.data.imageUrl); $('#productModal').modal('show'); }); }); From d512a900f1e5bac77f687ff25d51a3c92a52f690 Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Fri, 28 Jun 2024 15:23:01 +0900 Subject: [PATCH 50/56] refactor(global): change resultCode variable to code in ResultCode enum --- src/main/java/gift/global/response/ResultCode.java | 10 +++++----- .../java/gift/global/response/ResultResponseDto.java | 2 +- .../gift/global/response/SimpleResultResponseDto.java | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/gift/global/response/ResultCode.java b/src/main/java/gift/global/response/ResultCode.java index b7e87fae2..bf2c46adc 100644 --- a/src/main/java/gift/global/response/ResultCode.java +++ b/src/main/java/gift/global/response/ResultCode.java @@ -12,12 +12,12 @@ public enum ResultCode { // status 를 HttpStatus 로 관리하는 것이 좋을까, 아니면 int로 관리하는 것이 좋을까? private final int status; - private final String resultCode; + private final String code; private final String message; - ResultCode(int status, String resultCode, String message) { + ResultCode(int status, String code, String message) { this.status = status; - this.resultCode = resultCode; + this.code = code; this.message = message; } @@ -25,8 +25,8 @@ public int getStatus() { return status; } - public String getResultCode() { - return resultCode; + public String getCode() { + return code; } public String getMessage() { diff --git a/src/main/java/gift/global/response/ResultResponseDto.java b/src/main/java/gift/global/response/ResultResponseDto.java index 1829813c0..bb3aec0c1 100644 --- a/src/main/java/gift/global/response/ResultResponseDto.java +++ b/src/main/java/gift/global/response/ResultResponseDto.java @@ -3,6 +3,6 @@ public record ResultResponseDto(String code, String message, T data) { // 자바 record 에서 생성자를 만들 경우, 반드시 canonical(표준) 생성자를 사용해야 한다!! public ResultResponseDto(ResultCode resultCode, T data) { - this(resultCode.getResultCode(), resultCode.getMessage(), data); + this(resultCode.getCode(), resultCode.getMessage(), data); } } diff --git a/src/main/java/gift/global/response/SimpleResultResponseDto.java b/src/main/java/gift/global/response/SimpleResultResponseDto.java index 2abcd9905..895a220db 100644 --- a/src/main/java/gift/global/response/SimpleResultResponseDto.java +++ b/src/main/java/gift/global/response/SimpleResultResponseDto.java @@ -2,6 +2,6 @@ public record SimpleResultResponseDto(String code, String message) { public SimpleResultResponseDto(ResultCode resultCode) { - this(resultCode.getResultCode(), resultCode.getMessage()); + this(resultCode.getCode(), resultCode.getMessage()); } } From bedd0c2b6de5930cf7622f99abae04543b5014b0 Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Fri, 28 Jun 2024 15:23:28 +0900 Subject: [PATCH 51/56] feat(global): create ErrorCode enum --- .../java/gift/global/response/ErrorCode.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 src/main/java/gift/global/response/ErrorCode.java diff --git a/src/main/java/gift/global/response/ErrorCode.java b/src/main/java/gift/global/response/ErrorCode.java new file mode 100644 index 000000000..ccf8c8953 --- /dev/null +++ b/src/main/java/gift/global/response/ErrorCode.java @@ -0,0 +1,28 @@ +package gift.global.response; + +public enum ErrorCode { + // Product + PRODUCT_NOT_FOUND_ERROR(400, "EP001", "Product Not Found Error"), + ; + private final int status; + private final String code; + private final String message; + + ErrorCode(int status, String code, String message) { + this.status = status; + this.code = code; + this.message = message; + } + + public int getStatus() { + return status; + } + + public String getCode() { + return code; + } + + public String getMessage() { + return message; + } +} From 9d897c44dd911c17b2137b50f2408c0a0f24cbca Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Fri, 28 Jun 2024 15:25:32 +0900 Subject: [PATCH 52/56] feat(global): create ErrorResponseDto record --- src/main/java/gift/global/response/ErrorResponseDto.java | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/main/java/gift/global/response/ErrorResponseDto.java diff --git a/src/main/java/gift/global/response/ErrorResponseDto.java b/src/main/java/gift/global/response/ErrorResponseDto.java new file mode 100644 index 000000000..1c631b9c1 --- /dev/null +++ b/src/main/java/gift/global/response/ErrorResponseDto.java @@ -0,0 +1,7 @@ +package gift.global.response; + +public record ErrorResponseDto(String code, String message) { + public ErrorResponseDto(ErrorCode errorCode) { + this(errorCode.getCode(), errorCode.getMessage()); + } +} From 39cddd4f2d26940fe23d60f85a9c0024d99ea2f6 Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Fri, 28 Jun 2024 15:27:39 +0900 Subject: [PATCH 53/56] fix(global): add private constructor to ResponseHelper class --- src/main/java/gift/global/utils/ResponseHelper.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/gift/global/utils/ResponseHelper.java b/src/main/java/gift/global/utils/ResponseHelper.java index 22ddedd4c..0be286587 100644 --- a/src/main/java/gift/global/utils/ResponseHelper.java +++ b/src/main/java/gift/global/utils/ResponseHelper.java @@ -6,6 +6,8 @@ import org.springframework.http.ResponseEntity; public class ResponseHelper { + private ResponseHelper() {} + public static ResponseEntity> createResponse(ResultCode resultCode, T data) { ResultResponseDto resultResponseDto = new ResultResponseDto<>(resultCode, data); return org.springframework.http.ResponseEntity.status(resultCode.getStatus()) From 18efb9c19f451ecc42484f92586f80c4e8c34646 Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Fri, 28 Jun 2024 15:41:28 +0900 Subject: [PATCH 54/56] refactor(global): update error handling to return ErrorResponseDto instead of String --- .../global/exception/BusinessException.java | 18 ++++++++++++++++-- .../global/handler/GlobalExceptionHandler.java | 6 ++++-- .../java/gift/global/utils/ResponseHelper.java | 10 +++++++--- .../exception/ProductNotFoundException.java | 3 ++- 4 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/main/java/gift/global/exception/BusinessException.java b/src/main/java/gift/global/exception/BusinessException.java index f0dac8c6a..8c46ea1b5 100644 --- a/src/main/java/gift/global/exception/BusinessException.java +++ b/src/main/java/gift/global/exception/BusinessException.java @@ -1,7 +1,21 @@ package gift.global.exception; +import gift.global.response.ErrorCode; + public class BusinessException extends RuntimeException { - public BusinessException(String message) { - super(message); + private final ErrorCode errorCode; + + public BusinessException(ErrorCode errorCode) { + super(errorCode.getMessage()); + this.errorCode = errorCode; + } + + public ErrorCode getErrorCode() { + return errorCode; + } + + @Override + public String toString() { + return errorCode.toString() + " : " + super.toString(); } } diff --git a/src/main/java/gift/global/handler/GlobalExceptionHandler.java b/src/main/java/gift/global/handler/GlobalExceptionHandler.java index 2f64b1248..48486ee22 100644 --- a/src/main/java/gift/global/handler/GlobalExceptionHandler.java +++ b/src/main/java/gift/global/handler/GlobalExceptionHandler.java @@ -1,6 +1,8 @@ package gift.global.handler; import gift.global.exception.BusinessException; +import gift.global.response.ErrorResponseDto; +import gift.global.utils.ResponseHelper; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; @@ -8,7 +10,7 @@ @RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(BusinessException.class) - public ResponseEntity handleBusinessException(BusinessException e) { - return ResponseEntity.status(400).body(e.getMessage()); + public ResponseEntity handleBusinessException(BusinessException e) { + return ResponseHelper.createErrorResponse(e.getErrorCode()); } } diff --git a/src/main/java/gift/global/utils/ResponseHelper.java b/src/main/java/gift/global/utils/ResponseHelper.java index 0be286587..928d30f68 100644 --- a/src/main/java/gift/global/utils/ResponseHelper.java +++ b/src/main/java/gift/global/utils/ResponseHelper.java @@ -1,8 +1,6 @@ package gift.global.utils; -import gift.global.response.ResultCode; -import gift.global.response.ResultResponseDto; -import gift.global.response.SimpleResultResponseDto; +import gift.global.response.*; import org.springframework.http.ResponseEntity; public class ResponseHelper { @@ -19,4 +17,10 @@ public static ResponseEntity createSimpleResponse(Resul return ResponseEntity.status(resultCode.getStatus()) .body(resultResponseDto); } + + public static ResponseEntity createErrorResponse(ErrorCode errorCode) { + ErrorResponseDto errorResponseDto = new ErrorResponseDto(errorCode); + return ResponseEntity.status(errorCode.getStatus()) + .body(errorResponseDto); + } } diff --git a/src/main/java/gift/product/exception/ProductNotFoundException.java b/src/main/java/gift/product/exception/ProductNotFoundException.java index 08446c981..693aadee3 100644 --- a/src/main/java/gift/product/exception/ProductNotFoundException.java +++ b/src/main/java/gift/product/exception/ProductNotFoundException.java @@ -1,9 +1,10 @@ package gift.product.exception; import gift.global.exception.BusinessException; +import gift.global.response.ErrorCode; public class ProductNotFoundException extends BusinessException { public ProductNotFoundException() { - super("Product Not Found Exception"); + super(ErrorCode.PRODUCT_NOT_FOUND_ERROR); } } From 025b774f611f863701c7a88159bf43750a9b349b Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Fri, 28 Jun 2024 15:51:11 +0900 Subject: [PATCH 55/56] refactor(product): rename ProductRequest to ProductRequestDto --- .../gift/product/controller/ProductController.java | 10 +++++----- src/main/java/gift/product/domain/Product.java | 10 +++++----- .../{ProductRequest.java => ProductRequestDto.java} | 6 ++---- 3 files changed, 12 insertions(+), 14 deletions(-) rename src/main/java/gift/product/dto/{ProductRequest.java => ProductRequestDto.java} (72%) diff --git a/src/main/java/gift/product/controller/ProductController.java b/src/main/java/gift/product/controller/ProductController.java index 296856a96..e3356b1d4 100644 --- a/src/main/java/gift/product/controller/ProductController.java +++ b/src/main/java/gift/product/controller/ProductController.java @@ -4,7 +4,7 @@ import gift.global.response.ResultResponseDto; import gift.global.response.SimpleResultResponseDto; import gift.global.utils.ResponseHelper; -import gift.product.dto.ProductRequest; +import gift.product.dto.ProductRequestDto; import gift.product.domain.Product; import gift.product.service.ProductService; import org.springframework.http.ResponseEntity; @@ -34,14 +34,14 @@ public ResponseEntity> getProductById(@PathVariable(n } @PostMapping("") - public ResponseEntity createProduct(@RequestBody ProductRequest productRequest) { - productService.createProduct(productRequest.toServiceDto()); + public ResponseEntity createProduct(@RequestBody ProductRequestDto productRequestDto) { + productService.createProduct(productRequestDto.toServiceDto()); return ResponseHelper.createSimpleResponse(ResultCode.CREATE_PRODUCT_SUCCESS); } @PutMapping("/{id}") - public ResponseEntity updateProduct(@PathVariable(name = "id") Long id, @RequestBody ProductRequest productRequest) { - productService.updateProduct(productRequest.toServiceDto(id)); + public ResponseEntity updateProduct(@PathVariable(name = "id") Long id, @RequestBody ProductRequestDto productRequestDto) { + productService.updateProduct(productRequestDto.toServiceDto(id)); return ResponseHelper.createSimpleResponse(ResultCode.UPDATE_PRODUCT_SUCCESS); } diff --git a/src/main/java/gift/product/domain/Product.java b/src/main/java/gift/product/domain/Product.java index d0d473e73..edc7add4b 100644 --- a/src/main/java/gift/product/domain/Product.java +++ b/src/main/java/gift/product/domain/Product.java @@ -1,6 +1,6 @@ package gift.product.domain; -import gift.product.dto.ProductRequest; +import gift.product.dto.ProductRequestDto; import org.springframework.data.annotation.Id; import org.springframework.data.relational.core.mapping.Table; @@ -22,11 +22,11 @@ public Product(Long id, String name, int price, String imageUrl) { this.imageUrl = imageUrl; } - public Product(Long id, ProductRequest productRequest) { + public Product(Long id, ProductRequestDto productRequestDto) { this.id = id; - this.name = productRequest.name(); - this.price = productRequest.price(); - this.imageUrl = productRequest.imageUrl(); + this.name = productRequestDto.name(); + this.price = productRequestDto.price(); + this.imageUrl = productRequestDto.imageUrl(); } diff --git a/src/main/java/gift/product/dto/ProductRequest.java b/src/main/java/gift/product/dto/ProductRequestDto.java similarity index 72% rename from src/main/java/gift/product/dto/ProductRequest.java rename to src/main/java/gift/product/dto/ProductRequestDto.java index 2a5d42f58..e2dfd53ff 100644 --- a/src/main/java/gift/product/dto/ProductRequest.java +++ b/src/main/java/gift/product/dto/ProductRequestDto.java @@ -1,11 +1,9 @@ package gift.product.dto; -import gift.product.service.ProductService; - import java.util.Objects; -public record ProductRequest(String name, int price, String imageUrl) { - public ProductRequest { +public record ProductRequestDto(String name, int price, String imageUrl) { + public ProductRequestDto { Objects.requireNonNull(name); Objects.requireNonNull(imageUrl); } From 52a232e2e12af9c1a24c8332a58df6ff2178f1fb Mon Sep 17 00:00:00 2001 From: JaeBin <201924576@pusan.ac.kr> Date: Fri, 28 Jun 2024 16:21:17 +0900 Subject: [PATCH 56/56] feat(view): add search product by productId --- src/main/resources/templates/index.html | 74 +++++++++++++++++++------ 1 file changed, 58 insertions(+), 16 deletions(-) diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html index 6b8985ecd..72fc92ae8 100644 --- a/src/main/resources/templates/index.html +++ b/src/main/resources/templates/index.html @@ -17,6 +17,15 @@

Manage Products

+ +
+
+ +
+
+ +
+
@@ -29,9 +38,6 @@

Manage Products

- - - @@ -114,18 +120,9 @@ // "Add New Product" 버튼 클릭 시 모달을 열고 폼 초기화 $('#addProduct').click(function () { - // modal title 지정 $('#productModalLabel').text('Add New Product'); - - // productForm 을 지정 - // jQuery 객체는 배열 형태로 반환되기 때문에, 순수 DOM 요소를 선택하기 위해 배열의 첫 번째 요소를 선택 - // 폼 내의 모든 입력 필드를 초기화 $('#productForm')[0].reset(); - - // hidden 필드값인 id 제거 $('#productId').val(''); - - // close 로 되어 있던 modal 속성을 show 로 수정 $('#productModal').modal('show'); }); @@ -138,7 +135,7 @@ }; const productId = $('#productId').val(); - // + // POST 요청으로 Setting const ajaxOptions = { url: apiBaseUrl, type: 'POST', @@ -150,8 +147,8 @@ } }; - // 만약 아이디 값이 존재한다면 => 기존 객체를 수정 - // 따라서 url 과 method를 PUT에 맞게 수정해준다 + // 만약 productId 값이 할당되어 있으면, 해당 데이터 수정 요청이다 + // 때문에, PUT 으로 요청을 바꾼다 if (productId) { ajaxOptions.url = apiBaseUrl + '/' + productId; ajaxOptions.type = 'PUT'; @@ -160,9 +157,11 @@ $.ajax(ajaxOptions); }); - // "Edit" 버튼 클릭 시 해당 제품 데이터를 가져와 모달에 표시 $(document).on('click', '.editProduct', function () { + // $(this)는 jQuery 선택자로, 현재 이벤트가 발생한 요소를 선택 + // .closest('tr')는 선택된 요소(this)부터 시작하여 가장 가까운 tr (테이블 행) 요소를 찾는다 + // .data('id')는 선택된 요소의 data-id 속성 값을 가져온다 const productId = $(this).closest('tr').data('id'); $.get(apiBaseUrl + '/' + productId, function (response) { $('#productModalLabel').text('Edit Product'); @@ -205,6 +204,49 @@ $('.productCheckbox').prop('checked', this.checked); }); + // 제품 ID로 검색 + $('#searchProduct').click(function () { + const productId = $('#searchProductId').val(); + if (productId) { + $.ajax({ + url: apiBaseUrl + '/' + productId, + type: 'GET', + success: function (response) { + // 검색 결과를 테이블 본문에 표시 + const $productTableBody = $('#productTableBody'); + $productTableBody.empty(); + const product = response.data; + $productTableBody.append(` + + + + + + + + `); + }, + // 'xhr'은 XMLHttpRequest 객체로 요청에 대한 상세 정보를 저장한다 + // + error: function (xhr) { + // response 에 json 값이 있는 지 확인하고, error code를 확인한다 + if (xhr.responseJSON && xhr.responseJSON.code === 'EP001') { + alert(xhr.responseJSON.message); + return + } + alert('An error occurred while fetching the product.'); + } + }); + } + if (!productId) { + // 검색 필드가 비어 있을 때 전체 제품 목록을 로드 + loadProducts(); + } + }); + // 페이지 로드 시 제품 목록을 불러오기 loadProducts(); });
Actions
${product.name}${product.price}${product.imageUrl} + + +