Skip to main content

Draft, Context

Function Node의 코드는 (draft, context) => {...} 형태의 함수로 작성합니다. 이 문서는 코드 안에서 사용하는 draft, context 두 파라미터의 구조와 제공되는 함수/객체를 설명합니다.

draft 객체 구조

draft는 response, json 두 개의 하위 속성을 가집니다.

  • draft.response: Flow의 최종 HTTP 응답을 구성할 때 사용하는 속성입니다.
    • statusCode: 응답 상태 코드입니다. 기본값으로 200을 가지고 있으므로 매번 값을 부여할 필요는 없으며, 오류가 발생했을 때나 200 이외의 값으로 수동으로 대체해야 할 때만 부여하면 됩니다.
    • body: 응답 본문입니다. 기본값으로 빈 오브젝트를 가지고 있으므로 새로운 오브젝트 유형의 값을 통째로 부여하는 것은 권장하지 않습니다. 그렇게 하면 기존에 쌓인 body 값을 의도치 않게 덮어써 유실될 수 있으므로, 필요한 속성만 개별적으로 채워주세요.
    • headers: 응답 헤더입니다. 기본값으로 Content-Type 등의 값을 가지고 있으므로 꼭 필요한 경우에만 부여하면 됩니다.
  • draft.json: Node 간에 자유롭게 전달할 데이터를 담는 속성입니다. 구조에 제약이 없으며, 이전 Node에서 저장한 값을 이후 Node에서 읽거나 가공할 수 있습니다.
module.exports = async (draft, context) => {
draft.json.ifObj = { id: 1 };

// 기본값(200)을 사용하는 경우 별도로 부여하지 않아도 됩니다.
draft.response.statusCode = 500;
// body 전체를 대체하지 않고 필요한 속성만 채워야 기존 값 유실을 방지할 수 있습니다.
draft.response.body.result = "ok";
};

context가 제공하는 함수/객체

context에는 log, fn, request 외에도 Function Node 안에서 바로 사용할 수 있도록 미리 만들어진 함수와 객체들이 많이 포함되어 있습니다. 각 함수의 설명과 파라미터, 예제 코드는 아래를 참고하세요.

사용자/조직 정보

/**
* context.user - 함수가 아닌 데이터입니다. Flow를 실행시킨 사용자 정보를 담고 있습니다.
* @typedef {Object} ContextUser
* @property {string} key
* @property {string} id
* @property {string} name
* @property {string} email
* @property {string} bukrs
* @property {string} ekgrp
*/

/**
* context.orgn - 함수가 아닌 데이터입니다. 현재 Partner/System의 조직도(트리) 정보를 담고 있습니다.
* @typedef {Object[]} ContextOrgn
*/
/**
* 특정 사용자 1명의 정보를 조회합니다.
* @param {string} userID - 조회할 사용자 ID
* @param {Object} [options]
* @param {string[]} [options.fields] - 조회할 필드 목록, 제공하지 않으면 모든 사용자 속성을 리턴합니다
* @returns {Promise<Object>} 사용자 정보
*/

// 예제
const userInfo = await context.getUser('U0001', { fields: ['name', 'email'] });
/**
* 사용자 목록을 조회합니다.
* @param {Object} [options]
* @param {string[]} [options.fields]
* @param {string[]} [options.id] 조회하고자 하는 아이디를 배열로 제공, 제공하지 않으면 모든 사용자 리스트를 리턴합니다
* @returns {Promise<Object[]>} 사용자 목록
*/

// 예제
const users = await context.getUsers({ fields: ['id', 'name'] });
/**
* 메뉴 / 역할(Role) 목록을 조회합니다. (context.getMenus, context.getRoles 동일한 구조)
* @param {Object} [options]
* @param {string[]} [options.fields]
* @param {string[]} [options.id] 조회하고자 하는 아이디를 배열로 제공, 제공하지 않으면 모든 리스트를 리턴합니다
* @returns {Promise<Object[]>}
*/

