-
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
장박2바스단구터계니 #12
base: main
Are you sure you want to change the base?
장박2바스단구터계니 #12
Changes from 57 commits
f9e0307
e648bf4
553f0b4
8562789
9bdc819
d6d5791
bfbe857
7136b50
ef488cf
4417703
735a52c
d6c477a
45db0d5
e0ebacc
4c293d3
7d238dd
bc28771
cc9f732
88a844e
23204b6
38f6d6e
4f73250
2d5de0c
79f6030
b21565c
dd3558c
c19b047
1cf3d23
68a62e1
4d9df77
9e311e1
b26bb07
8b1ca97
4d897d2
bebac97
c8641e8
783da48
b1e8ccc
16fea55
590a390
b4e4d55
9ad8cb0
32d3221
f30f6b0
764e7a3
b73a42a
226bb5b
f717690
77ca508
36f2ae9
0bc7551
b00f4a5
44b593a
6a3db19
4c80a76
660f85a
6642bf4
887acd7
73a8d5a
aee5a62
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,36 @@ | ||
# jwp-shopping-cart | ||
|
||
### 기능 목록 | ||
|
||
- [x] 상품 목록 페이지 연동 | ||
- [x] 상품 객체 생성 (ID, 이름, 이미지, 가격) | ||
- [x] `/`로 접근할 경우 상품 목록 페이지 조회 | ||
|
||
- [x] 상품 관리 CRUD API 작성 | ||
- [x] 상품 생성 | ||
- [x] 상품 목록 조회 | ||
- [x] 상품 수정 | ||
- [x] 상품 삭제 | ||
|
||
- [x] 관리자 도구 페이지 연동 | ||
- [x] `/admin`로 접근할 경우 전체 상품 조회 | ||
- [x] `상품 추가` 클릭 시 상품 생성 API 호출 | ||
- [x] `수정` 클릭 시 상품 수정 API 호출 | ||
- [x] `삭제` 클릭 시 상품 삭제 API 호출 | ||
|
||
- [ ] 사용자 기능 구현 | ||
- [x] 사용자 저장 | ||
- [x] `/settings`로 접근할 경우 전체 사용자 조회 | ||
- [ ] `select` 클릭 시 이후 요청에 사용자 인증 정보 포함 | ||
|
||
- [ ] 장바구니 기능 구현 | ||
- [ ] 사용자 인증 정보는 요청 header에 포함 | ||
- [ ] basic 인증 사용 | ||
- [ ] `담기` 클릭 시 장바구니에 추가 | ||
- [ ] `/cart`로 접근할 경우 전체 장바구니 목록 조회 | ||
- [ ] `delete`클릭 시 장바구니에서 제거 | ||
|
||
### 리팩터링 목록 | ||
|
||
- [x] 예외 처리 | ||
- [x] 전체 test 코드 작성 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package cart; | ||
|
||
import cart.dao.MemberDao; | ||
import cart.dao.ProductDao; | ||
import cart.entity.Member; | ||
import cart.entity.Product; | ||
import javax.annotation.PostConstruct; | ||
import org.springframework.stereotype.Component; | ||
|
||
@Component | ||
public class DbInit { | ||
|
||
private final ProductDao productDao; | ||
private final MemberDao memberDao; | ||
|
||
public DbInit(ProductDao productDao, MemberDao memberDao) { | ||
this.productDao = productDao; | ||
this.memberDao = memberDao; | ||
} | ||
|
||
@PostConstruct | ||
private void saveDummyData() { | ||
productDao.save(new Product( | ||
"피자", | ||
"https://cdn.dominos.co.kr/admin/upload/goods/20200311_x8StB1t3.jpg", | ||
13000)); | ||
productDao.save(new Product( | ||
"샐러드", | ||
"https://m.subway.co.kr/upload/menu/K-%EB%B0%94%EB%B9%84%ED%81%90-%EC%83%90%EB%9F%AC%EB%93%9C-%EB%8B%A8%ED%92%88_20220413025007802.png", | ||
20000)); | ||
productDao.save(new Product( | ||
"치킨", | ||
"https://cdn.thescoop.co.kr/news/photo/202010/41306_58347_1055.jpg", | ||
10000)); | ||
Comment on lines
+10
to
+34
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. data.sql 파일을 이용해서 초기값을 설정해보는건 어떨까? 🤔 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. 더미데이터 넣을 때 data.sql 안쓰고 |
||
|
||
memberDao.save(new Member( | ||
"[email protected]", | ||
"boxster" | ||
) | ||
); | ||
memberDao.save(new Member( | ||
"[email protected]", | ||
"member" | ||
) | ||
); | ||
} | ||
Comment on lines
+22
to
+46
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.
한편으로는 sql 작성에 비해 dao 구현에 의존적이라 dao 변경에 영향을 받을 수 있을 것 같은데 어떻게 생각해? 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. sql 은 sql에 의존적이라고 생각해서 이렇게 했어요~ 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,20 @@ | ||
package cart.auth; | ||
|
||
public class AuthMember { | ||
|
||
private final String email; | ||
private final String password; | ||
|
||
public AuthMember(String email, String password) { | ||
this.email = email; | ||
this.password = password; | ||
} | ||
|
||
public String getEmail() { | ||
return email; | ||
} | ||
|
||
public String getPassword() { | ||
return password; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package cart.auth; | ||
|
||
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 AuthPrincipal { | ||
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. Custom Annotation 👍 |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package cart.auth; | ||
|
||
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 AuthenticationArgumentResolver implements HandlerMethodArgumentResolver { | ||
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 static final String AUTHORIZATION = "Authorization"; | ||
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. 어떤 리뷰에서 봤는데 Spring이 제공하는 |
||
|
||
private final AuthenticationExtractor authenticationExtractor; | ||
|
||
public AuthenticationArgumentResolver(AuthenticationExtractor authenticationExtractor) { | ||
this.authenticationExtractor = authenticationExtractor; | ||
} | ||
|
||
@Override | ||
public boolean supportsParameter(MethodParameter parameter) { | ||
return parameter.withContainingClass(AuthMember.class) | ||
.hasParameterAnnotation(AuthPrincipal.class); | ||
} | ||
|
||
@Override | ||
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, | ||
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) { | ||
String authentication = webRequest.getHeader(AUTHORIZATION); | ||
return authenticationExtractor.extractAuthInfo(authentication); | ||
Comment on lines
+28
to
+29
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,7 @@ | ||
package cart.auth; | ||
|
||
public class AuthenticationException extends RuntimeException { | ||
public AuthenticationException() { | ||
super("인증에 실패했습니다."); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
package cart.auth; | ||
|
||
import java.util.Base64; | ||
import java.util.regex.Pattern; | ||
|
||
public class AuthenticationExtractor { | ||
|
||
private static final String BASIC_PREFIX = "Basic "; | ||
private static final String BASIC_DELIMITER = ":"; | ||
private static final String EMPTY = ""; | ||
private static final Pattern BASIC_CREDENTIAL_PATTERN = Pattern.compile("^Basic [A-Za-z0-9+/]+=*$"); | ||
private static final int EMAIL = 0; | ||
private static final int PASSWORD = 1; | ||
Comment on lines
+12
to
+13
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 AuthMember extractAuthInfo(String authentication) { | ||
validateCredentials(authentication); | ||
String[] emailAndPassword = extractBasicAuthInfo(authentication); | ||
String email = emailAndPassword[EMAIL]; | ||
String password = emailAndPassword[PASSWORD]; | ||
return new AuthMember(email, password); | ||
} | ||
|
||
private void validateCredentials(String authorization) { | ||
validateNull(authorization); | ||
validateBasicAuth(authorization); | ||
} | ||
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 void validateNull(String authorization) { | ||
if (authorization == null || authorization.isBlank()) { | ||
throw new AuthenticationException(); | ||
} | ||
} | ||
|
||
private void validateBasicAuth(String authorization) { | ||
if (!BASIC_CREDENTIAL_PATTERN.matcher(authorization).matches()) { | ||
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. 개인적으로 정규 표현식 너무 가독성 떨어진다고 생각하는데 어떻게 생각하시죠 박박박스터씨? |
||
throw new AuthenticationException(); | ||
} | ||
} | ||
|
||
private String[] extractBasicAuthInfo(String authorization) { | ||
String credentials = authorization.replace(BASIC_PREFIX, EMPTY); | ||
String decodedString = decodeCredentials(credentials); | ||
String[] emailAndPassword = decodedString.split(BASIC_DELIMITER); | ||
validateLength(emailAndPassword); | ||
return emailAndPassword; | ||
} | ||
|
||
private String decodeCredentials(String credentials) { | ||
try { | ||
return new String(Base64.getDecoder().decode(credentials)); | ||
} catch (IllegalArgumentException e) { | ||
throw new AuthenticationException(); | ||
} | ||
} | ||
|
||
private void validateLength(String[] emailAndPassword) { | ||
if (emailAndPassword.length != 2) { | ||
throw new AuthenticationException(); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package cart.config; | ||
|
||
import cart.auth.AuthenticationArgumentResolver; | ||
import cart.auth.AuthenticationExtractor; | ||
import java.util.List; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.context.annotation.Scope; | ||
import org.springframework.web.method.support.HandlerMethodArgumentResolver; | ||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; | ||
|
||
@Configuration | ||
@Scope | ||
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. Scope는 어떤 이유로 사용했어? 🤔 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 WebMvcConfig implements WebMvcConfigurer { | ||
|
||
@Override | ||
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) { | ||
resolvers.add(new AuthenticationArgumentResolver(authenticationExtractor())); | ||
} | ||
|
||
@Bean | ||
public AuthenticationExtractor authenticationExtractor() { | ||
return new AuthenticationExtractor(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package cart.controller; | ||
|
||
import cart.auth.AuthenticationException; | ||
import cart.dto.ErrorResponse; | ||
import java.util.NoSuchElementException; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import org.springframework.http.HttpStatus; | ||
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 | ||
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 ControllerAdvice { | ||
|
||
private final Logger logger = LoggerFactory.getLogger(getClass()); | ||
|
||
@ExceptionHandler | ||
public ResponseEntity<ErrorResponse> handleException(NoSuchElementException e) { | ||
return ResponseEntity.badRequest().body(new ErrorResponse(e.getMessage())); | ||
} | ||
|
||
@ExceptionHandler | ||
public ResponseEntity<ErrorResponse> handleException(MethodArgumentNotValidException e) { | ||
String defaultMessage = e.getBindingResult().getFieldError().getDefaultMessage(); | ||
return ResponseEntity.badRequest().body(new ErrorResponse(defaultMessage)); | ||
} | ||
|
||
@ExceptionHandler | ||
public ResponseEntity<ErrorResponse> handleException(IllegalArgumentException e) { | ||
return ResponseEntity.badRequest().body(new ErrorResponse(e.getMessage())); | ||
} | ||
|
||
@ExceptionHandler | ||
public ResponseEntity<ErrorResponse> handleException(AuthenticationException e) { | ||
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(new ErrorResponse(e.getMessage())); | ||
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. 인증을 위해 들어오는 값에 대한 로깅을 조금 더 자세히 남겨보는건 어떨까? |
||
} | ||
|
||
@ExceptionHandler | ||
public ResponseEntity<ErrorResponse> handleException(Exception e) { | ||
logger.error(e.getMessage()); | ||
return ResponseEntity.internalServerError().body(new ErrorResponse("알 수 없는 에러가 발생했습니다.")); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package cart.controller; | ||
|
||
import cart.dto.MemberResponse; | ||
import cart.dto.ProductResponse; | ||
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; | ||
|
||
@Controller | ||
public class PageController { | ||
private final ProductService productService; | ||
private final MemberService memberService; | ||
|
||
public PageController(ProductService productService, MemberService memberService) { | ||
this.productService = productService; | ||
this.memberService = memberService; | ||
} | ||
|
||
@GetMapping("/") | ||
public String index(Model model) { | ||
List<ProductResponse> products = productService.findProducts(); | ||
model.addAttribute("products", products); | ||
return "index.html"; | ||
} | ||
|
||
@GetMapping("/admin") | ||
public String admin(Model model) { | ||
List<ProductResponse> products = productService.findProducts(); | ||
model.addAttribute("products", products); | ||
return "admin.html"; | ||
} | ||
|
||
@GetMapping("/settings") | ||
public String setting(Model model) { | ||
List<MemberResponse> members = memberService.findMembers(); | ||
model.addAttribute("members", members); | ||
return "settings.html"; | ||
} | ||
|
||
@GetMapping("/cart") | ||
public String cart() { | ||
return "cart.html"; | ||
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 comment
The reason will be displayed to describe this comment to others. Learn more.
뭐죠? x해줘요