본문 바로가기
TIL/Java | Spring Boot

[Spring] 데이터 접근 기술 - 1. JDBC, Connection Pool과 DataSource

by yeon_zoo 2022. 12. 27.

1. JDBC 이해

JDBC는 대표적으로 다음 3가지를 표준 인터페이스로 정의한다. 

  • java.sql.Connection : 연결
  • java.sql.Statement : SQL을 담은 내용
  • java.sql.ResultSet : SQL 요청 응답

위의 내용들은 각각의 DB 벤더에서 구현하여 라이브러리(JDBC 드라이버)로 제공한다. 이를 이용하면 개발자는 DB를 바꿔도 추상화된 인터페이스만을 의존하고 있기 때문에 사용처에서 코드를 수정할 필요가 없다. 개발자가 새로운 사용법을 학습할 필요가 없게 되는 것이다. 

 

하지만 이런 표준화에도 한계는 있다. 데이터베이스마다 다른 것들이 있다. 보통 ANSI SQL을 통해서 규격화된 모양을 가지고 있지만 페이징 처리하는 방법은 SQL마다 다르다. DB를 변경하면 JDBC를 사용하는 코드를 변경할 필요는 없지만 SQL은 DB에 맞게 수정이 필요한 것이다. 이럴 때 JPA를 이용한다면 각 DB마다 다른 SQL을 정의하는 문제를 해결할 수 있다. 

 

※ ORM : 객체와 관계형 데이터베이스 테이블을 매핑하는 기술. 개발자가 반복적인 SQL 작성하는 것을 막고 SQL을 동적으로 만들어서 실행할 수 있도록 돕는다. 또한, 서로 다른 데이터베이스가 서로 다른 SQL 문법을 갖는 것도 해결해준다. JPA는 자바 진영의 ORM 표준 인터페이스이다. 

 

※ SQL Mapper vs. ORM 기술

SQL Mapper는 SQL만 알면 금방 작성이 가능하다. 반면에 ORM은 SQL을 작성하지 않아도 되기 때문에 개발 생산성은 높아지지만, 관리하지 쉽지 않아서 실무에 적용하려면 깊게 공부할 필요가 있다. 


2. 커넥션 풀(Connection Pool)과 데이터소스(DataSource) 이해

커넥션 풀을 이해하려면 커넥션이 생성되는 과정을 이해해야 한다. 정리하면 커넥션이 생성되는 과정은 다음과 같다. 

  1. 사용자가 어플리케이션 로직에 요청한다. 
  2. 어플리케이션 로직에서 DB 드라이버로 커넥션을 조회한다. 
  3. DB 드라이버는 DB에 TCP/IP 커넥션을 연결한다. 
  4. DB 드라이버는 DB에 추가적인 정보(ID, PW, 부가적인 정보)를 전달한다.
  5. DB 내부에서는 ID와 PW 등을 이용하여 인증하고 DB 세션을 생성한다. 
  6. DB는 커넥션을 생성해 DB 드라이버에 부여한다. 
  7. DB 드라이버는 어플리케이션 로직에 이 커넥션을 반환한다. 

이렇듯 커넥션을 새로 만드는 것은 과정이 복잡하고 시간 소모가 많다. (SQL을 실행하는 시간 외에도 새로운 커넥션을 생성하는 시간까지 소요되기 때문이다) 그리고 TCP/IP 커넥션을 생성하기 위해서 리소스도 매번 사용해야 한다. 이를 해결하고자 나온 아이디어가 커넥션을 미리 생성해두고 생성된 커넥션을 가져다가 쓰는 커넥션 풀의 개념이다. 대표적인 예로는 HikariCP 가 있다. 

 

커넥션 풀에서 커넥션을 여러 개 (디폴트로는 10개 정도) 만들어두고 사용한다. getConnection() 하게 되면 10개 중에 하나를 사용한다. 해당 커넥션을 가져왔던 application 로직이 수행을 끝내고 나면 풀에 커넥션을 반납한다. 이 때 커넥션을 종료하는 것은 아니고, 10개의 커넥션은 application 이 켜져 있는 동안 내내 연결 상태를 유지하고 있다. 

 

DataSource의 이해

Connection을 얻는 방법에는 JDBC DriverManager를 직접 사용하거나 커넥션 풀을 사용하는 방법 등 다양한 방법이 존재한다. 만약 connection을 얻는 방법이 변경되면 어플리케이션 로직 내의 코드가 변경되게 된다. 따라서 커넥션 획득 방법을 추상화할 필요가 있다. (javax.sql.DataSource를 통해서 커넥션 획득 방법을 추상화하고 있다. 이 인터페이스의 핵심 기능은 커넥션 조회 하나이다.) 스프링에서는 DataSource 추상화 방법을 따른 DriverManager로 DriverManagerDataSource를 제공하고 있다. 

 

일반적인 DriverManager를 이용해서 커넥션을 호출하는 것과 DataSource를 이용해서 호출하는 것에는 큰 차이가 있다. (방법은 동일하다 -> 커넥션을 매번 새로 생성하는 방식이지만.) DriverManager을 사용하면 매번 커넥션을 획득할 때 마다 파라미터로 URL, Username, password를 넘겨줘야 한다. 반면에 DataSource를 사용하면 객체를 생성할 때만 해당 정보들을 넘겨주면 되고 커넥션을 획득하는 경우는 dataSource.getConnection()를 이용해서 호출하면 된다. 이러한 경우를 바람직하게 설정과 사용이 분리된 경우로 본다. 

 

HikariCP 커넥션 풀

hikari는 별도의 커넥션 풀 전용 쓰레드에서 커넥션 풀에 10개의 커넥션을 채운다. 커넥션을 생성하는 일은 외부 TCP/IP와의 연결이 필요한 일이므로 시간이 오래 소요된다. 따라서 어플리케이션 실행시간에 영향이 없도록 하려고 별도 쓰레드를 이용하는 것이다. 만약 커넥션 풀이 안 찼는데 커넥션을 요청한다면 내부적으로 잠깐 기다렸다가 커넥션을 얻은 후에 수행한다. 만약 요청한 connection 수가 connection pool 내의 conn 수 보다 많다면 요청은 waiting queue로 가게 된다. 풀에 빈 커넥션이 생길 때까지 block 되는 것이다. 이런 상황을 대비하기 위해서 풀이 다 찬 경우 얼마동안 기다릴지 미리 설정이 필요하다. (그리고 되도록 이 대기 시간을 짧게 설정하는 것이 좋다. 사용자는 이 시간을 길게 기다리지 못하니까..)

댓글