[F-Lab 모각코 챌린지 31일차] JPA? Hibernate? JDBC?

부추·2023년 7월 1일
0

F-Lab 모각코 챌린지

목록 보기
31/66

TIL

JDBC, Spring JDBC, JPA, Hibernate, Spring Data JPA...



자바를 이용하여 데이터 관련 처리를 하거나 스프링 프로젝트에서 DB에 접근하게 되면 빼놓을 수 없는 단어들이다. 프로젝트 구현에만 집중하느라 JPA Repository의 derived query같은 편리한 기능들만 따와 쓰기 바빴고 정확한 원리나 그 근간에 있는 기술의 역사 등은 대충 넘겼다. 그동안 프로젝트를 진행하면서 '대충 이런 것들이겠지' 하고 쓰던게 마음에 걸려 한번 정리하는 시간을 가져보겠다!

1. JPA : ORM 가이드

Java Persistence API로, JAVA 언어 자체에서 제공하는 ORM(Object Relational Mapping) 기술에 대한 표준 명세이다.

ORM 프레임워크를 사용하면 MySQL, MariaDB 등의 관계형 DB 테이블과 자바의 Object를 매핑하여 SQL문을 직접 작성하지 않고 객체 관련 method를 호출하는 것만으로도 DB 데이터에 접근이 가능하다. 자바 어플리케이션 개발 과정에서 DB연결 및 SQL 작성에 시간을 많이 쏟는 것은 자바의 객체 지향 패러다임과 맞지 않기 때문에 이같은 ORM 기술이 발달한 듯 하다.

아무튼, JPA는 쉽게 말해 자바로 ORM 쓸거면 이 형식을 따라주세요~ 하고 자바 에서 만들어준 스펙이랄까. 안내서에 가까우므로 실제 구현은 없고 인터페이스로서 존재한다. JPA에는 실제 기능을 가진 method가 없기 때문에 JPA 하나만 가지고는 뭘 할 수가 없다.

위 사진에 보이는 Annotatation, Enum, Interface, Exception들은 JPA를 정의한 내용들이 있는 jakarta.persistence 패키지 내부의 클래스들이다. 스프링을 이용한 DB 프로젝트를 진행할 때 몇 번이고 이용했던 annotation들이 존재함을 확인할 수 있다.

아, 그래. 자바로 ORM 기술을 쓸 거면 이걸 Implement하라고 가이드를 준 건 알겠다. 근데 이제 뭐함? 구현체가 있어야 할 거 아냐. 그래야 그 구현체의 실제 method를 써서 데이터 CRUD를 진행할 수 있을텐데. 설마 개발자가 직접 JPA 구현해야 하는건가?



2. Hibernate : JPA의 구현체들 중 가장 대중적!

물론 아니다.(사실은 그럴 수도 있지만! 무료 자동차가 제공되는데 자동차를 직접 만들어 쓸 필요는 없다) 똑똑하신 개발자님들이 JPA를 구현한 프레임워크들이 이미 여러가지가 있기 때문이다. 그 전에, 자바 어플리케이션이 DB에 접근할 수 있는 근간의 기술이 되는 JDBC부터 알아보자.

# JDBC : java에서 DB와 연결하기 위한 API 제공

Java Database Connctivity의 약자인 JDBC는 java에서 다양한 DB에 접근하기 위한 API를 제공한다. JDBC API를 사용하는 전반적인 과정은 다음과 같다.

  • 사용하는 DB에 맞는 driver class load
  • 주소에 맞는 DB connect
  • connection object에 query 주입 + 실행
  • 결과 data 처리 (data <-> object)
  • DB connection 끊기

JDBC가 직접적으로 사용됐던 옛~날 코드를 보면 위 과정을 실제로 다 진행했다. 관련된 예외 처리도 하나하나 다 해줘야 했다. 아이디로 유저 하나 찾는데 필요한 코드가 30줄이 넘었다. 1) DB 연결 시작부터 종료까지 과정이 길고 복잡한 점, 2) 개발자가 일련의 과정에서 발생하는 checked exception을 전부 직접 처리해야 한다는 점, 3) DB independent하다는 점. 때문에 순수한 JDBC는 지금와선 쓰지 않는다. (내가 자동차를 직접 만드는 변태라 JPA를 손수 구현한다고 하면 사용해야 할 수도..)

