동적 쿼리¶
동적 쿼리는 매개변수의 값에 따라 동적으로 쿼리가 변형 되는 것을 말합니다. 일반적으로 동적 쿼리는 SQL 주입(injection)과 같은 보안 취약성을 갖기 때문에 최대한 사용을 자제하는 것이 좋습니다. 하지만 많은 SQL 문장에서 사용하는 IN
절이나 클라이언트로부터 전달되는 조회 조건에 따른 동적인 WHERE
절 구성 등의 상황에서 동적 쿼리를 사용하면 가독성 높고 편리하게 SQL 문장을 작성할 수 있습니다. 하지만 조건절(CASE WHEN
등)을 사용하거나 논리 연산자(AND
, OR
등)를 사용하여 쿼리 구성이 가능하다면 동적 쿼리를 피하는 것이 좋습니다.
이 문서와 관련된 예제 코드는 다음 예제를 참조 하십시요.
개요¶
FoxQuery 는 동적 쿼리를 지원하기 위해 매크로 스크립트를 사용합니다. 매크로 스트립트는 C# 스크립트 코드로서 <statement>
태그 내에 <macro>
태그에 작성합니다.
매크로 호출을 위해 <text>
태그 내에서 매크로 이름을 $$
문자로 둘러쌓여야 하며 ()
문자를 사용하여 매크로 호출임을 나타내야 합니다. 즉, 위 SEARCH_CONDITION
매크로를 호출하기 위한 매크로 호출 식은 $$SEARCH_CONDITION()$$
입니다.
FoxDbAccess
객체가 FoxQuery 로부터 Command
객체를 생성할 때 <text>
태그에서 매크로 호출을 발견하면 주어진 이름의 매크로 스크립트 코드를 수행하고 이 코드가 반환한 문자열로 매크로 호출 식을 치환합니다. 예를 들어 다음과 같이 SEARCH_CONDITION
매크로가 다음과 같이 정의 되어 있다면,
다음과 동등한 FoxQuery 를 사용하는 것과 같습니다. SEARCH_CONDITION
매크로 호출이 매크로 수행 결과 값으로 치환된 것을 알 수 있습니다.
실제 상황에서 유용한 동적 쿼리는 매개변수 인자 값에 따라 조건절이 동적으로 생성되는 쿼리일 것입니다. FoxQuery 매크로는 다음과 같이 매크로를 작성할 수 있습니다.
위 SEARCH_CONDITION
매크로는 사용자(개발자)가 전달한 매개변수 product_name
인자값이 null
이 아닌 경우에만 LIKE
연산자를 사용하여 조건을 추가합니다. 즉, FoxQuery를 호출하는 코드에서 매개변수 product_name
의 인자값을 어떻게 제공하느냐에 따라 쿼리가 동적으로 변경되고 수행됩니다.
env
객체¶
env
객체는 FoxQuery에 의해 매크로 스크립트에 제공되는 전역 객체 입니다. 스크립트 코드는 env
객체를 통해 FoxQuery의 <parameter>
태그(들)를 통해 정의된 매개변수들과 FoxQuery 를 호출하는 코드에서 제공된 매개변수 인자값들에 접근이 가능합니다.
Information
env
객체는 NeoDEEX.Data.Query.Script
네임스페이스의 FoxQueryScriptEnvironment
타입에 의해 구현됩니다. 이 타입은 public
타입이지만 개발자가 직접 이 타입을 이용해야할 상황은 거의 없습니다.
env.Args
컬렉션¶
매크로 스크립트 코드는 env.Args
컬렉션 객체를 통해 FoxQuery 를 호출하는 코드가 제공한 매개변수들에 접근할 수 있습니다.
Information
env.Args
컬렉션은 NeoDEEX.Data.Query.Script
네임스페이스의 FoxQueryScriptArgument
타입에 의해 구현됩니다. 이 타입은 public
타입이지만 개발자가 직접 이 타입을 이용해야할 상황은 거의 없습니다.
-
인덱서 / 속성
매개변수 인자에 접근하는 방법은 인덱서(indexer)를 사용하거나 동적 객체(dynamic object) 속성을 사용할 수 있습니다. 예를 들어, 다음과 같이 FoxQuery 를 호출하는 경우,
env.Args["param1"]
혹은env.Args.param1
는 전달된 매개변수param1
의 인자값을 읽거나 변경할 수 있습니다. 만약 주어진 이름의 매개변수 인자가 존재하지 않는 경우는null
을 반환하며 존재하지 않는 매개변수 인자에 대한 변경은 무시됩니다.Information
위 예제의 경우 매개변수 인자 객체가
Dictionary<string, object>
이지만DataRow
혹은Object
매개변수 인자에 대해서도 동일하게 작동합니다. -
매개변수 인자값들 열거
env.Args
컬렉션은 매개변수 인자들에 대한 열거 역시 제공합니다.env.Args
컬렉션은IEnumerable<string>
인터페이스를 구현하며 이를 통해 매개변수 인자 이름을 열거할 수 있습니다. 따라서 다음 매크로 스크립트는 모든 매개변수 인자를 로그에 기록할 수 있습니다. -
기타 속성/메서드
env.Args
컬렉션은 이 외에도 다음과 같은 속성/메서드를 제공하여 매크로 스크립트 작성을 용이하게 만듭니다.-
Count
속성매개변수 인자의 개수를 반환합니다.
-
ContainsKey
메서드주어진 이름의 매개변수 인자가 존재하는지 반환합니다.
Note
앞서
env.Args
컬렉션에 인덱서 혹은 동적 객체 속성을 사용할 때 존재하지 않는 매개변수 인자를 사용하는 경우null
이 반환된다고 하였습니다. 이러한 상황은 매개변수 인자값을 명시적으로null
전달한 경우를 구분할 수 없습니다. 따라서ContainsKey
메서드를 사용하면 매개변수 인자가 제공되지 않은 것인지 매개변수 인자 값이null
인지 구분이 가능합니다. -
Add
/Remove
메서드매개변수 인자 값을 추가/제거하는 메서드 입니다. 매개변수 인자가
Dictionary
인 경우에만 호출 가능하며DataRow
혹은Object
매개변수 인자에 대해 이 메서들이 호출되면InvalidOperationException
예외가 발생합니다. -
ToDictionary
메서드매개변수 인자 객체의 이름/값을 쌍으로 사용하여
Dictionary<string, object?>
객체를 생성하여 반환합니다.
-
env.Params
컬렉션¶
매크로 스트립트는 env.Params
컬렉션은 FoxQuery 에서 <parameter>
태그 를 통해 정의된 매개변수 설정들에 접근이 가능합니다.
Information
env.Params
컬렉션은 NeoDEEX.Data.Query.Script
네임스페이스의 FoxQueryScriptParameterCollection
타입에 의해 구현됩니다. 이 타입은 public
타입이지만 개발자가 직접 이 타입을 이용해야할 상황은 거의 없습니다.
-
인덱서 / 속성
<parameter>
태그에 의해 정의된 매개변수 설정에 접근하기 위해서는env.Params
컬렉션의 인덱서(indexer)를 사용하거나 동적 객체(dynamic object) 속성을 사용할 수 있습니다. 인덱서나 속성에 접근하면 매개변수 설정 정보를 담는FoxQueryParameter
객체를 반환합니다.FoxQueryParameter
객체는<parameter>
태그와 거의 1 대 1로 대응되는 엔티티 객체로 볼 수 있습니다. 예를 들어 다음과 같은 FoxQuery 정의에 대해서,매크로 스크립트의
env.Params["param1"]
이나env.Params.param1
코드는param1
매개변수 설정을 포함하는FoxQueryParameter
객체를 반환합니다. 예를 들어 다음과 같은 스크립트 코드는 로그에param1
매개변수 정보와param2
매개변수 정보를 표시합니다.위 매크로 스크립트 코드에서 주목할 부분은
param2
매개변수의Size
속성입니다.<parameter name="param2" ...>
에서size
속성이 설정되지 않았기 때문에FoxQueryParameter
객체의Size
속성도 설정되지 않습니다(null
). 즉,env.Params
컬렉션이 제공하는 DB 매개변수 정보는<parameter>
태그에서 설정된 정보 그대로 입니다.Note
<parameter>
태그에서 필수가 아닌 선택적인 속성은FoxQueryParameter
타입의 속성 역시 설정되지 않은 경우null
을 반환합니다(Nullable<T>
타입 혹은 레퍼런스 타입).매개변수 속성을 변경하는 것도 가능합니다. 하지만 이 변경 사항은 현재 수행하는 쿼리에만 적용되며 FoxQuery 자체에 적용되지 않습니다.
-
매개변수 정의들 열거
env.Params
컬렉션 역시IEnumerable<FoxQueryParameter>
인터페이스를 구현하며 이를 통해FoxQueryParameter
객체들을 열거할 수 있습니다. 따라서 다음과 같은 스크립트 코드를 작성하여 매개변수 정보를 로그에 기록할 수 있습니다. -
매개변수 추가/제거
env.Params
컬렉션은Add
메서드와Remove
메서드를 통해 동적으로 DB 매개변수를 추가/제거할 수 있습니다. 이 두 메서드를 사용하여 매크로 스크립트는 다양한 조건이나 로직에 따라서<parameters>
태그에 추가된 DB 매개변수를 제거하거나 새로운 DB 매개변수를 추가할 수 있습니다.Note
FoxQuery 는 # 문자로 둘러쌓인 DB 매개변수 이름을 발견하면
<parameter>
태그에서 DB 매개변수 정보를 찾습니다. 만약 발견되지 않으면 기본 설정을 사용하여 DB 매개변수를 자동으로 추가합니다. 따라서 기본 설정을 사용하는 DB 매개변수를 추가하기 위해env.Params
컬렉션의Add
메서드를 호출할 필요는 없습니다. 하지만 위 스크립트 코드 조각처럼dbType
,size
속성을 설정하고자 한다면 명시적으로Add
메서드를 호출하고 속성을 지정해야 합니다.
로깅 ¶
매크로 스크립트의 로깅을 위해 env
객체는 Log
속성을 제공하며 이 속성은 IFoxLog
인터페이스 입니다. 아무런 설정이 없다면 Log
속성이 나타내는 로거는 대체 로거(Fallback logger)로서 아무런 로그도 남기지 않습니다. env.Log
를 통해 로그를 기록하고자 한다면 구성 설정에서 FoxQueryMapper
설정을 수행해야 합니다. queryMappers
설정에서 scriptLogger
속성에 로거의 이름을 명시하고 logging
설정에서 해당 이름을 가진 로거를 구성하면 됩니다. 다음 구성 설정은 매크로 스크립트가 env.Log
속성을 통해 기록하는 로그가 콘솔에 출력되도록 합니다.
매크로 스크립트 코드는 C# 코드이지만 디버거를 사용하여 디버깅하는 기능은 지원되지 않습니다. 따라서 현재 유일한 디버깅 도구는 스크립트 코드에서 사용가능한 로깅 뿐입니다.
-
WriteLog
메서드env
객체는 로그를 보다 편리하게 남길 수 있도록WriteLog
메서드를 제공합니다.WriteLog
메서드는 구성된 로거에 수행되는 FoxQuery 의 고유의 쿼리 ID, 매크로 이름을 포함하여 주어진 메시지를Verbose
수준으로 기록합니다.다음은
sample3.dump_params
쿼리 ID를 가진 FoxQuery 에서DUMP_PARAMS
매크로가 기록한 로그의 예입니다. -
Trace
메서드env.Trace
메서드는 디버거에 주어진 메시지를 출력합니다.다음은 디버거에 출력되는
Trace
메시지의 예를 보여 줍니다.
헬퍼 메서드들¶
FoxQuery는 몇가지 헬퍼 메서드들을 제공하여 특정 상황에서 매크로 스크립트를 보다 편리하게 작성할 수 있도록 해 줍니다. 이들 헬퍼 메서드들은 '
문자를 문자열 앞뒤에 추가해주는 단순한 메서드들 부터 IN
절, SET
절, VALUES
절을 자동으로 작성해 주는 고급 메서드들을 포함합니다.
문자열 관련 메서드¶
-
Quoted
확장 메서드주어진 문자열 앞뒤에 SQL 문자열을 의미하는
'
문자를 추가한 문자열을 반환합니다. -
SurroundWith
확장 메서드주어진 문자열 앞/뒤를 주어진 문자로 감싼 문자열을 반환합니다. 주로
LIKE
절에%
문자를 포함하는 조건을 작성할 때 사용할 수 있습니다.
StringBuilder 관련 헬퍼 메서드¶
-
AppendQuoted
확장 메서드StringBuilder
객체에'
문자로 둘러쌓인 문자열을 추가할 때 사용합니다. -
AppendSurroundWith
확장 메서드StringBuilder
객체에 주어진 문자(열)로 둘러쌓인 문자열을 추가할 때 사용합니다. 주로LIKE
절에%
문자로 둘러 쌓인 문자열을 추가하거나 SQL 문장에 DB 매개변수를 추가할 때 사용합니다. 다음 매크로 스크립트 코드는INSERT
문장의VALUES
절에 DB 매개변수들을 추가하는 예를 보여줍니다. -
Trim
/TrimStart
/TrimEnd
확장 메서드StringBuilder
객체의 앞/뒤에서 주어진 문자(열)을 제거(trim)합니다.
SQL 헬퍼 메서드 ¶
FoxQuery 는 매크로 스크립트가 동적 SQL 문장을 작성하는데 도움이되는 추가적인 메서드들을 NeoDEEX.Data.Query.Script
네임스페이스의 FoxQueryScriptSqlHelper
클래스에 구현해 놓았습니다. 매크로 스크립트에서는 긴 이름 대신 SQL
이라는 별칭 클래스 이름을 사용하여 이들 메서드들을 호출할 수 있습니다.
IN
메서드¶
IN
메서드는 SQL IN
절을 생성해 줍니다.
첫번째 매개변수가 배열과 같은 컬렉션(IEnumerable
)인 경우 각 값들을 IN
절에 나열하고 그렇지 않은 경우 하나의 값만이 IN
절에 포함됩니다. 다음은 몇가지 IN
메서드 호출 예 입니다.
다음 FoxQuery 는 IN
메서드를 사용하여 IN
절을 생성하는 구체적인 예를 보여 줍니다.
위 FoxQuery 를 호출하는 코드는 다음과 같습니다.
위 코드와 매크로 스크립트에 의해 생성되고 수행되는 SQL 문장은 다음과 같습니다.
기본 IN
메서드는 코드로부터 전달받은 매개변수 값을 SQL 문장에 직접 삽입하기 때문에 SQL Injection 공격에 취약합니다. 또한 매개변수 값에 따라서 문자열을 나타내는 따옴표('
) 사용 여부도 처리하기 까다롭습니다. 이러한 문제점을 해결하기 위해 매개변수화된 IN
절을 생성해주는 메서드들이 제공됩니다.
이 메서드는 IN
절에 사용될 값들을 매개변수화하여 그 매개변수를 env.Params
컬렉션에 추가하며 매개변수 인자값들은 env.Args
컬렉션에 추가합니다. 다음은 몇몇 IN
메서드 호출 결과를 보여줍니다.
추가되는 매개변수는 _IN_P
라는 기본 이름에 중복 방지를 위해 숫자가 붙으며 또한 한 IN
메서드 호출 내에서 사용된 IN
절의 값 개수 만큼 추가적인 인덱스가 붙게 됩니다. 위 예제의 첫번째 IN
메서드 호출 결과로 _IN_P0_0
매개변수와 _IN_P0_1
매개변수가 env.Params
컬렉션에 추가되며 이들 매개변수 인자값으로 env.Args
컬렉션에 _IN_P0_0
이름을 가진 인자값 1 과 _IN_P0_1
이름을 가진 인자값 2가 추가됩니다.
매개변수화된 IN
절을 사용하는 예제는 다음과 같습니다. FoxQuery 는 매크로 스트립트에서 IN
메서드 호출 부분만을 변경하면 됩니다. FoxQuery 를 호출하는 코드는 변함이 없습니다.
위 FoxQuery 와 매크로에 의해 최종적으로 생성되고 수행되는 SQL 문장은 다음과 같습니다.
SET
메서드¶
SET
메서드는 IDictionary<string, object>
객체로부터 SQL UPDATE
문장의 SET
절을 생성해 주는 헬퍼 메서드 입니다.
IDictionary<string, object>
객체의 키/값은 컬럼 이름과 컬럼 값으로 사용됩니다. 다음은 SET
메서드 호출의 예를 보여줍니다.
앞서 살펴본 IN
메서드와 마찬가지로 이 메서드는 SQL Injection 공격에 매우 취약합니다. 따라서 매개변수화된 SET
절을 생성해 주는 다음 SET
메서드를 사용해야 합니다.
매개변수화를 수행하는 위 SET
메서드를 호출하면 IDictionary<string, object>
객체의 키/값은 DB 매개변수 이름과 매개변수 인자 값으로 env.Params
컬렉션과 env.Args
컬렉션에 추가됩니다. 예를 들어 다음과 같이 SET
메서드를 호출한다면,
env.Params
컬렉션에는 c0
, c1
매개변수가 추가되며, env.Args
컬렉션에는 c0
이름을 갖는 인자값 value0
와 c1
이름을 갖는 인자값 1 이 추가됩니다. 이러한 매개변수/인자값 추가는 해당 컬렉션에 DB 매개변수/인자값이 존재하지 않는 경우에만 수행됩니다.
다음은 SET
메서드를 활용하여 동적 UPDATE
를 수행하는 FoxQuery 의 예를 보여줍니다.
Note
위 SET_CLAUSE
매크로에서 col_id
를 제거하지 않으면 SET
절에 col_id
를 변경하는 코드도 작성됩니다. env.Args
컬렉션의 ToDictionary
메서드는 새로운 Dictionary<string, object>
객체를 생성하고 값을 복제하기 때문에 col_id
를 제거하는 작업은 #col_id#
DB 매개변수를 바인딩하는데 영향을 주지 않습니다.
다음 코드는 FoxQuery 를 호출하는 예를 보여 줍니다.
dynamic_update
FoxQuery 와 위 C# 코드를 통해 생성되고 수행되는 SQL 문장은 다음과 같습니다.
COLUMNS
및 VALUES
메서드¶
COLUMNS
메서드와 VALUES
메서드는 INSERT
문장에서 컬럼 목록과 VALUES
절을 생성하는데 도움을 주는 메서드 입니다. 먼저, COLUMNS
메서드는 주어진 문자열 컬렉션을 INSERT
문장에서 컬럼 목록으로 사용할 수 있는 문자열로 반환합니다.
다음은 COLUMNS
메서드와 VALUES
메서드를 호출한 결과를 보여줍니다.
앞서 IN
메서드와 SET
메서드에서 지적한 것과 동일하게 SQL Injection 위험을 피하기 위해서는 매개변수화된 메서드를 사용해야 합니다. 매개변수화를 수행하는 VALUES
메서드는 SET
메서드와 동일하게 env.Params
컬렉션과 env.Args
컬렉션에 DB 매개변수와 매개변수 인자값을 적절히 추가합니다. 다음은 COLUMNS
메서드와 VALUES
메서드를 사용하여 INSERT
를 수행하는 FoxQuery 와 이를 호출하는 C# 코드를 보여줍니다.
위 동적 쿼리에 의해 생성되고 수행되는 SQL 문장은 다음과 같습니다.
Summary¶
Fox Query 가 제공하는 동적 쿼리 기능은 스크립트 코드를 사용하는 매크로 방식 입니다. 즉 <macro>
태그와 C# 스크립트 코드를 사용하여 매크로를 작성하고, <text>
태그 내에서 이 매크로를 호출하는 방식을 사용하여 SQL 문장을 동적으로 생성/변경 할 수 있습니다.
매크로 스크립트 지원을 위해 Fox Query 는 env
객체를 통해 쿼리가 수행되는 DB 매개변수 목록(env.Params
), 호출 코드가 전달한 매개변수 인자들(env.Args
)에 대한 정보를 제공합니다. 또한 IN
, SET
, COLUMNS
, VALUES
메서드와 같은 헬퍼 메서드를 제공하여 동적 쿼리 작성을 편하게 해 줍니다. 다만, 동적 쿼리는 SQL Injection 과 같은 보안 취약점과 SQL 문장 가독성을 낮출 수 있으므로 과도한 사용을 자제해야 합니다.