PreparedStatement
안전한 매개변수화 쿼리를 작성합니다
PreparedStatement은(는) CoddyKit의 무료 Java Academy 강의입니다. 이것은 4개 중 2번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Java Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Java Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
PreparedStatement를 사용하는 이유
사용자 입력을 문자열로 연결하여 SQL을 만드는 것은 위험하고 느립니다.
PreparedStatement는 두 문제를 모두 해결합니다:
- 안전성 — 값이 SQL과 분리되어 전송되므로 SQL 주입을 방지합니다.
- 성능 — 데이터베이스가 쿼리를 한 번만 구문 분석하고 실행 계획을 세운 뒤 재사용할 수 있습니다.
SQL 주입 문제
문자열을 연결하여 쿼리를 만든다고 생각해 보십시오. 사용자가 이름으로 '; DROP TABLE users; --를 입력하면 연결된 SQL이 이를 명령으로 실행합니다.
이것이 SQL 주입이며, 가장 오래되고 피해가 큰 웹 취약점 중 하나입니다. PreparedStatement는 설계상 이를 불가능하게 만듭니다.
public class Main {
public static void main(String[] args) {
String userInput = "'; DROP TABLE users; --";
// Dangerous: never do this
String unsafe = "SELECT * FROM users WHERE name = '" + userInput + "'";
System.out.println("Injected SQL becomes:");
System.out.println(unsafe);
}
}자리 표시자
PreparedStatement에서는 나중에 제공할 각 값의 자리에 ?를 하나씩 작성합니다.
그런 다음 타입별 설정 메서드로 값을 바인딩합니다:
ps.setString(1, name)ps.setInt(2, age)
매개변수 인덱스는 1부터 시작하며 ? 표시의 순서와 일치합니다.
안전한 쿼리
prepareStatement로 문을 만들고 매개변수를 바인딩한 다음 executeQuery를 호출합니다.
값이 SQL 텍스트에 전혀 포함되지 않으므로 사용자가 무엇을 입력하더라도 주입이 발생할 수 없습니다.
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class Main {
public static void main(String[] args) {
String url = "jdbc:postgresql://localhost:5432/shop";
String sql = "SELECT id FROM users WHERE name = ?";
try (Connection conn = DriverManager.getConnection(url, "app", "secret");
PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, "Ada");
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) System.out.println(rs.getInt("id"));
}
} catch (SQLException e) {
System.out.println(e.getMessage());
}
}
}여러 매개변수 바인딩
각 ?에는 고유한 설정 메서드 호출이 필요합니다. 순서가 중요합니다. 매개변수 1은 첫 번째 ?, 매개변수 2는 두 번째 ?에 해당하며 이후에도 같은 방식입니다.
열의 형식에 맞는 설정 메서드를 사용하십시오. 예를 들어 setInt, setString, setDouble, setBoolean 등이 있습니다.
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class Main {
public static void main(String[] args) {
String url = "jdbc:postgresql://localhost:5432/shop";
String sql = "INSERT INTO users(name, age, active) VALUES (?, ?, ?)";
try (Connection conn = DriverManager.getConnection(url, "app", "secret");
PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, "Grace");
ps.setInt(2, 42);
ps.setBoolean(3, true);
int rows = ps.executeUpdate();
System.out.println("Inserted " + rows + " row");
} catch (SQLException e) {
System.out.println(e.getMessage());
}
}
}NULL 처리
SQL NULL을 바인딩하려면 java.sql.Types 상수와 함께 setNull(index, type)을 사용합니다.
Java의 null을 setString에 전달하고 제대로 처리되기를 기대하지 마십시오. 명시적으로 지정해야 합니다:
ps.setNull(2, java.sql.Types.INTEGER)
생성된 키 가져오기
INSERT 후에는 자동으로 생성된 식별자가 필요한 경우가 많습니다.
준비할 때 Statement.RETURN_GENERATED_KEYS를 전달한 다음 ps.getGeneratedKeys()를 읽습니다.
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class Main {
public static void main(String[] args) {
String url = "jdbc:postgresql://localhost:5432/shop";
String sql = "INSERT INTO users(name) VALUES (?)";
try (Connection conn = DriverManager.getConnection(url, "app", "secret");
PreparedStatement ps = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
ps.setString(1, "Linus");
ps.executeUpdate();
try (ResultSet keys = ps.getGeneratedKeys()) {
if (keys.next()) System.out.println("New id: " + keys.getLong(1));
}
} catch (SQLException e) {
System.out.println(e.getMessage());
}
}
}일괄 삽입
수천 개의 행을 한 번에 하나씩 문으로 삽입하면 느립니다. 일괄 처리를 사용하십시오:
- 매개변수를 바인딩한 다음
ps.addBatch()를 호출합니다. - 각 행에 대해 반복합니다.
ps.executeBatch()를 한 번 호출합니다.
이렇게 하면 행을 함께 전송하여 왕복 횟수를 크게 줄일 수 있습니다.
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class Main {
public static void main(String[] args) {
String url = "jdbc:postgresql://localhost:5432/shop";
String sql = "INSERT INTO tags(label) VALUES (?)";
String[] labels = {"java", "jdbc", "sql"};
try (Connection conn = DriverManager.getConnection(url, "app", "secret");
PreparedStatement ps = conn.prepareStatement(sql)) {
for (String label : labels) {
ps.setString(1, label);
ps.addBatch();
}
int[] counts = ps.executeBatch();
System.out.println("Batched " + counts.length + " inserts");
} catch (SQLException e) {
System.out.println(e.getMessage());
}
}
}문 재사용
하나의 PreparedStatement를 서로 다른 매개변수로 여러 번 실행할 수 있습니다.
자리 표시자에 값을 다시 바인딩하고 execute를 다시 호출하십시오. 데이터베이스가 캐시된 실행 계획을 재사용하므로 준비된 문은 반복 쿼리에서 더 빠릅니다.
절대 연결하지 않기
핵심 원칙은 다음과 같습니다. 변경될 수 있는 모든 값은 ? 매개변수여야 합니다.
고정되어 있고 코드가 제어하는 식별자(허용 목록에 있는 열 이름 등)만 연결할 수 있습니다. 이 경우에도 허용 목록과 대조하여 검증하십시오. 사용자가 제공한 데이터는 항상 바인딩하고 절대 연결하지 않습니다.
닫기 및 재사용 범위
PreparedStatement는 해당 Connection에 연결되어 있으므로 사용이 끝나면 닫아야 합니다.
리소스 자동 관리 블록 안에서는 연결보다 문이 먼저 닫히도록 문을 선언하십시오. 문을 반복문에서 여러 번 재사용한다면 반복문 밖에서 한 번만 준비하고 반복문 안에서는 매개변수만 다시 바인딩하십시오. 반복할 때마다 다시 준비하면 캐시된 실행 계획을 버리게 됩니다.
빠른 확인
PreparedStatement는 왜 SQL 인젝션을 방지하나요?
복습
안전하고 빠른 쿼리를 작성하는 방법을 배웠습니다.
?자리 표시자와 자료형에 맞는 설정 메서드를 사용합니다(인덱스는 1부터 시작).setString,setInt,setNull등으로 값을 바인딩합니다.RETURN_GENERATED_KEYS와getGeneratedKeys로 자동 생성 키를 가져옵니다.addBatch와executeBatch를 함께 사용해 여러 쓰기 작업을 일괄 처리합니다.- 절대로 사용자 입력을 SQL에 결합하지 않습니다.
자주 묻는 질문
“PreparedStatement” 강의는 무료인가요?
네 — “PreparedStatement” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Java Academy 강의 전체를 잠금 해제할 수 있습니다. Java Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
“PreparedStatement”에서 뭘 배우나요?
안전한 매개변수화 쿼리를 작성합니다 브라우저에서 직접 실행하는 실습 코드로 Java Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
Java Academy을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 Java Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 2번째 강의입니다.
“PreparedStatement” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 Java Academy 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 Java Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- JDBC 기초
- PreparedStatement
- 트랜잭션
- HikariCP를 사용한 연결 풀링