[스프링] 스프링 Data JPA에 대하여

2025. 8. 16. 18:54·스프링 데이터 JPA

스프링을 공부하다 보면 가장 자주 마주치는 주제 중 하나가 JPA다.
JPA는 객체를 데이터베이스에 매핑해주는 표준 기술이라서, 개발자가 SQL을 직접 쓰지 않고도 엔티티를 통해 데이터를 다룰 수 있게 해준다.
처음 써보면 굉장히 편리하다. 하지만 프로젝트가 커질수록, 단순한 CRUD(저장, 조회, 수정, 삭제) 코드가 계속 반복되는 걸 금방 느끼게 된다.

 

이 글에서는 왜 스프링 Data JPA가 필요하게 되었는지를 직접 코드 예시로 보여주고, 같은 기능을 스프링 Data JPA로 바꿨을 때 얼마나 코드가 간단해지는지를 비교해본다. 결국 “이런 불편함 때문에 스프링 Data JPA가 등장했다”는 걸 자연스럽게 이해하게 될 것이다.

 

JPA만으로 구현해보기

예시로 Member와 Team이라는 두 엔티티가 있다.

@Entity
@Getter @Setter
@NoArgsConstructor
@ToString(of = {"id", "username", "age"})
public class Member {
    @Id @GeneratedValue
    @Column(name = "member_id")
    private Long id;
    private String username;
    private int age;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "team_id")
    private Team team;
}
@Entity
@Getter @Setter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Team {
    public Team(String name) { this.name = name; }

    @Id @GeneratedValue
    @Column(name = "team_id")
    private Long id;
    private String name;

    @OneToMany(mappedBy ="team")
    private List<Member> members=new ArrayList<>();
}

이제 이 엔티티를 DB에 저장하고 조회하려면 레포지토리가 필요하다. JPA만 쓰면 이렇게 작성해야 한다.

 

@Repository
public class MemberJpaRepository {
    @PersistenceContext
    private EntityManager em;

    public Member save(Member member){ em.persist(member); return member; }
    public Member findById(Long id){ return em.find(Member.class,id); }
    public void delete(Member member){ em.remove(member); }
    public List<Member> findAll(){
        return em.createQuery("select m from Member m", Member.class).getResultList();
    }
    public Optional<Member> findById(Long id){
        return Optional.ofNullable(em.find(Member.class,id));
    }
    public long count(){
        return em.createQuery("select count(m) from Member m", Long.class).getSingleResult();
    }
}

겉으로 보면 단순하지만, Team 엔티티도 똑같은 CRUD가 필요하다. 그래서 또 이런 레포지토리를 만들어야 한다.

 

@Repository
public class TeamJpaRepository {
    @PersistenceContext
    private EntityManager em;

    public Team save(Team team){ em.persist(team); return team; }
    public void delete(Team team){ em.remove(team); }
    public List<Team> findAll(){
        return em.createQuery("select t from Team t", Team.class).getResultList();
    }
    public Optional<Team> findById(Long id){
        return Optional.ofNullable(em.find(Team.class,id));
    }
    public long count(){
        return em.createQuery("select count(t) from Team t", Long.class).getSingleResult();
    }
}

 

 

여기서 문제를 느낄 수 있다.

  • save, find, delete, count 같은 메서드가 엔티티마다 똑같이 반복된다.
  • 공통 정책을 바꾸려면 모든 레포지토리를 손대야 한다.
  • 정작 중요한 비즈니스 로직보다 CRUD 코드가 더 눈에 띈다.

이 반복과 복잡함이 커지면 개발자는 코드에 지쳐버린다.


바로 이런 불편함 때문에 스프링 Data JPA가 등장한 것이다.

 

 

스프링 Data JPA로 바꿔보기

같은 기능을 스프링 Data JPA로 구현하면 이렇게 끝난다.

public interface MemberRepository extends JpaRepository<Member,Long> { }
public interface TeamRepository extends JpaRepository<Team,Long> { }

이 두 줄이 아까 우리가 직접 작성했던 MemberJpaRepository, TeamJpaRepository와 똑같은 기능을 한다.
CRUD는 이미 스프링이 구현해주고 있기 때문이다. 덕분에 우리는 반복 코드를 작성하지 않아도 된다.

 

    @Test
    public void basisCRUD(){
        Member member1 = new Member("member1");
        Member member2 = new Member("member2");
        memberRepository.save(member1);
        memberRepository.save(member2);

        Member findMember1 = memberRepository.findById(member1.getId()).get();
        Member findMember2 = memberRepository.findById(member2.getId()).get();
        assertThat(findMember1.getId()).isEqualTo(member1.getId());
        assertThat(findMember1.getUsername()).isEqualTo(member1.getUsername());

        List<Member> all= memberRepository.findAll();
        assertThat(all.size()).isEqualTo(2);
        long count=memberRepository.count();
        assertThat(count).isEqualTo(2);

        //삭제 검증
        memberRepository.delete(member1);
        memberRepository.delete(member2);
        long deleteCount=memberRepository.count();
        assertThat(deleteCount).isEqualTo(0);
    }

