-
Notifications
You must be signed in to change notification settings - Fork 122
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
경북대 BE_김동현_1주차 과제(3단계) #201
base: donghyuun
Are you sure you want to change the base?
Changes from 40 commits
902b824
4e8d336
b8a1e9b
1bb950d
ddd17ee
5cdd9db
9e860bf
d6fcb53
5e3b595
22320ce
b260e4a
c937c17
ceb052a
29d8beb
7d6874f
5a6d0d3
35718a5
93d2b81
43d6adc
e104fac
b99a57c
19f0229
0eafb22
60171d0
a446935
304c9db
0c195c7
e1710be
fd7f6a5
9f832a2
f3c8fc5
0997aa1
7af869e
8012427
37ff02d
adb5221
f1ba685
d67b2a9
ca31aa2
fb8bbb8
96a9416
c633d3e
5a61b96
682473e
9e8fa45
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,9 @@ | ||
# spring-gift-product | ||
# spring-gift-product | ||
|
||
## 기능 목록 | ||
- 상품 추가 기능 | ||
- 상품 조회 기능 | ||
- 상품 수정 기능 | ||
- 상품 삭제 기능 | ||
- 상품 테스트 코드 작성 | ||
- 리팩토링 |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,37 @@ | ||||||
package gift.Controller; | ||||||
|
||||||
import gift.Service.ProductService; | ||||||
import gift.Model.Product; | ||||||
import java.util.List; | ||||||
import java.util.Map; | ||||||
import org.springframework.beans.factory.annotation.Autowired; | ||||||
import org.springframework.stereotype.Controller; | ||||||
import org.springframework.ui.Model; | ||||||
import org.springframework.web.bind.annotation.GetMapping; | ||||||
import org.springframework.web.bind.annotation.RequestMapping; | ||||||
|
||||||
@Controller | ||||||
@RequestMapping(value = "/api") | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 별건 아니지만 줄일 수 있는 부분은 줄이는 것이 좋은 것 같아요ㅎㅎ
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 넵 반영하겠습니다! |
||||||
public class BasicController { | ||||||
|
||||||
private final ProductService productService; | ||||||
|
||||||
@Autowired | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 생성자가 1개인 경우에는 @Autowired 빼셔도 됩니다! 알고 계시겠지만 혹시나 하여 남깁니다! 🙏
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 넵 알겠습니다! |
||||||
public BasicController(ProductService productService) { | ||||||
this.productService = productService; | ||||||
} | ||||||
|
||||||
/** | ||||||
* 홈 화면으로 이동, 상품 목록 넘겨줌 | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이러한 주석은 코드와 강결합된 주석이라서요ㅎㅎ 코드가 바뀜에 따라 변경되지 않을 확률이 매우 높습니다! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 멘토님 말씀 듣고 생각해보니 추상화 시키는 것이 좋은 것 같습니다! |
||||||
* | ||||||
* @param model | ||||||
* @return 홈 화면 html 명 | ||||||
*/ | ||||||
@GetMapping | ||||||
String homePage(Model model) { | ||||||
// 현재 상품 목록 조회 | ||||||
List<Product> products = productService.getProducts(); | ||||||
model.addAttribute("products", products); | ||||||
return "index"; | ||||||
} | ||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
package gift.Controller; | ||
|
||
import gift.Global.Response.ErrorCode; | ||
import gift.Global.Response.ErrorResponseDto; | ||
import gift.Global.Response.ResponseMaker; | ||
import gift.Global.Response.ResultCode; | ||
import gift.Global.Response.ResultResponseDto; | ||
import gift.Global.Response.SimpleResultResponseDto; | ||
import gift.Service.ProductService; | ||
import gift.DTO.ProductDTO; | ||
import gift.Model.Product; | ||
import jakarta.validation.Valid; | ||
import java.util.List; | ||
import java.util.Map; | ||
import javax.swing.text.StyledEditorKit.BoldAction; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.stereotype.Controller; | ||
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.RequestBody; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.ResponseBody; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
@RestController | ||
@RequestMapping(value = "/api/products") | ||
public class ProductController { | ||
|
||
private final ProductService productService; | ||
|
||
@Autowired | ||
public ProductController(ProductService productService) { | ||
this.productService = productService; | ||
} | ||
|
||
/** | ||
* 상품 추가 | ||
* | ||
* @param productDTO | ||
* @return 결과 메시지 | ||
*/ | ||
@PostMapping | ||
public ResponseEntity<SimpleResultResponseDto> postProduct(@Valid @ModelAttribute ProductDTO productDTO) { | ||
productService.postProduct(productDTO); | ||
return ResponseMaker.createSimpleResponse(ResultCode.CREATE_PRODUCT_SUCCESS); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 서로 다른 클래스를 왔다갔다 하면서 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 수정 사항으로 ResultCode 등의 Enum 에러 코드 사용하지 않도록 하였습니다. 모든 상황의 성공에 ok 를 붙이는 건 디테일 하지 않다고 생각하여 서비스or에러 핸들러 부분에서 응답 코드, 메시지를 매개변수로 입력하면 그에 맞게 응답 객체를 생성하도록 했습니다. pr 올리겠습니다ㅎㅎ |
||
} | ||
|
||
/** | ||
* 전체 상품 목록 조회 | ||
* | ||
* @return products (상품 목록) | ||
*/ | ||
@GetMapping | ||
public ResponseEntity<ResultResponseDto<List<Product>>> getProducts() { | ||
List<Product> products = productService.getProducts(); | ||
// 성공 시 | ||
return ResponseMaker.createResponse(ResultCode.GET_ALL_PRODUCTS_SUCCESS, products); | ||
} | ||
|
||
|
||
/** | ||
* 상품 수정 | ||
* | ||
* @param id | ||
* @param productDTO | ||
* @return 결과 메시지 | ||
*/ | ||
@PutMapping("/{id}") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 삭제와 함께 적절한 HttpMethod 사용 좋네요! 👍 |
||
public ResponseEntity<SimpleResultResponseDto> updateProduct(@PathVariable("id") Long id, | ||
@RequestBody ProductDTO productDTO) { | ||
productService.updateProduct(id, productDTO); | ||
return ResponseMaker.createSimpleResponse(ResultCode.UPDATE_PRODUCT_SUCCESS); | ||
} | ||
|
||
|
||
/** | ||
* 해당 ID 리스트에 속한 상품 삭제 | ||
* | ||
* @param productIds | ||
* @return 결과 메시지 | ||
*/ | ||
@DeleteMapping | ||
public ResponseEntity<?> deleteSelectedProducts(@RequestBody List<Long> productIds) { | ||
productService.deleteProductsByIds(productIds); | ||
return ResponseMaker.createSimpleResponse(ResultCode.DELETE_PRODUCTS_SUCCESS); | ||
} | ||
|
||
/** | ||
* 해당 ID 를 가진 상품 삭제 | ||
* | ||
* @param id | ||
* @return 결과 메시지 | ||
*/ | ||
@DeleteMapping("/{id}") | ||
public ResponseEntity<?> deleteProduct(@PathVariable("id") Long id) { | ||
productService.deleteProduct(id); | ||
return ResponseMaker.createSimpleResponse(ResultCode.DELETE_PRODUCT_SUCCESS); | ||
} | ||
|
||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package gift.DTO; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 자바에선 최근에 요러한 케이스에 대해 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 다른 분들의 코드도 살펴보니 그런 것 같습니다 반영하겠습니다! |
||
|
||
public class ProductDTO { | ||
|
||
private String name; | ||
private int price; | ||
private String imageUrl; | ||
|
||
public ProductDTO() { | ||
} | ||
|
||
public ProductDTO(String name, int price, String imageUrl) { | ||
this.name = name; | ||
this.price = price; | ||
this.imageUrl = imageUrl; | ||
} | ||
|
||
public String getName() { | ||
return name; | ||
} | ||
|
||
public void setName(String name) { | ||
this.name = name; | ||
} | ||
|
||
public int getPrice() { | ||
return price; | ||
} | ||
|
||
public void setPrice(int price) { | ||
this.price = price; | ||
} | ||
|
||
public String getImageUrl() { | ||
return imageUrl; | ||
} | ||
|
||
public void setImageUrl(String imageUrl) { | ||
this.imageUrl = imageUrl; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package gift.Global.Exception; | ||
|
||
import gift.Global.Response.ErrorCode; | ||
|
||
/** | ||
* RuntimeException 을 상속받는 커스텀 에러 | ||
* 개발자가 직접 날리는 에러 | ||
*/ | ||
public class BusinessException extends RuntimeException{ | ||
|
||
private final ErrorCode errorCode; | ||
|
||
public BusinessException(ErrorCode errorCode) { | ||
super(errorCode.getMessage()); | ||
this.errorCode = errorCode; | ||
} | ||
|
||
public ErrorCode getErrorCode() { | ||
return errorCode; | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package gift.Global.Handler; | ||
|
||
import gift.Global.Exception.BusinessException; | ||
import gift.Global.Response.ErrorCode; | ||
import gift.Global.Response.ErrorResponseDto; | ||
import gift.Global.Response.ResponseMaker; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.MethodArgumentNotValidException; | ||
import org.springframework.web.bind.annotation.ExceptionHandler; | ||
import org.springframework.web.bind.annotation.RestControllerAdvice; | ||
|
||
@RestControllerAdvice | ||
public class GlobalExceptionHandler { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 에러 핸들링 처리하신 부분 좋네요! 👍 |
||
|
||
/** | ||
* RuntimeException 을 상속받는 커스텀 에러 핸들러 | ||
* 개발자가 직접 날리는 에러 | ||
* @param e | ||
* @return | ||
*/ | ||
@ExceptionHandler(BusinessException.class) | ||
public ResponseEntity<ErrorResponseDto> handleBusinessException(BusinessException e) { | ||
return ResponseMaker.createErrorResponse(e.getErrorCode()); | ||
} | ||
|
||
|
||
/** | ||
* MethodArgumentNotValidException 에러 핸들러 | ||
* 매개변수 인자가 유효하지 않은 경우 발생 | ||
* @param e | ||
* @return | ||
*/ | ||
@ExceptionHandler(MethodArgumentNotValidException.class) | ||
public ResponseEntity<ErrorResponseDto> MethodArgumentNotValidException( | ||
MethodArgumentNotValidException e) { | ||
return ResponseMaker.createErrorResponse(ErrorCode.INVALID_PRODUCT_ARGUMENT); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package gift.Global.Response; | ||
|
||
public enum ErrorCode { | ||
|
||
// Product CRUD 동작 | ||
GET_ALL_PRODUCTS_FAILED(400, "PE001", "전체 상품 목록 조회에 실패했습니다."), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 상품 외에 다른 도메인이 추가된다면 서로 다른 도메인 간의 결합이 해당 클래스 안에서 일어날 것 같네요ㅎㅎ 추후에 분리를 염두해두면 좋을 것 같습니다! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 추가로 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이 부분의 수정 사항에 대해 pr 요청 시 설명 기입해놓겠습니다! |
||
CREATE_PRODUCT_FAILED(400, "PE002", "상품 추가에 실패했습니다."), | ||
UPDATE_PRODUCT_FAILED(400, "PE003", "상품 수정에 실패했습니다."), | ||
DELETE_PRODUCT_FAILED(400, "PE004", "상품 삭제에 실패했습니다."), | ||
DELETE_PRODUCTS_FAILED(400, "PE005", "상품들을 삭제에 실패했습니다."), | ||
|
||
// 값 유효성 검사 | ||
INVALID_PRODUCT_ARGUMENT(400, "VE001", "상품 입력 정보가 유효하지 않습니다."), | ||
|
||
; | ||
; | ||
private final int status; | ||
private final String code; | ||
private final String message; | ||
|
||
ErrorCode(int status, String code, String message) { | ||
this.status = status; | ||
this.code = code; | ||
this.message = message; | ||
} | ||
|
||
public int getStatus() { | ||
return status; | ||
} | ||
|
||
public String getCode() { | ||
return code; | ||
} | ||
|
||
public String getMessage() { | ||
return message; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package gift.Global.Response; | ||
|
||
public record ErrorResponseDto(String code, String message) { | ||
public ErrorResponseDto(ErrorCode errorCode) { | ||
this(errorCode.getCode(), errorCode.getMessage()); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package gift.Global.Response; | ||
|
||
import org.springframework.http.ResponseEntity; | ||
|
||
/** | ||
* 응답 객체를 생성하는 클래스 | ||
*/ | ||
public class ResponseMaker { | ||
|
||
/** | ||
* BODY 에 성공 메시지와 데이터를 보냄 | ||
* @param resultCode | ||
* @param data | ||
* @return | ||
* @param <T> | ||
*/ | ||
public static <T> ResponseEntity<ResultResponseDto<T>> createResponse(ResultCode resultCode, T data) { | ||
ResultResponseDto<T> resultResponseDto = new ResultResponseDto<>(resultCode, data); | ||
return ResponseEntity.status(resultCode.getStatus()) | ||
.body(resultResponseDto); | ||
} | ||
|
||
/** | ||
* BODY 에 성공 메시지만 보냄 (데이터 X) | ||
* @param resultCode | ||
* @return | ||
*/ | ||
public static ResponseEntity<SimpleResultResponseDto> createSimpleResponse(ResultCode resultCode) { | ||
SimpleResultResponseDto simpleResultResponseDto = new SimpleResultResponseDto(resultCode); | ||
return ResponseEntity.status(resultCode.getStatus()) | ||
.body(simpleResultResponseDto); | ||
} | ||
|
||
/** | ||
* BODY 에 에러 메시지만 보냄 (데이터 X) | ||
* @param errorCode | ||
* @return | ||
*/ | ||
public static ResponseEntity<ErrorResponseDto> createErrorResponse(ErrorCode errorCode) { | ||
ErrorResponseDto errorResponseDto = new ErrorResponseDto(errorCode); | ||
return ResponseEntity.status(errorCode.getStatus()) | ||
.body(errorResponseDto); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오 validation 기능을 활용하셨군요 좋습니당! 👍 저도 좋아합니다!