// 예제
const menus = await context.getMenus();
const roles = await context.getRoles();
/**
* 특정 결재(Workflow)의 결재선(요청자/승인자/참조자)을 조회합니다.
* @param {string} wfId - 결재 ID
* @returns {Promise<Array<{
* id: string,
* name: string,
* seq: number,
* role: 'REQUESTER' | 'APPROVER' | 'REVIEWER'
* }>>}
*/

// 예제
const approvalLine = await context.getListappr('WF0001');
/**
* 사용자를 생성/수정합니다. (context.createUser, context.updateUser 동일한 구조)
* @param {string} id - 사용자 ID
* @param {Object} [options]
* @param {string} [options.name]
* @param {string} [options.rolesKey]
* @param {*} [options["..."]] - 그 외 사용자 속성
* @returns {Promise<Object>}
*/

// 예제
await context.createUser('U0002', { name: '홍길동', rolesKey: 'ROLE01' });
await context.updateUser('U0002', { name: '홍길동2' });

외부 시스템 연동

/**
* OData 서비스를 호출합니다. (context.odata.get / post / patch / delete 동일한 구조)
* @param {Object} options
* @param {string} options.url - 호출할 OData URL
* @param {Object} [options.headers] - 추가 헤더
* @param {string} [options.username] - Basic 인증 사용자명
* @param {string} [options.password] - Basic 인증 비밀번호
* @param {Object} [options.body] - 요청 본문
* @param {boolean} [options.includeHeaders=false] - true이면 statusCode/headers까지 포함하여 반환
* @returns {Promise<Object>}
*/

// 예제
const result = await context.odata.get({
url: 'https://example.com/odata/MyEntity',
headers: { 'sap-client': '100' },
});
/**
* SOAP(RFC 기반 웹서비스)를 호출합니다.
* @param {Object} options
* @param {string} options.wsdlID - "파일ID:버전" 형태
* @param {string} options.operation - 호출할 오퍼레이션명
* @param {Object} options.payload - 오퍼레이션 파라미터
* @param {string} [options.language] - sap-language
* @param {string} [options.username] - username 인증 시 사용자명 (지정 시 p12 인증 생략)
* @param {string} [options.password] - username 인증 시 비밀번호
* @param {string} [options.p12ID] - "파일ID:버전" 형태, p12 인증서 인증 시 사용 (username 미지정 시 필수)
* @param {string} [options.tenantID] - p12ID 대신 테넌트 인증서를 사용할 때
* @returns {Promise<{ statusCode: number, body: * }>}
*/

// 예제
const result = await context.soap({
wsdlID: 'MY_WSDL:1',
operation: 'ZFM_TEST',
payload: { IV_PARAM: '1' },
});
/**
* 일반 REST API를 호출합니다. (context.restApi.get / post / patch / delete 동일한 구조)
* @param {Object} options
* @param {string} options.url
* @param {Object} [options.headers]
* @param {string} [options.username] - Basic 인증 사용자명
* @param {string} [options.password] - Basic 인증 비밀번호
* @param {*} [options.body]
* @returns {Promise<Object>}
*/

// 예제
const result = await context.restApi.post({
url: 'https://api.example.com/orders',
body: { id: 1 },
});
/**
* context.ftp - SFTP 서버에 접속하여 파일을 다루는 함수 모음입니다.
* @property {(config: {
* host: string,
* port?: number,
* username: string,
* password: string
* }) => Promise<{ client: Object }>} connect
* @property {(client: Object, path: string) => Promise<{ results: Object[], client: Object }>} list
* @property {(client: Object, path: string) => Promise<{ results: Object, client: Object }>} stat
* @property {(client: Object, path: string) => Promise<{ results: boolean, client: Object }>} exists
* @property {(client: Object, path: string) => Promise<{ results: *, client: Object }>} delete
* @property {(client: Object, content: string | Buffer, path: string) => Promise<{ client: Object }>} upload
* @property {(
* client: Object,
* pathList: string[],
* callback?: (buffer: Buffer, meta: { path: string, index: number }) => Promise<*>
* ) => Promise<{ results: Array<{ path: string, result?: *, error?: Error }>, client: Object }>} copy
*/

