DB & SQL

Statement 대신 PreparedStatement를 사용해야 하는 이유

devJK93 2024. 1. 30.

1) 쿼리의 실행과정

 

QueryExecutionPhases

 

쿼리의 실행과정은 위 그림과 같다.

 

1) Parsing & Normalization Phase

- Query 문법 확인, Semantic check(?) 및 쿼리에 있는 테이블, 컬럼 존재여부 확인

 

2) Compilation Phase

- 쿼리를 machine이 이해할 수 있는 형식으로 컴파일

 

3) Query Optimization Phase

- 쿼리를 실행할 수 있는 방법들을 체크하고, 최적의 쿼리 실행 방법을 선택

 

4) Cache

- 3) 단계의 최적의 방법을 저장

 

5) Execution Phase

- 쿼리 실행

 

2) Statement, PreparedStatement의 동작 방식의 차이점

 

1️⃣ 캐시 사용 유무

// [Statement]
Connection conn = DriverManager.getConnection(url, id, pwd);
Statement stmt = conn.createStatement();
stmt.executeUpdate("Update Member Set Name = " + "JK" Where id = " + 100);
stmt.executeUpdate("Update Member Set Name = " + "SY" Where id = " + 101; 

// [PreparedStatement]
Connection conn = DriverManager.getConnection(url, id, pwd);
String sql = "Update Member Set Name = ? Where id = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);

pstmt.setString(1, "JK");
pstmt.setInt(2, 100);
pstmt.executeUpdate(); 

pstmt.setString(1, "SY");
pstmt.setInt(2, 101);
pstmt.executeUpdate();

 

- Statement, PreparedStatement 방식 둘 다 Query를 컴파일하고 최적화한 다음 실행되기 전에 Caching 되지만 Statement는 첫 수행한 Query와 완전히 일치하는 Query를 요청하는 경우에만 캐싱한 데이터를 재활용할 수 있다.

 

- Statement의 경우, 첫 번째 Query와 일치하지 않는 별도의 Query를 실행하게 되면 컴파일을 다시 해야 한다.

즉, 위 그림의 1 ~ 5단계를 모두 다시 거쳐야 하는 셈이다.

 

- PreparedStatement 방식은 Statement 방식과 다르게 파라미터를 바인딩하기 때문에 기존에 컴파일된 Query를 재활용할 수 있다. 

즉,  처음 1 ~ 5단계를 모두 거쳤으면 다음부터는 1 ~ 4단계는 뛰어넘고 바로 쿼리 실행이 가능하다.

 

- 또, 파라미터를 Query문 안에 직접 넣어주는 Statement 방식은 가독성도 좋지 않고 코딩하기에도 번거롭다.

 

2️⃣ SQL Injection 방지

// [기존 Statement 방식]

Connection conn = DriverManager.getConnection(url, id, pwd);
Statement stmt = conn.createStatement();
stmt.executeQuery("Select * from Member Where id = " + id + " and pwd = " + pwd); 

→ 로그인 시, 사용자에게서 입력받은 값을 Where 조건절에 넣어주어 조회하는 Query이지만 만약, 다음과 같이 공격자가 파라미터 값을 악의적으로 조작해서 보낼 수도 있다.

// [사용자에게서 입력받은 값]
id = test123
pwd = 1234’ or 1=1

Query = Select * from Member Where id = ‘test123’ and pwd = ‘1234’ OR ‘1’ = ‘1’;

→ 이처럼 공격자가 악의적으로 파라미터 값을 조작하여 “OR 1=1” 조건을 추가하는 방법으로 사용자 정보를 탈취할 수 있다.

// [PreparedStatment 방식]
Connection conn = DriverManager.getConnection(url, id, pwd);
String sql = "Select * from Member Where id = ? and pwd = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, id);
pstmt.setString(2, pwd);
pstmt.executeQuery();

→ PreparedStatment 방식은 사용자에게서 받은 파라미터 값이 악의적으로 조작되었다 하더라도 파라미터 바인딩을 통해 SQL Injection을 방지할 수 있다.

→ 정확히는 pstmt.setString() 메소드 내부에서 사용되는 javaEncode() 메소드가 SQL Injection을 방지해주는데 내부 구현이 어떻게 되어있는지 살펴보자! 

// [javaEncode() 메소드]
public static void javaEncode(String s, StringBuilder buff, boolean forSQL) {
    int length = s.length();

    for (int i = 0, i < length; i++) {
        char c = s.charAt(i);

        switch (c) { 
            case ‘\t’:
                buff.append(“\\t”);
                break;
            case ‘\n’:
                buff.append(“\\n”);
                break;
            case ‘\f’:
                buff.append(“\\f”);
                break;
            case ‘“’;
                buff.append(‘\’’);
                break;
                ...
        }
    }
}
 
→ 이처럼 공격자가 악의적으로 조작한 파라미터 값에 “\”를 붙여줌으로써 SQL Injection을 방지할 수 있는 것이다.

 

---

 

출처 : [Why Prepared Statement is faster than Statement in Java JDBC](https://javabypatel.blogspot.com/2017/06/why-prepared-statement-is-faster-than-statement-in-java.html)

[Statement 보다 PreparedStatement를 사용해야 하는 이유](https://shuu.tistory.com/129)

 

댓글