ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • MyBatis - ${}와 #{}의 차이
    Database/MyBatis 2022. 3. 22. 12:39

    검색 유형에 따른 코드의 구성

    #{}를 사용한 기존 코드
    <choose>
        <when test="'INDX_SEQ'.equals(searchType)">
            SELECT
            <include refid="dataColumns"/>
            FROM TBL_XBR_IDX_CALC
            WHERE INDX_SEQ LIKE '%' || #{searchKeyword} || '%'
            ORDER BY INDX_SEQ, CACL_ORD
        </when>
        <when test="'INDX_NM'.equals(searchType)">
            SELECT
            <include refid="dataColumns"/>
            FROM TBL_XBR_IDX_CALC
            WHERE INDX_NM LIKE '%' || #{searchKeyword} || '%'
            ORDER BY INDX_SEQ, CACL_ORD
        </when>
        <when test="'CACL_ORD'.equals(searchType)">
            SELECT
            <include refid="dataColumns"/>
            FROM TBL_XBR_IDX_CALC
            WHERE CACL_ORD LIKE '%' || #{searchKeyword} || '%'
            ORDER BY INDX_SEQ, CACL_ORD
        </when>
        <when test="'TRGT_ELMT_ID'.equals(searchType)">
            SELECT
            <include refid="dataColumns"/>
            FROM TBL_XBR_IDX_CALC
            WHERE TRGT_ELMT_ID LIKE '%' || #{searchKeyword} || '%'
            ORDER BY INDX_SEQ, CACL_ORD
        </when>
        <when test="'TRGT_ELMT_LABEL'.equals(searchType)">
            SELECT
            <include refid="dataColumns"/>
            FROM TBL_XBR_IDX_CALC
            WHERE TRGT_ELMT_LABEL LIKE '%' || #{searchKeyword} || '%'
            ORDER BY INDX_SEQ, CACL_ORD
        </when>
        <when test="'INDX_CALC_BASE_DT'.equals(searchType)">
            SELECT
            <include refid="dataColumns"/>
            FROM TBL_XBR_IDX_CALC
            WHERE INDX_CALC_BASE_DT LIKE '%' || #{searchKeyword} || '%'
            ORDER BY INDX_SEQ, CACL_ORD
        </when>
        <when test="'OPRTR'.equals(searchType)">
            SELECT
            <include refid="dataColumns"/>
            FROM TBL_XBR_IDX_CALC
            WHERE OPRTR LIKE '%' || #{searchKeyword} || '%'
            ORDER BY INDX_SEQ, CACL_ORD
        </when>
        <when test="'FNCTN'.equals(searchType)">
            SELECT
            <include refid="dataColumns"/>
            FROM TBL_XBR_IDX_CALC
            WHERE FNCTN LIKE '%' || #{searchKeyword} || '%'
            ORDER BY INDX_SEQ, CACL_ORD
        </when>
        <when test="'USE_TXN_IND_CD'.equals(searchType)">
            SELECT
            <include refid="dataColumns"/>
            FROM TBL_XBR_IDX_CALC
            WHERE USE_TXN_IND_CD LIKE '%' || #{searchKeyword} || '%'
            ORDER BY INDX_SEQ, CACL_ORD
        </when>
        <when test="'RGS_DTTM'.equals(searchType)">
            SELECT
            <include refid="dataColumns"/>
            FROM TBL_XBR_IDX_CALC
            WHERE RGS_DTTM LIKE '%' || #{searchKeyword} || '%'
            ORDER BY INDX_SEQ, CACL_ORD
        </when>
    </choose>

    사용자로부터 검색 유형(searchType)과 검색 키워드(searchKeyword)를 입력받아 조건에 맞는 데이터를 조회하기 위한 코드이다. 코드를 보면 알겠지만, 검색 유형(searchType) 조건에 따라 중복된 코드가 작성된 것을 확인할 수 있다. 중복된 코드를 제거하고 코드를 간결하게 구성하기 위해 아래와 같이 코드를 작성하였다.

     

    ${}를 사용한 변경된 코드
    SELECT
    <include refid="dataColumns"/>
    FROM TBL_XBR_IDX_CALC
    WHERE ${searchType} LIKE '%' || #{searchKeyword} || '%'
    ORDER BY INDX_SEQ, CACL_ORD

    검색 유형(searchType)을 #{}이 아닌 ${}으로 입력받아 코드를 구성하였다. 앞선 #{}를 사용한 코드와 비교해 보았을 때, 코드의 중복을 줄이고 훨씬 간결하게 구성하였음을 확인할 수 있다. 그렇다면, MyBatis의 #{}과 ${}의 차이점을 알아보도록 하겠다.


    MyBatis의 ${}와 #{}의 차이

    #{}의 사용
    <select id="selectName" parameter="String" returnData="HashMap">
        SELECT name
        FROM user
        WHERE id = #{id}
    </select>

    우선, 위와 같은 코드를 #{}을 사용하여 작성한다.

     

    SELECT name
    FROM user
    WHERE id = ?

    MyBatis에서는 #{}이 사용된 쿼리문이 실행되면 위와 같이 쿼리문에 ?가 생기며 파싱 된다. 쿼리문을 작성할 때 #{}을 사용하는 경우 PreparedStatement를 생성하게 되는데, 이때 ?에 파라미터가 바인딩되어 수행된다. 이렇게 파싱 된 쿼리문은 재활용(캐싱)되기 때문에 좀 더 효율적으로 사용할 수 있다.

     

    <select id="selectName" parameter="String" returnData="HashMap">
        SELECT name
        FROM user_#{tableId}
        WHERE id = #{id}
    </select>

    또한, #{}는 변수에 작은따옴표(')가 자동으로 붙여 쿼리문이 수행되기 때문에 위 코드와 같은 식으로 작성할 수 없다. 위 쿼리문이 수행되면 tableId 변수 양쪽에 작은따옴표(')가 붙기 때문에 SQLSyntaxErrorException 오류가 발생한다.

     

    ${}의 사용
    <select id="selectName" parameter="String" returnData="HashMap">
        SELECT name
        FROM user_${tableId}
        WHERE id = #{id}
    </select>

    쿼리문에 #{}을 사용한 것과 다르게 ${}을 사용하면 작은따옴표(')가 붙지 않기 때문에 위 코드와 같이 테이블 이름이나 칼럼 이름을 동적으로 결정할 때 사용할 수 있다.

    ${}을 사용하면 값이 넣어진 채로 쿼리문이 수행된다. 그렇기 때문에 파라미터의 값이 바뀔 때마다 항상 쿼리문 파싱을 진행해야 하기 때문에, 성능상의 단점이 존재한다.

    또한, ${}를 사용한 경우 SQL Injection에 취약하다는 단점이 있다.


    SQL Injection

    SQL Injection이란?

    SQL Injection은 응용 프로그램 보안 상의 허점을 의도적으로 이용해 악의적인 SQL 문을 실행되게 함으로써 데이터베이스를 비정상적으로 조작하는 방법이다. MyBatis의 ${}을 사용하면 주로 발생할 수 있는 가장 큰 허점이며, 이를 막기 위해서는 #{}을 사용하는 것을 지향하고 있다.

     

    <select id="selectUser" parameterType="Map" resultType="...">
        SELECT *
        FROM user
        WHERE id = '${id}' AND password = '${password}'
    </select>

    사용자로부터 아이디와 패스워드를 입력받고 사용자의 정보를 조회할 수 있는 쿼리문이 있다고 가정해보겠다. 만일 id의 파라미터 값으로 admin' --이 입력되는 경우는 어떻게 될까? 실제로 파싱 되는 쿼리문은 아래와 같을 것이다.

     

    SELECT *
    FROM user
    WHERE id = 'admin' -- ' AND password = ''

    비밀번호에 대한 조건은 -- 주석처리가 되기 때문에 사라지게 되며 id만 입력해도 관리자 계정 정보를 조회할 수 있게 된다. 따라서, ${} 사용하게 되는 경우 #{}을 사용하는 것보다 SQL Injection에 취약하다.

     

    728x90

    댓글

Designed by Tistory.