// 예제
const { client } = await context.ftp.connect({ host: 'sftp.example.com', username: 'user', password: 'pass' });
await context.ftp.upload(client, 'hello', '/upload/hello.txt');
/**
* SAP RFC 함수를 호출합니다.
* @param {string} functionName - RFC 함수명
* @param {Object} parameters - RFC 파라미터
* @param {Object} connectionInfo - RFC 접속 정보 (ashost, sysnr, client, user, passwd 등)
* @param {Object} [options]
* @param {boolean} [options.gzipRequest] - 요청 파라미터 압축 전송 여부, Request 크기가 5MB 초과할 때 사용
* @param {string} [options.version] - RFC 함수 모듈 버전 (예: '750')
* @returns {Promise<Object>}
*/

// context.rfc.testConnection(connectionInfo, options) 은 connectionInfo 정보만으로 접속 테스트를 수행합니다.

// 예제
const connectionInfo = {
"ashost": "10.22.61.252",
"sysnr": "00",
"client": "100",
"user": "RFC_FI",
"passwd": "abcdef",
"lang": "ko",
"codepage": "4103"
}
const result = await context.rfc.invoke('ZFM_TEST', { IV_PARAM: '1' }, connectionInfo, { version: "750" });

AI/문서 처리

/**
* 사내 GenAI 에이전트에게 질의합니다.
* @param {Object} options
* @param {string} [options.model] - 사용할 모델명
* @param {string} options.question - 질문 내용
* @param {boolean} [options.isFunctionCall]
* @param {string} [options.image] - 이미지 URL (멀티모달 질의 시)
* @param {boolean} [options.isRag] - RAG 사용 여부
* @param {string} [options.sessionId] - 대화 세션 유지용 ID
* @param {string} [options.defId]
* @param {boolean} [options.schemaIt]
* @param {boolean} [options.getModels] - 사용 가능한 모델 목록만 조회
* @returns {Promise<Object>}
*/

// 예제
const answer = await context.genai({ question: '안녕' });
/**
* 문서 인식(OCR/AI) 처리를 요청합니다.
* @param {Object} options
* @param {string} options.processorId - 사용할 프로세서 ID
* @param {string} options.imageUrl - 인식할 이미지/문서 URL
* @param {string} [options.aiType]
* @returns {Promise<Object>}
*/

// 예제
const result = await context.docuAi({ processorId: 'p1', imageUrl: 'https://.../invoice.png' });
/**
* 클라우드 셀프서비스 기능을 호출합니다.
* @param {Object} options
* @param {string} options.action - 실행할 액션명
* @param {Object} [options.params] - 액션 파라미터
* @returns {Promise<Object>}
*/

// 예제
const result = await context.cloud({ action: 'someAction', params: {} });

메시징

/**
* 메일을 발송합니다. mailType에 따라 2단계로 호출하는 함수입니다: context.email.send(mailType)(payload)
* @param {'Template' | 'Text'} mailType
* @returns {(payload: TemplatePayload | TextPayload) => Promise<Object>}
*/

/**
* @typedef {Object} TemplatePayload
* @property {string} name - 사용할 메일 템플릿명
* @property {Object} data - 템플릿에 전달할 데이터
* @property {string} fromAddress
* @property {string[]} toAddresses
*/

/**
* @typedef {Object} TextPayload
* @property {string} fromAddress
* @property {string[]} toAddresses
* @property {Object} data
* @property {string} data.subject
* @property {string} data.text
*/

// 예제
await context.email.send('Template')({
fromAddress: 'noreply@example.com',
toAddresses: ['a@example.com'],
name: '템플릿명'
data: { subject: '제목', text: '본문' },
});
await context.email.send('Text')({
fromAddress: 'noreply@example.com',
toAddresses: ['a@example.com'],
data: { subject: '제목', text: '본문' },
});
/**
* 발송된 메일의 결과를 조회합니다.
* @param {Object} payload
* @param {string} payload.messageId
* @returns {Promise<Object>}
*/

// 예제
await context.email.get({ messageId })
/**
* 메일 템플릿을 관리합니다.
* @param {Object} payload - context.email.template.upload(payload)
* @param {string} payload.name
* @param {Object} payload.data
* @param {string} payload.data.subject
* @param {string} payload.data.text
* @param {string} payload.data.html
* @param {Object} [payload.tags]
* @returns {Promise<Object>}
*/

