우테코 5기

JDBC, SQL Mapper, ORM

teo_99 2023. 6. 15. 14:52

영속성이란?

영속성이란 데이터를 생성한 프로그램의 실행이 종료되더라도 사라지지 않는 데이터의 특성을 의미합니다. 우리가 일반적으로 코드 레벨에서 다루는 객체는 이런 영속성을 가지지 않습니다. 객체는 메모리 상에서만 존재하며 어플리케이션이 종료되면 사라집니다.

 

우리가 만든 객체들에게 영속성이라는 특성을 부여하려면 어떻게 해야할까요? 쉽게 말해, 객체를 어떻게 영구하게 저장할 수 있을까요? 이런 고민을 해결해주는게 JDBC, SQL Mapper, ORM입니다.

 

그리고 각각은 다른 특성을 가지고 있습니다. 이번에는 세 가지 기술에 대해 정리해보고자 합니다.

 

JDBC 

가장 먼저 살펴볼 영속화 기술은 JDBC입니다.

JDBC(Java DataBase Connectivity)는 자바에서 데이터베이스에 접속할 수 있도록 하는 자바 API입니다. JDBC를 통해 코드 레벨에서 데이터베이스 작업이 가능해집니다.

 

그리고 JDBC는 큰 장점이 있는데요, 바로 어떤 DBMS이든지 간에 동일한 인터페이스로 다룰 수 있다는 것입니다. 이런 메커니즘을 가능하게 하는 이유는 JDBC가 아래와 같은 구조이기 때문입니다. 

 

JDBC는 크게 JDBC 인터페이스와 JDBC 드라이버로 구성이 되어 있는데요,

클라이언트(개발자)가 사용하는 부분은 JDBC 인터페이스이며, 구현체는 JDBC DriverManager에 의해 관리됩니다. 그러니까 사용자는 단순히 JDBC API에만 접근하면 되고 어떤 영속화 도구를 사용하든 관계 없이 JDBC Driver Manager가 적절히 관리해준다는 것이죠. 

 

이런 JDBC API를 활용해서 개발자는 특정 벤더사에 의존적이지 않은 코드를 작성할 수 있게 되었습니다. 하지만 JDBC API를 직접적으로 사용하는 것은 몇가지 문제가 있었는데요,

 

  1. 중복 코드가 많음
  2. 커넥션 관리 등 데이터베이스를 다루기 위한 부가적인 작업이 번거로움
  3. 커넥션과 같은 시스템 자원을 직접적으로 다루기에 자원 누수가 발생할 수 있음 
  4. JDBC API는 Checked Exception을 던지기에, 이에 따른 관리가 어려움

 

그리고 이런 문제점들을 해결하기 위해 나온 것이 SQL Mapper입니다.

 

SQL Mapper

SQL Mapper란, JDBC를 한단계 더 추상화한 것입니다. 앞서 말한 문제점들을 해결하기 위해서 JDBC를 캡슐화 한 것이죠. 대표적인 SQL Mapper의 예로는 MyBatis, Spring JDBC가 있습니다.

 

아래 그림을 통해 대표적인 MaBatis, Spring JDBC의 구조를 보겠습니다.

 

 

MyBatis 구조

 

Spring JDBC 구조

공통점이 보이시나요? MaBatis나 Spring JDBC나 앞서 설명드린대로 JDBC를 사용하는 구조입니다. 정말 JDBC를 추상화 한 것이라고 볼 수 있는 것이죠.

 

그리고 여기서 Datasource라는 개념도 등장했습니다. DataSource란 DriverManager를 한단계 더 추상화 한 것이라 볼 수 있는데요, 기존 JDBC API에서는 Connection을 개발자가 직접 관리해주어야 했습니다. 하지만 DataSource를 사용하는 경우, 단순하게 설정 정보를 세팅만 해주면 connection pool을 쉽게 관리할 수 있게 됩니다. 

 

