-
Notifications
You must be signed in to change notification settings - Fork 100
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단계 - 주문 기능 구현] 디노(신종화) 미션 제출합니다. #53
Conversation
Co-authored-by: jjongwa <[email protected]>, youngh0 <[email protected]>
Co-authored-by: jjongwa <[email protected]>, youngh0 <[email protected]>
Co-authored-by: jjongwa <[email protected]>, youngh0 <[email protected]>
Co-authored-by: jjongwa <[email protected]>, youngh0 <[email protected]>
Co-authored-by: jjongwa <[email protected]>, youngh0 <[email protected]>
Co-authored-by: jjongwa <[email protected]>, youngh0 <[email protected]>
Co-authored-by: jjongwa <[email protected]>, youngh0 <[email protected]>
Co-authored-by: jjongwa <[email protected]>, youngh0 <[email protected]>
Co-authored-by: jjongwa <[email protected]>, youngh0 <[email protected]>
Co-authored-by: jjongwa <[email protected]>, youngh0 <[email protected]>
Co-authored-by: jjongwa <[email protected]>, youngh0 <[email protected]>
Co-authored-by: jjongwa <[email protected]>, youngh0 <[email protected]>
Co-authored-by: jjongwa <[email protected]> Co-authored-by: youngh0 <[email protected]>
Co-authored-by: jjongwa <[email protected]> Co-authored-by: youngh0 <[email protected]>
Co-authored-by: jjongwa <[email protected]> Co-authored-by: youngh0 <[email protected]>
Co-authored-by: jjongwa <[email protected]> Co-authored-by: youngh0 <[email protected]>
Co-authored-by: jjongwa <[email protected]> Co-authored-by: youngh0 <[email protected]>
Co-authored-by: jjongwa <[email protected]> Co-authored-by: youngh0 <[email protected]>
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.
안녕하세요 디노! 빙봉 입니다 🙂
간단한 코멘트 몇 가지 확인 부탁드리며 README 작성에 힘 써주시면 감사하겠습니다!
궁금한 점은 언제든 편하게 DM 주세요!
.gitignore
Outdated
|
||
!**/src/main/resources/application.properties | ||
|
||
application.properties |
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.
- test패키지의 application.propreties까지 ignore 하신 이유가 있을까요?
- 적어주신 application.properties - main과 실제 AWS에서 돌고있는 프로퍼티들은 다른거죠?
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.
네네 실제 AWS에서는 다른 프로퍼티 파일을 사용하고 있습니다.
db 아이디와 패스워드 등의 보안 정보가 노출되기 때문에 프로퍼티는 무조건 gitIgnore 해야 한다!
라는 생각을 가지고 있었던 것 같습니다.
생각해 보니 db 설정에 대한 정보는 aws 서버에 따로 저장되어 있고, 현재 진행중인 프로젝트 패키지에서는 별다른 정보가 없어 그대로 push 해도 되겠군요..!
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.
테스트는 로컬에서도 정상적으로 돌아가야합니다! test패키지의 프로퍼티는 넣어주시면 좋을 것 같아요.
프로덕션 패키지의 application.properties도 local에서는 돌아갈 수 있게끔 설정해주시면 좋을 것 같습니다.
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.
마지막 커밋 사항을 올리지 않았네요..
적용했습니다..! 6fb696e
build.gradle
Outdated
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.
적어둘 곳이 없어서 여기에 적습니다 🙇
README.md에 기능 구현 목록 혹은 주문 기능의 시나리오가 있어야할 것 같아요. (재화에 관련된 요구사항이 크루마다 달라서 더더욱 자세히 적어야 좋을 것 같습니다.
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.
README 작성이 가장 기본인데.. 깜빡했습니다ㅜㅜ 바로 올리겠습니다!
return ResponseEntity.ok(CartResponse.from(cartResult)); | ||
} | ||
|
||
@PostMapping("/payment") |
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는 뭘까요?
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를 만들지 말지 여부를 논의하던 중 예시로 만들어 놓고, 실수로 지우지 못한 것 같습니다ㅜㅜ 바로 변경했습니다!
} | ||
|
||
@GetMapping("/{orderId}") | ||
public ResponseEntity<OrderResponse> findProductsByOrder(final MemberAuth memberAuth, @PathVariable("orderId") Long orderId) { |
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.
함수명이 findProductsByOrder가 맞을까요?
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.
findOrder 또는 findOrderByProductId 등이 올바를 것 같습니다.
+findOrder는 findOrders와 혼동될 것 같아 findSingleOrder로 변경했습니다!
src/main/resources/schema.sql
Outdated
quantity INT UNSIGNED NOT NULL | ||
); | ||
|
||
CREATE TABLE IF NOT EXISTS orders ( |
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.
예약어더라도 주문 테이블만 복수형태로 쓰는 건 일관성에 맞지 않아보여요! 앞에 테이블 명에 prefix를 붙이는 건 어떨까요?
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.
shopping_order로 변경했습니다!
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.
예약어가 있는 경우를 방지하기 위해 전체 테이블 명 컨벤션을 따로 잡기도 합니다! (e.g. table_order, tb_order...)
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.
아하 간단하게 tb_를 prefix로 두는 컨벤션도 있겠군요..!
+그러면 이런한 경우에서 현업에서는 예약어가 있는 테이블만 prefix를 적용하는지 아니면 전체적인 테이블의 prefix를 정하는지 궁금합니다.!
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.
전체적인 테이블의 prefix로 컨벤션을 정합니다!
} | ||
|
||
public OrderDto findByOrderId(final MemberAuth memberAuth, final Long orderId) { | ||
return OrderDto.of(orderRepository.findById(orderId)); |
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.
주문한 ID와 요청 온 회원정보가 일치하지 않는지는 확인하지 않아도 될까요?
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.
쿼리문에서 해당 사용자가 진행한 주문인지 확인하도록 변경하였습니다!
product_name VARCHAR(255) NOT NULL, | ||
product_price INT UNSIGNED NOT NULL, | ||
product_quantity INT UNSIGNED NOT NULL, | ||
product_image TEXT NOT NULL |
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.
ordered_item은 이미 주문을 완료한 물품들에 대한 기록 테이블입니다.
사용자가 특정 물건을 주문하고 난 뒤에 해당 물건의 정보가 바뀌어도 사용자는 주문 목록에서 구매했던 당시의 물건 정보를 확인할 수 있도록 하기 위해 다음과 같이 구성하였습니다!
@Component | ||
public class PointPolicy { | ||
|
||
private static final double EARNEDRATE = 0.01; |
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 static final double EARNEDRATE = 0.01; | |
private static final double EARNED_RATE = 0.01; | |
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.
변경했습니다ㅡㅜ! cc8e9d5
|
||
import org.springframework.stereotype.Component; | ||
|
||
@Component |
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.
처음엔 DIscoutPolicy로 쿠폰과 포인트에 따른 할인 정책을 바꿔 적용할 수 있도록 설계했다가
쿠폰 정책과 포인트 정책을 따로 분리하여 관리하는 것으로 의견이 모아져 변경하게 되었습니다.
그러는 과정에서 쿠폰 쪽은 정액 할인과 정률 할인이 나뉘기 때문에 인터페이스를 놓고 분리하였고,
포인트 정책은 다른 클래스로 바꿀 일이 없다고 생각해 놔두었는데, 조금 더 명시적인 효과를 주고 싶었고,
상태를 가지지 않고 단순한 계산 로직만 구현되어 있어서 싱글톤으로 관리해도 문제가 없다고 생각했습니다.!
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.
PointPolicy의 역할은 포인트 정책을 가지고 있는 역할로, 특정 정책에 따라 계산법을 가지고 있는 역할이라고 생각하는데요. 굳이 Component로 만들 필요 없이 enum으로 만들어도 되지 않을까 싶었습니다. 이와 관련해서 비슷한 리뷰를 남긴 적이 있어 링크 드려요!
한 번 읽어보시면 좋을 것 같습니다!
this.member = member; | ||
} | ||
|
||
public boolean isCorrectQuantity(final Long productId, final Integer quantity) { |
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.
해당 검증을 orderService에서 처리하도록 변경하고 난 뒤 검토를 하지 못했습니다.. 해당 메서드 삭제 완료했습니다!
안녕하세요 빙봉! 주말 잘 보내셨나요ㅎㅎ 남겨 주신 리뷰 사항 개선했습니다! 추가적으로 로직 관련해서 개선하고 싶은 부분이 있는데, 감사합니다!!🙇♂️ |
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.
안녕하세요 디노! 빙봉입니다 🙂
기존 코멘트에 대한 답변 및 추가 코멘트 남겼으니 확인 부탁드려요!
궁금한 점은 언제든 편하게 DM 주세요~
src/test/resources/reset.sql
Outdated
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.
test-schema.sql과 내용이 완전 같은데요. 단순히 DROP하고 다시 생성하는 용도라면 TRUNCATE 구문을 활용할 수 있을 것 같습니다. 더불어 TABLE을 DROP 시키는 것과 TRUNCATE하는 것은 용도가 다르니 한 번 찾아보세요!
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.
다음과 같이 변경해 보았습니다! (7e1f7f5)
TRUNCATE를 이용하니 굳이 테스트에 따로 스키마를 만들 필요가 없어졌네요..!!🙇♂️
@Sql(value = "/reset.sql",executionPhase = ExecutionPhase.BEFORE_TEST_METHOD) | ||
@Sql(value = "classpath:/test-data.sql", executionPhase = ExecutionPhase.BEFORE_TEST_METHOD) |
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.
몰랐습니다!!
저도 주드와 준팍과 같은 상황이 있어 아예 init-mode=never, 테이블을 DROP하고 다시 만드는 방식 등을 적용해 테스트 격리를 진행했는데, 알려주신 방식과 TRUNCATE를 사용해 보면서 조금 더 시야가 넓어진 것 같아요. 감사합니다!
|
||
public MemberArgumentResolver(MemberDao memberDao) { | ||
this.memberDao = memberDao; | ||
private static final Logger logger = LoggerFactory.getLogger(MemberArgumentResolver.class); |
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 static final Logger logger = LoggerFactory.getLogger(MemberArgumentResolver.class); | |
private static final Logger logger = LoggerFactory.getLogger(MemberArgumentResolver.class); | |
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.
여기도 있었군요,,,,
변경했습니다..! c3c0e67
src/main/java/cart/WebMvcConfig.java
Outdated
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.
WebMvcConfig도 config라는 패키지를 만들어서 구분해주면 좋을 것 같습니다.
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.
config 패키지를 만들어 CorsConfig와 WebMvcConfig을 위치했습니다! 07c3532
.collect(Collectors.toUnmodifiableList()), | ||
couponIds.stream() | ||
.map(couponRepository::findById).collect(Collectors.toList()), | ||
null |
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.
createdAt에는 왜 null이 들어가나요?
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.
데이터베이스의 스키마에 Default 값으로 Current_DateTime을 삽입하도록 설정하였는데,
DB단에 가서 삽입하는게 맞을지, 아니면 Order객체를 생성하는 시점에 LocalDateTime.current()를 불러와 삽입하는게 맞을지 고민했습니다.
이러한 경우 도메인 상에서는 해당 필드를 어떻게 놔두어야 할 지도 고민하는 과정에서 미처 검토하지 못하고 null을 우선 넣어 놓은 것 같습니다.
Order의 created_at에는 도메인 단에서 생성된 시간과 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.
default는 말 그대로 혹시 모를 상황에 대비하기 위해 기본값을 정의해두는 거라서요. DB에서 기본값을 바탕으로 삽입하기 보다는 application에서 넣어주는 게 맞지 않을까요? 그리고 Order 객체 데이터의 관점으로 봤을 때도 생성한 시점은 application에서 생성한 시점이 맞으니 도메인 단에서 생성된 시간이 맞을 것 같습니다.
|
||
useMemberCoupon(couponIds); | ||
addOrderedCoupon(couponIds, couponPolicies, orderId); | ||
addPointHistory(member, paymentPrice, usedPoint, orderId); |
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 void addOrderedCoupon(List<Long> couponIds, List<CouponPolicy> couponPolicies, Long orderId) { | ||
if (couponPolicies.size() > 0) { |
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.
isEmpty()를 써도 되지 않을까요?
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.
변경했습니다! (1cd781c)
this.pointPolicy = pointPolicy; | ||
} | ||
|
||
public Long createOrder(final MemberAuth memberAuth, final CreateOrderDto createOrderDto) { |
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.
관련있는 로직에 따라 메서드를 분리해 보았습니다! (1cd781c)
total_price INT UNSIGNED NOT NULL, | ||
payment_price INT UNSIGNED NOT NULL, | ||
point INT UNSIGNED DEFAULT 0, | ||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP |
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.
updated_at은 필요없나요?
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.
shopping_order, ordered_item은 처리된 주문에 대한 정보를 저장, 그 주문 당시에 구매한 물건의 정보를 저장하는 테이블이라 데이터에 대한 수정이 없을 것이라 생각했습니다.
(주문 취소의 경우에는 DELETE 처리)
만약 데이터의 정보가 수정될 일이 있는 테이블이라면 스키마에 updated_at을 추가할 것 같습니다.
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.
+현업에서는 각 테이블의 스키마에 created_at, updated_at, status 등을 필수적으로 포함하고 있는 편인가요?
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.
이력용 테이블이라면 updated_at은 없어도 될 것 같습니다. 설명 감사합니다! 🙇
현업에서는 각 테이블의 스키마에 created_at, updated_at, status 등을 필수적으로 포함하고 있는 편인가요?
디노가 잘 이야기해주신대로 수정될 일이 있는 테이블이라면 넣고 수정될 일이 없는 테이블(이력용 테이블)이라면 넣지 않습니다. status는 도메인별로 상황에 따라 다를 것 같고요!
discount_percent DECIMAL(5, 2) CHECK (discount_percent >= 0 AND discount_percent <= 100), | ||
discount_amount INT UNSIGNED NOT NULL, | ||
CONSTRAINT chk_coupon CHECK ((discount_percent = 0 AND discount_amount <> 0) OR (discount_percent <> 0 AND discount_amount = 0)) |
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.
check 조건의 경우, application 쪽에서 할 수 있을 것 같은데요.
DDL에서 제약조건으로 추가하신 이유를 알 수 있을까요?
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.
정률 할인 쿠폰의 할인율은 음수 또는 100%를 넘지 못하기 떄문에,
혹시라도 DB 콘솔에서 올바르지 않은 값을 추가하지 않도록 테이블 설정에 조건을 추가했습니다.
그리고 현재 쿠폰은 api를 통해 새로 발급받을 수 없는 상태이기 때문에 application 단에서 검증을 할 상황이 없었고,
만약 쿠폰 추가 api를 만든다면 RequestDto 단에서 검증을 추가적으로 진행할 것 같습니다..!
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에 제약조건을 넣을 경우 성능이 현저히 떨어지는데요. 그래서 가급적 DB에는 제약조건을 넣지 않는 게 좋습니다. 할인 쿠폰의 메타 데이터를 DB에 넣을 때 직접 DML을 쳐서 넣진 않습니다. 어드민용 API를 개발하게 되고 어드민에서 작업하게 될 확률이 매우 높습니다. 이런 이유로 쿠폰 메타 데이터의 경우 어드민의 비즈니스 로직 단에서 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.
성능 관련해서 차이가 있는지 몰랐습니다.. 알려주셔서 감사합니다!
안녕하세요 빙봉~ 디노입니다!🦖 우선 자세한 리뷰 정말정말 감사합니다! 올려 주신 타 PR 링크에서 정말 배울 점이 많았어요..! 리뷰 사항에 대해 변경하던 중 추가적으로 궁금한 사항이 생겨 질문 드립니다..! TRUNCATE를 활용하기 위해 TRUNCATE.sql을 만들고 각 테스트마다 sql 어노테이션을 붙이는 방식으로 테스트 격리를 시도했는데요. 마지막까지 잘 부탁드립니다!!🙇♂️🙇♂️🙇♂️ +추가적으로 여쭤볼게 있습니다..! 우테코 활동을 기록하기 위한 리포지터리를 작성하고 있는데 리뷰어 부분에 빙봉의 깃허브 주소를 태그해도 될까요? https://github.com/jjongwa/woowacourse 이 부분입니다..! |
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.
안녕하세요 디노! 빙봉입니다 🙂
Read와 Write로 서비스를 구분하고 테스트가 꼼꼼했던 점이 인상깊었습니다!
몇 가지 추가 코멘트 드렸으니 확인해보시면 좋을 것 같아요! 주문 기능 구현 미션은 여기서 머지하겠습니다.
그동안 고생 많으셨습니다! 방학 잘 보내세요!
<include resource="org/springframework/boot/logging/logback/defaults.xml"/> | ||
<timestamp key="BY_DATE" datePattern="yyyy-MM-dd"/> | ||
|
||
<springProperty scope="context" name="LOG_DIR" source="log.directory"/> |
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.
- log.directory 정의가 되어있어야 로컬에서 실행시켰을 때 제대로 된 로그 디렉토리가 생성될 것 같네요
- gitignore에 해당 디렉토리 이름도 추가되면 좋을 것 같습니다
spring.thymeleaf.cache=false | ||
spring.datasource.url=jdbc:h2:mem:testdb;MODE=MySQL | ||
spring.datasource.driver-class-name=org.h2.Driver | ||
spring.sql.init.schema-locations=classpath:/schema.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.
기본경로라 없어도 되지 않을까요?
total_price INT UNSIGNED NOT NULL, | ||
payment_price INT UNSIGNED NOT NULL, | ||
point INT UNSIGNED DEFAULT 0, | ||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP |
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.
이력용 테이블이라면 updated_at은 없어도 될 것 같습니다. 설명 감사합니다! 🙇
현업에서는 각 테이블의 스키마에 created_at, updated_at, status 등을 필수적으로 포함하고 있는 편인가요?
디노가 잘 이야기해주신대로 수정될 일이 있는 테이블이라면 넣고 수정될 일이 없는 테이블(이력용 테이블)이라면 넣지 않습니다. status는 도메인별로 상황에 따라 다를 것 같고요!
discount_percent DECIMAL(5, 2) CHECK (discount_percent >= 0 AND discount_percent <= 100), | ||
discount_amount INT UNSIGNED NOT NULL, | ||
CONSTRAINT chk_coupon CHECK ((discount_percent = 0 AND discount_amount <> 0) OR (discount_percent <> 0 AND discount_amount = 0)) |
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에 제약조건을 넣을 경우 성능이 현저히 떨어지는데요. 그래서 가급적 DB에는 제약조건을 넣지 않는 게 좋습니다. 할인 쿠폰의 메타 데이터를 DB에 넣을 때 직접 DML을 쳐서 넣진 않습니다. 어드민용 API를 개발하게 되고 어드민에서 작업하게 될 확률이 매우 높습니다. 이런 이유로 쿠폰 메타 데이터의 경우 어드민의 비즈니스 로직 단에서 validation을 추가하는 형식으로 개발합니다. 이 점 고려해서 추후 팀 프로젝트 개발하실 때 참고하면 좋을 것 같네요!
@JdbcTest | ||
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) | ||
@Sql(value = "classpath:truncate.sql", executionPhase = ExecutionPhase.AFTER_TEST_METHOD) | ||
@Sql(value = "classpath:test-data.sql", executionPhase = ExecutionPhase.BEFORE_TEST_METHOD) |
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.
TRUNCATE를 활용하기 위해 TRUNCATE.sql을 만들고 각 테스트마다 sql 어노테이션을 붙이는 방식으로 테스트 격리를 시도했는데요.
ExecutionPhase를 BEFORE_TEST_METHOD로 설정해야 할 지, AFTER_TEST_METHOD 설정해야 할 지 명확한 기준이 서질 않았습니다.
개인적인 생각으로는 새롭게 삽입된 데이터에 대해서는 해당 테스트가 책임지고 초기화를 진행해야 하기 때문에 After_method를 적용해야 한다고 생각하는데, 이러한 상황에는 맨 처음에 삽입되는 data.sql의 기록을 지워주는 작업을 언제 수행해 주어야 하는지 잘 모르겠습니다..
데이터의 초기화를 적용해야 하는 시점에 대한 기준이 존재하는지 궁금합니다.!
디노의 생각 좋네요! 언제 테스트 데이터를 초기화해주는 지 기준이 존재하는 건 아니며 상황에 맞게 해주시면 됩니다. 다만 실무에서는 테이블 간 의존도 강하고 외부 의존성이 아주 많습니다.
이상적으로는 디노의 말대로 새롭게 삽입된 데이터는 해당 테스트가 책임져야하는데요. 테이블 간 의존이 강하고 외부 의존성이 많으면 그러지 못하는 경우가 있습니다. 그래서 안전하게 하기 위해서 before 시점에 초기화 하기도 합니다.
또한 외부 의존성이 너무 많고 테이블 간 의존도 강한데 테이블 내에 더미 데이터가 많이 필요한 경우가 있습니다. 이런 경우에는 dev DB에 직접 붙어서 테스트를 하기도 합니다. 이때는 초기화를 사용하지 않고 해당 테스트에서 사용된 데이터들만 삭제합니다.
이처럼 많은 경우에서 다양하게 사용될 수 있어서 적절하게 선택해서 사용하는 게 좋을 것 같네요
우선 현재는 전체 테스트를 실행시켰을 때 데이터 중복 삽입 문제로 에러가 발생하는 테스트에는 Before와 After 모두 설정해 주는 방식으로 처리는 했습니다..
데이터 중복 삽입 자체가 문제가 되는 지점이군요. 여유가 되신다면 데이터 중복 삽입 문제에 대해 딥다이브 한 번 해보시는 걸 추천드립니다. (제가 기존에 드렸던 링크에 해답이 있을 것 같긴 하네요)
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.
링크 안의 링크를 못봤었네요..! INSERT IGNORE 구문을 사용하면 해당 문제는 해결할 수 있을 것 같습니다ㅎㅎ
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.
추가적으로 여쭤볼게 있습니다..! 우테코 활동을 기록하기 위한 리포지터리를 작성하고 있는데 리뷰어 부분에 빙봉의 깃허브 주소를 태그해도 될까요? https://github.com/jjongwa/woowacourse 이 부분입니다..!
넵~ 하셔도 됩니다 🙂
@Override | ||
public void updateQuantity(final CartItem cartItem) { | ||
final String sql = "UPDATE cart_item SET quantity = ? WHERE id = ?"; | ||
jdbcTemplate.update(sql, cartItem.getQuantity(), cartItem.getId()); |
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.
jdbcTemplate.update가 어떤 걸 반환하는지 한 번 살펴보시면 좋을 것 같습니다. void로 반환하면 id에 해당하는 값이 없더라도 오류없이 해당 로직이 실행됩니다.
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.
해당 쿼리가 영향을 미친 row의 개수를 반환합니다!
이전에도 지적받은 사항인데 적용하지 못했네요.. 알려 주셔서 감사합니다!
안녕하세요 빙봉!
우테코 5기 백엔드 진행중인 디노라고 합니다. 🦖
2단계 미션 PR 제출합니다. 🙇♂️
이번 미션은 3인 페어 + 2단계 기능 구현을 찢어지지 않고 함께 진행해 보았습니다..!
서로 합이 잘 맞아서 즐겁게 진행했지만 세 명이서 하다 보니 확실히 의견 충돌도 잦고 진행 속도도 생각보다 느려서 여러 가지 느낀 점이 많았던 미션이었던 것 같습니다..!
우선 이번 미션에서 가장 중요하게 여긴 부분은
계층 간 분리를 명확히 하기 + DB의 구조에 따라가기 보단 최대한 도메인 중심적으로 구조 잡아가기
였습니다!
마지막 미션 잘 부탁드립니다!!🙇♂️🙇♂️
2단계 커밋 범위
application.properties - main
application.properties - test