JDBC + DataSource + 영속성 프레임워크 -1

spaghetti·2025년 10월 15일

데이터는 영속성이 중요하다. 영속성이란 프로그램이 종료되더라도 사라지지 않는 것을 뜻하며 애플리케이션은 데이터를 영구적으로 저장하기 위해서 JDBC, DataSource, 영속성 프레임워크 등의 기술들을 조합하여 사용한다. 구조를 보면 아래와 같다.

JDBC(Java Database Connectivity)

JDBC는 자바를 사용하여 데이터베이스에 접속하고 SQL을 실행하기 위한 Java API 다. JDBC의 구성 요소는 아래와 같다.

  • JDBC API

    • 자바 프로그램이 데이터베이스에 접속하고 SQL 문을 실행하며 결과를 처리하는 데 사용되는 인터페이스와 클래스 집합이다. 주로 java.sqljavax.sql 패키지에 포함되어 있다.
  • JDBC Driver

    • 데이터베이스 제공 회사가 제공하는 소프트웨어 모듈이다. JDBC API의 인터페이스를 구현하며, 자바 프로그램의 요청(SQL)을 해당 데이터베이스가 이해할 수 있는 네이티브 프로토콜로 변환하여 데이터베이스와 실제로 통신하는 역할을 한다. 보통 ojdbc.jar 과 같이 jar로 제공한다.

    • jdbc url 에서 사용하는 속성 jdbc:postgresql://localhost/test?user=fred&password=secret&ssl=true 과 같이 option부분은 jdbc의 동작방식을 설정하는 것이다. 드라이버는 ? 뒤에 붙은 옵션들을 파싱하여 자신의 내부 동작 모드를 변경한다. 즉, 이 옵션들은 드라이버 내부에 저장되는 설정 값으로, 이는 각 dbms의 jdbc driver에 따라 다르니 공식문서를 참고해야한다.

  • JDBC Driver Manager

    • JDBC 드라이버들을 관리하는 클래스다. 애플리케이션의 데이터베이스 연결 요청이 들어오면 적절한 JDBC 드라이버를 찾아 Connection 객체를 생성하고 반환하는 역할을 한다.
  • Connection

    • 특정 데이터베이스와의 연결 세션을 나타내는 인터페이스다. 데이터베이스와의 모든 통신은 이 Connection 객체를 통해 이루어지며, SQL 문 실행, 트랜잭션 관리(commit, rollback) 등의 기능을 제공한다. ORM 혹은 스프링의 @transactional 의 경우도 내부적으로는 Connection 객체를 활용하고 있다.
    • Connection은 Driver Manager를 통해서 획득하거나, Connection pool을 통해서 획득하는 등 여러 방법으로 해당 객체를 확장해서 사용할 수 있다.
  • Statement

    • 데이터베이스에 SQL 문을 전달하고 실행하는 데 사용되는 인터페이스이다.

      • Statement: 정적(Static) SQL 문을 실행할 때 사용된다. 정적인 sql문을 실행하기 때문에 injection 공격에 매우 취약해 직접적으로 쓴다면 주의가 필요하다.
      • PreparedStatement: 미리 컴파일된 SQL 문을 실행할 때 사용되며, 매개변수(parameter)를 동적으로 설정할 수 있어 성능 향상SQL Injection 방지에 유리하다. 영속성 프레임워크들은 모두 내부적으로 해당 객체를 사용하고 있다.
      • CallableStatement: 데이터베이스의 저장 프로시저(Stored Procedure)를 호출할 때 사용된다.
    • Statement 객체를 실제로 생성하고 메서드를 실행하는 것은 JDBC 드라이버의 역할이다.

      • 드라이버의 구현체 사용: Connection 인터페이스를 통해 createStatement()를 호출하면, JDBC 드라이버가 Statement 인터페이스를 구현한 특정 클래스(Statement 구현체)의 인스턴스를 반환한다.

      • SQL 번역 및 전송: 자바 애플리케이션이 stmt.executeQuery(SQL)를 호출하면, 이 구현체는 인자로 받은 SQL 문자열을 데이터베이스 서버가 이해할 수 있는 네이티브 프로토콜 형태로 변환한다. 이 프로토콜 데이터를 네트워크를 통해 DBMS로 전송한다.

      • 아래의 그림은 실제로 Statement 인터페이스의 구현체가 postgreSql driver의 구현체를 사용하고 있음을 보여준다.

      • 네트워크를 통해 전달받은 SQL 요청은 DBMS의 역할이다.

        1. SQL 파싱 및 실행: DBMS는 수신한 프로토콜 데이터를 다시 SQL로 해석하고, 해당 SQL 문(예: SELECT * FROM users)을 데이터베이스 엔진에서 실제로 실행하게 된다.
        2. 결과 반환: 실행 결과(데이터 또는 변경된 행의 수)를 다시 네이티브 프로토콜 형태로 JDBC 드라이버에게 반환한다.
    • JDBC 드라이버에서 해당 SQL을 번역하기 떄문에, DBMS에서 사용하는 문법을 해당 JDBC에 제공하고 있지 않으면 구문을 해석할 수 없기때문에 오류가 난다. 실제로 PostgreSQL의 확장 플러그인으로 $$ 라는 문법을 사용할 일이 있었는데 해당 부분을 처리해주는 JDBC가 기본 PostgreSQL Driver에는 표준 구문으로 인식하기 위한 처리가 존재하지 않는다. 그래서 정적 SQL을 지원하는 Statement 가 아닌 내부적으로 PreparedStatement 를 사용하는 클래스들은 처리과정에서 오류가 발생하게 되는 것을 발견했다.

  • ResultSet

    SELECT와 같은 질의(Query) 결과를 담는 데이터 집합이다. 데이터베이스에서 반환된 테이블 형태의 데이터를 자바 프로그램이 접근하고 처리할 수 있도록 하며, 커서를 이용하여 한 행씩 이동하며 데이터를 읽어온다.

  • jdbc 실행 순서는 다음과 같다.

    1. jdbc 드라이버 로드
    2. connection 객체 생성
    3. statement 객체 생성
    4. sql 실행 및 결과 처리
    5. 자원해제(resultSet, Statement, Connection 객체들을 close)

