Programming-[Backend]/JPA

[JPA기본] 1. JPA 필요성 : 객체와 RDB의 패러다임 불일치

컴퓨터 탐험가 찰리 2021. 9. 24. 20:12
728x90
반응형

 

1. JPA란

 

JPA는 SQL(Structured Query Language) 중심적인 개발을 대체해주는 ORM(Object Relational mapping) 이다. 객체 지향적인 개발 스타일을 유지하면서 데이터 베이스와의 상호작용이 가능하도록 해준다. 객체와 데이터베이스의 관계를 매핑 해주는 것이 ORM이다. 객체 지향적 언어인 자바와 관계형 데이터 베이스(RDB, Relational DataBase)간에는 다음 섹션에서 다룰 패러다임 불일치의 문제가 있는데, 여기서 발생하는 개발에서의 생산성 저하 문제를 해결해주는 도구라고 할 수 있다.

 

원래 SQL 중심적인 개발에서는 정보를 저장하는 테이블마다 반복적으로 CRUD에 해당하는 SQL 코드를 작성해야만 했다.

 

객체는 어떤 데이터의 속성과 기능을 갖고 있는 대상이다. 이 객체의 정보는 관계형 데이터베이스, 비관계형 데이터 베이스(NoSQL, Non Relational Structured Query Language), File 등으로 저장할 수 있다. 보통 체계적 저장을 위해 관계형 데이터베이스를 많이 사용한다. 그러나 객체와 데이터베이스는 구조적으로 다르기 때문에 객체를 데이터 베이스에 저장하기 위해서는 SQL문을 작성해주어야 한다.

 


 

2. 패러다임의 불일치

 

백엔드 구조를 개발하기 위해서 객체 지향적 언어와 RDB를 많이 사용한다. 그런데 이 두 주체간 데이터를 다루는 구조적인 체계, 패러다임이 다르기 때문에 개발 생산성이 떨어진다. 어떤 불편함과 문제가 있기에 ORM을 사용해야하는 걸까?

 

SQL 중심적 개발 시의 문제점

 

유지보수가 어렵다.

서비스를 개발할 때, RDB에서 데이터를 다루기 위해서는 CRUD(Create, Read, Update, Delete)에 해당하는 기본적인 SQL을 작성한다. 만약 아래와 같은 객체와 RDB 테이블 구조가 있는 경우가 있다고 생각해보자.

 

animal에 대한 정보를 다루기 위해서, 정보를 다루는 모든 코드에 다음과 같은 SQL문이 필요하다. size값은 굳이 필요없는데, 나중을 위해서 테이블상에 넣어놓은 값이라고 가정하자.

 

INSERT INTO ANIMAL(ID, NAME) VALUES (1, "찰리") ...

SELECT ID, NAME FROM ANIMAL A ...

UPDATE ANIAML SET ID=1, NAME="벤자민" WHERE ...

 

SQL의 종류를 3종으로 표현했을 뿐이고, 총 갯수로는 수많은 SQL문이 존재할 것이다. 이렇게 서비스를 운영하다가, size 정보가 필요해서 size를 추가한다면? 수많은 SQL문에 SIZE에 대한 부분을 모두 추가해주어야 한다. 엄청나게 번거롭고 힘든 일이 될 것이다.

 


 

객체와 RDB의 차이점

 

1. 상속, 연관 관계 방식

2. 엔티티 호출 방식

3. 데이터 식별 방법

   ...등의 차이가 있다.

 

 

상속 및 연관 관계

 

객체의 상속 관계상 데이터를 불러오기 위해서 참조 방식을 사용한다.

위 예제를 보면 Cat 객체는 animal을 상속받아서 본인의 필드값인 tail, hair 뿐만 아니라 id, name, size도 갖고 있다. cat 정보를 객체로 불러오기 위해서는 어떻게 해야할까? 참조 방식을 사용하면 된다. 위 객체 예제에서처럼 Cat 객체를 Animal 객체가 참조하도록 넣고, 필요한 속성값을 animal.getCat().getHair() 등으로 조회하면 된다.

 

테이블의 슈퍼-서브 타입 관계상 데이터를 불러오기 위해서는 테이블끼리 join하여 사용한다.

테이블에는 상속 관계가 없다. 각 테이블이 PK, FK로 지정된 값으로 연결되어 있음을 단순히 표시하고 있을 뿐이다. cat 정보를 불러오기 위해서는 animal 테이블에 cat 테이블을 join 하는 쿼리문을 짜서, 그 join된 테이블에서 name, size, tail, hair 등의 정보를 가져와야 한다. 만약 human의 정보를 가져오고 싶다해도, 같은 방식으로 join을 하여 정보를 가져올 수 있는 전체 테이블을 만들어 놓고서야 human 객체에 대한 전체 정보를 가져올 수 있게 된다.

 

