안녕하세요. 오늘도 이번 주 받은 코드 리뷰에 대해서 정리해 볼 예정입니다.
제가 받은 피드백은 아래 pr을 통해 확인하실 수 있습니다.
https://github.com/kakao-tech-campus-2nd-step2/spring-gift-jpa/pull/55
충남대 BE_이은경 3주차 과제 (1단계) by pkyung · Pull Request #55 · kakao-tech-campus-2nd-step2/spring-gift-jpa
과제 진행 사항 엔티티 매핑 jdbc template 기반 코드 jpa 로 리펙토링 멤버 클래스 엔티티 매핑 상품 클래스 엔티티 매핑 위시 클래스 엔티티 매핑
github.com
https://github.com/kakao-tech-campus-2nd-step2/spring-gift-jpa/pull/182
충남대 BE_이은경 3주차 과제 (2단계) by pkyung · Pull Request #182 · kakao-tech-campus-2nd-step2/spring-gift-jpa
과제 진행 사항 연관 관계 매핑 위시 클래스 연관 관계 매핑
github.com
https://github.com/kakao-tech-campus-2nd-step2/spring-gift-jpa/pull/301
충남대 BE_이은경 3주차 과제(3단계) by pkyung · Pull Request #301 · kakao-tech-campus-2nd-step2/spring-gift-jpa
과제 진행 사항 페이지네이션 로그인 / 회원가입 페이지 생성 및 js 위시 리스트 페이지 생성 및 js spring data jpa를 이용해서 페이지네이션 추가 상품과 위시리스트에 페이지네이션 적용
github.com
구현한 기능📄
이번 주는 jdbc였던 코드는 jpa로 수정하고, 저번에 만들었던 위시리스트 클래스에 연관관계를 달았습니다.
또한, 페이지네이션을 구현하고 페이지를 제작하는 작업을 했습니다.
# spring-gift-jpa 🎁
## 3주차 기능 목록 📄
### 엔티티 매핑
- [x] jdbc template 기반 코드 jpa 로 리펙토링
- [x] 멤버 클래스 엔티티 매핑
- [x] 상품 클래스 엔티티 매핑
- [x] 위시 클래스 엔티티 매핑
### 연관 관계 매핑
- [x] 위시 클래스 연관 관계 매핑
### 페이지네이션
- [x] 로그인 / 회원가입 페이지 생성 및 js
- [x] 위시 리스트 페이지 생성 및 js
- [x] spring data jpa를 이용해서 페이지네이션 추가
- [x] 상품과 위시리스트에 페이지네이션 적용
이번 주는 점차 피드백이 줄고 있음을 체감했습니다. 아마도 제가 멘토님 마음에 코드를 짜고 있는 게 아닐까 행복 회로를 돌려보았습니다.😎
받은 피드백🎯 - step1 (엔티티 매핑)
테스트는 클래스 별로 분리해라
기존에는 적은 테스트의 양으로 분리하기가 뭔가 아까운 느낌이 들었습니다. 또한, 위시리스트의 경우 멤버와 상품 다 연관성이 있기에 세 개의 repository를 선언해야하기에 굳이 나누지 않아도 똑같다는 느낌이 들었습니다.
하지만 테스트 코드는 클래스 별로 1:1 매칭되는 것이 좋다고 하셨습니다. 추후 코드량이 늘어나서 테스트 케이스의 양이 많아져 늘어나도 클린한 코드를 유지할 수 있기 때문이라고 말씀해주셨습니다.
// 기존
public class JpaRepositoryTest {
@Autowired
private MemberRepository memberRepository;
@Autowired
private ProductRepository productRepository;
@Autowired
private WishRepository wishRepository;
}
// 수정
public class JpaMemberRepositoryTest {
@Autowird
private MemberRepository memberRepository;
}
public class JpaProductRepositoryTest {
@Autowird
private ProductRepository productRepository;
}
public class JpaWishRepositoryTest {
@Autowired
private MemberRepository memberRepository;
@Autowired
private ProductRepository productRepository;
@Autowired
private WishRepository wishRepository;
}
자바 17이상에서 string 관리하기
기존에 쿼리를 이런 식으로 작성했는데 멘토님께서 자바 17이상이니 """ 이걸 쓰면 더 깔끔할 것 같다고 하셨습니다.
public interface WishRepository extends JpaRepository<Wish, Long> {
@Query("SELECT new gift.model.Product(p.id, p.name, p.price, p.imageUrl) " +
"FROM products p " +
"JOIN wishes w ON p.id = w.productId " +
"WHERE w.memberId = :memberId")
List<Product> findAllByMemberId(@Param("memberId") Long memberId);
void deleteByProductIdAndMemberId(Long productId, Long memberId);
}
사실 항상 자바 8 또는 11을 이용해서 프로젝트를 진행했기에 이 부분을 모르고 있었습니다. 그래서 자바 17 string """ 이라고 구글링해보니 자바 17이상에서 text blocks을 사용하는 예제가 있었습니다.
이렇게 text blocks을 이용하여 긴 문자열을 쉽게 다룰 수 있는 것입니다.
public class Main {
public static void main(String[] args) {
String multilineText = """
Java 17의 Text Blocks를 사용하여,
멀티 라인 문자열을 쉽게 작성할 수 있습니다.
들여쓰기와 줄바꿈이 자동으로 처리됩니다.
""";
System.out.println(multilineText);
}
}
그래서 바로 수정해보았습니다.
String findAllByMemberIdQuery = """
SELECT new gift.model.Product(p.id, p.name, p.price, p.imageUrl)
FROM products p
JOIN wishes w ON p.id = w.product.id
WHERE w.member.id = :memberId
""";
@Query(findAllByMemberIdQuery)
List<Product> findAllByMemberId(@Param("memberId") Long memberId);
받은 피드백🎯 - step2 (연관관계 매핑)
이 부분에서는 N+1을 잘 해결했다는 칭찬을 받았습니다😀 (야호)
NotEmpty와 NotBlank의 차이점 알아보기
NotEmpty, NotNull, NoyBlank 세 개의 차이에 대해 알아보았습니다.
@NotNull
- null만 허용하지 않는다.
- "" 이나 " "는 허용한다.
@NotEmpty
- null과 ""를 허용하지 않는다.
- " " 허용한다.
@NotBlank
- null과 "" 과 " " 를 모두 허용하지 않는다.
- 따라서 @NotBlank가 걸려있으면 위의 두 개를 모두 검증한다.
그래서 String일 경우 모두 @NotBlank로 수정해주었습니다.
받은 피드백🎯 - step3 (페이지네이션)
페이지네이션 오프셋 방식 vs 커서 방식
제가 사용하고 있는 페이지네이션은 무슨 방식이며 지금 상황에서 어떤 방식을 사용하는 것이 효과적인지에 대해서 물어봐주셨습니다.
String findAllByMemberIdQuery = """
SELECT new gift.model.Product(p.id, p.name, p.price, p.imageUrl)
FROM products p
JOIN wishes w ON p.id = w.product.id
WHERE w.member.id = :memberId
""";
@Query(findAllByMemberIdQuery)
Page<Product> findAllByMemberId(@Param("memberId") Long memberId, Pageable pageable);
그래서 페이지네이션의 오프셋 방식과 커서 방식에 대해 알아보았습니다.
오프셋 방식
오프셋 방식의 페이지 처리는 클라이언트에서 페이지 당 요청하는 자료의 개수와 현재 페이지 번호를 파라미터로 요청하고, 서버에서 오프셋 값을 구하는 방식입니다. 이 값은 쿼리의 offset 부분에 입력되는 값입니다.
이 방식의 단점은 뒷 부분의 쿼리에서 속도 저하 문제가 있습니다. offset의 개수가 늘어날수록 굉장히 느려진다고 합니다. 그 이유는 데이터의 개수는 변경될 수 있기 때문에 매번 데이터를 확인하여 해당 offset 수 만큼 지나간 후에 데이터를 반환하기 때문입니다.
하지만 구현 방식이 쉽고 쿼리가 복잡하지 않습니다.
커서 방식
커서 방식의 페이지 처리는 limit와 where 절을 사용하여 쿼리를 제작합니다. 이 방식은 커서가 뒤로 많이 간 상황에서도 항상 일정한 성능을 보장한다는 장점이 있습니다.
커서 방식은 무한 스크롤이나 더보기를 구현할 때 좋다고 합니다.
하지만 구현 난이도가 있습니다.
SELECT * FROM products LIMIT 40;
SELECT * FROM products WHERE id > 40 LIMIT 40;
그래서 아래와 같이 대답했습니다.
지금 사용하고 있는 방식은 오프셋 방식입니다.
현재 상태에서는 오프셋 방식을 사용하는 것이 효과적일 것 같습니다.
오프셋 방식은 커서 방식에 비해서 구현이 쉽고 직관적입니다.
그리고 데이터가 많은 성능이 중요한 프로그램이 아니기 때문입니다.
데이터의 양이 많아진다면 커서 방식의 구현이 필요할 것 같습니다.
후기🎓
이번 주도 좋은 피드백을 많이 받아 발전하는 일주일이었습니다. 특히, 페이지네이션에 대해 대충 코드만 작성할 줄만 알지 고민해본적이 없었는데 생각하면서 구현할 수 있는 시간이었습니다.
'🍫카카오 테크 캠퍼스 2기 BE' 카테고리의 다른 글
[카카오 테크 캠퍼스 / BE] 아이디어톤 상을 타버렸습니다 (1) | 2024.08.30 |
---|---|
[카카오 테크 캠퍼스 / BE] 2단계 네 번째 코드 리뷰 (1) | 2024.07.24 |
[카카오 테크 캠퍼스 / BE] 2단계 두 번째 코드 리뷰 (0) | 2024.07.14 |
[카카오 테크 캠퍼스 / BE] 2단계 첫 번째 코드 리뷰 후기 (0) | 2024.07.02 |
[카카오 테크 캠퍼스 / BE] 두 번째 미니과제, 자동차 경주🚗 (1) | 2024.06.10 |