// 예제
await context.email.template.upload({
name: "템플릿명",
data: {
subject: "",
text: "",
html: ""
}
})

/**
* @param {Object} payload - context.email.template.get(payload)
* @param {string} payload.name
* @returns {Promise<Object>}
*/

// 예제
await context.email.template.get({
name: "템플릿명"
})

await context.email.template.list() // 파라미터 없이 전체 템플릿 목록 조회
/**
* 발신자를 등록/조회합니다. (context.email.sender.create / get 동일한 구조)
* @param {Object} payload
* @param {string} payload.email
* @returns {Promise<Object>}
*/

// 예제
await context.email.sender.create({ email: 'sender@example.com' });
/**
* 어플 푸시/알림을 발송합니다.
* @param {Object} payload
* @param {string} payload.opType
* @param {string} payload.topic
* @param {string} [payload.msgTitle]
* @param {string} [payload.msgBody]
* @param {Object} [payload.msgData]
* @param {string[]} [payload.tokens]
* @param {boolean} [payload.useCustomerRole=false]
* @returns {Promise<Object>}
*/

// 예제
await context.notification.send({ opType: 'PUSH', topic: 'TOPIC1', msgTitle: '알림', msgBody: '내용' });

Task

/**
* Task를 생성합니다. (context.task.createV2)
* @param {Object} params
* @param {string} params.Id
* @param {string} params.FlowId
* @param {string} params.FlowQualifier
* @param {Object} params.Payload
* @param {string} params.RunId
* @param {string} params.BatchId
* @param {string} params.TableId
* @param {string} params.DatasetId
* @returns {Promise<Object>}
*/

// 예제
await context.task.createV2({
Id: 'G1',
FlowId: 'FLOW01',
FlowQualifier: request.stage,
Payload: { foo: 'bar' }
});
/**
* Task 실행 이력을 조회합니다. (context.task.getRun)
* @param {Object} params
* @param {string} params.Id
* @param {string} params.RunId
* @returns {Promise<Object>}
*/

// 예제
await context.task.getRun({
Id: "G1",
RunId: ""
})
/**
* 예약 실행(Cron)을 등록합니다. (context.task.addSchedule)
* @param {string} groupID
* @param {string} flowID
* @param {string} flowQualifier
* @param {string} cron - Cron 표현식
* @param {Object} payload
* @returns {Promise<Object>}
*/

// 예제
await addSchedule("G1", "Flow ID", request.stage, cron, payload)

/**
* @param {string} groupID - context.task.deleteSchedule(groupID)
* @returns {Promise<Object>}
*/

// 예제
await deleteSchedule(groupID)

/**
* @param {string} groupID - context.task.listSchedules(groupID)
* @returns {Promise<Object[]>}
*/

// 예제
await listSchedules(groupID)

DB/캐시/파일

/**
* RDB(mysql/oracle/mssql 등)에 접근하는 knex(https://knexjs.org/) 쿼리 빌더를 반환합니다.
* @param {'mysql' | 'oracle' | 'mssql' | string} [dbType='mysql']
* @param {Object} [options]
* @param {string} [options.database]
* @param {string} [options.host]
* @param {string} [options.port]
* @param {string} [options.user]
* @param {string} [options.password]
* @param {string} [options.ns] NameServer IP Address
* @param {string} [options.rrtype] NameServer RR Type - 'A' or 'CNAME'
* @param {string} [options.stage] 제공하지 않으면 endpoint stage를 사용
* @param {Function} [options.wrapIdentifier]
* @returns {import('knex').Knex} knex 쿼리 빌더/실행기
*/

// 예제 (Select)
const db = context.sql('mysql');
const query = db.select(tableName).where('id', 'abc_value');
// query의 메서드 함수들을 knex의 query builder를 참조
const rows = await query.run();

/**
* rows 즉 query.run()의 리턴값
* @param {number} statusCode
* @param {Object} body
*/
/**
* INSERT 쿼리 빌더를 생성합니다.
* @param {string} table - 테이블명
* @param {Object|Object[]} [data] - 저장할 컬럼/값 (생략 시 이후 knex의 .insert()로 직접 지정 가능)
* @returns {import('knex').Knex.QueryBuilder & { run: () => Promise<{ statusCode: number, body: Object }> }}
*/

