본문으로 건너뛰기

dbclient

dbclient는 XML SQL 계약을 기준으로 관계형 데이터베이스 요청을 실행하는 모듈입니다. SQL Server, Oracle, MySQL/MariaDB, PostgreSQL, SQLite 데이터 원본을 설정할 수 있으며, transact에서는 CommandType=D 거래의 기본 실행 모듈로 사용합니다.

책임 범위​

  • DataSource 설정을 읽어 애플리케이션/프로젝트별 데이터베이스 연결을 관리합니다.
  • XML statement 계약을 로드하고 ApplicationID|ProjectID|TransactionID|StatementID 기준으로 실행합니다.
  • 요청 파라미터를 DB provider별 native parameter로 변환합니다.
  • Json, Scalar, NonQuery, Xml, SchemeOnly, CodeHelp 같은 반환 형식을 처리합니다.
  • SQL 실행 로그와 프로파일 로그를 설정에 따라 파일 또는 logger 모듈로 전송합니다.

주요 API​

메서드경로용도
GET/dbclient/api/query/hasstatement 계약 존재 여부를 확인합니다.
GET/dbclient/api/query/refresh계약 파일을 다시 로드합니다.
GET/dbclient/api/query/retrieve계약 원문 또는 상세 정보를 조회합니다.
GET/dbclient/api/query/meta입력/출력 메타 정보를 반환합니다.
GET/dbclient/api/query/reports계약 기반 보고용 정보를 조회합니다.
POST/dbclient/api/querySQL 계약을 실행합니다.

핵심 구현​

  • Areas/dbclient/Controllers/QueryController.cs: query API 진입점입니다.
  • DataClient/QueryDataClient.cs: 계약 해석, 파라미터 바인딩, SQL 실행 핵심 구현입니다.
  • Extensions/DatabaseMapper.cs: XML statement와 데이터 원본 매핑을 관리합니다.
  • Events/DbClientRequestHandler.cs: 모듈 내부 DB 실행 요청을 처리합니다.
  • Events/ManagedRequestHandler.cs: 운영/관리 화면에서 전달되는 데이터 원본 변경 요청을 반영합니다.
  • Profiler: provider별 DB command 실행 프로파일을 수집합니다.

결과 변환과 메모리 사용​

일반 SQL 실행은 ExecuteReader → DataTableHelper.DataReaderToDataSet → 관계/메타 처리 → JSON 변환 순서로 진행합니다. 다중 결과 집합, 열 이름·타입·문자열 길이, 행 순서와 null 처리를 유지하며, 반환된 DataSet은 호출자가 해제합니다.

IgnoreResult=true인 입력은 현재 결과 집합을 끝까지 읽고 형식을 검증하면서 후속 BaseFieldMappings에 필요한 행만 보관합니다.

반환 형식후속 매핑에 사용할 행
Json, Scalar, NonQuery, Xml마지막 행
SchemeOnly, SQLText첫 행

첫 행을 보관할 때는 검증용 행 하나를 추가로 재사용합니다. 이 경로에서 다음 결과 집합으로 직접 이동하지 않으며, NonQuery의 영향받은 행 수는 기존과 같이 첫 Read 전에 누적합니다. Reader 종료와 트랜잭션 처리는 기존 실행 경로가 담당합니다.

Form 출력은 첫 행의 값을 사용하고, Grid/Chart 출력은 모든 행의 순서와 값을 유지합니다. 내부 컬렉션 용량 지정과 불필요한 배열/테이블 생성 제거에는 별도 설정이 필요하지 않습니다. 일반 결과의 전체 DataSet 구성은 관계 처리와 메타 생성을 위해 유지됩니다.

계약과 식별자​

계약은 기본적으로 다음 위치에 둡니다.

contracts/dbclient/{ApplicationID}/{ProjectID}/{TransactionID}.xml

쿼리 ID는 다음 형식입니다.

ApplicationID|ProjectID|TransactionID|StatementID

statement ID는 보통 GD01, LD01, MD01, DD01처럼 조회, 목록, 수정, 삭제 성격이 드러나도록 나눕니다. 계약에는 DataSourceID, SQL 본문, 입력 파라미터, 반환 형식을 함께 선언합니다.

런타임 쿼리 ID의 마지막 항목은 XML의 id와 최소 두 자리로 채운 seq를 합친 값입니다. 예를 들어 id="GD01", seq="0"이면 HDS|TST|TST010|GD0100처럼 요청합니다.

