커넥션 풀
데이터 베이스 커넥션 획득 과정
- 애플리케이션 로직은 DB 드라이버를 통해 커넥션 조회
- DB 드라이버는 DB와 TCP/IP 커넥션 연결(3 way handshake)
- DB 드라이버는 TCP/IP 커넥션이 연결되면, ID, PW와 기타 부가정보를 DB에 전달.
- DB는 ID, PW를 통해 내부 인증을 완료하고, 내부에 DB 세션을 생성
- DB는 커넥션 생성이 완료 되었다는 응답을 보냄
- DB 드라이버는 커넥션 객체를 생성해서 클라이언트에 반환
이러한 커넥션 획득 과정은 복잡하고 시간도 많이 소모된다. 그럼 결과적으로 고객이 애플리케이션을 사용할 때 응답 속도에 영향을 줌.
우리가 전에 DriverManager를 사용해서 커넥션을 생성할 때 이런 식으로 일일이 생성해줬다.
그래서 이러한 문제를 해결하기 위해 커넥션 풀이란 것을 사용한다!
커넥션 풀은 커넥션을 관리하는 풀인데 애플리케이션을 시작하는 시점에 커넥션 풀은 필요한 만큼 커넥션을 미리 확보해서 풀에 보관한다.
커넥션 풀에 들어 있는 커넥션은 TCP/IP로 DB와 커넥션이 연결되어 있는 상태이기 때문에 언제든지 즉시 SQL을 DB에 전달할 수 있다.
커넥션 풀 사용
- 애플리케이션 로직에서 이제 DB 드라이버를 통해서 새로운 커넥션을 획득하는 것이 아닌 커넥션 풀을 통해 이미 생성 되어 있는 커넥션을 객체 참조로 그냥 가져다 쓰기만 하면 된다.
- 커넥션 풀에 커넥션을 요청하면 커넥션 풀은 자신이 가지고 있는 커넥션 중 하나를 반환한다.
- 커넥션을 모두 사용하고 나면 이제는 커넥션을 종료하는 것이 아니라 커넥션 풀에 반환한다.(커넥션 풀에 있는 커넥션들은 항상 살아있음)
데이터소스
만약 우리가 전에 DriveManager를 통해서 커넥션을 획득하고 CRUD를 했지만 커넥션 풀을 사용하려면 어떻게 해야될까?
아마 그럼 우리는 DriveManager를 통해 커넥션을 획득하는 애플리케이션 코드들도 함께 변경해야될 것이다.
만약 이게 단순한 CRUD가 아닌 몇백 몇천줄이면..? 언제 다고칠까??
그래서 자바는 이런 문제를 해결하기 위해 DataSource라는 인터페이스를 제공한다.

DataSource는 커넥션을 획득하는 방법을 추상화 했다.
그래서 대부분의 커넥션 풀은 DataSource 인터페이스를 이미 구현해두었다. 따라서 우리들은 구현된 커넥션 풀에 의존하는 것이 아닌 DataSource 인터페이스에만 의존하도록 애플리케이션 로직을 작성하면 된다.
근데 안타깝게도 우리가 저번에 만든 DriverManager는 DataSource를 사용하지 않는다. 그래서 코드를 다 바꿔줘야한다.
하지만 Java에서는 DriveManagerDataSource라는 DataSource 인터페이스를 구현한 클래스를 제공한다.
(어쨋든 DriveManager는 바꿔야함..)
그럼 DataSource 인터페이스를 통해 커넥션을 획득하는 방법
// 기존 DriveManager를 통한 커넥션 획득법
DriverManager.getConnection(URL, USERNAME, PASSWORD);
DriverManager.getConnection(URL, USERNAME, PASSWORD);
// 새로운 DriveManagerDataSource를 통한 커넥션 획득법
void dataSourceDriveManager() throws SQLException {
DriverManagerDataSource datasource = new DriverManagerDataSource(URL, USERNAME, PASSWORD);
useDataSource(datasource);
}
private void useDataSource(DataSource dataSource) throws SQLException {
Connection con1 = dataSource.getConnection();
Connection con2 = dataSource.getConnection();
}
DriverManager는 커넥션을 획득할 때마다 URL, USERNAME, PASSWORD 같은 파라미터를 계속 전달해야한다.
하지만 DataSource를 사용하는 방식은 처음 객체를 생성할 때만 필요한 파라미터를 넘겨두고, 커넥션을 획득할 때는 단순히
dataSource.getConnection()만 호출하면 된다.
이러한 설정을 만드는 부분과 datasource를 사용하는 부분을 분리해서 귀찮아지는 부분이 많이 해결됐다.
그럼 우리가 전에 했던 JDBC DriveManager를 이용한 CRUD 코드를 변경해보자
public class MemberRepositoryV1 {
private final DataSource dataSource;
public MemberRepositoryV1(DataSource dataSource) {
this.dataSource = dataSource;
}
public Member save(Member member) {
String sql = "insert into member(member_id, money) values(?, ?)";
Connection con = null;
PreparedStatement pstmt = null;
try {
con = getConnection();
pstmt = con.prepareStatement(sql);
pstmt = setString(1, member.getMemberId());
pstmt = setInt(2, member.getMoney());
pstmt = executeUpdate();
return member;
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
close(con, pstmt, null);
}
}
// RUD 생략
private void close(Connection con, Statement stmt, ResultSet rs) {
JdbcUtils.closeResultSet(rs);
JdbcUtils.closeStatement(stmt);
JdbcUtils.closeConnection(con);
}
private Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
- 이제 외부에서 DataSource 의존관계 주입을 받아서 직접 만든 DBConnectionUtil을 사용하지 않아도된다.
DataSource 인터페이스는 표준 인터페이스이기 때문에 DriverManagerDataSource에서 HikariDataSource로 변경되어도 해당 코드를 변경하지 않아도 된다. - JdbcUtils 편의 메서드를 사용하여 자원정리를 좀더 편하게 해주었다
그럼 어케 사용되는지는 알아야겠쥬?
MemberRepositoryV1 repository;
@Test
void beforEach() throws Exception {
//기본 DriverManager - 항상 새로운 커넥션 획득
//DriverManagerDataSource datasource =
// new DriverManagerDataSource(URL, USERNAME, PASSWORD);
//커넥션 풀
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl(URL);
dataSource.setUsername(USERNAME);
dataSource.setPassword(PASSWORD);
repository = new MemberRepository(dataSource);
}
- 일단 제일 유심히 볼 것은 DriverManagerDataSource를 쓰던 HikariDataSource를 쓰던 DataSource 인터페이스의 구현체들이니 우리는 DataSource인터페이스에 의존해서 그냥 저것만 교체해주면 된다!! 편하다 그춍?
요약
- DriverManager : 매번 새 커넥션을 만듦 -> 느림
- 커넥션 풀 : 미리 커넥션을 만들어 두고 재사용 -> 빠름
- DataSource : 커넥션 관리 방식을 표준화 -> 코드 변경 없이 구현체 교체 가능
'Back-End > DataBase' 카테고리의 다른 글
| SQL로 요리하듯 데이터 다루기 (0) | 2025.01.15 |
|---|---|
| Spring JdbcTemplate의 RowMapper() (1) | 2024.12.09 |
| JDBC - CRUD (0) | 2024.12.06 |
| SQL 기초 3 (Join) (1) | 2024.11.08 |
| SQL 기초 2 (2) | 2024.11.07 |