드디어 구조에 대해 볼 수 있어!
- Spring Data JPA의 내부 동작 구조, 구현 원리 이해
- Repository 계층 구조, 각 인터페이스의 역할을 실무적으로 보기
- 자동 구현 방식과 Query 생성 원리
- 실무에서 사용할 수 있는 Repository 작성법, 예시 알기
- Spring Data JPA / JPA / Hibernate의 관계 이해
Spring Data JPA : 기본 구조
Spring Data JPA는 JPA의 진짜 개 어려운, 아직도 헤메고 있는 보일러플레이트? 한 코드를 대폭 줄이기 위한 스프링 프로젝트다
Repository 인터페이스만 정의해주면 구현하지 않아도 CRUD, 페이징, 정렬, 쿼리 작성까지 자동 제공한다
개발자는 핵심 비즈니스 로직에만 집중할 수 있게 된다
하지만,
어렵다고 Spring Data JPA에 의존만 하지말고
제대로 이해하기 위해 Service, Repository를 계속 분석하고 공부하자
Repository 인터페이스 계층 구조 : Spring Data JPA의 Repository 구조
Repository\ (Marker Interface)
Repository\CrudRepository<T, ID>
Repository\CrudRepository<T, ID>\save(), findById(), deleteById(), 등등...
Repository\PagingAndSortingRepository<T, ID> - > 페이징, 정렬기능 제공
Repository/JpaRepository<T, ID> - > CRUD + 페이징 + JPA 확장기능 제공
| 인터페이스 | 기능 | 주의사항 |
| Repository | 마커(Marker) 인터페이스 | 기능없음, 인식용 |
| CrudRepository | CRUD(생성, 조회, 수정, 삭제) | 기본적 Entity 관리하기 |
| PagingAndSortingRepository | 페이징, 정렬 | Pageable, Sort를 지원함 |
| JpaRepository | CRUD + 페이징 + Batch, Flush | 가장 널리 사용된다고 한다 |
* 실무에서는 대부분 JpaRepository를 사용한다 (CRUD + 페이징 + Batch, Flush)
* QueryDSL, Specification, Native Query도 함께 사용할 수 있다.
Spring Data JPA가 제공하는 기능들
| 기능 | 설명 | 예 |
| 메서드 이름 기반 쿼리(ex findAllById) | findBy로 시작하는 규칙 기반 쿼리 | findByUsername(String userName) |
| @Query | JPQL 직접 작성 가능 | @Query ("select * from members as m where m.age > :age") |
| QueryDSL | 동적 쿼리 빌더 (추가적인 라이브러리가 필요함) |
|
| Specification | 복합 검색(Criteria API 기반) | |
| Projections | DTO 매핑(interface, class) | |
| Native Query | 원시 SQL을 지원함 | nativeQuery = true |
자동 구현의 원리에 대해서
동작방식
1. Repository 인터페이스를 작성함(extends JpaRepository<T ID>(상속))
2. Spring Boot 실행 시 프록시 객체를 생성함(SimpleJpaRepository)
3. 메서드 이름을 분석하고 - > JPQL을 생성한다
4. EntityManager를 통해서 DB를 조회함
Controller - > Service - > MemberRepository (프록시 객체 : SimpleJpaRepository)
SimpleJpaRepository - EntityManager 를 통해 DB 조회?
자동 쿼리 생성 규칙 예
findByUserName - > SELECT m FROM Member AS m WHERE m.userName = ?1
findByEmailAndAge - > SELECT m FROM Member AS m WHERE m.email = ?1 AND m.age = ?2
findTop3ByOrderByAgeDesc - > SELECT m FROM Member AS m ORDER BY m.age DESC LIMIT 3
실무 예시.. 는 아니고 내 과제 코드
Entity 작성
User, 내 과제코드
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.io.Serial;
import java.io.Serializable;
import java.time.Instant;
import java.util.UUID;
@Entity // @Entity를 적어주니 @AllArgsContructor를 안써도 되나..?
@Getter
@Setter
@NoArgsConstructor(force=true)// 클래스에 필수 필드가 포함되어 있다. NoArgsContructor를 강제 적용해야한다.
public class User implements Serializable {
@Serial
private static final long serialVersionUID = 1L; // 직렬화
@Id // PK에 적어주는것?
@GeneratedValue
private final UUID id;
@Column(nullable = false) // not null
private final Instant createdAt;
@Column(nullable = true) // nullable = true가 default값이다
private Instant updatedAt;
@Column(nullable = false, length = 50)
private String username;
@Column(nullable = false, unique = true, length = 100) // unique key
private String email;
@Column(nullable = false, length = 60)
private String password;
// 따로 생성자를 하나 만들었음
public User(String nickName, String email, String password) {
this.id = UUID.randomUUID();
this.createdAt = Instant.now();
this.username = nickName;
this.email = email;
this.password = password;
}
Repository 작성 : 예시
// 인터페이스만 적어주면됨, extends JpaRepository 잊지 말것
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByUsername(String username);
List<User> findByAge(int age);
}
public interface OrderRepository extends JpaRepository<Order, Long> {
List<Order> findByUser_UserId(User user);
}
Service 작성 : 예시
@Service
@RequiredArgsconstructor
public class MemberService {
private final UserRepository userRepository;
public User saveUser(User user) {
return userRepository.save(user);
}
public User findUser(String userName) {
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new IllegalArgumentException("not found"));
return user;
}
public List<User> findUsers(int age) {
return userRepotiroy.findByAge(age);
}
}
Controller 작성 : 예시
@RestController
@RequiredArgsConstructor
@RequestMapping("/users")
public class UserController {
private final UserService userService;
@PostMapping
public User createUser(@RequestBody User user) {
return userService.saveUser(user);
}
@GetMapping("/{username}")
public User getUser(@PathVariable("username") String username) {
return userService.findUser(username);
}
@GetMapping()
public List<User> getUsers(@RequestParam int age) {
return userService.findUsers(age);
}
}
* 기본 제공 기능은 JpaRepository로도 실무에서 대부분 충분하다고 함
* QueryDSL로 복잡한 검색들을 보완할 수 있다
* Native Query가 필요하면 @Query(nativeQuery = true)를 넣으면 된다고 함..
* Specification을 사용하면 유지보수가 복잡해질 수 있다. 가급적이면 QueryDSL을 사용하는게 좋다.
정리
| 특징 | |
| Repository | Maker 역할이며, 기능은 따로 없다고 함 |
| CrudRepository | 기본적 CRUD를 제공해준다 |
| PagingAndSortingRepository | 페이징 기능, 정렬을 추가해준다 |
| JpaRepository | CRUD + JPA 특화 기능을 제공해준다 |
| 주요 기능 | 메서드 규칙 기반 자동 쿼리, @Query, QueryDSL |
Spring Data JPA는 개발자의 반복적 CRUD 부담을 줄이고, 명명 규칙 기반으로 자동 쿼리를 제공해준다.
JpaRepository를 extends 해주는 것으로 대부분의 기능을 사용할 수 있으며,
복잡한 검색의 경우 QueryDSL이나 @Query로 보완할 수 있다.
'Spring Boot' 카테고리의 다른 글
| Spring Data JPA : Repository 작성 (0) | 2025.07.25 |
|---|---|
| Entity : 연관관계 맵핑 (3) | 2025.07.25 |
| Spring Data JPA : 프로젝트 설정 (0) | 2025.07.22 |
| Spring Data JPA (2) | 2025.07.22 |
| Entity 설계 (7) | 2025.07.22 |