다시 본론으로 돌아와서, Spring JDBC나 MyBatis는 어떤 식으로 JDBC를 추상화할까요?

 

  1. SQL문과 객체 필드를 매핑해서 데이터를 객체화 할 수 있음
  2. Connection 관리 등 부가적인 작업을 개발자가 수행할 필요가 없음

이외에도 트랜잭션 제어, Statement 준비와 실행 등과 같이 기존의 JDBC API를 사용하면서 마주쳤던 자질구레한 일들을 모두 담당하는게 SQL Mapper입니다.

 

여기에 한술 더 떠서 SQL을 Java 코드 외부(XML)로 분리해버리자! 라는 취지를 가진게 MyBatis입니다. SQL을 외부에서 관리했을 때 동적 쿼리 생성, 가독성, 쿼리 관리의 용이성 측면에서 이점을 얻을 수 있었기 때문입니다.

 

 하지만 이런 SQL Mapper도 문제점은 있었습니다.

  1. 데이터 중심 개발(SQL문을 직접 작성해야 하기 때문에 SQL에 의존적인 개발을 하기가 쉬움)
  2. 패러다임의 불일치

일반적으로 객체지향적인 코드를 짜는 이유는 비즈니스의 변경에 쉽게 대응할 수 있기 때문입니다. 하지만 이런 SQL Mapper, JDBC를 사용하게 되면 데이터 중심 개발을 하게 되기 쉽습니다. 개발자는 쿼리에 대해 항상 고민해야 하며, 객체를 어떻게 영속화/복원할지를 항상 고민해야 합니다.

 

그러다보면 비즈니스 로직과 데이터베이스는 강한 결합이 되고, 결국 유지보수하기 어려운 코드가 탄생하게 됩니다. 객체지향 언어를 사용하면서 절차지향적, 데이터 중심적인 코드를 작성하게 되는거죠. 

 

그리고 이는 곧 ORM의 등장 배경이 됩니다.

 

ORM(Object Relational Mapping)

ORM은 앞서 말한 문제점(패러다임의 불일치, 데이터 중심 개발)을 해결하기 위해 등장했습니다. ORM은 데이터베이스와 객체 지향 프로그래밍 언어 간의 호환되지 않는 데이터를 변환하는 프로그래밍 기법을 의미합니다. Java에서는 JPA로 ORM 기법을 활용할 수 있습니다. JPA의 대표적인 구현체로는 Hibernate가 있구요.

 

Hibernate도 내부적으로 JDBC를 사용합니다.

 

ORM을 사용하면 개발자는 더욱 객체지향스러운 코드에만 집중할 수 있습니다. 단순히 객체를 저장, 조회할 때 컬렉션을 사용하는 것처럼 CRUD를 진행할 수 있다는 것이죠. 

// SQL Mapper 사용 시
String sql = "insert ...";
jdbcTemplate.update(sql, ...);

// ORM 사용 시
entityManager.persist(member);

 

이외에도 JPA와 같은 ORM은 쿼리 자동 생성, 변경 감지, 지연 쓰기, 지연 로딩, 1차 캐시와 같은 기술들을 제공하면서 개발자가 더욱 객체지향적인 코드에만 집중할 수 있도록 합니다. 이런 기술이 가능한 이유는 영속성 컨텍스트라고 하는 개념이 존재하기 때문인데 이에 대해서는 깊게 설명하지 않겠습니다.

 

아무튼 이런 ORM의 등장으로 개발자는 객체지향적인 코드에만 집중할 수 있게 되었고, 생산성을 극대화시킬 수 있었습니다. 또한 특정 벤더사에 종속적이지 않은 코드를 작성할 수 있게 되었습니다.

 

하지만 ORM이라고 해서 단점이 없는 실버 불릿은 아닙니다. 일단 학습 비용이 비싸고, 복잡한 쿼리의 사용이 어려우며 성능은 일반 쿼리보다 떨어질 수 있음을 알아야 합니다.

 

 

참고자료

https://www.youtube.com/watch?v=mezbxKGu68Y

https://velog.io/@byeongju/DataSource-cbd8ln4x

https://www.devteam.space/blog/hibernate-vs-jdbc-which-to-choose/