[week07] 제이스 7주차 미션#21
Conversation
|
JPQL로 쿼리문을 잘 작성하신 것 같습니다. |
yousung1020
left a comment
There was a problem hiding this comment.
매우 훌륭하게 미션을 진행하신 것 같습니다! 다음 피드백 사항만 반영해주시면 감사하겠습니다! 수고하셨습니다!
| "WHERE s.shopAddress LIKE %:region% " + | ||
| "AND (:cursorId IS NULL OR m.id < :cursorId) " + | ||
| "ORDER BY m.id DESC") | ||
| List<Mission> findByRegionWithCursor( |
There was a problem hiding this comment.
Pageable 파라미터를 사용하면서 List 타입을 반환하면 다음 페이지의 존재 여부 파악이 되지 않습니다! Slice와 같은 타입으로 변경하시면 좋을 것 같아요!
| "JOIN FETCH s.shopCategory " + | ||
| "WHERE s.shopAddress LIKE %:region% " + | ||
| "AND (:cursorId IS NULL OR m.id < :cursorId) " + | ||
| "ORDER BY m.id DESC") |
There was a problem hiding this comment.
order by를 제거하고 Pageable에게 정렬 책임을 위임하는 것이 나을 것 같습니다! Pageable에도 정렬조건이 있고, ORDER BY 절에 의한 정렬 쿼리가 작성되어 있다면 Hibernate가 sql을 파싱하는 과정에서 정렬 구문이 중복되거나 충돌하여 syntax 에러가 발생할 수 있기 때문입니다!
| @Email(message = "올바른 이메일 형식이 아닙니다") String email, | ||
| @NotNull(message = "성별은 필수입니다") Gender gender, | ||
| @NotNull(message = "생년월일은 필수입니다") @Past(message = "생년월일은 과거 날짜여야 합니다") LocalDate birth, | ||
| @NotNull(message = "우편번호는 필수입니다") Integer zipcode, |
There was a problem hiding this comment.
우편번호는 반드시 문자열 형태로 받으셔야 합니다! 우편번호가 0으로 시작하는 경우 0이 사라져버려 데이터 왜곡 문제가 발생합니다. 따라서 String 자료형으로 받아 안전하게 처리하면 좋을 것 같습니다!
| String address, | ||
| @Valid @NotEmpty(message = "약관 목록은 필수입니다") List<TermsDTO> termsList, | ||
| @NotBlank(message = "이름은 필수입니다") @Size(max = 50, message = "이름은 50자 이하여야 합니다") String name, | ||
| @Email(message = "올바른 이메일 형식이 아닙니다") String email, |
There was a problem hiding this comment.
@Email 어노테이션은 입력된 값이 있을 때는 형식을 검사하지만, 값이 null이거나 빈 문자열인 경우에는 검증을 그냥 통과시켜버립니다. 따라서 이메일이 필수인 경우에는 @NotBlank 어노테이션을 함께 붙여주어야 안전합니다!
There was a problem hiding this comment.
전반적으로 수정할 것이 꽤 있는 것 같습니다!
- new ObjectMapper() 를 통해 새로운 객체를 매번 정적 변수로써 생성하고 있습니다. Bean으로 등록하여 생성자 주입으로 받아 사용하는 것이 좋아보입니다!
- 로직 구조가 매 요청마다 Apple의 JWKS URL로 http 요청을 보내는 것 같습니다. 그래서 로그인 요청 속도가 저하될 것 같습니다ㅠㅠ
| @Override | ||
| @SuppressWarnings("unchecked") | ||
| public SocialUserInfo getUserInfo(String accessToken) { | ||
| HttpHeaders headers = new HttpHeaders(); | ||
| headers.setBearerAuth(accessToken); | ||
| ResponseEntity<Map> response = restTemplate.exchange( | ||
| USER_INFO_URL, HttpMethod.GET, new HttpEntity<>(headers), Map.class); | ||
| Map<String, Object> body = restClient.get() | ||
| .uri(USER_INFO_URL) | ||
| .header(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken) | ||
| .retrieve() | ||
| .body(new ParameterizedTypeReference<Map<String, Object>>() {}); | ||
|
|
||
| if (body == null) throw new IllegalStateException("Kakao API returned null body"); |
There was a problem hiding this comment.
스프링 부트와 RestClient의 장점이 많이 상쇄된 것 같습니다! 장점을 극대화하려면 응답 구조에 맞는 record 클래스 같은 dto를 사용하는 것이 좋아보입니다!
오프세, 커서 기반 페이지네이션 및 request body가 있는 API에 검증 어노테이션 추가 작업을 했습니다.
이번에 Page라는 offset 기반의 타입이 있었는데 실제 현업에서 Page type의 response를 봤어서 내려오는 방식이 신기했었는데 이런 과정들이 있었구나 하고 느꼈습니다.
1. 내가 진행중인 미션 조회하기 (오프셋 기반)
2. 내가 생성한 리뷰들 조회하기 (커서 기반)
해당 API는 cursor 기반으로 잡고, request parameter에 orderby 넣어줄 요소를 추가로 받아서 처리했습니다.
3. Request Body가 있는 API에 검증 어노테이션 붙혀 검증하기
GeneralErrorCode에 Validation error 를 추가해서 검증에 대한 exception 처리를 했습니다.