-
Notifications
You must be signed in to change notification settings - Fork 170
[4기 - 박세연] SpringBoot Part2 Weekly Mission 제출합니다. #777
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
Conversation
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.
과제하시느라 고생 하셨습니다.
| public VouchersResponse(List<Voucher> vouchers) { | ||
| this.result = vouchers.stream() | ||
| .map(VoucherCreationResponse::new) | ||
| .collect(Collectors.toUnmodifiableList()); |
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.
toList() 로 대체 가능합니다.
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.
넵 변경하겠습니다.
| public void start() { | ||
| boolean isExit = false; | ||
|
|
||
| while(!isExit) { |
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.
가능하면 ! 조건에 부정을 두지 않는 것이 가독성에 좋습니다. isRunning = true 로 하고, false 일 때 반환하는것이 좋을거 같아요.
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.
좋은 의견 감사합니다!
| return true; | ||
| } | ||
|
|
||
| private boolean processVoucherMenuSelection(VoucherMenu selectMenu) { |
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.
리턴값을 사용하지 않는데 반환하는 이유가 있나요?
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.
console 기능에서 customer에 대한 기능이 추가될 때 못 바꾼 것 같습니다 바로 고칠게요!
| private final CommandWriter commandWriter; | ||
| private final SystemWriter commandWriter; | ||
|
|
||
| @Autowired |
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.
생성자에 Autowired 애노테이션이 어떤 의미가 있나요?
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.
생성자에 Autowired 어노테이션을 붙이면서 DI를 명시하는 것입니다.
여러 생성자가 있을 경우 Autowired는 필수이며, 스프링 버전이 올라가면서 생성자가 단일인 경우 생략이 가능하지만 여러 개인 경우 필수로 알고 있습니다!
| println(PrintMessageType.NAME_INPUT.getMessage()); | ||
| } | ||
|
|
||
| public void printReuslt(String result) { |
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.
오타 있습니다.
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.
수정하겠습니다
| import org.weekly.weekly.util.DiscountType; | ||
|
|
||
| public class PercentDiscount implements Discount{ | ||
| private final int PERCENT = 100; |
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.
상수로 둘거면 static 을 두세요.
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.
넵 알겠습니다
| package org.weekly.weekly.voucher.domain; | ||
|
|
||
| import org.weekly.weekly.util.ExceptionMsg; | ||
| import org.weekly.weekly.voucher.exception.VoucherException; |
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.
미사용 import 제거해 주세요.
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.
제거했습니다!
| } | ||
|
|
||
| this.amount -= afterAmount; | ||
| this.amount += afterAmount; |
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.
이 케이스가 어떤건지 잘 이해가 되지 않습니다.
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.
afterAmount가 0보다 큰 경우에는 바우처 금액을 다 사용한 경우이기 때문에 그대로 반환했지만
afterAmount가 0보다 작은 경우, 바우처로 할인할 수 있는 금액이 제공받은 금액(beforeAmount)보다 크기 때문에 바우처 금액을 남기는 것을 표현했습니다.
| } | ||
|
|
||
| @Test | ||
| @Order(1) |
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.
테스트는 독립적으로 실행될 수 있게 해야 변경과 유지보수에 용이 합니다.
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.
넵! 테스트가 많아 질 수록 서로 연관 관계를 겪게 되어 불편하다고 느끼고 있었는데, 좋은 의견 감사합니다!
다만 하나 궁금한 점은 테스트에서 사용하는 db의 경우 schema만 적용하여 data.sql은 적용하지 않고 각각의 테스트를 진행하나요?
작성하다 보니 굳이 필요하다는 생각이 안 들어서 질문드립니다.
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.
테스트는 명확하게 해야 보기 더 쉽습니다. "할인번호를_입력하면_통과" 같은 경우는 고정할인, 퍼센트할인 테스트를 분리하는게 좋습니다.
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.
주의하겠습니다!
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 메서드를 호출하게 자주 씁니다
DTO와 매퍼는 선택이라고 생각합니다. 저는 개인적으로 DTO는 정말 데이터 전송용 객채로만으로 자주쓰긴 합니다.
나중에 이력서볼때 같이 라이브코딩 하시면좋을것같아여. 코드는 많이 좋아졌습니다!
| private void handleVoucherCreation() { | ||
| VoucherCreationRequest voucherCreationRequest = commandLineApplication.createVoucherFromInput(); | ||
| Response response = voucherController.createVoucher(voucherCreationRequest); | ||
| logger.info("{}{}", PrintMessageType.CREATE_VOUCHER_SUCCESS.getMessage(),response.getResult()); | ||
| commandLineApplication.printResult(response); | ||
| } | ||
|
|
||
| private void handleVoucherSearch() { | ||
| Response response = voucherController.getVouchers(); | ||
| logger.info("{}{}", PrintMessageType.FIND_ALL_VOUCHER_SUCCESS.getMessage(), response.getResult()); | ||
| commandLineApplication.printResult(response); | ||
| } |
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.
메서드 안에 너무 많은 책임이 있는것같습니다. Request생성, 요청, 결과출력까지 너무 수정에 취약합니다.
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.
확실히 책임이 많긴 하네요. 그래서 handle이라는 메서드 명을 줘서 전반적인 처리를 하는 것처럼 보이게 하긴 했지만 어떻게 해결을 해야할지 애매하네요,,
| if (CustomerMenu.CREATE.equals(selectMenu)) { | ||
| handleCustomerCreation(); | ||
| return false; | ||
| } | ||
|
|
||
| if (CustomerMenu.DELETE.equals(selectMenu)) { | ||
| handleCustomerDelete(); | ||
| return false; | ||
| } | ||
|
|
||
| if (CustomerMenu.DELETE_ALL.equals(selectMenu)) { | ||
| handleCustomerDeleteAll(); | ||
| return false; | ||
| } | ||
|
|
||
| if (CustomerMenu.FIND_ALL.equals(selectMenu)) { | ||
| handleCustomerFindAll(); | ||
| return false; | ||
| } | ||
|
|
||
| if (CustomerMenu.FIND_DETAIL.equals(selectMenu)) { | ||
| handleFindDetail(); | ||
| return false; | ||
| } | ||
|
|
||
| if (CustomerMenu.UPDATE.equals(selectMenu)) { | ||
| handleUpdateCustomer(); | ||
| return false; | ||
| } |
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.
switch로도 한 번 바꿔보시는거 추천드립니다. 가독성 더 좋은걸로 선택하시는것도 괜찮아보여요
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.
예전 우테코 프리코스에서 else를 사용하지 말라는 그런 조건이 있어서 유지하고 있었는데, 굳이 꼭 그럴 필요는 없이 가독성이 더 좋은 것을 선택해 보겠습니다!
| public CustomerResponse findDetailCustomer(CustomerUpdateRequest updateRequest) { | ||
| return customerService.findDetailCustomer(updateRequest); | ||
| } | ||
|
|
||
| public CustomersResponse findAllCustomer() { | ||
| return customerService.findAllCustomer(); | ||
| } | ||
|
|
||
| public CustomerResponse updateCustomer(CustomerUpdateRequest updateRequest) { | ||
| return customerService.updateCustomer(updateRequest); |
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.
Response를 공유해서 쓰는건 비효율적입니다. 추후 Create와 Update가 각각 다른 값을 반환하는데 Response가 값이 같으면 요청하는쪽에서 혼란이 올 수 있습니다.
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.
처음에는 ResponseEntity처럼 공통된 값을 반환하는 것을 생각했는데 오히려 혼란이 올 수 있다는 생각을 못했네요. 수정하겠습니다!
| } | ||
|
|
||
| public static Customer of(UUID uuid, String name, String email) { | ||
| CustomerValidator.validateEmailFormat(email); |
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을 생성시에 넣는건 개인적으로 좋아하는 패턴입니다. 저는 생성자 내부에서 자주 씁니다 ㅎㅎ
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.
넵! api에서도 값을 가져올때 생성자자에서 처리를 안 하다 보니까 생성자에서 예외처리를 해야하나 라고 고민하고 있었는데 좋은 의견 감사합니다!
| public CustomerResponse findDetailCustomer(CustomerUpdateRequest updateRequest) { | ||
| String email = updateRequest.email(); | ||
| Customer customer = validateCustomerExistAndGet(email); | ||
| return CustomerResponse.of(customer); | ||
| } |
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.
find도 @transaction(readOnly = true)를 해주면 좋습니다!( db마다 다르긴합니다.)
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.
해당 구문에서는 어짜피 select만 하기 때문이겠네요
넵!
| import org.weekly.weekly.util.ExceptionMsg; | ||
|
|
||
| public class InputException extends RuntimeException{ | ||
|
|
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.
클래스 시작시 공백 통일해주세요! (자기만의 컨벤션 지키면서 개발하기)
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.
넵 고치겠습니다!
| import java.io.Console; | ||
|
|
||
| @Component | ||
| @ConditionalOnProperty(value="command.read", havingValue = "console") |
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.
제가 안써봤는데, 혹시 어떤코드인지 알 수 있을까요:?
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.
spring.config.activate.on-profile: default
command:
write: system
read: scannerapplication.yaml에 이렇게 되어있습니다.
해당 경우 @Profiles와 @ConditionalOnProperty 중 어떤 것을 사용할 까 선택해야 했습니다.
먼저
[ ConditionalOnProperty란 ]
어노테이션에서 적용한 profiles와 매칭이 되는 클래스만 빈에 등록 했다면,
ConditionalOnProperty의 경우 특정 값에서 동작한다는 것을 명시하고 yaml에서 맞는 값을 적용하면 동작이 되도록 합니다.
결국, 제가 생각하기에는 주체가 클래스에 있냐, yaml에 있냐를 통해 조절한다고 느꼈습니다.
(클래스마다, 하나의 이름과 같은 프로퍼티를 적용하기 때문)
[ 문제 상황 ]
-
Profiles는 동작하는 profile에 맞게 빈에 등록하기 때문에 profile의 이름이 변경되면 해당 프로파일을 참고하는
모든 클래스를 찾아서 다시 바꿔줘야 한다는 생각이 있었습니다. -
어떤 환경에서 동작을 하게 할 것인지,
한번에 파악하기가 힘들었습니다.
예를 들어,
- default:
System클래스와Scanner클래스 - dev:
System클래스와Console클래스 - local:
System클래스와Scanner클래스
등으로 이렇게 구성이 복잡하게 연결되어 있다고 가정했었는데,
코드 자체를 Profiles로 보면 Profiles("!dev"), Profiles({"dev", "test"}) 처럼 적용되다 보니 위와 같은 문제가 발생했습니다.
[ 해결 ]
따라서, 위와 같은 문제를 해결하기 위해 주체를 클래스로 적용하여 각 Profile마다 다르게 동작할 수 있도록 적용하였습니다.
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.
감사합니다! 저도 하나 배워가네요
| private final Logger logger = LoggerFactory.getLogger(SystemWriter.class); | ||
| private void println(String msg) { | ||
| System.out.println(msg); | ||
| } | ||
| private void print(String msg) {System.out.print(msg);} | ||
|
|
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.
공백이 좀 보기 힘든것같습니다
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.
고치겠습니다!
|
|
||
|
|
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.
공백이 두개입니다!
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.
넵 확인했습니다!
📌 과제 설명
회원에 대한 CRUD기능 구현 및 많은 단위 테스트 작성을 하였고, JdbcTemplate을 통해 dao를 구현 하였습니다.
👩💻 요구 사항과 구현 내용
입력 기능 추가
[ 프로그램 실행 구문 ]
[ voucher 실행 구문 ]
[ customer 실행 구문 ]
바우처 추가 기능
JdbcTemplate을 사용해서 구현회원 추가 기능
exit: 종료create: 회원 생성delete: 회원 삭제delete_all: 회원 전체 삭제find_all: 회원 전체 조회find_detail: 이메일을 통한 상세 검색update: 유저 정보 업데이트구현 기능
1. 입력
2. 바우처
3. 회원
4. 그 외
✅ 피드백 반영사항
적절한 네이밍
불필요한 this
Response 인터페이스
response클래스에 붙여 동일한 동작을 할 수 있도록 하려고 적용했습니다!✅ PR 포인트 & 궁금한 점