개발 공부/백엔드

스프링 DB 접근 기술 : 3. JPA

baby-t 2025. 9. 29. 12:10

https://www.inflearn.com/

 

인프런 - 라이프타임 커리어 플랫폼

프로그래밍, 인공지능, 데이터, 마케팅, 디자인등 입문부터 실전까지 업계 최고 선배들에게 배울 수 있는 곳.

www.inflearn.com

인프런 사이트의 김영한님의 강의를 보면서 작성한 글입니다.

 

이전 포스팅에서는 JdbcTemplate을 사용하여 반복적인 JDBC 코드를 제거했습니다. 하지만 여전히 SQL 쿼리는 개발자가 직접 작성해야 했습니다.

이번 포스팅에서는 현대적인 Java 웹 개발의 표준 기술인 **JPA(Java Persistence API)**를 도입하여, SQL마저도 JPA가 처리하도록 만들어 보겠습니다. JPA를 사용하면, 개발자는 데이터 중심의 설계에서 벗어나 객체 중심의 설계에 온전히 집중할 수 있어 개발 생산성을 획기적으로 높일 수 있습니다. ✨


## 1. JPA와 ORM이란?

JPA는 자바 진영의 ORM(Object-Relational Mapping) 기술 표준입니다.

  • ORM: 이름 그대로 **객체(Object)와 관계형 데이터베이스(Relational Database)를 자동으로 매핑(mapping)**해주는 기술입니다. 마치 자바 객체와 DB 테이블 사이의 '통역사'와 같습니다.
  • JPA: 이 ORM 기술을 어떻게 사용해야 하는지 표준 명세(인터페이스)를 정의한 것입니다. Hibernate는 이 JPA 명세를 구현한 가장 인기 있는 구현체 중 하나입니다.

## 2. JPA 설정

### 1. 의존성 추가

build.gradle 파일에서 jdbc 의존성을 jpa로 변경합니다. spring-boot-starter-data-jpa는 jdbc 관련 라이브러리를 이미 포함하고 있습니다.

Groovy
 
dependencies {
    // implementation 'org.springframework.boot:spring-boot-starter-jdbc' // 이 부분은 이제 필요 없습니다.
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    runtimeOnly 'com.h2database:h2'
}

### 2. application.properties 설정

JPA와 관련된 설정을 추가합니다.

Properties
 
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none
  • spring.jpa.show-sql=true: JPA가 생성하는 SQL 쿼리를 콘솔에 출력해 줍니다.
  • spring.jpa.hibernate.ddl-auto=none: JPA가 애플리케이션 실행 시점에 테이블을 자동으로 생성할지 여부를 결정합니다. create로 설정하면, 엔티티 정보를 바탕으로 기존 테이블을 삭제하고 새로 생성해주므로 개발 환경에서 유용합니다.

## 3. 엔티티 매핑

JPA를 사용하려면, 관리할 도메인 객체가 데이터베이스의 테이블과 매핑되는 **엔티티(Entity)**임을 알려주어야 합니다. Member 클래스에 어노테이션을 추가합니다.

Java
 
@Entity
public class Member {

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    // ... Getters and Setters ...
}
  • @Entity: 이 클래스가 JPA가 관리하는 엔티티임을 나타냅니다.
  • @Id: 이 필드가 테이블의 기본 키(Primary Key)임을 나타냅니다.
  • @GeneratedValue(...): 기본 키 값을 자동으로 생성하는 전략을 설정합니다. IDENTITY 전략은 데이터베이스의 AUTO_INCREMENT 기능에 생성을 위임합니다.

## 4. JPA 리포지토리 구현 (JpaMemberRepository)

JPA는 EntityManager를 통해 모든 데이터 조작을 수행합니다. 순수 JDBC나 JdbcTemplate을 사용할 때보다 코드가 극적으로 간결해집니다.

Java
 
public class JpaMemberRepository implements MemberRepository {

    private final EntityManager em;

    public JpaMemberRepository(EntityManager em) {
        this.em = em; // 스프링 부트가 생성한 EntityManager를 주입받음
    }

    @Override
    public Member save(Member member) {
        em.persist(member); // persist: 영속화하다. member 객체를 DB에 저장.
        return member;
    }

    @Override
    public Optional<Member> findById(Long id) {
        Member member = em.find(Member.class, id); // find: PK로 객체 하나를 조회.
        return Optional.ofNullable(member);
    }
    
    // ... findByName, findAll 구현 ...
}

save, findById처럼 PK 기반의 단순한 작업은 SQL 없이 메소드 호출만으로 끝납니다.

### JPQL (Java Persistence Query Language)

PK 기반이 아닌 나머지 조회 쿼리(예: 이름으로 찾기, 모두 찾기)는 SQL을 작성해야 하지만, 테이블을 대상으로 하는 SQL이 아닌 엔티티 객체를 대상으로 하는 객체 지향 쿼리인 JPQL을 사용해야 합니다.

Java
@Override
public Optional<Member> findByName(String name) {
    List<Member> result = em.createQuery("select m from Member m where m.name=:name", Member.class)
            .setParameter("name", name)
            .getResultList();

    return result.stream().findFirst();
}

@Override
public List<Member> findAll() {
    return em.createQuery("select m from Member m", Member.class).getResultList();
}

[질문] setParameter는 무엇인가요?

JPQL에서 :name 부분은 파라미터를 바인딩하는 문법입니다. .setParameter("name", name) 코드는 JPQL의 :name 자리에, 메소드가 전달받은 name 변수의 값을 채워 넣는 역할을 합니다.


## 5. 서비스 계층과 @Transactional

JPA를 통한 모든 데이터 변경 작업(저장, 수정, 삭제)은 반드시 트랜잭션(Transaction) 안에서 이루어져야 합니다.

스프링은 @Transactional 어노테이션을 통해 이 문제를 매우 간단하게 해결해 줍니다. 데이터 변경이 필요한 서비스 계층의 클래스나 메소드에 이 어노테이션을 붙여주기만 하면 됩니다.

Java
 
@Transactional
public class MemberService {
    // ...
}

이제 MemberService의 public 메소드가 호출될 때, 스프링이 트랜잭션을 시작하고, 메소드가 정상 종료되면 트랜잭션을 커밋(commit)합니다. 만약 예외가 발생하면 롤백(rollback)을 수행하여 데이터의 일관성을 보장합니다.