-
Notifications
You must be signed in to change notification settings - Fork 0
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
내 장바구니에는 농담곰 100개를 넣을거야 #8
base: main
Are you sure you want to change the base?
Changes from 26 commits
7f1e545
8f50529
3a0b3f8
41fe890
fa370e7
ed53a6b
db9d90b
228e49d
a492491
f955e81
cf85243
8d26c34
50f2d2b
d3fcaad
55ec7c5
fc56438
31ce858
fdd28a6
39805cc
2815731
1b7de2d
195e2f1
02cf75b
14caa3d
2238a07
dd84b6c
b53fb4a
fc919cb
cee8d87
f3e9e2a
ff52547
0a2cec8
74d207d
bc4c973
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,41 @@ | ||
# jwp-shopping-cart | ||
|
||
## 기능 목록 | ||
### step1 | ||
- [x] 상품 목록 페이지 연동 | ||
- [x] index.html 파일 내 TODO 주석을 참고하여 설계한 상품 정보에 맞게 코드를 변경 | ||
- [x] index.html 파일을 이용하여 상품 목록이 노출되는 페이지를 완성 | ||
- [x] '/' url로 접근할 경우 상품 목록 페이지를 조회 | ||
- [x] 상품 기본 정보 : 상품 ID, 상품 이름, 상품 이미지, 상품 가격 | ||
|
||
- [x] 상품 관리 CRUD API 작성 | ||
- [x] Create : POST - /admin | ||
- [x] Read : GET - /admin | ||
- [x] Update : POST - /admin/{id} | ||
- [x] Delete : DELETE - /admin/{id} | ||
|
||
- [x] 관리자 도구 페이지 연동 | ||
- [x] admin.html, admin.js 파일 내 TODO 주석을 참고하여 코드를 변경 | ||
- [x] admin.html 파일과 상품 관리 CRUD API를 이용하여 상품 관리 페이지를 완성 | ||
- [x] '/admin' url로 접근할 경우 관리자 도구 페이지를 조회 | ||
|
||
### step2 | ||
- [x] 사용자 기능 구현 | ||
- [x] 사용자가 가지고 있는 정보 : email, password | ||
|
||
- [x] 사용자 설정 페이지 연동 | ||
- [x] settings.html, settings.js 파일 내 TODO 주석을 참고하여 설계한 사용자 정보에 맞게 코드를 변경 | ||
- [x] settings.html 파일을 이용해서 사용자를 선택하는 기능을 구현 | ||
- [x] /settings url로 접근할 경우 모든 사용자의 정보를 확인하고 사용자를 선택 가능 | ||
|
||
- [x] 인증 관련 | ||
- [x] 사용자 설정 페이지에서 사용자를 선택하면, 이후 요청에 선택한 사용자의 인증 정보가 포함 | ||
- [x] 사용자 정보는 요청 Header의 Authorization 필드를 사용해 인증 처리를 하여 획득 | ||
- [x] 인증 방식은 Basic 인증 사용 | ||
|
||
- [x] 장바구니 기능 구현 | ||
- [x] 장바구니에 상품 추가 | ||
- [x] 장바구니에 담긴 상품 제거 | ||
- [x] 장바구니 목록 조회 | ||
|
||
- [x] 장바구니 페이지 연동 | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package cart.controller; | ||
|
||
import cart.dto.CartProductAddRequest; | ||
import cart.dto.CartProductRemoveRequest; | ||
import cart.dto.ProductDto; | ||
import cart.service.CartService; | ||
import java.util.List; | ||
import javax.servlet.http.HttpServletRequest; | ||
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.PostMapping; | ||
import org.springframework.web.bind.annotation.RequestBody; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
@RestController | ||
@RequestMapping("/carts") | ||
public class CartController { | ||
|
||
private final CartService cartService; | ||
|
||
public CartController(CartService cartService) { | ||
this.cartService = cartService; | ||
} | ||
|
||
@GetMapping() | ||
public ResponseEntity<List<ProductDto>> findAllProductInCart(HttpServletRequest request) { | ||
int memberId = (int) request.getAttribute("memberId"); | ||
|
||
List<ProductDto> allProduct = cartService.findAllProduct(memberId); | ||
return ResponseEntity.status(HttpStatus.OK).body(allProduct); | ||
} | ||
|
||
@PostMapping() | ||
public ResponseEntity<Object> addProductToCart(HttpServletRequest request, | ||
@RequestBody CartProductAddRequest cartProductAddRequest) { | ||
int memberId = (int) request.getAttribute("memberId"); | ||
|
||
cartService.addProduct(memberId, cartProductAddRequest.getProductId()); | ||
return ResponseEntity.status(HttpStatus.CREATED).build(); | ||
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.
|
||
} | ||
|
||
@DeleteMapping() | ||
public ResponseEntity<Object> removeProductFromCart(HttpServletRequest request, | ||
@RequestBody CartProductRemoveRequest cartProductRemoveRequest) { | ||
int memberId = (int) request.getAttribute("memberId"); | ||
|
||
cartService.deleteProduct(memberId, cartProductRemoveRequest.getProductId()); | ||
return ResponseEntity.status(HttpStatus.NO_CONTENT).build(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package cart.controller; | ||
|
||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.HttpRequestMethodNotSupportedException; | ||
import org.springframework.web.bind.MethodArgumentNotValidException; | ||
import org.springframework.web.bind.annotation.ControllerAdvice; | ||
import org.springframework.web.bind.annotation.ExceptionHandler; | ||
|
||
@ControllerAdvice | ||
public class CartControllerAdvice { | ||
|
||
public static final String SERVER_ERROR = "알 수 없는 에러가 발생했습니다."; | ||
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.
|
||
private Logger log = LoggerFactory.getLogger(getClass()); | ||
|
||
@ExceptionHandler(MethodArgumentNotValidException.class) | ||
public ResponseEntity<String> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { | ||
String errorMessage = e.getBindingResult().getAllErrors().get(0).getDefaultMessage(); | ||
log.info(errorMessage); | ||
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorMessage); | ||
} | ||
|
||
@ExceptionHandler(HttpRequestMethodNotSupportedException.class) | ||
public ResponseEntity<String> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) { | ||
log.info(e.getMessage()); | ||
return ResponseEntity.status(HttpStatus.METHOD_NOT_ALLOWED).body(e.getMessage()); | ||
} | ||
|
||
@ExceptionHandler(IllegalArgumentException.class) | ||
public ResponseEntity<String> handleIllegalArgumentException(IllegalArgumentException e) { | ||
log.info(e.getMessage()); | ||
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage()); | ||
} | ||
Comment on lines
+18
to
+35
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. 예외의 대한 로깅은 info보다 warn으로 조정해보는건 어떨까? |
||
|
||
@ExceptionHandler(Exception.class) | ||
public ResponseEntity<String> handleServerException(Exception e) { | ||
log.error(e.getMessage()); | ||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(SERVER_ERROR); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package cart.controller; | ||
|
||
import cart.dto.ProductAddRequest; | ||
import cart.dto.ProductModifyRequest; | ||
import cart.service.ProductService; | ||
import java.net.URI; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.validation.annotation.Validated; | ||
import org.springframework.web.bind.annotation.DeleteMapping; | ||
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("/products") | ||
public class ProductController { | ||
|
||
private final ProductService productService; | ||
|
||
public ProductController(ProductService productService) { | ||
this.productService = productService; | ||
} | ||
|
||
@PostMapping() | ||
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 ResponseEntity<Integer> productAdd(@Validated @RequestBody ProductAddRequest productAddRequest) { | ||
int productId = productService.save(productAddRequest); | ||
return ResponseEntity.status(HttpStatus.CREATED).location(URI.create("/products/" + productId)).build(); | ||
} | ||
|
||
@PutMapping("/{id}") | ||
public ResponseEntity<Object> productModify(@Validated @RequestBody ProductModifyRequest productModifyRequest, | ||
@PathVariable int id) { | ||
productService.update(productModifyRequest, id); | ||
return ResponseEntity.status(HttpStatus.OK).build(); | ||
} | ||
|
||
@DeleteMapping("/{id}") | ||
public ResponseEntity<Object> productRemove(@PathVariable int id) { | ||
productService.delete(id); | ||
return ResponseEntity.status(HttpStatus.NO_CONTENT).build(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package cart.controller; | ||
|
||
import cart.dto.MemberDto; | ||
import cart.dto.ProductDto; | ||
import cart.service.MemberService; | ||
import cart.service.ProductService; | ||
import java.util.List; | ||
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("/") | ||
public class ViewController { | ||
|
||
private final ProductService productService; | ||
private final MemberService memberService; | ||
|
||
public ViewController(ProductService productService, MemberService memberService) { | ||
this.productService = productService; | ||
this.memberService = memberService; | ||
} | ||
|
||
@GetMapping() | ||
public String home(Model model) { | ||
List<ProductDto> products = productService.findAll(); | ||
model.addAttribute("products", products); | ||
return "index"; | ||
} | ||
|
||
@GetMapping("admin") | ||
public String productList(Model model) { | ||
List<ProductDto> products = productService.findAll(); | ||
model.addAttribute("products", products); | ||
return "admin"; | ||
} | ||
|
||
@RequestMapping("settings") | ||
public String userSetting(Model model) { | ||
List<MemberDto> members = memberService.findAll(); | ||
model.addAttribute("members", members); | ||
return "settings"; | ||
} | ||
|
||
@RequestMapping("cart") | ||
public String cartList() { | ||
return "cart"; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package cart.dao; | ||
|
||
import cart.entity.ProductEntity; | ||
import java.util.List; | ||
import org.springframework.jdbc.core.BeanPropertyRowMapper; | ||
import org.springframework.jdbc.core.JdbcTemplate; | ||
import org.springframework.stereotype.Repository; | ||
|
||
@Repository | ||
public class CartDao { | ||
|
||
private final JdbcTemplate jdbcTemplate; | ||
|
||
public CartDao(JdbcTemplate jdbcTemplate) { | ||
this.jdbcTemplate = jdbcTemplate; | ||
} | ||
|
||
public void save(int memberId, int productId) { | ||
String sql = "INSERT INTO cart (member_id, product_id) VALUES (?, ?)"; | ||
jdbcTemplate.update(sql, memberId, productId); | ||
} | ||
|
||
public List<ProductEntity> findAllByMemberId(int memberId) { | ||
String sql = "SELECT id, p.name, p.imgUrl, p.price\n" | ||
+ "FROM cart AS c\n" | ||
+ "INNER JOIN product AS p\n" | ||
+ "ON c.product_id = p.id\n" | ||
+ "WHERE member_id = ?;"; | ||
BeanPropertyRowMapper<ProductEntity> mapper = BeanPropertyRowMapper.newInstance(ProductEntity.class); | ||
return jdbcTemplate.query(sql, mapper, memberId); | ||
} | ||
|
||
public void delete(int memberId, int productId) { | ||
String sql = "DELETE FROM cart WHERE member_id = ? AND product_id = ?"; | ||
jdbcTemplate.update(sql, memberId, productId); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package cart.dao; | ||
|
||
import cart.entity.MemberEntity; | ||
import java.util.List; | ||
import java.util.Optional; | ||
import org.springframework.dao.EmptyResultDataAccessException; | ||
import org.springframework.jdbc.core.BeanPropertyRowMapper; | ||
import org.springframework.jdbc.core.JdbcTemplate; | ||
import org.springframework.stereotype.Repository; | ||
|
||
@Repository | ||
public class MemberDao { | ||
|
||
private final JdbcTemplate jdbcTemplate; | ||
|
||
public MemberDao(JdbcTemplate jdbcTemplate) { | ||
this.jdbcTemplate = jdbcTemplate; | ||
} | ||
|
||
public List<MemberEntity> findAll() { | ||
String sql = "SELECT * FROM member"; | ||
BeanPropertyRowMapper<MemberEntity> mapper = BeanPropertyRowMapper.newInstance(MemberEntity.class); | ||
return jdbcTemplate.query(sql, mapper); | ||
} | ||
|
||
public Optional<MemberEntity> findByEmailAndPassword(String email, String password) { | ||
String sql = "SELECT * FROM member WHERE email = ? AND password = ?"; | ||
BeanPropertyRowMapper<MemberEntity> mapper = BeanPropertyRowMapper.newInstance(MemberEntity.class); | ||
try { | ||
return Optional.ofNullable(jdbcTemplate.queryForObject(sql, mapper, email, password)); | ||
} catch (final EmptyResultDataAccessException e) { | ||
return Optional.empty(); | ||
} | ||
Comment on lines
+29
to
+33
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. 메서드로 분리하면 이쁠거 같아용 |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
package cart.dao; | ||
|
||
import cart.entity.ProductEntity; | ||
import java.util.List; | ||
import java.util.Optional; | ||
import org.springframework.dao.EmptyResultDataAccessException; | ||
import org.springframework.jdbc.core.BeanPropertyRowMapper; | ||
import org.springframework.jdbc.core.JdbcTemplate; | ||
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource; | ||
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; | ||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; | ||
import org.springframework.jdbc.core.namedparam.SqlParameterSource; | ||
import org.springframework.jdbc.support.GeneratedKeyHolder; | ||
import org.springframework.stereotype.Repository; | ||
|
||
@Repository | ||
public class ProductDao { | ||
|
||
private final JdbcTemplate jdbcTemplate; | ||
private final NamedParameterJdbcTemplate namedParameterJdbcTemplate; | ||
|
||
public ProductDao(JdbcTemplate jdbcTemplate) { | ||
this.jdbcTemplate = jdbcTemplate; | ||
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate); | ||
} | ||
|
||
public int save(ProductEntity productEntity) { | ||
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. CartDao에서 save는 void였는데 여기는 int여서 통일시키는것에 대해서는 어떻게 생각하시나요? |
||
GeneratedKeyHolder keyholder = new GeneratedKeyHolder(); | ||
String sql = "INSERT INTO product (name, imgUrl, price) VALUES (:name, :imgUrl, :price)"; | ||
SqlParameterSource namedParameters = new BeanPropertySqlParameterSource(productEntity); | ||
namedParameterJdbcTemplate.update(sql, namedParameters, keyholder, new String[]{"id"}); | ||
|
||
return keyholder.getKey().intValue(); | ||
} | ||
|
||
public Optional<ProductEntity> findById(int id) { | ||
String sql = "SELECT * FROM product WHERE id = ?"; | ||
BeanPropertyRowMapper<ProductEntity> mapper = BeanPropertyRowMapper.newInstance(ProductEntity.class); | ||
try { | ||
return Optional.ofNullable(jdbcTemplate.queryForObject(sql, mapper, id)); | ||
} catch (final EmptyResultDataAccessException e) { | ||
return Optional.empty(); | ||
} | ||
} | ||
|
||
public List<ProductEntity> findAll() { | ||
String sql = "SELECT * FROM product"; | ||
BeanPropertyRowMapper<ProductEntity> mapper = BeanPropertyRowMapper.newInstance(ProductEntity.class); | ||
return jdbcTemplate.query(sql, mapper); | ||
} | ||
|
||
public void update(ProductEntity productEntity) { | ||
String sql = "UPDATE product SET name=:name, imgUrl=:imgUrl, price=:price WHERE id=:id"; | ||
SqlParameterSource namedParameters = new BeanPropertySqlParameterSource(productEntity); | ||
namedParameterJdbcTemplate.update(sql, namedParameters); | ||
} | ||
|
||
public void delete(int id) { | ||
String sql = "DELETE FROM product WHERE id=:id"; | ||
MapSqlParameterSource mapSqlParameters = new MapSqlParameterSource() | ||
.addValue("id", id); | ||
namedParameterJdbcTemplate.update(sql, mapSqlParameters); | ||
} | ||
} |
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.
기능 목록 곰곰 👍