반응형

🧩 들어가며

현대의 자바 기반 애플리케이션에서 데이터 접근 계층은 단순한 SQL 실행 그 이상을 요구받고 있습니다.
객체 지향 언어인 Java와 관계형 데이터베이스(RDB) 사이의 간극을 효율적으로 연결해주는 기술이 바로 JPA(Java Persistence API) 입니다.

이번 글에서는 실무 관점에서 JPA가 왜 필요한지, 어떤 구조로 동작하는지, 그리고 실질적으로 우리가 어떻게 다뤄야 하는지에 대해 깊이 있는 시선으로 정리해보려 합니다.


🔍 JPA란 무엇인가?

JPA는 Java 진영의 ORM(Object Relational Mapping) 표준 명세입니다.
즉, 자바 객체 ↔ 관계형 데이터베이스 사이의 매핑을 선언적으로 처리할 수 있도록 도와주는 인터페이스 모음이죠.

💡 Hibernate, EclipseLink, OpenJPA는 JPA의 구현체입니다.
스프링부트에서 주로 사용하는 건 Hibernate이며, JPA를 추상 레이어로 사용합니다.


📐 왜 JPA인가?

1. SQL 중심 개발의 한계

  • 중복되는 SQL
  • 비즈니스 로직에 쿼리 로직이 섞임
  • 테이블 구조 변경 시 코드 유지보수 어려움
  • 객체와 테이블 간의 불일치

2. 객체 지향적으로 설계된 애플리케이션의 요구

  • 컬렉션 기반의 연관 관계 관리
  • 캡슐화된 비즈니스 메서드
  • 영속성 생명주기와 트랜잭션 추적

👉 이를 해결하기 위해 JPA는 다음과 같은 추상화를 제공합니다.


🧱 핵심 개념 정리

🗃️ Entity

@Entity
@Table(name = "users")
public class User {

    @Id @GeneratedValue
    private Long id;

    private String name;

    @Column(unique = true)
    private String email;
}

 

  • 클래스 자체가 데이터베이스 테이블과 매핑됩니다.
  • @Id, @Column, @Table 등은 모두 매핑 메타데이터입니다.

🔄 EntityManager

JPA의 핵심 동작을 담당하는 인터페이스.
Hibernate의 Session과 유사한 개념으로, 영속성 컨텍스트를 관리합니다.

EntityManager em = entityManagerFactory.createEntityManager();
em.getTransaction().begin();

User user = em.find(User.class, 1L);
user.setName("변경된 이름");

em.getTransaction().commit();

 

📌 주의: Spring Data JPA에서는 직접 EntityManager를 다룰 일은 거의 없습니다. 대신, Repository가 이를 추상화합니다.


📦 영속성 컨텍스트 (Persistence Context)

  • 엔티티 객체를 1차 캐시에 보관
  • 같은 트랜잭션 내 동일 객체 반환 (== 동일성 보장)
  • 변경 감지(Dirty Checking) → flush 시 자동 update
User user = em.find(User.class, 1L);  
user.setName("홍길동"); // 별도 update 쿼리 없이도 자동 감지됨

🧠 Dirty Checking & Flush

  • Dirty Checking: 엔티티 객체의 변경 여부 추적
  • Flush: 변경된 내용을 SQL로 동기화 (트랜잭션 커밋 시 자동 수행)
em.flush(); // DB에 SQL 전송

🏗️ Spring Data JPA와의 관계

Spring Data JPA는 JPA의 Repository 패턴을 자동화한 구현체입니다.
즉, JpaRepository 인터페이스만 상속받으면 기본적인 CRUD는 따로 SQL 없이 구현됩니다.

public interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByEmail(String email);
}


🚀 커스텀 쿼리는 @Query 어노테이션으로 처리하거나, QueryDSL, Specification으로 확장 가능


🧬 연관관계 매핑

예시: 회원과 주문 (1:N)

@Entity
public class Member {

    @Id
    private Long id;

    @OneToMany(mappedBy = "member")
    private List<Order> orders = new ArrayList<>();
}

@Entity
public class Order {

    @Id
    private Long id;

    @ManyToOne
    private Member member;
}

 

  • 양방향 매핑 시 mappedBy 설정 필수
  • 지연 로딩(LAZY)이 기본값이므로 주의

⚠️ 실무에서 자주 하는 실수

항목 설명
양방향 매핑 남용 단방향으로도 충분한 경우가 많음
즉시 로딩(EAGER) 사용 성능 저하, N+1 문제 발생 가능
식별자 전략 잘못 사용 GenerationType.IDENTITY는 제약이 있음
트랜잭션 없이 EntityManager 사용 변경 감지 미동작

 


🎯 마무리

JPA는 단순히 SQL을 안 쓰기 위한 도구가 아닙니다.
도메인 모델을 중심으로 한 객체 지향 아키텍처를 구현하기 위한 전략이며,
이는 결국 유지보수성과 생산성을 극대화합니다.

Spring Boot 3와 함께 JPA를 적절히 활용하면,
비즈니스 로직에 집중할 수 있는 유연하고 강력한 백엔드 아키텍처를 구성할 수 있습니다.

 

반응형

'Spring Boot' 카테고리의 다른 글

Spring Boot 3의 Repository, Service, Controller 흐름  (3) 2025.07.19
JWT  (0) 2024.01.26
스프링 시큐리티  (0) 2024.01.26
front + backend 게시판 CRUD 구현 (1)  (0) 2023.10.19

+ Recent posts