자바 백엔드 입문 41편. Phase 6 Data Access 시작. JDBC가 뭐고 DataSource가 왜 필요하고 커넥션 풀(HikariCP)이 어떻게 성능을 살리는지 수도꼭지 비유로 풀어쓴 학습 노트.
이 글은 자바 백엔드 입문 시리즈 59편 중 41편이에요. Phase 5 Validation 2편이 끝났고, 이번 41편부터 Phase 6 Data Access 3편으로 들어갑니다. 자바 백엔드가 데이터베이스에 어떻게 연결하는지 — 가장 밑바닥인 JDBC와 DataSource 부터.
JDBC·DataSource가 헷갈리는 이유
처음 자바와 DB 연결을 배우면 JDBC·DriverManager·Connection·DataSource·커넥션 풀·HikariCP 같은 단어가 한꺼번에 쏟아져요. 무엇이 무엇을 감싸고 누가 누구를 호출하는지 안 잡혀요.
이 글에서는 수도꼭지 비유로 풀어요. DB = 저수지, JDBC 드라이버 = 정수장, Connection = 수도 파이프 한 개, DataSource = 집의 수도 시스템, 커넥션 풀 = "파이프 여러 개 미리 깔아두기". 끝까지 따라오시면 자바 백엔드가 DB에 연결되는 전체 그림이 한 번에 들어와요.
JDBC — 자바 표준 DB 연결 규격
JDBC(Java Database Connectivity) 는 자바가 "어떤 DB든 같은 방식으로 다루자" 라는 표준 API 규격이에요. 1997년 자바 1.1부터 들어 있어요.
핵심 발상 — 자바 코드는 "JDBC 인터페이스" 만 알면 되고, 실제 PostgreSQL·MySQL·Oracle 같은 DB와의 통신은 JDBC 드라이버가 처리해요.
[자바 코드]
↓ JDBC 인터페이스 (java.sql.*)
[JDBC 드라이버] ← DB마다 별도 jar (postgresql-jdbc.jar 등)
↓ DB 고유 프로토콜
[데이터베이스]
비유로 풀면 — JDBC = "국제 표준 콘센트", JDBC 드라이버 = "국가별 어댑터". 자바 코드는 "표준 콘센트에만 꽂으면 됨" 이고, 어댑터(드라이버)가 실제 국가별 콘센트(DB)에 맞춰 변환.
DB 종류별 드라이버
자주 만나는 드라이버들.
| DB | 드라이버 좌표 |
|---|---|
| PostgreSQL | org.postgresql:postgresql |
| MySQL | com.mysql:mysql-connector-j |
| Oracle | com.oracle.database.jdbc:ojdbc11 |
| MariaDB | org.mariadb.jdbc:mariadb-java-client |
| H2 (인메모리) | com.h2database:h2 |
| SQLite | org.xerial:sqlite-jdbc |
build.gradle 에 드라이버 의존성 박으면 — 자동으로 클래스 패스에 등록되어 "이 DB로 연결 가능" 상태가 돼요.
JDBC 원시 사용 코드 — 왜 직접 안 쓰나
JDBC를 직접 쓰는 코드는 정말 더러워요. 한 번 보자.
public Order findById(Long id) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = DriverManager.getConnection(url, user, password);
ps = conn.prepareStatement("SELECT id, amount FROM orders WHERE id = ?");
ps.setLong(1, id);
rs = ps.executeQuery();
if (rs.next()) {
return new Order(rs.getLong("id"), rs.getInt("amount"));
}
return null;
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
if (rs != null) try { rs.close(); } catch (Exception ignore) {}
if (ps != null) try { ps.close(); } catch (Exception ignore) {}
if (conn != null) try { conn.close(); } catch (Exception ignore) {}
}
}
한 SELECT 한 줄 짜는 데 20줄의 보일러플레이트 + try-catch-finally + 자원 정리. 비즈니스 로직은 "SELECT 한 줄" 뿐인데. 이게 1990년대 자바 코드의 실제 모습이었어요.
해결책 — JdbcTemplate(27편) 또는 JPA(29~32편) 같은 추상화 레이어. 28편에서 다룰 @Transactional 도 같이.
DataSource — 커넥션 관리 추상화
DriverManager.getConnection() 을 직접 호출하는 위 코드의 가장 큰 문제 — 매 요청마다 새 커넥션을 만든다. DB 커넥션은 "여는 데 100~500ms 걸리는 무거운 자원" 이라 — 매 요청 500ms를 커넥션 만드는 데 쓰면 백엔드가 굴러갈 수 없어요.
해결책이 DataSource. "이미 만들어둔 커넥션을 빌려주고, 끝나면 반납받는" 추상화. 커넥션 풀(Connection Pool) 이 핵심.
// DriverManager — 직접 커넥션 생성 (느림)
Connection conn = DriverManager.getConnection(url, user, password);
// DataSource — 풀에서 빌려옴 (빠름)
DataSource dataSource = ...;
Connection conn = dataSource.getConnection(); // 풀에서 즉시 반환
// ... 사용
conn.close(); // 실제 닫지 않고 풀에 반납
비유 — DriverManager = "매번 수도 공사를 새로 한다", DataSource 풀 = "수도 파이프 10개를 미리 깔아둔다". 누가 물 쓰겠다고 하면 즉시 한 파이프 빌려주고, 다 쓰면 반납.
HikariCP — Spring Boot의 기본 커넥션 풀
자바 진영의 커넥션 풀 라이브러리는 여러 개 — Apache DBCP·c3p0·Tomcat JDBC Pool·HikariCP. 현대 표준은 HikariCP.
- 빠름 — 다른 풀 대비 2~10배 처리량
- 가벼움 — 130KB 짜리 단일 jar
- 검증된 안정성 — 대규모 운영 환경에서 사실상 표준
Spring Boot 2.0부터 HikariCP가 기본 커넥션 풀이에요. 별도 설정 없이 자동 사용.
Spring Boot의 application.yml 설정
Spring Boot에서 DB 연결 설정은 application.yml 한 파일에 다 박혀요.
spring:
datasource:
url: jdbc:postgresql://localhost:5432/myshop
username: postgres
password: secret
driver-class-name: org.postgresql.Driver
# HikariCP 설정 (모두 선택, 기본값 합리적)
hikari:
maximum-pool-size: 10 # 최대 커넥션 개수
minimum-idle: 5 # 최소 유휴 커넥션
connection-timeout: 30000 # 커넥션 대기 타임아웃 (ms)
idle-timeout: 600000 # 유휴 커넥션 정리 시간
max-lifetime: 1800000 # 커넥션 최대 수명
JDBC URL 포맷:
| DB | URL 패턴 |
|---|---|
| PostgreSQL | jdbc:postgresql://host:5432/dbname |
| MySQL | jdbc:mysql://host:3306/dbname |
| Oracle | jdbc:oracle:thin:@host:1521:sid |
| H2 인메모리 | jdbc:h2:mem:testdb |
application.yml 박으면 Spring Boot가 자동으로 — DataSource Bean 생성 + HikariCP 풀 초기화 + JdbcTemplate Bean 등록. 우리는 그저 @Autowired private DataSource dataSource 로 받아 쓰면 끝.
DataSource Bean 직접 만들기 (Java Config)
application.yml 대신 Java Config로 박을 수도 있어요. 두 DB를 동시에 쓰는 시나리오 등.
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties("spring.datasource.primary")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties("spring.datasource.secondary")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
}
대부분의 시스템은 DB 하나만 쓰니까 — application.yml 자동 설정으로 충분. Java Config는 "한 앱이 여러 DB 연결" 같은 특수 케이스에만.
커넥션 풀 크기 — 얼마가 적절한가
자주 받는 질문 — "maximum-pool-size 는 얼마로 박아야 하나?". 답은 CPU 코어 수 × 2 ~ 4 가 권장 시작점.
이유 — 한국 회사 백엔드는 보통 DB 작업이 "몇 ms 안에 끝나는 짧은 쿼리들" 이라, 커넥션 100개를 박아도 동시에 다 활성화되지 않아요. "DB가 동시에 처리 가능한 쿼리 수" 가 한계라 — 커넥션을 너무 많이 박으면 오히려 DB가 컨텍스트 스위칭으로 느려져요.
HikariCP 공식 가이드도 "커넥션 수보다 처리량 가속이 더 빠른 답" 이라고 명시. 10개로 시작해서 모니터링하면서 조정.
JDBC를 직접 쓸 때 가장 위험한 함정 — 커넥션 누수. conn.close() 를 빠뜨리면 풀에 반납되지 않아 결국 모든 커넥션이 묶여 — DB가 응답 안 함. JdbcTemplate·JPA를 쓰면 자동 관리되니까 직접 JDBC 쓸 일은 거의 없어요.
DB·드라이버 호환성 점검
DB 버전과 드라이버 버전 호환성도 중요해요. 보통 한 번 박으면 변경 안 하니까 신경 안 써도 되지만.
- PostgreSQL 16 +
org.postgresql:postgresql:42.7.x안전 - MySQL 8.x +
com.mysql:mysql-connector-j:8.x.x - Spring Boot가 자동으로 호환되는 드라이버 버전을 관리해줘서 —
start.spring.io에서 드라이버 추가만 하면 충돌 거의 없음
26편 한 줄 정리 — 다음 27편으로
한 줄 정리 — JDBC = 자바 표준 DB API. DataSource = 커넥션 관리 추상화. HikariCP = Spring Boot 기본 커넥션 풀. application.yml 한 번 박으면 Spring Boot가 자동으로 DataSource + 커넥션 풀 + JdbcTemplate 다 준비.
시험 직전 한 번 더 — JDBC·DataSource 입문자가 매번 헷갈리는 것
- JDBC = Java Database Connectivity. 자바 표준 DB API (1997)
- "국제 표준 콘센트" 같은 추상화. 자바 코드는 인터페이스만 알면 됨
- JDBC 드라이버 = DB별 어댑터 (postgresql-jdbc·mysql-connector 등)
- 드라이버 의존성 =
build.gradle에 박으면 자동 등록 - DriverManager = JDBC 1.0의 커넥션 팩토리 (지금은 거의 안 씀)
- 직접 JDBC = 20줄 보일러플레이트 + try-catch-finally
- DataSource = 커넥션 관리 추상화 (DriverManager 후속)
- 핵심 = 커넥션 풀(Connection Pool) — 미리 만들어둔 커넥션 빌려주기
- DB 커넥션 생성 = 100~500ms (무거운 자원)
- 풀에서 빌려오기 = 1ms 이내 (빠름)
- 자바 진영 커넥션 풀 = HikariCP·DBCP·c3p0·Tomcat JDBC
- HikariCP = Spring Boot 2.0+ 기본. 빠르고 가볍고 안정
- Spring Boot =
application.yml박으면 자동 DataSource + HikariCP + JdbcTemplate - JDBC URL =
jdbc:postgresql://host:5432/dbname형식 maximum-pool-size= CPU 코어 × 2~4 권장 시작점- 너무 많은 커넥션 = DB가 컨텍스트 스위칭으로 느려짐
- 커넥션 누수 =
close()빠뜨리면 풀 고갈 - JdbcTemplate·JPA 쓰면 자동 관리 — 직접 JDBC 쓸 일 거의 없음
- 두 개 이상 DB = Java Config로 DataSource Bean 명시
- 한 DB만 =
application.yml자동 설정 충분
시리즈 다른 편 (앞뒤 글 모음)
이전 글:
- 36편 — Logback SLF4J 로깅
- 37편 — Spring Security 기초
- 38편 — Spring ApplicationEvent @EventListener
- 39편 — Spring @Async CompletableFuture 비동기
- 40편 — Spring WebClient RestClient HTTP 클라이언트
다음 글: