From bb85df0df6c0ebe6275373f3c7b961c86a0205fd Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Mon, 1 Jul 2024 15:58:34 +0900
Subject: [PATCH 01/53] =?UTF-8?q?docs=20:=200=EB=8B=A8=EA=B3=84=20?=
=?UTF-8?q?=EC=A4=80=EB=B9=84=20=EB=AC=B8=EC=84=9C=20=EC=9E=91=EC=84=B1?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 8376bdfff..3bc7e2563 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,10 @@
-# spring-gift-wishlist
\ No newline at end of file
+# spring-gift-wishlist
+
+# 2주차 위시 리스트 - 요청과 응답 심화
+
+### step 0
+
+1주차 코드 가져오기
+
+1주차 PR에 대해서 잘못된 저장소에서 fork하는 바람에 리뷰를 받지 못하였습니다.
+
From 9ccee7ee7323edac4365a4bd9a5c6d6068b771b9 Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Mon, 1 Jul 2024 15:58:53 +0900
Subject: [PATCH 02/53] =?UTF-8?q?init=20:=20=EC=9D=B4=EC=A0=84=20=EC=9E=91?=
=?UTF-8?q?=EC=97=85=20=EC=BD=94=EB=93=9C=20=EB=B3=B5=EC=82=AC?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../gift/controller/AdminPageController.java | 62 ++++++++++++++
.../gift/controller/ProductController.java | 79 +++++++++++++++++
.../gift/database/JdbcProductRepository.java | 84 +++++++++++++++++++
src/main/java/gift/dto/ProductDTO.java | 72 ++++++++++++++++
src/main/java/gift/model/Product.java | 51 +++++++++++
.../java/gift/service/ProductService.java | 26 ++++++
.../java/gift/service/ProductServiceImpl.java | 64 ++++++++++++++
7 files changed, 438 insertions(+)
create mode 100644 src/main/java/gift/controller/AdminPageController.java
create mode 100644 src/main/java/gift/controller/ProductController.java
create mode 100644 src/main/java/gift/database/JdbcProductRepository.java
create mode 100644 src/main/java/gift/dto/ProductDTO.java
create mode 100644 src/main/java/gift/model/Product.java
create mode 100644 src/main/java/gift/service/ProductService.java
create mode 100644 src/main/java/gift/service/ProductServiceImpl.java
diff --git a/src/main/java/gift/controller/AdminPageController.java b/src/main/java/gift/controller/AdminPageController.java
new file mode 100644
index 000000000..7efe83bf7
--- /dev/null
+++ b/src/main/java/gift/controller/AdminPageController.java
@@ -0,0 +1,62 @@
+package gift.controller;
+
+import gift.dto.ProductDTO;
+import gift.service.ProductService;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.DeleteMapping;
+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.PutMapping;
+import org.springframework.web.bind.annotation.RequestAttribute;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+@Controller
+public class AdminPageController {
+
+ private final ProductService pm;
+
+ public AdminPageController(ProductService pm) {
+ this.pm = pm;
+ }
+
+ @GetMapping("/admin")
+ public String adminPage(Model model) {
+ model.addAttribute("products",pm.readAll());
+ model.addAttribute("productDTO",new ProductDTO());
+ return "admin/index";//렌더링하는 html 이름
+ }
+
+ @PostMapping("/admin") //admin으로 오는 post에 대해서 submit
+ public String adminPageSubmit(@ModelAttribute("productDTO") ProductDTO productDTO) {
+ pm.create(productDTO); //서비스에 접근해서 해당 부분을 추가해주도록 한다.
+ return "redirect:/admin";
+ }
+
+ @PutMapping("/admin/{id}")
+ public String adminPageUpdate(@PathVariable Long id,@ModelAttribute("productDTO") ProductDTO productDTO) {
+ changeCheckAndUpdate(id,productDTO);
+ return "redirect:/admin";
+ }
+
+ @DeleteMapping("/admin/{id}")
+ public String adminPageDelete(@PathVariable Long id) {
+ pm.delete(id);
+ return "redirect:/admin";
+ }
+
+ private void changeCheckAndUpdate(Long id, ProductDTO dto) {
+
+ if (dto.getName().length()>0){
+ pm.updateName(id, dto.getName());
+ }
+ if (dto.getPrice()!=null){
+ pm.updatePrice(id, dto.getPrice());
+ }
+ if (dto.getImageUrl().length()>0){
+ pm.updateImageUrl(id, dto.getImageUrl());
+ }
+ }
+}
diff --git a/src/main/java/gift/controller/ProductController.java b/src/main/java/gift/controller/ProductController.java
new file mode 100644
index 000000000..7b8a3de37
--- /dev/null
+++ b/src/main/java/gift/controller/ProductController.java
@@ -0,0 +1,79 @@
+package gift.controller;
+
+import gift.dto.ProductDTO;
+import gift.service.ProductService;
+import java.util.List;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 상품 추가,수정,삭제,조회를 위한 api end-point
+ *
+ * $/api/products
+ */
+@RestController
+public class ProductController {
+
+ private final ProductService pm;
+
+ public ProductController(ProductService pm) {
+ this.pm = pm;
+ }
+
+ /**
+ * 상품 전체 목록 반환
+ * @return 상품 DTO
+ */
+ @GetMapping("/api/products")
+ public List getList(){
+ List dto = pm.readAll();
+ return dto;
+ }
+
+ /**
+ * 새로운 상품 생성
+ * @param dto id가 존재하는 상태로 입력되더라도 무시됨.
+ */
+ @PostMapping("/api/products")
+ public void add(ProductDTO dto){
+ pm.create(dto);
+ }
+
+ /**
+ * 기존 상품 수정
+ * @param id 수정하고자 하는 상품의 id
+ * @param dto 수정하고자 하는 값 이외 null로 지정
+ */
+ @PutMapping("/api/products")
+ public void update(@RequestParam("id") Long id, @RequestBody ProductDTO dto){
+ if(id==null){
+ throw new IllegalArgumentException("id를 입력해주세요");
+ }
+ changeCheckAndUpdate(id, dto);
+ }
+
+ @DeleteMapping("/api/products")
+ public void delete(@RequestParam("id") Long id){
+ pm.delete(id);
+ }
+
+ private void changeCheckAndUpdate(Long id, ProductDTO dto) {
+ if (dto.getName()!=null){
+ pm.updateName(id, dto.getName());
+ }
+ if (dto.getPrice()!=null){
+ pm.updatePrice(id, dto.getPrice());
+ }
+ if (dto.getImageUrl()!=null){
+ pm.updateImageUrl(id, dto.getImageUrl());
+ }
+ }
+
+
+}
\ No newline at end of file
diff --git a/src/main/java/gift/database/JdbcProductRepository.java b/src/main/java/gift/database/JdbcProductRepository.java
new file mode 100644
index 000000000..10db1ac7b
--- /dev/null
+++ b/src/main/java/gift/database/JdbcProductRepository.java
@@ -0,0 +1,84 @@
+package gift.database;
+
+
+import gift.model.Product;
+import java.sql.PreparedStatement;
+import java.util.List;
+import javax.sql.DataSource;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.jdbc.support.GeneratedKeyHolder;
+import org.springframework.jdbc.support.KeyHolder;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public class JdbcProductRepository {
+ private final JdbcTemplate template;
+
+ public JdbcProductRepository(DataSource dataSource) {
+ this.template = new JdbcTemplate(dataSource);
+ createTable(); //초기 테이블 생성
+ }
+
+ private void createTable() {
+ template.update("create table if not exists product("
+ + "id long primary key auto_increment, "
+ + "name varchar(255), "
+ + "price int,"
+ + "imageUrl varchar(255))");
+ }
+
+ /**
+ * 새로운 상품 추가
+ * @param product id 값은 무시됨.
+ * @return DB에 저장된 id값이 포함된 객체 반환
+ */
+ public Product insertProduct(Product product) {
+ String sql = "insert into product (name, price, imageUrl) values (?,?,?)";
+ KeyHolder keyHolder = new GeneratedKeyHolder();
+ template.update(connection -> {
+ PreparedStatement ps = connection.prepareStatement(sql,new String[]{"id"});
+ ps.setString(1, product.getName());
+ ps.setInt(2, product.getPrice());
+ ps.setString(3, product.getImageUrl());
+ return ps;
+ },keyHolder);
+ long key = keyHolder.getKey().longValue();
+ product.setId(key);
+ return product;
+ }
+
+ //기존 상품 수정
+ public void updateProduct(Long id,Product product) {
+ String sql = "update product set name = ?, price = ?, imageUrl = ? where id = ?";
+ template.update(sql,product.getName(),product.getPrice(),product.getImageUrl(),id);
+ }
+
+ //상품 단일 조회
+ public Product getProduct(Long id) {
+ String sql = "select * from product where id = ?";
+ return template.queryForObject(sql,productRowMapper(),id);
+ }
+
+ //상품 전체 조회
+ public List findAllProducts() {
+ String sql = "select * from product";
+ return template.query(sql,productRowMapper());
+ }
+
+ //상품 삭제
+ public void deleteProduct(Long id) {
+ String sql = "delete from product where id = ?";
+ template.update(sql,id);
+ }
+
+
+ private RowMapper productRowMapper() {
+ return (rs, rowNum) -> new Product(rs.getLong("id"),
+ rs.getString("name"),
+ rs.getInt("price"),
+ rs.getString("imageUrl"));
+
+ }
+
+}
diff --git a/src/main/java/gift/dto/ProductDTO.java b/src/main/java/gift/dto/ProductDTO.java
new file mode 100644
index 000000000..12b4a8dbc
--- /dev/null
+++ b/src/main/java/gift/dto/ProductDTO.java
@@ -0,0 +1,72 @@
+package gift.dto;
+
+import gift.model.Product;
+
+public class ProductDTO {
+
+ private Long id;
+ private String name;
+ private Integer price;
+ private String imageUrl;
+
+ //타임리프 사용을 위한 기본 생성자
+ public ProductDTO() {}
+
+ public ProductDTO(Long id, String name, Integer price, String imageUrl) {
+ this.id = id;
+ this.name = name;
+ this.price = price;
+ this.imageUrl = imageUrl;
+ }
+
+ //상품 모델을 DTO로 빠르게 전환
+ public ProductDTO(Product product) {
+ this.id = product.getId();
+ this.name = product.getName();
+ this.price = product.getPrice();
+ this.imageUrl = product.getImageUrl();
+ }
+
+ //디버깅 필요 시 체크용 toString
+ @Override
+ public String toString() {
+ return "ProductDTO{" +
+ "id=" + id +
+ ", name='" + name + '\'' +
+ ", price=" + price +
+ ", imageUrl='" + imageUrl + '\'' +
+ '}';
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Integer getPrice() {
+ return price;
+ }
+
+ public String getImageUrl() {
+ return imageUrl;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public void setPrice(Integer price) {
+ this.price = price;
+ }
+
+ public void setImageUrl(String imageUrl) {
+ this.imageUrl = imageUrl;
+ }
+}
diff --git a/src/main/java/gift/model/Product.java b/src/main/java/gift/model/Product.java
new file mode 100644
index 000000000..03cb9f7f1
--- /dev/null
+++ b/src/main/java/gift/model/Product.java
@@ -0,0 +1,51 @@
+package gift.model;
+
+public class Product {
+
+ //null을 위해 wrapper class 사용
+ private Long id;
+ private String name;
+ private Integer price;
+ private String imageUrl;
+
+ public Product(Long id, String name, Integer price, String imageUrl) {
+ this.id = id;
+ this.name = name;
+ this.price = price;
+ this.imageUrl = imageUrl;
+ }
+
+ //setter - 타임리프 사용을 위해 필요
+ public void setId(Long id) {
+ this.id=id;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public void setPrice(int price) {
+ this.price = price;
+ }
+
+ public void setImageUrl(String imageUrl) {
+ this.imageUrl = imageUrl;
+ }
+
+ //getter
+ 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/service/ProductService.java b/src/main/java/gift/service/ProductService.java
new file mode 100644
index 000000000..68c5f3456
--- /dev/null
+++ b/src/main/java/gift/service/ProductService.java
@@ -0,0 +1,26 @@
+package gift.service;
+
+import gift.dto.ProductDTO;
+import java.util.List;
+
+public interface ProductService {
+
+ /*
+ 상품 수정 시 만약 문제가 생기면 어떤 값에 의해서 생기는 지 파악의 어려움을 막기 위해
+ update를 각 항목마다 분리
+ */
+
+ //상품 리스트 전체 조회
+ List readAll();
+ //새 상품 생성
+ void create(ProductDTO prod);
+ //상품 이름 변경
+ void updateName(long id,String name);
+ //상품 가격 변경
+ void updatePrice(long id,int price);
+ //상품 이미지 변경
+ void updateImageUrl(long id,String url);
+ //상품 삭제
+ void delete(long id);
+
+}
diff --git a/src/main/java/gift/service/ProductServiceImpl.java b/src/main/java/gift/service/ProductServiceImpl.java
new file mode 100644
index 000000000..393bb4d14
--- /dev/null
+++ b/src/main/java/gift/service/ProductServiceImpl.java
@@ -0,0 +1,64 @@
+package gift.service;
+
+import gift.database.JdbcProductRepository;
+import gift.dto.ProductDTO;
+import gift.model.Product;
+import java.util.ArrayList;
+import java.util.List;
+import org.springframework.stereotype.Service;
+
+@Service
+public class ProductServiceImpl implements ProductService{
+
+ private final JdbcProductRepository jdbcProductRepository;
+
+ public ProductServiceImpl(JdbcProductRepository jdbcProductRepository) {
+ this.jdbcProductRepository = jdbcProductRepository;
+ }
+
+ @Override
+ public List readAll() {
+ var products = jdbcProductRepository.findAllProducts();
+ List productDTOList = new ArrayList<>();
+
+ for(var product : products) { //DTO로 전환
+ productDTOList.add(new ProductDTO(product));
+ }
+
+ return productDTOList;
+ }
+
+ //새로운 상품 추가
+ @Override
+ public void create(ProductDTO prod) {
+ jdbcProductRepository.insertProduct(new Product(null,prod.getName(),prod.getPrice(),prod.getImageUrl()));
+ }
+
+
+ @Override
+ public void updateName(long id, String name) {
+ var prod = jdbcProductRepository.getProduct(id);
+ prod.setName(name);
+ jdbcProductRepository.updateProduct(id,prod);
+
+ }
+
+ @Override
+ public void updatePrice(long id, int price) {
+ var prod = jdbcProductRepository.getProduct(id);
+ prod.setPrice(price);
+ jdbcProductRepository.updateProduct(id,prod);
+ }
+
+ @Override
+ public void updateImageUrl(long id, String url) {
+ var prod = jdbcProductRepository.getProduct(id);
+ prod.setImageUrl(url);
+ jdbcProductRepository.updateProduct(id,prod);
+ }
+
+ @Override
+ public void delete(long id) {
+ jdbcProductRepository.deleteProduct(id);
+ }
+}
From 2edaa1f466ac1f51326ab0b5262491a480a328d4 Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Wed, 3 Jul 2024 15:28:40 +0900
Subject: [PATCH 03/53] =?UTF-8?q?docs=20:=20step1=20milestone=20=EC=B6=94?=
=?UTF-8?q?=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 19 +++++-
src/main/resources/templates/admin/index.html | 66 +++++++++++++++++++
2 files changed, 84 insertions(+), 1 deletion(-)
create mode 100644 src/main/resources/templates/admin/index.html
diff --git a/README.md b/README.md
index 3bc7e2563..cb6e626f1 100644
--- a/README.md
+++ b/README.md
@@ -2,9 +2,26 @@
# 2주차 위시 리스트 - 요청과 응답 심화
-### step 0
+# step 0
1주차 코드 가져오기
1주차 PR에 대해서 잘못된 저장소에서 fork하는 바람에 리뷰를 받지 못하였습니다.
+# step 1
+
+목표
+
+- 상품 추가, 수정 시 잘못된 값에 대한 처리 + 응답 설정
+- 상품 이름 제한 : 공백 포함 최대 15글자
+- 일부 특수 문자만 허용
+- "카카오" 포함 문구 입력 시 따로 confirm 이후 진행 가능하도록
+
+## milestone
+
+-[ ] 스프링 validation 의존성 추가
+-[ ] feat : DTO valid 추가
+-[ ] refact : service - 상품 update 로직 변경 (하나로 통합)
+-[ ] feat : @ControllerAdvice 클래스 추가
+-[ ] feat : "카카오" 검사를 위한 예외클래스 추가
+
diff --git a/src/main/resources/templates/admin/index.html b/src/main/resources/templates/admin/index.html
new file mode 100644
index 000000000..47c7a64a3
--- /dev/null
+++ b/src/main/resources/templates/admin/index.html
@@ -0,0 +1,66 @@
+
+
+
+
+ 상품 관리자 페이지
+
+
+ gift 상품 주문 관리자 페이지
+
+
+
+
+
+ 아이디 |
+ 상품명 |
+ 가격 |
+ 이미지 |
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
From 963dd376f0717fc5e7ac216d311fa49c2a1c850e Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Wed, 3 Jul 2024 15:30:51 +0900
Subject: [PATCH 04/53] =?UTF-8?q?build=20:=20spring=20validation=20?=
=?UTF-8?q?=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 2 +-
build.gradle | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index cb6e626f1..5df55c753 100644
--- a/README.md
+++ b/README.md
@@ -19,7 +19,7 @@
## milestone
--[ ] 스프링 validation 의존성 추가
+-[X] 스프링 validation 의존성 추가
-[ ] feat : DTO valid 추가
-[ ] refact : service - 상품 update 로직 변경 (하나로 통합)
-[ ] feat : @ControllerAdvice 클래스 추가
diff --git a/build.gradle b/build.gradle
index df7db9334..333a9b39a 100644
--- a/build.gradle
+++ b/build.gradle
@@ -21,6 +21,7 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
+ implementation 'org.springframework.boot:spring-boot-starter-validation'
runtimeOnly 'com.h2database:h2'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
From d79909ed426d643ebe8d5617785717aa66e1a348 Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Wed, 3 Jul 2024 18:41:11 +0900
Subject: [PATCH 05/53] =?UTF-8?q?build=20:=20spring=20webflux=20=EC=B6=94?=
=?UTF-8?q?=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
test를 위해서 추가
---
build.gradle | 2 ++
1 file changed, 2 insertions(+)
diff --git a/build.gradle b/build.gradle
index 333a9b39a..f5f54ac12 100644
--- a/build.gradle
+++ b/build.gradle
@@ -22,6 +22,8 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-validation'
+ implementation 'org.springframework.boot:spring-boot-starter-webflux'
+ testImplementation 'io.projectreactor:reactor-test'
runtimeOnly 'com.h2database:h2'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
From 4dc8e451c736f8b56269190f6a55c8034ee0df6f Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Wed, 3 Jul 2024 19:56:09 +0900
Subject: [PATCH 06/53] =?UTF-8?q?feat=20:=20NotNull,=20namesize=20?=
=?UTF-8?q?=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
컨트롤러에서 해당 부분에 대해서 @Valid 구현 필요
---
src/main/java/gift/dto/ProductDTO.java | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/src/main/java/gift/dto/ProductDTO.java b/src/main/java/gift/dto/ProductDTO.java
index 12b4a8dbc..5b53a726e 100644
--- a/src/main/java/gift/dto/ProductDTO.java
+++ b/src/main/java/gift/dto/ProductDTO.java
@@ -1,12 +1,19 @@
package gift.dto;
import gift.model.Product;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+
public class ProductDTO {
private Long id;
+ @Size(min=1,max=15)
+ @NotNull
private String name;
+ @NotNull
private Integer price;
+ @NotNull
private String imageUrl;
//타임리프 사용을 위한 기본 생성자
From 1ce8f4b53dfb34eaa130b051e5f5d458a95094bd Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Wed, 3 Jul 2024 20:22:32 +0900
Subject: [PATCH 07/53] =?UTF-8?q?fix=20:=20add=20=EC=97=90=EC=84=9C=20@Req?=
=?UTF-8?q?uestBody=20=EA=B0=80=20=EC=97=86=EC=96=B4=EC=84=9C=20=EC=83=9D?=
=?UTF-8?q?=EA=B8=B0=EB=8A=94=20500=EC=98=A4=EB=A5=98=20=ED=95=B4=EA=B2=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/main/java/gift/controller/ProductController.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/main/java/gift/controller/ProductController.java b/src/main/java/gift/controller/ProductController.java
index 7b8a3de37..f9d62ead8 100644
--- a/src/main/java/gift/controller/ProductController.java
+++ b/src/main/java/gift/controller/ProductController.java
@@ -41,7 +41,7 @@ public List getList(){
* @param dto id가 존재하는 상태로 입력되더라도 무시됨.
*/
@PostMapping("/api/products")
- public void add(ProductDTO dto){
+ public void add(@RequestBody ProductDTO dto){
pm.create(dto);
}
From a98e47dd92958c2b143555e0198e3d6922a58c06 Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Thu, 4 Jul 2024 15:42:48 +0900
Subject: [PATCH 08/53] =?UTF-8?q?docs=20:=20=EC=9E=91=EC=97=85=20=EC=9D=BC?=
=?UTF-8?q?=EC=A7=80=20=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
history.md | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
create mode 100644 history.md
diff --git a/history.md b/history.md
new file mode 100644
index 000000000..a7d26a27a
--- /dev/null
+++ b/history.md
@@ -0,0 +1,16 @@
+# 작업 일지
+
+스스로 기록용도로 작성
+
+## 7월 4일 (목)
+
+### PR 코멘트 반영하기
+
+1. GetMapping 등에서 url 이 동일한 것에 대해서 통합표기 (@controller)
+2. rest 도메인과 관련해서 엔드포인트 네이밍의 해석의 모호함
+3. 문법 띄워쓰기 관련 google java style guide 참고하기 (내가 못하면 cmd + option + L) 을 매번 눌러주자
+4. pm 변수명 삭제 → productService로 변경 (처음에는 productManager 정도로 생각하고 임시로 작성하였다가 바꾸지 않았던 것이 문제가 됨)
+5. 불필요한 주석 삭제 → 사용하지 않는 것들에 대해서 (가독성 떨어뜨림)
+
+에 대해서 진행
+
From 6bfa3e5ccbbe95117c22fa5ad5da1374de1663b9 Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Thu, 4 Jul 2024 15:43:23 +0900
Subject: [PATCH 09/53] =?UTF-8?q?refact=20:=20api=20request=EB=A5=BC=20?=
=?UTF-8?q?=EC=9C=84=ED=95=9C=20=EA=B3=B5=ED=86=B5=20=EA=B2=BD=EB=A1=9C=20?=
=?UTF-8?q?=EC=A0=95=EB=A6=AC?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../gift/controller/ProductController.java | 31 +++++++++++--------
1 file changed, 18 insertions(+), 13 deletions(-)
diff --git a/src/main/java/gift/controller/ProductController.java b/src/main/java/gift/controller/ProductController.java
index f9d62ead8..676a5003c 100644
--- a/src/main/java/gift/controller/ProductController.java
+++ b/src/main/java/gift/controller/ProductController.java
@@ -9,6 +9,7 @@
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@@ -17,6 +18,7 @@
*
* $/api/products
*/
+@RequestMapping("/api")
@RestController
public class ProductController {
@@ -28,49 +30,52 @@ public ProductController(ProductService pm) {
/**
* 상품 전체 목록 반환
+ *
* @return 상품 DTO
*/
- @GetMapping("/api/products")
- public List getList(){
+ @GetMapping("/products")
+ public List getList() {
List dto = pm.readAll();
return dto;
}
/**
* 새로운 상품 생성
+ *
* @param dto id가 존재하는 상태로 입력되더라도 무시됨.
*/
- @PostMapping("/api/products")
- public void add(@RequestBody ProductDTO dto){
+ @PostMapping("/products")
+ public void add(@RequestBody ProductDTO dto) {
pm.create(dto);
}
/**
* 기존 상품 수정
- * @param id 수정하고자 하는 상품의 id
+ *
+ * @param id 수정하고자 하는 상품의 id
* @param dto 수정하고자 하는 값 이외 null로 지정
*/
- @PutMapping("/api/products")
- public void update(@RequestParam("id") Long id, @RequestBody ProductDTO dto){
- if(id==null){
+ @PutMapping("/products")
+ public void update(@RequestParam("id") Long id, @RequestBody ProductDTO dto) {
+ if (id == null) {
throw new IllegalArgumentException("id를 입력해주세요");
}
changeCheckAndUpdate(id, dto);
}
- @DeleteMapping("/api/products")
- public void delete(@RequestParam("id") Long id){
+ @DeleteMapping("/products")
+ public void delete(@RequestParam("id") Long id) {
pm.delete(id);
}
private void changeCheckAndUpdate(Long id, ProductDTO dto) {
- if (dto.getName()!=null){
+ if (dto.getName() != null) {
pm.updateName(id, dto.getName());
}
- if (dto.getPrice()!=null){
+ if (dto.getPrice() != null) {
pm.updatePrice(id, dto.getPrice());
}
- if (dto.getImageUrl()!=null){
+ if (dto.getImageUrl() != null) {
pm.updateImageUrl(id, dto.getImageUrl());
}
}
From 8c1cf2da22737eecf04be3de9a31964f204a2ff9 Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Thu, 4 Jul 2024 15:44:35 +0900
Subject: [PATCH 10/53] =?UTF-8?q?test=20:=20api=20200=20=EC=9D=91=EB=8B=B5?=
=?UTF-8?q?=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
경로 변경한 것에 대해서 정상 동작 확인
---
.../controller/ProductControllerTest.java | 67 +++++++++++++++++++
1 file changed, 67 insertions(+)
create mode 100644 src/test/java/gift/controller/ProductControllerTest.java
diff --git a/src/test/java/gift/controller/ProductControllerTest.java b/src/test/java/gift/controller/ProductControllerTest.java
new file mode 100644
index 000000000..6f316cc18
--- /dev/null
+++ b/src/test/java/gift/controller/ProductControllerTest.java
@@ -0,0 +1,67 @@
+package gift.controller;
+
+import gift.dto.ProductDTO;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.web.server.LocalServerPort;
+import org.springframework.http.MediaType;
+import org.springframework.test.web.reactive.server.WebTestClient;
+import org.springframework.test.web.reactive.server.WebTestClient.ResponseSpec;
+import org.springframework.web.reactive.function.BodyInserters;
+
+
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+class ProductControllerTest {
+
+ @LocalServerPort
+ private int port;
+ private String baseUrl;
+ @Autowired
+ private WebTestClient webClient;
+
+ @BeforeEach
+ void setUp() {
+ baseUrl = "http://localhost:" + port;
+ webClient = WebTestClient.bindToServer().baseUrl(baseUrl).build();
+ }
+
+ //사실상 get test는 필요없다고 생각되어짐.
+ //post나 put 등의 작업에서 잘못된 데이터에 대해서 해당 응답을 돌려주는 지를 검증해야함.
+ @Test
+ @DisplayName("정상 get 응답 확인")
+ void getProduct() {
+
+ webClient.get().uri("/api/products").accept(MediaType.APPLICATION_JSON).exchange()
+ .expectStatus().isOk();
+
+ }
+
+
+ @Test
+ @DisplayName("name이 15글자가 넘는 경우")
+ void addMore15word() {
+ //given
+ String name = "123123123123123123123";
+ int price = 123;
+ String imageUrl = "abcd";
+ ProductDTO dto = getProductDTO(name, price, imageUrl);
+
+ //when then
+ createPostReqeust(dto).expectStatus().isBadRequest();
+ }
+
+
+ private ResponseSpec createPostReqeust(ProductDTO dto) {
+ return webClient.post().uri("/api/products").accept(MediaType.APPLICATION_JSON)
+ .body(BodyInserters.fromValue(dto)).exchange();
+
+ //request body 에는 BodyInserters.formValue로 객체 -> body 데이터로 변환
+ }
+
+ private static ProductDTO getProductDTO(String name, int price, String imageUrl) {
+ return new ProductDTO(null, name, price, imageUrl);
+ }
+}
\ No newline at end of file
From 1ef4e4203c1eb51417757e81cb4f4d6ab41b0ea5 Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Thu, 4 Jul 2024 15:47:08 +0900
Subject: [PATCH 11/53] =?UTF-8?q?refact=20:=20=EB=AA=A8=ED=98=B8=ED=95=9C?=
=?UTF-8?q?=20=EB=B3=80=EC=88=98=20=EB=AA=85=20=EC=88=98=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
history.md | 4 ++--
.../gift/controller/ProductController.java | 19 +++++++++----------
2 files changed, 11 insertions(+), 12 deletions(-)
diff --git a/history.md b/history.md
index a7d26a27a..d50fa10c1 100644
--- a/history.md
+++ b/history.md
@@ -6,10 +6,10 @@
### PR 코멘트 반영하기
-1. GetMapping 등에서 url 이 동일한 것에 대해서 통합표기 (@controller)
+1. ~~GetMapping 등에서 url 이 동일한 것에 대해서 통합표기 (@controller)~~
2. rest 도메인과 관련해서 엔드포인트 네이밍의 해석의 모호함
3. 문법 띄워쓰기 관련 google java style guide 참고하기 (내가 못하면 cmd + option + L) 을 매번 눌러주자
-4. pm 변수명 삭제 → productService로 변경 (처음에는 productManager 정도로 생각하고 임시로 작성하였다가 바꾸지 않았던 것이 문제가 됨)
+4. ~~pm 변수명 삭제 → productService로 변경 (처음에는 productManager 정도로 생각하고 임시로 작성하였다가 바꾸지 않았던 것이 문제가 됨)~~
5. 불필요한 주석 삭제 → 사용하지 않는 것들에 대해서 (가독성 떨어뜨림)
에 대해서 진행
diff --git a/src/main/java/gift/controller/ProductController.java b/src/main/java/gift/controller/ProductController.java
index 676a5003c..32c2fc934 100644
--- a/src/main/java/gift/controller/ProductController.java
+++ b/src/main/java/gift/controller/ProductController.java
@@ -3,7 +3,6 @@
import gift.dto.ProductDTO;
import gift.service.ProductService;
import java.util.List;
-import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
@@ -22,10 +21,10 @@
@RestController
public class ProductController {
- private final ProductService pm;
+ private final ProductService productService;
- public ProductController(ProductService pm) {
- this.pm = pm;
+ public ProductController(ProductService productService) {
+ this.productService = productService;
}
/**
@@ -35,7 +34,7 @@ public ProductController(ProductService pm) {
*/
@GetMapping("/products")
public List getList() {
- List dto = pm.readAll();
+ List dto = productService.readAll();
return dto;
}
@@ -46,7 +45,7 @@ public List getList() {
*/
@PostMapping("/products")
public void add(@RequestBody ProductDTO dto) {
- pm.create(dto);
+ productService.create(dto);
}
/**
@@ -65,18 +64,18 @@ public void update(@RequestParam("id") Long id, @RequestBody ProductDTO dto) {
@DeleteMapping("/products")
public void delete(@RequestParam("id") Long id) {
- pm.delete(id);
+ productService.delete(id);
}
private void changeCheckAndUpdate(Long id, ProductDTO dto) {
if (dto.getName() != null) {
- pm.updateName(id, dto.getName());
+ productService.updateName(id, dto.getName());
}
if (dto.getPrice() != null) {
- pm.updatePrice(id, dto.getPrice());
+ productService.updatePrice(id, dto.getPrice());
}
if (dto.getImageUrl() != null) {
- pm.updateImageUrl(id, dto.getImageUrl());
+ productService.updateImageUrl(id, dto.getImageUrl());
}
}
From 59b5741a53ee1324a55a6525b39236811ca24623 Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Thu, 4 Jul 2024 15:57:20 +0900
Subject: [PATCH 12/53] =?UTF-8?q?refact=20:=20=EB=AA=A8=ED=98=B8=ED=95=9C?=
=?UTF-8?q?=20=EB=B3=80=EC=88=98=20=EB=AA=85=20=EC=88=98=EC=A0=95,=20?=
=?UTF-8?q?=EA=B3=B5=ED=86=B5=20=EA=B2=BD=EB=A1=9C=20=EC=A0=95=EB=A6=AC+re?=
=?UTF-8?q?st=20=ED=95=98=EA=B2=8C=20=EA=B2=BD=EB=A1=9C=20=EC=84=A4?=
=?UTF-8?q?=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
관리자 페이지는 "/admin/products" 로 변경
---
.../gift/controller/AdminPageController.java | 34 +++++++++----------
1 file changed, 17 insertions(+), 17 deletions(-)
diff --git a/src/main/java/gift/controller/AdminPageController.java b/src/main/java/gift/controller/AdminPageController.java
index 7efe83bf7..bcf83978d 100644
--- a/src/main/java/gift/controller/AdminPageController.java
+++ b/src/main/java/gift/controller/AdminPageController.java
@@ -10,53 +10,53 @@
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
-import org.springframework.web.bind.annotation.RequestAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
+@RequestMapping("/admin/products")
@Controller
public class AdminPageController {
- private final ProductService pm;
+ private final ProductService productService;
- public AdminPageController(ProductService pm) {
- this.pm = pm;
+ public AdminPageController(ProductService productService) {
+ this.productService = productService;
}
- @GetMapping("/admin")
+ @GetMapping
public String adminPage(Model model) {
- model.addAttribute("products",pm.readAll());
+ model.addAttribute("products", productService.readAll());
model.addAttribute("productDTO",new ProductDTO());
return "admin/index";//렌더링하는 html 이름
}
- @PostMapping("/admin") //admin으로 오는 post에 대해서 submit
+ @PostMapping //admin으로 오는 post에 대해서 submit
public String adminPageSubmit(@ModelAttribute("productDTO") ProductDTO productDTO) {
- pm.create(productDTO); //서비스에 접근해서 해당 부분을 추가해주도록 한다.
- return "redirect:/admin";
+ productService.create(productDTO); //서비스에 접근해서 해당 부분을 추가해주도록 한다.
+ return "redirect:/admin/products";
}
- @PutMapping("/admin/{id}")
+ @PutMapping("/{id}")
public String adminPageUpdate(@PathVariable Long id,@ModelAttribute("productDTO") ProductDTO productDTO) {
changeCheckAndUpdate(id,productDTO);
- return "redirect:/admin";
+ return "redirect:/admin/products";
}
- @DeleteMapping("/admin/{id}")
+ @DeleteMapping("/{id}")
public String adminPageDelete(@PathVariable Long id) {
- pm.delete(id);
- return "redirect:/admin";
+ productService.delete(id);
+ return "redirect:/admin/products";
}
private void changeCheckAndUpdate(Long id, ProductDTO dto) {
if (dto.getName().length()>0){
- pm.updateName(id, dto.getName());
+ productService.updateName(id, dto.getName());
}
if (dto.getPrice()!=null){
- pm.updatePrice(id, dto.getPrice());
+ productService.updatePrice(id, dto.getPrice());
}
if (dto.getImageUrl().length()>0){
- pm.updateImageUrl(id, dto.getImageUrl());
+ productService.updateImageUrl(id, dto.getImageUrl());
}
}
}
From c1e4ceec773462eacb0ca20271b7595737b80c93 Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Thu, 4 Jul 2024 18:53:55 +0900
Subject: [PATCH 13/53] =?UTF-8?q?feat=20:=20product=20=EC=83=9D=EC=84=B1?=
=?UTF-8?q?=20=EC=8B=9C=20name=20=EA=B8=B8=EC=9D=B4=20Valid=20=EC=B6=94?=
=?UTF-8?q?=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
전역적으로 예외 처리를 위한 Advisor 추가
예외관련 내용을 담은 ExceptionResponseDTO 추가
---
history.md | 4 ++++
.../gift/controller/ProductController.java | 3 ++-
.../java/gift/dto/ExceptionResponseDTO.java | 14 +++++++++++
.../ProductExceptionAdvisor.java | 23 +++++++++++++++++++
4 files changed, 43 insertions(+), 1 deletion(-)
create mode 100644 src/main/java/gift/dto/ExceptionResponseDTO.java
create mode 100644 src/main/java/gift/exceptionAdvisor/ProductExceptionAdvisor.java
diff --git a/history.md b/history.md
index d50fa10c1..6da4f9a46 100644
--- a/history.md
+++ b/history.md
@@ -14,3 +14,7 @@
에 대해서 진행
+### valid 적용 테스트 코드 작성하기
+
+이름 길이가 15글자를 넘는 경우와 0글자인 경우에 대해 로직과 테스트 추가
+
diff --git a/src/main/java/gift/controller/ProductController.java b/src/main/java/gift/controller/ProductController.java
index 32c2fc934..ec3a4b16e 100644
--- a/src/main/java/gift/controller/ProductController.java
+++ b/src/main/java/gift/controller/ProductController.java
@@ -2,6 +2,7 @@
import gift.dto.ProductDTO;
import gift.service.ProductService;
+import jakarta.validation.Valid;
import java.util.List;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
@@ -44,7 +45,7 @@ public List getList() {
* @param dto id가 존재하는 상태로 입력되더라도 무시됨.
*/
@PostMapping("/products")
- public void add(@RequestBody ProductDTO dto) {
+ public void add(@RequestBody @Valid ProductDTO dto) {
productService.create(dto);
}
diff --git a/src/main/java/gift/dto/ExceptionResponseDTO.java b/src/main/java/gift/dto/ExceptionResponseDTO.java
new file mode 100644
index 000000000..57af8f978
--- /dev/null
+++ b/src/main/java/gift/dto/ExceptionResponseDTO.java
@@ -0,0 +1,14 @@
+package gift.dto;
+
+public class ExceptionResponseDTO {
+
+ private String message;
+
+ public ExceptionResponseDTO(String message) {
+ this.message = message;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+}
diff --git a/src/main/java/gift/exceptionAdvisor/ProductExceptionAdvisor.java b/src/main/java/gift/exceptionAdvisor/ProductExceptionAdvisor.java
new file mode 100644
index 000000000..e46302aa8
--- /dev/null
+++ b/src/main/java/gift/exceptionAdvisor/ProductExceptionAdvisor.java
@@ -0,0 +1,23 @@
+package gift.exceptionAdvisor;
+
+
+import gift.dto.ExceptionResponseDTO;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestController;
+
+@ControllerAdvice
+@RestController
+public class ProductExceptionAdvisor {
+
+ @ExceptionHandler(MethodArgumentNotValidException.class) //유효성 검사 실패 시
+ public ResponseEntity productValidationException(
+ MethodArgumentNotValidException ex) {
+ return new ResponseEntity<>(
+ new ExceptionResponseDTO(ex.getBindingResult().getFieldError().getDefaultMessage()),
+ HttpStatus.BAD_REQUEST);
+ }
+}
From 1c688634a9300cb6189d1fa608c865f4047bdc50 Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Thu, 4 Jul 2024 18:54:19 +0900
Subject: [PATCH 14/53] =?UTF-8?q?refact=20:=20NotNull=EA=B3=BC=20size=20?=
=?UTF-8?q?=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20=EC=A4=91?=
=?UTF-8?q?=EB=B3=B5=20=EC=88=98=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/main/java/gift/dto/ProductDTO.java | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/src/main/java/gift/dto/ProductDTO.java b/src/main/java/gift/dto/ProductDTO.java
index 5b53a726e..e34f9c937 100644
--- a/src/main/java/gift/dto/ProductDTO.java
+++ b/src/main/java/gift/dto/ProductDTO.java
@@ -8,12 +8,11 @@
public class ProductDTO {
private Long id;
- @Size(min=1,max=15)
- @NotNull
+ @Size(min = 1, max = 15, message = "상품 이름은 1~15글자로 제한됩니다.")
private String name;
- @NotNull
+ @NotNull(message = "가격을 입력해주세요")
private Integer price;
- @NotNull
+ @NotNull(message = "이미지 주소를 입력해주세요")
private String imageUrl;
//타임리프 사용을 위한 기본 생성자
From 9bed6ab600dbcbc7f95f7a4d1c3132bb930dfe11 Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Thu, 4 Jul 2024 18:54:50 +0900
Subject: [PATCH 15/53] =?UTF-8?q?feat=20:=20Product=20=EC=9D=B4=EB=A6=84?=
=?UTF-8?q?=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=9E=91?=
=?UTF-8?q?=EC=84=B1?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../gift/controller/ProductControllerTest.java | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)
diff --git a/src/test/java/gift/controller/ProductControllerTest.java b/src/test/java/gift/controller/ProductControllerTest.java
index 6f316cc18..48dc9db0b 100644
--- a/src/test/java/gift/controller/ProductControllerTest.java
+++ b/src/test/java/gift/controller/ProductControllerTest.java
@@ -41,7 +41,7 @@ void getProduct() {
@Test
- @DisplayName("name이 15글자가 넘는 경우")
+ @DisplayName("name이 15글자가 넘는 경우 실패")
void addMore15word() {
//given
String name = "123123123123123123123";
@@ -51,9 +51,23 @@ void addMore15word() {
//when then
createPostReqeust(dto).expectStatus().isBadRequest();
+
+ }
+
+ @Test
+ @DisplayName("name이 0글자인 경우 실패")
+ void addZero() {
+ //given
+ ProductDTO dto = getProductDTO("", 123, "test");
+
+ //when
+ createPostReqeust(dto).expectStatus().isBadRequest().expectBody()
+ .jsonPath("$.message").isEqualTo("상품 이름은 1~15글자로 제한됩니다.");
+
}
+
private ResponseSpec createPostReqeust(ProductDTO dto) {
return webClient.post().uri("/api/products").accept(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(dto)).exchange();
From dd205dcc86dc7a9a487495aa5cc9e17bc994065d Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Thu, 4 Jul 2024 19:11:37 +0900
Subject: [PATCH 16/53] =?UTF-8?q?refact=20:=20=EB=AC=B8=EC=9E=90=EC=97=B4?=
=?UTF-8?q?=EC=9D=B4=20=EB=B9=84=EC=96=B4=EC=9E=88=EB=8A=94=20=EA=B2=BD?=
=?UTF-8?q?=EC=9A=B0=EB=A5=BC=20=EC=9C=84=ED=95=B4=20=EC=96=B4=EB=85=B8?=
=?UTF-8?q?=ED=85=8C=EC=9D=B4=EC=85=98=20=EB=B3=80=EA=B2=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/main/java/gift/dto/ProductDTO.java | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/main/java/gift/dto/ProductDTO.java b/src/main/java/gift/dto/ProductDTO.java
index e34f9c937..7b2c4904d 100644
--- a/src/main/java/gift/dto/ProductDTO.java
+++ b/src/main/java/gift/dto/ProductDTO.java
@@ -1,6 +1,7 @@
package gift.dto;
import gift.model.Product;
+import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
@@ -12,7 +13,7 @@ public class ProductDTO {
private String name;
@NotNull(message = "가격을 입력해주세요")
private Integer price;
- @NotNull(message = "이미지 주소를 입력해주세요")
+ @NotBlank(message = "이미지 주소를 입력해주세요")
private String imageUrl;
//타임리프 사용을 위한 기본 생성자
From f9637f0dc7dbdec24c3fd57f0c3640cdfda48169 Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Thu, 4 Jul 2024 19:11:57 +0900
Subject: [PATCH 17/53] =?UTF-8?q?test=20:=20=EA=B0=80=EA=B2=A9=EA=B3=BC=20?=
=?UTF-8?q?=EC=9D=B4=EB=AF=B8=EC=A7=80=EA=B0=80=20=EB=B9=84=EC=96=B4?=
=?UTF-8?q?=EC=9E=88=EB=8A=94=20=EA=B2=BD=EC=9A=B0=20=ED=85=8C=EC=8A=A4?=
=?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../controller/ProductControllerTest.java | 33 ++++++++++++++++---
1 file changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/test/java/gift/controller/ProductControllerTest.java b/src/test/java/gift/controller/ProductControllerTest.java
index 48dc9db0b..b88221145 100644
--- a/src/test/java/gift/controller/ProductControllerTest.java
+++ b/src/test/java/gift/controller/ProductControllerTest.java
@@ -40,6 +40,10 @@ void getProduct() {
}
+ private static ProductDTO getProductDTO(String name, Integer price, String imageUrl) {
+ return new ProductDTO(null, name, price, imageUrl);
+ }
+
@Test
@DisplayName("name이 15글자가 넘는 경우 실패")
void addMore15word() {
@@ -50,7 +54,7 @@ void addMore15word() {
ProductDTO dto = getProductDTO(name, price, imageUrl);
//when then
- createPostReqeust(dto).expectStatus().isBadRequest();
+ createPostAndCheckBadRequest(dto, "상품 이름은 1~15글자로 제한됩니다.");
}
@@ -61,12 +65,29 @@ void addZero() {
ProductDTO dto = getProductDTO("", 123, "test");
//when
- createPostReqeust(dto).expectStatus().isBadRequest().expectBody()
- .jsonPath("$.message").isEqualTo("상품 이름은 1~15글자로 제한됩니다.");
+ createPostAndCheckBadRequest(dto, "상품 이름은 1~15글자로 제한됩니다.");
+
+ }
+
+ @Test
+ @DisplayName("price가 0인 경우 실패")
+ void priceZero() {
+
+ ProductDTO dto = getProductDTO("asd", null, "test");
+
+ createPostAndCheckBadRequest(dto, "가격을 입력해주세요");
}
+ //private function//
+ @Test
+ @DisplayName("imgUrl 이 없는 경우 실패")
+ void imgUrlNotInput() {
+ ProductDTO dto = getProductDTO("asd", 123, null);
+
+ createPostAndCheckBadRequest(dto, "이미지 주소를 입력해주세요");
+ }
private ResponseSpec createPostReqeust(ProductDTO dto) {
return webClient.post().uri("/api/products").accept(MediaType.APPLICATION_JSON)
@@ -75,7 +96,9 @@ private ResponseSpec createPostReqeust(ProductDTO dto) {
//request body 에는 BodyInserters.formValue로 객체 -> body 데이터로 변환
}
- private static ProductDTO getProductDTO(String name, int price, String imageUrl) {
- return new ProductDTO(null, name, price, imageUrl);
+ private void createPostAndCheckBadRequest(ProductDTO dto, String compareMsg) {
+ createPostReqeust(dto).expectStatus().isBadRequest().expectBody().jsonPath("$.message")
+ .isEqualTo(compareMsg);
}
+
}
\ No newline at end of file
From 8f3fa03c1aade54b47f4b12543cef4a33753bcd2 Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Thu, 4 Jul 2024 23:16:14 +0900
Subject: [PATCH 18/53] =?UTF-8?q?feat=20:=20=EC=83=81=ED=92=88=EC=9D=B4?=
=?UTF-8?q?=EB=A6=84=20=ED=8A=B9=EC=88=98=EB=AC=B8=EC=9E=90=20=ED=83=90?=
=?UTF-8?q?=EC=83=89=20Valid=20=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/main/java/gift/dto/ProductDTO.java | 3 +++
.../controller/ProductControllerTest.java | 21 +++++++++++++++++--
2 files changed, 22 insertions(+), 2 deletions(-)
diff --git a/src/main/java/gift/dto/ProductDTO.java b/src/main/java/gift/dto/ProductDTO.java
index 7b2c4904d..dd986ab95 100644
--- a/src/main/java/gift/dto/ProductDTO.java
+++ b/src/main/java/gift/dto/ProductDTO.java
@@ -3,6 +3,7 @@
import gift.model.Product;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
@@ -10,6 +11,8 @@ public class ProductDTO {
private Long id;
@Size(min = 1, max = 15, message = "상품 이름은 1~15글자로 제한됩니다.")
+ @Pattern(regexp = "^[ㄱ-ㅎ가-힣a-zA-Z0-9\\(\\)\\[\\]\\+\\-&/_]+$", message = "사용할 수 없는 특수문자입니다.")
+ //이 정규표현식을 만족해야지만 ok
private String name;
@NotNull(message = "가격을 입력해주세요")
private Integer price;
diff --git a/src/test/java/gift/controller/ProductControllerTest.java b/src/test/java/gift/controller/ProductControllerTest.java
index b88221145..8c2b1353b 100644
--- a/src/test/java/gift/controller/ProductControllerTest.java
+++ b/src/test/java/gift/controller/ProductControllerTest.java
@@ -79,8 +79,6 @@ void priceZero() {
}
- //private function//
-
@Test
@DisplayName("imgUrl 이 없는 경우 실패")
void imgUrlNotInput() {
@@ -89,6 +87,25 @@ void imgUrlNotInput() {
createPostAndCheckBadRequest(dto, "이미지 주소를 입력해주세요");
}
+ @Test
+ @DisplayName("사용할 수 없는 특수문자")
+ void nameCantUseWords() {
+ ProductDTO dto = getProductDTO("asd!", 123, "test");
+ ProductDTO dto2 = getProductDTO("asd?", 123, "test2");
+ ProductDTO dto3 = getProductDTO("&/_+-[]()", 123, "test3");
+ ProductDTO dto4 = getProductDTO("asdd", 123, "ttt");
+
+ createPostAndCheckBadRequest(dto, "사용할 수 없는 특수문자입니다.");
+ createPostAndCheckBadRequest(dto2, "사용할 수 없는 특수문자입니다.");
+
+ createPostReqeust(dto3).expectStatus().isOk();
+ createPostReqeust(dto4).expectStatus().isOk();
+ }
+
+
+ //private function//
+
+
private ResponseSpec createPostReqeust(ProductDTO dto) {
return webClient.post().uri("/api/products").accept(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(dto)).exchange();
From b11dde549f527c140d50cd9a56f699ab995ba9df Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Fri, 5 Jul 2024 06:29:31 +0900
Subject: [PATCH 19/53] =?UTF-8?q?feat=20:=20=EC=B9=B4=EC=B9=B4=EC=98=A4=20?=
=?UTF-8?q?=EC=82=AC=EC=9A=A9=20Valid=20=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../gift/controller/AdminPageController.java | 16 ++++++++------
.../gift/controller/ProductController.java | 2 +-
.../ProductExceptionAdvisor.java | 13 ++++++++---
.../ProductServiceException.java | 11 ++++++++++
.../java/gift/service/ProductServiceImpl.java | 22 ++++++++++++++-----
.../controller/ProductControllerTest.java | 7 ++++++
6 files changed, 54 insertions(+), 17 deletions(-)
create mode 100644 src/main/java/gift/exceptionAdvisor/ProductServiceException.java
diff --git a/src/main/java/gift/controller/AdminPageController.java b/src/main/java/gift/controller/AdminPageController.java
index bcf83978d..fbd52896a 100644
--- a/src/main/java/gift/controller/AdminPageController.java
+++ b/src/main/java/gift/controller/AdminPageController.java
@@ -2,6 +2,7 @@
import gift.dto.ProductDTO;
import gift.service.ProductService;
+import jakarta.validation.Valid;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.DeleteMapping;
@@ -25,19 +26,20 @@ public AdminPageController(ProductService productService) {
@GetMapping
public String adminPage(Model model) {
model.addAttribute("products", productService.readAll());
- model.addAttribute("productDTO",new ProductDTO());
+ model.addAttribute("productDTO", new ProductDTO());
return "admin/index";//렌더링하는 html 이름
}
@PostMapping //admin으로 오는 post에 대해서 submit
- public String adminPageSubmit(@ModelAttribute("productDTO") ProductDTO productDTO) {
+ public String adminPageSubmit(@ModelAttribute("productDTO") @Valid ProductDTO productDTO) {
productService.create(productDTO); //서비스에 접근해서 해당 부분을 추가해주도록 한다.
return "redirect:/admin/products";
}
@PutMapping("/{id}")
- public String adminPageUpdate(@PathVariable Long id,@ModelAttribute("productDTO") ProductDTO productDTO) {
- changeCheckAndUpdate(id,productDTO);
+ public String adminPageUpdate(@PathVariable Long id,
+ @ModelAttribute("productDTO") @Valid ProductDTO productDTO) {
+ changeCheckAndUpdate(id, productDTO);
return "redirect:/admin/products";
}
@@ -49,13 +51,13 @@ public String adminPageDelete(@PathVariable Long id) {
private void changeCheckAndUpdate(Long id, ProductDTO dto) {
- if (dto.getName().length()>0){
+ if (dto.getName().length() > 0) {
productService.updateName(id, dto.getName());
}
- if (dto.getPrice()!=null){
+ if (dto.getPrice() != null) {
productService.updatePrice(id, dto.getPrice());
}
- if (dto.getImageUrl().length()>0){
+ if (dto.getImageUrl().length() > 0) {
productService.updateImageUrl(id, dto.getImageUrl());
}
}
diff --git a/src/main/java/gift/controller/ProductController.java b/src/main/java/gift/controller/ProductController.java
index ec3a4b16e..c1dc523aa 100644
--- a/src/main/java/gift/controller/ProductController.java
+++ b/src/main/java/gift/controller/ProductController.java
@@ -56,7 +56,7 @@ public void add(@RequestBody @Valid ProductDTO dto) {
* @param dto 수정하고자 하는 값 이외 null로 지정
*/
@PutMapping("/products")
- public void update(@RequestParam("id") Long id, @RequestBody ProductDTO dto) {
+ public void update(@RequestParam("id") Long id, @RequestBody @Valid ProductDTO dto) {
if (id == null) {
throw new IllegalArgumentException("id를 입력해주세요");
}
diff --git a/src/main/java/gift/exceptionAdvisor/ProductExceptionAdvisor.java b/src/main/java/gift/exceptionAdvisor/ProductExceptionAdvisor.java
index e46302aa8..19fe26ea1 100644
--- a/src/main/java/gift/exceptionAdvisor/ProductExceptionAdvisor.java
+++ b/src/main/java/gift/exceptionAdvisor/ProductExceptionAdvisor.java
@@ -15,9 +15,16 @@ public class ProductExceptionAdvisor {
@ExceptionHandler(MethodArgumentNotValidException.class) //유효성 검사 실패 시
public ResponseEntity productValidationException(
- MethodArgumentNotValidException ex) {
- return new ResponseEntity<>(
- new ExceptionResponseDTO(ex.getBindingResult().getFieldError().getDefaultMessage()),
+ MethodArgumentNotValidException exception) {
+ return new ResponseEntity<>(new ExceptionResponseDTO(
+ exception.getBindingResult().getFieldError().getDefaultMessage()),
+ HttpStatus.BAD_REQUEST);
+ }
+
+ @ExceptionHandler(ProductServiceException.class)
+ public ResponseEntity productServiceException(
+ ProductServiceException exception) {
+ return new ResponseEntity<>(new ExceptionResponseDTO(exception.getMessage()),
HttpStatus.BAD_REQUEST);
}
}
diff --git a/src/main/java/gift/exceptionAdvisor/ProductServiceException.java b/src/main/java/gift/exceptionAdvisor/ProductServiceException.java
new file mode 100644
index 000000000..973978699
--- /dev/null
+++ b/src/main/java/gift/exceptionAdvisor/ProductServiceException.java
@@ -0,0 +1,11 @@
+package gift.exceptionAdvisor;
+
+public class ProductServiceException extends RuntimeException {
+
+ public ProductServiceException() {
+ }
+
+ public ProductServiceException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/gift/service/ProductServiceImpl.java b/src/main/java/gift/service/ProductServiceImpl.java
index 393bb4d14..2747dcf19 100644
--- a/src/main/java/gift/service/ProductServiceImpl.java
+++ b/src/main/java/gift/service/ProductServiceImpl.java
@@ -2,13 +2,14 @@
import gift.database.JdbcProductRepository;
import gift.dto.ProductDTO;
+import gift.exceptionAdvisor.ProductServiceException;
import gift.model.Product;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Service;
@Service
-public class ProductServiceImpl implements ProductService{
+public class ProductServiceImpl implements ProductService {
private final JdbcProductRepository jdbcProductRepository;
@@ -21,7 +22,7 @@ public List readAll() {
var products = jdbcProductRepository.findAllProducts();
List productDTOList = new ArrayList<>();
- for(var product : products) { //DTO로 전환
+ for (var product : products) { //DTO로 전환
productDTOList.add(new ProductDTO(product));
}
@@ -31,15 +32,18 @@ public List readAll() {
//새로운 상품 추가
@Override
public void create(ProductDTO prod) {
- jdbcProductRepository.insertProduct(new Product(null,prod.getName(),prod.getPrice(),prod.getImageUrl()));
+ checkKakao(prod.getName());
+ jdbcProductRepository.insertProduct(
+ new Product(null, prod.getName(), prod.getPrice(), prod.getImageUrl()));
}
@Override
public void updateName(long id, String name) {
var prod = jdbcProductRepository.getProduct(id);
+ checkKakao(prod.getName());
prod.setName(name);
- jdbcProductRepository.updateProduct(id,prod);
+ jdbcProductRepository.updateProduct(id, prod);
}
@@ -47,18 +51,24 @@ public void updateName(long id, String name) {
public void updatePrice(long id, int price) {
var prod = jdbcProductRepository.getProduct(id);
prod.setPrice(price);
- jdbcProductRepository.updateProduct(id,prod);
+ jdbcProductRepository.updateProduct(id, prod);
}
@Override
public void updateImageUrl(long id, String url) {
var prod = jdbcProductRepository.getProduct(id);
prod.setImageUrl(url);
- jdbcProductRepository.updateProduct(id,prod);
+ jdbcProductRepository.updateProduct(id, prod);
}
@Override
public void delete(long id) {
jdbcProductRepository.deleteProduct(id);
}
+
+ private void checkKakao(String productName) {
+ if (productName.contains("카카오")) {
+ throw new ProductServiceException("카카오 문구는 md협의 이후 사용할 수 있습니다.");
+ }
+ }
}
diff --git a/src/test/java/gift/controller/ProductControllerTest.java b/src/test/java/gift/controller/ProductControllerTest.java
index 8c2b1353b..0daf32819 100644
--- a/src/test/java/gift/controller/ProductControllerTest.java
+++ b/src/test/java/gift/controller/ProductControllerTest.java
@@ -102,6 +102,13 @@ void nameCantUseWords() {
createPostReqeust(dto4).expectStatus().isOk();
}
+ @Test
+ @DisplayName("카카오 사용하기")
+ void useKakao() {
+ ProductDTO dto = getProductDTO("나카카오콩따러간다", 123, "test");
+
+ createPostAndCheckBadRequest(dto, "카카오 문구는 md협의 이후 사용할 수 있습니다.");
+ }
//private function//
From 0a578c4dc4a251d5f70822912ab27cea15b19ceb Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Fri, 5 Jul 2024 06:30:18 +0900
Subject: [PATCH 20/53] =?UTF-8?q?docs=20:=20=EC=9E=91=EC=97=85=EC=9D=BC?=
=?UTF-8?q?=EC=A7=80=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 6 +++---
history.md | 6 ++++++
2 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/README.md b/README.md
index 5df55c753..dda1c2758 100644
--- a/README.md
+++ b/README.md
@@ -20,8 +20,8 @@
## milestone
-[X] 스프링 validation 의존성 추가
--[ ] feat : DTO valid 추가
+-[X] feat : DTO valid 추가
-[ ] refact : service - 상품 update 로직 변경 (하나로 통합)
--[ ] feat : @ControllerAdvice 클래스 추가
--[ ] feat : "카카오" 검사를 위한 예외클래스 추가
+-[X] feat : @ControllerAdvice 클래스 추가
+-[X] feat : "카카오" 검사를 위한 예외클래스 추가
diff --git a/history.md b/history.md
index 6da4f9a46..f97340763 100644
--- a/history.md
+++ b/history.md
@@ -18,3 +18,9 @@
이름 길이가 15글자를 넘는 경우와 0글자인 경우에 대해 로직과 테스트 추가
+### TODO
+
+예외에 대한 메세지가 너무 흩어져있어서 관리가 안되고 있다고 생각되어짐.
+한 군데로 모아서 예외에 대한 관리를 진행할 필요가 있다고 생각.
+예외 테스트코드에서 문자열 비교를 통해서 assert 하는 것은 너무 안좋은 방법이라고 생각되어짐.
+
From 8fb79979e835e3c5b4a5a83a2dfae88fbc959589 Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Fri, 5 Jul 2024 06:30:58 +0900
Subject: [PATCH 21/53] =?UTF-8?q?chore=20:=20=EA=B0=80=EB=8F=85=EC=84=B1?=
=?UTF-8?q?=EC=9D=84=20=EC=9C=84=ED=95=9C=20=EC=A0=95=EB=A0=AC?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../java/gift/dto/ExceptionResponseDTO.java | 2 --
src/main/java/gift/dto/ProductDTO.java | 21 ++++++++-----------
.../java/gift/service/ProductService.java | 11 +++++++---
3 files changed, 17 insertions(+), 17 deletions(-)
diff --git a/src/main/java/gift/dto/ExceptionResponseDTO.java b/src/main/java/gift/dto/ExceptionResponseDTO.java
index 57af8f978..423195e6a 100644
--- a/src/main/java/gift/dto/ExceptionResponseDTO.java
+++ b/src/main/java/gift/dto/ExceptionResponseDTO.java
@@ -1,13 +1,11 @@
package gift.dto;
public class ExceptionResponseDTO {
-
private String message;
public ExceptionResponseDTO(String message) {
this.message = message;
}
-
public String getMessage() {
return message;
}
diff --git a/src/main/java/gift/dto/ProductDTO.java b/src/main/java/gift/dto/ProductDTO.java
index dd986ab95..e6a672c2b 100644
--- a/src/main/java/gift/dto/ProductDTO.java
+++ b/src/main/java/gift/dto/ProductDTO.java
@@ -9,18 +9,19 @@
public class ProductDTO {
- private Long id;
- @Size(min = 1, max = 15, message = "상품 이름은 1~15글자로 제한됩니다.")
+ private Long id;
@Pattern(regexp = "^[ㄱ-ㅎ가-힣a-zA-Z0-9\\(\\)\\[\\]\\+\\-&/_]+$", message = "사용할 수 없는 특수문자입니다.")
+ @Size(min = 1, max = 15, message = "상품 이름은 1~15글자로 제한됩니다.")
//이 정규표현식을 만족해야지만 ok
- private String name;
+ private String name;
@NotNull(message = "가격을 입력해주세요")
- private Integer price;
+ private Integer price;
@NotBlank(message = "이미지 주소를 입력해주세요")
- private String imageUrl;
+ private String imageUrl;
//타임리프 사용을 위한 기본 생성자
- public ProductDTO() {}
+ public ProductDTO() {
+ }
public ProductDTO(Long id, String name, Integer price, String imageUrl) {
this.id = id;
@@ -40,12 +41,8 @@ public ProductDTO(Product product) {
//디버깅 필요 시 체크용 toString
@Override
public String toString() {
- return "ProductDTO{" +
- "id=" + id +
- ", name='" + name + '\'' +
- ", price=" + price +
- ", imageUrl='" + imageUrl + '\'' +
- '}';
+ return "ProductDTO{" + "id=" + id + ", name='" + name + '\'' + ", price=" + price
+ + ", imageUrl='" + imageUrl + '\'' + '}';
}
public Long getId() {
diff --git a/src/main/java/gift/service/ProductService.java b/src/main/java/gift/service/ProductService.java
index 68c5f3456..432c2bb7d 100644
--- a/src/main/java/gift/service/ProductService.java
+++ b/src/main/java/gift/service/ProductService.java
@@ -12,14 +12,19 @@ public interface ProductService {
//상품 리스트 전체 조회
List readAll();
+
//새 상품 생성
void create(ProductDTO prod);
+
//상품 이름 변경
- void updateName(long id,String name);
+ void updateName(long id, String name);
+
//상품 가격 변경
- void updatePrice(long id,int price);
+ void updatePrice(long id, int price);
+
//상품 이미지 변경
- void updateImageUrl(long id,String url);
+ void updateImageUrl(long id, String url);
+
//상품 삭제
void delete(long id);
From 1f1844aaa2a8761a411008a3b06029289a051534 Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Fri, 5 Jul 2024 15:29:11 +0900
Subject: [PATCH 22/53] =?UTF-8?q?docs=20:=20milestone=20=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
step2를 위해서
---
README.md | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/README.md b/README.md
index dda1c2758..bc3d3cc1b 100644
--- a/README.md
+++ b/README.md
@@ -25,3 +25,24 @@
-[X] feat : @ControllerAdvice 클래스 추가
-[X] feat : "카카오" 검사를 위한 예외클래스 추가
+## milestone 2
+
+회원 가입, 로그인, 추후 회원별 기능 이용을 위해
+
+회원은 이메일과 비밀번호를 입력하여 가입한다.
+토큰을 받으려면 이메일과 비밀번호를 보내야 하며, 가입한 이메일과 비밀번호가 일치하면 토큰이 발급된다.
+토큰은 JWT 를 사용하도록 한다.
+관리자는 회원을 조회 추가 수정 삭제 할 수 있다.
+
+- [ ] feat : 회원 모델 만들기
+- [ ] feat : 회원 Repository 만들기
+- [ ] feat : 회원 가입 서비스 만들기
+- [ ] feat : 회원 가입 컨트롤러 만들기
+- [ ] feat : 로그인 서비스 만들기
+- [ ] feat : 인증 서비스 만들기
+- [ ] feat : 인증 컨트롤러 만들기
+
+### 회원 모델 만들기
+
+Member.class : 회원모델
+MemberRole.class : 회원 등급 enum class
From a9c327bb70e4c4598402c2066577e167547df7b9 Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Fri, 5 Jul 2024 15:30:05 +0900
Subject: [PATCH 23/53] =?UTF-8?q?feat=20:=20=ED=9A=8C=EC=9B=90=20=EB=AA=A8?=
=?UTF-8?q?=EB=8D=B8=20+=20=ED=9A=8C=EC=9B=90=20=EB=93=B1=EA=B8=89=20?=
=?UTF-8?q?=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
history.md | 9 +++++++
src/main/java/gift/model/Member.java | 33 ++++++++++++++++++++++++
src/main/java/gift/model/MemberRole.java | 5 ++++
3 files changed, 47 insertions(+)
create mode 100644 src/main/java/gift/model/Member.java
create mode 100644 src/main/java/gift/model/MemberRole.java
diff --git a/history.md b/history.md
index f97340763..76ebf3189 100644
--- a/history.md
+++ b/history.md
@@ -24,3 +24,12 @@
한 군데로 모아서 예외에 대한 관리를 진행할 필요가 있다고 생각.
예외 테스트코드에서 문자열 비교를 통해서 assert 하는 것은 너무 안좋은 방법이라고 생각되어짐.
+## 7월 5일 (금)
+
+1. 회원가입 관련 구현
+2. 로그인 관련 구현
+
+궁금증 : 모델에서 null 허용이 되는 wrapper class를 사용하는 것이 좋은 방법일까? 나쁜 방법일까?
+
+-[ ] Product 에 setter가 필요한가?
+-
\ No newline at end of file
diff --git a/src/main/java/gift/model/Member.java b/src/main/java/gift/model/Member.java
new file mode 100644
index 000000000..9adff91a7
--- /dev/null
+++ b/src/main/java/gift/model/Member.java
@@ -0,0 +1,33 @@
+package gift.model;
+
+public class Member {
+
+ private Long id;
+ private String email;
+ private String password;
+ private MemberRole role;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+}
diff --git a/src/main/java/gift/model/MemberRole.java b/src/main/java/gift/model/MemberRole.java
new file mode 100644
index 000000000..2a555e5bc
--- /dev/null
+++ b/src/main/java/gift/model/MemberRole.java
@@ -0,0 +1,5 @@
+package gift.model;
+
+public enum MemberRole {
+ GUEST, COMMON_MEMBER, ADMIN_MEMBER
+}
From 9bdaa3d6c2d8a80dee3009d22d7a6f23acd09e41 Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Fri, 5 Jul 2024 17:34:02 +0900
Subject: [PATCH 24/53] =?UTF-8?q?build=20:=20JWT=20=EC=9D=98=EC=A1=B4?=
=?UTF-8?q?=EC=84=B1=20=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
build.gradle | 3 +++
1 file changed, 3 insertions(+)
diff --git a/build.gradle b/build.gradle
index f5f54ac12..e32fc3a28 100644
--- a/build.gradle
+++ b/build.gradle
@@ -27,6 +27,9 @@ dependencies {
runtimeOnly 'com.h2database:h2'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
+ compileOnly 'io.jsonwebtoken:jjwt-api:0.12.6'
+ runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.6'
+ runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.6'
}
tasks.named('test') {
From 7ec163396fcaf116d1f36757b8807a86c0e5e544 Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Sat, 6 Jul 2024 14:00:40 +0900
Subject: [PATCH 25/53] =?UTF-8?q?feat=20:=20memberRepository=20=EC=B6=94?=
=?UTF-8?q?=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../gift/database/JdbcMemeberRepository.java | 57 +++++++++++++++++++
1 file changed, 57 insertions(+)
create mode 100644 src/main/java/gift/database/JdbcMemeberRepository.java
diff --git a/src/main/java/gift/database/JdbcMemeberRepository.java b/src/main/java/gift/database/JdbcMemeberRepository.java
new file mode 100644
index 000000000..64af3f96e
--- /dev/null
+++ b/src/main/java/gift/database/JdbcMemeberRepository.java
@@ -0,0 +1,57 @@
+package gift.database;
+
+import gift.model.Member;
+import gift.model.MemberRole;
+import java.sql.PreparedStatement;
+import javax.sql.DataSource;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.jdbc.support.GeneratedKeyHolder;
+import org.springframework.jdbc.support.KeyHolder;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public class JdbcMemeberRepository {
+
+ private final JdbcTemplate template;
+
+ public JdbcMemeberRepository(DataSource dataSource) {
+ this.template = new JdbcTemplate(dataSource);
+ createTable();
+ }
+
+ private void createTable() {
+ template.update(
+ "create table if not exists member("
+ + "id long primary key auto_increment, "
+ + "email varchar(255) not null, "
+ + "password varchar(255) not null, "
+ + "role varchar(255) not null, "
+ + "token varchar(255))");
+ }
+
+ public Member findByEmail(String email) {
+ String sql = "select * from member where email = ?";
+ return template.queryForObject(sql, memberRowMapper(), email);
+ }
+
+ public void create(String email, String password, String role) {
+ String sql = "insert into member (email, password,role) values (?, ?, ?)";
+ KeyHolder keyHolder = new GeneratedKeyHolder();
+ template.update(connection -> {
+ PreparedStatement ps = connection.prepareStatement(sql, new String[]{"id"});
+ ps.setString(1, email);
+ ps.setString(2, password);
+ ps.setString(3, role);
+ return ps;
+ }, keyHolder);
+ }
+
+ private RowMapper memberRowMapper() {
+ return (rs, rowNum) -> new Member(
+ rs.getLong("id"),
+ rs.getString("email"),
+ rs.getString("password"),
+ MemberRole.valueOf(rs.getString("role")));
+ }
+}
From 99257350b0e327c08018ed47325d29fb4f3ec115 Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Sat, 6 Jul 2024 14:01:28 +0900
Subject: [PATCH 26/53] =?UTF-8?q?feat=20:=20memberService=20=EC=B6=94?=
=?UTF-8?q?=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/main/java/gift/dto/LoginToken.java | 48 ++++++++++++++
src/main/java/gift/dto/MemberDTO.java | 52 ++++++++++++++++
.../MemberServiceException.java | 21 +++++++
src/main/java/gift/model/Member.java | 15 +++++
src/main/java/gift/service/MemberService.java | 17 +++++
.../java/gift/service/MemberServiceImpl.java | 62 +++++++++++++++++++
6 files changed, 215 insertions(+)
create mode 100644 src/main/java/gift/dto/LoginToken.java
create mode 100644 src/main/java/gift/dto/MemberDTO.java
create mode 100644 src/main/java/gift/exceptionAdvisor/MemberServiceException.java
create mode 100644 src/main/java/gift/service/MemberService.java
create mode 100644 src/main/java/gift/service/MemberServiceImpl.java
diff --git a/src/main/java/gift/dto/LoginToken.java b/src/main/java/gift/dto/LoginToken.java
new file mode 100644
index 000000000..5e10d0557
--- /dev/null
+++ b/src/main/java/gift/dto/LoginToken.java
@@ -0,0 +1,48 @@
+package gift.dto;
+
+import gift.model.MemberRole;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.security.Keys;
+import java.nio.charset.StandardCharsets;
+import java.util.Objects;
+
+public class LoginToken {
+
+ private String token;
+ private String secretKey = "testword";
+
+ public LoginToken() {
+ }
+
+ public LoginToken(String email, MemberRole role) {
+ this.token = Jwts.builder()
+ .setSubject(email)
+ .claim("role", role.toString())
+ .signWith(Keys.hmacShaKeyFor(secretKey.getBytes(StandardCharsets.UTF_8)))
+ .compact();
+ }
+
+ public String getToken() {
+ return token;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ LoginToken that = (LoginToken) o;
+ return Objects.equals(token, that.token);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(token);
+ }
+
+
+}
diff --git a/src/main/java/gift/dto/MemberDTO.java b/src/main/java/gift/dto/MemberDTO.java
new file mode 100644
index 000000000..3f83e6e6f
--- /dev/null
+++ b/src/main/java/gift/dto/MemberDTO.java
@@ -0,0 +1,52 @@
+package gift.dto;
+
+import gift.model.MemberRole;
+
+public class MemberDTO {
+
+ private Long id;
+ private String email;
+ private String password;
+ private MemberRole role;
+
+ public MemberDTO(String email, String password, MemberRole role) {
+ this.email = email;
+ this.password = password;
+ this.role = role;
+ if (this.role == null) {
+ this.role = MemberRole.COMMON_MEMBER;
+ }
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public MemberRole getRole() {
+ return role;
+ }
+
+ public void setRole(MemberRole role) {
+ this.role = role;
+ }
+}
diff --git a/src/main/java/gift/exceptionAdvisor/MemberServiceException.java b/src/main/java/gift/exceptionAdvisor/MemberServiceException.java
new file mode 100644
index 000000000..660ff4314
--- /dev/null
+++ b/src/main/java/gift/exceptionAdvisor/MemberServiceException.java
@@ -0,0 +1,21 @@
+package gift.exceptionAdvisor;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.http.HttpStatusCode;
+
+public class MemberServiceException extends RuntimeException {
+
+ private HttpStatus responseStatus;
+
+ public MemberServiceException() {
+ }
+
+ public MemberServiceException(String message, HttpStatus responseStatus) {
+ super(message);
+ this.responseStatus = responseStatus;
+ }
+
+ public HttpStatusCode getStatusCode() {
+ return responseStatus;
+ }
+}
diff --git a/src/main/java/gift/model/Member.java b/src/main/java/gift/model/Member.java
index 9adff91a7..c475a3cff 100644
--- a/src/main/java/gift/model/Member.java
+++ b/src/main/java/gift/model/Member.java
@@ -7,6 +7,13 @@ public class Member {
private String password;
private MemberRole role;
+ public Member(Long id, String email, String password, MemberRole role) {
+ this.id = id;
+ this.email = email;
+ this.password = password;
+ this.role = role;
+ }
+
public Long getId() {
return id;
}
@@ -30,4 +37,12 @@ public String getPassword() {
public void setPassword(String password) {
this.password = password;
}
+
+ public MemberRole getRole() {
+ return role;
+ }
+
+ public void setRole(MemberRole role) {
+ this.role = role;
+ }
}
diff --git a/src/main/java/gift/service/MemberService.java b/src/main/java/gift/service/MemberService.java
new file mode 100644
index 000000000..811e075dc
--- /dev/null
+++ b/src/main/java/gift/service/MemberService.java
@@ -0,0 +1,17 @@
+package gift.service;
+
+import gift.dto.LoginToken;
+import gift.dto.MemberDTO;
+
+public interface MemberService {
+
+ //회원가입을 진행한다.
+ public void register(MemberDTO memberDTO);
+
+ //로그인을 진행한다.
+ public LoginToken login(MemberDTO memberDTO);
+
+ //회원의 접근 권한을 확인한다.
+ public boolean checkRole(LoginToken loginToken);
+
+}
diff --git a/src/main/java/gift/service/MemberServiceImpl.java b/src/main/java/gift/service/MemberServiceImpl.java
new file mode 100644
index 000000000..0f629b82b
--- /dev/null
+++ b/src/main/java/gift/service/MemberServiceImpl.java
@@ -0,0 +1,62 @@
+package gift.service;
+
+import gift.database.JdbcMemeberRepository;
+import gift.dto.LoginToken;
+import gift.dto.MemberDTO;
+import gift.exceptionAdvisor.MemberServiceException;
+import gift.model.Member;
+import gift.model.MemberRole;
+import org.springframework.dao.EmptyResultDataAccessException;
+import org.springframework.http.HttpStatus;
+import org.springframework.stereotype.Service;
+
+@Service
+public class MemberServiceImpl implements MemberService {
+
+ private JdbcMemeberRepository jdbcMemeberRepository;
+
+ public MemberServiceImpl(JdbcMemeberRepository jdbcMemeberRepository) {
+ this.jdbcMemeberRepository = jdbcMemeberRepository;
+ }
+
+ @Override
+ public void register(MemberDTO memberDTO) {
+ if (checkEmailDuplication(memberDTO.getEmail())) {
+ throw new MemberServiceException("이메일이 중복됩니다", HttpStatus.FORBIDDEN);
+ }
+
+ jdbcMemeberRepository.create(memberDTO.getEmail(), memberDTO.getPassword(),
+ MemberRole.COMMON_MEMBER.toString());
+ }
+
+ @Override
+ public LoginToken login(MemberDTO memberDTO) {
+ Member member = jdbcMemeberRepository.findByEmail(memberDTO.getEmail());
+
+ if (memberDTO.getPassword().equals(member.getPassword())) {
+ return new LoginToken(member.getEmail(), member.getRole());
+ }
+
+ throw new MemberServiceException("잘못된 로그인 시도입니다.", HttpStatus.FORBIDDEN);
+ }
+
+ @Override
+ public boolean checkRole(LoginToken loginToken) {
+ return false;
+ }
+
+ /**
+ * 이메일 중복 확인
+ *
+ * @param email
+ * @return 중복된 이메일인 경우 true 반환
+ */
+ private boolean checkEmailDuplication(String email) {
+ try {
+ jdbcMemeberRepository.findByEmail(email);
+ return true;
+ } catch (EmptyResultDataAccessException e) {
+ return false;
+ }
+ }
+}
From 5f4d556836ce34e793e1ecf96df7feb0e74b5d25 Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Sat, 6 Jul 2024 14:02:17 +0900
Subject: [PATCH 27/53] =?UTF-8?q?feat=20:=20memberController=20=EC=B6=94?=
=?UTF-8?q?=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../gift/controller/MemberController.java | 33 +++++++++++++++++++
1 file changed, 33 insertions(+)
create mode 100644 src/main/java/gift/controller/MemberController.java
diff --git a/src/main/java/gift/controller/MemberController.java b/src/main/java/gift/controller/MemberController.java
new file mode 100644
index 000000000..bb852c81f
--- /dev/null
+++ b/src/main/java/gift/controller/MemberController.java
@@ -0,0 +1,33 @@
+package gift.controller;
+
+import gift.dto.LoginToken;
+import gift.dto.MemberDTO;
+import gift.service.MemberService;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+@RequestMapping("/api/member")
+@RestController
+public class MemberController {
+
+ private final MemberService memberService;
+
+ public MemberController(MemberService memberService) {
+ this.memberService = memberService;
+ }
+
+ @PutMapping
+ public void register(@RequestBody MemberDTO memberDTO) {
+ memberService.register(memberDTO);
+ }
+
+ @GetMapping("/login")
+ public LoginToken login(@RequestParam("email") String email,
+ @RequestParam("password") String password) {
+ return memberService.login(new MemberDTO(email, password, null));
+ }
+}
From 45ce4a6b192479e09a7cb73ee4bd47de92d7a85f Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Sat, 6 Jul 2024 14:03:14 +0900
Subject: [PATCH 28/53] =?UTF-8?q?refact=20:=20productexception=20HTTP=20st?=
=?UTF-8?q?atus=20=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
예외Advisor에서 응답에 대한 httpstatus 없이 동작할 수 있도록
---
.../exceptionAdvisor/ProductExceptionAdvisor.java | 12 +++++++++---
.../exceptionAdvisor/ProductServiceException.java | 11 ++++++++++-
src/main/java/gift/service/ProductServiceImpl.java | 4 +++-
3 files changed, 22 insertions(+), 5 deletions(-)
diff --git a/src/main/java/gift/exceptionAdvisor/ProductExceptionAdvisor.java b/src/main/java/gift/exceptionAdvisor/ProductExceptionAdvisor.java
index 19fe26ea1..b5f9d74a2 100644
--- a/src/main/java/gift/exceptionAdvisor/ProductExceptionAdvisor.java
+++ b/src/main/java/gift/exceptionAdvisor/ProductExceptionAdvisor.java
@@ -2,7 +2,6 @@
import gift.dto.ExceptionResponseDTO;
-import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
@@ -18,13 +17,20 @@ public ResponseEntity productValidationException(
MethodArgumentNotValidException exception) {
return new ResponseEntity<>(new ExceptionResponseDTO(
exception.getBindingResult().getFieldError().getDefaultMessage()),
- HttpStatus.BAD_REQUEST);
+ exception.getStatusCode());
}
@ExceptionHandler(ProductServiceException.class)
public ResponseEntity productServiceException(
ProductServiceException exception) {
return new ResponseEntity<>(new ExceptionResponseDTO(exception.getMessage()),
- HttpStatus.BAD_REQUEST);
+ exception.getStatusCode());
+ }
+
+ @ExceptionHandler(MemberServiceException.class)
+ public ResponseEntity memberServiceException(
+ MemberServiceException exception) {
+ return new ResponseEntity<>(new ExceptionResponseDTO(exception.getMessage()),
+ exception.getStatusCode());
}
}
diff --git a/src/main/java/gift/exceptionAdvisor/ProductServiceException.java b/src/main/java/gift/exceptionAdvisor/ProductServiceException.java
index 973978699..46723054b 100644
--- a/src/main/java/gift/exceptionAdvisor/ProductServiceException.java
+++ b/src/main/java/gift/exceptionAdvisor/ProductServiceException.java
@@ -1,11 +1,20 @@
package gift.exceptionAdvisor;
+import org.springframework.http.HttpStatus;
+
public class ProductServiceException extends RuntimeException {
+ private HttpStatus responseStatus;
+
public ProductServiceException() {
}
- public ProductServiceException(String message) {
+ public ProductServiceException(String message, HttpStatus responseStatus) {
super(message);
+ this.responseStatus = responseStatus;
+ }
+
+ public HttpStatus getStatusCode() {
+ return responseStatus;
}
}
diff --git a/src/main/java/gift/service/ProductServiceImpl.java b/src/main/java/gift/service/ProductServiceImpl.java
index 2747dcf19..80a21c726 100644
--- a/src/main/java/gift/service/ProductServiceImpl.java
+++ b/src/main/java/gift/service/ProductServiceImpl.java
@@ -6,6 +6,7 @@
import gift.model.Product;
import java.util.ArrayList;
import java.util.List;
+import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
@Service
@@ -68,7 +69,8 @@ public void delete(long id) {
private void checkKakao(String productName) {
if (productName.contains("카카오")) {
- throw new ProductServiceException("카카오 문구는 md협의 이후 사용할 수 있습니다.");
+ throw new ProductServiceException("카카오 문구는 md협의 이후 사용할 수 있습니다.",
+ HttpStatus.BAD_REQUEST);
}
}
}
From 0caa2b39fbb6282e5b39bc341c5a8fb1cf00c9c5 Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Sat, 6 Jul 2024 14:03:58 +0900
Subject: [PATCH 29/53] =?UTF-8?q?test=20:=20memberController=20=ED=85=8C?=
=?UTF-8?q?=EC=8A=A4=ED=8A=B8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
회원 가입, 중복된 이메일 가입 시도
---
.../gift/controller/MemberControllerTest.java | 89 +++++++++++++++++++
1 file changed, 89 insertions(+)
create mode 100644 src/test/java/gift/controller/MemberControllerTest.java
diff --git a/src/test/java/gift/controller/MemberControllerTest.java b/src/test/java/gift/controller/MemberControllerTest.java
new file mode 100644
index 000000000..cbb872d88
--- /dev/null
+++ b/src/test/java/gift/controller/MemberControllerTest.java
@@ -0,0 +1,89 @@
+package gift.controller;
+
+import gift.dto.MemberDTO;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.web.server.LocalServerPort;
+import org.springframework.http.MediaType;
+import org.springframework.test.web.reactive.server.WebTestClient;
+import org.springframework.test.web.reactive.server.WebTestClient.ResponseSpec;
+import org.springframework.web.reactive.function.BodyInserters;
+
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+class MemberControllerTest {
+
+ @LocalServerPort
+ private int port;
+ private String baseUrl;
+ @Autowired
+ private WebTestClient webClient;
+
+ @BeforeEach
+ void setUp() {
+ baseUrl = "http://localhost:" + port;
+ webClient = WebTestClient.bindToServer().baseUrl(baseUrl).build();
+ }
+
+ @Test
+ @DisplayName("회원가입 성공")
+ void registerMember() {
+ //given
+ String email = "abcd@gmail.com";
+ String password = "abcd";
+ MemberDTO dto = new MemberDTO(email, password, null);
+
+ //when
+ ResponseSpec responseSpec = webClient.put().uri("/api/member")
+ .accept(MediaType.APPLICATION_JSON)
+ .body(BodyInserters.fromValue((dto))).exchange();
+
+ //then
+ responseSpec.expectStatus().isOk();
+ }
+
+ @Test
+ @DisplayName("중복된 이메일로 회원가입 시도")
+ void registerDuplicateEmail() {
+ //given
+ String email = "abcd@gmail.com";
+
+ MemberDTO dto = new MemberDTO(email, "1234", null);
+ MemberDTO dto2 = new MemberDTO(email, "4567", null);
+
+ //when
+ ResponseSpec responseSpec = webClient.put().uri("/api/member")
+ .accept(MediaType.APPLICATION_JSON)
+ .body(BodyInserters.fromValue((dto))).exchange();
+ ResponseSpec responseSpec2 = webClient.put().uri("/api/member")
+ .accept(MediaType.APPLICATION_JSON)
+ .body(BodyInserters.fromValue((dto2))).exchange();
+
+ responseSpec.expectStatus().isOk();
+ responseSpec2.expectStatus().isForbidden();
+ }
+ /*
+ @Test
+ @DisplayName("이메일을 입력하지 않은 경우")
+
+ @Test
+ @DisplayName("패스워드를 입력하지 않은 경우")
+
+ @Test
+ @DisplayName("로그인 성공")
+
+ @Test
+ @DisplayName("패스워드가 불일치한 경우")
+
+ @Test
+ @DisplayName("회원가입이 되지않은 이메일로 로그인을 시도하는 경우")
+
+ @Test
+ @DisplayName("로그인 성공으로 Token을 받아오기 성공")
+
+
+
+ */
+}
\ No newline at end of file
From 771d69a0822646b2ddc7fa89e84fed815ca9f5ea Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Sat, 6 Jul 2024 14:04:17 +0900
Subject: [PATCH 30/53] =?UTF-8?q?docs=20:=20=EB=AC=B8=EC=84=9C=20=EC=97=85?=
=?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=8A=B8,=20=EC=88=98=EC=A0=95=EC=82=AC?=
=?UTF-8?q?=ED=95=AD=20=EB=93=B1=EC=97=90=20=EB=8C=80=ED=95=B4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 5 +++++
history.md | 18 +++++++++++++++++-
2 files changed, 22 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index bc3d3cc1b..85584d6ee 100644
--- a/README.md
+++ b/README.md
@@ -46,3 +46,8 @@
Member.class : 회원모델
MemberRole.class : 회원 등급 enum class
+
+### 응답 코드
+
+헤더나 토큰이 유효하지 않은 경우 -> 401
+잘못된 로그인, 비밀번호 찾기, 비밀번호 변경 요청 -> 403
diff --git a/history.md b/history.md
index 76ebf3189..5e0cb95c7 100644
--- a/history.md
+++ b/history.md
@@ -32,4 +32,20 @@
궁금증 : 모델에서 null 허용이 되는 wrapper class를 사용하는 것이 좋은 방법일까? 나쁜 방법일까?
-[ ] Product 에 setter가 필요한가?
--
\ No newline at end of file
+
+readme 와 history와 notion 3개의 중복된 문서 작성 문제... 굳이 필요없는 내용을 많이 남긴다는 생각 자체가 잘못된 것 같기도?
+주말에 특정한 규칙을 정하기 필요
+
+-[ ] 문서정리규칙 정하기
+
+### 수정해야할 것
+
+1. 규격화된 테스트로 전체적인 동작에 문제가 없는지 확인하기
+2. 컨트롤러 - 서비스 - 모델 사이에서 명확하게 책임 정해서 작업하기
+3. JWT 공부해서 제대로 적용시키기 (비밀키 숨기기)
+4. 인증토큰에 대해서 서버에서 어떻게 인증토큰을 통해서 권한을 설정하는지 제대로 알고 적용시키기
+
+- [ ] 테스트코드를 위한 보일러플레이트 클래스를 만들어서 사용해도 되는가? - 좋은가? 나쁜가?
+
+- [ ] Repository는 정확히 어떤 책임을 지고 있는 것인지? 새로운 멤버를 등록하였다면 이걸 다시 Model로 반환해줘야하는가?
+
From 997d53cf280c3673858bfb0d1ad735b4e4e7233f Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Sat, 6 Jul 2024 14:34:01 +0900
Subject: [PATCH 31/53] =?UTF-8?q?test=20:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?=
=?UTF-8?q?=EC=84=B1=EA=B3=B5=20=ED=85=8C=EC=8A=A4=ED=8A=B8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../gift/controller/MemberControllerTest.java | 62 +++++++++++++++----
1 file changed, 50 insertions(+), 12 deletions(-)
diff --git a/src/test/java/gift/controller/MemberControllerTest.java b/src/test/java/gift/controller/MemberControllerTest.java
index cbb872d88..bc0e1ddc3 100644
--- a/src/test/java/gift/controller/MemberControllerTest.java
+++ b/src/test/java/gift/controller/MemberControllerTest.java
@@ -1,5 +1,6 @@
package gift.controller;
+import gift.dto.LoginToken;
import gift.dto.MemberDTO;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
@@ -36,9 +37,7 @@ void registerMember() {
MemberDTO dto = new MemberDTO(email, password, null);
//when
- ResponseSpec responseSpec = webClient.put().uri("/api/member")
- .accept(MediaType.APPLICATION_JSON)
- .body(BodyInserters.fromValue((dto))).exchange();
+ ResponseSpec responseSpec = registerMemberPutRequest(dto);
//then
responseSpec.expectStatus().isOk();
@@ -54,26 +53,61 @@ void registerDuplicateEmail() {
MemberDTO dto2 = new MemberDTO(email, "4567", null);
//when
- ResponseSpec responseSpec = webClient.put().uri("/api/member")
- .accept(MediaType.APPLICATION_JSON)
- .body(BodyInserters.fromValue((dto))).exchange();
- ResponseSpec responseSpec2 = webClient.put().uri("/api/member")
- .accept(MediaType.APPLICATION_JSON)
- .body(BodyInserters.fromValue((dto2))).exchange();
+ ResponseSpec responseSpec = registerMemberPutRequest(dto);
+ ResponseSpec responseSpec2 = registerMemberPutRequest(dto2);
responseSpec.expectStatus().isOk();
responseSpec2.expectStatus().isForbidden();
}
- /*
+
@Test
@DisplayName("이메일을 입력하지 않은 경우")
+ void nullEmail() {
+ //given
+ MemberDTO dto = new MemberDTO(null, "1234", null);
+
+ //when
+ ResponseSpec responseSpec = registerMemberPutRequest(dto);
+
+ //then
+ responseSpec.expectStatus().isBadRequest();
+ }
@Test
@DisplayName("패스워드를 입력하지 않은 경우")
+ void nullPassword() {
+ //given
+ MemberDTO dto = new MemberDTO("abcd@abcd", null, null);
+
+ //when
+ ResponseSpec responseSpec = registerMemberPutRequest(dto);
+
+ //then
+ responseSpec.expectStatus().isBadRequest();
+ }
+
@Test
@DisplayName("로그인 성공")
+ void login() {
+ //given
+ String email = "abcd@gmail.com";
+ String password = "abcd";
+ MemberDTO dto = new MemberDTO(email, password, null);
+ ResponseSpec responseSpec = registerMemberPutRequest(dto);
+
+ //when
+ ResponseSpec responseSeec = webClient.get().uri(uriBuilder -> uriBuilder
+ .path("/api/member/login")
+ .queryParam("email", dto.getEmail())
+ .queryParam("password", dto.getPassword())
+ .build()).accept(MediaType.APPLICATION_JSON).exchange();
+ //then
+ responseSeec.expectStatus().isOk();
+ responseSeec.expectBody(LoginToken.class);
+ }
+ /*
@Test
@DisplayName("패스워드가 불일치한 경우")
@@ -82,8 +116,12 @@ void registerDuplicateEmail() {
@Test
@DisplayName("로그인 성공으로 Token을 받아오기 성공")
+ */
+ private ResponseSpec registerMemberPutRequest(MemberDTO dto) {
+ ResponseSpec responseSpec = webClient.put().uri("/api/member")
+ .accept(MediaType.APPLICATION_JSON).body(BodyInserters.fromValue(dto)).exchange();
+ return responseSpec;
+ }
-
- */
}
\ No newline at end of file
From 9382b541c576e388ba617f120ad6f428fc00870b Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Sat, 6 Jul 2024 14:34:51 +0900
Subject: [PATCH 32/53] =?UTF-8?q?refact=20:=20JWT=20=EB=B3=B4=EC=95=88=20?=
=?UTF-8?q?=EB=AC=B8=EC=A0=9C=20=EC=88=98=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
시크릿키의 길이로 인한 암호화 과정 문제 수정
---
src/main/java/gift/dto/LoginToken.java | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/src/main/java/gift/dto/LoginToken.java b/src/main/java/gift/dto/LoginToken.java
index 5e10d0557..3413b33bf 100644
--- a/src/main/java/gift/dto/LoginToken.java
+++ b/src/main/java/gift/dto/LoginToken.java
@@ -2,14 +2,12 @@
import gift.model.MemberRole;
import io.jsonwebtoken.Jwts;
-import io.jsonwebtoken.security.Keys;
-import java.nio.charset.StandardCharsets;
+import io.jsonwebtoken.Jwts.SIG;
import java.util.Objects;
public class LoginToken {
private String token;
- private String secretKey = "testword";
public LoginToken() {
}
@@ -18,7 +16,7 @@ public LoginToken(String email, MemberRole role) {
this.token = Jwts.builder()
.setSubject(email)
.claim("role", role.toString())
- .signWith(Keys.hmacShaKeyFor(secretKey.getBytes(StandardCharsets.UTF_8)))
+ .signWith(SIG.HS256.key().build())
.compact();
}
From 3c1c77001980a329a0c5b45c3bc7493f8b68274d Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Sat, 6 Jul 2024 16:08:54 +0900
Subject: [PATCH 33/53] =?UTF-8?q?docs=20:=20step3=20=EB=AC=B8=EC=84=9C=20?=
=?UTF-8?q?=EC=83=9D=EC=84=B1?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 32 +++++++++++++++++++++++++-------
1 file changed, 25 insertions(+), 7 deletions(-)
diff --git a/README.md b/README.md
index 85584d6ee..d22a1b4b5 100644
--- a/README.md
+++ b/README.md
@@ -34,13 +34,13 @@
토큰은 JWT 를 사용하도록 한다.
관리자는 회원을 조회 추가 수정 삭제 할 수 있다.
-- [ ] feat : 회원 모델 만들기
-- [ ] feat : 회원 Repository 만들기
-- [ ] feat : 회원 가입 서비스 만들기
-- [ ] feat : 회원 가입 컨트롤러 만들기
-- [ ] feat : 로그인 서비스 만들기
-- [ ] feat : 인증 서비스 만들기
-- [ ] feat : 인증 컨트롤러 만들기
+- [X] feat : 회원 모델 만들기
+- [X] feat : 회원 Repository 만들기
+- [X] feat : 회원 가입 서비스 만들기
+- [X] feat : 회원 가입 컨트롤러 만들기
+- [X] feat : 로그인 서비스 만들기
+- [X] feat : 인증 서비스 만들기
+- [X] feat : 인증 컨트롤러 만들기
### 회원 모델 만들기
@@ -51,3 +51,21 @@ MemberRole.class : 회원 등급 enum class
헤더나 토큰이 유효하지 않은 경우 -> 401
잘못된 로그인, 비밀번호 찾기, 비밀번호 변경 요청 -> 403
+
+## milestone 3
+
+위시 리스트
+
+위시 리스트는 일종의 장바구니로 생각할 수 있다.
+위시 리스트에 등록된 상품 목록을 조회할 수 있다.
+위시 리스트에 상품을 추가할 수 있다.
+위시 리스트에 담긴 상품을 삭제할 수 있다.
+사용자 정보는 요청 헤더의 Authorization 필드를 사용한다.
+
+- [ ] feat : 위시 리스트 모델 만들기
+- [ ] feat : 위시리스트 Repository 만들기
+- [ ] feat : 위시리스트 서비스 만들기
+- [ ] feat : 위시리스트 컨트롤러 만들기
+- [ ] feat : 인증을 위한 ArgumentResolver 만들기
+- [ ] test : 테스트 코드로 동작 확인
+
From 953b831b8c67c08c254dbbc85cd340475398dad3 Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Sat, 6 Jul 2024 16:24:10 +0900
Subject: [PATCH 34/53] =?UTF-8?q?feat=20:=20=EC=9C=84=EC=8B=9C=EB=A6=AC?=
=?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EB=AA=A8=EB=8D=B8=20=EB=A7=8C=EB=93=A4?=
=?UTF-8?q?=EA=B8=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 2 +-
src/main/java/gift/model/WishList.java | 36 ++++++++++++++++++++++++++
2 files changed, 37 insertions(+), 1 deletion(-)
create mode 100644 src/main/java/gift/model/WishList.java
diff --git a/README.md b/README.md
index d22a1b4b5..554894654 100644
--- a/README.md
+++ b/README.md
@@ -62,7 +62,7 @@ MemberRole.class : 회원 등급 enum class
위시 리스트에 담긴 상품을 삭제할 수 있다.
사용자 정보는 요청 헤더의 Authorization 필드를 사용한다.
-- [ ] feat : 위시 리스트 모델 만들기
+- [X] feat : 위시 리스트 모델 만들기
- [ ] feat : 위시리스트 Repository 만들기
- [ ] feat : 위시리스트 서비스 만들기
- [ ] feat : 위시리스트 컨트롤러 만들기
diff --git a/src/main/java/gift/model/WishList.java b/src/main/java/gift/model/WishList.java
new file mode 100644
index 000000000..b2e3c88a8
--- /dev/null
+++ b/src/main/java/gift/model/WishList.java
@@ -0,0 +1,36 @@
+package gift.model;
+
+import java.util.Map;
+
+public class WishList {
+
+ private Long id;
+ private Long memberId;
+ private Map wishList;
+
+ public WishList(Long id, Long memberId, Map wishList) {
+ this.id = id;
+ this.memberId = memberId;
+ this.wishList = wishList;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public Long getMemberId() {
+ return memberId;
+ }
+
+ public Map getWishList() {
+ return wishList;
+ }
+
+ public void updateProduct(Long productId, Integer count) {
+ wishList.put(productId, count);
+ }
+
+ public void deleteProduct(Long productId) {
+ wishList.remove(productId);
+ }
+}
From 3b73a9cd51ed0c87e09d3c85fba28f35b7b1025a Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Sun, 7 Jul 2024 02:36:44 +0900
Subject: [PATCH 35/53] =?UTF-8?q?feat=20:=20=EC=9C=84=EC=8B=9C=EB=A6=AC?=
=?UTF-8?q?=EC=8A=A4=ED=8A=B8=20Repository=20=EB=A7=8C=EB=93=A4=EA=B8=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 2 +-
.../gift/database/JdbcWishListRepository.java | 86 +++++++++++++++++++
2 files changed, 87 insertions(+), 1 deletion(-)
create mode 100644 src/main/java/gift/database/JdbcWishListRepository.java
diff --git a/README.md b/README.md
index 554894654..3032f121a 100644
--- a/README.md
+++ b/README.md
@@ -63,7 +63,7 @@ MemberRole.class : 회원 등급 enum class
사용자 정보는 요청 헤더의 Authorization 필드를 사용한다.
- [X] feat : 위시 리스트 모델 만들기
-- [ ] feat : 위시리스트 Repository 만들기
+- [X] feat : 위시리스트 Repository 만들기
- [ ] feat : 위시리스트 서비스 만들기
- [ ] feat : 위시리스트 컨트롤러 만들기
- [ ] feat : 인증을 위한 ArgumentResolver 만들기
diff --git a/src/main/java/gift/database/JdbcWishListRepository.java b/src/main/java/gift/database/JdbcWishListRepository.java
new file mode 100644
index 000000000..6b2dbbfb7
--- /dev/null
+++ b/src/main/java/gift/database/JdbcWishListRepository.java
@@ -0,0 +1,86 @@
+package gift.database;
+
+import gift.model.WishList;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.List;
+import javax.sql.DataSource;
+import org.springframework.jdbc.core.BatchPreparedStatementSetter;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.jdbc.support.GeneratedKeyHolder;
+import org.springframework.jdbc.support.KeyHolder;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public class JdbcWishListRepository {
+
+ private final JdbcTemplate template;
+
+ public JdbcWishListRepository(DataSource dataSource) {
+ this.template = new JdbcTemplate(dataSource);
+ createTable();
+ }
+
+ private void createTable() {
+ template.update("create table if not exists wishlist("
+ + "id long primary key auto_increment, "
+ + "member_id long not null,"
+ + "product_id long not null,"
+ + "product_value int not null)");
+ }
+
+ public WishList findByMemeberId(Long memberId) {
+ String sql = "select * from wishlist where member_id = ?";
+ template.queryForObject(sql, wishListRowMapper());
+ }
+
+ public void insertWishList(WishList wishList) {
+ String sql = "insert into wishlist (member_id, product_id, product_value) values (?,?,?)";
+ KeyHolder keyHolder = new GeneratedKeyHolder();
+ List productIds = wishList.getWishList().keySet().stream().toList();
+
+ template.batchUpdate(sql, new BatchPreparedStatementSetter() {
+
+ @Override
+ public void setValues(PreparedStatement ps, int i) throws SQLException {
+
+ ps.setLong(1, wishList.getMemberId());
+ ps.setLong(2, productIds.get(i));
+ ps.setInt(3, wishList.getWishList().get(productIds.get(i)));
+ }
+
+ @Override
+ public int getBatchSize() {
+ return 100;
+ }
+ });
+ }
+
+ public void updateWishList(WishList wishList) {
+ String sql = "update wishlist set member_id = ?, product_id = ?,"
+ + " product_value = ? where member_id =?,product_id=?";
+ for (long productId : wishList.getWishList().keySet()) {
+ template.update(sql, wishList.getMemberId(), productId,
+ wishList.getWishList().get(productId));
+ }
+ }
+
+ public void deleteWishList(long memberId, long productId) {
+ String sql = "delete from wishlist where member_id = ? and product_id = ?";
+ template.update(sql, memberId, productId);
+ }
+
+ private RowMapper wishListRowMapper() {
+ return (rs, rowNum) -> {
+ WishList wishList = new WishList(
+ rs.getLong("id"),
+ rs.getLong("member_id"),
+ new HashMap<>()
+ );
+ wishList.updateProduct(rs.getLong("product_id"), rs.getInt("product_value"));
+ return wishList;
+ };
+ }
+}
From ab8035df08f60a122377454e52a4d25dd872db19 Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Sun, 7 Jul 2024 02:37:46 +0900
Subject: [PATCH 36/53] =?UTF-8?q?feat=20:=20=EC=9C=84=EC=8B=9C=EB=A6=AC?=
=?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=84=9C=EB=B9=84=EC=8A=A4=20=EC=B6=94?=
=?UTF-8?q?=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 2 +-
.../java/gift/service/WishListService.java | 15 +++++++
.../gift/service/WishListServiceImpl.java | 41 +++++++++++++++++++
3 files changed, 57 insertions(+), 1 deletion(-)
create mode 100644 src/main/java/gift/service/WishListService.java
create mode 100644 src/main/java/gift/service/WishListServiceImpl.java
diff --git a/README.md b/README.md
index 3032f121a..a9134df93 100644
--- a/README.md
+++ b/README.md
@@ -64,7 +64,7 @@ MemberRole.class : 회원 등급 enum class
- [X] feat : 위시 리스트 모델 만들기
- [X] feat : 위시리스트 Repository 만들기
-- [ ] feat : 위시리스트 서비스 만들기
+- [X] feat : 위시리스트 서비스 만들기
- [ ] feat : 위시리스트 컨트롤러 만들기
- [ ] feat : 인증을 위한 ArgumentResolver 만들기
- [ ] test : 테스트 코드로 동작 확인
diff --git a/src/main/java/gift/service/WishListService.java b/src/main/java/gift/service/WishListService.java
new file mode 100644
index 000000000..9bc5e0c70
--- /dev/null
+++ b/src/main/java/gift/service/WishListService.java
@@ -0,0 +1,15 @@
+package gift.service;
+
+import gift.dto.WishListDTO;
+
+public interface WishListService {
+
+ void addNewProduct(long memberId, long productId);
+
+ void deleteProduct(long memberId, long productId);
+
+ void updateProduct(long memberId, long productId, int productValue);
+
+ WishListDTO getWishList(long memberId);
+
+}
diff --git a/src/main/java/gift/service/WishListServiceImpl.java b/src/main/java/gift/service/WishListServiceImpl.java
new file mode 100644
index 000000000..a495905dd
--- /dev/null
+++ b/src/main/java/gift/service/WishListServiceImpl.java
@@ -0,0 +1,41 @@
+package gift.service;
+
+import gift.database.JdbcWishListRepository;
+import gift.dto.WishListDTO;
+import gift.model.WishList;
+import java.util.HashMap;
+import org.springframework.stereotype.Service;
+
+@Service
+public class WishListServiceImpl implements WishListService {
+
+ private JdbcWishListRepository jdbcWishListRepository;
+
+ public WishListServiceImpl(JdbcWishListRepository jdbcWishListRepository) {
+ this.jdbcWishListRepository = jdbcWishListRepository;
+ }
+
+ @Override
+ public void addNewProduct(long memberId, long productId) {
+ WishList wishList = new WishList(null, memberId, new HashMap<>());
+ wishList.updateProduct(productId, 1);
+ jdbcWishListRepository.insertWishList(wishList);
+ }
+
+ @Override
+ public void deleteProduct(long memberId, long productId) {
+ jdbcWishListRepository.deleteWishList(memberId, productId);
+ }
+
+ @Override
+ public void updateProduct(long memberId, long productId, int productValue) {
+ WishList wishList = jdbcWishListRepository.findByMemeberId(memberId);
+ wishList.updateProduct(productId, productValue);
+ jdbcWishListRepository.updateWishList(wishList);
+ }
+
+ @Override
+ public WishListDTO getWishList(long memberId) {
+ return null;
+ }
+}
From ac1b086706dcec4ccb6253e4da86e8414b8a0eb4 Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Sun, 7 Jul 2024 02:39:13 +0900
Subject: [PATCH 37/53] =?UTF-8?q?refact=20:=20LoginToken=20=EC=9D=B8?=
=?UTF-8?q?=EC=A6=9D=EC=9D=84=20=EC=9C=84=ED=95=B4=20=EC=9D=BC=EB=B6=80=20?=
=?UTF-8?q?=EC=88=98=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
JWT는 추후 구현 진행하도록 일부 수정
---
.../gift/ArgumentResolver/LoginMember.java | 12 +++++++
.../LoginMemberArgumentResolver.java | 34 +++++++++++++++++++
.../gift/controller/MemberController.java | 2 +-
.../java/gift/{dto => model}/LoginToken.java | 30 +++++++++++++---
src/main/java/gift/service/MemberService.java | 3 +-
.../java/gift/service/MemberServiceImpl.java | 16 +++++++--
.../gift/controller/MemberControllerTest.java | 2 +-
7 files changed, 89 insertions(+), 10 deletions(-)
create mode 100644 src/main/java/gift/ArgumentResolver/LoginMember.java
create mode 100644 src/main/java/gift/ArgumentResolver/LoginMemberArgumentResolver.java
rename src/main/java/gift/{dto => model}/LoginToken.java (54%)
diff --git a/src/main/java/gift/ArgumentResolver/LoginMember.java b/src/main/java/gift/ArgumentResolver/LoginMember.java
new file mode 100644
index 000000000..e010e9406
--- /dev/null
+++ b/src/main/java/gift/ArgumentResolver/LoginMember.java
@@ -0,0 +1,12 @@
+package gift.ArgumentResolver;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.PARAMETER)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface LoginMember {
+ //컨트롤러에서 사용을 위해
+}
diff --git a/src/main/java/gift/ArgumentResolver/LoginMemberArgumentResolver.java b/src/main/java/gift/ArgumentResolver/LoginMemberArgumentResolver.java
new file mode 100644
index 000000000..fffcf2aae
--- /dev/null
+++ b/src/main/java/gift/ArgumentResolver/LoginMemberArgumentResolver.java
@@ -0,0 +1,34 @@
+package gift.ArgumentResolver;
+
+import gift.service.MemberService;
+import jakarta.servlet.http.HttpServletRequest;
+import org.springframework.core.MethodParameter;
+import org.springframework.web.bind.support.WebDataBinderFactory;
+import org.springframework.web.context.request.NativeWebRequest;
+import org.springframework.web.method.support.HandlerMethodArgumentResolver;
+import org.springframework.web.method.support.ModelAndViewContainer;
+
+public class LoginMemberArgumentResolver implements HandlerMethodArgumentResolver {
+
+ private final MemberService memberService;
+
+ public LoginMemberArgumentResolver(MemberService memberService) {
+ this.memberService = memberService;
+ }
+
+ @Override
+ public boolean supportsParameter(MethodParameter parameter) {
+ return parameter.hasParameterAnnotation(LoginMember.class);
+ }
+
+ @Override
+ public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
+ NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
+ HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
+ String token = request.getHeader("Authorization");
+ if (token == null) {
+ return null;
+ }
+ return memberService.getLoginUser(token);
+ }
+}
diff --git a/src/main/java/gift/controller/MemberController.java b/src/main/java/gift/controller/MemberController.java
index bb852c81f..fbf7b85ed 100644
--- a/src/main/java/gift/controller/MemberController.java
+++ b/src/main/java/gift/controller/MemberController.java
@@ -1,7 +1,7 @@
package gift.controller;
-import gift.dto.LoginToken;
import gift.dto.MemberDTO;
+import gift.model.LoginToken;
import gift.service.MemberService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PutMapping;
diff --git a/src/main/java/gift/dto/LoginToken.java b/src/main/java/gift/model/LoginToken.java
similarity index 54%
rename from src/main/java/gift/dto/LoginToken.java
rename to src/main/java/gift/model/LoginToken.java
index 3413b33bf..84ab24956 100644
--- a/src/main/java/gift/dto/LoginToken.java
+++ b/src/main/java/gift/model/LoginToken.java
@@ -1,29 +1,49 @@
-package gift.dto;
+package gift.model;
-import gift.model.MemberRole;
-import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.Jwts.SIG;
+import java.security.Key;
import java.util.Objects;
public class LoginToken {
private String token;
+ private Key key = SIG.HS256.key().build();
+
+ private String email;
+ private String role;
+ private Long memberId;
+
public LoginToken() {
}
- public LoginToken(String email, MemberRole role) {
+ public LoginToken(Long memberId, String email, MemberRole role) {
+ this.token = email + ":" + role.toString();
+ /* JWT 학습 이후 구현하기
this.token = Jwts.builder()
.setSubject(email)
.claim("role", role.toString())
- .signWith(SIG.HS256.key().build())
+ .signWith(key)
.compact();
+
+ */
+ this.email = email;
+ this.role = role.toString();
}
public String getToken() {
return token;
}
+ public Long getUserId() {
+ if (token != null) {
+ return memberId;
+ }
+ //JwtParser parser = Jwts.parser().build();
+ return null;
+
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) {
diff --git a/src/main/java/gift/service/MemberService.java b/src/main/java/gift/service/MemberService.java
index 811e075dc..6a561d851 100644
--- a/src/main/java/gift/service/MemberService.java
+++ b/src/main/java/gift/service/MemberService.java
@@ -1,7 +1,7 @@
package gift.service;
-import gift.dto.LoginToken;
import gift.dto.MemberDTO;
+import gift.model.LoginToken;
public interface MemberService {
@@ -14,4 +14,5 @@ public interface MemberService {
//회원의 접근 권한을 확인한다.
public boolean checkRole(LoginToken loginToken);
+ MemberDTO getLoginUser(String token);
}
diff --git a/src/main/java/gift/service/MemberServiceImpl.java b/src/main/java/gift/service/MemberServiceImpl.java
index 0f629b82b..2d1253b88 100644
--- a/src/main/java/gift/service/MemberServiceImpl.java
+++ b/src/main/java/gift/service/MemberServiceImpl.java
@@ -1,9 +1,9 @@
package gift.service;
import gift.database.JdbcMemeberRepository;
-import gift.dto.LoginToken;
import gift.dto.MemberDTO;
import gift.exceptionAdvisor.MemberServiceException;
+import gift.model.LoginToken;
import gift.model.Member;
import gift.model.MemberRole;
import org.springframework.dao.EmptyResultDataAccessException;
@@ -34,7 +34,11 @@ public LoginToken login(MemberDTO memberDTO) {
Member member = jdbcMemeberRepository.findByEmail(memberDTO.getEmail());
if (memberDTO.getPassword().equals(member.getPassword())) {
- return new LoginToken(member.getEmail(), member.getRole());
+ LoginToken loginToken = new LoginToken(member.getId(), member.getEmail(),
+ member.getRole());
+ jdbcMemeberRepository.update(member.getEmail(), member.getPassword(),
+ member.getRole().toString(), loginToken.getToken());
+ return loginToken;
}
throw new MemberServiceException("잘못된 로그인 시도입니다.", HttpStatus.FORBIDDEN);
@@ -45,6 +49,14 @@ public boolean checkRole(LoginToken loginToken) {
return false;
}
+ @Override
+ public MemberDTO getLoginUser(String token) {
+ Member member = jdbcMemeberRepository.findByToken(token);
+
+ return new MemberDTO(member.getEmail(), null, member.getRole());
+ }
+
+
/**
* 이메일 중복 확인
*
diff --git a/src/test/java/gift/controller/MemberControllerTest.java b/src/test/java/gift/controller/MemberControllerTest.java
index bc0e1ddc3..b752ab422 100644
--- a/src/test/java/gift/controller/MemberControllerTest.java
+++ b/src/test/java/gift/controller/MemberControllerTest.java
@@ -1,7 +1,7 @@
package gift.controller;
-import gift.dto.LoginToken;
import gift.dto.MemberDTO;
+import gift.model.LoginToken;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
From 2c3d3506c098819b16ce9d19a40e110604d9877e Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Sun, 7 Jul 2024 12:51:07 +0900
Subject: [PATCH 38/53] =?UTF-8?q?refact=20:=20member=20Role=20=EC=88=98?=
=?UTF-8?q?=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Role에 대한 관리 및 검사 추가
---
src/main/java/gift/controller/MemberController.java | 4 ++++
.../java/gift/database/JdbcMemeberRepository.java | 12 ++++++++++++
2 files changed, 16 insertions(+)
diff --git a/src/main/java/gift/controller/MemberController.java b/src/main/java/gift/controller/MemberController.java
index fbf7b85ed..b5ea5a66a 100644
--- a/src/main/java/gift/controller/MemberController.java
+++ b/src/main/java/gift/controller/MemberController.java
@@ -2,6 +2,7 @@
import gift.dto.MemberDTO;
import gift.model.LoginToken;
+import gift.model.MemberRole;
import gift.service.MemberService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PutMapping;
@@ -22,6 +23,9 @@ public MemberController(MemberService memberService) {
@PutMapping
public void register(@RequestBody MemberDTO memberDTO) {
+ if (memberDTO.getRole() == null) {
+ memberDTO.setRole(MemberRole.COMMON_MEMBER);
+ }
memberService.register(memberDTO);
}
diff --git a/src/main/java/gift/database/JdbcMemeberRepository.java b/src/main/java/gift/database/JdbcMemeberRepository.java
index 64af3f96e..50f98305c 100644
--- a/src/main/java/gift/database/JdbcMemeberRepository.java
+++ b/src/main/java/gift/database/JdbcMemeberRepository.java
@@ -47,6 +47,16 @@ public void create(String email, String password, String role) {
}, keyHolder);
}
+ public Member findByToken(String token) {
+ String sql = "select * from member where token = ?";
+ return template.queryForObject(sql, memberRowMapper(), token);
+ }
+
+ public void update(String email, String password, String role, String token) {
+ String sql = "update member set password = ?, role = ?, token = ? where email = ?";
+ template.update(sql, password, role, token, email);
+ }
+
private RowMapper memberRowMapper() {
return (rs, rowNum) -> new Member(
rs.getLong("id"),
@@ -54,4 +64,6 @@ private RowMapper memberRowMapper() {
rs.getString("password"),
MemberRole.valueOf(rs.getString("role")));
}
+
+
}
From dd1d3394706fdbab020d10a6f40867676fefa2e4 Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Sun, 7 Jul 2024 12:51:56 +0900
Subject: [PATCH 39/53] =?UTF-8?q?refact=20:=20=EB=B3=80=EC=88=98=EB=AA=85?=
=?UTF-8?q?=20=EC=88=98=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
user -> member로 통일성 있는 사용
---
src/main/java/gift/model/LoginToken.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/main/java/gift/model/LoginToken.java b/src/main/java/gift/model/LoginToken.java
index 84ab24956..ee7dbcdb5 100644
--- a/src/main/java/gift/model/LoginToken.java
+++ b/src/main/java/gift/model/LoginToken.java
@@ -35,7 +35,7 @@ public String getToken() {
return token;
}
- public Long getUserId() {
+ public Long getMemberId() {
if (token != null) {
return memberId;
}
From 2b72758344e142e8e092e988cae0def7f704293e Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Sun, 7 Jul 2024 12:52:33 +0900
Subject: [PATCH 40/53] =?UTF-8?q?fix=20:=20=EB=B2=84=EA=B7=B8=20=EC=88=98?=
=?UTF-8?q?=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/main/java/gift/database/JdbcWishListRepository.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/main/java/gift/database/JdbcWishListRepository.java b/src/main/java/gift/database/JdbcWishListRepository.java
index 6b2dbbfb7..491e45ffe 100644
--- a/src/main/java/gift/database/JdbcWishListRepository.java
+++ b/src/main/java/gift/database/JdbcWishListRepository.java
@@ -33,7 +33,7 @@ private void createTable() {
public WishList findByMemeberId(Long memberId) {
String sql = "select * from wishlist where member_id = ?";
- template.queryForObject(sql, wishListRowMapper());
+ return template.queryForObject(sql, wishListRowMapper());
}
public void insertWishList(WishList wishList) {
From 98a90c22c1eeb7caa1e82e8b62fa184eee505a83 Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Sun, 7 Jul 2024 12:52:48 +0900
Subject: [PATCH 41/53] =?UTF-8?q?feat=20:=20wishlist=20controller=20?=
=?UTF-8?q?=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../gift/controller/WishListController.java | 51 ++++++++++++
src/main/java/gift/dto/WishListDTO.java | 32 ++++++++
.../controller/WishListControllerTest.java | 77 +++++++++++++++++++
3 files changed, 160 insertions(+)
create mode 100644 src/main/java/gift/controller/WishListController.java
create mode 100644 src/main/java/gift/dto/WishListDTO.java
create mode 100644 src/test/java/gift/controller/WishListControllerTest.java
diff --git a/src/main/java/gift/controller/WishListController.java b/src/main/java/gift/controller/WishListController.java
new file mode 100644
index 000000000..e7af5051f
--- /dev/null
+++ b/src/main/java/gift/controller/WishListController.java
@@ -0,0 +1,51 @@
+package gift.controller;
+
+import gift.ArgumentResolver.LoginMember;
+import gift.dto.MemberDTO;
+import gift.dto.WishListDTO;
+import gift.service.WishListService;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RequestMapping("api/member/{memberid}/wishlist")
+@RestController
+public class WishListController {
+
+ private final WishListService wishListService;
+
+ public WishListController(WishListService wishListService) {
+ this.wishListService = wishListService;
+ }
+
+ //상품 리스트 조회
+ @GetMapping
+ public WishListDTO getWishList(@PathVariable int memberid, @LoginMember MemberDTO memberDTO) {
+ return wishListService.getWishList(memberid);
+ }
+
+ //상품 추가
+ @PutMapping
+ public void addWishList(@PathVariable int memberid, @RequestBody WishListDTO wishListDTO) {
+ wishListService.addNewProduct(memberid, wishListDTO.getProductId());
+ }
+
+ //상품 삭제
+ @DeleteMapping
+ public void deleteWishList(@PathVariable int memberid, @RequestBody WishListDTO wishListDTO) {
+ wishListService.deleteProduct(memberid, wishListDTO.getProductId());
+ }
+
+ //상품 수정
+ @PutMapping
+ public void updateWishList(@PathVariable int memberid, @RequestBody WishListDTO wishListDTO) {
+ wishListService.updateProduct(memberid, wishListDTO.getProductId(),
+ wishListDTO.getProductValue());
+ }
+
+
+}
diff --git a/src/main/java/gift/dto/WishListDTO.java b/src/main/java/gift/dto/WishListDTO.java
new file mode 100644
index 000000000..32516d004
--- /dev/null
+++ b/src/main/java/gift/dto/WishListDTO.java
@@ -0,0 +1,32 @@
+package gift.dto;
+
+public class WishListDTO {
+
+ private Long memberId;
+ private Long productId;
+ private Integer productValue;
+
+ public Long getMemberId() {
+ return memberId;
+ }
+
+ public void setMemberId(Long memberId) {
+ this.memberId = memberId;
+ }
+
+ public Long getProductId() {
+ return productId;
+ }
+
+ public void setProductId(Long productId) {
+ this.productId = productId;
+ }
+
+ public Integer getProductValue() {
+ return productValue;
+ }
+
+ public void setProductValue(Integer productValue) {
+ this.productValue = productValue;
+ }
+}
diff --git a/src/test/java/gift/controller/WishListControllerTest.java b/src/test/java/gift/controller/WishListControllerTest.java
new file mode 100644
index 000000000..9c7a15aa3
--- /dev/null
+++ b/src/test/java/gift/controller/WishListControllerTest.java
@@ -0,0 +1,77 @@
+package gift.controller;
+
+import gift.dto.WishListDTO;
+import gift.model.LoginToken;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.web.server.LocalServerPort;
+import org.springframework.http.MediaType;
+import org.springframework.test.web.reactive.server.WebTestClient;
+import org.springframework.test.web.reactive.server.WebTestClient.ResponseSpec;
+import org.springframework.web.reactive.function.BodyInserters;
+
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+class WishListControllerTest {
+
+ @LocalServerPort
+ private int port;
+ private String baseUrl;
+ @Autowired
+ private WebTestClient webClient;
+
+ @BeforeEach
+ void setUp() {
+ baseUrl = "http://localhost:" + port;
+ webClient = WebTestClient.bindToServer().baseUrl(baseUrl).build();
+ }
+
+ @Test
+ @DisplayName("위시 리스트 추가")
+ void addWishList() {
+ //given
+ LoginToken loginToken = registerAndLogin("test", "123");
+
+ //when
+ addWishListPutRequest(loginToken);
+
+ //then
+
+ }
+
+
+ @Test
+ @DisplayName("위시 리스트 조회")
+ void getWishList() {
+
+ }
+
+ @Test
+ @DisplayName("위시 리스트 아이템 수량 변경")
+ void updateWishList() {
+
+ }
+
+ @Test
+ @DisplayName("위시 리스트 아이템 삭제")
+ void deleteWishList() {
+
+ }
+
+ private LoginToken registerAndLogin(String email, String password) {
+ return null;
+ }
+
+ private void addWishListPutRequest(long memberId, WishListDTO wishListDTO) {
+ ResponseSpec responseSpec = webClient.put().uri(uriBuilder -> {
+ return uriBuilder
+ .path("/api/member")
+ .queryParam("memberId", loginToken.getMemberId())
+ .path("/wishlist")
+ .build();
+ }).accept(MediaType.APPLICATION_JSON)
+ .body(BodyInserters.fromValue(wishListDTO)).exchange();
+ }
+}
\ No newline at end of file
From 098ac4479ae4751679bb1a106907e0ea039ed6e4 Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Sun, 7 Jul 2024 13:52:23 +0900
Subject: [PATCH 42/53] =?UTF-8?q?refact=20:=20repository=20=EA=B7=9C?=
=?UTF-8?q?=EA=B2=A9=20=ED=86=B5=EC=9D=BC?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
일관성 있는 규칙을 적용
---
.../gift/database/JdbcMemeberRepository.java | 30 +++++++++++++------
.../gift/database/JdbcProductRepository.java | 26 +++++++---------
.../java/gift/service/MemberServiceImpl.java | 4 +--
.../java/gift/service/ProductServiceImpl.java | 18 +++++------
4 files changed, 43 insertions(+), 35 deletions(-)
diff --git a/src/main/java/gift/database/JdbcMemeberRepository.java b/src/main/java/gift/database/JdbcMemeberRepository.java
index 50f98305c..f880524bf 100644
--- a/src/main/java/gift/database/JdbcMemeberRepository.java
+++ b/src/main/java/gift/database/JdbcMemeberRepository.java
@@ -30,10 +30,6 @@ private void createTable() {
+ "token varchar(255))");
}
- public Member findByEmail(String email) {
- String sql = "select * from member where email = ?";
- return template.queryForObject(sql, memberRowMapper(), email);
- }
public void create(String email, String password, String role) {
String sql = "insert into member (email, password,role) values (?, ?, ?)";
@@ -47,16 +43,32 @@ public void create(String email, String password, String role) {
}, keyHolder);
}
+ public void update(long id, Member member) {
+ String sql = "update member set email=?, password=?, role=?, token=? where id=?";
+ template.update(sql, member.getEmail(), member.getPassword(), member.getRole(),
+ member.getToken(), id);
+ }
+
+ public void delete(long id) {
+ String sql = "delete from member where id = ?";
+ template.update(sql, id);
+ }
+
+ public Member findById(long id) {
+ String sql = "select * from member where id = ?";
+ return template.queryForObject(sql, memberRowMapper(), id);
+ }
+
+ public Member findByEmail(String email) {
+ String sql = "select * from member where email = ?";
+ return template.queryForObject(sql, memberRowMapper(), email);
+ }
+
public Member findByToken(String token) {
String sql = "select * from member where token = ?";
return template.queryForObject(sql, memberRowMapper(), token);
}
- public void update(String email, String password, String role, String token) {
- String sql = "update member set password = ?, role = ?, token = ? where email = ?";
- template.update(sql, password, role, token, email);
- }
-
private RowMapper memberRowMapper() {
return (rs, rowNum) -> new Member(
rs.getLong("id"),
diff --git a/src/main/java/gift/database/JdbcProductRepository.java b/src/main/java/gift/database/JdbcProductRepository.java
index 10db1ac7b..c06c66a93 100644
--- a/src/main/java/gift/database/JdbcProductRepository.java
+++ b/src/main/java/gift/database/JdbcProductRepository.java
@@ -28,46 +28,42 @@ private void createTable() {
+ "imageUrl varchar(255))");
}
- /**
- * 새로운 상품 추가
- * @param product id 값은 무시됨.
- * @return DB에 저장된 id값이 포함된 객체 반환
- */
- public Product insertProduct(Product product) {
+
+ public Product create(String name, int price, String imageUrl) {
String sql = "insert into product (name, price, imageUrl) values (?,?,?)";
KeyHolder keyHolder = new GeneratedKeyHolder();
template.update(connection -> {
PreparedStatement ps = connection.prepareStatement(sql,new String[]{"id"});
- ps.setString(1, product.getName());
- ps.setInt(2, product.getPrice());
- ps.setString(3, product.getImageUrl());
+ ps.setString(1, name);
+ ps.setInt(2, price);
+ ps.setString(3, imageUrl);
return ps;
},keyHolder);
long key = keyHolder.getKey().longValue();
- product.setId(key);
- return product;
+
+ return new Product(key, name, price, imageUrl);
}
//기존 상품 수정
- public void updateProduct(Long id,Product product) {
+ public void update(long id, Product product) {
String sql = "update product set name = ?, price = ?, imageUrl = ? where id = ?";
template.update(sql,product.getName(),product.getPrice(),product.getImageUrl(),id);
}
//상품 단일 조회
- public Product getProduct(Long id) {
+ public Product findById(long id) {
String sql = "select * from product where id = ?";
return template.queryForObject(sql,productRowMapper(),id);
}
//상품 전체 조회
- public List findAllProducts() {
+ public List findAll() {
String sql = "select * from product";
return template.query(sql,productRowMapper());
}
//상품 삭제
- public void deleteProduct(Long id) {
+ public void delete(long id) {
String sql = "delete from product where id = ?";
template.update(sql,id);
}
diff --git a/src/main/java/gift/service/MemberServiceImpl.java b/src/main/java/gift/service/MemberServiceImpl.java
index 2d1253b88..cfd51801f 100644
--- a/src/main/java/gift/service/MemberServiceImpl.java
+++ b/src/main/java/gift/service/MemberServiceImpl.java
@@ -36,8 +36,8 @@ public LoginToken login(MemberDTO memberDTO) {
if (memberDTO.getPassword().equals(member.getPassword())) {
LoginToken loginToken = new LoginToken(member.getId(), member.getEmail(),
member.getRole());
- jdbcMemeberRepository.update(member.getEmail(), member.getPassword(),
- member.getRole().toString(), loginToken.getToken());
+ member.setToken(loginToken.getToken());
+ jdbcMemeberRepository.update(member.getId(), member);
return loginToken;
}
diff --git a/src/main/java/gift/service/ProductServiceImpl.java b/src/main/java/gift/service/ProductServiceImpl.java
index 80a21c726..a6facd256 100644
--- a/src/main/java/gift/service/ProductServiceImpl.java
+++ b/src/main/java/gift/service/ProductServiceImpl.java
@@ -20,7 +20,7 @@ public ProductServiceImpl(JdbcProductRepository jdbcProductRepository) {
@Override
public List readAll() {
- var products = jdbcProductRepository.findAllProducts();
+ var products = jdbcProductRepository.findAll();
List productDTOList = new ArrayList<>();
for (var product : products) { //DTO로 전환
@@ -34,37 +34,37 @@ public List readAll() {
@Override
public void create(ProductDTO prod) {
checkKakao(prod.getName());
- jdbcProductRepository.insertProduct(
+ jdbcProductRepository.create(
new Product(null, prod.getName(), prod.getPrice(), prod.getImageUrl()));
}
@Override
public void updateName(long id, String name) {
- var prod = jdbcProductRepository.getProduct(id);
+ var prod = jdbcProductRepository.findById(id);
checkKakao(prod.getName());
prod.setName(name);
- jdbcProductRepository.updateProduct(id, prod);
+ jdbcProductRepository.update(id, prod);
}
@Override
public void updatePrice(long id, int price) {
- var prod = jdbcProductRepository.getProduct(id);
+ var prod = jdbcProductRepository.findById(id);
prod.setPrice(price);
- jdbcProductRepository.updateProduct(id, prod);
+ jdbcProductRepository.update(id, prod);
}
@Override
public void updateImageUrl(long id, String url) {
- var prod = jdbcProductRepository.getProduct(id);
+ var prod = jdbcProductRepository.findById(id);
prod.setImageUrl(url);
- jdbcProductRepository.updateProduct(id, prod);
+ jdbcProductRepository.update(id, prod);
}
@Override
public void delete(long id) {
- jdbcProductRepository.deleteProduct(id);
+ jdbcProductRepository.delete(id);
}
private void checkKakao(String productName) {
From c9af84daa8e9c99013c91c970d0db102fcfac31c Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Sun, 7 Jul 2024 14:00:12 +0900
Subject: [PATCH 43/53] =?UTF-8?q?fix=20:=20Product=20=ED=85=8C=EC=8A=A4?=
=?UTF-8?q?=ED=8A=B8=20=ED=86=B5=EA=B3=BC?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
관련 오류 수정
---
history.md | 27 +++++++++++++
.../gift/controller/WishListController.java | 11 +++---
src/main/java/gift/model/Member.java | 9 +++++
.../java/gift/service/ProductServiceImpl.java | 8 ++--
.../controller/WishListControllerTest.java | 10 ++---
.../java/gift/service/MemberServiceTest.java | 39 +++++++++++++++++++
6 files changed, 88 insertions(+), 16 deletions(-)
create mode 100644 src/test/java/gift/service/MemberServiceTest.java
diff --git a/history.md b/history.md
index 5e0cb95c7..b9ef8aeb1 100644
--- a/history.md
+++ b/history.md
@@ -49,3 +49,30 @@ readme 와 history와 notion 3개의 중복된 문서 작성 문제... 굳이
- [ ] Repository는 정확히 어떤 책임을 지고 있는 것인지? 새로운 멤버를 등록하였다면 이걸 다시 Model로 반환해줘야하는가?
+- [ ] Repository는 무엇을 얼마만큼 제어해야할까?
+ 만약 Model의 내부 값이 변하면, 이것은 service에서 하나하나 분리해서 repository를 업데이트 해줘야하는가?
+
+## 로그인 인증 과정에서의 어려움
+
+상품과 멤버에 대해 컨트롤러와 서비스에서 규칙 없이 작성하였고, 위시 리스트 구현에서 어디서 무엇을 가져와서 어썬 식으로 사용해야할 지
+생각하기에 어려움이 너무 커졌다.
+
+# 7월 7일 refact 계획
+
+1. dto, 컨트롤, 서비스, repository에 대해 통일성 부여하기
+
+dto -> 데이터 transfer를 위해 존재
+dto -> 모델로 변환하는 로직은 있으면 좋겠다는 생각 (model은 dto를 몰라도 되도록)
+클라이언트 -> 컨트롤러 (dto사용) -> 서비스 (같은 dto 사용??)
+
+근데 서비스에서는 특정 필드의 값만 필요한 경우가 있다.
+서비스 내부에서는 항상 model로 변환해서? 서비스는 여러 모델들의 협업?
+
+### repository 들 먼저 구조 변경해주기
+
+실제 사용되는 String, int 등의 value를 직접 넘겨주도록 한다.
+repository는 결국 DB와 매우 큰 밀착관계이고, Model을 몰라도 된다?
+
+반환은 Model을 해준다?
+
+- [ ] Repository 통일성 부여 : 기본 검색은 id를 기반으로 한다. Model을 params로 받지 않는다. 반환은 Model로 해준다.
\ No newline at end of file
diff --git a/src/main/java/gift/controller/WishListController.java b/src/main/java/gift/controller/WishListController.java
index e7af5051f..02f1cd762 100644
--- a/src/main/java/gift/controller/WishListController.java
+++ b/src/main/java/gift/controller/WishListController.java
@@ -7,12 +7,13 @@
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
-@RequestMapping("api/member/{memberid}/wishlist")
+@RequestMapping("api/wishlist")
@RestController
public class WishListController {
@@ -23,25 +24,25 @@ public WishListController(WishListService wishListService) {
}
//상품 리스트 조회
- @GetMapping
+ @GetMapping("/{memberid}")
public WishListDTO getWishList(@PathVariable int memberid, @LoginMember MemberDTO memberDTO) {
return wishListService.getWishList(memberid);
}
//상품 추가
- @PutMapping
+ @PostMapping("/{memberid}")
public void addWishList(@PathVariable int memberid, @RequestBody WishListDTO wishListDTO) {
wishListService.addNewProduct(memberid, wishListDTO.getProductId());
}
//상품 삭제
- @DeleteMapping
+ @DeleteMapping("/{memberid}")
public void deleteWishList(@PathVariable int memberid, @RequestBody WishListDTO wishListDTO) {
wishListService.deleteProduct(memberid, wishListDTO.getProductId());
}
//상품 수정
- @PutMapping
+ @PutMapping("/{memberid}")
public void updateWishList(@PathVariable int memberid, @RequestBody WishListDTO wishListDTO) {
wishListService.updateProduct(memberid, wishListDTO.getProductId(),
wishListDTO.getProductValue());
diff --git a/src/main/java/gift/model/Member.java b/src/main/java/gift/model/Member.java
index c475a3cff..d19785e8d 100644
--- a/src/main/java/gift/model/Member.java
+++ b/src/main/java/gift/model/Member.java
@@ -6,6 +6,7 @@ public class Member {
private String email;
private String password;
private MemberRole role;
+ private String token;
public Member(Long id, String email, String password, MemberRole role) {
this.id = id;
@@ -45,4 +46,12 @@ public MemberRole getRole() {
public void setRole(MemberRole role) {
this.role = role;
}
+
+ public String getToken() {
+ return token;
+ }
+
+ public void setToken(String token) {
+ this.token = token;
+ }
}
diff --git a/src/main/java/gift/service/ProductServiceImpl.java b/src/main/java/gift/service/ProductServiceImpl.java
index a6facd256..84779255c 100644
--- a/src/main/java/gift/service/ProductServiceImpl.java
+++ b/src/main/java/gift/service/ProductServiceImpl.java
@@ -3,7 +3,6 @@
import gift.database.JdbcProductRepository;
import gift.dto.ProductDTO;
import gift.exceptionAdvisor.ProductServiceException;
-import gift.model.Product;
import java.util.ArrayList;
import java.util.List;
import org.springframework.http.HttpStatus;
@@ -32,10 +31,9 @@ public List readAll() {
//새로운 상품 추가
@Override
- public void create(ProductDTO prod) {
- checkKakao(prod.getName());
- jdbcProductRepository.create(
- new Product(null, prod.getName(), prod.getPrice(), prod.getImageUrl()));
+ public void create(ProductDTO dto) {
+ checkKakao(dto.getName());
+ jdbcProductRepository.create(dto.getName(), dto.getPrice(), dto.getImageUrl());
}
diff --git a/src/test/java/gift/controller/WishListControllerTest.java b/src/test/java/gift/controller/WishListControllerTest.java
index 9c7a15aa3..bf10633e8 100644
--- a/src/test/java/gift/controller/WishListControllerTest.java
+++ b/src/test/java/gift/controller/WishListControllerTest.java
@@ -1,6 +1,5 @@
package gift.controller;
-import gift.dto.WishListDTO;
import gift.model.LoginToken;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
@@ -8,10 +7,7 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalServerPort;
-import org.springframework.http.MediaType;
import org.springframework.test.web.reactive.server.WebTestClient;
-import org.springframework.test.web.reactive.server.WebTestClient.ResponseSpec;
-import org.springframework.web.reactive.function.BodyInserters;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class WishListControllerTest {
@@ -35,7 +31,7 @@ void addWishList() {
LoginToken loginToken = registerAndLogin("test", "123");
//when
- addWishListPutRequest(loginToken);
+ //addWishListPutRequest(loginToken);
//then
@@ -63,7 +59,7 @@ void deleteWishList() {
private LoginToken registerAndLogin(String email, String password) {
return null;
}
-
+ /*
private void addWishListPutRequest(long memberId, WishListDTO wishListDTO) {
ResponseSpec responseSpec = webClient.put().uri(uriBuilder -> {
return uriBuilder
@@ -74,4 +70,6 @@ private void addWishListPutRequest(long memberId, WishListDTO wishListDTO) {
}).accept(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(wishListDTO)).exchange();
}
+
+ */
}
\ No newline at end of file
diff --git a/src/test/java/gift/service/MemberServiceTest.java b/src/test/java/gift/service/MemberServiceTest.java
new file mode 100644
index 000000000..0de6d9460
--- /dev/null
+++ b/src/test/java/gift/service/MemberServiceTest.java
@@ -0,0 +1,39 @@
+package gift.service;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import gift.database.JdbcMemeberRepository;
+import gift.dto.MemberDTO;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+class MemberServiceTest {
+
+ @Autowired
+ private MemberService memberService;
+
+ @Autowired
+ private JdbcMemeberRepository jdbcMemeberRepository;
+
+ @Test
+ @DisplayName("기본 로그인 테스트")
+ void register() {
+
+ //given
+ //email과 password를 입력하면, 계정을 생성해준다.
+ //user role이 null 이면 common으로 설정한다.
+ String email = "testmail";
+ MemberDTO memberDTO = new MemberDTO(email, "abcd", null);
+
+ //when
+ memberService.register(memberDTO);
+
+ //then
+ assertNotNull(jdbcMemeberRepository.findByEmail(email));
+
+ }
+
+}
\ No newline at end of file
From 75ccd139e2739432280bdf4fa5fb534e99906c23 Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Sun, 7 Jul 2024 14:08:46 +0900
Subject: [PATCH 44/53] =?UTF-8?q?fix=20:=20Member=20=ED=85=8C=EC=8A=A4?=
=?UTF-8?q?=ED=8A=B8=20=ED=86=B5=EA=B3=BC?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
정상동작확인
---
history.md | 3 ++-
src/main/java/gift/controller/MemberController.java | 8 +++++---
src/main/java/gift/database/JdbcMemeberRepository.java | 2 +-
src/main/java/gift/dto/MemberDTO.java | 3 +++
src/test/java/gift/controller/MemberControllerTest.java | 1 -
5 files changed, 11 insertions(+), 6 deletions(-)
diff --git a/history.md b/history.md
index b9ef8aeb1..80f507efe 100644
--- a/history.md
+++ b/history.md
@@ -75,4 +75,5 @@ repository는 결국 DB와 매우 큰 밀착관계이고, Model을 몰라도 된
반환은 Model을 해준다?
-- [ ] Repository 통일성 부여 : 기본 검색은 id를 기반으로 한다. Model을 params로 받지 않는다. 반환은 Model로 해준다.
\ No newline at end of file
+- [X] Repository 통일성 부여 : 기본 검색은 id를 기반으로 한다. Model을 params로 받지 않는다. 반환은 Model로 해준다.
+
diff --git a/src/main/java/gift/controller/MemberController.java b/src/main/java/gift/controller/MemberController.java
index b5ea5a66a..da9f3d3e4 100644
--- a/src/main/java/gift/controller/MemberController.java
+++ b/src/main/java/gift/controller/MemberController.java
@@ -4,6 +4,8 @@
import gift.model.LoginToken;
import gift.model.MemberRole;
import gift.service.MemberService;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotBlank;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
@@ -22,7 +24,7 @@ public MemberController(MemberService memberService) {
}
@PutMapping
- public void register(@RequestBody MemberDTO memberDTO) {
+ public void register(@RequestBody @Valid MemberDTO memberDTO) {
if (memberDTO.getRole() == null) {
memberDTO.setRole(MemberRole.COMMON_MEMBER);
}
@@ -30,8 +32,8 @@ public void register(@RequestBody MemberDTO memberDTO) {
}
@GetMapping("/login")
- public LoginToken login(@RequestParam("email") String email,
- @RequestParam("password") String password) {
+ public LoginToken login(@RequestParam("email") @NotBlank String email,
+ @RequestParam("password") @NotBlank String password) {
return memberService.login(new MemberDTO(email, password, null));
}
}
diff --git a/src/main/java/gift/database/JdbcMemeberRepository.java b/src/main/java/gift/database/JdbcMemeberRepository.java
index f880524bf..207ff1435 100644
--- a/src/main/java/gift/database/JdbcMemeberRepository.java
+++ b/src/main/java/gift/database/JdbcMemeberRepository.java
@@ -45,7 +45,7 @@ public void create(String email, String password, String role) {
public void update(long id, Member member) {
String sql = "update member set email=?, password=?, role=?, token=? where id=?";
- template.update(sql, member.getEmail(), member.getPassword(), member.getRole(),
+ template.update(sql, member.getEmail(), member.getPassword(), member.getRole().toString(),
member.getToken(), id);
}
diff --git a/src/main/java/gift/dto/MemberDTO.java b/src/main/java/gift/dto/MemberDTO.java
index 3f83e6e6f..8d6673d10 100644
--- a/src/main/java/gift/dto/MemberDTO.java
+++ b/src/main/java/gift/dto/MemberDTO.java
@@ -1,11 +1,14 @@
package gift.dto;
import gift.model.MemberRole;
+import jakarta.validation.constraints.NotBlank;
public class MemberDTO {
private Long id;
+ @NotBlank(message = "이메일을 입력해주세요")
private String email;
+ @NotBlank(message = "비밀번호를 입력해주세요")
private String password;
private MemberRole role;
diff --git a/src/test/java/gift/controller/MemberControllerTest.java b/src/test/java/gift/controller/MemberControllerTest.java
index b752ab422..e54e0477c 100644
--- a/src/test/java/gift/controller/MemberControllerTest.java
+++ b/src/test/java/gift/controller/MemberControllerTest.java
@@ -56,7 +56,6 @@ void registerDuplicateEmail() {
ResponseSpec responseSpec = registerMemberPutRequest(dto);
ResponseSpec responseSpec2 = registerMemberPutRequest(dto2);
- responseSpec.expectStatus().isOk();
responseSpec2.expectStatus().isForbidden();
}
From b8f64546434ae6e4b826291b233d5ac4327be1be Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Sun, 7 Jul 2024 23:34:25 +0900
Subject: [PATCH 45/53] =?UTF-8?q?refact=20:=20=ED=81=B4=EB=9E=98=EC=8A=A4?=
=?UTF-8?q?=20=EC=9D=B4=EB=A6=84=20=EB=B3=80=EA=B2=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../dto/{ExceptionResponseDTO.java => ExceptionResponse.java} | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
rename src/main/java/gift/dto/{ExceptionResponseDTO.java => ExceptionResponse.java} (63%)
diff --git a/src/main/java/gift/dto/ExceptionResponseDTO.java b/src/main/java/gift/dto/ExceptionResponse.java
similarity index 63%
rename from src/main/java/gift/dto/ExceptionResponseDTO.java
rename to src/main/java/gift/dto/ExceptionResponse.java
index 423195e6a..1a9021905 100644
--- a/src/main/java/gift/dto/ExceptionResponseDTO.java
+++ b/src/main/java/gift/dto/ExceptionResponse.java
@@ -1,9 +1,9 @@
package gift.dto;
-public class ExceptionResponseDTO {
+public class ExceptionResponse {
private String message;
- public ExceptionResponseDTO(String message) {
+ public ExceptionResponse(String message) {
this.message = message;
}
public String getMessage() {
From f5787ddf40211c1e45c6737e516859a4ee1be5f2 Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Mon, 8 Jul 2024 17:22:36 +0900
Subject: [PATCH 46/53] =?UTF-8?q?refact=20:=20PR=20=EB=B0=98=EC=98=81=20?=
=?UTF-8?q?=EB=A6=AC=ED=8E=99=ED=86=A0=EB=A7=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
1. 불필요 주석 삭제
2. restful 한 api url 설정
3. camelCase 로 변경
4. 데이터베이스 unique 설정
5. 네이밍단순화 (addNew -> add)
---
history.md | 24 ++++++++++++++++++
.../gift/ArgumentResolver/LoginMember.java | 2 +-
.../gift/controller/AdminPageController.java | 6 ++---
.../gift/controller/ProductController.java | 10 ++++----
.../gift/controller/WishListController.java | 25 +++++++++----------
.../gift/database/JdbcMemeberRepository.java | 2 +-
.../ProductExceptionAdvisor.java | 14 +++++------
src/main/java/gift/service/MemberService.java | 9 +++----
.../java/gift/service/ProductService.java | 11 --------
.../java/gift/service/ProductServiceImpl.java | 9 ++-----
.../java/gift/service/WishListService.java | 2 +-
.../gift/service/WishListServiceImpl.java | 2 +-
12 files changed, 60 insertions(+), 56 deletions(-)
diff --git a/history.md b/history.md
index 80f507efe..2992dab22 100644
--- a/history.md
+++ b/history.md
@@ -77,3 +77,27 @@ repository는 결국 DB와 매우 큰 밀착관계이고, Model을 몰라도 된
- [X] Repository 통일성 부여 : 기본 검색은 id를 기반으로 한다. Model을 params로 받지 않는다. 반환은 Model로 해준다.
+# 7월 8일 (월)
+
+1. 피드백에 따라서 수정 진행
+2. wishlist 완벽하게 동작하도록 수정하기
+
+## 피드백 반영
+
+1. 공통적인 포멧이 있는 것은 좋지만, DTO는 용도에 맞게 사용하는 것이 좋다. 불필요한 데이터까지 내줄 필요 없음
+
+2. 문서는 코드 자체로 잘 이해할 수 있도록 해두는 것이 좋다. -> 클린코드
+
+3. 불필요한 축약어 사용 X, stream 적극적인 활용 -> ok
+
+4. 파일 전반적인 설명 주석은 상단에, 그리고 주석이 필요하다면 자세하게 -> ok
+
+5. rest 규격에 맞게 경로 설계하기 -> {id} 등으로 받기 -> ok
+
+6. 변수명 camelCase 로 -> ok
+
+7. 인증 토큰은 저장할 필요가 있는가?
+
+8. 중복된 이메일 -> 데이터베이스에서도 막을 수 있도록 유니크 설정시켜주기 -> ok
+
+9. addNew 와 같이 중복된 네이밍 X
diff --git a/src/main/java/gift/ArgumentResolver/LoginMember.java b/src/main/java/gift/ArgumentResolver/LoginMember.java
index e010e9406..5b82d0dcd 100644
--- a/src/main/java/gift/ArgumentResolver/LoginMember.java
+++ b/src/main/java/gift/ArgumentResolver/LoginMember.java
@@ -8,5 +8,5 @@
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginMember {
- //컨트롤러에서 사용을 위해
+
}
diff --git a/src/main/java/gift/controller/AdminPageController.java b/src/main/java/gift/controller/AdminPageController.java
index fbd52896a..09b4d1cb1 100644
--- a/src/main/java/gift/controller/AdminPageController.java
+++ b/src/main/java/gift/controller/AdminPageController.java
@@ -27,12 +27,12 @@ public AdminPageController(ProductService productService) {
public String adminPage(Model model) {
model.addAttribute("products", productService.readAll());
model.addAttribute("productDTO", new ProductDTO());
- return "admin/index";//렌더링하는 html 이름
+ return "admin/index";
}
- @PostMapping //admin으로 오는 post에 대해서 submit
+ @PostMapping
public String adminPageSubmit(@ModelAttribute("productDTO") @Valid ProductDTO productDTO) {
- productService.create(productDTO); //서비스에 접근해서 해당 부분을 추가해주도록 한다.
+ productService.create(productDTO);
return "redirect:/admin/products";
}
diff --git a/src/main/java/gift/controller/ProductController.java b/src/main/java/gift/controller/ProductController.java
index c1dc523aa..720c3c4b8 100644
--- a/src/main/java/gift/controller/ProductController.java
+++ b/src/main/java/gift/controller/ProductController.java
@@ -6,11 +6,11 @@
import java.util.List;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
@@ -55,16 +55,16 @@ public void add(@RequestBody @Valid ProductDTO dto) {
* @param id 수정하고자 하는 상품의 id
* @param dto 수정하고자 하는 값 이외 null로 지정
*/
- @PutMapping("/products")
- public void update(@RequestParam("id") Long id, @RequestBody @Valid ProductDTO dto) {
+ @PutMapping("/products/{id}")
+ public void update(@PathVariable Long id, @RequestBody @Valid ProductDTO dto) {
if (id == null) {
throw new IllegalArgumentException("id를 입력해주세요");
}
changeCheckAndUpdate(id, dto);
}
- @DeleteMapping("/products")
- public void delete(@RequestParam("id") Long id) {
+ @DeleteMapping("/products/{id}")
+ public void delete(@PathVariable Long id) {
productService.delete(id);
}
diff --git a/src/main/java/gift/controller/WishListController.java b/src/main/java/gift/controller/WishListController.java
index 02f1cd762..95dd11229 100644
--- a/src/main/java/gift/controller/WishListController.java
+++ b/src/main/java/gift/controller/WishListController.java
@@ -23,28 +23,27 @@ public WishListController(WishListService wishListService) {
this.wishListService = wishListService;
}
- //상품 리스트 조회
- @GetMapping("/{memberid}")
- public WishListDTO getWishList(@PathVariable int memberid, @LoginMember MemberDTO memberDTO) {
- return wishListService.getWishList(memberid);
+ @GetMapping("/{memberId}")
+ public WishListDTO getWishList(@PathVariable int memberId, @LoginMember MemberDTO memberDTO) {
+ return wishListService.getWishList(memberId);
}
//상품 추가
- @PostMapping("/{memberid}")
- public void addWishList(@PathVariable int memberid, @RequestBody WishListDTO wishListDTO) {
- wishListService.addNewProduct(memberid, wishListDTO.getProductId());
+ @PostMapping("/{memberId}")
+ public void addWishList(@PathVariable int memberId, @RequestBody WishListDTO wishListDTO) {
+ wishListService.addProduct(memberId, wishListDTO.getProductId());
}
//상품 삭제
- @DeleteMapping("/{memberid}")
- public void deleteWishList(@PathVariable int memberid, @RequestBody WishListDTO wishListDTO) {
- wishListService.deleteProduct(memberid, wishListDTO.getProductId());
+ @DeleteMapping("/{memberId}")
+ public void deleteWishList(@PathVariable int memberId, @RequestBody WishListDTO wishListDTO) {
+ wishListService.deleteProduct(memberId, wishListDTO.getProductId());
}
//상품 수정
- @PutMapping("/{memberid}")
- public void updateWishList(@PathVariable int memberid, @RequestBody WishListDTO wishListDTO) {
- wishListService.updateProduct(memberid, wishListDTO.getProductId(),
+ @PutMapping("/{memberId}")
+ public void updateWishList(@PathVariable int memberId, @RequestBody WishListDTO wishListDTO) {
+ wishListService.updateProduct(memberId, wishListDTO.getProductId(),
wishListDTO.getProductValue());
}
diff --git a/src/main/java/gift/database/JdbcMemeberRepository.java b/src/main/java/gift/database/JdbcMemeberRepository.java
index 207ff1435..67f6d7a25 100644
--- a/src/main/java/gift/database/JdbcMemeberRepository.java
+++ b/src/main/java/gift/database/JdbcMemeberRepository.java
@@ -24,7 +24,7 @@ private void createTable() {
template.update(
"create table if not exists member("
+ "id long primary key auto_increment, "
- + "email varchar(255) not null, "
+ + "email varchar(255) unique not null, "
+ "password varchar(255) not null, "
+ "role varchar(255) not null, "
+ "token varchar(255))");
diff --git a/src/main/java/gift/exceptionAdvisor/ProductExceptionAdvisor.java b/src/main/java/gift/exceptionAdvisor/ProductExceptionAdvisor.java
index b5f9d74a2..907210c24 100644
--- a/src/main/java/gift/exceptionAdvisor/ProductExceptionAdvisor.java
+++ b/src/main/java/gift/exceptionAdvisor/ProductExceptionAdvisor.java
@@ -1,7 +1,7 @@
package gift.exceptionAdvisor;
-import gift.dto.ExceptionResponseDTO;
+import gift.dto.ExceptionResponse;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
@@ -13,24 +13,24 @@
public class ProductExceptionAdvisor {
@ExceptionHandler(MethodArgumentNotValidException.class) //유효성 검사 실패 시
- public ResponseEntity productValidationException(
+ public ResponseEntity productValidationException(
MethodArgumentNotValidException exception) {
- return new ResponseEntity<>(new ExceptionResponseDTO(
+ return new ResponseEntity<>(new ExceptionResponse(
exception.getBindingResult().getFieldError().getDefaultMessage()),
exception.getStatusCode());
}
@ExceptionHandler(ProductServiceException.class)
- public ResponseEntity productServiceException(
+ public ResponseEntity productServiceException(
ProductServiceException exception) {
- return new ResponseEntity<>(new ExceptionResponseDTO(exception.getMessage()),
+ return new ResponseEntity<>(new ExceptionResponse(exception.getMessage()),
exception.getStatusCode());
}
@ExceptionHandler(MemberServiceException.class)
- public ResponseEntity memberServiceException(
+ public ResponseEntity memberServiceException(
MemberServiceException exception) {
- return new ResponseEntity<>(new ExceptionResponseDTO(exception.getMessage()),
+ return new ResponseEntity<>(new ExceptionResponse(exception.getMessage()),
exception.getStatusCode());
}
}
diff --git a/src/main/java/gift/service/MemberService.java b/src/main/java/gift/service/MemberService.java
index 6a561d851..64ae30e9e 100644
--- a/src/main/java/gift/service/MemberService.java
+++ b/src/main/java/gift/service/MemberService.java
@@ -5,14 +5,11 @@
public interface MemberService {
- //회원가입을 진행한다.
- public void register(MemberDTO memberDTO);
+ void register(MemberDTO memberDTO);
- //로그인을 진행한다.
- public LoginToken login(MemberDTO memberDTO);
+ LoginToken login(MemberDTO memberDTO);
- //회원의 접근 권한을 확인한다.
- public boolean checkRole(LoginToken loginToken);
+ boolean checkRole(LoginToken loginToken);
MemberDTO getLoginUser(String token);
}
diff --git a/src/main/java/gift/service/ProductService.java b/src/main/java/gift/service/ProductService.java
index 432c2bb7d..63c5409b6 100644
--- a/src/main/java/gift/service/ProductService.java
+++ b/src/main/java/gift/service/ProductService.java
@@ -5,27 +5,16 @@
public interface ProductService {
- /*
- 상품 수정 시 만약 문제가 생기면 어떤 값에 의해서 생기는 지 파악의 어려움을 막기 위해
- update를 각 항목마다 분리
- */
-
- //상품 리스트 전체 조회
List readAll();
- //새 상품 생성
void create(ProductDTO prod);
- //상품 이름 변경
void updateName(long id, String name);
- //상품 가격 변경
void updatePrice(long id, int price);
- //상품 이미지 변경
void updateImageUrl(long id, String url);
- //상품 삭제
void delete(long id);
}
diff --git a/src/main/java/gift/service/ProductServiceImpl.java b/src/main/java/gift/service/ProductServiceImpl.java
index 84779255c..41295a441 100644
--- a/src/main/java/gift/service/ProductServiceImpl.java
+++ b/src/main/java/gift/service/ProductServiceImpl.java
@@ -3,7 +3,6 @@
import gift.database.JdbcProductRepository;
import gift.dto.ProductDTO;
import gift.exceptionAdvisor.ProductServiceException;
-import java.util.ArrayList;
import java.util.List;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
@@ -20,13 +19,9 @@ public ProductServiceImpl(JdbcProductRepository jdbcProductRepository) {
@Override
public List readAll() {
var products = jdbcProductRepository.findAll();
- List productDTOList = new ArrayList<>();
- for (var product : products) { //DTO로 전환
- productDTOList.add(new ProductDTO(product));
- }
-
- return productDTOList;
+ return products.stream().map(product -> new ProductDTO(product.getId(), product.getName(),
+ product.getPrice(), product.getImageUrl())).toList();
}
//새로운 상품 추가
diff --git a/src/main/java/gift/service/WishListService.java b/src/main/java/gift/service/WishListService.java
index 9bc5e0c70..8a9b6c9e9 100644
--- a/src/main/java/gift/service/WishListService.java
+++ b/src/main/java/gift/service/WishListService.java
@@ -4,7 +4,7 @@
public interface WishListService {
- void addNewProduct(long memberId, long productId);
+ void addProduct(long memberId, long productId);
void deleteProduct(long memberId, long productId);
diff --git a/src/main/java/gift/service/WishListServiceImpl.java b/src/main/java/gift/service/WishListServiceImpl.java
index a495905dd..205e1db26 100644
--- a/src/main/java/gift/service/WishListServiceImpl.java
+++ b/src/main/java/gift/service/WishListServiceImpl.java
@@ -16,7 +16,7 @@ public WishListServiceImpl(JdbcWishListRepository jdbcWishListRepository) {
}
@Override
- public void addNewProduct(long memberId, long productId) {
+ public void addProduct(long memberId, long productId) {
WishList wishList = new WishList(null, memberId, new HashMap<>());
wishList.updateProduct(productId, 1);
jdbcWishListRepository.insertWishList(wishList);
From 9f5fefb8d8b514bea8a9e99905cc24e9e4ab54f3 Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Mon, 8 Jul 2024 18:10:01 +0900
Subject: [PATCH 47/53] =?UTF-8?q?refact=20:=20=ED=81=B4=EB=9E=98=EC=8A=A4?=
=?UTF-8?q?=20=EC=9D=B4=EB=A6=84=20=EB=B3=80=EA=B2=BD,=20=EA=B0=84?=
=?UTF-8?q?=EB=8B=A8=20=EC=A3=BC=EC=84=9D=20=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
...xceptionAdvisor.java => ExceptionAdvisor.java} | 15 +++++++++++++--
1 file changed, 13 insertions(+), 2 deletions(-)
rename src/main/java/gift/exceptionAdvisor/{ProductExceptionAdvisor.java => ExceptionAdvisor.java} (80%)
diff --git a/src/main/java/gift/exceptionAdvisor/ProductExceptionAdvisor.java b/src/main/java/gift/exceptionAdvisor/ExceptionAdvisor.java
similarity index 80%
rename from src/main/java/gift/exceptionAdvisor/ProductExceptionAdvisor.java
rename to src/main/java/gift/exceptionAdvisor/ExceptionAdvisor.java
index 907210c24..fc7674a96 100644
--- a/src/main/java/gift/exceptionAdvisor/ProductExceptionAdvisor.java
+++ b/src/main/java/gift/exceptionAdvisor/ExceptionAdvisor.java
@@ -10,9 +10,12 @@
@ControllerAdvice
@RestController
-public class ProductExceptionAdvisor {
+public class ExceptionAdvisor {
- @ExceptionHandler(MethodArgumentNotValidException.class) //유효성 검사 실패 시
+ /*
+ ProductController 유효성 검사 실패 핸들러
+ */
+ @ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity productValidationException(
MethodArgumentNotValidException exception) {
return new ResponseEntity<>(new ExceptionResponse(
@@ -20,6 +23,10 @@ public ResponseEntity productValidationException(
exception.getStatusCode());
}
+ /*
+ ProductService 예외 핸들러
+ 금지된 문구 사용 etc
+ */
@ExceptionHandler(ProductServiceException.class)
public ResponseEntity productServiceException(
ProductServiceException exception) {
@@ -27,6 +34,10 @@ public ResponseEntity productServiceException(
exception.getStatusCode());
}
+ /*
+ MemberService 예외 핸들러
+ 이메일 중복 등
+ */
@ExceptionHandler(MemberServiceException.class)
public ResponseEntity memberServiceException(
MemberServiceException exception) {
From d09d4ff406a68bf3e158569704fbb26604edca0b Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Mon, 8 Jul 2024 18:10:30 +0900
Subject: [PATCH 48/53] =?UTF-8?q?feat=20:=20=EC=9D=B4=EB=A9=94=EC=9D=BC=20?=
=?UTF-8?q?=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B2=80=EC=82=AC=20=EC=B6=94?=
=?UTF-8?q?=EA=B0=80,=20=EB=B6=88=ED=95=84=EC=9A=94=20=EC=A3=BC=EC=84=9D?=
=?UTF-8?q?=20=EC=82=AD=EC=A0=9C?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../java/gift/service/MemberServiceImpl.java | 16 +++++++++-------
1 file changed, 9 insertions(+), 7 deletions(-)
diff --git a/src/main/java/gift/service/MemberServiceImpl.java b/src/main/java/gift/service/MemberServiceImpl.java
index cfd51801f..b993c958b 100644
--- a/src/main/java/gift/service/MemberServiceImpl.java
+++ b/src/main/java/gift/service/MemberServiceImpl.java
@@ -31,7 +31,7 @@ public void register(MemberDTO memberDTO) {
@Override
public LoginToken login(MemberDTO memberDTO) {
- Member member = jdbcMemeberRepository.findByEmail(memberDTO.getEmail());
+ Member member = findByEmail(memberDTO.getEmail());
if (memberDTO.getPassword().equals(member.getPassword())) {
LoginToken loginToken = new LoginToken(member.getId(), member.getEmail(),
@@ -57,12 +57,6 @@ public MemberDTO getLoginUser(String token) {
}
- /**
- * 이메일 중복 확인
- *
- * @param email
- * @return 중복된 이메일인 경우 true 반환
- */
private boolean checkEmailDuplication(String email) {
try {
jdbcMemeberRepository.findByEmail(email);
@@ -71,4 +65,12 @@ private boolean checkEmailDuplication(String email) {
return false;
}
}
+
+ private Member findByEmail(String email) {
+ try {
+ return jdbcMemeberRepository.findByEmail(email);
+ } catch (EmptyResultDataAccessException e) {
+ throw new MemberServiceException("잘못된 로그인 시도입니다.", HttpStatus.FORBIDDEN);
+ }
+ }
}
From 309efa530458c0f09d85ba6b3a8edf8ab5472d6e Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Mon, 8 Jul 2024 18:10:50 +0900
Subject: [PATCH 49/53] =?UTF-8?q?test=20:=20=EC=97=86=EB=8A=94=20=EC=9D=B4?=
=?UTF-8?q?=EB=A9=94=EC=9D=BC=EB=A1=9C=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?=
=?UTF-8?q?=EC=8B=9C=EB=8F=84=20=ED=85=8C=EC=8A=A4=ED=8A=B8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../gift/controller/MemberControllerTest.java | 54 ++++++++++++++-----
1 file changed, 42 insertions(+), 12 deletions(-)
diff --git a/src/test/java/gift/controller/MemberControllerTest.java b/src/test/java/gift/controller/MemberControllerTest.java
index e54e0477c..2c287f1b6 100644
--- a/src/test/java/gift/controller/MemberControllerTest.java
+++ b/src/test/java/gift/controller/MemberControllerTest.java
@@ -5,7 +5,6 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.http.MediaType;
@@ -19,7 +18,6 @@ class MemberControllerTest {
@LocalServerPort
private int port;
private String baseUrl;
- @Autowired
private WebTestClient webClient;
@BeforeEach
@@ -32,7 +30,7 @@ void setUp() {
@DisplayName("회원가입 성공")
void registerMember() {
//given
- String email = "abcd@gmail.com";
+ String email = "asdef@gmail.com";
String password = "abcd";
MemberDTO dto = new MemberDTO(email, password, null);
@@ -53,7 +51,7 @@ void registerDuplicateEmail() {
MemberDTO dto2 = new MemberDTO(email, "4567", null);
//when
- ResponseSpec responseSpec = registerMemberPutRequest(dto);
+ registerMemberPutRequest(dto);
ResponseSpec responseSpec2 = registerMemberPutRequest(dto2);
responseSpec2.expectStatus().isForbidden();
@@ -93,29 +91,61 @@ void login() {
String email = "abcd@gmail.com";
String password = "abcd";
MemberDTO dto = new MemberDTO(email, password, null);
- ResponseSpec responseSpec = registerMemberPutRequest(dto);
+ registerMemberPutRequest(dto);
//when
- ResponseSpec responseSeec = webClient.get().uri(uriBuilder -> uriBuilder
+ ResponseSpec responseSpec = webClient.get().uri(uriBuilder -> uriBuilder
.path("/api/member/login")
.queryParam("email", dto.getEmail())
.queryParam("password", dto.getPassword())
.build()).accept(MediaType.APPLICATION_JSON).exchange();
//then
- responseSeec.expectStatus().isOk();
- responseSeec.expectBody(LoginToken.class);
+ responseSpec.expectStatus().isOk();
+ responseSpec.expectBody(LoginToken.class);
}
- /*
+
@Test
@DisplayName("패스워드가 불일치한 경우")
+ void wrongPassword() {
+ //given
+ String email = "abcd@gmail.com";
+ String password = "abcd";
+ String wrongPassword = "wrong";
+ MemberDTO dto = new MemberDTO(email, password, null);
+ registerMemberPutRequest(dto);
+
+ //when
+ ResponseSpec responseSpec = webClient.get().uri(uriBuilder -> uriBuilder
+ .path("/api/member/login")
+ .queryParam("email", dto.getEmail())
+ .queryParam("password", wrongPassword)
+ .build()).accept(MediaType.APPLICATION_JSON).exchange();
+
+ //then
+ responseSpec.expectStatus().isForbidden();
+
+ }
@Test
@DisplayName("회원가입이 되지않은 이메일로 로그인을 시도하는 경우")
+ void noRegister() {
+ //given
+ String email = "imno@gmail.com";
+ String password = "abcd";
+ MemberDTO dto = new MemberDTO(email, password, null);
+
+ //when
+ ResponseSpec responseSpec = webClient.get().uri(uriBuilder -> uriBuilder
+ .path("/api/member/login")
+ .queryParam("email", dto.getEmail())
+ .queryParam("password", dto.getPassword())
+ .build()).accept(MediaType.APPLICATION_JSON).exchange();
+
+ //then
+ responseSpec.expectStatus().isForbidden();
+ }
- @Test
- @DisplayName("로그인 성공으로 Token을 받아오기 성공")
- */
private ResponseSpec registerMemberPutRequest(MemberDTO dto) {
ResponseSpec responseSpec = webClient.put().uri("/api/member")
From 59eaa346167b9b01b64eeaf5cb7e0aa4bb925e86 Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Mon, 8 Jul 2024 18:57:53 +0900
Subject: [PATCH 50/53] =?UTF-8?q?feat=20:=20controller=20test=20helper=20?=
=?UTF-8?q?=EC=83=9D=EC=84=B1?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
httpmethod 편하게 생성, 검증을 위해
---
.../gift/controller/WebTestClientHelper.java | 88 +++++++++++++++++++
1 file changed, 88 insertions(+)
create mode 100644 src/test/java/gift/controller/WebTestClientHelper.java
diff --git a/src/test/java/gift/controller/WebTestClientHelper.java b/src/test/java/gift/controller/WebTestClientHelper.java
new file mode 100644
index 000000000..80d2128bd
--- /dev/null
+++ b/src/test/java/gift/controller/WebTestClientHelper.java
@@ -0,0 +1,88 @@
+package gift.controller;
+
+import jakarta.validation.constraints.NotNull;
+import java.util.Map;
+import org.springframework.http.MediaType;
+import org.springframework.test.web.reactive.server.WebTestClient;
+import org.springframework.test.web.reactive.server.WebTestClient.ResponseSpec;
+import org.springframework.web.reactive.function.BodyInserters;
+import org.springframework.web.util.UriComponentsBuilder;
+
+/**
+ * webflux 를 이용한 webTestClient를 편하게 사용할 수 있습니다.
build.gradle 추가-> implementation
+ * 'org.springframework.boot:spring-boot-starter-webflux'
(@SpringBootTest) 에서 Rest api test에
+ * 사용할 수 있습니다.
url에 매개변수를 삽입 : uriMakeUseParameters로 url 생성
+ *
+ * 사용예시
+ *
{@code
+ * @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+ * class ControllerTest {
+ *
+ * @LocalServerPort
+ * private int port;
+ * WebTestClientHelper webClient;
+ *
+ * @BeforeEach
+ * void setUp() {
+ * webClient = new WebTestClientHelper(port);
+ * }
+ *
+ * @Test
+ * @DisplayName("WebTestClientHelper 정상 작동 확인")
+ * void isOk() {
+ * //given
+ * String url = "/admin/products";
+ *
+ * //when
+ * var response = webClient.get(url);
+ *
+ * //then
+ * response.expectStatus().isOk();
+ * response.expectHeader().contentType("application/json");
+ * response.expectBody().json(...)
+ *
+ * }
+ * }
+ */
+public class WebTestClientHelper {
+
+ private final WebTestClient webTestClient;
+
+ public WebTestClientHelper(int port) {
+ this.webTestClient = WebTestClient.bindToServer().baseUrl("http://localhost:" + port)
+ .build();
+ }
+
+ public ResponseSpec get(String url) {
+ return webTestClient.get().uri(url).accept(MediaType.APPLICATION_JSON).exchange();
+ }
+
+ public ResponseSpec post(String url, Object body) {
+ return webTestClient.post().uri(url).accept(MediaType.APPLICATION_JSON)
+ .body(BodyInserters.fromValue(body)).exchange();
+ }
+
+ public ResponseSpec put(String url, Object body) {
+ return webTestClient.put().uri(url).accept(MediaType.APPLICATION_JSON)
+ .body(BodyInserters.fromValue(body)).exchange();
+ }
+
+ public ResponseSpec delete(String url) {
+ return webTestClient.delete().uri(url).accept(MediaType.APPLICATION_JSON).exchange();
+ }
+
+ /**
+ * url을 만들어 준다.
+ *
+ * @param path 기본경로 ex)/api/products
+ * @param parameters url 매개변수 ex) [["email" = "happy"],["password" = "1234"]]
+ * @return String type 생성된 uri
+ */
+ public String uriMakeUseParameters(@NotNull String path, Map parameters) {
+ UriComponentsBuilder builder = UriComponentsBuilder.newInstance();
+ builder.path(path);
+ parameters.forEach(builder::queryParam);
+ return builder.build().toString();
+
+ }
+}
From 3b827c218c3c25dd3b0d2e2c6312ee2ec93c02a2 Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Mon, 8 Jul 2024 22:06:33 +0900
Subject: [PATCH 51/53] =?UTF-8?q?refact=20:=20token=20=EA=B8=B0=EB=B0=98?=
=?UTF-8?q?=20=EC=82=AC=EC=9A=A9=EC=9E=90=20=EC=9D=B8=EC=A6=9D=20=EA=B8=B0?=
=?UTF-8?q?=EB=8A=A5=20=EC=88=98=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../gift/controller/MemberController.java | 4 +-
.../gift/database/JdbcMemeberRepository.java | 12 +---
src/main/java/gift/dto/LoginMemberToken.java | 21 ++++++
src/main/java/gift/model/LoginToken.java | 66 -------------------
.../java/gift/service/AuthenticationTool.java | 37 +++++++++++
src/main/java/gift/service/MemberService.java | 6 +-
.../java/gift/service/MemberServiceImpl.java | 25 +++----
.../gift/controller/MemberControllerTest.java | 4 +-
.../controller/WishListControllerTest.java | 26 +++-----
9 files changed, 91 insertions(+), 110 deletions(-)
create mode 100644 src/main/java/gift/dto/LoginMemberToken.java
delete mode 100644 src/main/java/gift/model/LoginToken.java
create mode 100644 src/main/java/gift/service/AuthenticationTool.java
diff --git a/src/main/java/gift/controller/MemberController.java b/src/main/java/gift/controller/MemberController.java
index da9f3d3e4..451a6ab65 100644
--- a/src/main/java/gift/controller/MemberController.java
+++ b/src/main/java/gift/controller/MemberController.java
@@ -1,7 +1,7 @@
package gift.controller;
+import gift.dto.LoginMemberToken;
import gift.dto.MemberDTO;
-import gift.model.LoginToken;
import gift.model.MemberRole;
import gift.service.MemberService;
import jakarta.validation.Valid;
@@ -32,7 +32,7 @@ public void register(@RequestBody @Valid MemberDTO memberDTO) {
}
@GetMapping("/login")
- public LoginToken login(@RequestParam("email") @NotBlank String email,
+ public LoginMemberToken login(@RequestParam("email") @NotBlank String email,
@RequestParam("password") @NotBlank String password) {
return memberService.login(new MemberDTO(email, password, null));
}
diff --git a/src/main/java/gift/database/JdbcMemeberRepository.java b/src/main/java/gift/database/JdbcMemeberRepository.java
index 67f6d7a25..6563b79f4 100644
--- a/src/main/java/gift/database/JdbcMemeberRepository.java
+++ b/src/main/java/gift/database/JdbcMemeberRepository.java
@@ -26,8 +26,7 @@ private void createTable() {
+ "id long primary key auto_increment, "
+ "email varchar(255) unique not null, "
+ "password varchar(255) not null, "
- + "role varchar(255) not null, "
- + "token varchar(255))");
+ + "role varchar(255) not null)");
}
@@ -44,9 +43,9 @@ public void create(String email, String password, String role) {
}
public void update(long id, Member member) {
- String sql = "update member set email=?, password=?, role=?, token=? where id=?";
+ String sql = "update member set email=?, password=?, role=? where id=?";
template.update(sql, member.getEmail(), member.getPassword(), member.getRole().toString(),
- member.getToken(), id);
+ id);
}
public void delete(long id) {
@@ -64,11 +63,6 @@ public Member findByEmail(String email) {
return template.queryForObject(sql, memberRowMapper(), email);
}
- public Member findByToken(String token) {
- String sql = "select * from member where token = ?";
- return template.queryForObject(sql, memberRowMapper(), token);
- }
-
private RowMapper memberRowMapper() {
return (rs, rowNum) -> new Member(
rs.getLong("id"),
diff --git a/src/main/java/gift/dto/LoginMemberToken.java b/src/main/java/gift/dto/LoginMemberToken.java
new file mode 100644
index 000000000..63e4a7069
--- /dev/null
+++ b/src/main/java/gift/dto/LoginMemberToken.java
@@ -0,0 +1,21 @@
+package gift.dto;
+
+public class LoginMemberToken {
+
+ private String token;
+
+ public LoginMemberToken() {
+ }
+
+ public LoginMemberToken(String token) {
+ this.token = token;
+ }
+
+ public String getToken() {
+ return token;
+ }
+
+ public void setToken(String token) {
+ this.token = token;
+ }
+}
diff --git a/src/main/java/gift/model/LoginToken.java b/src/main/java/gift/model/LoginToken.java
deleted file mode 100644
index ee7dbcdb5..000000000
--- a/src/main/java/gift/model/LoginToken.java
+++ /dev/null
@@ -1,66 +0,0 @@
-package gift.model;
-
-import io.jsonwebtoken.Jwts.SIG;
-import java.security.Key;
-import java.util.Objects;
-
-public class LoginToken {
-
- private String token;
-
- private Key key = SIG.HS256.key().build();
-
- private String email;
- private String role;
- private Long memberId;
-
- public LoginToken() {
- }
-
- public LoginToken(Long memberId, String email, MemberRole role) {
- this.token = email + ":" + role.toString();
- /* JWT 학습 이후 구현하기
- this.token = Jwts.builder()
- .setSubject(email)
- .claim("role", role.toString())
- .signWith(key)
- .compact();
-
- */
- this.email = email;
- this.role = role.toString();
- }
-
- public String getToken() {
- return token;
- }
-
- public Long getMemberId() {
- if (token != null) {
- return memberId;
- }
- //JwtParser parser = Jwts.parser().build();
- return null;
-
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
-
- LoginToken that = (LoginToken) o;
- return Objects.equals(token, that.token);
- }
-
- @Override
- public int hashCode() {
- return Objects.hashCode(token);
- }
-
-
-}
diff --git a/src/main/java/gift/service/AuthenticationTool.java b/src/main/java/gift/service/AuthenticationTool.java
new file mode 100644
index 000000000..104e6e097
--- /dev/null
+++ b/src/main/java/gift/service/AuthenticationTool.java
@@ -0,0 +1,37 @@
+package gift.service;
+
+import gift.exceptionAdvisor.MemberServiceException;
+import gift.model.Member;
+import io.jsonwebtoken.JwtException;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.Jwts.SIG;
+import javax.crypto.SecretKey;
+import org.springframework.http.HttpStatus;
+import org.springframework.stereotype.Service;
+
+@Service
+public class AuthenticationTool {
+
+ private final SecretKey key = SIG.HS256.key().build();
+
+ public AuthenticationTool() {
+ }
+
+ public String makeToken(Member member) {
+ return Jwts.builder().claim("id", member.getId())
+ .signWith(key).toString();
+ }
+
+ public long parseToken(String token) {
+ try {
+ var claims = Jwts.parser().verifyWith(key).build().parseSignedClaims(token)
+ .getPayload();
+ return Long.parseLong(claims.get("id").toString());
+ } catch (JwtException e) {
+ throw new MemberServiceException("JWT 인증 실패", HttpStatus.FORBIDDEN);
+ }
+
+ }
+
+
+}
diff --git a/src/main/java/gift/service/MemberService.java b/src/main/java/gift/service/MemberService.java
index 64ae30e9e..21d8dee99 100644
--- a/src/main/java/gift/service/MemberService.java
+++ b/src/main/java/gift/service/MemberService.java
@@ -1,15 +1,15 @@
package gift.service;
+import gift.dto.LoginMemberToken;
import gift.dto.MemberDTO;
-import gift.model.LoginToken;
public interface MemberService {
void register(MemberDTO memberDTO);
- LoginToken login(MemberDTO memberDTO);
+ LoginMemberToken login(MemberDTO memberDTO);
- boolean checkRole(LoginToken loginToken);
+ boolean checkRole(MemberDTO memberDTO);
MemberDTO getLoginUser(String token);
}
diff --git a/src/main/java/gift/service/MemberServiceImpl.java b/src/main/java/gift/service/MemberServiceImpl.java
index b993c958b..1e554a518 100644
--- a/src/main/java/gift/service/MemberServiceImpl.java
+++ b/src/main/java/gift/service/MemberServiceImpl.java
@@ -1,9 +1,9 @@
package gift.service;
import gift.database.JdbcMemeberRepository;
+import gift.dto.LoginMemberToken;
import gift.dto.MemberDTO;
import gift.exceptionAdvisor.MemberServiceException;
-import gift.model.LoginToken;
import gift.model.Member;
import gift.model.MemberRole;
import org.springframework.dao.EmptyResultDataAccessException;
@@ -15,8 +15,12 @@ public class MemberServiceImpl implements MemberService {
private JdbcMemeberRepository jdbcMemeberRepository;
- public MemberServiceImpl(JdbcMemeberRepository jdbcMemeberRepository) {
+ private AuthenticationTool authenticationTool;
+
+ public MemberServiceImpl(JdbcMemeberRepository jdbcMemeberRepository,
+ AuthenticationTool authenticationTool) {
this.jdbcMemeberRepository = jdbcMemeberRepository;
+ this.authenticationTool = authenticationTool;
}
@Override
@@ -30,30 +34,27 @@ public void register(MemberDTO memberDTO) {
}
@Override
- public LoginToken login(MemberDTO memberDTO) {
+ public LoginMemberToken login(MemberDTO memberDTO) {
Member member = findByEmail(memberDTO.getEmail());
if (memberDTO.getPassword().equals(member.getPassword())) {
- LoginToken loginToken = new LoginToken(member.getId(), member.getEmail(),
- member.getRole());
- member.setToken(loginToken.getToken());
- jdbcMemeberRepository.update(member.getId(), member);
- return loginToken;
+ String token = authenticationTool.makeToken(member);
+ return new LoginMemberToken(token);
}
throw new MemberServiceException("잘못된 로그인 시도입니다.", HttpStatus.FORBIDDEN);
}
@Override
- public boolean checkRole(LoginToken loginToken) {
+ public boolean checkRole(MemberDTO memberDTO) {
return false;
}
@Override
public MemberDTO getLoginUser(String token) {
- Member member = jdbcMemeberRepository.findByToken(token);
-
- return new MemberDTO(member.getEmail(), null, member.getRole());
+ long id = authenticationTool.parseToken(token);
+ Member member = jdbcMemeberRepository.findById(id);
+ return new MemberDTO(member.getEmail(), member.getPassword(), member.getRole());
}
diff --git a/src/test/java/gift/controller/MemberControllerTest.java b/src/test/java/gift/controller/MemberControllerTest.java
index 2c287f1b6..21fdc4e50 100644
--- a/src/test/java/gift/controller/MemberControllerTest.java
+++ b/src/test/java/gift/controller/MemberControllerTest.java
@@ -1,7 +1,7 @@
package gift.controller;
import gift.dto.MemberDTO;
-import gift.model.LoginToken;
+import gift.service.AuthenticationTool;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
@@ -102,7 +102,7 @@ void login() {
//then
responseSpec.expectStatus().isOk();
- responseSpec.expectBody(LoginToken.class);
+ responseSpec.expectBody(AuthenticationTool.class);
}
@Test
diff --git a/src/test/java/gift/controller/WishListControllerTest.java b/src/test/java/gift/controller/WishListControllerTest.java
index bf10633e8..2125c6c39 100644
--- a/src/test/java/gift/controller/WishListControllerTest.java
+++ b/src/test/java/gift/controller/WishListControllerTest.java
@@ -1,39 +1,33 @@
package gift.controller;
-import gift.model.LoginToken;
+import gift.service.AuthenticationTool;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalServerPort;
-import org.springframework.test.web.reactive.server.WebTestClient;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class WishListControllerTest {
@LocalServerPort
private int port;
- private String baseUrl;
- @Autowired
- private WebTestClient webClient;
+ WebTestClientHelper webClient;
@BeforeEach
void setUp() {
- baseUrl = "http://localhost:" + port;
- webClient = WebTestClient.bindToServer().baseUrl(baseUrl).build();
+ webClient = new WebTestClientHelper(port);
}
@Test
- @DisplayName("위시 리스트 추가")
- void addWishList() {
- //given
- LoginToken loginToken = registerAndLogin("test", "123");
+ @DisplayName("WebTestClientHelper 정상 작동 확인")
+ void isOk() {
- //when
- //addWishListPutRequest(loginToken);
+ var response = webClient.get("/admin/products");
- //then
+ response.expectStatus().isOk();
+ response.expectHeader().contentType("application/json");
+ //response.expectBody().json(...)
}
@@ -56,7 +50,7 @@ void deleteWishList() {
}
- private LoginToken registerAndLogin(String email, String password) {
+ private AuthenticationTool registerAndLogin(String email, String password) {
return null;
}
/*
From 8e77209e795bd02a2addce0293f1974be38eee0d Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Mon, 8 Jul 2024 23:39:26 +0900
Subject: [PATCH 52/53] =?UTF-8?q?refact=20:=20wishlist=20=EB=B2=84?=
=?UTF-8?q?=EA=B7=B8=20=EC=88=98=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../gift/controller/WishListController.java | 30 ++++++++++---------
.../gift/database/JdbcWishListRepository.java | 2 +-
src/main/java/gift/dto/MemberDTO.java | 10 +++++++
src/main/java/gift/dto/WishListDTO.java | 9 ++++++
.../java/gift/service/AuthenticationTool.java | 4 +--
.../java/gift/service/WishListService.java | 3 +-
.../gift/service/WishListServiceImpl.java | 9 ++++--
.../gift/controller/MemberControllerTest.java | 4 +--
.../gift/controller/WebTestClientHelper.java | 4 +++
9 files changed, 53 insertions(+), 22 deletions(-)
diff --git a/src/main/java/gift/controller/WishListController.java b/src/main/java/gift/controller/WishListController.java
index 95dd11229..a9626fdff 100644
--- a/src/main/java/gift/controller/WishListController.java
+++ b/src/main/java/gift/controller/WishListController.java
@@ -4,16 +4,16 @@
import gift.dto.MemberDTO;
import gift.dto.WishListDTO;
import gift.service.WishListService;
+import java.util.List;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
-@RequestMapping("api/wishlist")
+@RequestMapping("/api/wishlist")
@RestController
public class WishListController {
@@ -23,27 +23,29 @@ public WishListController(WishListService wishListService) {
this.wishListService = wishListService;
}
- @GetMapping("/{memberId}")
- public WishListDTO getWishList(@PathVariable int memberId, @LoginMember MemberDTO memberDTO) {
- return wishListService.getWishList(memberId);
+ @GetMapping
+ public List getWishList(@LoginMember MemberDTO memberDTO) {
+ return wishListService.getWishList(memberDTO.getId());
}
//상품 추가
- @PostMapping("/{memberId}")
- public void addWishList(@PathVariable int memberId, @RequestBody WishListDTO wishListDTO) {
- wishListService.addProduct(memberId, wishListDTO.getProductId());
+ @PostMapping
+ public void addWishList(@LoginMember MemberDTO memberDTO,
+ @RequestBody WishListDTO wishListDTO) {
+ //wishListDTO.setMemberId(memberDTO.getId());
+ wishListService.addProduct(wishListDTO.getMemberId(), wishListDTO.getProductId());
}
//상품 삭제
- @DeleteMapping("/{memberId}")
- public void deleteWishList(@PathVariable int memberId, @RequestBody WishListDTO wishListDTO) {
- wishListService.deleteProduct(memberId, wishListDTO.getProductId());
+ @DeleteMapping
+ public void deleteWishList(@RequestBody WishListDTO wishListDTO) {
+ wishListService.deleteProduct(wishListDTO.getMemberId(), wishListDTO.getProductId());
}
//상품 수정
- @PutMapping("/{memberId}")
- public void updateWishList(@PathVariable int memberId, @RequestBody WishListDTO wishListDTO) {
- wishListService.updateProduct(memberId, wishListDTO.getProductId(),
+ @PutMapping
+ public void updateWishList(@RequestBody WishListDTO wishListDTO) {
+ wishListService.updateProduct(wishListDTO.getMemberId(), wishListDTO.getProductId(),
wishListDTO.getProductValue());
}
diff --git a/src/main/java/gift/database/JdbcWishListRepository.java b/src/main/java/gift/database/JdbcWishListRepository.java
index 491e45ffe..eff0d0530 100644
--- a/src/main/java/gift/database/JdbcWishListRepository.java
+++ b/src/main/java/gift/database/JdbcWishListRepository.java
@@ -53,7 +53,7 @@ public void setValues(PreparedStatement ps, int i) throws SQLException {
@Override
public int getBatchSize() {
- return 100;
+ return 1;
}
});
}
diff --git a/src/main/java/gift/dto/MemberDTO.java b/src/main/java/gift/dto/MemberDTO.java
index 8d6673d10..fea9789e5 100644
--- a/src/main/java/gift/dto/MemberDTO.java
+++ b/src/main/java/gift/dto/MemberDTO.java
@@ -12,6 +12,9 @@ public class MemberDTO {
private String password;
private MemberRole role;
+ public MemberDTO() {
+ }
+
public MemberDTO(String email, String password, MemberRole role) {
this.email = email;
this.password = password;
@@ -21,6 +24,13 @@ public MemberDTO(String email, String password, MemberRole role) {
}
}
+ public MemberDTO(Long id, String email, String password, MemberRole role) {
+ this.id = id;
+ this.email = email;
+ this.password = password;
+ this.role = role;
+ }
+
public Long getId() {
return id;
}
diff --git a/src/main/java/gift/dto/WishListDTO.java b/src/main/java/gift/dto/WishListDTO.java
index 32516d004..64bb8ae60 100644
--- a/src/main/java/gift/dto/WishListDTO.java
+++ b/src/main/java/gift/dto/WishListDTO.java
@@ -6,6 +6,15 @@ public class WishListDTO {
private Long productId;
private Integer productValue;
+ public WishListDTO() {
+ }
+
+ public WishListDTO(Long memberId, Long productId, Integer productValue) {
+ this.memberId = memberId;
+ this.productId = productId;
+ this.productValue = productValue;
+ }
+
public Long getMemberId() {
return memberId;
}
diff --git a/src/main/java/gift/service/AuthenticationTool.java b/src/main/java/gift/service/AuthenticationTool.java
index 104e6e097..52c2fa014 100644
--- a/src/main/java/gift/service/AuthenticationTool.java
+++ b/src/main/java/gift/service/AuthenticationTool.java
@@ -19,13 +19,13 @@ public AuthenticationTool() {
public String makeToken(Member member) {
return Jwts.builder().claim("id", member.getId())
- .signWith(key).toString();
+ .signWith(key).compact();
}
public long parseToken(String token) {
try {
var claims = Jwts.parser().verifyWith(key).build().parseSignedClaims(token)
- .getPayload();
+ .getPayload();//TODO : 수정필요
return Long.parseLong(claims.get("id").toString());
} catch (JwtException e) {
throw new MemberServiceException("JWT 인증 실패", HttpStatus.FORBIDDEN);
diff --git a/src/main/java/gift/service/WishListService.java b/src/main/java/gift/service/WishListService.java
index 8a9b6c9e9..0269423af 100644
--- a/src/main/java/gift/service/WishListService.java
+++ b/src/main/java/gift/service/WishListService.java
@@ -1,6 +1,7 @@
package gift.service;
import gift.dto.WishListDTO;
+import java.util.List;
public interface WishListService {
@@ -10,6 +11,6 @@ public interface WishListService {
void updateProduct(long memberId, long productId, int productValue);
- WishListDTO getWishList(long memberId);
+ List getWishList(long memberId);
}
diff --git a/src/main/java/gift/service/WishListServiceImpl.java b/src/main/java/gift/service/WishListServiceImpl.java
index 205e1db26..a9208c8c2 100644
--- a/src/main/java/gift/service/WishListServiceImpl.java
+++ b/src/main/java/gift/service/WishListServiceImpl.java
@@ -4,6 +4,7 @@
import gift.dto.WishListDTO;
import gift.model.WishList;
import java.util.HashMap;
+import java.util.List;
import org.springframework.stereotype.Service;
@Service
@@ -35,7 +36,11 @@ public void updateProduct(long memberId, long productId, int productValue) {
}
@Override
- public WishListDTO getWishList(long memberId) {
- return null;
+ public List getWishList(long memberId) {
+ WishList wishList = jdbcWishListRepository.findByMemeberId(memberId);
+ var products = wishList.getWishList();
+ return products.keySet().stream()
+ .map(key -> new WishListDTO(wishList.getMemberId(), key, products.get(key))).toList();
+
}
}
diff --git a/src/test/java/gift/controller/MemberControllerTest.java b/src/test/java/gift/controller/MemberControllerTest.java
index 21fdc4e50..1ec605bd6 100644
--- a/src/test/java/gift/controller/MemberControllerTest.java
+++ b/src/test/java/gift/controller/MemberControllerTest.java
@@ -1,7 +1,7 @@
package gift.controller;
+import gift.dto.LoginMemberToken;
import gift.dto.MemberDTO;
-import gift.service.AuthenticationTool;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
@@ -102,7 +102,7 @@ void login() {
//then
responseSpec.expectStatus().isOk();
- responseSpec.expectBody(AuthenticationTool.class);
+ responseSpec.expectBody(LoginMemberToken.class);
}
@Test
diff --git a/src/test/java/gift/controller/WebTestClientHelper.java b/src/test/java/gift/controller/WebTestClientHelper.java
index 80d2128bd..57bf87679 100644
--- a/src/test/java/gift/controller/WebTestClientHelper.java
+++ b/src/test/java/gift/controller/WebTestClientHelper.java
@@ -71,6 +71,10 @@ public ResponseSpec delete(String url) {
return webTestClient.delete().uri(url).accept(MediaType.APPLICATION_JSON).exchange();
}
+ public WebTestClient moreAction() {
+ return webTestClient;
+ }
+
/**
* url을 만들어 준다.
*
From 7bb04e22ba5cae287bc477f45d2f135173ec898c Mon Sep 17 00:00:00 2001
From: mac <20j.code@gmail.com>
Date: Mon, 8 Jul 2024 23:39:42 +0900
Subject: [PATCH 53/53] =?UTF-8?q?test=20:=20wishlist=20add=20=ED=85=8C?=
=?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../controller/WishListControllerTest.java | 56 +++++++++++--------
1 file changed, 32 insertions(+), 24 deletions(-)
diff --git a/src/test/java/gift/controller/WishListControllerTest.java b/src/test/java/gift/controller/WishListControllerTest.java
index 2125c6c39..95ce02c28 100644
--- a/src/test/java/gift/controller/WishListControllerTest.java
+++ b/src/test/java/gift/controller/WishListControllerTest.java
@@ -1,11 +1,17 @@
package gift.controller;
-import gift.service.AuthenticationTool;
+import gift.dto.LoginMemberToken;
+import gift.dto.MemberDTO;
+import gift.dto.WishListDTO;
+import java.util.HashMap;
+import java.util.Objects;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalServerPort;
+import org.springframework.http.MediaType;
+import org.springframework.web.reactive.function.BodyInserters;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class WishListControllerTest {
@@ -20,15 +26,17 @@ void setUp() {
}
@Test
- @DisplayName("WebTestClientHelper 정상 작동 확인")
- void isOk() {
-
- var response = webClient.get("/admin/products");
-
- response.expectStatus().isOk();
- response.expectHeader().contentType("application/json");
- //response.expectBody().json(...)
-
+ @DisplayName("위시 리스트 아이템 추가")
+ void addWishList() {
+ //given
+ String email = "abec";
+ String password = "abecdddd";
+ LoginMemberToken loginMemberToken = registerAndLogin(email, password);
+ webClient.moreAction().post().uri("api/wishlist")
+ .header("Authorization", loginMemberToken.getToken())
+ .accept(MediaType.APPLICATION_JSON)
+ .body(BodyInserters.fromValue(new WishListDTO(0L, 123L, 1)))
+ .exchange().expectStatus().isOk();
}
@@ -50,20 +58,20 @@ void deleteWishList() {
}
- private AuthenticationTool registerAndLogin(String email, String password) {
- return null;
- }
- /*
- private void addWishListPutRequest(long memberId, WishListDTO wishListDTO) {
- ResponseSpec responseSpec = webClient.put().uri(uriBuilder -> {
- return uriBuilder
- .path("/api/member")
- .queryParam("memberId", loginToken.getMemberId())
- .path("/wishlist")
- .build();
- }).accept(MediaType.APPLICATION_JSON)
- .body(BodyInserters.fromValue(wishListDTO)).exchange();
+ private LoginMemberToken registerAndLogin(String email, String password) {
+ //register
+ MemberDTO memberDTO = new MemberDTO(email, password, null);
+ webClient.put("/api/member", memberDTO);
+
+ //login
+ HashMap userInfo = new HashMap<>();
+ userInfo.put("email", memberDTO.getEmail());
+ userInfo.put("password", memberDTO.getPassword());
+ String uri = webClient.uriMakeUseParameters("/api/member/login", userInfo);
+ String token = Objects.requireNonNull(
+ webClient.moreAction().get().uri(uri).accept(MediaType.APPLICATION_JSON).exchange()
+ .expectBody(LoginMemberToken.class).returnResult().getResponseBody()).getToken();
+ return new LoginMemberToken(token);
}
- */
}
\ No newline at end of file