작년에 스프링 부트 프로젝트를 참여하면서 웹사이트를 만들었었다.
만드는 과정에서 JPA, JPQL, ORM, Hibernate, nativeQuery, jQuery 생소한 내용들이 많이 쏟아져나와서 처음엔 헷갈렸다. 하나씩 알아보자!
JPA는 Java Persistence API의 약자로 자바에서 ORM(Object-Relational Mapping)을 사용하기 위한 자바 API 표준명세이다. 표준명세라고 하면은 어떤 기술이나 제품이 가져야하는 특성, 요구사항, 규칙 등을 명세화한 문서나 규격을 의미한다. 즉, JPA에서 기술명세는 해당 기술이나 제품의 동작원리, 사용방법 API 안터페이스 등을 자세하게 기술하고 정의한 문서이다.
ORM은 객체 지향 프로그래밍 언어와 관계형데이터베이스, 예를들어 Oracle, Mysql, H2 등 데이터베이스간에 데이터를 변환하는 기술이다.
변환을 시켜줘야하는 기술이 있어야하는 이유는 일반적으로 객체 지향 프로그래밍에서는 클래스와 객체를 사용하여 데이터를 표현하지만, 데이터베이스에서는 테이블, 행, 열 같은 구조로 데이터를 표현하고 담고 있다.
이러한 구조적 차이를 해결하기 위해서 객체와 테이블 간의 매핑(Mapping)을 수행한다. ORM을 사용하면 객체 지향 프로그래밍에서 사용하는 객체를 데이터베이스에서 사용하는 테이블로 매핑이할 수 있다.
이렇게 매핑된 객체는 데이터베이스에서 데이터를 읽어와서 객체로 변환시키거나, 객체를 데이터베이스에 저장할 때 사용된다.
ORM을 사용하면 개발자는 SQL 쿼리를 직접 작성하지 않아도 되고 좀 더 개발적인, 더 객체지향방식으로 데이터를 조작할 수 있다. 이로서 개발 생산성과 유지보수성을 높이는 데 높움이 된다.
대표적인 ORM 프레임워크가 처음엔 다들 헷갈려하는 Hibernate, JPA가 있다.(하이버네이트는 인터페이스인 JPA를 구현한 구현체이다)
다시 JPA 이야기로 돌아가자면, 그럼 JPA를 사용해서 좋은 점은 무엇일까?
DB에 저장되는 entity가 영속성 컨텍스트에 포함이된다면 이러한 이점과 특징이 있다.
1차 cache : 영속성 컨텍스트에는 1차 캐시를 담아두는 보관함이 있는데 데이터 베이스에 동일한 쿼리가 불필요하게 날아가는 것을 방지한다.
동일성 보장 트랜잭션을 지원하는 쓰기 지연 기능 : transaction.commit()을 하여 한번에 DB에 SQL 쿼리를 보내어 처리할 수 있다.
변경감지 : 영속이 되어 있는 객체가 변한다면 DB에 쿼리가 업데이트된다.
지연로딩 : N+1 쿼리문제를 해결 가능하다. 알 수 없는 쿼리가 날아가는 것을 막는다.
그럼 JPA 기술이 있고 개발자가 직접 SQL 쿼리를 작성하지 않아도 되는건데 그럼 안배워도 되는거아냐?
라고 나는 제일 먼저 떠올랐다.
하지만! 그건 아니다.
JPA를 사용하더라도 SQL 쿼리 작성과 해석능력이 중요하다 그 이유는
첫째, JPA는 복잡한 SQL 쿼리를 작성하는 것보다는 간단한 메소드 호출을 통해 데이터를 조회하거나 조작하는 정도로 사용하는 것을 권장한다. 예를들어 간단한 CRUD 작업이나 간단한 검색 쿼리등에만 해당한다. 복잡한 탐색 쿼리나 성능 최적화를 위해서는 여전히 SQL 쿼리를 작성해야한다.
둘째, JPA는 하이버네이트 같은 ORM 프레임워크를 기반으로 하고 있기 때문에 JPA로 실행되는 쿼리는 대부분 내부적으로 SQL 쿼리로 변환되어서 실행된다. 그래서 JPA를 사용하더라도 SQL 쿼리에 대한 이해가 없다면 JPA 동작원리나 성능 최적화 등을 이해하는데 어려울 수 있다.
JPA 핵심인 EntityManager와 EntityManagaerFactory 라이브러리는 엔티티의 CRUD를 처리한다. 하지만 개발자가 직접 EntityManager를 작성하는 일은 거의 없다. 주로 Repository Interface만 사용한다. Repository Interface(ex.Spring Data JPA)의 이점은 다음과 같다.
맨 앞에 말했다시피 JPA는 Interface의 집합이고 이를 구현한 구현체가 Hibernate, EclipseLink, DataNucleus 등 여러 ORM 프레임워크가 존재하는데 주로 Hibernate 사용한다.
JDBC는 Java Database Connectivity의 약자로 자바 언어를 이용하여 DB와 연동하기 위해 표준 인터페이스를 제공하는 API이다. JDBC API를 사용하면 자바 어플리케이션에서 DB에 접근하여 저장된 데이터를 CRUD하는 작업을 수행한다.
JDBC는 DB와 연결을 설정하고 관리하는데 필요한 클래스와 인터페이스를 제공한다. JDBC API는 데이터베이스 벤더에서 제공하는 드라이버를 이용하여 데이터베이스와 통신하며, 이 드라이버를 로드하고 연결을 설정하는 작업이 필요하다.
JDBC를 사용하여 데이터베이스와 연동하려면 다음과 같은 단계를 거쳐야 한다.
드라이버 로드: 해당 데이터베이스 벤더에서 제공하는 JDBC 드라이버를 로드한다.
연결 설정: DriverManager.getConnection() 메소드를 사용하여 데이터베이스와의 연결을 설정한다.
쿼리 실행: 연결된 데이터베이스에 SQL 쿼리를 실행하고 결과를 받아온다.
결과 처리: 받아온 결과를 처리하여 원하는 형태로 출력한다.
스프링부트는 내부적으로 JDBC를 사용하여 데이터베이스와 연동하고 있다. 스프링부트는 JDBC 드라이버와 연결 설정 정보를 자동으로 구성하고, DataSource를 빈으로 등록하여 데이터베이스 연결을 관리한다. 따라서 스프링부트에서는 JDBC를 직접 다룰 필요가 없으며, 데이터베이스와의 연동을 쉽게 구현할 수 있다.
스프링부트에서는 JPA(Java Persistence API)를 이용한 ORM 프레임워크인 Hibernate, MyBatis와 같은 데이터베이스 관련 라이브러리도 제공한다. 이러한 라이브러리를 사용하면 JDBC API를 직접 다루지 않아도 쉽게 데이터베이스와 연동할 수 있으며, 더욱 안정적인 코드 작성이 가능하다.
하지만, 만약 특별한 요구사항이 있어서 직접 JDBC API를 사용해야 할 경우에도 스프링부트에서는 JDBC API를 사용할 수 있다. 이 경우에도 스프링부트는 DataSource를 빈으로 등록하고, JDBC 드라이버와 연결 설정 정보를 자동으로 구성해주기 때문에 JDBC API를 이용한 데이터베이스 연동이 용이해진다.