// 예제 (Insert)
const db = context.sql('mysql');
const query = db.insert(tableName, { id: 'abc_value', name: '홍길동' });
const result = await query.run();
/**
* UPDATE 쿼리 빌더를 생성합니다. where 등 조건절은 knex의 query builder 메서드로 이어서 작성합니다.
* @param {string} table - 테이블명
* @param {Object} [data] - 수정할 컬럼/값 (생략 시 이후 knex의 .update()로 직접 지정 가능)
* @returns {import('knex').Knex.QueryBuilder & { run: () => Promise<{ statusCode: number, body: Object }> }}
*/

// 예제 (Update)
const db = context.sql('mysql');
const query = db.update(tableName, { name: '홍길동2' }).where('id', 'abc_value');
const result = await query.run();
/**
* 여러 쿼리를 하나의 요청으로 묶어(Multi) 순차 실행합니다. add()로 추가한 각 쿼리는 select/insert/update 등
* knex query builder를 그대로 사용할 수 있습니다.
* @param {string} table - 기본으로 사용할 테이블명 (각 add 콜백 내부에서 다른 테이블로 교체 가능)
* @param {Object} [mOptions]
* @param {boolean} [mOptions.force] - 일부 쿼리 실패 시에도 나머지 쿼리를 계속 실행할지 여부
* @param {boolean} [mOptions.gzipRequest] - 요청 파라미터 압축 전송 여부
* @returns {{
* add: (callback: (this: import('knex').Knex.QueryBuilder) => (import('knex').Knex.QueryBuilder | void)) => void,
* run: () => Promise<{ statusCode: number, body: Object[] }>
* }}
*/

// 예제 (Multi)
const db = context.sql('mysql');
const query = db.multi(tableName);
query.add(function () {
return this.update({...}).where('id', 'abc_value');
});
query.add(function () {
return this.table('other_table').insert({ id: 'new_value' });
});
const result = await query.run();
/**
* context.dynamodb - DynamoDB 테이블에 접근하는 함수 모음입니다.
* @typedef {Object} DynamodbOptions
* @property {boolean} [useCustomerRole=true] - 고객사 IAM Role 사용 여부
* @property {boolean} [toDev=false] - 개발(Dev) 계정의 Role 사용 여부
* @property {boolean} [useExactTableName] - table 인자를 가공 없이 그대로 사용
*
* @param {string} table - 테이블명
* @param {Object} keys - Partition/Sort Key
* @param {DynamodbOptions} [options]
* @returns {Promise<Object>} context.dynamodb.getItem(table, keys, options)
*/

/**
* @param {string} table
* @param {Object} keys
* @param {Object} values - 저장/수정할 값
* @param {DynamodbOptions} [options]
* @returns {Promise<Object>} context.dynamodb.insertItem / updateItem (table, keys, values, options)
*/

/**
* @param {string} table
* @param {Object} keys
* @param {DynamodbOptions} [options]
* @returns {Promise<Object>} context.dynamodb.deleteItem(table, keys, options)
*/

/**
* @param {string} table
* @param {Object} partitionKey
* @param {Object} [sortKey]
* @param {DynamodbOptions} [options]
* @returns {Promise<Object[]>} context.dynamodb.query(table, partitionKey, sortKey, options)
*/

/**
* @param {string} table
* @param {Object[]} items - 트랜잭션/배치 아이템 목록
* @param {DynamodbOptions} [options]
* @returns {Promise<Object>} context.dynamodb.transaction / batchGetItem (table, items, options)
*/

// 예제
const item = await context.dynamodb.getItem('MY_TABLE', { id: '1' });
/**
* context.athena - Athena 쿼리를 실행합니다.
* @param {string} statement - 실행할 SQL
* @param {string} dbName - 대상 DB명
* @param {Object} [options]
* @param {boolean} [options.useCustomerRole]
* @returns {Promise<{ queryId: string }>} context.athena.startQuery(statement, dbName, options)
*/

