노트

Spring Data 리포지터리와 쿼리 메서드

Spring Data Repositories and Query Methods

백엔드#spring#jpa · 연결된 개념 13개

쉽게 말하면

Spring Data 리포지터리는 '상태로 연락처 찾기'처럼 정해진 말투로 메서드 이름만 적어 두면, 스프링이 그 이름을 읽고 쿼리와 구현을 대신 만들어 주는 거예요. 반복되는 저장·조회 코드를 직접 짜지 않아도 돼요.

비유가 깨지는 곳 이름만 적으면 뭐든 되는 마법은 아니에요. 앱 시작 때 이름을 파싱해서 필드명 오타는 바로 실패하고, 조건이 셋을 넘어 이름이 길어지면 @Query로 직접 쓰는 편이 나아요.

Spring Data는 인터페이스만 선언하면 구현체를 런타임에 만들어 주는 영속성 추상화(persistence abstraction)다. JpaRepository<엔티티, ID타입>을 상속하면 저장·조회·삭제·페이징이 바로 생기고, 메서드 이름 규칙만으로 쿼리를 만들 수도 있다.

인터페이스 계층

  • Repository: 메서드가 없는 표시용(marker) 인터페이스
  • CrudRepository: save, findById, findAll, delete 등 기본 CRUD(Create, Read, Update, Delete)
  • PagingAndSortingRepository: 정렬·페이징
  • JpaRepository: 위를 모두 포함하고 flush, 배치 삭제 같은 JPA 기능을 더한다

필요한 만큼만 고를 수 있게 나뉜 구조라 인터페이스 분리 원칙(Interface Segregation Principle, ISP)의 예로 자주 든다.

쿼리 메서드

public interface ContactRepository extends JpaRepository<Contact, Long> {
    List<Contact> findByStatus(String status);
    List<Contact> findByStatusAndCreatedAtAfterOrderByCreatedAtDesc(String status, LocalDateTime after);
    Optional<Contact> findFirstByEmailIgnoreCase(String email);
    long countByStatus(String status);
    boolean existsByEmail(String email);
    Page<Contact> findByStatus(String status, Pageable pageable);
}
  • 이름은 By를 기준으로 앞은 동작(find·read·query·count·exists·delete), 뒤는 조건이다. 조건은 And·Or로 잇고 GreaterThan, Like, In, IsNull, OrderBy 같은 키워드를 붙인다
  • 앱이 시작할 때 이름을 파싱하므로 필드명 오타는 시작 시점에 실패한다
  • 조건이 셋을 넘어 이름이 길어지면 @Query로 직접 쓴다
  • Pageable을 받으면 페이지네이션이 된다(페이지네이션 (offset·cursor))

알아 둘 점

  • save()는 새 엔티티면 persist, 아니면 merge한다. 영속 상태의 엔티티는 save 없이도 트랜잭션 커밋 때 바뀐 값이 반영된다(더티 체킹)
  • 실행되는 SQL은 spring.jpa.show-sql=true 또는 logging.level.org.hibernate.SQL=DEBUG로 본다(로그 레벨과 Logback)
  • 연관 엔티티를 목록에서 건드리면 N+1 문제이 생기기 쉽다. @EntityGraph나 fetch join으로 함께 가져온다
  • 생성·수정 시각 자동 기록은 JPA Auditing

SQL을 직접 쓰는 아래층은 JDBC와 JdbcTemplate, 엔티티가 관리되는 원리는 영속성 컨텍스트를 본다.

책: 6장. 메모리에서 데이터베이스로 — JPA

출처: Spring Data JPA 문서: Core concepts · Query Creation · Paging and Sorting

연결된 개념

이 노트를 가리키는 문서

뜻이 가까운 노트

  • 스프링 REST 컨트롤러

    @RestController는 메서드의 반환값을 뷰 이름이 아니라 HTTP 응답 본문(보통 JSON)으로 쓰는 컨트롤러다. @Controller에 @ResponseBody를 합친 것으로, 직렬화(serialization)는 Jackson이 맡는다.

  • JPA 연관관계 매핑

    JPA(Jakarta Persistence, 옛 이름 Java Persistence API) 연관관계 매핑(association mapping)은 테이블의 외래키(foreign key) 관계를 엔티티 사이의 참조로 표현하는 방법이다. 핵심은 외래키를 관리하는 쪽(연관관계의 주인, owning side)을 정하는 것이고, 양방향 관계에서 반대쪽은 mappedBy로 "나는 읽기 전용 거울"이라고 표시한다.

  • Lombok

    Lombok은 게터·세터·생성자·toString 같은 반복 코드를 어노테이션으로 대신 생성해 주는 자바 라이브러리다. 컴파일할 때 어노테이션 처리기(annotation processor)가 바이트코드(bytecode)에 메서드를 넣는다. 스프링과는 별개 라이브러리라 따로 추가한다.

  • 영속성 전이 (cascade)

    영속성 전이(cascade, transitive persistence)는 부모 엔티티에 한 영속성 작업(저장·병합·삭제 등)을 연관된 자식 엔티티에도 자동으로 적용하는 JPA 옵션이다. 기본값은 아무것도 전이하지 않는 것이다.

  • 저장소 역할 분담 (DB·캐시·큐·검색)

    서버 애플리케이션 옆에는 거의 늘 관계형 DB, 인메모리 캐시, 메시지 브로커(message broker), 검색 엔진이 붙는다. 하나로 다 하지 않는 이유는 데이터의 성격(영구성·속도·전달·검색)마다 잘하는 도구가 다르기 때문이다.

보기 옵션