현대 자바 백엔드 개발에서 가장 많이 사용되는 기술 중 하나가 바로 JPA다. 스프링 부트를 사용하는 대부분의 프로젝트에서는 이미 JPA가 기본처럼 사용되고 있다. 하지만 단순히 “JPA를 쓴다”는 것과, “JPA가 무엇이며 왜 필요한지 정확히 알고 사용하는 것”은 분명한 차이가 있다.
많은 초보 개발자들이 JPA를 처음 접할 때 겪는 공통적인 어려움은 “왜 쓰는지 모르고 쓰는 것”이다. SQL보다 더 어렵게 느껴지고, 엔티티, 영속성 컨텍스트, 지연 로딩 등 생소한 개념들에 부담을 느낀다.
이 글에서는 JPA가 등장하게 된 배경부터, 왜 필요한지, 그리고 핵심 개념인 ORM과 영속성 컨텍스트까지 차근차근 정리한다.
1. JPA란?
JPA(Java Persistence API)는 자바 애플리케이션에서 데이터를 관계형 데이터베이스에 저장하거나 조회할 때 사용하는 표준 ORM(Object Relational Mapping) 기술이다. 객체 중심으로 개발할 수 있도록 도와주며, SQL을 직접 작성하지 않고도 데이터베이스 작업을 처리할 수 있다.
2. JPA가 필요한 이유
기존 JDBC나 MyBatis 방식에서는 SQL을 직접 작성하고, 결과를 자바 객체에 일일이 매핑해야 한다. 이 방식은 반복적인 코드가 많고 유지보수가 어렵다.
예를 들어, 특정 회원 데이터를 조회할 때는 다음과 같이 처리해야 한다.
String sql = "SELECT * FROM member WHERE id = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setLong(1, 1L);
ResultSet rs = pstmt.executeQuery();
Member member = new Member();
if (rs.next()) {
member.setId(rs.getLong("id"));
member.setName(rs.getString("name"));
}
이처럼 SQL을 직접 다루기 때문에 쿼리의 변경이 자주 발생하면 애플리케이션 코드도 같이 수정해야 한다. 비즈니스 로직보다 SQL 작성과 매핑에 더 많은 코드가 필요하다.
JPA를 사용하면 다음과 같이 간단하게 처리할 수 있다.
Member member = em.find(Member.class, 1L);
SQL을 직접 작성하지 않아도 객체만으로 데이터를 처리할 수 있기 때문에 생산성과 유지보수성이 크게 향상된다.
3. ORM(Object Relational Mapping)
ORM은 객체와 테이블을 매핑하는 기술이다. 자바 객체를 DB 테이블처럼 사용하고, 객체의 필드는 테이블의 컬럼에 대응된다.
예를 들어, 다음과 같은 엔티티 클래스가 있다고 하자.
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
private String name;
}
(위 코드안에 들어있는 어노테이션에는 다음 장에서 살펴볼 것이다.
지금은 이런게 있다 라는 점만 이해하고 넘어가면 된다.)
이 클래스는 내부적으로 다음과 같은 SQL 테이블과 대응된다.
CREATE TABLE member (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255)
);
ORM을 사용하면 자바 클래스만 작성하면 되고, SQL 테이블 생성 및 조회는 JPA가 대신 처리해준다.
4. 영속성 컨텍스트
JPA에서 가장 중요한 개념 중 하나는 영속성 컨텍스트다. 이는 엔티티 객체를 보관하고 관리하는 JPA 내부의 메모리 공간이다.
Member member = new Member();
member.setName("김영한");
em.persist(member); // 영속 상태 진입
persist()를 호출하면 엔티티는 영속성 컨텍스트에 저장되며, DB에 INSERT 쿼리가 나간다. 이때부터 해당 엔티티는 JPA가 관리하게 된다.
영속 상태에서는 객체의 값이 변경되면 자동으로 UPDATE 쿼리가 반영된다. 예를 들어,
member.setName("이영한");
처럼 값을 변경하면 별도 SQL 없이도 UPDATE 쿼리가 자동 생성된다.
이러한 동작은 영속성 컨텍스트가 엔티티의 변경 사항을 추적하고 있기 때문이다.
5. 엔티티 생명주기
JPA에서 엔티티 객체는 아래와 같은 상태를 가진다.
| 비영속 | JPA가 관리하지 않는 상태. 단순히 new로 생성한 객체 |
| 영속 | persist() 호출 후 JPA가 관리하는 상태 |
| 준영속 | detach() 등으로 영속성 컨텍스트에서 분리된 상태 |
| 삭제 | remove() 호출로 삭제 예정 상태 |
즉 다음과 같은 예시다.
Member member = new Member(); // 비영속
em.persist(member); // 영속
em.detach(member); // 준영속
em.remove(member); // 삭제
영속 상태가 되어야만 JPA가 객체를 DB에 자동 반영할 수 있으므로, 엔티티의 상태를 정확히 이해하는 것이 중요하다.
6. EntityManager와 EntityManagerFactory
JPA는 DB 작업을 직접 수행하지 않는다. 대신 EntityManager를 통해 작업을 처리한다.
EntityManagerFactory
- 하나의 애플리케이션에 하나만 생성
- 데이터베이스 연결을 관리하고 EntityManager를 생성하는 역할
EntityManagerFactory emf = Persistence.createEntityManagerFactory("myUnit");
EntityManager
- 트랜잭션 단위로 사용
- 실제 쿼리를 실행하고 영속성 컨텍스트를 관리
EntityManager em = emf.createEntityManager();
em.getTransaction().begin(); // 트랜잭션 시작
Member member = em.find(Member.class, 1L);
em.getTransaction().commit(); // 트랜잭션 커밋
EntityManager는 꼭 트랜잭션과 함께 사용해야 한다.
조회, 저장, 수정, 삭제 등 모든 작업은 트랜잭션 안에서 수행되어야 한다.

마무리하며
JPA는 객체 중심 개발을 가능하게 하며, SQL에 의존하지 않고도 데이터베이스 작업을 처리할 수 있다.
핵심은 ORM을 통해 객체와 테이블을 자동으로 연결하고, 영속성 컨텍스트를 통해 엔티티의 상태를 효율적으로 관리하는 데 있다.
JPA의 개념과 흐름을 명확히 이해하면, 데이터 접근 코드를 단순화하고 유지보수 비용을 크게 줄일 수 있을 것이다.
감사합니다.
'스프링' 카테고리의 다른 글
| [스프링] JPA 연관관계 매핑 (2) | 2025.07.25 |
|---|---|
| [스프링] JPA 기본 매핑 어노테이션 (4) | 2025.07.24 |
| [스프링] 빈 스코프 (1) | 2025.07.24 |
| [스프링] 빈 생명주기 콜백 (3) | 2025.07.24 |
| [스프링] 의존관계 자동 주입에 대하여 (0) | 2025.06.26 |
