💡 성능 향상 기법
- PreparedStatement 사용의 장점
- 연결 풀 (Connection Pool) 사용
- 데이터 소스 (Data Source)의 개념
- 연결 풀의 장점(Connection Pool)
- 캐싱 전략
1. PreparedStatement 사용의 장점
PreparedStatement는 SQL 쿼리를 미리 컴파일하고, 동일한 쿼리를 반복해서 실행할 때 효율적으로 사용할 수 있는 인터페이스입니다. 이는 성능과 보안 측면에서 많은 장점을 제공합니다.
- 성능 향상:
- 쿼리 컴파일: SQL 쿼리를 미리 컴파일하여, 쿼리를 여러 번 실행할 때 컴파일 시간을 절약할 수 있습니다.
- 쿼리 계획 재사용: 동일한 쿼리를 반복적으로 실행할 때, 쿼리 계획을 재사용하여 실행 시간을 단축할 수 있습니다.
- 보안 향상:
- SQL 인젝션 방지: 쿼리와 데이터가 분리되어 있어 SQL 인젝션 공격을 방지할 수 있습니다.
예시 코드
String sql = "INSERT INTO users (name, email) VALUES (?, ?)";
try (Connection connection = DBConnectionManager.getConnection();
PreparedStatement preparedStatement = connection.prepareStatement(sql)) {
preparedStatement.setString(1, "고길동");
preparedStatement.setString(2, "a@example.com");
preparedStatement.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
SQLInjectionExample
package ch01;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Scanner;
public class SQLInjectionExample {
public static void main(String[] args) {
try (Connection conn = DBConnectionManager.getConnection()){
Scanner scanner = new Scanner(System.in);
System.out.print("사용자 입름을 입력하세요 : ");
String username = scanner.nextLine();
// 취약한 SQL 쿼리 작성해보기(SQL 인젝션이 가능)
String query = " SELECT * FROM user WHERE name = " + username + " ";
try (Statement stmt = conn.createStatement()){
ResultSet resultSet = stmt.executeQuery(query);
while(resultSet.next()) {
System.out.println("사용자 ID : " + resultSet.getInt("id"));
System.out.println("사용자 이름 : " + resultSet.getString("name"));
System.out.println("사용자 이메일 : " + resultSet.getString("email"));
}
} catch (Exception e) {
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
} // end of main
}
SQL 인젝션 확인
SQL 인젝션 예방 코드
package ch01;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Scanner;
public class SQLInjectionExample {
public static void main(String[] args) {
try (Connection conn = DBConnectionManager.getConnection()){
Scanner scanner = new Scanner(System.in);
System.out.print("사용자 입름을 입력하세요 : ");
String username = scanner.nextLine();
// 취약한 SQL 쿼리 작성해보기(SQL 인젝션이 가능)
String query = " SELECT * FROM user WHERE name = " + username + " ";
String query2 = " SELECT * FROM user WHERE name = ? ";
try (Statement stmt = conn.createStatement();
// Statement 사용과 인젝션 공격 확인
// ResultSet resultSet = stmt.executeQuery(query);
// SQL 인젝션 방지를 위한 PreparedStatement의 사용
PreparedStatement pstmt = conn.prepareStatement(query2)){
pstmt.setString(1, username);
ResultSet resultSet = pstmt.executeQuery();
while(resultSet.next()) {
System.out.println("사용자 ID : " + resultSet.getInt("id"));
System.out.println("사용자 이름 : " + resultSet.getString("name"));
System.out.println("사용자 이메일 : " + resultSet.getString("email"));
}
} catch (Exception e) {
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
} // end of main
}
2. 연결 풀 (Connection Pool)과 데이터 소스 (Data Source)
데이터베이스 연결의 생애주기
1. 애플리케이션 로직에서 데이터베이스 연결 요청
2. 데이터베이스 드라이버 초기화 및 연결 설정
3. TCP/IP 연결 설정
4. 사용자 인증 및 세션 생성
5. 커넥션 생성 완료
6. 데이터베이스 연결 사용 및 종료
일반적으로 데이터베이스 연결은 비용이 큰 작업입니다. 데이터베이스 서버와의 연결을 맺고 끊는 과정은 시간이 많이 소요되며, 데이터베이스 서버의 리소스도 소비된다. 그렇기 때문에 매번 데이터베이스 연결을 필요로 하는 요청이 발생할 때마다 새로운 연결을 생성하는 것은 효율적이지 않습니다.
성능 최적화의 여러 방법중 하나로 아래와 같은 기술들을 적용할 수 있습니다.
💡 **커넥션 풀(Connection Pool)**과 **데이터 소스 (Data Source)**는 데이터베이스 연결 관리를 효율적으로 하기 위해 사용하는 개념이다.
연결 풀 (Connection Pool)
- 정의: 데이터베이스 연결 풀은 일정 수의 데이터베이스 연결을 미리 생성해두고, 애플리케이션에서 필요할 때마다 이 연결을 재사용하는 기술입니다.
- 목적: 데이터베이스 연결을 효율적으로 관리하여 성능을 향상시키고, 데이터베이스 서버의 부하를 줄입니다.
- 작동 원리: 애플리케이션이 데이터베이스 연결을 요청하면, 연결 풀에서 사용 가능한 연결을 반환합니다. 사용이 끝난 연결은 폐기되지 않고 다시 연결 풀에 반환되어 재사용됩니다.
💡 핵심 정리
- DB 드라이버가 아닌 커넥션 풀에서 커넥션을 가져온다.
- 커넥션 사용 후 재사용할 수 있도록 커넥션 풀에살아 있는 상태로 반환 한다
커넥션 생성에 필요한 6가지 단계들이 모두 처리된 상태이므로, 언제든지 즉시 SQL 문을 데이터베이스로 전달할 수 있어서 사용자 응답 속도가 빨라진다.
서버당 최대 커넥션 개수를 제한할 수 있어 DB에 무한정 연결이 생성되는 것을 막아 데이터베이스를 보호하는 효과도 존재한다.
데이터 소스 (Data Source)
- 정의: 데이터 소스는 데이터베이스 연결 정보를 캡슐화한 객체로, 애플리케이션이 데이터베이스와 상호작용할 때 사용됩니다. 일반적으로 데이터 소스는 연결 풀을 포함하여 데이터베이스 연결을 관리합니다.
- 목적: 데이터 소스는 데이터베이스 연결 설정을 단순화하고, 연결 풀을 통해 효율적인 연결 관리를 제공합니다.
- 사용 방법: 데이터 소스를 설정하고, 애플리케이션에서 데이터 소스를 통해 데이터베이스 연결을 요청합니다.
핵심 정리
데이터 소스 (Data Source)는 추상화된 개념으로 설계되어 있으며, 데이터베이스 연결을 관리하고 사용할 수 있는 수준의 인터페이스를 제공합니다. 이 추상화된 개념을 구현한 다양한 라이브러리를 사용하여, 프로젝트의 성격이나 정책에 맞게 선택하고 쉽게 사용할 수 있습니다
연결 풀과 데이터 소스의 관계
- 통합 관리: 데이터 소스는 연결 풀을 사용하여 데이터베이스 연결을 관리합니다. 데이터 소스를 통해 데이터베이스 연결을 요청하면, 내부적으로 연결 풀에서 관리하는 연결이 반환됩니다.
- 연결 추상화: 데이터 소스는 연결 풀을 포함한 다양한 연결 관리 방식을 추상화하여 제공합니다. 이를 통해 애플리케이션 개발자는 데이터 소스를 통해 쉽게 데이터베이스 연결을 사용할 수 있습니다.
라이브러리를 프로젝트에 추가 방법
- Maven을 사용하는 경우 또는 Gradle 빌드 툴을 사용할 수 있다.
- 라이브러리를 직접 추가하는 방법
https://github.com/brettwooldridge/HikariCP
https://mvnrepository.com/artifact/com.zaxxer/HikariCP
HikariCP 라이브러리를 사용하기 위해서 다른 추가적인 라이브러리 설정이 필요하다.
https://mvnrepository.com/artifact/mysql/mysql-connector-java/8.0.21
https://mvnrepository.com/artifact/org.slf4j/slf4j-api/2.0.0-alpha5
https://mvnrepository.com/artifact/org.slf4j/slf4j-simple/2.0.0
package com.tenco.quiz.ver3;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
/**
* 커넥션 풀을 활용하는 예제로 수정해 보자. HikariCP-5.1.0.jar lib 설정
*/
public class DBConnectionManager {
private static HikariDataSource dataSource;
private static final String URL = "jdbc:mysql://localhost:3306/quizdb?serverTimezone=Asia/Seoul";
private static final String USER = "root";
private static final String PASSWORD = "asd123";
static {
// HikariCP 를 사용하기 위한 설정이 필요 하다.
// HikariConfig --> 제공해줘서 이 클래스를 활용해서 설정을 상세히 할 수 있다.
HikariConfig config = new HikariConfig();
config.setJdbcUrl(URL);
config.setUsername(USER);
config.setPassword(PASSWORD);
config.setMaximumPoolSize(10); // 최대 연결 수 설정 10
dataSource = new HikariDataSource(config);
}
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
// 테스트 코드 확인
public static void main(String[] args) {
try {
Connection conn = DBConnectionManager.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
} // end of main
// 기본 JDBC 드라이버 사용 버전
// public static Connection getConnection() throws SQLException {
// return DriverManager.getConnection(URL, USER, PASSWORD);
// }
} // end of class
'Java' 카테고리의 다른 글
JDBC 실습 예제 (0) | 2024.07.05 |
---|---|
JDBC에서의 예외 처리 (0) | 2024.07.05 |
JDBC를 활용한 CRUD와 SOLID 원칙 (1) | 2024.07.05 |
JDBC 배치 처리 (0) | 2024.06.13 |
JDBC 트랜잭션 관리와 배치 처리 (0) | 2024.06.12 |