2024. 10. 19. 17:50ㆍSpring Boot/Spring Data JPA
Repository 인터페이스 정의하기
먼저, 도메인 클래스에 특정한 Repository 인터페이스를 정의해야 합니다. 이 인터페이스는 반드시 Repository를 확장해야 하며, 도메인 클래스와 해당 도메인 클래스의 ID 타입으로 지정해야 합니다. 도메인 타입에 대해 CRUD(생성, 읽기, 업데이트, 삭제) 메서드를 노출하고 싶다면, CrudRepository 또는 그 변형 중 하나를 확장할 수 있습니다.
Repository 정의 미세 조정
Repository 인터페이스를 정의하는 몇 가지 방법이 있습니다.
- CrudRepository 확장
가장 일반적인 방법은 CrudRepository를 확장하는 것입니다. 이 인터페이스를 사용하면 CRUD 작업을 위한 메서드를 사용할 수 있습니다. CRUD는 Create(생성), Read(읽기), Update(업데이트), Delete(삭제)를 의미합니다. 3.0 버전에서는 ListCrudRepository도 도입되었는데, 이 인터페이스는 CrudRepository와 유사하지만 여러 엔티티를 반환할 때 Iterable 대신 List를 반환하는 차이점이 있습니다. - Reactive Store를 사용하는 경우
만약 Reactive Store를 사용하는 경우에는 ReactiveCrudRepository 또는 RxJava3CrudRepository를 선택할 수 있습니다. 사용 중인 Reactive 프레임워크에 따라 적합한 인터페이스를 선택하면 됩니다. - Kotlin을 사용하는 경우
Kotlin을 사용하는 경우에는 CoroutineCrudRepository를 사용할 수 있습니다. 이 인터페이스는 Kotlin의 코루틴을 활용하여 비동기 작업을 처리합니다. - 정렬 및 페이징 지원
정렬 또는 페이징 기능이 필요한 경우 PagingAndSortingRepository, ReactiveSortingRepository, RxJava3SortingRepository, CoroutineSortingRepository를 확장할 수 있습니다. 단, 3.0 버전 이후로 이러한 정렬용 인터페이스는 더 이상 CRUD 인터페이스를 확장하지 않으므로, 정렬 및 페이징 기능과 CRUD 기능을 모두 사용하려면 두 인터페이스를 모두 확장해야 합니다. - @RepositoryDefinition 사용
Spring Data 인터페이스를 확장하고 싶지 않은 경우에는 @RepositoryDefinition을 사용하여 Repository 인터페이스를 정의할 수 있습니다. CrudRepository 중 일부 메서드만 노출하려는 경우, 원하는 메서드만 직접 복사해서 사용할 수 있습니다. 여러 엔티티를 반환하는 메서드에 대해서는 Iterable, List, Collection 또는 VAVR list 중 적절한 반환 타입을 선택할 수 있습니다. - 공통 메서드를 가진 기본 인터페이스 정의
여러 Repository가 동일한 메서드를 가지도록 하려면 공통 기본 인터페이스를 정의하고 이를 상속받게 할 수 있습니다. 이런 인터페이스는 @NoRepositoryBean으로 어노테이션 처리하여야 합니다. 이는 Spring Data가 직접 인스턴스를 생성하지 않도록 방지합니다.
CRUD 메서드 선택적으로 노출하기
필요에 따라 CRUD 메서드를 선택적으로 노출할 수 있습니다. 다음은 findById와 save만을 노출하는 인터페이스의 예입니다.
@NoRepositoryBean
interface MyBaseRepository<T, ID> extends Repository<T, ID> {
Optional<T> findById(ID id);
<S extends T> S save(S entity);
}
interface UserRepository extends MyBaseRepository<User, Long> {
User findByEmailAddress(EmailAddress emailAddress);
}
이 예에서 공통적인 기본 인터페이스를 정의하여 모든 도메인 Repository에서 findById(…)와 save(…) 메서드를 사용할 수 있습니다. 이 메서드들은 기본적으로 Spring Data가 제공하는 저장소 구현체로 라우팅됩니다. 예를 들어 JPA를 사용하는 경우 SimpleJpaRepository가 이를 구현합니다. 이로 인해 UserRepository는 사용자 저장, 사용자 ID로 개별 사용자 찾기, 이메일 주소로 사용자 검색 쿼리를 실행할 수 있습니다.
주의: 중간 Repository 인터페이스에는 반드시 @NoRepositoryBean 어노테이션을 추가해야 합니다. 그렇지 않으면 Spring Data가 런타임에 인스턴스를 생성하려고 시도하며 실패하게 됩니다.
Using Repositories with Multiple Spring Data Modules
하나의 Spring Data 모듈만 사용하는 경우는 간단하지만, 여러 모듈을 사용할 때는 각 Repository 정의가 어느 모듈을 사용할지 명확히 구분되어야 합니다. Spring Data는 여러 Repository 팩토리가 클래스 경로에 있을 때 엄격한 Repository 구성 모드에 들어갑니다. 이때 Repository 또는 도메인 클래스의 세부 정보를 사용하여 각 Repository 정의가 특정 모듈과 연관될 수 있도록 결정합니다.
모듈별 인터페이스 사용
모듈별 인터페이스를 사용하는 경우, Repository 정의는 특정 Spring Data 모듈에 속하게 됩니다. 예를 들어 JpaRepository를 확장하면 해당 Repository는 Spring Data JPA 모듈에 속하게 됩니다.
interface MyRepository extends JpaRepository<User, Long> { }
@NoRepositoryBean
interface MyBaseRepository<T, ID> extends JpaRepository<T, ID> { … }
interface UserRepository extends MyBaseRepository<User, Long> { … }
이 예제에서, MyRepository와 UserRepository는 JpaRepository를 확장하고 있으므로 Spring Data JPA 모듈의 유효한 후보가 됩니다.
일반 인터페이스 사용
Repository나 CrudRepository와 같은 일반적인 인터페이스만 확장할 경우, 여러 모듈을 사용할 때 어떤 모듈과 연관될지 명확히 구분할 수 없습니다. 이런 경우에는 명확한 모듈별 Repository 정의를 사용해야 합니다.
interface AmbiguousRepository extends Repository<User, Long> { … }
@NoRepositoryBean
interface MyBaseRepository<T, ID> extends CrudRepository<T, ID> { … }
interface AmbiguousUserRepository extends MyBaseRepository<User, Long> { … }
도메인 클래스에 어노테이션 사용
도메인 클래스에 모듈별 어노테이션을 사용하여 특정 모듈과의 연관성을 명확히 할 수도 있습니다. 예를 들어 JPA를 사용하는 경우 @Entity 어노테이션을 사용하고, MongoDB를 사용하는 경우 @Document 어노테이션을 사용할 수 있습니다.
interface PersonRepository extends Repository<Person, Long> { … }
@Entity
class Person { … }
interface UserRepository extends Repository<User, Long> { … }
@Document
class User { … }
이 예에서는 PersonRepository는 JPA @Entity 어노테이션을 사용하는 Person을 참조하므로 JPA 모듈과 연관됩니다. UserRepository는 MongoDB @Document 어노테이션을 사용하는 User를 참조하므로 MongoDB 모듈과 연관됩니다.
잘못된 예: 혼합된 어노테이션 사용
하나의 도메인 클래스에 여러 모듈별 어노테이션을 혼합해서 사용하는 것은 Spring Data가 어떤 모듈과 연관될지 판단할 수 없으므로 피해야 합니다.
interface JpaPersonRepository extends Repository<Person, Long> { … }
interface MongoDBPersonRepository extends Repository<Person, Long> { … }
@Entity
@Document
class Person { … }
이 예에서 Person 클래스는 JPA와 MongoDB 어노테이션을 모두 사용하여 두 가지 모듈과 혼동을 일으킵니다. 이는 Spring Data에서 정의되지 않은 동작을 초래할 수 있습니다.
Repository 스캔 범위 설정
마지막으로, 여러 Spring Data 모듈을 사용할 때는 Repository 정의를 적절한 패키지에 위치시키고 각 모듈별로 Repository를 스캔할 패키지를 설정해야 합니다. @EnableJpaRepositories와 @EnableMongoRepositories를 사용하여 각각의 모듈에 맞는 Repository 패키지를 설정할 수 있습니다.
@EnableJpaRepositories(basePackages = "com.acme.repositories.jpa")
@EnableMongoRepositories(basePackages = "com.acme.repositories.mongo")
class Configuration { … }
이렇게 하면 JPA 모듈은 com.acme.repositories.jpa 패키지를, MongoDB 모듈은 com.acme.repositories.mongo 패키지를 스캔하여 Repository를 찾아서 등록하게 됩니다.
이와 같이, 다중 모듈 환경에서도 올바른 Repository 정의를 통해 각각의 모듈이 정확하게 작동할 수 있도록 설정할 수 있습니다.
'Spring Boot > Spring Data JPA' 카테고리의 다른 글
Persisting Entities (0) | 2024.10.19 |
---|---|
Configuration (3) | 2024.10.19 |
Core concepts (0) | 2024.10.19 |
Getting Started (0) | 2024.10.19 |
Query Hint (0) | 2023.06.06 |