JPA 벌크 업데이트로 다건 상태 갱신 최적화 (IN + @Modifying)

송현진·2025년 8월 12일
0

Jpa

목록 보기
12/12

운영자가 크롤링된 상품들 중 일부를 선택해 확정(isConfirmed) 상태를 일괄 변경해야 했다. 기존에는 findById → setIsConfirmed → save를 반복하면서 엔티티 단위로 갱신했는데 이 방식은 건수만큼의 SELECT/UPDATE와 엔티티 관리 비용이 발생해 비효율적이다. 그래서 DB에 직접 한 번의 UPDATE를 보내는 벌크 쿼리로 바꿨다.

public interface CrawlingProductRepository extends JpaRepository<CrawlingProduct, Long> {

    @Modifying(clearAutomatically = true, flushAutomatically = true)
    @Query("""
      UPDATE CrawlingProduct p
         SET p.isConfirmed = :isConfirmed,
             p.updatedAt = CURRENT_TIMESTAMP
       WHERE p.id IN :ids
    """)
    int bulkUpdateConfirm(@Param("ids") List<Long> ids,
                          @Param("isConfirmed") boolean isConfirmed);
}

이 방식으로 변경하면 대량의 상품 상태를 갱신할 때에도 단 한 번의 UPDATE 쿼리로 처리할 수 있어 성능이 크게 향상된다. 기존처럼 건수만큼 SELECTUPDATE를 반복하지 않으므로 네트워크 왕복 횟수와 JPA의 더티체킹, 스냅샷 관리 같은 불필요한 연산이 사라진다. 또한 WHERE p.id IN (:ids) 조건이 기본키를 사용하므로 PK 인덱스를 바로 활용해 검색 속도가 빠르며 대량 엔티티를 1차 캐시에 적재하지 않아 메모리 사용량을 줄일 수 있다. 한 번의 쿼리로 원자적으로 상태를 변경하기 때문에 트랜잭션 관리가 단순해지고 반환값으로 영향받은 행 수를 확인할 수 있어 운영 화면에서 직관적인 피드백 제공도 가능하다.

어노테이션/옵션 이유

  • @Modifying : 작성한 JPQL이 조회용이 아니라 데이터를 수정하는 쿼리임을 스프링 데이터 JPA에 알려준다. 이게 없으면 JPA는 이 쿼리를 SELECT로 해석하려고 해서 예외가 발생한다.
  • flushAutomatically = true : 벌크 업데이트 실행 전에 영속성 컨텍스트에 쌓인 미반영 변경사항을 먼저 DB에 반영한다. 그렇지 않으면 이전 변경 내용이 덮어씌워질 수 있다.
  • clearAutomatically = true : 벌크 업데이트는 1차 캐시(영속성 컨텍스트)를 거치지 않기 때문에 기존에 메모리에 올라와 있던 엔티티와 DB 상태가 불일치할 수 있다. 이 옵션을 켜면 쿼리 실행 후 자동으로 1차 캐시를 비워 stale 데이터 문제를 막는다.

주의할 점

벌크 업데이트는 JPA의 엔티티 관리 흐름을 우회하기 때문에 몇 가지 주의가 필요하다. 먼저 @Version을 이용한 낙관적 락이 동작하지 않아 동시 수정 검증이 필요한 경우에는 버전 조건을 직접 넣거나 사전 조회 로직을 추가해야 한다. 또한 IN 절에 너무 많은 ID를 한 번에 넣으면 성능이 저하되거나 DB/드라이버 바인딩 한도를 초과할 수 있으니 대량 처리 시에는 적절한 크기로 청크를 나눠 실행하는 것이 안전하다. 그리고 동일 트랜잭션 안에서 벌크 실행 전 이미 로드한 엔티티가 있다면 벌크 실행 후에도 그 엔티티의 필드 값이 갱신되지 않으므로 반드시 캐시를 비우거나 재조회해야 한다. 마지막으로 CURRENT_TIMESTAMP는 DB 시간을 사용하므로 애플리케이션 서버 시간과 다를 수 있다. 시간 동기화가 필요하면 서버와 DB의 NTP 설정을 확인하는 것이 좋다.

📝 배운점

단순한 상태 변경 작업은 굳이 엔티티를 조회해서 변경하는 것보다 벌크 업데이트를 통해 직접 DB에 반영하는 것이 훨씬 효율적이라는 걸 다시 느꼈다. 하지만 벌크 업데이트는 JPA의 엔티티 관리 기능과 일관성 보장 장치를 거치지 않기 때문에 플러시, 캐시 비우기, 락 처리 같은 보조 장치를 반드시 함께 고려해야 한다. 특히 PK 조건이 있는 경우 인덱스가 이미 잘 잡혀 있어 추가 튜닝이 필요 없지만 조건이 바뀌면 인덱스 설계까지 같이 재검토해야 한다는 점도 기억하게 됐다.

profile
개발자가 되고 싶은 취준생

0개의 댓글