보통 이런 패러다임 불일치 관계가 있기 때문에, 다음 과정을 거쳐서 데이터를 다루게 된다.

 

1. JOIN문을 통한 SQL문 실행으로 모든 데이터를 가져옴

2. Animal 객체를 생성, DB에서 SQL문으로 조회한 모든 데이터 입력

3. Cat 객체를 생성, DB에서 SQL문으로 조회한 모든 데이터 입력

4. Animal - Cat 간 관계 설정 ex) animal.setCat(cat) ...

 

이런 과정들이 번거로우므로, animal-cat이라는 객체를 만들고 모든 필드값들을 넣어서 사용하는 경우도 있다고 한다. 그러나 JPA는 이런 과정들이 생략될 수 있다. cat 객체에 해당하는 필드값들은 cat 테이블에서 가져오고, animal 객체에 해당하는 값들은 자동으로 join문을 짜서 animal 테이블에서 가져오게 된다. INSERT 때도 마찬가지로 상속관계에 따른 테이블별 쿼리를 자동으로 생성한다. 물론, 객체와 테이블간 매핑을 정확하게 잘 해주어야 한다.

 

 

엔티티 그래프 및 엔티티 신뢰 문제

객체는 기본적으로 탐색이 가능하다. 객체간 관게를 잘 맺어놨다면, animal.getCat()으로 Cat 객체에 접근하여 필드값들을 가져올 수 있다. 그러나 SQL문으로 작성한 구조에서는 JOIN을 한 부분까지만 정보를 가져올 수 있다. JPA 없이 SQL문 실행을 통한 구조에서 JOIN을 하지않고 Animal 테이블에서만 조회한 상태인데, 그 아래 animal 객체를 다루는 부분에서 animal.getCat()으로 cat 객체에 대한 정보를 조회했을 때, 결과값이 null값이 나올 수도 있다. 그래서 과연 해당 엔티티가 가지는 연관관계 범위가 어디인지 정확히 알 수 없으므로 신뢰성이 떨어지게 된다.

 

// SELECT * FROM ANIMAL ...

// Animal animal = new Animal();

// ... 비즈니스 로직

// animal.getCat().getHair()   -> SQL문을 보지 않으면 결과값이 있을지 null일지 알 수 없음

 

그렇다고해서 모든 객체를 미리 전부 로딩할 수도 없다. JPA에서는 모든 객체를 로딩하지 않고 해당 객체의 필드값이 실제로 필요할때 호출하는 지연 로딩 등의 개념을 도입하여 성능적으로도, 엔티티 자유도에서도 장점을 갖는다.

 

 

데이터 식별 방법

다음과 같이 animal 데이터를 RDB에서 조회한다고 가정하자

 

1
2
3
4
5
6
7
8
9
10
11
12
13
Long animal_id = 10L;
 
Animal animal1 = animalDAO.getAnimal();
Animal animal2 = animalDAO.getAnimal();
 
class AnimalDAO {
 
  public Animal findAnimal(Long animalId) {
    //SQL문...
    //JDBC API, SQL 실행문...
    return new Animal(id, name, size);
  }
}
cs

 

이 경우 animal1과 animal2는 다르다. 갖고 있는 필드값들은 같을지언정, 다른 메모리 주소를 갖는 데이터가 되기 때문이다. 그러나, 자바에서 List<Animal> animals =... 와 같이 List안에 Animal 객체가 담겨있는 경우, List의 첫번째 요소를 반복적으로 조회하는 경우 이 객체는 같은 객체를 가리키게 된다.

 

특정 기능을 하는 메서드에서 조회하는 animal 객체의 값이 동일하지 않고 호출 때마다 반복된다면, 반복적으로 조회하는 등 번거로운 작업이 필요하고, RDB-자바 객체간 다른 개념으로 인해 혼동이 올 수 있다.

 

이런 모순을 해결하기 위해서 JPA에서는 영속성(Persistence)이라는 개념을 도입한다. 영속성 컨텍스트라는 값이 저장되는 캐시를 두고, RDB에서 조회해온 객체 정보를 1개의 Transaction이 끝날 때까지(쉽게는 1개 메서드 동안) 유지해준다. 상세한 내용은 뒤에서 배울 것이다.

 

 


 

참조

 

1. 인프런_자바 ORM 표준 JPA 프로그래밍 - 기본편_김영한 님 강의

https://www.inflearn.com/course/ORM-JPA-Basic/lecture/21735?tab=curriculum

728x90
반응형