[JPA] 1. JPA 소개

Park Yeongseo·2023년 9월 20일
0
post-thumbnail

김영한 님의 『 자바 ORM 표준 JPA 프로그래밍』을 정리한 포스트입니다

[1] JPA 소개

1.1 SQL 직접 사용의 문제점

1.1.1 반복

DB는 객체 구조와는 다른, 데이터 중심의 구조를 가지므로 객체를 DB에 직접 저장하거나 조회할 수는 없다. 따라서 개발자는 SQL과 JDBC API를 이용해 어플리케이션과 DB 사이의 변환 작업을 직접 해주어야 한다. 이 작업은 너무 반복적이고 번거로운 일이다.

1.1.2 SQL에 의존적인 개발

새로운 요구 사항이 생기면 테이블에 새로운 필드를 추가하고 SQL도 수정해야 하며, 관련된 객체와 코드 등도 모두 다 직접 수정해주어야 한다. 이것도 번거로운 일이다.

SQL에 모든 것을 의존하는 상황에서는 엔티티(= 비즈니스 요구사항을 모델링한 객체)를 신뢰하고 사용할 수 없다. 직접 DAO 열어 SQL과 관련 객체들을 확인해야 한다. 이를 바탕으로 한 계층 분할은 진정한 계층 분할이 아니며, 해당 두 계층은 논리적으로 강한 의존 관계를 가지고 있다.

  • 진정한 의미의 계층 분할이 어렵다.
  • 엔티티를 신뢰할 수 없다.
  • SQL에 의존적인 개발을 피하기 어렵다.

1.1.3 JPA와 문제 해결

JPA를 이용하면 개발자는 직접 SQL을 작성할 필요없이 JPA가 제공하는 API를 사용하면 된다.

❓ JPA는 별도의 수정 메소드를 제공하지 않고, 객체를 조회해서 값을 변경하는 것만으로도 DB에 적절한 UPDATE SQL을 전달한다. 이게 어떻게 가능한 걸까?
❓ JPA는 연관된 객체를 사용하는 시점에 적절한 SELECT SQL을 실행한다. 따라서 JPA를 사용하면 연관된 객체를 마음껏 조회할 수 있다. 이건 또 어떻게 가능한 걸까?

1.2. 패러다임의 불일치

객체지향 프로그래밍은 시스템의 복잡성을 제어할 수 있는 다양한 장치들을 제공하기 때문에, 현대의 복잡한 app들은 대부분 객체지향 언어로 개발한다.

하지만 그런 객체를 계속해서 사용하기 위해서는 그 객체의 상태를 어딘가에 저장해야 할 필요가 있다. 객체가 단순한 경우에는 모든 필드를 DB에 저장해버리면 되지만 객체가 어떤 객체를 상속하거나 참조하고 있는 경우, 관련된 모든 객체의 상태까지도 저장해야하므로 쉽지 않다.

✏️ 자바는 이를 위해 객체 ↔ 파일의 직렬화 및 역직렬화 기능을 지원한다. 하지만 직렬화된 객체는 검색하기 어렵기에 현실적으로는 사용할 수가 없다.

현실적으로는 RDB에 객체를 저장하는 것이 가능한 대책이 될 수 있겠지만, RDB의 경우 데이터 중심으로 구조화 되어있으며, 객체 지향이 목표로 하는 여러 개념들을 지원하지 못한다. 즉 객체와 RDB는 서로 지향하는 목적이 다르며, 기능과 표현 방법 또한 다르다. 이를 객체와 관계형 데이터베이스의 패러다임 불일치 문제라 한다. 따라서 객체를 테이블 구조에 저장하는 데에는 한계가 있다.

1.2.1 문제 1. 상속

객체에는 상속이 있지만 테이블에는 없다. 가장 비슷한 형태로는 DB의 슈퍼타입-서브타입 관계를 사용해 테이블을 설계하는 방법이 있지만, 이와 함께 JDBC API만을 사용하면 작성해야 할 코드의 양도 훨씬 늘어난다.

JPA와 상속
JPA는 상속과 관련된 패러다임 불일치 문제를 개발자 대신 해결해준다. 예를 들어 특정 객체를 저장해야하는 경우, 개발자는 자바 컬렉션에 저장하듯 JPA에 객체를 저장하면 된다.
JPA의 API를 사용하면 위와 같은 슈퍼-서브 타입 관계의 테이블들에 각각 맞는 객체의 정보를 저장해준다.

1.2.2 문제 2. 연관 관계

객체는 참조를 통해 다른 객체와 연관관계를 가지고, 참조에 접근해 연관된 객체를 조회한다.

  • 객체의 경우 참조를 가지고 있는 쪽에서만 참조되는 쪽을 조회할 수 있다.

