스프링과 데이터베이스[1] - JDBC와 커넥션 풀
들어가기에 앞서
개발자라면 데이터베이스는 절대 피해갈 수 없는 반드시 겪어야 하는 과정일 것이다. 일반적으로 개발을 할 때 애플리케이션과 데이터베이스가 어떻게 연결되고 데이터베이스와의 연결을 어떻게 관리하며 데이터베이스가 필요한 요청이 들어왔을 때 어떻게 처리되는지를 제대로 이해하고 사용하는 것이 좋을 것이다. 사실 지금은 스프링 부트를 사용하면 아주 간단한 조작만으로도 데이터베이스와의 연결이 가능하고 요청을 처리할 수 있다. 그렇기 때문에 의식적으로 어떤 방식으로 동작하는지를 이해하고 사용하는 것이 더욱 중요하다고 할 수 있다.
JDBC
데이터베이스는 종류가 굉장히 다양하다. 가장 많이 사용하는 RDBS만 보더라도 MySQL, Oracle, MSSQL 등 이미 유명한 데이터베이스들이 잔뜩 있다. 문제는 이 모든 데이터베이스마다 드라이버의 종류가 다르고 연결하는 방식이 다르다는 것이다. 매번 특정 데이터베이스마다 다른 설정들을 해주지 않아도 되도록 모든 드라이버를 통합해서 공통적으로 사용할 수 있도록 만든 것이 JDBC이다.
JDBC는 JavaDataBaseConnectivity의 줄임말로서 자바에서 데이터베이스에 접속할 수 있도록 하는 API이다. JDBC를 이용해 몇 가지 정보만 넣어준 뒤 데이터베이스로 연결을 요청하면 JDBC가 복잡한 작업들을 처리한 뒤 데이터베이스와 연결해서 Connection을 받아오는 방식으로 동작한다.
JDBC에 필요한 정보는 아래와 같다.
- url : 데이터베이스의 주소와 종류를 입력한다.
- username : 데이터베이스에 로그인 하기 위한 아이디를 입력한다.
- password : 데이터베이스에 로그인 하기 위한 패스워드를 입력한다.
- driver-class-name : 드라이버의 종류(데이터베이스의 종류)를 입력한다.
각 정보마다 JDBC에서 요구하는 형식이 있기 때문에 요구하는 형식에 맞게 입력하면 된다.
JDBC가 데이터베이스와의 연결에 필요한 정보를 받아서 연결을 요청하면 데이터베이스는 Connection이라는 것을 반환해준다. 이 Connection이 애플리케이션에서 데이터베이스로 요청하는 수단이다. 쉽게 생각하면 하나의 Connection이 하나의 쿼리를 담당해준다고 생각하면 된다.
데이터베이스가 Connection을 반환하면 데이터베이스 내부에는 세션을 생성한다. 하나의 Connection에 하나의 세션이 지정된다. Connection이 종료되면 세션도 종료되고 세션이 종료되면 Connection도 종료된다. 만약 애플리케이션에서 동작시킨 메소드가 블락이 걸렸을 경우 하나의 Connection이 묶이게 되버린다. 이 경우에 Connection을 강제로 종료하고 싶다면 데이터베이스에서 세션을 종료시키면 된다.
Connection
가장 기본적으로는 로직이 동작하다가 데이터베이스의 작업이 필요하면 그때 마다 Connection을 요청하여 작업을 진행한다. 슈도 코드로 예시를 살펴보자.
fun 우리반에 학생이 몇 명있는지 알고 싶다() {
val connection = DriverManager.getConnection(url, username, password)
val query = 조회쿼리
connection을 이용해서 데이터베이스에 쿼리 전송
결과 값 저장 및 조회
}
위의 코드와 같이 진행될 것이다. 조회를 위해 데이터베이스의 데이터를 가져오기 위해서 Connection을 요청하고 반환받은 Connection을 이용해서 데이터를 조회하는 코드이다.
이때 Connection을 가져오는 객체로 DriverManager를 사용하고 있다. DriverManager는 위에서 설명했던 url, username, password를 입력하면 하나의 커넥션을 반환해주는 객체이다.
가장 기본적으로는 이렇게 사용하지만 실제로 실무에서는 이렇게 사용하지 않는다. 매번 Connection이 필요할 때마다 요청하게 되면 사용자 입장에서는 데이터를 가져오고 가공하는 것 뿐만 아니라 Connection을 요청하는 시간까지도 기다려야하고 개발자 입장에서도 매번 데이터베이스로 요청을 보내야 하기 때문에 리소스가 많이 낭비된다. 그래서 이러한 문제점을 극복하기 위해서 커넥션 풀이라는 것을 사용하기 시작했다.
Connection Pool
Connection Pool은 이름 그대로 Connection을 담아놓은 공간이다. 매번 데이터베이스에 Connection을 요청하지 않고 처음 애플리케이션이 동작할 때 특정 개수의 Connection을 데이터베이스에서 한 번에 받아놓고 Connection Pool에 저장해서 사용하는 것이다. 이렇게하면 Connection이 필요할 때 데이터베이스에 요청하는 것이 아니라 애플리케이션이 들고있는 Connection Pool에서 Connection을 받아서 사용하고 사용이 끝나면 다시 반환하는 방식으로 Connection을 관리하는 것이다.
Connection은 하나가 생성될 때마다 데이터베이스에서 세션이 생성된다고 했으므로 Connection의 개수만큼 데이터베이스에서는 세션을 유지하고 있다.
앞에서 살펴본 DriverManager는 필요할 때마다 요청하여 Connection을 가져오는 방식이었다. Connection Pool을 관리하기 위해서는 직접 구현하여 사용하는 방법도 있겠지만 지금은 성능적으로나 여러면으로 최적화된 라이브러리들이 많기 때문에 라이브러리를 사용하는 것이 좋다. 이전에는 정말 많은 라이브러리들이 있었지만 이제는 거의 통합되어 Hikari CP를 주로 사용하고 있다.
JDBC가 여러 데이터베이스 드라이버들의 연결을 추상화 시킨 API인 것 처럼 여러 커넥션을 관리할 수 있도록 추상화시킨 것이 있는데 바로 DataSource이다. DataSource를 이용하면 Connection을 받아야하는 객체에서 사용하는 라이브러리가 바뀔 때마다 의존성을 바꿔주지 않고 DataSource를 의존성 주입받아서 사용할 수 있다.
가장 대표적인 Hikari CP를 사용하는 방법은 아래와 같다.
val dataSource: HikariDataSource = HikariDataSource()
dataSource.jdbcUrl = URL
dataSource.username = USERNAME
dataSource.password = PASSWORD
dataSource.maximumPoolSize = 10
dataSource.poolName = "MyPool"
val con = dataSource.connection
HikariDataSource타입으로 객체를 생성하고 필요한 데이터를 넣어주면 된다. 이때 커넥션 풀의 최대 개수와 풀 이름도 지정할 수 있다. 커넥션 풀의 최대 개수를 잘 조절해야 애플리케이션을 효과적으로 사용할 수 있으므로 현명하게 지정해야 한다. 기본값은 10이다.
이렇게 지정한 뒤 getConnection을 이용해 Connection을 가져오면 데이터베이스에 요청해서 가져오는 것이 아니라 Connection Pool에 있는 Connection을 가져와서 사용하게 된다. 물론 사용이 끝나면 다시 Connection Pool에 반납해서 계속해서 재사용 할 수 있다.
'Spring > Core' 카테고리의 다른 글
@Transactional 어노테이션 알아보기[1] (0) | 2023.02.18 |
---|---|
스프링과 데이터베이스[2] - 트랜잭션 사용 (0) | 2022.11.09 |
스프링 주입의 다양한 방법들 (0) | 2022.06.13 |
스프링 빈 등록의 다양한 방법들 (0) | 2022.06.09 |
[3/3] Spring과 싱글톤 패턴 - @Configuration의 비밀 (0) | 2022.06.07 |
댓글
이 글 공유하기
다른 글
-
@Transactional 어노테이션 알아보기[1]
@Transactional 어노테이션 알아보기[1]
2023.02.18 -
스프링과 데이터베이스[2] - 트랜잭션 사용
스프링과 데이터베이스[2] - 트랜잭션 사용
2022.11.09 -
스프링 주입의 다양한 방법들
스프링 주입의 다양한 방법들
2022.06.13 -
스프링 빈 등록의 다양한 방법들
스프링 빈 등록의 다양한 방법들
2022.06.09