diff --git a/README.md b/README.md
index 75f82d8fa..557565400 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,34 @@
-# spring-gift-enhancement
\ No newline at end of file
+# spring-gift-enhancement
+
+## Step1
+- 기능 요구사항
+ - 상품 정보에 카테고리를 추가한다. 상품과 카테고리 모델 간의 관계를 고려하여 설계하고 구현한다.
+ - 상품에는 항상 하나의 카테고리가 있어야 한다.
+ - 상품 카테고리는 수정할 수 있다.
+ - 관리자 화면에서 상품을 추가할 때 카테고리를 지정할 수 있다.
+ - 카테고리는 1차 카테고리만 있으며 2차 카테고리는 고려하지 않는다.
+ - 카테고리의 예시는 아래와 같다.
+ - 교환권, 상품권, 뷰티, 패션, 식품, 리빙/도서, 레저/스포츠, 아티스트/캐릭터, 유아동/반려, 디지털/가전, 카카오프렌즈, 트렌드 선물, 백화점, ...
+ - 아래 예시와 같이 HTTP 메시지를 주고받도록 구현한다.
+
+ - Request
+
+ GET /api/categories HTTP/1.1
+
+
+ - Response
+
+
+ HTTP/1.1 200
+ Content-Type: application/json
+ [
+ {
+ "id": 91,
+ "name": "교환권",
+ "color": "#6c95d1",
+ "imageUrl": "https://gift-s.kakaocdn.net/dn/gift/images/m640/dimm_theme.png",
+ "description": ""
+ }
+ ]
+
+
diff --git a/build.gradle b/build.gradle
index 4c0637e25..7f478a05e 100644
--- a/build.gradle
+++ b/build.gradle
@@ -19,8 +19,7 @@ repositories {
dependencies {
// MySQL
- runtimeOnly 'com.mysql:mysql-connector-j' // MySQL 드라이버 의존성 추가
- testRuntimeOnly 'mysql:mysql-connector-java'
+ runtimeOnly 'mysql:mysql-connector-java:8.0.30' // MySQL 드라이버 의존성 추가
// JPA
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
diff --git a/src/main/java/gift/controller/AdminController.java b/src/main/java/gift/controller/AdminController.java
index 7d71f74c5..a5c9e4267 100644
--- a/src/main/java/gift/controller/AdminController.java
+++ b/src/main/java/gift/controller/AdminController.java
@@ -2,16 +2,12 @@
import gift.domain.ProductDTO;
import gift.service.ProductService;
-import gift.service.ProductServiceStatus;
import jakarta.validation.Valid;
-import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
-import org.springframework.web.servlet.ModelAndView;
@Controller
@RequestMapping("/admin/products")
@@ -36,25 +32,19 @@ public String addProductForm(Model model) {
}
@PostMapping("/add")
- public ModelAndView addProduct(@ModelAttribute @Valid ProductDTO productDTO, BindingResult bindingResult) {
+ public String addProduct(@ModelAttribute @Valid ProductDTO productDTO, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
- return new ModelAndView("create");
+ return "create";
}
- ProductServiceStatus response = productService.createProduct(productDTO);
- if (response == ProductServiceStatus.SUCCESS) {
- return new ModelAndView("redirect:/admin/products");
- }
-
- ModelAndView mav = new ModelAndView("create");
- mav.setStatus(HttpStatus.INTERNAL_SERVER_ERROR);
- mav.addObject("error", "상품 추가 실패");
- return mav;
+ productService.createProduct(productDTO);
+ return "redirect:/admin/products";
}
@GetMapping("/update/{id}")
public String editProductForm(@PathVariable Long id, Model model) {
- Optional productDTO = productService.getProduct(id);
+ var productDTO = productService.getProduct(id)
+ .orElse(null); // Optional에서 직접 null을 반환
if (productDTO == null) {
return "redirect:/admin/products";
}
@@ -63,28 +53,18 @@ public String editProductForm(@PathVariable Long id, Model model) {
}
@PostMapping("/update/{id}")
- public ModelAndView editProduct(@PathVariable Long id, @ModelAttribute @Valid ProductDTO productDTO, BindingResult bindingResult) {
+ public String editProduct(@PathVariable Long id, @ModelAttribute @Valid ProductDTO productDTO, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
- return new ModelAndView("update");
+ return "update";
}
- ProductServiceStatus response = productService.editProduct(id, productDTO);
- if (response == ProductServiceStatus.SUCCESS) {
- return new ModelAndView("redirect:/admin/products");
- }
-
- ModelAndView mav = new ModelAndView("update");
- mav.setStatus(HttpStatus.INTERNAL_SERVER_ERROR);
- mav.addObject("error", "상품 수정 실패");
- return mav;
+ productService.editProduct(id, productDTO);
+ return "redirect:/admin/products";
}
@PostMapping("/delete/{id}")
public String deleteProduct(@PathVariable Long id) {
- ProductServiceStatus response = productService.deleteProduct(id);
- if (response == ProductServiceStatus.SUCCESS) {
- return "redirect:/admin/products"; // 성공적으로 삭제 후 목록 페이지로 리다이렉트
- }
- return "error"; // 실패 시 에러 페이지로 리다이렉트
+ productService.deleteProduct(id);
+ return "redirect:/admin/products";
}
}
diff --git a/src/main/java/gift/controller/CategoryController.java b/src/main/java/gift/controller/CategoryController.java
new file mode 100644
index 000000000..cf4614b45
--- /dev/null
+++ b/src/main/java/gift/controller/CategoryController.java
@@ -0,0 +1,60 @@
+package gift.controller;
+
+import gift.domain.CategoryDTO;
+import gift.service.CategoryService;
+import jakarta.validation.Valid;
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+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;
+
+@RestController
+@RequestMapping("/api/category")
+public class CategoryController {
+ private final CategoryService categoryService;
+
+ @Autowired
+ public CategoryController(CategoryService categoryService){
+ this.categoryService = categoryService;
+ }
+
+ @GetMapping
+ public ResponseEntity> getCategory() {
+ List response = categoryService.getCategories();
+ return new ResponseEntity<>(response, HttpStatus.OK);
+ }
+
+ @GetMapping("/{id}")
+ public ResponseEntity getCategoryById(@PathVariable Long id) throws Exception {
+ CategoryDTO response = categoryService.getCategory(id);
+ return new ResponseEntity<>(response, HttpStatus.OK);
+ }
+
+ @PutMapping
+ public ResponseEntity addCategory(@Valid @RequestBody CategoryDTO category) {
+ CategoryDTO createdCategory = categoryService.createCategory(category);
+ return new ResponseEntity<>(createdCategory, HttpStatus.CREATED);
+ }
+
+ @PostMapping("/{id}")
+ public ResponseEntity updateCategory(@PathVariable Long id, @Valid @RequestBody CategoryDTO category) {
+ CategoryDTO updatedCategory = categoryService.updateCategory(id, category);
+ return new ResponseEntity<>(updatedCategory, HttpStatus.OK);
+ }
+
+ @DeleteMapping("/{id}")
+ public ResponseEntity deleteCategory(@PathVariable Long id) {
+ categoryService.deleteCategory(id);
+ return new ResponseEntity<>(HttpStatus.NO_CONTENT);
+ }
+
+
+}
diff --git a/src/main/java/gift/controller/MemberController.java b/src/main/java/gift/controller/MemberController.java
index 5e06d90df..9d77837d2 100644
--- a/src/main/java/gift/controller/MemberController.java
+++ b/src/main/java/gift/controller/MemberController.java
@@ -4,7 +4,6 @@
import gift.domain.MemberDTO;
import gift.service.JwtUtil;
import gift.service.MemberService;
-import gift.service.MemberServiceStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
@@ -28,29 +27,15 @@ public MemberController(MemberService memberService, JwtUtil jwtUtil) {
@PostMapping("/register")
public ResponseEntity> registerUser(@RequestBody MemberDTO memberDTO) {
- MemberServiceStatus status = memberService.save(memberDTO);
-
- if (status == MemberServiceStatus.EMAIL_ALREADY_EXISTS) {
- return ResponseEntity.status(HttpStatus.CONFLICT).body(Collections.singletonMap("error", "Email already exists"));
- }
-
- return ResponseEntity.ok().body(Collections.singletonMap("message", "Success"));
+ memberService.save(memberDTO);
+ return new ResponseEntity<>(Collections.singletonMap("status", "SUCCESS"), HttpStatus.OK);
}
@PostMapping("/login")
public ResponseEntity> loginUser(@RequestBody MemberDTO memberDTO) {
MemberEntity authenticatedMember = memberService.authenticateToken(memberDTO);
-
- if (authenticatedMember == null) {
- return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(Collections.singletonMap("error", "Invalid email or password"));
- }
-
- Long userId = authenticatedMember.getId();
-
- // 토큰 생성 및 응답
- String token = jwtUtil.generateToken(authenticatedMember.getEmail(), userId);
- Map responseBody = Collections.singletonMap("token", token);
- return ResponseEntity.ok().body(responseBody);
+ String token = jwtUtil.generateToken(authenticatedMember.getEmail(), authenticatedMember.getId());
+ Map response = Collections.singletonMap("token", token);
+ return new ResponseEntity<>(response, HttpStatus.OK);
}
-
}
\ No newline at end of file
diff --git a/src/main/java/gift/controller/ProductController.java b/src/main/java/gift/controller/ProductController.java
index 48922f571..387b1684e 100644
--- a/src/main/java/gift/controller/ProductController.java
+++ b/src/main/java/gift/controller/ProductController.java
@@ -2,7 +2,6 @@
import gift.domain.ProductDTO;
import gift.service.ProductService;
-import gift.service.ProductServiceStatus;
import jakarta.validation.constraints.Min;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
@@ -25,9 +24,9 @@ private ProductController(ProductService productService) {
// 상품 추가(Create)
@PostMapping
- public ResponseEntity addProduct(@RequestBody ProductDTO productDTO) {
- ProductServiceStatus response = productService.createProduct(productDTO);
- return new ResponseEntity<>(response, HttpStatus.CREATED);
+ public ResponseEntity> addProduct(@RequestBody ProductDTO productDTO) {
+ productService.createProduct(productDTO);
+ return new ResponseEntity<>(HttpStatus.CREATED);
}
// 단일 상품 조회(Read)
@@ -54,13 +53,15 @@ public ResponseEntity> selectAllProducts(
// 상품 수정(Update)
@PutMapping("/{id}")
- public ProductServiceStatus updateProduct(@PathVariable Long id, @RequestBody ProductDTO productDTO) {
- return productService.editProduct(id, productDTO);
+ public ResponseEntity> updateProduct(@PathVariable Long id, @RequestBody ProductDTO productDTO) {
+ productService.editProduct(id, productDTO);
+ return ResponseEntity.ok("상품이 추기 되었습니다.");
}
// 상품 삭제(Delete)
@DeleteMapping("/{id}")
- public ProductServiceStatus deleteProduct(@PathVariable Long id) {
- return productService.deleteProduct(id);
+ public ResponseEntity> deleteProduct(@PathVariable Long id) {
+ productService.deleteProduct(id);
+ return ResponseEntity.ok("상품이 삭제 되었습니다.");
}
}
\ No newline at end of file
diff --git a/src/main/java/gift/domain/CategoryDTO.java b/src/main/java/gift/domain/CategoryDTO.java
new file mode 100644
index 000000000..0031cf4d1
--- /dev/null
+++ b/src/main/java/gift/domain/CategoryDTO.java
@@ -0,0 +1,49 @@
+package gift.domain;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Pattern;
+import jakarta.validation.constraints.Size;
+
+public class CategoryDTO {
+ private Long id;
+
+ @NotBlank(message = "카테고리 이름은 필수 입력 항목입니다.")
+ private String name;
+
+ @Pattern(regexp = "^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$", message = "올바른 HEX 색상 코드로 입력해 주세요")
+ private String color;
+
+ @Pattern(regexp = "^https?://.*$", message = "올바른 이미지 URL 형식으로 입력해 주세요")
+ private String imageUrl;
+
+ @Size(max = 255, message = "설명은 최대 255글자 이하로 입력해 주세요")
+ private String description;
+
+ public CategoryDTO(Long id, String name, String color, String imageUrl, String description) {
+ this.id = id;
+ this.name = name;
+ this.color = color;
+ this.imageUrl = imageUrl;
+ this.description = description;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getColor() {
+ return color;
+ }
+
+ public String getImageUrl() {
+ return imageUrl;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+}
diff --git a/src/main/java/gift/domain/ProductDTO.java b/src/main/java/gift/domain/ProductDTO.java
index 018af99cb..5704bb206 100644
--- a/src/main/java/gift/domain/ProductDTO.java
+++ b/src/main/java/gift/domain/ProductDTO.java
@@ -15,13 +15,15 @@ public class ProductDTO {
@Pattern(regexp = "^(?!.*카카오).*$", message = "'카카오'가 포함된 문구는 담당 MD와 협의 후 사용 바랍니다.")
private String name;
-
@PositiveOrZero(message = "가격은 0 이상의 숫자를 입력해 주세요.")
private int price;
@Pattern(regexp = "^https?://.*$", message = "올바른 이미지 URL 형식으로 입력해 주세요")
private String imageUrl;
+ @NotNull(message = "카테고리는 필수 항목입니다.")
+ private Long categoryId;
+
public ProductDTO(){}
/**
@@ -31,12 +33,14 @@ public ProductDTO(){}
* @param name 상품의 이름
* @param price 상품의 가격
* @param imageUrl 상품의 이미지 주소
+ * @param categoryId 카테고리 고유의 ID
*/
- public ProductDTO(Long id, String name, int price, String imageUrl){
+ public ProductDTO(Long id, String name, int price, String imageUrl, Long categoryId){
this.id = id;
this.name = name;
this.price = price;
this.imageUrl = imageUrl;
+ this.categoryId = categoryId;
}
@@ -57,6 +61,8 @@ public String getImageUrl() {
return imageUrl;
}
+ public Long getCategoryId() {return categoryId;}
+
// Setter 메서드들
public void setId(Long id) {
this.id = id;
@@ -73,4 +79,6 @@ public void setPrice(int price) {
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
+
+ public void setCategoryId(Long categoryId) {this.categoryId = categoryId;}
}
\ No newline at end of file
diff --git a/src/main/java/gift/entity/CategoryEntity.java b/src/main/java/gift/entity/CategoryEntity.java
new file mode 100644
index 000000000..a26507cc7
--- /dev/null
+++ b/src/main/java/gift/entity/CategoryEntity.java
@@ -0,0 +1,96 @@
+package gift.entity;
+
+import gift.domain.CategoryDTO;
+import jakarta.persistence.CascadeType;
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.FetchType;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import jakarta.persistence.OneToMany;
+import jakarta.persistence.Table;
+import java.util.List;
+
+@Entity
+@Table(name = "category")
+public class CategoryEntity {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @Column(nullable = false)
+ private String name;
+
+ @Column(nullable = false, length = 7)
+ private String color;
+
+ @Column(name = "image_url", nullable = false)
+ private String imageUrl;
+
+ @Column(nullable = false)
+ private String description;
+
+ @OneToMany(mappedBy = "category", fetch = FetchType.LAZY,
+ cascade = CascadeType.ALL, orphanRemoval = true)
+ private List productEntities;
+
+ public CategoryEntity() {}
+
+ public CategoryEntity(String name, String color, String imageUrl, String description) {
+ this.name = name;
+ this.color = color;
+ this.imageUrl = imageUrl;
+ this.description = description;
+ }
+
+ public CategoryEntity(CategoryDTO categoryDTO) {
+ this.id = categoryDTO.getId();
+ this.name = categoryDTO.getName();
+ this.color = categoryDTO.getColor();
+ this.imageUrl = categoryDTO.getImageUrl();
+ this.description = categoryDTO.getDescription();
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getColor() {
+ return color;
+ }
+
+ public String getImageUrl() {
+ return imageUrl;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setId(Long id) {this.id = id;}
+
+ public void setName(String name) {this.name = name;}
+
+ public void setColor(String Color) {this.color = color;}
+
+ public void setImageUrl(String imageUrl) {this.imageUrl = imageUrl;}
+
+ public void setDescription(String description) {this.description = description;}
+
+ public static CategoryDTO toDTO(CategoryEntity category) {
+ return new CategoryDTO(category.getId(), category.getName(), category.getColor(), category.getImageUrl(), category.getDescription());
+ }
+
+ public void update(CategoryDTO categoryDTO) {
+ this.setName(categoryDTO.getName());
+ this.setColor(categoryDTO.getColor());
+ this.setImageUrl(categoryDTO.getImageUrl());
+ this.setDescription(categoryDTO.getDescription());
+ }
+
+}
diff --git a/src/main/java/gift/entity/MemberEntity.java b/src/main/java/gift/entity/MemberEntity.java
index d4d375846..660619485 100644
--- a/src/main/java/gift/entity/MemberEntity.java
+++ b/src/main/java/gift/entity/MemberEntity.java
@@ -25,7 +25,7 @@ public class MemberEntity {
@OneToMany(mappedBy = "userEntity")
private List wishListEntities;
- protected MemberEntity() {}
+ public MemberEntity() {}
public MemberEntity(String email, String password) {
this.email = email;
diff --git a/src/main/java/gift/entity/ProductEntity.java b/src/main/java/gift/entity/ProductEntity.java
index e4758e920..55e7b7a03 100644
--- a/src/main/java/gift/entity/ProductEntity.java
+++ b/src/main/java/gift/entity/ProductEntity.java
@@ -1,5 +1,6 @@
package gift.entity;
+import gift.domain.ProductDTO;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
@@ -7,6 +8,8 @@
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
+import jakarta.persistence.JoinColumn;
+import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import java.util.List;
@@ -18,7 +21,7 @@ public class ProductEntity {
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
- @Column(nullable = false)
+ @Column(nullable = false, length = 15)
private String name;
@Column(nullable = false)
@@ -30,12 +33,19 @@ public class ProductEntity {
@OneToMany(mappedBy = "productEntity", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
private List wishListEntities;
- public ProductEntity(Long id, String name, int price, String imageUrl) {}
+ @ManyToOne(targetEntity = CategoryEntity.class, fetch = FetchType.LAZY)
+ @JoinColumn(name = "category_id")
+ private CategoryEntity category;
+
+ public ProductEntity() {}
- public ProductEntity(String name, int price, String imageUrl) {
+ public ProductEntity(Long id, String name, int price, String imageUrl, CategoryEntity category) {}
+
+ public ProductEntity(String name, int price, String imageUrl, CategoryEntity category) {
this.name = name;
this.price = price;
this.imageUrl = imageUrl;
+ this.category = category;
}
public Long getId() {
@@ -57,4 +67,17 @@ public String getImageUrl(){
public List getWishListEntities() {
return wishListEntities;
}
+
+ public CategoryEntity getCategory() {return category;}
+
+ public static ProductDTO toDTO(ProductEntity productEntity) {
+ return new ProductDTO(
+ productEntity.getId(),
+ productEntity.getName(),
+ productEntity.getPrice(),
+ productEntity.getImageUrl(),
+ productEntity.getCategory().getId()
+ );
+ }
+
}
\ No newline at end of file
diff --git a/src/main/java/gift/entity/WishListEntity.java b/src/main/java/gift/entity/WishListEntity.java
index 58abd5322..1e6c4b3e8 100644
--- a/src/main/java/gift/entity/WishListEntity.java
+++ b/src/main/java/gift/entity/WishListEntity.java
@@ -1,5 +1,6 @@
package gift.entity;
+import gift.domain.WishListDTO;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
@@ -37,4 +38,8 @@ public ProductEntity getProductEntity() {
public MemberEntity getUserEntity() {
return userEntity;
}
+
+ public static WishListDTO toDTO(WishListEntity wishListEntity) {
+ return new WishListDTO(wishListEntity.getProductEntity().getId(), wishListEntity.getUserEntity().getId());
+ }
}
diff --git a/src/main/java/gift/repository/CategoryRepository.java b/src/main/java/gift/repository/CategoryRepository.java
new file mode 100644
index 000000000..118aa72fa
--- /dev/null
+++ b/src/main/java/gift/repository/CategoryRepository.java
@@ -0,0 +1,10 @@
+package gift.repository;
+
+import gift.entity.CategoryEntity;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface CategoryRepository extends JpaRepository {
+
+}
diff --git a/src/main/java/gift/service/CategoryService.java b/src/main/java/gift/service/CategoryService.java
new file mode 100644
index 000000000..80541326c
--- /dev/null
+++ b/src/main/java/gift/service/CategoryService.java
@@ -0,0 +1,58 @@
+package gift.service;
+
+import gift.domain.CategoryDTO;
+import gift.entity.CategoryEntity;
+import gift.repository.CategoryRepository;
+import jakarta.transaction.Transactional;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class CategoryService {
+ private final CategoryRepository categoryRepository;
+
+ @Autowired
+ public CategoryService(CategoryRepository categoryRepository) {
+ this.categoryRepository = categoryRepository;
+ }
+
+ // 카테고리 전체 조회
+ public List getCategories() {
+ List categories = categoryRepository.findAll();
+ return categories.stream().map(CategoryEntity::toDTO).collect(Collectors.toList());
+ }
+
+ // 카테고리 단일 조회
+ public CategoryDTO getCategory(Long id) {
+ CategoryEntity category = categoryRepository.findById(id)
+ .orElseThrow(() -> new RuntimeException("카테고리를 찾을 수 없습니다"));
+ return category.toDTO(category);
+ }
+
+ // 카테고리 생성
+ @Transactional
+ public CategoryDTO createCategory(CategoryDTO categoryDTO) {
+ CategoryEntity category = new CategoryEntity(categoryDTO);
+ CategoryEntity newCategory = categoryRepository.save(category);
+ return CategoryEntity.toDTO(newCategory);
+ }
+
+ // 카테고리 수정
+ @Transactional
+ public CategoryDTO updateCategory(Long id, CategoryDTO categoryDTO) {
+ CategoryEntity category = categoryRepository.findById(id)
+ .orElseThrow(() -> new RuntimeException("카테고리를 찾을 수 없습니다"));
+
+ category.update(categoryDTO);
+ return category.toDTO(category);
+ }
+
+ // 카테고리 삭제
+ @Transactional
+ public void deleteCategory(Long id) {
+ categoryRepository.deleteById(id);
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/gift/service/MemberService.java b/src/main/java/gift/service/MemberService.java
index d8dc71124..acee93144 100644
--- a/src/main/java/gift/service/MemberService.java
+++ b/src/main/java/gift/service/MemberService.java
@@ -21,24 +21,24 @@ public MemberEntity authenticateToken(MemberDTO memberDTO) {
MemberEntity foundMember = memberRepository.findByEmail(memberDTO.getEmail());
if (foundMember == null || !memberDTO.getPassword().equals(foundMember.getPassword())) {
- return null;
+ throw new IllegalArgumentException("로그인 정보가 올바르지 않습니다.");
}
return foundMember;
}
@Transactional
- public MemberServiceStatus save(MemberDTO memberDTO) {
+ public void save(MemberDTO memberDTO) {
if (existsByEmail(memberDTO.getEmail())) {
- return MemberServiceStatus.EMAIL_ALREADY_EXISTS;
+ throw new IllegalArgumentException("이미 가입된 이메일입니다.");
}
// DTO to Entity
MemberEntity memberEntity = new MemberEntity(memberDTO.getEmail(), memberDTO.getPassword());
memberRepository.save(memberEntity);
- return MemberServiceStatus.SUCCESS;
}
+
public boolean existsByEmail(String email) {
return memberRepository.existsByEmail(email);
}
diff --git a/src/main/java/gift/service/MemberServiceStatus.java b/src/main/java/gift/service/MemberServiceStatus.java
deleted file mode 100644
index e90cac64c..000000000
--- a/src/main/java/gift/service/MemberServiceStatus.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package gift.service;
-
-public enum MemberServiceStatus {
- SUCCESS,
- EMAIL_ALREADY_EXISTS,
- NOT_FOUND,
- UNAUTHORIZED
-}
\ No newline at end of file
diff --git a/src/main/java/gift/service/ProductService.java b/src/main/java/gift/service/ProductService.java
index ff5226d86..cf79f3297 100644
--- a/src/main/java/gift/service/ProductService.java
+++ b/src/main/java/gift/service/ProductService.java
@@ -1,8 +1,11 @@
package gift.service;
+import gift.entity.CategoryEntity;
import gift.entity.ProductEntity;
import gift.domain.ProductDTO;
+import gift.repository.CategoryRepository;
import gift.repository.ProductRepository;
+import jakarta.persistence.EntityNotFoundException;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
@@ -17,86 +20,71 @@ public class ProductService {
@Autowired
private ProductRepository productRepository;
- private ProductDTO toProductDTO(ProductEntity productEntity) {
- return new ProductDTO(
- productEntity.getId(),
- productEntity.getName(),
- productEntity.getPrice(),
- productEntity.getImageUrl()
- );
- }
-
- private ProductEntity toProductEntity(ProductDTO productDTO) {
- return new ProductEntity(
- productDTO.getId(),
- productDTO.getName(),
- productDTO.getPrice(),
- productDTO.getImageUrl()
- );
- }
+ @Autowired
+ private CategoryRepository categoryRepository;
public List getAllProducts() {
List response = productRepository.findAll();
return response.stream()
- .map(this::toProductDTO)
+ .map(ProductEntity::toDTO)
.collect(Collectors.toList());
}
public Page getAllProducts(Pageable pageable) {
Page productEntities = productRepository.findAll(pageable);
- return productEntities.map(this::toProductDTO);
+ return productEntities.map(ProductEntity::toDTO);
}
// Read(단일 상품) - getProduct()
public Optional getProduct(Long id) {
return productRepository.findById(id)
- .map(this::toProductDTO);
+ .map(ProductEntity::toDTO);
}
// Create(생성) - addProduct()
@Transactional
- public ProductServiceStatus createProduct(ProductDTO productDTO) {
- try {
- ProductEntity productEntity = toProductEntity(productDTO);
- productRepository.save(productEntity);
- return ProductServiceStatus.SUCCESS;
- } catch (Exception e) {
- return ProductServiceStatus.ERROR;
- }
+ public void createProduct(ProductDTO productDTO) {
+ CategoryEntity category = categoryRepository.findById(productDTO.getCategoryId())
+ .orElseThrow(() -> new EntityNotFoundException("해당 카테고리가 존재하지 않습니다."));
+
+ ProductEntity productEntity = new ProductEntity(
+ productDTO.getName(),
+ productDTO.getPrice(),
+ productDTO.getImageUrl(),
+ category
+ );
+ productRepository.save(productEntity);
}
// Update(수정) - updateProduct()
@Transactional
- public ProductServiceStatus editProduct(Long id, ProductDTO productDTO) {
- try {
- Optional existingProductEntityOptional = productRepository.findById(id);
- if (!existingProductEntityOptional.isPresent()) {
- return ProductServiceStatus.NOT_FOUND;
- }
- ProductEntity existingProductEntity = existingProductEntityOptional.get();
- ProductEntity updatedProductEntity = new ProductEntity(
- existingProductEntity.getId(),
- productDTO.getName(),
- productDTO.getPrice(),
- productDTO.getImageUrl()
- );
- productRepository.save(updatedProductEntity);
- return ProductServiceStatus.SUCCESS;
- } catch (Exception e) {
- return ProductServiceStatus.ERROR;
- }
+ public void editProduct(Long id, ProductDTO productDTO) {
+ ProductEntity existingProductEntity = productRepository.findById(id)
+ .orElseThrow(() -> new EntityNotFoundException("해당 ID의 상품을 찾을 수 없습니다"));
+
+ CategoryEntity categoryEntity = categoryRepository.findById(productDTO.getCategoryId())
+ .orElseThrow(() -> new EntityNotFoundException("해당 ID의 카테고리를 찾을 수 없습니다."));
+
+ ProductEntity updatedProductEntity = new ProductEntity(
+ existingProductEntity.getId(), // 기존 ID 유지
+ productDTO.getName(),
+ productDTO.getPrice(),
+ productDTO.getImageUrl(),
+ categoryEntity
+ );
+ productRepository.save(updatedProductEntity);
}
@Transactional
- public ProductServiceStatus deleteProduct(Long id) {
+ public void deleteProduct(Long id) {
+ if (!productRepository.existsById(id)) {
+ throw new EntityNotFoundException("해당 ID의 상품을 찾을 수 없습니다");
+ }
+
try {
- if (productRepository.existsById(id)) {
- productRepository.deleteById(id);
- return ProductServiceStatus.SUCCESS;
- }
- return ProductServiceStatus.NOT_FOUND;
+ productRepository.deleteById(id);
} catch (Exception e) {
- return ProductServiceStatus.ERROR;
+ throw new RuntimeException("상품 삭제 중 오류가 발생했습니다", e);
}
}
}
\ No newline at end of file
diff --git a/src/main/java/gift/service/ProductServiceStatus.java b/src/main/java/gift/service/ProductServiceStatus.java
deleted file mode 100644
index 6ebd985a8..000000000
--- a/src/main/java/gift/service/ProductServiceStatus.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package gift.service;
-
-public enum ProductServiceStatus {
- SUCCESS,
- NOT_FOUND,
- ERROR
-}
-
diff --git a/src/main/java/gift/service/WishListService.java b/src/main/java/gift/service/WishListService.java
index 0b498a693..311243857 100644
--- a/src/main/java/gift/service/WishListService.java
+++ b/src/main/java/gift/service/WishListService.java
@@ -6,10 +6,10 @@
import gift.entity.WishListEntity;
import gift.domain.WishListDTO;
import gift.repository.WishListRepository;
+import jakarta.persistence.EntityNotFoundException;
import java.util.Optional;
import gift.repository.MemberRepository;
import gift.repository.ProductRepository;
-import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
@@ -32,23 +32,19 @@ public WishListService(WishListRepository wishListRepository,
this.memberRepository = memberRepository;
}
- private WishListDTO toWishListDTO(WishListEntity wishListEntity) {
- return new WishListDTO(wishListEntity.getProductEntity().getId(), wishListEntity.getUserEntity().getId());
- }
-
private WishListEntity dtoToEntity(Long userId, ProductDTO product) throws Exception {
MemberEntity memberEntity = memberRepository.findById(userId)
- .orElseThrow(() -> new Exception("유저가 존재하지 않습니다."));
+ .orElseThrow(() -> new EntityNotFoundException("유저가 존재하지 않습니다."));
ProductEntity productEntity = productRepository.findById(product.getId())
- .orElseThrow(() -> new Exception("상품이 존재하지 않습니다."));
+ .orElseThrow(() -> new EntityNotFoundException("상품이 존재하지 않습니다."));
return new WishListEntity(productEntity, memberEntity);
}
public Page readWishList(Long userId, Pageable pageable) {
Page wishListEntities = wishListRepository.findByUserEntity_Id(userId, pageable);
- return wishListEntities.map(this::toWishListDTO);
+ return wishListEntities.map(WishListEntity::toDTO);
}
@Transactional
diff --git a/src/test/java/gift/repository/ProductRepositoryTest.java b/src/test/java/gift/repository/ProductRepositoryTest.java
index bb136f7ad..bb994c965 100644
--- a/src/test/java/gift/repository/ProductRepositoryTest.java
+++ b/src/test/java/gift/repository/ProductRepositoryTest.java
@@ -1,6 +1,8 @@
package gift.repository;
+import gift.entity.CategoryEntity;
import gift.entity.ProductEntity;
+import jakarta.transaction.Transactional;
import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
@@ -8,8 +10,9 @@
import static org.junit.jupiter.api.Assertions.*;
+@Transactional
@DataJpaTest
-@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.ANY)
+//@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.ANY)
class ProductRepositoryTest {
@Autowired
@@ -18,7 +21,7 @@ class ProductRepositoryTest {
@Test
void testSaveProduct() {
// given
- ProductEntity productEntity = new ProductEntity("아이스 아메리카노", 1000, "http://test.com/image.jpg");
+ ProductEntity productEntity = new ProductEntity("아이스 아메리카노", 1000, "http://test.com/image.jpg",new CategoryEntity());
// when
ProductEntity savedProduct = productRepository.save(productEntity);
@@ -31,7 +34,7 @@ void testSaveProduct() {
@Test
void testFindProductById() {
// given
- ProductEntity productEntity = new ProductEntity("아이스 아메리카노", 1000, "http://test.com/image.jpg");
+ ProductEntity productEntity = new ProductEntity("아이스 아메리카노", 1000, "http://test.com/image.jpg",new CategoryEntity());
ProductEntity savedProduct = productRepository.save(productEntity);
// when
@@ -45,7 +48,7 @@ void testFindProductById() {
@Test
void testDeleteProduct() {
// given
- ProductEntity productEntity = new ProductEntity("아이스 아메리카노", 1000, "http://test.com/image.jpg");
+ ProductEntity productEntity = new ProductEntity("아이스 아메리카노", 1000, "http://test.com/image.jpg",new CategoryEntity());
ProductEntity savedProduct = productRepository.save(productEntity);
// when
diff --git a/src/test/java/gift/repository/WishListRepositoryTest.java b/src/test/java/gift/repository/WishListRepositoryTest.java
new file mode 100644
index 000000000..a05225c74
--- /dev/null
+++ b/src/test/java/gift/repository/WishListRepositoryTest.java
@@ -0,0 +1,69 @@
+package gift.repository;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import gift.entity.CategoryEntity;
+import gift.entity.MemberEntity;
+import gift.entity.ProductEntity;
+import gift.entity.WishListEntity;
+import jakarta.transaction.Transactional;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+
+import java.util.Optional;
+
+@Transactional
+@DataJpaTest
+public class WishListRepositoryTest {
+
+ @Autowired
+ private WishListRepository wishListRepository;
+
+ @Autowired
+ private CategoryRepository categoryRepository;
+
+ @Autowired
+ private MemberRepository memberRepository;
+
+ @Autowired
+ private ProductRepository productRepository;
+
+ private MemberEntity user;
+ private ProductEntity product;
+ private WishListEntity wishListEntity;
+
+ @BeforeEach
+ void setUp() {
+ CategoryEntity category = new CategoryEntity("Blue", "color", "https://example.com/image.png", "New Category");
+ categoryRepository.save(category);
+
+ product = new ProductEntity("아이스티", 3000, "https://gift-s.kakaocdn.net/dn/gift/images/m640/dimm_theme.png", category);
+ productRepository.save(product);
+
+ user = new MemberEntity("admin@gmail.com", "password");
+ memberRepository.save(user);
+
+ wishListEntity = new WishListEntity(product, user);
+ wishListRepository.save(wishListEntity);
+ }
+
+ @Test
+ void testFindByUserEntity_IdAndProductEntity_Id() {
+ Optional wishEntity = wishListRepository.findByUserEntity_IdAndProductEntity_Id(user.getId(), product.getId());
+ assertThat(wishEntity).isPresent();
+ assertThat(wishEntity.get().getUserEntity().getId()).isEqualTo(user.getId());
+ assertThat(wishEntity.get().getProductEntity().getId()).isEqualTo(product.getId());
+ }
+
+ @Test
+ void testFindByUserEntity_Id() {
+ Pageable pageable = PageRequest.of(0, 10);
+ Page wishEntities = wishListRepository.findByUserEntity_Id(user.getId(), pageable);
+ assertThat(wishEntities).isNotEmpty();
+ }
+}
\ No newline at end of file