테스트 또한 잘 동작한다.

 

 

스프링 Data JPA는 어떻게 동작하는가

여기서 중요한 점은 “우리는 단순히 인터페이스만 선언한다는 것”이다.


구현체는 직접 만들 필요가 없다. 스프링이 애플리케이션 실행 시점에 자동으로 구현 클래스를 만들어 빈으로 등록해준다.
그래서 서비스 계층에서 memberRepository.save(...), memberRepository.findAll(...) 같은 메서드를 자연스럽게 호출할 수 있는 것이다.

 

내부 구조를 그림으로 보면 이해가 쉽다.

다이어그램을 보면 맨 위에 Repository가 있고, 그 아래로 CrudRepository, PagingAndSortingRepository, JpaRepository가 확장되어 내려간다.

  • CrudRepository: 저장, 삭제, 조회 같은 기본 기능을 제공한다.
  • PagingAndSortingRepository: 이름 그대로 페이징과 정렬 기능을 추가한다.
  • JpaRepository: JPA에 특화된 기능(배치성 처리, flush, 컬렉션 조회 등)을 더해준다.

즉, 우리가 JpaRepository를 상속하는 순간, 이 모든 기능이 한꺼번에 따라 들어오는 것이다.

그리고 실제 구현체는 SimpleJpaRepository라는 클래스가 기본으로 등록된다. 이 클래스가 모든 메서드를 구현하고 있기 때문에, 개발자는 따로 코드를 작성할 필요가 없다.

 

마무리하며며

지금까지 살펴본 것처럼, 순수 JPA에서는 엔티티마다 같은 CRUD 코드를 반복해서 작성해야 했지만, 스프링 Data JPA를 사용하면 JpaRepository를 상속하는 인터페이스 선언만으로 동일한 기능을 사용할 수 있다. 내부적으로는 SimpleJpaRepository라는 구현체가 이미 등록되어 있어서, 개발자가 직접 코드를 작성하지 않아도 된다.

 

이번 장에서는 기본적으로 제공되는 CRUD 기능만 사용해봤다. 하지만 실제 프로젝트에서는 단순 CRUD만으로는 부족하다. 예를 들어, “이름으로 회원을 찾는다” 같은 맞춤형 조회가 필요할 수 있다. 그렇다면 인터페이스에 없는 기능들은 어떻게 구현해야 할까?

 

다음 장에서는 바로 이 부분을 다룰 것이다. 스프링 Data JPA의 쿼리 메소드 기능을 통해, 메서드 이름만으로도 다양한 쿼리를 만들어내는 방법을 설명할 예정이다.

 

감사합니다.

'스프링 데이터 JPA' 카테고리의 다른 글

[스프링] 스프링 Data JPA(6) @EntityGraph  (0) 2025.08.21
[스프링] 스프링 Data JPA(5) 벌크성 수정쿼리  (0) 2025.08.21
[스프링] 스프링 Data JPA(4) JPA 페이징과 정렬  (0) 2025.08.21
[스프링] 스프링 Data JPA(3) @Query, 리포지토리 메소드에 쿼리 정의하기  (0) 2025.08.21
[스프링] 스프링 Data JPA(2) 메소드 이름으로 쿼리생성  (1) 2025.08.21
'스프링 데이터 JPA' 카테고리의 다른 글
  • [스프링] 스프링 Data JPA(5) 벌크성 수정쿼리
  • [스프링] 스프링 Data JPA(4) JPA 페이징과 정렬
  • [스프링] 스프링 Data JPA(3) @Query, 리포지토리 메소드에 쿼리 정의하기
  • [스프링] 스프링 Data JPA(2) 메소드 이름으로 쿼리생성
0kingki_
0kingki_
자바 + 스프링 웹 개발
  • 0kingki_
    0kingki_
    0kingki_
  • 전체
    오늘
    어제
    • 분류 전체보기 (134)
      • 코딩 테스트 (54)
      • 자바 (21)
      • 스프링 (27)
      • 타임리프 (16)
      • 스프링 데이터 JPA (8)
      • 최적화 (2)
      • QueryDSL (4)
      • AWS (2)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    spring
    예외 처리
    자바
    타임리프
    객체지향
    JPA
    스프링
    QueryDSL
    dfs
    최적화
    예외처리
    SOLID
    스프링 컨테이너
    스프링 데이터 JPA
    불변객체
    코딩테스트
    백준
    fetch join
    thymeleaf
    Java
    코딩 테스트
    쿼리dsl
    BFS
    mvc
    컬렉션
    쿼리
    다형성
    재귀
    LocalDateTime
    SpringDataJpa
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
0kingki_
[스프링] 스프링 Data JPA에 대하여
상단으로

티스토리툴바