Mybatis #(샵)과 $(달러)의 차이

[Desc]

MyBatis에서 변수 #과 $의 차이를 잘모르고 사용하는 것 같아 정리했습니다.


SELECT * FROM TEST
WHERE 1=1
AND TEST_ID = #{testId}

위와 같은 쿼리문을 수행하게 되면 database에서는 아래 쿼리문에 대한 의미, 구문분석 및 파싱을 진행하게 됩니다.

(오라클의 PreparedStatements)

//(오라클에서 파싱되어 받는쿼리) P1
SELECT * FROM TEST
WHERE 1=1
AND TEST_ID = ?

만약 testId에 1값이 바인드 되게 되면 database에서는 이미 파싱되어 있는 쿼리문(P1)을 재활용하게 됩니다.

testId 2값이 들어오면 P1에 파싱되어있는 쿼리문을 재활용을 합니다.

단 데이터베이스 옵티마이저에 대한 수행 계획은 항상 동일하다는게 단점입니다.

수행 계획에 따른 데이터 추출은 데이터의 분포도에 영향을 받게 되는데 

만약 [1]이라는 값이 TEST테이블에 4개가 존재하고 [2]라는 값이 1000개 존재한다고 한다면

1을 추출할때는 인덱스 스캔이 유리하고

2를 추출할때는 풀스캔이 유리합니다.

즉 위의 쿼리문을 파싱 할때 TEST_ID컬럼에 인덱스가 있다면 무조건 인덱스 스캔을 수행 계획으로 수립하게 되므로

2값이 들어올때에도 인덱스 스캔으로 인한 성능 저하가 발생할 수 있습니다.



하지만 #을 사용할때는 SQL Injection 대비가 가능

쿼리문을 실행하기에 앞서 구문분석, 의미 분석, 파싱 작업을 진행하기에 SQL Injection의 코드를 만나게 되면 오류를 발생 시킵니다.



그럼 아래와 같은 쿼리는 어떨까요?

SELECT * FROM TEST
WHERE 1=1
AND TEST_ID = ${testId}
${testId} 가 1 일때는 아래와 같은 쿼리문이 실행되며 데이터베이스에서는 의미,구문 분석 및 파싱 작업을 진행합니다.(Statements)


//오라클에서 받는쿼리
SELECT * FROM TEST
WHERE 1=1
AND TEST_ID = 1
여기서 유념해야하는 부분은 #을 사용할때는 ?로 치환되고 $사용할때는 1이라는 상수로 치환이 되어 수행됩니다. ${testId} 값이 변경되면 2,3,4,5... 과 같이 값이 변경 될 때마다 파싱 작업을 항상 진행하여 성능상의 단점이 존재합니다.
SELECT * FROM TEST
WHERE 1=1
AND TEST_ID = ${testId} AND PASSWORD = ${passWd}

//Sqlinjection 공격
SELECT * FROM TEST
WHERE 1=1
AND TEST_ID = ${testId} --AND PASSWORD = ${passWd}
또한 SQL Injection 공격에 취약할 수 있습니다.
그렇기 때문에 중요한 쿼리에서는 $를 사용하지않고 #을 사용합니다. 단 $를 사용할 때는


ORDER BY ${columnName} DESC
or
SELECT ${columnName} FROM tableName 


그렇다고 $달러방식이 단점만 있는 건 아니다. 위에서 언급했다시피 풀스캔과 인덱스스캔 수행계획에 따라서 사용 할 수 도 있을 것입니다.


참고 : http://lng1982.tistory.com/246


블로그 이미지

pstree

pstree.. process...

,