스프링

스프링에서 데이터베이스 다루기 4 - 스프링 데이터 JPA

yoon4360 2025. 4. 6. 12:28

 

앞서 소개한 JdbcTemplate도 기존 JDBC로 데이터 베이스를 다룰때 반복했던 작업들을 줄여주었지만

여전히 SQL을 직접 작성해야 했고, 객체 지향적인 코드와는 거리가 있다.

그래서 등장한 것이 바로 바로 JPA(Java Persistence API) 이다. 

이번 글에서는 JDBC와의 차이점과 연관관계 매핑까지 다뤄보려 한다.

 


 

JPA

JPA는 자바 ORM 표준이다.

즉 객체를 데이터베이스 테이블에 매핑하고, 객체 간의 관계를 데이터베이스 관계로 자연스럽게 이어주는 기술이다.

JPA를 쓰면 다음과 같은 이점이 있다.

  • SQL 없이 객체 중심의 프로그래밍이 가능하다.
  • 객체 간 연관관계를 표현 할 수 잇다. (@ManyToOne, @OneToMany 등)
  • 트랜잭션, 페이징, 캐싱 등 고급 기능을 지원해준다.

 


 

Spring Data JPA와 JpaRepository

Spring Data JPA는 JPA를 더욱 쉽게 사용할 수 있게 도와주는 스프링 기반 프레임워크이다.

이 프레임워크에서는 인터페이스만 정의하면, 기본 CRUD 기능은 자동으로 구현해준다.

public interface AccountRepository extends JpaRepository<Account, Long> {
    List<Account> findByName(String name);
}

 

  • CrudRepository: 기본적인 CRUD 기능을 제공해 준다.
  • JpaRepository: CrudRepository보다 확장된 기능(페이징, 정렬, 배치성 쿼리 등)을 제공해 준다.

JdbcTemplate → CrudRepository → JpaRepository 순으로 기능이 풍부해진다.

 

 


 

엔티티 매핑

엔티티 매핑은 데이터베이스 테이블과 자바 클래스(엔티티)를 연결하는 과정이다.

이를 통해 객체 지향 프로그래밍의 이점을 유지하면서도 관계형 데이터베이스와의 상호작용을 원활하게 할 수 있다.

JPA에서는 @Entity 애노테이션을 사용하여 엔티티 클래스를 선언하며,

@Table 애노테이션으로 매핑할 테이블의 정보를 지정할 수 있다.

 

작동 방식

  • @Entity: 해당 클래스가 JPA 엔티티임을 선언한다. 기본 생성자가 필요하며, final 클래스, enum, interface, inner 클래스에는 사용할 수 없다.
  • @Table: 엔티티와 매핑할 테이블을 지정한다. 생략 시 엔티티 이름이 테이블 이름으로 사용된다.
@Entity
@Table(name = "users")
@Getter
@Setter
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String username;
}

 


 

연관관계 매핑

객체 지향 프로그래밍에서는 객체 간의 관계를 필드로 표현하지만, 관계형 데이터베이스에서는 외래 키로 표현한다.

JPA는 이러한 차이를 해결하기 위해 연관 관계 매핑을 제공해준다.

이를 통해 객체 간의 관계를 데이터베이스 테이블의 관계와 매핑하여 일관된 데이터 관리를 할 수 있다.

 

작동 방식

  • 단방향 연관 관계: 한 엔티티가 다른 엔티티를 참조하는 형태이다.
    • @ManyToOne: 다대일(N:1) 관계를 매핑한다.
    • @OneToMany: 일대다(1:N) 관계를 매핑한다.
  • 양방향 연관 관계: 두 엔티티가 서로를 참조하는 형태이다.
    • 연관 관계의 주인을 정해야 하며, 주인은 외래 키를 관리한다. 주인이 아닌 쪽은 mappedBy 속성을 사용하여 주인을 지정한다.

 

@Entity
public class Member {
    @Id
    @GeneratedValue
    private Long id;

    @ManyToOne
    @JoinColumn(name = "team_id")
    private Team team;
}

@Entity
public class Team {
    @Id
    @GeneratedValue
    private Long id;

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

 

연관관계에서 주인은 누구?

 

  • @ManyToOne 쪽이 일반적으로 연관관계의 주인이 된다.
  • 이유는 외래 키를 실제로 가진 쪽이기 때문이다. 외래 키는 DB에서 한 테이블에만 존재하므로, JPA도 외래 키를 가진 엔티티를 주인으로 정하는 것이다.
  • 반면 @OneToMany(mappedBy = "team")는 DB에 외래 키를 가지지 않기에 연관관계 주인이 아니고, 단순히 조회만 가능하다.

쉽게 말해 주인 쪽만 insert/update SQL을 날릴 수 있고, 반대쪽은 읽기 전용이다.

 

 


 

마무리

스프링 데이터 JPA를 활용하면 엔티티 매핑, 연관 관계 설 등을 효율적으로 구현할 수 있다.

이번 글에서는 JPA의 기본적인 기능들 위주로 다뤄보았는데, 다음에는 지연로딩과 즉시로딩을 학습해 작성할 계획이다.

 

JPA를 사용하면 반복적인 코드를 줄이고, 비즈니스 로직에 집중할 수 있지만

각 기능의 작동 방식과 주의사항을 정확히 이해하고 사용해야 예상치 못한 문제를 방지할 수 있을 것이다.