JSON 배열 필터 SQL​

SQL의 WHERE 필수 조건 뒤에 <filters>를 선언하면 해당 위치에 AND (...) 조건을 생성합니다. 요청의 Filters는 객체 배열을 JSON으로 직렬화한 문자열입니다. SQL을 직접 전달하지 않습니다.

SELECT M.* FROM [Message] M WHERE M.IsDeleted = 0

<filters parameter="Filters" groupLogic="AND">
<field groupID="user" column="M.UserID" type="String" operators="eq,neq" />
<field groupID="content" column="M.MarkdownText" type="String"
operators="eq,neq,begins_with,ends_with,contains,not_contains" />
<field groupID="createdAt" column="M.CreatedAt" type="DateTime" operators="eq,neq,lte,gte" />
</filters>
<param id="@Filters" type="String" length="-1" value="NULL" />

XML 계약의 SQL 본문은 기존 예제와 같이 CDATA 안에 작성하고, <filters>는 CDATA 밖에 선언합니다. column에는 별칭을 포함한 단일 컬럼 식별자만 선언합니다. 함수, SQL 식, 정렬문은 허용하지 않습니다. 인용된 컬럼의 각 부분은 대상 DB 문법으로 변환합니다. 예를 들어 M.[Display.Name]은 SQL Server에서 그대로, Oracle·PostgreSQL·SQLite에서는 M."Display.Name", MySQL/MariaDB에서는 M.Display.Name이 됩니다. 인용하지 않은 이름은 그대로 두므로 DB의 기본 대소문자 접기 규칙이 적용됩니다. SQL 본문의 테이블명과 별칭은 이 변환의 대상이 아닙니다.

const filters = [
{ groupID: 'user', logic: 'AND', operator: 'eq', key: 'user001', value: '사용자 1' },
{ groupID: 'user', logic: 'OR', operator: 'eq', key: 'user002', value: '사용자 2' },
{ groupID: 'content', logic: 'AND', operator: 'contains', key: 'HandStack', value: 'HandStack' }
];

// DynamicObjects[].Parameters에 전달할 항목
const parameter = {
ParameterName: 'Filters', DbType: 'String', Length: -1,
Value: JSON.stringify(filters)
};

위 배열은 다음 형태의 조건과 세 개의 바인딩 매개변수로 변환합니다.

AND (
(M.UserID = @Filter_main_0 OR M.UserID = @Filter_main_1)
AND (M.MarkdownText LIKE @Filter_main_2 ESCAPE '!')
)

각 매개변수 값은 user001, user002, %HandStack%입니다. Oracle의 생성 SQL은 @ 대신 : 접두사를 사용합니다. 예시 SQL은 가독성을 위해 줄바꿈과 중복 괄호를 정리한 형태입니다.

설정의미
parameter입력 매개변수 이름. 기본값 Filters. @Filters와 같이 접두사를 붙인 입력 이름도 인식
groupLogic그룹 사이 연결. AND(기본) 또는 OR
field.logic해당 그룹을 앞 그룹과 잇는 연결자. AND 또는 OR이며 지정하면 groupLogic보다 우선. 생략하면 groupLogic을 따름
field.groupID요청 조건의 groupID와 일치하는 고유한 그룹 이름. 대소문자 구분
field.column서버 계약에서 허용하는 실제 컬럼
field.type비교할 key의 DB 타입. 기본값 String
field.operators쉼표로 구분한 허용 연산자. 기본값 eq
field.guidFormatGuid 타입의 물리 저장 형식. Default(기본), String36, String32, Binary16, Binary16BigEndian
field.booleanFormatBoolean 타입의 물리 저장 형식. Default(기본), Native, Numeric
field.timeFormatTime 타입의 물리 저장 형식. Default(기본), Native, String. Oracle은 String이 필요

