들어가며

  • 저번 포스트에서 커넥션 풀에 대해 알아보았다.
  • 이번 포스트에는 해당 커넥션 풀을 사용하는데 있어 사용하는 추상화된 인터페이스인 DataSource 인터페이스에 대해 알아본다.

 

 

 

DataSource

  • 지금까지 우리가 구현한 예제에서 커넥션을 얻을 때는 DriverManager.getConnection()을 사용하였다. 그런데 이제 DriverManager를 통해 계속 커넥션을 새로 생성하는게 아니라 커넥션 풀을 사용하여 커넥션을 얻으려고 한다.

  • 문제는 각 커넥션 풀을 사용하는 방법들이 모두 다를 수 있단 것이다. 즉 구체적인 방법에 애플리케이션이 의존할 수 있다.
    • 만약 커넥션 풀 1을 사용하는데 커넥션을 가져오는 코드가 getConnection()이라고 하자. 그런데 커넥션 풀 2로 바꾸어야 하는 상황인데 커넥션 풀 2의 커넥션을 가져오는 코드가 get() 이라면? 결국 모든 코드를 변경해야 하는 상황이 올 것이다.
    • 결국 어떤 커넥션 풀을 사용하는지에 따라 코드에 많은 변경이 가해지는 것이고 이는 결코 좋지 않은 신호이다.
  • 이러한 문제를 해결하기 위해서 DataSource라는 인터페이스가 등장했다.
  • DataSource는 커넥션을 얻는 방법을 추상화 하는 것으로 각 커넥션 풀 클래스들이 해당 인터페이스를 구현함으로서 커넥션을 가져오는 방식을 통일할 수 있다. 만약 커넥션 풀을 바꾸어야 하는 상황이 오더라도 구현 클래스만 갈아끼우면 된다.
    • 문제점이 하나 더 있는데 DriverManager 클래스를 보면 DataSource 를 구현하고 있지 않다. 따라서 DriverManager 클래스를 사용할 시 코드가 변경될 수 있다.
    • 이를 해결하기 위해 DataSource를 구현한 DriverManager인 DriverManagerDataSource 클래스를 제공하고 있다.

 

 

 

DataSource를 통한 커넥션 풀 얻기
DriverManagerDataSource

  • DriverManagerDataSource를 사용한 커넥션 풀 얻기를 한번 구현해 보자.
@Slf4j
public class UseConnectionPool{

    void dataSourceDriverManager() throws SQLException{
        DriverManagerDataSource dataSource = new DriverManagerDataSource(URL, USERNAME, PASSWORD);
        
        useDataSource(dataSource);
    }
    
    private void useDataSource(DataSource dataSource) throws SQLException{
        Connection conn1 = dataSource.getConnection();
        Connection conn2 = dataSource.getConnection();
        
        log.info("connection = {}, class = {}", conn1, conn1.getClass());
        log.info("connection = {}, class = {}", conn2, conn2.getClass());
    }
}
  • DriverManager에서는 항상 getConnection에서 URL, USERNAME, PASSWORD를 넣어주어야 했다. DataSourceDriverManager에서는 생성할 때 단 한번 사용되며 그 이후에는 사용하지 않아도 된다.

 

HikariCP

  • HikariCP를 사용하여 커넥션을 얻어보도록 하자.
@Slf4j
public class UseDataSourceConnection{
    private final HikariDataSource dataSource;

    UseDataSourceConnection(){
        dataSource = new HikariDataSource();
        
        dataSource.setJdbcUrl(URL);
        dataSource.setUsername(USERNAME);
        dataSource.setPassword(PASSWORD);
        dataSource.setMaximumPoolSize(10);
        dataSource.setPoolName("My Pool");
        
        Thread.sleep(1000);
    }
    
    public void useDataSource() throws SQLException{
        Connection conn1 = dataSource.getConnection();
        Connection conn2 = dataSource.getConnection();
        
        log.info("connection = {}, class = {}", conn1, conn1.getClass());
        log.info("connection = {}, class = {}", conn2, conn2.getClass());
    }
}
  • 설정할 것이 꽤 있는데 Url, Username, Password는 연결할 DB의 주소, ID, PW를 통해 인증을 수행해야 해서 그렇다. setMaximumPoolSize는 커넥션 풀의 최대 커넥션 개수이다.
  • Thread를 sleep하여 잠시 대기하도록 한 것은 커넥션 풀의 초기화 과정에서 다른 스레드를 사용하기 때문이다.
    • 커넥션 풀은 애플리케이션이 실행될 때 초기화되는데 만약 커넥션 풀의 용량이 매우 큼으로 인해 커넥션 풀 최화에 시간이 많이 들어가는 경우를 고려하여 별개의 스레드를 할당하여 수행하도록 하고 있다.
    • 즉 애플리케이션이 초기화되는 스레드와 커넥션 풀의 초기화를 담당하는 스레드는 별개의 스레드이다. 그런데 지금 로직은 어플리케이션의 초기화 속도가 너무 빠르기 때문에 커넥션 풀이 다 초기화되지 않은 상태에서 호출하는 상황이 일어날 수 있으므로 일부러 어플리케이션의 초기화 속도를 늦춘 것이다.
  • 로그를 통해 현재 사용되고 있는 커넥션이 몇 개고, 여유분이 몇 개인지도 확인이 가능하다.

 

 

 

정리

  • 지금까지 커넥션 풀의 개념과 DataSource를 사용하여 커넥션 풀을 자바에서 어떻게 사용하는지를 알아보았다.
  • 다음 포스트에서는 이전에 만들었던 레포지토리를 DataSource를 사용하여 커넥션 풀을 사용하도록 구현해본다.
복사했습니다!