0. 개요
- 이전 포스팅에서 JPA를 사용하는 이유에 대해서 알아보았다.
- 왜 JPA가 필요한지, 근본적인 문제점이 무엇인지에 대해서 알아보았다.
- 복습하자면 JPA를 사용하는 이유는 SQL 중심의 개발 방식을 객체지향 중심의 개발 방식으로 개선하기 위함이다.
- 그렇다면 JPA가 무엇인지 천천히 알아보자.
1. JPA란?
- JPA는 Java Persistent API의 축약어다.
- 쉽게 설명하자면 JAVA의 ORM 표준 기술을 의미한다.
a) ORM(Object-relational Mapping)
- ORM이 의미하는 바, 역할하는 바는 JPA가 필요한 이유와 동일하다.
- JPA가 필요한 이유는 OOP와 RDB의 구조적 차이로 인해 데이터를 매핑하는 과정에 있어서
SQL 중점적인 개발 방식을 사용하게 된다는 부분에서 문제에서 시작한다.
- ORM을 사용하면 개발자는 OOP로 개발하고, RDB는 RDB 답게 설계하여 사용할 수 있다.
- ORM은 OOP와 RDB 사이에서 중재자의 역할을 하는 것이다.
- 즉, ORM은 OOP와 RDB의 구조적 차이로 인해 발생하는 데이터 매핑 과정의 문제를 해결한다.
* 오해하지 말자!
- ORM은 Java에서만 사용되는 특별한 기술이 아니다.
- 객체지향을 사용하는 모든 프로젝트에 적용할 수 있는 개념이다.
- 그러므로 대부분의 대중적인 프로그래밍 언어는 ORM을 지원한다. ex) Java, Python, TypeScript
b) JPA의 구조적 위치
- JPA는 Java application과 JDBC 사이에 위치한다.
- 개발자 또는 application이 JDBC와 직접 소통하는 것이 아닌, JPA를 거쳐서 소통하는 방식으로 구조를 이룬다.
- 즉, application에서 특정 객체와 명령을 JPA에게 넘기면 JPA는 이를 분석 및 해석하여 SQL을 대신 작성한다.
- 이렇게 작성된 SQL은 JDBC에게 전달되고, JDBC는 이를 이용하여 DB에게 명령을 하달한다.
- 그렇다면 JPA는 중간에서 어떻게 OOP와 RDB의 문제점을 해결하는 것일까?
- 이에 대해서는 나중에 다시 언급하도록 하겠다.
c) JPA의 구조
- JPA는 특별한 동작을 하는 프로그램이 아니라 인터페이스의 모음이다.
- JPA의 대표적인 구현체로 Hibernate, EclipseLink, DataNucleus가 있다.
2. JPA를 사용하는 이유(1) - 생산성
- JPA를 사용하는 이유와 JPA 사용의 장점에 대해서 간단하게 알아보자.
a) 우수한 생산성 - CRUD
- JPA에는 인터페이스로 CRUD 동작이 메서드로 정의되어 있다.
→ 저장(create): jpa.persist(객체)
→ 조회(read): Object object = jpa.find(객체 속성 값)
→ 수정(update): object.setName(변경 값)
→ 삭제(delete): jpa.remove(객체)
3. JPA를 사용하는 이유(2) - 유지보수
a) 우수한 유지보수
- 객체(DAO)에 새로운 필드가 추가되면 JPA가 알아서 해당 필드를 추가하여 SQL을 작성한다.
- 그러므로 따로 SQL을 고칠 필요도 고민할 필요도 없다.
- 즉, 개발자는 객체지향대로 개발하면 된다.
4. JPA를 사용하는 이유(3) - 구조적 차이 해결
- JPA는 OOP와 RDB의 구조적 차이를 해결(= 패러다임 불일치의 해결)
- 그렇다면 어떤 방식으로 패러다임의 불일치를 해결하는지 알아보자.
a) 상속관계 처리
- OOP의 상속관계를 RDB는 FK를 통한 참조로 구현한다. 즉, 서로 다른 메커니즘으로 동작하게 된다.
- 저장(create) 작업을 하는 경우, 부모와 자식 클래스에 해당하는 각 테이블에 SQL을 따로따로 적용해야 한다.
- JPA는 상속관계를 이해하고 이에 알맞은 SQL을 작성한다.
b) 연관관계 처리
- 어떤 클래스 A 내부에 클래스 B의 객체를 값으로 가지는 관계를 연관관계라고 말한다.
- 이와 같은 개념은 OOP에는 존재하지만, 마찬가지로 RDB에는 존재하지 않는 개념이다.
- 만약 연관관계에 있는 값을 조회(read)하는 경우, 클래스 A에 해당하는 테이블 A를 조회하고
클래스 B에 해당하는 테이블 B를 조회하여 값을 찾아야 한다.
- JPA는 연관관계를 이해하고 이에 알맞은 SQL을 작성한다.
c) 객체 그래프 탐색 처리
- RDB의 구조와는 상관없이 OOP의 객체 그대로를 탐색 및 사용할 수 있다.
- 클래스 A의 객체 내부에 클래스 B의 객체를 값으로 보유하고 있는 경우,
RDB에서는 테이블 A(= 클래스 A)와 B(= 클래스 B) 각각에서 데이터를 조회해야 한다.
- 그러나 JPA가 이를 알아서 처리해주기 때문에, 개발자는 이에 상관없이 OOP대로 개발하면 된다.
d) 객체 비교 처리
- 다음 예시를 살펴보자.
int userId = 100;
User userA = jpa.find(User.class, userId);
User userB = jpa.find(User.class, userId);
userA == userB;
- 위의 코드에서 userA와 userB를 비교하는 경우, 서로 다른 객체라고 생각할 것이다.
- 그러나 JPA를 사용하는 경우, 동일한 트랜젝션에서 조회한 Entity는 동일한 객체임을 보장한다.
- 즉, userA와 userB를 동일한 객체로 판단한다. 이는 아래에서 설명할 JPA의 캐싱 기능 때문이다.
e) 성능 최적화
- JPA를 사용하면 오히려 응답 속도가 느려지는 게 아닐까? 생각할 수 있다.
- 그러나 JPA의 다음 기능을 사용하여 성능 최적화를 이뤄낼 수 있다.
1. 캐시와 동일성(Identity) 보장
→ 동일한 SQL을 중복적으로 사용하는 경우, 이에 대한 결과 값을 캐시에 저장한다.
→ 즉, 매번 SQL을 DB에게 전달하여 결과 값을 반환받는 것이 아니라 동일한 명령에 대한 결과 값을 저장하여 사용한다.
→ 그러므로 응답 속도가 더욱 빠르다.
* 여기서 말하는 캐시는 일반적으로 메모리에서 사용되는 캐시를 말하는 것이 아니다.
고객의 요청에 의해 수행되는 한 번의 SQL 트랜잭션 사이클 내에서 동일한 SQL 명령에 대해서 말하는 것이다.
그러므로 굉장히 짧은 Lifecycle을 가지는 캐시다.
2. 트랜잭션을 지원하는 쓰기 지연(Transactional write-behind)
- 동일한 작업에 대해서 일괄적으로 처리하는 기능을 말한다.
- 예를 들어, 특정 데이터를 저장하는 기능을 3번 연속으로 요청한다고 해보자.
- 이를 따로따로 처리하는 경우, 3번의 네트워킹이 발생하게 된다.
- 즉, 동일한 기능을 처리하기 위해 3번의 연산이 발생하고, 그만큼 시간이 더 오래 걸린다.
- 그러므로 트랜잭션을 단일 처리하지 않고, 모아서 일괄 처리한다.
3. 지연 로딩(Lazy Loading)
- 지연 로딩이란, 객체가 사용되는 시점에 SQL이 수행되는 것을 말한다.
- 이는 연관관계를 가지는 객체를 사용할 때 효율적이다.
- 예를 들어, 클래스 A 내부에 클래스 B의 객체를 값으로 가지고 있다고 해보자.
- 클래스 A의 구현체인 객체 A의 값을 조회한 후 객체 A가 갖고 있는 클래스 B의 객체 값을 조회하는 경우
우선 객체 A의 값을 조회할 때 SQL 트랜잭션이 한번 발생한다.
- 그리고 객체 A가 가지고 있는 클래스 B의 값을 조회할 때 SQL 트랜잭션이 한번 더 발생한다.
- 이와 같이 처리하는 방식을 지연 로딩이라고 한다.
→ 다음 코드를 살펴보면 이해가 쉬울 것이다.
class User {
int userId;
String userName;
Team team;
}
class Team {
Sring teamName;
}
User userA = userDAO.find(userId); // => SELECT * FROM USER
Team team = userA.getTeam();
String teamName = team.getTeamName(); // => SELECT * FROM TEAM
- 지연 로딩을 항상 사용하는 것은 비효율적이다.
- 그러므로 프로그램의 흐름에 따라서 지연 로딩의 사용 여부를 옵션을 통해 설정하는 것을 권장한다.
- 위의 코드를 예시로 들자면 다음과 같은 상황이다.
→ User 객체를 조회할 때 반드시 Team 값을 조회하는 경우, 트랜잭션을 두 번 수행할 필요가 없다.
→ 오히려 모든 트랜잭션을 받아서 다음과 같이 트랜잭션을 한 번에 수행하는 것이 효율적이다.
→ 이와 같은 조회 방식을 즉시 로딩이라고 한다.
User userA = userDAO.find(userId);
Team team = userA.getTeam();
String teamName = team.getTeamName();
// => SELETC USER.*, TEAM.* FROM USER JOIN TEAM...
'Back-end > JPA 개념' 카테고리의 다른 글
5. Field(칼럼) Mapping (0) | 2022.03.28 |
---|---|
4. Entity Mapping (0) | 2022.03.25 |
3. JPA의 내부구조와 동작 (0) | 2022.03.24 |
2. JPA의 기본 구조와 기능 (0) | 2022.03.23 |
0. JPA를 사용하는 이유 (0) | 2022.03.21 |
댓글