•
JPA는 지루하고 반복적인 CRUD SQL을 알아서 처리해줄 뿐만 아니라 객체 모델링과 관계형 데이터베이스 사이의 병목도 해결해준다.
•
외에도 실행 시점에 자동으로 SQL을 자동으로 생성하여 실행하는데, 이덕분에 개발자는 SQL을 직접 작성하는 것이 아니라 어떤 SQL이 실행될지 생각만 하면 된다.
•
JPA를 사용하면 어플리케이션을 테이블 중심이 아닌 객체 중심으로 개발하여 생산성과 유지보수성이 향상되며 테스트를 작성하기도 편리해진다. 이런 장점 덕분에 버그도 줄어든다.
1. SQL을 직접 다룰 때 발생하는 문제점
•
관계형 데이터베이스는 가장 대중적인 데이터 저장소다. 데이터베이스에서 데이터를 관리하려면 SQL을 사용해야 한다.
•
일반적으로 자바 어플리케이션은 JDBC API를 사용해서 질의를 데이터베이스에 전달하는데, 자바 백엔드 개발자들에겐 너무나 당연한 이야기고 누구나 능숙하게 SQL을 다룰 줄 안다.
1. 반복, 반복 그리고 반복
•
일반적으로 백엔드는 CRUD 작업이 90%를 이룬다고 생각하는데, 객체마다 CRUD를 위해 수많은 쿼리문을 작성해야 한다.
•
이렇듯 데이터 접근 계층을 개발하는 일은 지루함과 반복의 연속이다.
2. SQL에 의존적인 개발
•
SQL의 문제점은, 데이터 접근 계층을 사용해서 SQL을 숨겨도 DAO를 열어서 어떤 SQL이 실행되는지 확인해야 한다는 점이다.
•
비즈니스 요구사항을 모델링한 객체를 엔티티라하는데, SQL에 모든 것을 의존하는 상황에서는 개발자들이 엔티티를 신뢰하고 사용할 수 없다.
◦
DAO를 열어서 어떤 SQL이 실행되고 이것이 엔티티에 어떻게 매핑되는지 일일이 확인해야 한다.
◦
물리적으로는 SQL과 JDBC API를 데이터 접근 계층에 숨겼을지 몰라도 논리적으로는 엔티티와 아주 강한 의존관계를 가지게 된다.
▪
때문에 회원을 조회할 때는 물론이고 회원 객체에 속성을 하나 추가할 때도 DAO의 CRUD 코드와 SQL 대부분을 변경해야 하는 문제가 발생한다.
3. JPA와 문제 해결
•
JPA를 사용하면 개발자가 직접 SQL을 작정하지 않고 JPA가 제공하는 API를 사용하면 된다. 그러면 JPA가 개발자 대신에 적절한 SQL을 생성해서 데이터베이스에 전달한다.
2. 패러다임의 불일치
•
어플리케이션은 발전하면서 그 내부의 복잡성이 점점 커진다. 지속 가능한 어플리케이션을 만들려면 끊임없이 증가하는 복잡성과 싸워야한다.
•
객체지향은 추상화, 캡슐화, 정보은닉, 상속, 다형성 등의 개념들로 복잡성을 다룰 수 있게 해주는데 비즈니스 요구사항을 정의한 도메인 모델도 객체로 모델링하면 객체지향 언어가 가진 장점들을 활용할 수 있다.
•
문제는 이렇게 모델링한 도메인 모델을 저장할 때 발생한다. 결국 데이터의 영속성을 보장해야하기 때문이다.
•
일반적으로 객체는 속성과 기능을 가지는데, 기능은 클래스에 정의되어있으므로 객체 인스턴스의 상태만 저장했다가 필요할 때 불러와서 상태를 토대로 객체를 생성해주면 된다.
•
그러나 부모 객체를 상속받았거나 다른 객체를 참조하고 있다면 객체의 상태를 저장하기가 쉽지 않다.
◦
예를 들어 회원 객체를 저장하는 경우, 회원 객체가 팀 객체를 참조하고 있다면 회원 객체를 저장할 때, 팀 객체도 함께 저장해야 한다. 단순회 회원 객체만 저장하면 팀 객체를 잃어버린다.
•
객체와 관계형 데이터베이스는 지향하는 목적이 서로 달라 둘의 기능과 표현 방법이 다르다. 이것을 객체와 관계형 데이터베이스의 패러다임 불일치 문제라 한다. 따라서 객체를 테이블에 저장하는 데는 한계가 있다.
•
이런 패러다임 불일치는 개발자로 하여금 해결에 많은 시간을 소비하게 한다. 이 장에선 패러다임 불일치로 발생하는 문제와 JPA를 통한 해결책을 알아보겠다.
1. 상속
•
객체는 상속이라는 개념을 가지고 있지만 테이블은 없다.
◦
데이터베이스 모델링에서 이야기하는 슈퍼타입, 서브타입 관계를 사용하면 객체 상속과 가장 유사한 형태로 테이블을 설계할 수 있다.
◦
이 경우, 객체를 쿼리하려면 슈퍼타입, 서브타입 각각에 대해 수행해주어야 하므로 패러다임 불일치 해소를 위한 비용이 발생한다.
•
JPA는 상속과 관련된 패러다임 불일치 문제를 대신 해결해준다. 개발자는 마치 컬렉션 API에 객체를 저장하듯이 JPA에게 객체를 저장하면 된다.
2. 연관관계
•
객체는 참조를 사용해서 다른 객체와 연관관계를 가지고 참조에 접근해서 연관된 객체를 조회한다. 반면 테이블은 외래 키를 사용해서 다른 테이블과의 연관관계를 가지고 조인을 사용해서 연관된 테이블을 조회한다.
•
참조를 사용하는 객체와 외래 키를 사용하는 관계형 데이터베이스 사이의 패러다임 불일치는 극복하기 어렵다.
◦
객체를 테이블에 맞추어 객체가 다른 객체를 참조하지 않고 테이블처럼 키를 가지고 있게끔 모델링해보자.
▪
이 경우, 객체가 다른 객체를 참조할 수 없게된다. 좋은 객체 모델링을 기대하기 어렵고 결국 객체지향의 장점을 잃어버리게 된다.
◦
테이블을 객체에 맞추어 객체가 다른 객체를 참조하도록 모델링해보자.
▪
이 경우, 객체를 테이블에 저장하거나 조회하기가 쉽지 않다. 결국 개발자가 중간에서 이를 적절히 변환해주어야 한다.
•
JPA는 연관관계와 관련된 패러다임 불일치 문제를 해결해준다. 개발자는 단순히 객체 간의 관계를 설정하고 객체를 저장만 하면 된다.
◦
JPA는 참조를 외래 키로 변환해서 적절한 삽입 SQL을 데이터베이스에 전달한다. 객체를 조회하는 경우(외래 키를 참조로 변환하는 경우)에도 JPA가 처리해준다.
3. 객체 그래프 탐색
•
객체는 마음껏 그래프를 탐색할 수 있어야 한다. 그러나 SQL을 직접 다루면 처음 실행한 SQL에 따라 탐색 가능한 객체 그래프의 범위가 정해진다.
◦
비즈니스 로직에 따라 사용하는 객체 그래프가 다르고 이 비즈니스 로직은 언제든 변경될 가능성이 높은 부분인데 SQL에 의존하는 경우, 변경사항이 많아지고 유지보수가 어렵다. 오류또한 많이 일어날 수 밖에 없다.
◦
객체 그래프를 탐색할 수 있는지 확인하려면 DAO를 열어서 SQL을 직접 확인해야 한다. 그렇다고 연관된 모든 객체 그래프를 데이터베이스에서 조회해두어 메모리에 올려두는 것은 현명한 방법이 아니다.
◦
결국 상황에 따라 DAO에 메소드를 여럿 만들어 사용해야 한다.
•
JPA를 사용하면 객체 그래프를 마음껏 탐색할 수 있다.
•
JPA는 연관된 객체를 사용하는 시점에 적절한 조회 SQL을 실행한다. 따라서 JPA를 사용하면 연관된 객체를 신뢰하고 마음껏 조회할 수 있다.
◦
이 기능은 실제 객체를 사용하는 시점까지 조회를 미룬다고 해서 지연 로딩이라 한다.
◦
JPA는 연관된 객체를 즉시 조회할지, 실제 사용되는 시점에 지연해서 조회할지를 간단한 설정으로 정의할 수 있다.
4. 비교
•
데이터베이스는 기본 키의 값으로 각 행을 구분한다. 반면 객체는 동일성, 동등성 비교라는 두 가지 비교 방법이 있다. 따라서 두 방법 간에는 차이가 존재한다.
•
이런 이유로 패러다임 불일치 문제가 발생한다.
◦
같은 objectId로 조회해서 object1, object2를 만드는 경우, 참조값이 달라 다른 객체로 인식한다.
•
JPA는 같은 트랜잭션일 때, 같은 객체가 조회되는 것을 보장한다. 그러므로 앞선 경우에 대해 동일성 비교가 true임을 보장한다.
◦
객체 비교하기는 분산 환경이나 트랜잭션의 격리수준까지 고려하면 더 복잡해진다. 이는 추후에 다룰 예정이다.
5. 정리
•
객체 모델과 관계형 데이터베이스 모델은 지향하는 패러다임이 서로 다르다. 문제는 따라서 이 패러다임의 차이를 극복하기 위해 개발자가 너무 많은 비용을 사용해야 한다는 점이다.
•
더 어려운 문제는 객체지향 어플리케이션답게 정교한 객체 모델링을 수행할 수록 관계형 데이터베이스와의 패러다임 불일치 문제는 더 커진다는 점이다.
⇒ 분명 복잡성을 낮추기 위해 객체지향적으로 개발하는데, 패러다임 불일치 문제 때문에 더 복잡해지고 있다.
◦
결국 객체 모델링을 포기하고 점점 데이터 중심의 모델로 변해간다.
•
JPA는 패러다임의 불일치 문제를 해결해주고 정교한 객체 모델링을 유지할 수 있도록 도와준다.
3. JPA란 무엇인가?
•
JPA는 자바 진영의 ORM 기술 표준으로 어플리케이션과 JDBC 사이에서 동작한다.
◦
그렇다면 ORM은 무엇일까? 이름 그대로 객체와 관계형 데이터베이스를 매핑한다는 뜻이다. ORM 프레임워크는 객체와 테이블을 매핑해서 패러다임 불일치 문제를 개발자 대신 해결해준다.
•
어느정도 성숙한 객체지향 언어에는 대부분 ORM 프레임워크들이 존재하는데, 각 프레임워크의 성숙도에 따라 단순히 객체 하나를 CRUD 하는 정도부터 패러다임 불일치 문제를 대부분 해결해주기도 한다.
◦
자바 진영에도 다양한 ORM 프레임워크들이 있는데, 그중에 가장 많이 사용되는 건 하이버네이트 프레임워크로 거의 대부분의 패러다임 불일치 문제를 해결해주는 성숙한 ORM 프레임워크다.
1. JPA 소개
•
JPA는 자바 ORM 기술에 대한 API 표준 Spec으로 JPA를 사용하려면 JPA를 구현한 ORM 프레임워크를 선택해야 한다.
•
JPA라는 표준 덕분에 특정 구현 기술에 대한 의존도를 줄일 수 있고 다른 구현 기술로 손쉽게 대체할 수 있다. 도한 JPA 표준은 일반적이고 공통적인 기능의 모음이다.
◦
따라서 표준을 먼저 이해하고 필요에 따라 JPA 구현체가 제공하는 고유의 기능을 알아가면 된다.
2. 왜 JPA를 사용해야 하는가?
•
생산성
◦
JPA를 사용하면 지루하고 반복적인 코드와 CRUD용 SQL을 개발자가 직접 작성할 필요가 없어진다.
◦
더 나아가 JPA는 DDL 문을 자동으로 생성해주는 기능도 있다. 이런 기능들을 사용하면 데이터베이스 설계 중심의 패러다임을 효과적으로 객체 설계 중심으로 역전시킬 수 있다.
•
유지보수
◦
SQL에 의존적인 개발에서도 얘기했듯 SQL을 직접 다루면 변경사항 적용시 SQL도 변경이 필요했다.
◦
반면 JPA를 사용하면 이런 과정을 JPA가 대신 처리해주므로 이런 과정이 생략된다. 따라서 유지보수해야 하는 코드의 양도 줄어든다.
▪
또한 패러다임 불일치 문제를 해결해주므로 객체지향 언어가 가진 장점들을 활용해서 유연하고 유지보수하기 좋은 도메인 모델을 편리하게 설계할 수 있다.
•
패러다임 불일치 해결
◦
JPA는 상속, 연관관계, 객체 그래프 탐색, 비교하기와 같은 패러다임의 불일치 문제를 해결해준다.
•
성능
◦
JPA는 어플리케이션과 데이터베이스 사이에서 다양한 성능 최적화 기회를 제공한다.
▪
이처럼 어플리케이션과 데이터베이스 사이에 계층이 하나 더 있는 경우, 최적화 관점에서 시도해 볼 수 있는 것(캐시 등)들이 많다.
•
데이터 접근 추상화와 벤더 독립성
◦
데이터 접근이 추상화되므로 구현 세부 사항을 신경쓸 필요 없이 프로그래밍할 수 있다.
◦
예를 들어, 테스트 용으로 H2 데이터베이스를 사용하고 개발이나 제품 환경에서는 MySQL을 사용하는 경우, 각각 SQL을 모두 작성해주어야 하지만 JPA는 구현체를 바꾸는 방법으로 쉽게 해결해준다.
•
표준
◦
JPA는 자바 진영의 ORM 기술 표준이다. 표준을 사용하면 다른 구현 기술로 손쉽게 변경할 수 있다.
4. 정리
•
SQL을 직접 다루는 경우, 발생하는 다양한 문제와 객체지향 언어, 관계형 데이터베이스 사이의 패러다임 불일치 문제를 설명했다.
•
JPA가 각 문제를 어떻게 해결하는지도 알아보았고 마지막으론 JPA가 무엇인지 설명하고 JPA의 장점들을 소개했다.
•
다음 장에서는 테이블 하나를 CRUD하는 간단한 JPA 어플리케이션을 만들어본다.
Q & A, ORM에 대한 궁금증과 오해
ORM 프레임워크를 사용하면 SQL과 데이터베이스는 잘 몰라도 되나요?
•
ORM 프레임워크가 어플리케이션을 객체지향적으로 개발할 수 있도록 돕긴 하나 데이터는 결국 관계형 데이터베이스에 저장된다. 따라서 테이블 설계는 여전히 중요하고 SQL도 잘 알아야 한다.
•
ORM 프레임워크를 사용할 때 가장 중요한 일은 객체와 테이블을 매핑하는 것으로 매핑을 올바르게 수행하려면 객체와 관계형 데이터베이스 양쪽을 모두 잘 이해하고 있어야 한다.
성능이 느리진 않나요?
•
JPA는 다양한 성능 최적화 기능을 제공해서 잘 이해하고 사용하면 SQL을 직접 사용하는 경우보다 더 좋은 성능을 낼 수도 있다.
◦
JPA의 네이티브 SQL 기능을 사용해서 SQL을 직접 호출하는 것도 가능하다.
•
하지만 JPA를 잘 이해하지 못하고 사용하면 N+1 같은 문제로 인해 심각한 성능 저하가 발생할 수 잇다.
통계 쿼리처럼 매우 복잡한 SQL은 어떻게 하나요?
•
JPA는 통계 쿼리같이 복잡한 쿼리보다는 실시간 처리용 쿼리에 더 최적화되어 있다.
•
상황에 따라 다르지만 정말 복잡한 통계 쿼리는 SQL을 직접 작성하는 것이 더 나은 경우가 많다. 따라서 네이티브 SQL을 사용하거나 MyBatis나 QueryDsl을 혼용하는 것도 좋은 방법이다.
학습곡선이 높다고 하던데요?
•
높다. JPA를 사용하려면 객체와 관계형 데이터베이스를 어떻게 매핑하는지 학습한 후 JPA의 핵심 개념들을 이해해야 한다.
•
또한 JPA의 핵심 개념인 영속성 컨텍스트에 대한 이해가 부족하면 SQL을 직접 사용해서 개발하는 것만 못한 상황이 벌어질 수 있다.
•
JPA가 어려운 근본적인 이유는 ORM이 객체지향과 관계형 데이터베이스라는 두 기둥 위에 존재하기 때문이다. 이 둘의 기초가 부족하면 어려울 수 밖에 없다.