지원 타입은 String, AnsiString, Int16, Int32, Int64, Decimal, Double, Single, Boolean, Date, DateTime, DateTime2, DateTimeOffset, Time, Guid입니다. 숫자는 소수점 .을 쓰는 invariant 형식입니다. Date·DateTime·DateTime2는 yyyy-MM-dd, yyyy-MM 또는 시간대 없는 yyyy-MM-ddTHH:mm:ss[.fffffff] 형식이며 Date는 시간 부분을 제거합니다. yyyy-MM은 해당 월 1일 00:00으로 해석하므로 월 전체 일치로 오해하지 않도록 eq·neq에는 사용할 수 없고 lt·lte·gt·gte에서만 허용합니다. Time은 날짜와 시간대가 없는 하루 중 시각이며 HH:mm[:ss[.fffffff]](00:00 이상 24:00 미만) 형식을 받습니다. DateTimeOffset은 2026-10-14T21:34:56.123456+09:00처럼 명시적인 오프셋이나 Z가 필요하며 UTC로 정규화합니다. 서버의 로컬 시간대에 의존하는 날짜 변환은 하지 않습니다. Boolean은 true/false 또는 1/0을 받습니다.

연산자생성 조건
eq, neq=, <>
lt, lte<, <=
gt, gte>, >=
begins_withLIKE '값%'에 해당하는 매개변수
ends_withLIKE '%값'에 해당하는 매개변수
containsLIKE '%값%'에 해당하는 매개변수
not_containsNOT LIKE '%값%'에 해당하는 매개변수. 컬럼이 NULL인 행도 포함하며((컬럼 IS NULL OR 컬럼 NOT LIKE ...)) neq(<>)가 NULL 행을 제외하는 것과 다릅니다

