[BE][Team09][Bat & Pyro] API 를 JDBC 와 연동#59
[BE][Team09][Bat & Pyro] API 를 JDBC 와 연동#59ghojeong wants to merge 28 commits intocodesquad-members-2021:ghojeongfrom
Conversation
[BE] JDBC연동 - category class
[BE] category 테스트 코드 작성
[BE] JDBC 연동 도메인, 데이터베이스 데이터 삽입
[BE] DTO 생성 (get요청 불가능)
[BE] JDBC 와 서버를 연동
[BE] 주문하기 기능 구현
[BE] JDBC Template 으로 N + 1 문제 해결
[BE] N+1 문제 해결 & 주문하기 기능 구현
wheejuni
left a comment
There was a problem hiding this comment.
빡빡한 일정중에 코드 리뷰까지 요청하시느라 대단히 고생 많으셨습니다. 💯
우선 전반적으로 일정에 쫓기다보니 통합 테스트는 물론이고 유닛 테스트도 많이 부족하네요. 이 부분은 개선되기를 바랍니다.
아울러 enum 을 조금 더 적극적으로 사용해 보시면 전반적인 코드 퀄리티를 높일 수 있을 것 같습니다.
그리고 조금 더 쉽게 쉽게 생각하며 코딩하기를 권해 드리고 싶네요. 한번 꼬아서 생각하는 바람에, 코드도 꼬인 것 같은 부분이 여럿 있네요.
수고 많으셨습니다. 🥇
| if (dishService.order(detailHash, count)) { | ||
| return ResponseEntity.ok().body("주문완료"); | ||
| } | ||
| return ResponseEntity.ok().body("주문불가"); |
There was a problem hiding this comment.
dishService.order() 의 리턴 타입이 스트링이었으면 코드가 조금 더 간결해졌겠네요.
같은 메소드를 소비하는 다른 소비처가 있나요? 그리고 그곳이 boolean 타입 리턴을 필요로 하나요?
그렇지 않다면, 서비스에서 바로 스트링을 내리는 구현도 좋을 것 같습니다.
다만 지금은 주문의 상태에 관한, 그러니까 경우의 수 를 다루는 메소드이므로 enum 을 사용하기에 아주 적절한 상황으로 보입니다.
public enum OrderResult {
ORDER_SUCCESSFUL("주문완료"),
ORDER_NOT_MADE("주문불가")
...There was a problem hiding this comment.
다만 지금은 주문의 상태에 관한, 그러니까 경우의 수 를 다루는 메소드이므로 enum 을 사용하기에 아주 적절한 상황으로 보입니다.
enum 을 더 적극적으로 사용해보도록 하겠습니다!
There was a problem hiding this comment.
enum 생각을 하지 못하고 개발하였습니다....
image 구분할 때도 enum으로 했으면 더 좋았을 것 같습니다.
감사합니다.
| @ApiOperation(value = "메인 요리", notes = "메인 요리의 목록을 반환합니다.") | ||
| public ResponseEntity<List<DishDto>> getMainList() { | ||
| List<DishDto> dishes = dishService.getList(1); | ||
| List<DishDto> dishes = dishService.getList(1L); |
There was a problem hiding this comment.
이것도 enum 으로 main 과 1L 상수를 하나로 묶는게 좋겠군요.
생각없이 막코딩한 밑천이 다 드러나네요...
| import java.util.Objects; | ||
|
|
||
| public class Delivery { | ||
| private String deliveryType; |
There was a problem hiding this comment.
type 이라면... 문자열 필드보단 더 좋은 방법이 있지 않을까요?
There was a problem hiding this comment.
type 이라면, enum 으로 만들어서 관리하는 편이 훨씬 좋겠군요.
지적해주셔서 감사합니다.
There was a problem hiding this comment.
'deliveryType',' imageType' 그리고 sale에서 'Badge' 모두 enum으로 관리하면 더 좋았을 것 같습니다.
감사합니다.^^
|
|
||
| @Autowired | ||
| public DishDao(DataSource dataSource) { | ||
| jdbcTemplate = new JdbcTemplate(dataSource); |
There was a problem hiding this comment.
JdbcTemplate 도 의존성 주입을 받을 수 있습니다.
어떻게 하면 될지 검색해 보시고, 그렇게 하시는 편이 좋겠습니다.
There was a problem hiding this comment.
JdbcTemplate 도 의존성 주입을 받을 수 있습니다.
와! 이건 상상도 못했는데, 찾아보겠습니다.
알려주셔서 감사합니다.
| @Query("SELECT image.* from sidedish.image INNER JOIN sidedish.dish_image ON image.id = dish_image.image WHERE image.type = 'thumb' AND dish_image.dish = :detailHash;") | ||
| List<Image> findThumbImagesByDish(String detailHash); | ||
|
|
||
| @Query("SELECT image.* from sidedish.image INNER JOIN sidedish.dish_image ON image.id = dish_image.image WHERE image.type = 'detail' AND dish_image.dish = :detailHash;") | ||
| List<Image> findDetailImagesByDish(String detailHash); |
There was a problem hiding this comment.
두 쿼리 메소드를 하나로 합칠 수 있을 것 같은데요. 어떻게 좋은 방법 없을까요?
There was a problem hiding this comment.
WHERE image.type = ? 부분의 조건을 삭제하고, Service 에서 image.type 에 따라 dto 에 다르게 값을 넣어주면 되겠네요.
불필요하게 쿼리가 2번 날아가는 문제 지적해주셔서 감사합니다.
There was a problem hiding this comment.
파라미터값에 detail, thumb 을 받아도 될 것같습니다.
불필요한 쿼리메소드 줄이는 것에 신경써서 코딩하겠습니다.
감사합니다.
| public class DishDetailDto { | ||
| private final List<String> thumbImages; | ||
| private final int point; | ||
| private final Integer point; |
There was a problem hiding this comment.
insert 를 하는 초기화 sql 중에서 point 의 값이 비어있는 Record 들이 있어서,
터지는 것을 방지하기 위해서 임시로 Integer 로 바꾸었습니다.
코드를 바꾸는 좋지 못한 해결책 같네요.
| // return dishRepository.findAllByCategoryId(categoryId) | ||
| // .stream().map(this::convert) | ||
| // .collect(Collectors.toList()); | ||
| } | ||
|
|
||
| // private DishDto convert(Dish dish) { | ||
| // String detailHash = dish.getDetailHash(); | ||
| // List<Delivery> deliveries = deliveryRepository.findAllByDish(detailHash); | ||
| // List<Sale> sales = saleRepository.findAllByDish(detailHash); | ||
| // return DishDto.from(dish, deliveries, sales); | ||
| // } |
| List<Image> thumbImages = imageRepository.findThumbImagesByDish(detailHash); | ||
| List<Image> detailImages = imageRepository.findDetailImagesByDish(detailHash); |
There was a problem hiding this comment.
결국 두 종류의 이미지를 모두 불러와야 하는군요.
그렇다면 Image 안에 thumb, detail 구분이 가능한 필드만 있으면, 한번에 불러와서 DTO로 넘겨도 되는 거 아닐까요?
DTO가 만들어질 때 두 개를 구분해서 넣으면 될 것 같은데요. 어떻게 생각하세요?
There was a problem hiding this comment.
아, 그게 확실히 더 효율적일것 같습니다.
동의합니다.
| Dish dish = getDish(detailHash); | ||
| QuantityDto quantityDto = getDetailQuantity(detailHash); | ||
| if (quantityDto.getQuantity() > count) { | ||
| int upqua = quantityDto.getQuantity() - count; |
There was a problem hiding this comment.
upqua 라는 변수명을 보고 그 뜻을 알아볼 수 있는 사람은 단언컨대 한 명도 없을 것 같습니다...
There was a problem hiding this comment.
급하게 코딩하느라 변수명 신경 못썼습니다.
updatedQuantity 라고 의도했습니다...
부족한 부분을 콕 찝어서 말씀해주셔서 감사합니다. |
|
금요일날 시연이 끝나서, 리뷰를 받을 수 있을까 걱정했는데, 다음주 미션을 할 때는 특히 테스트 코드를 신경써서 개발을 해 볼 수 있도록 하겠습니다! |
|
저 역시 프로젝트 기간이 끝나서 리뷰를 못 받지 않을까 했었는데, 리뷰주셔서 감사합니다.~ 프로젝트는 기간이 정해져 있어 구현에만 집중하여 코드가 많이 부족합니다. 감사합니다. |
#20 의 피드백들을 반영하려다 보니, 구현할 내용이 자꾸만 커져버려서 너무 뒤늦게 PR 을 올리게 되었습니다.
JDBC 와 데이터베이스가 익숙치 않아서 시간이 너무 오래 걸려 죄송합니다.
피드백 주신대로 Spring profile 을 이용해서, H2 와 MySQL 의 분기처리가 쉽게 일어나도록 하였습니다.
기존의 지나치게 거대했던 Dish 도메인 객체도 Image, Sale, Delivery 등으로 분리될 수 있도록 하였습니다.
아직 미구현된 부분은 다음과 같습니다.
refreshable 여부를 결정하는 컴포넌트의 설계 및 구현이 아직 완성되지 않았으며,
익숙치 않은 Spring JDBC 를 사용하다보니, Derived Queries 를 제대로 활용하지 못하고 쿼리를 직접 작성하게 되었습니다.
(Derived Queries 로 발생하는 버그를 며칠간 제대로 해결하지 못해서 시간을 날리다 보니, 뒤늦게 그냥 쿼리를 작성하게 되었습니다.)
지금은 프로젝트 규모가 크지 않다보니 직접 쿼리를 짜는게, Derived Queries 보다 개발 속도가 빠른 것 같습니다.
익숙치 않은 삽질을 계속하다보니, 미흡한 부분이 많습니다.
N+1 문제도 아직 제대로 해결을 하지 못했는데, 쿼리 수정을 통해 해결할 수 있도록 하겠습니다.
#20 에서 주신 피드백 덕분에, 정말로 여러가지를 더 시도할 수 있었습니다.
언제나 좋은 피드백 감사합니다!
그리고 욕심을 부리다가 피드백에 빠르게 반응을 하지 못해서 죄송합니다.