별개로 Spring 환경에서 JDBC의 이용을 간편하게 해주는 Spring JDBC 모듈이 있지만(JdbcTemplate 이용), 이 역시 row mapper와 SQL문을 직접 작성해야 한다는 점 때문에 많이 쓰진 않는 것 같다.

JDBC는 순수하게 쓰려면 이용 과정이 매우 복잡하지만 자바 어플리케이션에서 DB와 연결하기 위해 필수적으로 이용해야하는 API이다. 이때문에 실제로 자바에서 DB와 관련해 동작하는 영속성 프레임워크들은 JDBC를 근간의 프레임워크로 사용한다. (영속성과 영속성 프레임워크가 무엇인지는 다른 글에서 정리하도록 하겠다)


다시 JPA를 구현한 프레임워크 얘기로 돌아와서. 자바에서 ORM기술을 사용하기 위한 JPA를 구현한 구현체 프레임워크들은 여러가지가 있다. 대표적으로 Hibernate, EclipseLink, DataNucleus 등이 있는데, 가장 많이 사용되는 것은 Hibernate이다. 역사가 오래된만큼 범용적으로 다양한 기술을 제공하고 있기 때문이라는데, 다른 것은 사용해보지 않아서 잘 모르겠다.

아무튼 "자바에서 ORM을 이용하기 위해선 기술 명세인 JPA를 따른 구현체를 사용해야 한다. 가장 대표적인 구현체는 Hibernate이다." 정도로 내용을 이해하면 될 것 같다.

자바 어플리케이션이 JPA 인터페이스에 구현된 내용을 사용하는 과정을 간단하게 볼 수 있는 그림이다.

JPA의 구현체들이 내부적으로 JDBC API를 사용하고 있다는 점까지 확인 가능한 그림이다.

JPA의 구현체, 대표적으로 Hibernate를 사용하여 디비에 접근하는 것은 직접 SQL문을 작성하는 것에 비해 어떤 장점과 단점이 있을까?

장점

  • 생산성 : SQL 작성 대신 간단한 method call로 DB에 존재하는 데이터의 CRUD가 가능하다. DB 연결과 exception 처리 등을 위한 코드를 반복적으로 작성할 필요가 없어진다.
  • 유지보수 : SQL을 직접 작성하는 경우, 테이블 구조가 변경되면 관련된 SQL문을 전부 수정해줘야 하지만 JPA를 사용하면 object 구조를 변경하는 것만으로도 관련 쿼리 수정 작업이 이뤄진다.
  • OOP 준수 : 자바 프로그래머로 하여금 SQL 작성자가 되게 하는 대신 객체 지향적인 비즈니스 로직 개발에 집중할 수 있도록 해준다.
  • DB independent : DBMS 회사마다 SQL문이 조금씩 다른 문제를 해결해준다. hibernate에게 어떤 DB driver을 사용할 것인지 property 설정만 해주면 해당 DBMS에 맞는 쿼리를 알아서 작성해준다.

단점

  • 성능 : method를 쿼리문으로 변환시키는 과정이 필요하기에, 아무래도 바로 실행 가능한 SQL문을 처음부터 제공하는 것에 비해 성능이 떨어진다. (초기에는 이게 심했다고 하나 최근엔 많은 개선이 이뤄진듯 하다)
  • 세밀함 : 복잡한 다수의 조건을 붙여 쿼리를 생성하는데 한계가 있다. JPQL같이 JPA에서 SQL과 유사한 쿼리를 직접 작성할 수 있게 해주는 기능을 사용하거나 QueryDSL을 사용하여 극복할 수 있다.
  • 공부? : DB 접근을 추상화한 ORM 프레임워크의 특징상 내부에 복잡한 동작이 숨어있고, 제대로 이해하지 않으면 잘못된 동작을 일으킬 수 있다.

자바에서 객체 중심의 개발에 집중할 수 있게 해주는 것만으로도 JPA의 역할은 차고 넘치는 것 같다. Hibernate, 참 좋은 놈이다.

