본문 바로가기

Spring Boot

Spring Data JPA : 구조 이해

드디어 구조에 대해 볼 수 있어!

 

 - 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