/**
* @param {string} queryId
* @param {Object} [options]
* @param {boolean} [options.useCustomerRole]
* @returns {Promise<Object>} context.athena.getQueryState / getQueryResults (queryId, options)
*/

// 예제
const { queryId } = await context.athena.startQuery('SELECT * FROM tbl LIMIT 10', 'my_db');
const rows = await context.athena.getQueryResults(queryId);
/**
* context.glue - Glue 데이터 카탈로그(DB/Table)를 관리합니다.
* @param {string} dbName
* @param {Object} [options]
* @param {boolean} [options.useCustomerRole]
* @returns {Promise<Object>} context.glue.db.get(dbName, options) / context.glue.db.create(dbName, options)
*/

/**
* @param {string} tableName
* @param {Object} options
* @param {string} options.dbName
* @param {Array<{ name: string, type: string }>} [options.columns] - create 시만 필요
* @param {Array<{ name: string, type: string }>} [options.partitions]
* @param {Object} [options.projection]
* @param {string} [options.bucket] - S3 버킷명 (미지정 시 Config의 S3Bucket 사용)
* @param {string} [options.prefix] - S3 경로 프리픽스
* @param {'csv' | 'parquet' | string} [options.dataType='csv']
* @param {boolean} [options.useCustomerRole]
* @returns {Promise<Object>} context.glue.table.get(tableName, options) / context.glue.table.create(tableName, options)
*/

// 예제
await context.glue.table.create('my_table', {
dbName: 'my_db',
columns: [{ name: 'id', type: 'string' }],
});
/**
* Flow 실행 컨테이너가 살아있는 동안(최대 30초) 값을 재사용할 수 있는 메모리 캐시입니다.
* @param {string} key
* @returns {*} context.cache.get(key)
*/

/**
* @param {string} key
* @param {*} value
* @returns {void} context.cache.set(key, value)
*/

// 예제
context.cache.set('token', tokenValue);
const token = context.cache.get('token');
/**
* context.redis - Redis 클라이언트입니다. (get, set 등 표준 Redis 명령 사용 가능)
* @property {(key: string) => Promise<string|null>} get
* @property {(key: string, value: string, ...args: *[]) => Promise<'OK'>} set
*/

// 예제
await context.redis.set('key', 'value');
const value = await context.redis.get('key');
/**
* context.file - Integration Hub 전용 S3 저장소에 파일을 다루는 함수 모음입니다.
* @param {string} [path='']
* @param {Object} [options]
* @param {string} [options.stage]
* @returns {Promise<boolean>} context.file.exist(path, options)
*/

/**
* @param {string} [path='']
* @param {Object} [options]
* @param {boolean} [options.internal=false] - true이면 외부에 노출되는 URL 대신 내부 S3 Key 반환
* @returns {Promise<string>} context.file.getUrl(path, options)
*/

/**
* @param {string} [path='']
* @param {Object} [options]
* @param {boolean} [options.gziped] - 압축된 파일인지 여부
* @param {boolean} [options.toJSON] - JSON.parse 시도 여부
* @param {boolean} [options.returnBuffer] - Buffer 그대로 반환
* @param {boolean} [options.doNotThrow] - 파일이 없을 때 예외 대신 빈 문자열 반환
* @param {boolean} [options.exactPath] - path를 가공 없이 그대로 S3 Key로 사용
* @returns {Promise<string|Object|Buffer>} context.file.get(path, options)
*/

/**
* @param {string} path - '/'로 끝나면 안 됨
* @param {string|Object|Buffer} value
* @param {Object} [options]
* @param {boolean} [options.gzip]
* @param {string} [options.contentType]
* @param {boolean} [options.useCustomerRole=false]
* @returns {Promise<Object>} context.file.upload(path, value, options)
*/

/**
* @param {string} fromPath
* @param {string} toPath
* @param {Object} [options]
* @param {boolean} [options.returnS3Key]
* @returns {Promise<Object>} context.file.copy(fromPath, toPath, options)
*/

/**
* @param {string} fromPath
* @param {string} toPath
* @returns {Promise<boolean>} context.file.move(fromPath, toPath)
*/

