che01 님의 블로그
Statement vs PreparedStatement 차이점 정리 본문
기본 개념
Statement는 SQL 문자열을 직접 작성해서 DB에 전송하는 방식이다. 매번 새로운 SQL 문자열을 조합해서 실행한다.
PreparedStatement는 SQL 템플릿을 미리 준비하고, 나중에 값만 바인딩하는 방식이다. ? 플레이스홀더를 사용해 값을 동적으로 할당한다.
보안성
Statement의 SQL Injection 취약점
String userInput = "admin' OR '1'='1' --";
String sql = "SELECT * FROM users WHERE username = '" + userInput + "'";
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);
// 실제 실행되는 SQL:
// SELECT * FROM users WHERE username = 'admin' OR '1'='1' --'
// 결과: 모든 사용자 정보가 노출됨
PreparedStatement의 안전한 처리
String sql = "SELECT * FROM users WHERE username = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, userInput);
ResultSet rs = pstmt.executeQuery();
// SQL과 데이터가 분리되어 처리되므로 공격이 무력화됨
성능
Statement
매번 SQL 파싱, 컴파일, 실행을 반복한다. 동일한 쿼리를 여러 번 실행할 때 비효율적이다.
for (int i = 0; i < 1000; i++) {
String sql = "INSERT INTO users (name, age) VALUES ('" + name + "', " + age + ")";
Statement stmt = conn.createStatement();
stmt.executeUpdate(sql);
}
PreparedStatement
한 번 파싱/컴파일 후 재사용한다. 반복 실행 시 뛰어난 성능을 보인다.
String sql = "INSERT INTO users (name, age) VALUES (?, ?)";
PreparedStatement pstmt = conn.prepareStatement(sql);
for (int i = 0; i < 1000; i++) {
pstmt.setString(1, "User" + i);
pstmt.setInt(2, 25);
pstmt.executeUpdate();
}
코드 가독성
Statement
String sql = "UPDATE users SET name = '" + name + "', " +
"age = " + age + ", " +
"email = '" + email + "' " +
"WHERE id = " + id;
문자열 조합이 복잡하고 따옴표 처리 등에서 오류가 발생하기 쉽다.
PreparedStatement
String sql = "UPDATE users SET name = ?, age = ?, email = ? WHERE id = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, name);
pstmt.setInt(2, age);
pstmt.setString(3, email);
pstmt.setLong(4, id);
SQL 구조가 명확하게 보이고 파라미터 설정이 체계적이다.
비교 정리
구분 Statement PreparedStatement
보안성 | SQL Injection 취약 | SQL Injection 방지 |
성능 | 매번 컴파일 필요 | 한 번 컴파일 후 재사용 |
가독성 | 문자열 조합으로 복잡 | 명확한 구조 |
유지보수 | 어려움 | 용이함 |
실무 활용도 | 제한적 사용 | 일반적 사용 |
사용 지침
PreparedStatement를 기본으로 사용하되, DDL 문이나 동적 SQL 구성이 반드시 필요한 경우에만 Statement를 고려한다. 사용자 입력값이 포함되는 모든 쿼리는 PreparedStatement로 처리해야 한다.
배치 처리가 필요한 경우 PreparedStatement와 addBatch() 메서드를 조합하면 성능을 더욱 향상시킬 수 있다.