Contents
MyBatis의 '${}'와 '#{}'
   Apr 19, 2023     3 min read

#{} vs ${}

⚓️서론

mybatis에서 중요한 문법이다. 면접에서도 많이 나오는 질문 중 하나인 것 같다.

필자 역시도 질문을 받았다.

이번 기회에 한번 정리하고 넘어가야겠다.


➡️Statement 와 PreparedStatement

본론에 들어가기 앞서 간단히 Statement 와 PreparedStatement의 차이를 간단히 알고 넘어가자.

두 개의 가장 큰 차이점은 캐시를 사용하는지 안하는지가 큰 차이를 가른다.

✅ Statement를 사용하면

  • 매번 쿼리를 수행할 때 3단계를 거친다.
    1. 쿼리 문장 분석
    2. 컴파일
    3. 실행

반면에…

✅PreparedStatement를 사용하면

  • 처음에만 3단계를 거치고 cache를 사용하여 담아서 재사용한다.

따라서 반복적으로 쿼리가 수행된다면 PreparedStatement를 사용하는것 좋고 DB에도 부담을 덜 준다.


➡️XML에서 ${ }를 사용하면

1️⃣값이 넣어진 채로 쿼리문을 수행한다.

  • 따라서 파라미터의 값이 바뀌다면, 쿼리문 파싱을 매번 진행한다. → 성능이 느림

2️⃣쿼리문에 작은 따옴표(’)가 붙지 않는다.

  • 따라서 테이블 이름이나 컬럼 이름을 동적으로 결정할 때 사용 가능하다 →
  • cf. #{ }는 작은 따옴표(’)가 붙음
<select id="select" resultType="String" parameterType="Map">
    SELECT
        name AS 이름
    FROM
        user_${id}
    WHERE
        id = #{id}
</select>

➡️XML에서 #{ }를 사용하면

  • #{ } 예제 - 쿼리문
<select id="select" resultType="String" parameterType="Map">
    SELECT
        name AS 이름
    FROM
        user
    WHERE
        id = #{id}
</select>
  • MyBatis에서는 #{ } 이 사용된 퀴리가 실행되면 파싱되면서 #{ }? 으로 된다.
SELECT
    name AS 이름, email AS 이메일
FROM
    user
WHERE
    id = ?

여기서 위에서 간단히 알아봤던 PreparedStatement 개념이 들어간다.

#{ } 을 사용하는 경우 PreparedStatement 를 생성하게 되는데 위의 ? 에 파라미터가 바인딩되어 수행된다.

따라서 파싱된 쿼리문은 캐싱되어서 재활용으로 쓰이기 때문에 효율적으로 쓸 수 있다.

✅작은 따옴표(’)는 자동으로 붙여서 수행된다.

  • #{id}'#{id}' 으로 자동으로 처리해준다.

  • 테이블 설계가 user_a, user_b 각각 구성되어있을 때 , 아래와 같은 방법으로는 작성이 불가하다. 쿼리를 실행하면 tableId 작은 따옴표가 붙기 때문에 SQLSyntaxErrorException 오류가 발생한다.


<select id="select" resultType="String" parameterType="Map">>
    SELECT
        name AS 이름
    FROM
        user_#{tableId}
    WHERE
        id = #{id}
</select>

➡️SQL Inject

  • 보안 관련 이슈
<select id="selectUserFromTable" parameterType="Map" resultType="...">
    SELECT
        *
    FROM
        user
    WHERE
        id = '${id}' AND password = '${password}'
</select>
  • 만일 id 파라미터의 값으로 ‘admin’ 이 입력되는 경우 → WHERE 절에서 비밀번호에 조건이 사라지게 되어 id만 입력해도 관리자 계정 정보를 조회할 수 있게 된다.
  • 결론 : ${} 사용하는 경우 SQL Injection에 취약하다.
SELECT
    *
FROM
    user
WHERE
    id = 'admin' -- 'AND password = ''

📎참고

  • Statement 와 PreparedStatement

https://devbox.tistory.com/entry/Comporison

  • **MyBatis에서 샾(#{})과 달러(${})의 차이는 무엇일까?**

https://madplay.github.io/post/difference-between-dollar-sign-and-sharp-sign-in-mybatis