// 예제
await context.file.upload('folder/data.json', { a: 1 });
const data = await context.file.get('folder/data.json', { toJSON: true });
/**
* context.git - 사내 Git 저장소를 다루는 함수 모음입니다.
* @param {Object} params - context.git.createBranch(params)
* @param {string} params.branchName
* @param {string} params.repo
* @returns {Promise<Object>}
*/

/**
* @param {Object} params - context.git.createCommit(params)
* @param {string} params.branchName
* @param {string} params.repo
* @param {Array<{ path: string, content: string }>} params.files
* @param {string} params.title
* @returns {Promise<Object>}
*/

/**
* @param {Object} params - context.git.createPullRequest(params)
* @param {string} params.branchName
* @param {string} params.repo
* @param {string} params.title
* @returns {Promise<Object>}
*/

/**
* @param {Object} params - context.git.mergePullRequest(params)
* @param {string} params.prNumber
* @param {string} params.repo
* @param {string} params.version
* @param {string} params.description
* @returns {Promise<Object>}
*/

// 예제
await context.git.createBranch({ branchName: 'feature/x', repo: 'my-repo' });
/**
* Parquet 변환/데이터 처리 작업을 실행합니다.
* @param {Object} runOptions - 작업 실행에 필요한 파라미터(action 등)
* @param {Object} [options]
* @param {boolean} [options.useCustomRole]
* @returns {Promise<Object>}
*/

// context.parquet.run(runOptions, options)
await context.parquet.run({ action: 'convert' });
/**
* 파일 업로드용 Presigned URL을 발급받습니다.
* @param {Object} options
* @param {string} options.configKey - 업로드 설정 Key
* @param {string} [options.template]
* @param {string} [options.contentMD5]
* @param {string} [options.contentType]
* @param {Object} [options.pathVariables]
* @returns {Promise<Object>}
*/

// 예제
const uploadInfo = await context.getUploadUrlViaProxy({ configKey: 'CFG1', contentType: 'application/json' });

암복호화/식별자

/**
* PGP 암복호화를 수행합니다. (context.pgp.encrypt / decrypt / generateKey / getFingerprint)
* @param {Object} restParams - 오퍼레이션별로 필요한 파라미터
* @param {string} [restParams.message] - encrypt/decrypt 대상 메시지
* @param {string} [restParams.publicKey] - encrypt/getFingerprint 시 사용할 공개키
* @param {string} [restParams.privateKey] - decrypt 시 사용할 개인키
* @param {string} [restParams.passphrase] - 개인키 암호
* @returns {Promise<Object>}
*/

// 예제
const encrypted = await context.pgp.encrypt({ message: 'secret', publicKey: '...' });
/**
* 비밀번호를 해시합니다.
* @param {string} password
* @param {string|number} salt
* @returns {string}
*/

// context.bcrypt.hashSync(password, salt)
const hashed = context.bcrypt.hashSync('myPassword', 10);
/**
* UUID(v4)를 생성합니다.
* @returns {string}
*/

// context.randomUUID()
const id = context.randomUUID();

포맷 변환

/**
* XML 문자열을 JSON으로 변환합니다. (context.xml.toJson)
* @param {string} xmlString
* @param {Object} [options]
* @param {string[]} [options.arrayPaths=[]] - 배열로 강제 파싱할 경로 목록
* @param {boolean} [options.convertToNumber=false] - 숫자처럼 보이는 값을 Number로 변환할지 여부
* @returns {Object}
*/

/**
* JSON을 XML 문자열로 변환합니다. (context.xml.fromJson)
* @param {Object} jsonObj
* @returns {string}
*/

// 예제
const json = context.xml.toJson('<root><a>1</a></root>', {
arrayPaths: ["root.a"]
});
const xml = context.xml.fromJson({ root: { a: 1 } });
/**
* JSON 배열을 CSV 문자열로 변환합니다. (context.csv.fromJson)
* @param {Object[]} jsonArray
* @param {Object|Array} [fields] - 출력할 컬럼 지정 (필드명 배열 또는 { fields, athenaString, noQuotes } 형태)
* @returns {string}
*/

