1. JDBC란 무엇인가?
**JDBC(Java Database Connectivity)**는 자바 애플리케이션에서 관계형 데이터베이스에 연결하고 SQL 쿼리를 실행하며, 결과를 처리하기 위한 표준 API입니다. JDBC를 사용하면 다양한 데이터베이스 시스템(MySQL, PostgreSQL, Oracle 등)에 독립적으로 접근할 수 있습니다.
• 데이터베이스 독립성: JDBC는 데이터베이스 벤더에 종속되지 않는 표준 인터페이스를 제공합니다.
• 표준화된 API: 개발자가 일관된 방식으로 데이터베이스 작업을 수행할 수 있습니다.
2. JDBC의 아키텍처
JDBC 아키텍처는 다음과 같은 두 계층으로 구성됩니다.
1. JDBC API: 애플리케이션과 JDBC 드라이버를 연결하는 표준 인터페이스입니다.
2. JDBC 드라이버: 특정 데이터베이스에 대한 구현체로, JDBC API를 통해 전달된 호출을 실제 데이터베이스에 맞게 변환하여 실행합니다.
JDBC 드라이버 유형:
• Type 1: JDBC-ODBC 브리지 드라이버 (현재는 거의 사용되지 않음)
• Type 2: 네이티브 API 드라이버
• Type 3: 네트워크 프로토콜 드라이버
• Type 4: 순수 자바 드라이버 (가장 일반적으로 사용됨)
3. JDBC의 주요 구성 요소
3.1 DriverManager
• 역할: JDBC 드라이버를 관리하고, 데이터베이스 연결을 얻는 데 사용됩니다.
• 사용 방법: DriverManager.getConnection() 메서드를 통해 데이터베이스에 연결합니다.
• 특징:
• 애플리케이션이 시작될 때 JDBC 드라이버를 로드하면, 해당 드라이버는 DriverManager에 자신을 등록합니다.
• DriverManager는 등록된 드라이버 목록을 관리하고, getConnection() 호출 시 적절한 드라이버를 선택하여 연결을 생성합니다.
3.2 Connection
• 역할: 데이터베이스와의 연결을 나타내는 객체입니다.
• 주요 기능:
• 트랜잭션 관리 (commit(), rollback())
• SQL 문 실행을 위한 Statement, PreparedStatement, CallableStatement 객체 생성
3.3 Statement와 PreparedStatement
• Statement:
• SQL 문을 실행하기 위한 객체입니다.
• 동적 SQL이나 간단한 쿼리에 사용됩니다.
• SQL 문이 매번 컴파일되므로, 반복 실행 시 성능이 저하될 수 있습니다.
• PreparedStatement:
• 미리 컴파일된 SQL 문을 실행하기 위한 객체입니다.
• 바인딩 변수를 사용하여 SQL 인젝션을 방지하고 성능을 향상시킵니다.
• 동일한 SQL 문을 여러 번 실행할 때 유리합니다.
3.4 ResultSet
• 역할: SQL 쿼리의 결과를 저장하고, 결과를 순회하며 데이터를 추출할 수 있습니다.
• 주요 메서드:
• next(): 다음 행(row)으로 이동
• getInt(), getString() 등: 특정 컬럼의 값을 가져옴
• 특징:
• ResultSet은 기본적으로 전방향(forward-only)이며, 읽기 전용입니다.
• 필요에 따라 스크롤 가능하거나 업데이트 가능한 ResultSet을 생성할 수 있습니다.
3.5 DataSource
• 역할: 데이터베이스 연결을 관리하기 위한 고수준의 인터페이스입니다.
• 사용 방법: DataSource를 통해 Connection 객체를 얻습니다.
• 특징:
• **커넥션 풀링(Connection Pooling)**을 지원하여 성능을 향상시킵니다.
• **JNDI(Java Naming and Directory Interface)**를 통해 등록 및 조회할 수 있습니다.
• 트랜잭션 관리와 분산 트랜잭션을 지원하는 고급 기능을 제공합니다.
• 이점:
• DriverManager보다 유연하고 확장성이 높습니다.
• 애플리케이션 서버나 프레임워크(Such as Spring)에서 자주 사용됩니다.
4. JDBC의 동작 방식
1. JDBC 드라이버 로드:
• Class.forName("com.mysql.cj.jdbc.Driver") 등을 사용하여 드라이버 클래스를 로드합니다.
• 최신 JDBC 버전에서는 자동 로딩이 지원되므로 생략할 수 있습니다.
2. 데이터베이스 연결:
• DriverManager.getConnection() 또는 DataSource.getConnection()을 사용하여 데이터베이스에 연결하고 Connection 객체를 얻습니다.
3. SQL 문 실행:
• Connection 객체에서 Statement 또는 PreparedStatement를 생성합니다.
• SQL 문을 실행하고 결과를 ResultSet으로 받습니다.
4. 결과 처리:
• ResultSet을 순회하며 데이터를 추출하고 처리합니다.
5. 자원 해제:
• ResultSet, Statement, Connection 객체를 close() 메서드를 호출하여 자원을 해제합니다.
5. DriverManager와 DataSource의 비교
5.1 DriverManager(물리적 접속)
• 역할: JDBC 드라이버를 관리하고 데이터베이스 연결을 얻는 데 사용됩니다.
• 특징:
• 간단한 애플리케이션에서 사용하기 적합합니다.
• 커넥션 풀링이나 트랜잭션 관리 등의 고급 기능을 지원하지 않습니다.
• 매번 새로운 연결을 생성하므로, 다수의 연결이 필요한 경우 성능 저하가 발생할 수 있습니다.
5.2 DataSource(논리적 접속)
• 역할: 데이터베이스 연결을 관리하기 위한 고수준의 인터페이스입니다.
• 특징:
• 커넥션 풀링을 지원하여 데이터베이스 연결의 재사용을 가능하게 합니다.
• 트랜잭션 관리와 분산 트랜잭션을 지원합니다.
• 설정이 복잡할 수 있지만, 애플리케이션 서버나 프레임워크(Spring 등)에서 쉽게 설정할 수 있습니다.
• 장점:
• 성능 향상: 커넥션 풀링을 통해 연결 생성 및 해제 비용을 절감합니다.
• 확장성: 대규모 애플리케이션에서 효율적인 연결 관리가 가능합니다.
• 유연성: 설정 파일이나 JNDI를 통해 연결 정보를 관리할 수 있습니다.
5.3 언제 어떤 것을 사용해야 하는가
• DriverManager:
• 간단한 애플리케이션이나 테스트 환경에서 사용하기 적합합니다.
• 커넥션 풀링이나 고급 기능이 필요하지 않은 경우에 사용합니다.
• DataSource:
• 성능과 확장성이 중요한 애플리케이션에서 사용해야 합니다.
• Spring이나 애플리케이션 서버를 사용하는 경우 자연스럽게 DataSource를 사용하게 됩니다.
6. JDBC를 사용한 데이터베이스 연결 예제
6.1 DriverManager를 사용한 예제
import java.sql.*;
public class JdbcExample {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/your_database";
String username = "your_username";
String password = "your_password";
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
// 1. JDBC 드라이버 로드
Class.forName("com.mysql.cj.jdbc.Driver");
// 2. 데이터베이스 연결
conn = DriverManager.getConnection(url, username, password);
// 3. SQL 문 준비
String sql = "SELECT * FROM users WHERE age > ?";
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, 20);
// 4. SQL 문 실행
rs = pstmt.executeQuery();
// 5. 결과 처리
while (rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("name");
int age = rs.getInt("age");
System.out.println("ID: " + id + ", 이름: " + name + ", 나이: " + age);
}
} catch (ClassNotFoundException e) {
System.err.println("드라이버 로드 실패: " + e.getMessage());
} catch (SQLException e) {
System.err.println("SQL 오류: " + e.getMessage());
} finally {
// 6. 자원 해제
try { if (rs != null) rs.close(); } catch (SQLException e) { /* 무시 */ }
try { if (pstmt != null) pstmt.close(); } catch (SQLException e) { /* 무시 */ }
try { if (conn != null) conn.close(); } catch (SQLException e) { /* 무시 */ }
}
}
}
설명:
• 드라이버 로드: Class.forName()을 통해 JDBC 드라이버를 로드합니다.
• 데이터베이스 연결: DriverManager.getConnection()을 사용하여 Connection 객체를 얻습니다.
• SQL 실행 및 결과 처리: PreparedStatement와 ResultSet을 사용하여 SQL 문을 실행하고 결과를 처리합니다.
• 자원 해제: 사용한 자원(ResultSet, PreparedStatement, Connection)을 명시적으로 닫아줍니다.
6.2 DataSource를 사용한 예제
import javax.sql.DataSource;
import java.sql.*;
public class DataSourceExample {
public static void main(String[] args) {
DataSource dataSource = getDataSource(); // DataSource 설정 메서드 호출
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
// 1. 데이터베이스 연결
conn = dataSource.getConnection();
// 2. SQL 문 준비
String sql = "SELECT * FROM users WHERE age > ?";
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, 20);
// 3. SQL 문 실행
rs = pstmt.executeQuery();
// 4. 결과 처리
while (rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("name");
int age = rs.getInt("age");
System.out.println("ID: " + id + ", 이름: " + name + ", 나이: " + age);
}
} catch (SQLException e) {
System.err.println("SQL 오류: " + e.getMessage());
} finally {
// 5. 자원 해제
try { if (rs != null) rs.close(); } catch (SQLException e) { /* 무시 */ }
try { if (pstmt != null) pstmt.close(); } catch (SQLException e) { /* 무시 */ }
try { if (conn != null) conn.close(); } catch (SQLException e) { /* 무시 */ }
}
}
private static DataSource getDataSource() {
// DataSource 설정 코드 (예: HikariCP 사용)
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/your_database");
config.setUsername("your_username");
config.setPassword("your_password");
config.setDriverClassName("com.mysql.cj.jdbc.Driver");
return new HikariDataSource(config);
}
}
설명:
• DataSource 설정: getDataSource() 메서드에서 HikariCP 등을 사용하여 DataSource를 설정합니다.
• 데이터베이스 연결: dataSource.getConnection()을 통해 연결을 얻습니다.
• 나머지 부분은 DriverManager를 사용하는 경우와 동일합니다.
7. JDBC의 장단점
장점
• 데이터베이스 독립성: 다양한 데이터베이스에 동일한 방식으로 접근할 수 있습니다.
• 직접적인 제어: SQL 문을 직접 작성하고 실행하므로, 데이터베이스 작업을 세밀하게 제어할 수 있습니다.
• 표준 API: 표준화된 인터페이스를 제공하여 일관성 있는 개발이 가능합니다.
단점
• 반복적인 코드: 연결, 자원 해제, 예외 처리 등 반복적인 코드가 많이 발생합니다.
• 자원 관리의 복잡성: 연결 누수(Connection Leak)를 방지하기 위해 자원을 적절히 해제해야 합니다.
• 예외 처리의 복잡성: SQL 예외 처리가 복잡할 수 있으며, 코드의 가독성을 떨어뜨립니다.
• 생산성 저하: 반복적인 작업으로 인해 개발 생산성이 낮아질 수 있습니다.
8. 마치며
JDBC는 자바 애플리케이션에서 데이터베이스와 상호 작용하기 위한 기본적인 방법을 제공합니다. 직접 SQL을 제어할 수 있어 세밀한 데이터베이스 작업이 가능하지만, 반복적인 코드와 자원 관리의 복잡성으로 인해 생산성이 떨어질 수 있습니다.
DriverManager는 간단한 애플리케이션에서 사용하기 적합하지만, 커넥션 풀링과 같은 고급 기능을 지원하지 않습니다. 반면, DataSource는 커넥션 풀링과 트랜잭션 관리 등 고급 기능을 제공하여 대규모 애플리케이션에서 효율적인 연결 관리를 가능하게 합니다.
프로젝트의 요구사항과 규모에 따라 DriverManager와 DataSource 중 적절한 방식을 선택하시기 바랍니다. 일반적으로 Spring 등의 프레임워크를 사용하면 DataSource를 활용하여 커넥션 풀링과 트랜잭션 관리를 자동화할 수 있습니다.
'spring' 카테고리의 다른 글
[Spring] Spring의 트랜잭션 동기화(Transaction Synchronization) (0) | 2024.11.28 |
---|---|
[Spring] JdbcTemplate (0) | 2024.11.22 |
[Spring] Spring Boot를 통한 JDBC, JdbcTemplate, JPA의 이해와 비교 (1) | 2024.11.22 |
[Spring] JdbcTemplate과 일반 JDBC 코드의 차이점 (1) | 2024.11.19 |
[Spring] 예시를 통한 BaseResponse 학습하기 (0) | 2024.11.07 |