DataSource

커넥션을 가장 기본적으로 사용할때는 Driver Manger를 통해 매번 데이터베이스에 직접 연결을 시도하는데, 이는 연결 요청이 있을때 마다 매번 새 연결을 만들고 닫아야하기 떄문에 시간이 많이 소요된다.

이러한 단점을 극복하기 위해 데이터베이스와 매번 새로운 커넥션을 생성하는 대신 미리 생성된 커넥션을 커넥션 풀에 보관하고 꺼내서 사용하는 방식을 사용한다.

이를 추상화한 DataSource 인터페이스를 통해서 커넥션 연결을한다. 이러한 DataSource의 구현체는 대표적으로 HikariCp, Apache DBCP 등이 있다.

기본 jdbc 를 직접 사용할 경우 개발자는 아래와 같이 일일히 커넥션을 호출하게 된다.

Connection conn = DriverManager.getConnection(url, user, password);

그러나 DataSource를 사용하면 DataSource.getConnection() 요청을 처리할 때, HikariCP와 같은 커넥션 풀 구현체가 가로채서 미리 만들어 둔 연결을 재사용한다.
내부적으로 다시 driverManger를 사용할때는

  • 풀 초기화: 애플리케이션 시작 시, 풀이 설정된 최소 크기만큼의 연결을 만들 때.
  • 풀 확장: 동시 요청이 증가하여 풀 내의 유휴 연결이 모두 소진되었고, 풀의 최대 크기(maximum-pool-size) 한도 내에서 새 연결을 만들어야 할 때

의 경우에만 사용된다.

보통은 Connection pool을 이용하기 위해 위의 구현체를 사용하지만 Driver Manger를 기반으로하여 커넥션 풀을 제공하지 않는 DriverManagerDataSource 도 제공하고 있다. 이는 spring 프레임워크에서 제공하고 있는 구현체다.

Spring Boot는 HikariCP를 기본적으로 사용하고 있는데, 이 기본적인 사용은 자동설정과 속성 기반 설정, 표준화된 인터페이스 제공을 통해 구현된다.

  • HikariCP 선택: Spring Boot는 클래스패스를 스캔하여 사용 가능한 커넥션 풀 구현체들을 찾는다. HikariCP가 발견되면, 성능상의 이유로 다른 풀보다 우선적으로 선택하게 된다.
  • DataSource 빈 생성: Spring Boot는 개발자의 코드 없이도 DataSource 인터페이스를 구현한 HikariCP 객체를 생성하고 이를 Spring 컨테이너의 빈(Bean)으로 자동 등록한다.
  • 설정파일(yml, properties)을 통해 spring.datasource.url 과 같은 설정들을 읽어와 HikariCP에 자동으로 주입하게 된다.

[출처]

profile
개발 그렇게 하는거 아닌데의 그렇게를 맡고있습니다

0개의 댓글