그런데 나를 포함해서 최근 DB 대부분 hibernate에서 구현한 JPA의 EntityManager를 쓴 적이 없을 것이다. 대신 JpaRepository를 implement하여 derived query를 이용했겠지. 그럼 이건 뭘까? JPA 이름이 들어간 것을 보니 JPA를 사용하긴 한건데, 내가 hibernate 기능을 사용한 것인지 의문이 들 수 있다.



3. Spring Data JPA : JPA를 더 간단하게!

Repository를 사용했다는 것은, hibernate에서 제공하는 JPA 인터페이스 기능을 직접 사용하는 대신 그를 한 단계 더! 추상화시킨 Spring Data JPA를 사용했다고 이해하면 된다.

Raw JPA를 사용하려면 EntityManager를 사용해 transaction을 열어 영속성 컨텍스트 위에서 entity들을 관리해야한다. commit을 통해 새 entity를 persistent state로 둘 수도 있고, 컨텍스트의 entity를 detach 한 후 수정한 뒤 commit을 해 데이터를 수정할 수도 있고, 직접 쿼리를 날리거나 데이터를 지울 수도 있다.

Hibernate의 EntityManager를 사용하는 예제의 코드 흐름을 따라가보면 대충 어떤 식으로 동작하는지 확인할 수 있다.

여기서 스프링 프레임워크는 DB 매니징을 위해 한 단계 더 발전한 인터페이스를 제공하는데, 이것이 Spring Data JPA의 JpaRepository이다. JpaRepository를 사용하면 EntityManagerFactory를 통해 EntityManager를 호출하고, 트랜잭션을 열고, 객체를 detach시키고, 커밋하고, 트랜잭션을 닫고... 하는 반복적인 코드를 작성하는 대신 간단한 쿼리문을 정의하는 것만으로 내부에서 JPA를 이용할 수 있게 해준다. Derived Query라고 하는 Spring Data JPA의 강력한 기능이 있는데 그것은 따로 정리할 수 있는 기회가 있었으면 좋겠다.

실제로 JpaRepository가 구현된 내용을 보면 내부적으로 EntityManager를 사용함을 알 수 있다. 결국 JPA 구현체를 더 간단히 이용하기 위한 상위의 추상화 인터페이스라는 것이다. JpaRepository를 쓸 때 기본적으로 사용 가능한 findById() 내부에서 JPA의 EntityManager.find() method가 쓰이는 것을 확인하자.

package org.springframework.data.jpa.repository.support;

import ...

public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID> {

    private final EntityManager em;

    public Optional<T> findById(ID id) {

        Assert.notNull(id, ID_MUST_NOT_BE_NULL);

        Class<T> domainType = getDomainClass();

        if (metadata == null) {
            return Optional.ofNullable(em.find(domainType, id));
        }

        LockModeType type = metadata.getLockModeType();

        Map<String, Object> hints = getQueryHints().withFetchGraphs(em).asMap();

        return Optional.ofNullable(type == null ? em.find(domainType, id, hints) : em.find(domainType, id, type, hints));
    }

    // Other methods...
}

"Spring Data JPA는 Hibernate와 같은 JPA 구현체를 더 편하게 쓸 수 있게 해주는 Spring Data 라이브러리이다."

정도로 정리할 수 있겠다.

추가로, Spring Data JPA 환경에서 JPA의 구현체로써 채택하고 있는 것은 Hibernate이다.

JPA, 그를 구현한 Hibernate, 그 근간에 사용된 JDBC, 그리고 JPA를 편하게 사용하기 위한 Spring Data JPA의 관계를 한 눈에 정리하면 아래 그림이다.

참고로 Spring Data JPA는 Spring Data 프로젝트의 일부이며 JPA 구현체가 아닌 mongoDB 등을 사용할 때는 JpaRepository가 아닌 MongoRepository 등을 사용할 수도 있다.

Reference

https://suhwan.dev/2019/02/24/jpa-vs-hibernate-vs-spring-data-jpa/

https://velog.io/@adam2/JPA%EB%8A%94-%EB%8F%84%EB%8D%B0%EC%B2%B4-%EB%AD%98%EA%B9%8C-orm-%EC%98%81%EC%86%8D%EC%84%B1-hibernate-spring-data-jpa

profile
부추튀김인지 부추전일지 모를 정도로 빠싹한 부추전을 먹을래

0개의 댓글