📙 목차
1. 패러다임 불일치(Paradigm Mismatch)
패러다임 불일치(Paradigm Mismatch)
- 객체 지향 프로그래밍(Object-Oriented Programming)과 관계형 데이터베이스(Relational Database)가 서로 다른 설계 철학과 구조를 가지고 있기 때문에 이 둘을 연결할 때 생기는 구조적·개념적 충돌을 의미한다.
- 객체는 클래스와 참조, 캡슐화, 상속, 다형성 등 객체 지향 개념을 따르지만
- RDB는 테이블, 외래 키, 정규화, SQL 등 관계형 모델을 기반으로 동작한다.
- 이러한 차이로 인해, 객체를 RDB에 저장하거나 불러올 때 반복적인 SQL 작성, JOIN 처리, 객체 조립, 동일성 문제 등 다양한 어려움이 발생한다.
객체 vs 관계형 데이터베이스
구분 | 객체(Object) | 관계형 DB(Relational DB) |
구조 단위 | 클래스 (Class) | 테이블 (Table) |
데이터 단위 | 인스턴스 (Object) | 레코드 (Row) |
속성 표현 | 필드(Field), 참조형 포함 | 컬럼(Column), 기본형만 |
관계 표현 | 참조(Reference)로 표현 | 외래 키(Foreign Key)로 연결 |
상속 | 클래스 상속 지원 | 직접적인 상속 구조 없음 |
다형성 | 메서드 오버라이딩 등 지원 | 미지원 (테이블로 분리 필요) |
데이터 탐색 | 객체 그래프 탐색 (객체 간 연결) | JOIN 기반 탐색 (명시적 SQL 필요) |
식별 방식 | 동일성 (==) or equals() | 기본 키(Primary Key)로 식별 |
데이터 변경 | 객체 필드만 수정하면 됨 | SQL로 명시적 갱신 필요 |
대표적인 패러다임 불일치 사례
구분 | 설명 |
상속 불일치 | 객체는 상속이 가능하지만, RDB는 상속 개념이 없음. 이를 테이블로 구현하려면 JOIN이나 단일 테이블 전략 등이 필요. |
참조 vs 외래 키 | 객체는 필드에 다른 객체를 직접 참조하지만, RDB는 외래 키(FK)로 관계를 표현함. 이를 매핑하려면 객체 그래프 ↔ 외래 키 변환 로직이 필요. |
동일성 문제 | 객체는 == 또는 equals()로 비교하지만, RDB는 기본 키(PK)로 식별. 식별 방식이 달라서 혼동 발생 가능. |
네비게이션 방식 차이 | 객체는 필드를 따라 연결된 객체 탐색이 가능하지만, RDB는 명시적인 JOIN이 필요함. 개발자가 직접 SQL을 짜야 함. |
데이터 갱신 방식 차이 | 객체는 필드만 수정하면 끝나지만, RDB는 변경 쿼리를 작성해야 함. 트랜잭션, 동기화 등의 문제가 따름. |
컬렉션 처리 문제 | 객체는 리스트, 세트 등 컬렉션으로 연관 객체를 가질 수 있지만, RDB는 이를 표현하기 위해 추가 테이블이 필요함. |
* 이런 문제를 해결하기 위해 등장한 것이 JPA (Java Persistence API) 같은 ORM(Object-Relational Mapping) 기술이다.
2. JPA
JPA (Java Persistence API)
- 자바 객체와 관계형 데이터베이스를 자동으로 매핑해주는 ORM 기술 표준
- 객체와 테이블 사이의 패러다임 불일치(Paradigm Mismatch) 문제를 해결하기 위해 자바 진영에서 만든 "표준 ORM API"이다.
- 반복적인 SQL 작성 없이, 객체 중심의 비즈니스 로직 개발이 가능하도록 도와준다.
- JPA는 인터페이스(표준)만 정의
- 실제 구현체로는 Hibernate, EclipseLink, DataNucleus 등이 있다.
- 우리가 보통 사용하는 @Entity, @Id, @OneToMany 같은 어노테이션이 JPA에 해당한다.
JPA 핵심 개념
개념 | 설명 |
Entity | DB 테이블에 매핑되는 자바 클래스 (@Entity 사용) |
EntityManager | JPA의 핵심 인터페이스. 객체 저장, 조회, 삭제, 갱신 등을 담당 |
영속성(Persistence) | 객체의 생명주기를 관리하고 DB와 동기화하는 개념 |
JPQL | 객체지향 쿼리 언어. SQL과 유사하지만, 테이블이 아닌 객체(Entity)를 대상으로 한다 |
어노테이션 기반 매핑 | @Table, @Column, @OneToMany, @JoinColumn 등을 이용해 객체와 테이블을 연결 |
3. JPA 사용 이유
JPA 사용 이유
- 생산성 향상
- 유지보수 용이
- 패러다임 불일치 문제 해결
- 성능 최적화
생산성 향상
- 반복적인 SQL 없이 persist(), find() 등 메서드만으로 저장·조회·수정·삭제 가능
- 객체처럼 다루기 때문에 직관적이고 코드가 간결하다.
jpa.persist(tutor); // 저장
Tutor tutor = jpa.find(Tutor.class, tutorId); // 조회
tutor.setName("수정 이름"); // 수정
jpa.remove(tutor); // 삭제
유지보수 용이
- 객체 필드가 변경되어도 SQL 수정 불필요 (JPA가 내부적으로 처리)
// 필드 추가 전
public class Tutor {
private String id;
private String name;
}
// 필드 추가 후
public class Tutor {
private String id;
private String name;
private Integer age;
}
패러다임 불일치 문제 해결
- 상속
- 객체의 상속 구조를 관계형 테이블에 맞게 변환하여 저장
- ex. 부모 클래스 Person과 자식 클래스 Tutor를 각각 테이블로 분리 저장하고, 조회 시 JOIN 처리
@Entity
public class Person {
@Id
private Long id;
private String name;
}
@Entity
public class Tutor extends Person {
private String subject;
}
- 연관관계 매핑
- 객체 간 참조 관계(@ManyToOne, @OneToMany)를 데이터베이스 외래키 관계로 자동 변환
- 코드에서는 객체 참조처럼 다루고, JPA가 적절한 SQL로 변환해 처리
@Entity
public class Tutor {
@ManyToOne
private Company company;
}
- 객체 그래프 탐색
- 여러 객체가 연결된 상태를 그대로 탐색 가능
- Tutor 객체에서 Company 객체로 자연스럽게 접근할 수 있고, 내부적으로 필요한 JOIN이나 추가 쿼리를 실행
Tutor tutor = jpa.find(Tutor.class, tutorId);
Company company = tutor.getCompany(); // SQL JOIN 없이 객체 참조처럼 사용 가능
- 객체 비교
- 같은 트랜잭션 내에서 같은 식별자를 가진 엔티티는 같은 객체 인스턴스로 관리
Tutor tutor1 = jpa.find(Tutor.class, tutorId);
Tutor tutor2 = jpa.find(Tutor.class, tutorId);
System.out.println(tutor1 == tutor2); // true
성능 최적화
- 1차 캐시
jpa.find(...); // 첫 호출 → DB 조회
jpa.find(...); // 두 번째 → 캐시 조회 (SQL 실행 없음)
- 쓰기 지연(Batching)
jpa.persist(...); // 쌓아두고
transaction.commit(); // 한 번에 DB 전송 (네트워크 비용 ↓)
- 지연 로딩 / 즉시 로딩
- 지연 로딩: 필요한 시점에 조회 → 불필요한 통신 방지
- 즉시 로딩: 한 번에 JOIN 조회 → 효율적인 데이터 접근
4. 영속성 컨텍스트
영속성 컨텍스트
- JPA에서 엔티티 객체를 관리하는 일종의 메모리 공간
- 데이터베이스와 애플리케이션 사이에서 엔티티의 상태를 관리하고, 데이터베이스 접근을 최적화하는 역할을 한다.
영속성 컨텍스트가 필요한 이유
- 데이터베이스에 자주 접근하는 비용을 줄이기 위해
- 동일한 엔티티에 대한 중복 조회를 방지하기 위해
- 트랜잭션 단위로 엔티티 상태 변화를 관리하기 위해
동작 방식
- 애플리케이션이 엔티티를 조회하면, JPA는 해당 엔티티를 영속성 컨텍스트에 저장한다.
- 같은 트랜잭션 내에서 동일한 엔티티를 다시 조회할 때는 데이터베이스가 아닌 영속성 컨텍스트에 저장된 객체를 반환한다.
- 엔티티의 상태가 변경되면, 영속성 컨텍스트가 그 변경 사항을 추적한다.
- 트랜잭션이 커밋될 때, 영속성 컨텍스트는 변경된 내용을 데이터베이스에 반영한다.
핵심 기능
- 동일성 보장 (Identity Guarantee)
- 영속성 컨텍스트는 같은 엔티티에 대해 항상 같은 자바 객체 인스턴스를 반환한다.
- 즉, 한 트랜잭션 내에서 동일한 엔티티 식별자(ID)를 가진 객체는 단 하나만 존재한다.
- 이를 통해 객체 참조 비교(==)가 가능해지고, 데이터 일관성이 유지된다.
- 쓰기 지연 (Write-Behind)
- 엔티티의 변경 사항을 즉시 데이터베이스에 반영하지 않고, 트랜잭션이 커밋되거나 flush() 호출 시점에 한꺼번에 반영한다.
- 이를 통해 여러 SQL 실행을 최소화하여 성능을 최적화한다.
- 변경 감지 (Dirty Checking)
- 영속성 컨텍스트는 트랜잭션 동안 엔티티의 상태 변화를 감지한다.
- 객체의 필드 값이 변경되면, 그 변경 내용을 기억했다가 flush 시점에 업데이트 쿼리를 생성한다.
- 개발자가 직접 update 쿼리를 작성하지 않아도 변경 사항이 자동으로 반영된다.
- flush()
- flush()는 영속성 컨텍스트에 저장된 변경 내용을 데이터베이스에 즉시 반영하는 동작이다.
- 트랜잭션 커밋 시 자동으로 호출되지만, 필요에 따라 개발자가 직접 호출할 수도 있다.
- 단, flush()는 변경 내용을 반영하지만 트랜잭션을 종료하지는 않는다.
엔티티 상태 관리
상태 | 설명 |
비영속 | 영속성 컨텍스트에 속하지 않은 상태 |
영속 | 영속성 컨텍스트에 관리되는 상태 |
준영속 | 영속성 컨텍스트에서 분리된 상태 |
삭제 | 삭제 예정 상태, 트랜잭션 커밋 시 삭제 |
'Spring > 강의' 카테고리의 다른 글
[📙 숙련 Spring] 3-3. Spring Data JPA (0) | 2025.05.21 |
---|---|
[📙 숙련 Spring] 3-2. JPA Entity 만들기 (2) | 2025.05.21 |
[📙 숙련 Spring] 2-4. Filter (0) | 2025.05.20 |
[📙 숙련 Spring] 2-3. Token, JWT(JSON Web Token) (0) | 2025.05.16 |
[📙 숙련 Spring] 2-2. Cookie, Session (2) | 2025.05.16 |