문자열 검색은 String·AnsiString 타입에서 지원합니다. %, _, !는 실제 문자로 검색하도록 이스케이프하며, SQL Server의 [도 처리합니다. contains는 FREETEXT 검색과 다릅니다. 값의 작은따옴표는 SQL에 치환하지 않고 그대로 바인딩합니다.

  • 매개변수 누락, 실제 null, 빈 문자열, 공백 문자열, JSON null, []는 조건을 추가하지 않습니다.
  • 잘못된 JSON, 배열이 아닌 값, 미등록 그룹, 허용되지 않은 연산자, 타입 변환 실패, 같은 그룹·키의 중복 조건은 오류입니다. SQL 하나에서 생성할 수 있는 필터 조건은 최대 1000개입니다.
  • 같은 그룹은 배열에 나타난 순서대로 결합하고 첫 항목의 logic은 사용하지 않습니다. A OR B AND C는 선택 순서에 따라 ((A OR B) AND C)로 처리합니다. 그룹 전체를 다시 괄호로 묶으므로 OR로 기존 필수 조건을 우회하지 않습니다.
  • 그룹 사이는 groupLogic으로 결합합니다. field에 logic을 지정하면 그 그룹을 앞 그룹과 잇는 연결자로 groupLogic보다 우선 적용합니다. 그룹 순서는 field 선언 순서가 아니라 요청 배열에서 그룹이 처음 나타난 순서이며, 첫 그룹의 field.logic은 사용하지 않습니다. 하나라도 field.logic이 적용되면 결합할 때마다 괄호로 묶어 (((A) OR (B)) AND (C))처럼 요청 순서대로 해석합니다. 그룹 안의 결합은 field.logic과 무관하게 요청 조건의 logic을 따르며, AND/OR 외의 값은 오류입니다.
  • key가 비교값입니다. DropDownTextList의 value는 표시명이므로 SQL 생성에 사용하지 않습니다. groupID는 UI 분류를 자동으로 SQL 컬럼으로 바꾸는 값이 아니라, 계약에서 등록한 검색 필드의 식별자입니다.
  • 기간은 같은 그룹에 시작 gte와 끝 lt를 AND로 보내 표현합니다. 끝을 포함하려면 끝 날짜·월·분의 다음 값을 lt로 보냅니다(예: 10월 전체는 gte 2026-10과 lt 2026-11). 시각만 비교하는 Time의 끝이 23:59이면 다음 분이 없으므로 lte 23:59:59.9999999를 사용합니다. DropDownTextList의 range 옵션이 이 조건 쌍을 만듭니다.
  • 원본 Filters JSON은 DB로 바인딩하지 않습니다. 생성한 매개변수는 요청별로 관리하고 기존 StatementMap·QueryObject는 변경하지 않습니다. 생성 이름 Filter_main_*, Filter_pre_*를 업무 매개변수로 사용하면 충돌 오류가 발생합니다.
  • <if>·<foreach> 내부와 <pretreatment>에서도 사용할 수 있습니다. 선언 위치가 실제로 렌더링될 때만 해당 필터를 처리합니다.
  • 기존 코드에서 DatabaseMapper.Find를 직접 호출하면서 활성 필터를 사용한다면 (statementMap, queryObject, dynamicParameters, databaseProvider) 오버로드를 사용합니다. FindPretreatment도 같은 추가 인자를 받습니다.

예제는 Contracts/dbclient/HDS/TST/TST010.xml의 GD06, GD08, GD09입니다. GD06은 기존 SQL Server 업무 쿼리를 유지하며, GD08은 SQLite의 상수 데이터만 조회하므로 테이블 생성이나 데이터 변경 없이 실행할 수 있습니다. GD09도 SQLite 상수 데이터로 createdAt(DateTime), closingMonth, workTime(Time) 기간 조건(gte, lt, lte)을 검증합니다. 예를 들어 [{"groupID":"createdAt","logic":"AND","operator":"gte","key":"2026-10","value":"2026-10 부터"},{"groupID":"createdAt","logic":"AND","operator":"lt","key":"2026-11","value":"2026-10 까지"}]는 Id 2, 3, 4를 반환합니다.

공급자별 SQL·바인딩 규칙​

일반 실행과 NativeDataClient 모두 실제 공급자의 DbParameter를 생성합니다. Oracle은 BindByName=true를 설정하므로 필터 매개변수와 기존 매개변수가 추가된 순서에 영향을 받지 않습니다. 이름은 모든 공급자에서 유효하도록 영문자로 시작합니다.

논리 타입/기능SQL ServerOracleMySQL/MariaDBPostgreSQLSQLite
매개변수@Filter_main_0:Filter_main_0@Filter_main_0@Filter_main_0@Filter_main_0
인용 식별자[Name]"Name"`Name`"Name""Name"
StringNVarCharNVarchar2VarCharTextString
AnsiStringVarCharVarchar2VarCharTextAnsiString
BooleanbitNUMBER(1)에 0/1Boolean/tinyintboolean0/1
DatedateDATEDATEdate연결의 날짜 저장 형식
DateTime / DateTime2datetime2TIMESTAMPDATETIMEtimestamp without time zone연결의 날짜 저장 형식
Timetime지원 안 함(timeFormat="String" 필요)TIMEtimeHH:mm:ss 문자열
DateTimeOffsetUTC datetimeoffsetUTC TIMESTAMP WITH TIME ZONEUTC 시각의 DATETIMEUTC timestamptzUTC 시각, 연결의 날짜 저장 형식
기본 GuiduniqueidentifierRAW(16), .NET 바이트 순서연결의 GuidFormatuuid연결의 BinaryGUID 설정
Double / SingleFloat / RealBINARY_DOUBLE / BINARY_FLOATDouble / Floatdouble precision / realDouble / Single

Int16·Int32·Int64·Decimal도 각 드라이버의 대응 숫자 타입으로 바인딩합니다. MySQL과 MariaDB는 기존 dbclient와 동일한 MySqlConnector 경로를 사용합니다.

GUID 컬럼의 물리 형식이 기본값과 다르면 계약에서 지정합니다.

<field groupID="id" column="M.ItemID" type="Guid" guidFormat="String36" operators="eq,neq" />
  • String36: 하이픈을 포함한 소문자 UUID 문자열, 예: 00112233-4455-6677-8899-aabbccddeeff.
  • String32: 하이픈 없는 소문자 32자리 문자열.
  • Binary16: Guid.ToByteArray()의 .NET 바이트 순서. 위 UUID는 33221100554477668899AABBCCDDEEFF.
  • Binary16BigEndian: RFC UUID 바이트 순서. 위 UUID는 00112233445566778899AABBCCDDEEFF. Oracle HEXTORAW로 이 순서를 저장했다면 이 형식을 지정합니다.

Time 컬럼의 물리 형식이 기본값과 다르면 계약에서 지정합니다.

<field groupID="workTime" column="M.WorkTime" type="Time" timeFormat="String" operators="gte,lt,lte" />
  • Default: SQL Server·MySQL/MariaDB·PostgreSQL은 time 컬럼과 직접 비교하는 TimeSpan 매개변수, SQLite는 HH:mm:ss 문자열 매개변수. Oracle은 시각만 저장하는 타입이 없으므로 Default와 Native를 오류로 처리합니다.
  • Native: TimeSpan 매개변수를 time 타입으로 바인딩합니다. Oracle에서는 사용할 수 없습니다.
  • String: 고정 폭 HH:mm:ss 문자열(소수 초가 있으면 .fffffff의 끝 0을 제외해 이어 붙임)로 바인딩해 VARCHAR2·TEXT 컬럼의 문자열 순서로 비교합니다. 저장 값이 HH:mm:ss 형식이어야 정확히 비교됩니다.

Oracle의 빈 문자열 key는 eq일 때 IS NULL, neq일 때 IS NOT NULL로 변환하며 DB 매개변수를 추가하지 않습니다. 빈 문자열의 lt·lte·gt·gte는 오류이고 문자열 검색은 % 패턴 규칙을 따릅니다. Filters 매개변수 자체가 비어 조건을 무시하는 동작과는 별개입니다.

Oracle의 기본 MAX_STRING_SIZE=STANDARD와 두 국가 문자셋을 함께 고려해 String은 2000 UTF-16 코드 단위 및 CESU-8 4000바이트 이내, AnsiString은 UTF-8 4000바이트 이내로 제한합니다. SQL Server의 LIKE 패턴은 이스케이프·와일드카드를 포함해 Unicode 4000자 또는 ANSI UTF-8 8000바이트 이내입니다. 이보다 긴 검색은 오류로 처리하며 자동으로 잘라내거나 LOB로 바꾸지 않습니다. NUL 문자는 모든 공급자에서 거부합니다. Unicode 검색에는 String을 사용하고 MySQL/MariaDB 연결·컬럼의 문자셋도 UTF-8에 맞춥니다.

서버 실행 결과의 대소문자·악센트 비교는 컬럼 collation, 날짜의 정밀도는 실제 컬럼 precision, NULL 비교는 DB 규칙을 따릅니다. MySQL/MariaDB·SQLite에서 DateTimeOffset을 검색하는 컬럼은 UTC 시각을 저장해야 합니다. SQLite의 DateTimeFormat·BinaryGUID, MySQL/MariaDB의 GuidFormat은 기존 데이터 저장 형식과 일치해야 합니다. Oracle의 기본 Boolean은 19c/21c와도 호환되는 숫자 컬럼을 기준으로 합니다. 23c 이상 SQL BOOLEAN 컬럼은 booleanFormat="Native"로 지정합니다. 다른 공급자의 기본값은 네이티브 Boolean이며, 정수 0/1 컬럼을 사용한다면 booleanFormat="Numeric"을 지정합니다. 전체 업무 SQL을 다른 DB 문법으로 번역하거나 collation·저장 형식을 자동 변경하는 기능은 아닙니다.

Boolean 컬럼에 맞춘 선언 예시는 다음과 같습니다. 허용 연산자는 업무 의미에 맞게 eq,neq로 제한할 수 있습니다.

<!-- Oracle 23c 이상 SQL BOOLEAN 컬럼 -->
<field groupID="enabled" column="M.Enabled" type="Boolean" booleanFormat="Native" operators="eq,neq" />
<!-- 숫자 0/1 컬럼 -->
<field groupID="enabled" column="M.Enabled" type="Boolean" booleanFormat="Numeric" operators="eq,neq" />

공급자별 실행 예제​

Contracts/dbclient/HDS/TST/TST011.xml은 공급자별 상수 데이터만 조회하는 계약입니다. 기존 테이블을 조회하거나 생성·변경하지 않습니다. 새 dbclient DLL과 계약을 배포한 뒤 데이터 원본을 구성하고 실행합니다. 각 statement는 POST /dbclient/api/query 요청의 DynamicObjects[].QueryID로 실행합니다.

QueryID 끝부분대상계약의 datasource
GD0100SQL ServerDB02
GD0200OracleDB03
GD0300MySQL 또는 MariaDBDB04
GD0400PostgreSQLDB05
GD0500SQLiteDB01

테스트 전용 DataSource를 추가하고 필요하면 각 statement의 datasource를 해당 ID로 바꿉니다. fixture의 MySQL/MariaDB는 GuidFormat=Char36, SQLite는 DateTimeFormat=ISO8601을 사용합니다. 기존 업무 데이터 원본의 저장 설정을 바꾸지 않습니다.

예를 들어 Oracle 계약은 다음 요청으로 실행합니다. 접근 제어 설정에 따라 AuthorizationKey 헤더를 지정합니다.

{
"Action": "SYN",
"ReturnType": 0,
"DynamicObjects": [
{
"QueryID": "HDS|TST|TST011|GD0200",
"JsonObject": 1,
"Parameters": [
{
"ParameterName": "Filters",
"DbType": "String",
"Length": -1,
"Value": "[{\"groupID\":\"user\",\"logic\":\"AND\",\"operator\":\"eq\",\"key\":\"user001\",\"value\":\"사용자 1\"}]"
}
]
}
]
}

위 요청은 user001 한 행을 반환합니다. Filters의 Value를 "[]", null, 빈 문자열로 바꾸거나 매개변수를 생략하면 삭제되지 않은 user001·user002 두 행을 반환합니다. 다른 공급자는 위 표에 맞춰 QueryID를 변경합니다. MariaDB는 MySQL과 별도의 연결로 같은 GD0300 계약을 사용합니다.

Native 실행은 테스트 계약 statement의 native="true" 설정을 적용한 뒤 같은 요청을 보냅니다. 실제 업무 테이블에 연결할 때는 collation, 문자열 길이, 날짜 소수초 정밀도, GUID 저장 형식도 대조합니다.

공급자 규칙의 근거: Oracle BindByName, Oracle Unicode 크기, Npgsql 날짜 매핑, MySqlConnector 지원 타입, SQL Server LIKE, MariaDB LIKE, SQLite 표현식.

SQLite 기본 계약 · 공급자별 실행 계약

주요 설정​

설정설명
ContractBasePathdbclient XML 계약 루트 목록입니다.
DataSource애플리케이션/프로젝트별 DB provider와 연결 문자열입니다.
DefaultCommandTimeoutstatement의 timeout이 음수일 때 적용되는 기본 SQL 제한 시간입니다. 생략하거나 파싱에 실패하면 현재 매퍼는 0을 사용합니다.
IsContractFileWatching계약 파일 변경 감시 여부입니다.
IsTransactionLogging요청/응답 거래 로그 기록 여부입니다.
IsProfileLoggingDB provider 호출 프로파일 로그 기록 여부입니다.
SubscribeAction기본 수신 이벤트는 dbclient.Events.DbClientRequest, dbclient.Events.ManagedRequest입니다.

DataSource 예시는 다음과 같습니다.

{
"ApplicationID": "HDS",
"ProjectID": "*",
"DataSourceID": "DB01",
"DataProvider": "SQLite",
"ConnectionString": "URI=file:../sqlite/HDS/dbclient/HDS.db;Journal Mode=Off;BinaryGUID=False;DateTimeFormat=Ticks;Version=3;",
"IsEncryption": "N"
}

ack 또는 rdy의 AppSettings:IsConfigurationWatching이 true이면 실행 중 실제로 로드된 module.json의 DataSource 변경을 감지해 DatabaseMapper.DataSourceMappings를 다시 구성합니다. 새 목록 전체가 유효할 때만 기존 캐시를 교체하며, 오류가 있으면 이전 설정과 캐시를 유지합니다. 교체 완료 후 시작하는 요청부터 새 연결 정보를 사용하고 빈 배열은 캐시 삭제로 반영됩니다.

transact 연동​

transact 거래 계약에서는 CommandType을 D로 둡니다.

{
"ServiceID": "LD01",
"CommandType": "D",
"ReturnType": "Json"
}

라우팅 예시는 다음과 같습니다.

{
"HDS|*|D|D": "http://localhost:8421/dbclient/api/query",
"HDS|*|D|P": "http://localhost:8421/dbclient/api/query",
"HDS|*|D|T": "http://localhost:8421/dbclient/api/query"
}

운영 주의사항​

  • 연결 문자열과 DB 계정은 환경별 설정으로 분리하고 실제 비밀번호를 공개 저장소에 두지 않습니다.
  • IsProfileLogging은 성능 분석에는 유용하지만 SQL과 파라미터가 남을 수 있으므로 운영에서는 목적과 보관 기간을 명확히 합니다.
  • 계약 파일 hot reload를 사용할 때는 변경 배포와 refresh 시점을 운영 절차에 포함합니다.
  • SQL provider별 파라미터 타입과 날짜/숫자 변환 규칙이 다르므로 계약 작성 후 실제 provider에서 검증합니다.