한편 테이블은 FK를 통해 다른 테이블과 연관관계를 가지고, JOIN으로 연관된 테이블을 조회한다.

  • 테이블의 경우 하나의 FK로, 다른 방향에서의 JOIN도 가능하다.
class Member {
	String id;//member 테이블의 id. pk
	Long teamId;//team_id. fk
	String userName;
}

class Team {
	Long id;//team 테이블의 pk
	String name;
}

위와 같이 테이블 컬럼을 그대로 가져와 클래스를 만드는 방식으로 모델링하면, 객체를 테이블에 저장하거나 조회할 때에는 편리하지만 객체 지향적이지는 않다.

class Member {
	String id;//member 테이블의 id. pk
	Team team;
	String userName;

	Team getTeam(){
		return this.team;
	}
}

class Team {
	Long id;//team 테이블의 pk
	String name;
}

Member 클래스의 객체가 속한 팀을 찾는 가장 객체 지향적인 방식은 이와 같이 내부에 Team 필드를 두고 getTeam() 과 같은 메서드를 만들어 참조하는 방식이다. 하지만 이 경우 객체를 테이블에 저장하거나 조회하기가 쉽지 않다. 객체는 참조, 테이블은 FK를 통해 다른 것과 연관을 맺기 때문이다.

이를 해결하기 위해서는 다음과 같이 개발자가 중간에서 변환의 역할을 해주어야 한다. 하지만 JPA를 사용하면 JPA가 이러한 문제들을 전부 처리해준다.

1.2.3 객체 그래프 탐색

객체가 연관된 모든 객체들을 탐색하는 것을 객체 그래프 탐색이라 한다. 객체는 객체 그래프를 자유롭게 탐색할 수 있어야 하는데, SQL을 직접 다룰 때에는 처음 실행하는 SQL에 따라 객체 그래프를 어디까지 탐색할 수 있는지가 정해진다. SQL을 직접 다룰 때, 객체 그래프 탐색이 가능한지를 살펴보려면 결국 직접 SQL을 확인해야 하게 된다. 즉 엔티티가 SQL에 종속적이게 된다. 이를 해결하려면 DAO에서도 상황에 따라 여러 메서드를 만들어 사용해야 하지만, JPA를 사용하면 객체 그래프를 자유롭게 탐색할 수 있다.

지연 로딩
JPA는 연관된 객체를 사용하는 시점에 적절한 SELECT SQL을 실행한다. 즉 실제 객체를 사용하는 시점까지 DB 조회를 미룬다. 이를 가리켜 지연 로딩이라 한다.

1.2.4 비교

DB는 PK 값으로 각 행을 구분한다. 한편 객체에는 두 가지 비교 방법이 있다.

  • 동일성(identity) : == 비교. 객체 인스턴스의 주소값 비교.
  • 동일성(equality) : equals() 메서드를 사용해 객체 내부의 값을 비교.

JPA는 같은 트랜잭션에 대해 같은 객체가 조회되는 것을 보장한다.

1.3 JPA란?

1.3.1 JPA

JPA(Java Persistence API)

  • 자바 ORM 기술에 대한 API 표준 명세.
  • ORM?
    • Object-Relational Mapping.
    • 객체와 관계형 DB를 매핑해 패러다임의 불일치 문제를 개발자 대신 해결해줌.
    • 정교한 객체 모델링이 가능하게 함
    • ex) 하이버네이트, EclipseLink, DataNucleus, 스프링 데이터 JPA, …
  • 특정 구현 기술에 대한 의존도를 줄일 수 있고, 다른 구현 기술로 손쉽게 이동할 수 있다.

1.3.2 JPA 왜 써야됨?

  • 생산성
    • 반복적인 일을 JPA가 해결해주므로 생산성이 확대됨
  • 유지보수
    • SQL 의존적인 개발의 경우, 엔티티에 필드가 하나만 추가돼도 SQL문, 테이블, JDBC API 코드 등을 모두 변경해야 한다.
    • 이것들을 JPA는 대신 해줄 수 있으므로, 유지보수에 드는 품이 줄어든다.
  • 패러다임 불일치 해결
  • 성능
    • 같은 대상(?)을 두 번 조회하는 코드가 있다고 할 때, JDBC API의 경우 조회시마다 DB와 통신을 해야하지만, JPA의 경우 한 번만 SQL을 전달하고 두 번째에는 조회 결과를 재사용한다.
  • 데이터 접근 추상화와 벤더 독립성
    • RDB의 경우 같은 기능도 벤더마다 사용법이 다른 경우가 많다. JPA는 app과 DB 사이의 인터페이스를 제공해 app이 특정 DB 기술에 종속되지 않도록 한다.
  • 표준
    • 표준임
profile
블로그 이전 준비 중

0개의 댓글