F-LAB JAVA · 6주차 · Phase 5 · DataSource 인터페이스 (추상화의 진화)
🔌 Phase 5 시작 — 추상화로 가는 길
이 Unit을 끝내면 다음을 답할 수 있어야 한다.
커넥션을 얻는 방법은 DriverManager·HikariCP·DBCP2·Tomcat JDBC Pool 등 다양한데 각자 API 사용법이 달라서 획득 방법이 바뀌면 애플리케이션 코드가 모두 바뀌어야 하며, 이는 5주차의 ConnectionMaker 인터페이스 도입과 똑같은 OCP 위반 문제다.
Connection 을 얻는 방법은 다양하다 — (1)DriverManager.getConnection()(매번 새로, 풀 X), (2)HikariCP(커넥션 풀), (3)DBCP2(Apache 커넥션 풀), (4)Tomcat JDBC Pool(Tomcat 내장 풀).
문제는 각자 사용법이 다르다 는 것 —DriverManager.getConnection(url, ...)와new HikariDataSource()+ 설정 +getConnection()은 코드가 전혀 다르다.
그래서 커넥션 획득 방법이 바뀌면 (예: DriverManager → HikariCP) 애플리케이션의 모든 DB 접근 코드 가 바뀌어야 한다.
이는 5주차의ConnectionMaker인터페이스 도입 직전과 똑같은 문제 — 구체에 강결합되어 변경 압력이 전파되는 OCP 위반 이며, 해결은 동일하다: 인터페이스로 추상화 (다음 Unit).
다양한 커넥션 획득 = 가전마다 다른 충전기:
DriverManager:
- 가전 A 전용 충전기
- DriverManager.getConnection(url, ...)
HikariCP:
- 가전 B 전용 충전기
- new HikariDataSource(); .setJdbcUrl(); .getConnection();
- 다른 모양
DBCP2:
- 가전 C 전용 충전기
Tomcat JDBC Pool:
- 가전 D 전용 충전기
문제:
- 충전기 (커넥션 방식) 바꾸면
- 가전 (애플리케이션) 의 모든 부품 (코드) 교체
- 비효율
5주차 ConnectionMaker:
- 표준 USB 정신
- "어떤 충전기든 같은 포트"
해결 (다음 Unit):
- DataSource (만능 어댑터)
- 표준 인터페이스
→ 획득 방식 다양 + 사용법 제각각 → 변경 시 전체 영향 → 5주차 OCP 문제와 동일.
1. 다양한 획득 방식
2. DriverManager 사용
3. HikariCP 사용
4. DBCP2 / Tomcat 사용
5. 사용법이 모두 다름
6. 변경의 압력 전파
7. 5주차 ConnectionMaker와 닮음
8. OCP 위반
9. 면접 + 자기 점검
커넥션 획득 방식:
1. DriverManager
- 매번 새 연결 (풀 X)
- JDBC 기본
2. HikariCP
- Spring Boot 기본
- 가장 빠른 풀
3. DBCP2 (Apache)
- 전통 풀
4. Tomcat JDBC Pool
- Tomcat 내장 풀
각자 특성:
DriverManager:
- 풀 없음, 직접 연결
- 학습/테스트용
Pool 들 (HikariCP/DBCP2/Tomcat):
- 미리 N개 (Phase 4)
- 재사용
- 운영 환경
선택 기준:
- 운영 환경: 풀 사용
- 단순/테스트: DriverManager
- 성능: HikariCP
- 레거시: DBCP2
- Tomcat 통합: Tomcat JDBC Pool
획득 방식 (ILIC)
ILIC 환경:
- 운영: HikariCP (Spring Boot 기본)
- 테스트: H2 + DriverManager 도 가능
- 레거시 마이그레이션 시 DBCP2
애플리케이션 코드:
- 어떤 방식이든 결국 Connection 객체 필요
- 하지만 획득 방법은 다름
→ 추상화 필요 (다음 Unit)
커넥션 획득 방식이 다양한 이유는?
답:
1. 4가지:
특성:
선택:
공통:
// DriverManager 사용
public Connection getConnection() throws SQLException {
return DriverManager.getConnection(
"jdbc:mysql://localhost:3306/ilic",
"ilic_user",
"password"
);
}
// 매 호출마다 새 연결
DriverManager 특징:
- 풀 X (매번 새 연결)
- 단순 (JDBC 표준)
- 학습/테스트용
- 운영 X (성능 ↓)
// 사용 패턴
Connection c = DriverManager.getConnection(url, user, pwd);
try {
// 사용
} finally {
c.close(); // 진짜 close (TCP 종료)
}
// 다음 호출 또 새 연결
DriverManager 단점:
- 매번 연결 비용 (Unit 4.1)
- 자원 관리 직접
- 운영 부적합
→ 풀로 대체
// DriverManager (ILIC 가 풀 없으면)
public class ShipmentDao {
public Shipment get(Long id) throws Exception {
// DriverManager — 매 요청마다 새 연결
Connection c = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/ilic",
"ilic_user",
"password"
);
// ↑ TCP handshake + 인증 + 세션 (매번)
try {
// SQL 실행
} finally {
c.close();
// ↑ TCP 종료 (매번)
}
return null;
}
}
// → ILIC 의 431 API 마다 새 연결 → 성능 폭망
// → 실제는 풀 사용 (다음 섹션들)
class Shipment {}
DriverManager 사용 방식은?
답:
1. 사용:
특징:
단점:
용도:
// HikariCP 직접 사용 (Spring 없이)
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/ilic");
config.setUsername("ilic_user");
config.setPassword("password");
config.setMaximumPoolSize(20);
HikariDataSource ds = new HikariDataSource(config);
// 사용
Connection c = ds.getConnection(); // 풀에서 빌림
// 사용
c.close(); // 풀로 반환
HikariCP vs DriverManager:
DriverManager:
- DriverManager.getConnection(url)
- 매번 새 연결
HikariCP:
- new HikariDataSource(config)
- 풀에서 재사용
→ 코드가 다름!
풀 설정 (HikariCP):
- maximumPoolSize
- minimumIdle
- connectionTimeout
- idleTimeout
→ HikariConfig 객체
# Spring Boot 는 application.yml 만
spring:
datasource:
url: jdbc:mysql://localhost:3306/ilic
username: ilic_user
password: password
hikari:
maximum-pool-size: 20
# Spring 이 HikariDataSource 자동 생성
// HikariCP 직접 (ILIC 가 Spring 안 썼다면)
public class ShipmentDao {
private final HikariDataSource dataSource;
public ShipmentDao() {
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/ilic");
config.setUsername("ilic_user");
config.setPassword("password");
config.setMaximumPoolSize(20);
this.dataSource = new HikariDataSource(config);
}
public Shipment get(Long id) throws Exception {
Connection c = dataSource.getConnection(); // 풀에서
try {
// SQL
} finally {
c.close(); // 풀로 반환
}
return null;
}
}
// ILIC 는 Spring Boot (HikariCP 자동)
// → application.yml 만 설정
class HikariConfig {
void setJdbcUrl(String s) {}
void setUsername(String s) {}
void setPassword(String s) {}
void setMaximumPoolSize(int n) {}
}
class HikariDataSource {
HikariDataSource(HikariConfig c) {}
Connection getConnection() throws java.sql.SQLException { return null; }
}
class Shipment {}
HikariCP 사용 방식은?
답:
1. 사용:
vs DriverManager:
설정:
Spring:
// DBCP2 (Apache)
BasicDataSource ds = new BasicDataSource();
ds.setUrl("jdbc:mysql://localhost:3306/ilic");
ds.setUsername("ilic_user");
ds.setPassword("password");
ds.setMaxTotal(20);
// 사용
Connection c = ds.getConnection();
// Tomcat JDBC Pool
org.apache.tomcat.jdbc.pool.DataSource ds
= new org.apache.tomcat.jdbc.pool.DataSource();
PoolProperties p = new PoolProperties();
p.setUrl("jdbc:mysql://localhost:3306/ilic");
p.setUsername("ilic_user");
p.setPassword("password");
p.setMaxActive(20);
ds.setPoolProperties(p);
// 사용
Connection c = ds.getConnection();
모두 다른 API:
HikariCP:
- HikariConfig + HikariDataSource
- setMaximumPoolSize
DBCP2:
- BasicDataSource
- setMaxTotal
Tomcat:
- DataSource + PoolProperties
- setMaxActive
→ 같은 일, 다른 API
코드 변경 부담:
풀 교체 (HikariCP → DBCP2):
- 설정 클래스 변경
- 메서드 이름 변경
- import 변경
→ 변경 광범위
// 3가지 풀 비교 (ILIC, 같은 일 다른 API)
// HikariCP
HikariConfig hc = new HikariConfig();
hc.setJdbcUrl(url);
hc.setMaximumPoolSize(20);
HikariDataSource hikari = new HikariDataSource(hc);
// DBCP2
BasicDataSource dbcp = new BasicDataSource();
dbcp.setUrl(url);
dbcp.setMaxTotal(20);
// Tomcat JDBC Pool
org.apache.tomcat.jdbc.pool.DataSource tomcat
= new org.apache.tomcat.jdbc.pool.DataSource();
PoolProperties pp = new PoolProperties();
pp.setUrl(url);
pp.setMaxActive(20);
tomcat.setPoolProperties(pp);
// → 풀 바꾸면 코드 전부 다시 (애플리케이션 코드까지)
// → 5주차 OCP 위반
String url;
class HikariConfig { void setJdbcUrl(String s) {} void setMaximumPoolSize(int n) {} }
class HikariDataSource { HikariDataSource(HikariConfig c) {} }
class BasicDataSource { void setUrl(String s) {} void setMaxTotal(int n) {} }
class PoolProperties { void setUrl(String s) {} void setMaxActive(int n) {} }
DBCP2 / Tomcat JDBC Pool 사용 방식은?
답:
1. DBCP2:
Tomcat:
API:
부담:
사용법이 모두 다름:
DriverManager:
DriverManager.getConnection(url, ...)
HikariCP:
new HikariConfig + new HikariDataSource
DBCP2:
new BasicDataSource
Tomcat:
new DataSource + PoolProperties
→ 코드 전부 달라
같은 목적, 다른 코드:
목적: Connection 객체 획득
방식: 모두 다름
- 클래스
- 설정
- 메서드
→ 비효율
학습 부담:
풀 라이브러리마다:
- 새 API 학습
- 다른 설정 방식
- 다른 모니터링
→ 시간 낭비
코드 분산:
애플리케이션 곳곳:
- 풀 의존 코드 산재
- 변경 시 모두 영향
→ 유지보수 ↓
// 사용법 다름 (ILIC)
// ❌ 풀 의존 코드 (변경 광범위)
public class BadShipmentDao {
private final HikariDataSource dataSource; // HikariCP 직접
public BadShipmentDao() {
// HikariCP 전용 설정
HikariConfig config = new HikariConfig();
config.setJdbcUrl("...");
this.dataSource = new HikariDataSource(config);
}
public Connection getConnection() throws Exception {
return dataSource.getConnection();
}
}
// HikariCP → DBCP2 변경 시:
// - HikariConfig → BasicDataSource
// - HikariDataSource → BasicDataSource
// - import 전부 변경
// - 모든 DAO 가 풀에 의존하면 → 모두 변경
// → 추상화 필요 (다음)
class HikariConfig { void setJdbcUrl(String s) {} }
class HikariDataSource {
HikariDataSource(HikariConfig c) {}
Connection getConnection() throws Exception { return null; }
}
사용법이 모두 다르다는 문제는?
답:
1. 사용법 다름:
같은 목적:
학습 부담:
분산:
변경 압력:
외부 변경 (풀 라이브러리):
- 애플리케이션에 전파
- 코드 변경 필요
→ 강결합 신호
전파 경로:
HikariCP → DBCP2 변경:
1. 설정 코드 변경
2. 의존성 변경
3. 사용 코드 변경 (getConnection 호출)
4. 모니터링 코드 변경
→ 전 영역
5주차 OCP:
"확장 열림, 변경 닫힘":
- 풀 변경 = 변경 (위반)
- 코드 전체 영향
→ OCP 위반
해결 원리 (5주차):
인터페이스 추상화:
- 풀에 의존 X
- 인터페이스 의존
- 풀은 외부 주입
→ DataSource (다음 Unit)
// 변경 압력 전파 (ILIC, 추상화 없을 때)
// 100 개의 DAO 가 모두 HikariCP 직접 의존
public class ShipmentDao {
private final HikariDataSource dataSource; // 강결합!
}
public class BookingDao {
private final HikariDataSource dataSource; // 강결합!
}
// ... 100 개
// HikariCP → DBCP2 변경:
// → 100 개 DAO 전부 수정
// → 변경 압력 전파
// 5주차 정신:
// → 인터페이스 추상화 필요
// → DataSource (다음 Unit)
class HikariDataSource {}
변경의 압력이 코드에 전파되는 이유는?
답:
1. 변경 압력:
전파 경로:
OCP:
해결:
5주차 ConnectionMaker:
처음:
- UserDao 가 NConnectionMaker 직접
- 구체에 강결합
해결:
- ConnectionMaker 인터페이스
- 외부 주입
→ OCP 달성
같은 구조:
5주차 UserDao:
- 구체 ConnectionMaker 의존 → 인터페이스 추상화
6주차 ShipmentDao:
- 구체 풀 (HikariCP) 의존 → DataSource 추상화
→ 똑같은 패턴
5주차 ↔ 6주차 매핑:
5주차:
- NConnectionMaker (구체)
- ConnectionMaker (인터페이스)
6주차:
- HikariDataSource (구체)
- DataSource (인터페이스, 다음 Unit)
→ 같은 사상
자바 표준에 녹은 OCP:
자바 (javax.sql):
- DataSource 인터페이스
- JDBC 4.0+
- 5주차 정신을 표준으로
→ OOP 원칙의 실제 사례
// 5주차 ↔ 6주차 닮음 (ILIC)
// 5주차 (자체 추상화)
public interface ConnectionMaker {
Connection makeConnection() throws Exception;
}
public class ShipmentDao5 {
private final ConnectionMaker connectionMaker; // 인터페이스
public ShipmentDao5(ConnectionMaker cm) {
this.connectionMaker = cm;
}
}
// 6주차 (자바 표준 추상화 — DataSource)
public class ShipmentDao6 {
private final DataSource dataSource; // 자바 표준 인터페이스
public ShipmentDao6(DataSource ds) {
this.dataSource = ds;
}
}
// → 5주차 ConnectionMaker = 6주차 DataSource
// → 같은 OCP/DIP 정신
// → 자바가 표준 인터페이스로 정착
interface DataSource {}
5주차 ConnectionMaker 와 닮음은?
답:
1. 5주차:
6주차:
같은 구조:
자바 표준:
OCP (개방-폐쇄 원칙):
- 확장에 열림 (Open)
- 변경에 닫힘 (Closed)
새 기능: 추가
기존 코드: 변경 X
현재 OCP 위반:
풀 변경 (확장 가정):
- HikariCP → DBCP2
- 기존 코드 변경 (닫힘 위반)
→ OCP 위반
해결 = 추상화:
인터페이스 도입:
- 새 풀 추가 (확장 ✅)
- 기존 코드 그대로 (변경 X ✅)
→ OCP 준수
5주차 정신:
"변경에 닫히려면 추상화":
- 인터페이스
- 구체는 외부 주입
- 코드는 인터페이스만
→ 다음 Unit (5.2)
// OCP 위반 vs 준수 (ILIC)
// ❌ OCP 위반 (현재)
public class ShipmentDao {
private final HikariDataSource dataSource; // 구체!
// HikariCP → DBCP2 변경 시 코드 수정 (변경에 열림 = OCP 위반)
}
// ✓ OCP 준수 (다음 Unit, DataSource)
public class ShipmentDaoGood {
private final DataSource dataSource; // 인터페이스!
public ShipmentDaoGood(DataSource ds) {
this.dataSource = ds;
}
// HikariCP → DBCP2 변경 시:
// → 주입만 다르게 (코드 X)
// → OCP 준수
}
class HikariDataSource {}
interface DataSource {}
OCP 위반인 이유는?
답:
1. OCP:
위반:
해결:
5주차:
| Q | 핵심 답변 |
|---|---|
| 획득 방식 종류? | DriverManager/HikariCP/DBCP2/Tomcat |
| DriverManager? | 풀 X, 매번 |
| HikariCP? | 풀, 가장 빠름 |
| 사용법 다른가? | 모두 다름 |
| 변경 압력? | 코드 전파 |
| 5주차 닮음? | ConnectionMaker |
| OCP 위반? | 풀 변경 → 코드 |
| 해결? | 인터페이스 (DataSource) |
| Spring? | HikariCP 자동 |
| 강결합 신호? | 구체 의존 |
답:
답:
답:
답:
답:
1. 획득 방식 다양
2. 변경 압력
3. OCP 위반과 해결
이번 Unit에서 다양한 획득 방식의 문제를 봤다면, 다음은 추상화 필요성 정리.
🔌 Phase 5 — DataSource 인터페이스
✅ Unit 5.1 다양한 커넥션 획득 방식 ← 여기
⏭ Unit 5.2 추상화의 필요성
⏭ Unit 5.3 DataSource 인터페이스 ★깊이
⏭ Unit 5.4 DriverManagerDataSource
🧪 Part A (9 Unit) ✅
💾 Part B — DB 접근의 진화
✅ Phase 3 — JDBC (3)
✅ Phase 4 — Connection Pool (4)
🔌 Phase 5 — DataSource (1/4)
총: 17/28 Unit
🔌 Phase 5 시작 — 추상화로 가는 길