/**
* CSV 문자열을 JSON 배열로 변환합니다. (context.csv.toJson)
* @param {string} csvString
* @param {Object} [options]
* @param {boolean} [options.skipEmptyLines=true]
* @param {string[]|boolean} [options.columns] - 컬럼명 지정 또는 첫 줄을 헤더로 사용할지 여부
* @param {number} [options.fromLine=1] - 읽기 시작할 줄 번호
* @param {boolean} [options.hasHeader=true]
* @returns {Object[]}
*/

// 예제
const csv = context.csv.fromJson([{ id: 1, name: 'a' }]);
const rows = context.csv.toJson(csv);

날짜/범용 유틸리티

/**
* context.dayjs - dayjs(https://day.js.org/) 라이브러리
* context.kst - 'Asia/Seoul' 시간대로 고정된 dayjs 인스턴스
*/

// 예제
const today = context.dayjs().format('YYYY-MM-DD');
const kstNow = context.kst.format('YYYY-MM-DD HH:mm:ss');
/**
* 지정한 초만큼 대기합니다.
* @param {number} [seconds=0]
* @returns {Promise<void>}
*/

// 예제
await context.wait(1.5);
/**
* 콜백을 반복 실행합니다. (context.repeat: 동기, context.asyncRepeat: 비동기)
* @param {Object} [options]
* @param {number} [options.maxTimes=5] - 최대 반복 횟수
* @param {Array} [options.cbArguments=[]] - callback에 추가로 전달할 인자 목록
* @param {(state: { result: *, curIndex: number }, ...cbArguments: *[]) => (Object|*)} callback
* 반환값이 `{ break: true }`이면 반복을 중단합니다.
* @returns {Promise<*>|*} 마지막 callback의 반환값
*/

// 예제
const result = await context.asyncRepeat({ maxTimes: 3 }, async ({ curIndex }) => ({ curIndex }));
/**
* 여러 객체를 깊은 병합(deep merge)합니다. 앞의 인자일수록 우선순위가 높습니다.
* @param {...Object} objects
* @returns {Object}
*/

// 예제
const merged = context.merge(override, base);
/**
* 객체를 깊은 복사(deep clone)합니다.
* @param {*} obj
* @returns {*}
*/

// 예제
const copied = context.clone(draft.json);
/**
* fn 실행 중 예외가 발생하면 defaultValue를 반환합니다.
* @param {Function} fn
* @param {*} [defaultValue]
* @returns {*}
*/

// 예제
const value = context.tryit(() => JSON.parse(str), {});
/**
* 인자를 순서대로 확인하여 처음으로 undefined가 아닌 값을 반환합니다.
* @param {...*} values
* @returns {*}
*/

// 예제
const finalValue = context.defined(draft.json.value, 'default');
/**
* Array.prototype.sort()에 사용할 비교 함수를 만들어줍니다.
* @param {string} field - 정렬 기준 필드명
* @returns {(a: Object, b: Object) => number}
*/

// 예제
list.sort(context.createSorter('name'));
/**
* 객체에서 값이 undefined인 속성을 제거합니다.
* @param {Object} obj
* @returns {Object}
*/

// 예제
context.deleteUndefined(draft.json.payload);
/**
* 지정한 길이의 임의 문자열(영소문자+숫자)을 생성합니다.
* @param {number} length
* @returns {string}
*/

// 예제
const id = context.makeid(8);
/**
* 날짜를 가감합니다.
* @param {Date|string} datetime
* @param {Object} [options]
* @param {number} [options.milliseconds=0]
* @param {number} [options.seconds=0]
* @param {number} [options.minutes=0]
* @param {number} [options.hours=0]
* @param {number} [options.days=0]
* @param {number} [options.months=0]
* @param {number} [options.years=0]
* @param {boolean} [options.ignoreMilliseconds=false]
* @returns {string} ISO 형식의 UTC 시간 문자열
*/

// 예제
const tomorrow = context.adjustTime(new Date(), { days: 1 });
/**
* 데이터를 압축/해제합니다. (context.zip / context.unzip)
* @param {string|Buffer} data
* @returns {Buffer}
*/

// 예제
const compressed = context.zip(JSON.stringify(draft.json));
const original = context.unzip(compressed);