스프링 데이터 JPA의 트레이드 오프
- 스프링 데이터 JPA를 도입한 기존 예제
- 도입 과정
- JpaRepository를 구현하는 인터페이스 SpringDataJpaItemRepository를 생성
- 기존에 있던 ItemService는 ItemRepository라는 인터페이스에 의존하고 있기에 서로 맞지 않는다.
서로 맞추려면 ItemService의 의존 관계를 추가해야하고 코드에 변경이 생긴다.
- 기존에 있던 ItemService는 ItemRepository라는 인터페이스에 의존하고 있기에 서로 맞지 않는다.
- 어댑터 역할을 하도록 JpaItemRepositoryV2를 ItemRepository를 구현하도록 만들고 SpringDataJpaItemRepositor에 의존하도록 만들었다.
- ItemService는 ItemRepository를 그대로 의존하며 코드를 변경하지 않을 수 있게 됐다.
- JpaRepository를 구현하는 인터페이스 SpringDataJpaItemRepository를 생성
- 문제점
- 구조를 맞추기위해 어댑터 역할을 하는 JpaItemRepositoryV2가 들어가면서 전체 구조가 복잡해지고 사용하는 클래스도 많아졌다.
- DI, OCP 원칙은 지킬 수 있지만 구조가 복잡해지면서 어댑터 코드와 실제 코드까지 함께 유지보수해야 한다.
- 다른 방법
- ItemService의 코드를 일부 고쳐서 의존 관계 SpringDataJpaItemRepository를 추가하는 방법
- DI, OCP 원칙에는 어긋나지만 복잡한 어댑터를 제거하고 구조를 단순하게 가져갈 수 있다.
- ItemService의 코드를 일부 고쳐서 의존 관계 SpringDataJpaItemRepository를 추가하는 방법
- 트레이드 오프
- DI, OCP 원칙을 지키기 위해 어댑터를 도입하고 더 많은 코드를 유지
- 어댑터를 제거하고 구조를 단순하게 가져가지만 DI, OCP 원칙을 포기
- 여기서 발생하는 트레이드 오프는 구조의 안정성 vs 단순한 구조와 개발의 편리성 사이의 선택으로 옳고 그름은 없다.
- 어설픈 추상화는 독이 되는 경우가 많고 추상화도 비용이 발생한다. (유지보수, 인건비, 코드 작성...)
- 추상화 비용을 넘어설 만큼의 효과가 있을 때 추상화를 도입하는 것이 실용적이다.
- 도입 과정
실용적인 구조
- JPA + Querydsl을 사용한 예제를 스프링 데이터 JPA + Querydsl을 사용하도록 변경
- 단순한 기본 CRUD는 스프링 데이터 JPA 레포지토리가 담당
- 복잡한 쿼리는 Querydsl 레포지토리가 담당
//간단한 CRUD를 위한 Spring Data JPA Repository
public interface ItemRepositoryV2 extends JpaRepository<Item, Long> {
}
- 기본적인 CRUD 사용을 위한 레포지토리
//Querydsl을 사용해서 복잡한 쿼리를 처리하는 Repository
@Repository
public class ItemQueryRepositoryV2 {
private final JPAQueryFactory query;
public ItemQueryRepositoryV2(EntityManager em) {
this.query = new JPAQueryFactory(em);
}
public List<Item> findAll(ItemSearchCond cond) {
return query.select(item)
.from(item)
.where(
likeItemName(cond.getItemName()),
maxPrice(cond.getMaxPrice()))
.fetch();
}
private BooleanExpression likeItemName(String itemName) {
if (StringUtils.hasText(itemName)) {
return item.itemName.like("%" + itemName + "%");
}
return null;
}
private Predicate maxPrice(Integer maxPrice) {
if (maxPrice != null) {
return item.price.loe(maxPrice);
}
return null;
}
}
- Querydsl을 사용해 복잡한 쿼리(동적 쿼리)를 처리하는 레포지토리
@Service
@RequiredArgsConstructor
@Transactional
public class ItemServiceV2 implements ItemService {
private final ItemRepositoryV2 itemRepositoryV2;
private final ItemQueryRepositoryV2 itemQueryRepositoryV2;
@Override
public Item save(Item item) {
return itemRepositoryV2.save(item);
}
@Override
public void update(Long itemId, ItemUpdateDto updateParam) {
Item findItem = itemRepositoryV2.findById(itemId).orElseThrow();
findItem.setItemName(updateParam.getItemName());
findItem.setPrice(updateParam.getPrice());
findItem.setQuantity(updateParam.getQuantity());
}
@Override
public Optional<Item> findById(Long id) {
return itemRepositoryV2.findById(id);
}
@Override
public List<Item> findItems(ItemSearchCond cond) {
return itemQueryRepositoryV2.findAll(cond);
}
}
- 서비스 계층에서도 단순 쿼리에는 스프링 데이터 JPA 레포지토리인 itemRepositoryV2를 사용했고 복잡한 쿼리를 사용할 때는 Querydsl을 사용한 itemQueryRepositoryV2를 사용했다.
- 스프링 데이터 JPA가 제공하는 커스텀 레포지토리(사용자 정의 레포지토리)를 사용해도 비슷하게 문제를 해결할 수 있다.
다양한 데이터 접근 기술 조합
- JdbcTemplate, MyBatis 같은 기술은 직접 SQL을 작성해야 하지만 기술이 단순하기에 SQL에 익숙하다면 금방 적응할수 있다.
- JPA, 스프링 데이터 JPA, Querydsl 같은 기술은 개발 생산성을 혁신할 수 있지만 학습 곡선이 높기 때문에 감안해야 한다.
- 복잡한 통계 쿼리를 주로 작성하는 경우에 맞지 않는다.
- JPA, 스프링 데이터 JPA, Querydsl을 기본적으로 사용하고 복잡한 쿼리를 사용해야 하는 부분에는 JdbcTemplate이나 MyBatis를 함께 사용하는 것을 권장한다.
- JPA 관련 기술을 사용하는 경우에는 트랜잭션 매니저는 JpaTransactionManager를 선택하면 된다.
- JPA 관련 기술을 사용하면 스프링 부트에서 자동으로 JpaTransactionManager를 스프링 빈에 등록해준다.
- JPA와 JdbcTemplate 두 기술을 함께 사용하더라도 JpaTransactionManager가 DataSourceTransactionManager의 기능을 대부분 제공해준다.
- JPA와 JdbcTemplate을 함께 사용하는 경우 JPA 플러시 타이밍에 주의해야한다.
- JPA는 데이터 변경 시 변경사항을 즉시 DB에 반영하지 않고 트랜잭션 커밋 시점에 DB에 반영한다.
JPA를 통해 데이터 변경 후 JdbcTemplate을 호출하는 경우 문제가 발생할 수 있기에 JPA가 제공하는
플러시 기능을 사용해서 DB에 데이터를 반영해줘야 한다.
- JPA는 데이터 변경 시 변경사항을 즉시 DB에 반영하지 않고 트랜잭션 커밋 시점에 DB에 반영한다.
정리
- JPA, 스프링 데이터 JPA, Querydsl을 기본적으로 사용하고 복잡한 쿼리를 사용해야 하는 부분에는 JdbcTemplate이나 MyBatis를 함께 사용하는 것이 좋다.
- JPA와 JdbcTemplate을 함께 사용하는 경우 JPA 플러시 타이밍에 주의해야한다.
- JPA는 데이터 변경 시 변경사항을 즉시 DB에 반영하지 않고 트랜잭션 커밋 시점에 DB에 반영하기에
JPA를 통해 데이터 변경 후 JdbcTemplate을 호출하는 경우 문제가 발생할 수 있기에 JPA가 제공하는
플러시 기능을 사용해서 DB에 데이터를 반영해줘야 한다.
- JPA는 데이터 변경 시 변경사항을 즉시 DB에 반영하지 않고 트랜잭션 커밋 시점에 DB에 반영하기에
출처 : [인프런 김영한 스프링 DB 2편 - 데이터 접근 활용 기술]
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-db-2/dashboard
스프링 DB 2편 - 데이터 접근 활용 기술 강의 | 김영한 - 인프런
김영한 | 백엔드 개발에 필요한 DB 데이터 접근 기술을 활용하고, 완성할 수 있습니다. 스프링 DB 접근 기술의 원리와 구조를 이해하고, 더 깊이있는 백엔드 개발자로 성장할 수 있습니다., 백엔드
www.inflearn.com
'Spring > [인프런 김영한 스프링 DB 2편 - 데이터 접근 활용 기술]' 카테고리의 다른 글
[인프런 김영한 스프링 DB 2편 - 데이터 접근 활용 기술] 스프링 트랜잭션 전파 - 기본 (0) | 2024.11.29 |
---|---|
[인프런 김영한 스프링 DB 2편 - 데이터 접근 활용 기술] 스프링 트랜잭션 이해 (1) | 2024.11.29 |
[인프런 김영한 스프링 DB 2편 - 데이터 접근 활용 기술] 데이터 접근 기술 - Querydsl (0) | 2024.11.26 |
[인프런 김영한 스프링 DB 2편 - 데이터 접근 활용 기술] 데이터 접근 기술 - 스프링 데이터 JPA (0) | 2024.11.26 |
[인프런 김영한 스프링 DB 2편 - 데이터 접근 활용 기술] 데이터 접근 기술 - JPA (1) | 2024.11.23 |