다음을 통해 공유


Azure 기밀 원장의 간단한 사용자 정의 함수(미리 보기)

Azure 기밀 원장의 간단한 UDF(사용자 정의 함수)를 사용하면 원장 신뢰 경계 내에서 실행할 수 있는 사용자 지정 JavaScript 함수를 만들 수 있습니다. 이 기능은 간단하고 사용하기 쉽도록 설계되어 복잡한 애플리케이션 개발 없이도 원장 API의 기능을 확장할 수 있습니다.

기본 제공 JavaScript API를 사용하여 사용자 지정 코드를 실행하여 사용자 지정 쿼리 및 계산, 조건부 검사, 사후 처리 작업 등과 같은 다양한 작업을 수행할 수 있습니다. 이 기능은 기존 원장 API와 직접 통합하거나 기밀 환경에서 간단한 사용자 지정 논리를 실행해야 하는 시나리오에 적합합니다.

UDF의 빠른 개요 및 데모를 보려면 다음 비디오를 시청하세요.

중요합니다

사용자 정의 함수는 현재 API 버전 2024-12-09-preview에서 미리 보기로 제공됩니다. 이 등록 양식을 통해 이 미리 보기에 대한 액세스를 요청할 수 있습니다. 베타, 미리 보기 또는 아직 일반 공급으로 릴리스되지 않은 Azure 기능에 적용되는 법적 용어는 Microsoft Azure 미리 보기에 대한 추가 사용 약관 을 참조하세요.

팁 (조언)

사용자 지정 Role-Based 액세스 제어(RBAC) 또는 외부 기밀 워크로드와의 통합과 같은 고급 시나리오는 Azure 기밀 원장의 고급 사용자 정의 함수를 참조하세요.

사용 사례

Azure 기밀 원장 UDF를 사용하면 사용자 지정 논리를 실행하여 원장의 기능을 확장할 수 있습니다. UDF에 대한 몇 가지 일반적인 사용 사례는 다음과 같습니다.

  • 사용자 지정 계산 및 쿼리: 독립 실행형 UDF를 실행하여 비즈니스 논리에 따라 원장 애플리케이션 테이블에 데이터를 읽거나 씁니다.

  • 데이터 유효성 검사 및 입력 검사: 원장 항목이 원장에 기록되기 전에 UDF를 사전 후크 로 사용하여 사전 처리 작업을 실행합니다(예: 입력 데이터를 삭제하거나 사전 조건을 확인하는 경우).

  • 데이터 보강 및 스마트 계약: 원장 항목을 작성한 후 UDF를 후크 로 사용하여 사후 처리 작업을 실행합니다( 예: 원장에 사용자 지정 메타데이터를 추가하거나 사후 쓰기 워크플로를 트리거하는 경우).

UDF 작성

Azure 기밀 원장 UDF는 고유 ID를 사용하여 원장에 저장된 엔터티이며 UDF가 호출될 때 실행되는 JavaScript 코드를 포함합니다. 이 섹션에서는 UDF 코드를 작성하고 JavaScript API를 사용하여 다양한 작업을 수행하는 방법을 설명합니다.

함수 구조

UDF의 코드에는 실행 시 스크립트의 진입점인 내보낸 함수가 필요합니다. 기본 UDF 코드 템플릿은 다음과 같습니다.

export function main() {
    // Your JavaScript code here
}

비고

UDF를 실행할 때 실행 중에 호출되는 내보낸 진입점 함수의 exportedFunctionName 이름을 인수로 수정할 수 있습니다. 지정하지 않으면 기본 이름은 .입니다 main.

비고

람다 함수는 지원되지만 내보낸 함수 이름을 명시적으로 정의하고 진입점 함수 이름과 일치해야 합니다. 다음은 그 예입니다.

export const main = () => { 
    // Your JavaScript code here 
};

함수 인수

UDF에서 허용하는 선택적 런타임 인수를 지정할 수 있습니다. 매개 변수를 사용하여 arguments UDF를 실행할 때 런타임에 인수의 값을 전달할 수 있습니다.

인수는 항상 문자열 배열로 전달됩니다. UDF 코드에 지정된 인수가 UDF를 실행할 때 전달된 인수와 일치하는지 확인하는 것은 사용자의 책임입니다. 또한 사용자는 런타임에 인수가 예상 데이터 형식으로 올바르게 구문 분석되는지 확인해야 합니다.

export function main(arg1, arg2) {
    // Your JavaScript code here
}

JavaScript API

UDF의 JavaScript 코드는 제한된 API 집합을 제공하는 샌드박스 환경 내에서 실행됩니다.

모든 JavaScript 표준 전역 함수, 개체 및 값을 사용할 수 있습니다. CCF(기밀 컨소시엄 프레임워크)에서 제공하는 특정 기능 및 유틸리티(예: 암호화 도우미 함수, 원장 테이블 접근자 등)에 액세스하는 데 호출 ccf 된 전역 개체를 사용할 수 있습니다. 전역 개체의 ccf 전체 API는 여기에 설명되어 있습니다.

전역 개체를 사용하여 context 현재 요청의 컨텍스트 정보에 액세스할 수도 있습니다. 이 개체는 함수 실행()을 시작한 요청 메타데이터 및 함수 호출자(context.requestcontext.userId)의 사용자 ID에 대한 액세스를 제공합니다. 트랜잭션 후크의 경우 쓰기 작업에 연결된 컬렉션 ID 및 트랜잭션 콘텐츠도 개체에 추가 context 됩니다(context.collectionIdcontext.contents 각각).

다음 코드 조각은 JavaScript API 사용에 대한 몇 가지 기본 예제를 보여 줍니다.

export function main(args) {
    
    // Basic instructions
    const a = 1 + 1;

    // Basic statements
    if (a > 0) {
        console.log("a is positive");
    } else {
        console.log("a is negative or zero");
    }

    // Parse the string argument as a JSON object
    JSON.parse(args);

    // Logging utilities
    console.log("Hello world");
    
    // Math utilities
    Math.random();
    
    // CCF cryptography utilities
    ccf.crypto.digest("SHA-256", ccf.strToBuf("Hello world"));
    
    // Write to a custom ledger table
    ccf.kv["public:mytable"].set(ccf.strToBuf("myKey"), ccf.strToBuf("myValue"));

    // Read from a custom ledger table
    ccf.bufToStr(ccf.kv["public:mytable"].get(ccf.strToBuf("myKey")));

    // Read from the ledger entry table
    ccf.kv["public:confidentialledger.logs"].get(ccf.strToBuf("subledger:0"));

    // Get the request metadata that originated the function execution
    const requestMetadata = context.request;
    
    // Get the collection ID and transaction content (for transaction hooks only)
    const collectionId = context.collectionId;
    const contents = context.contents;

    // Throw exceptions
    throw new Error("MyCustomError");
}

팁 (조언)

원장 맵을 사용하여 데이터를 저장하고 검색하는 방법에 대한 자세한 내용은 Key-Value Store API의 CCF 설명서를 참조하세요.

비고

UDF에서는 모듈 가져오기가 지원되지 않습니다. JavaScript 코드는 자체 포함되어야 하며 외부 라이브러리 또는 모듈을 사용할 수 없습니다. 현재 웹 API 도 지원되지 않습니다.

UDF 관리

Azure 기밀 원장 애플리케이션은 UDF 엔터티를 만들고, 읽고, 업데이트하고, 삭제하는 전용 CRUD API를 제공합니다. UDF는 원장에 안전하게 저장되며 원장 애플리케이션에서만 액세스할 수 있습니다.

비고

간단한 사용자 정의 함수 및 고급 사용자 정의 함수는 상호 배타적인 기능입니다. 고급 UDF가 정의된 경우 간단한 UDF를 만들거나 실행할 수 없으며 그 반대의 경우도 마찬가지입니다. 둘 사이를 전환하려면 UDF 개요 페이지의 지침을 따릅니다.

UDF 만들기 또는 업데이트

PUT /app/userDefinedFunctions/myFunction
{
    "code": "export function main() { return "Hello World"; }",
}

중요합니다

UDF를 만들거나 업데이트하려면 관리자 역할이 필요합니다.

UDF 가져오기

GET /app/userDefinedFunctions/myFunction

UDF 나열

GET /app/userDefinedFunctions

UDF 삭제

DELETE /app/userDefinedFunctions/myFunction

중요합니다

UDF를 삭제하려면 관리자 역할이 필요합니다.

비고

UDF를 삭제하면 엔터티가 원장의 현재 상태에서만 제거됩니다. 삭제된 모든 UDF는 항상 변경할 수 없는 원장 기록에 유지됩니다(커밋된 모든 트랜잭션으로).

UDF 실행

만든 Azure 기밀 원장 사용자는 독립 실행형 함수 또는 쓰기 작업에 연결된 트랜잭션 후크로 UDF를 실행할 수 있습니다. 각 UDF 실행은 별도의 런타임 환경 및 샌드박스에서 실행됩니다. 즉, UDF 실행은 다른 UDF 또는 다른 원장 작업과 격리됩니다.

UDF 실행은 요청 본문에 지정할 수 있는 선택적 속성을 사용하여 제어할 수 있습니다. 현재 지원되는 속성은 다음과 같습니다.

  • arguments: UDF에 전달할 인수를 나타내는 문자열 배열입니다. 인수는 UDF 코드에 정의된 순서와 동일한 순서로 전달됩니다. 기본값은 빈 배열입니다.

  • exportedFunctionName: 실행 중에 호출할 내보낸 함수의 이름입니다. 지정하지 않으면 기본값 main입니다.

  • runtimeOptions: UDF 실행에 대한 런타임 옵션을 지정하는 개체입니다. 다음과 같은 옵션을 사용할 수 있습니다.

    • max_heap_bytes: 최대 힙 크기(바이트)입니다. 기본값은 10,485,760(10MB)입니다.

    • max_stack_bytes: 최대 스택 크기(바이트)입니다. 기본값은 1,048,576(1MB)입니다.

    • max_execution_time_ms: 최대 실행 시간(밀리초)입니다. 기본값은 1000(1초)입니다.

    • log_exception_details: 예외 세부 정보를 기록할지 여부를 지정하는 부울 값입니다. 기본값은 true입니다.

    • return_exception_details: 응답에서 예외 세부 정보를 반환할지 여부를 지정하는 부울 값입니다. 기본값은 true입니다.

독립 실행형 함수

API를 사용하여 UDF를 POST /app/userDefinedFunctions/{functionId}:execute 직접 실행할 수 있습니다.

POST /app/userDefinedFunctions/myFunction:execute
{}

요청 본문을 사용하여 함수 인수 및 JavaScript 런타임 속성과 같은 선택적 실행 매개 변수를 지정할 수 있습니다.

POST /app/userDefinedFunctions/myFunction:execute
{
    "arguments": ["arg1", "arg2"],
    "exportedFunctionName": "myMainFunction",
    "runtimeOptions": {
        "max_heap_bytes": 5,
        "max_stack_bytes": 1024,
        "max_execution_time_ms": 5000,
        "log_exception_details": true,
        "return_exception_details": true
    }
}

응답은 UDF 실행의 결과를 나타냅니다(성공 또는 실패). UDF가 성공하면 응답에는 문자열 형식(있는 경우)으로 반환된 함수 값이 포함됩니다.

{
    "result": 
        {
            "returnValue": "MyReturnValue"
        }, 
    "status": "Succeeded"
}

UDF가 실패한 경우 응답에는 자세한 스택 추적이 포함된 오류 메시지가 포함됩니다.

{
    "error": {
        "message": "Error while executing function myFunction: Error: MyCustomError\n    at myMainFunction (myFunction)\n"
    }, 
    "status": "Failed"
}

중요합니다

UDF를 실행하려면 기여자 역할이 필요합니다.

트랜잭션 훅

UDF는 원장 쓰기 API(POST /app/transactions)의 일부로 원장에 항목이 기록되기 전(후크 전) 또는 기록된 후(후크 후) 후크로 실행할 수도 있습니다. 후크는 쓰기 작업의 동일한 컨텍스트에서 실행됩니다. 즉, 후크에 의해 원장에 기록된 모든 데이터가 동일한 쓰기 트랜잭션에 자동으로 포함됩니다.

쓰기 요청의 요청 본문은 프리 훅과 포스트 훅으로 각각 실행할 UDF ID를 지정하는 데 사용할 수 있습니다.

POST /app/transactions?collectionId=myCollection
{
  "contents": "myValue", 
  "preHooks": [ 
    { 
        "functionId": "myPreHook"
    } 
  ], 
  "postHooks": [ 
    { 
        "functionId": "myPostHook" 
    }
  ] 
} 

중요합니다

후크는 쓰기 작업의 요청 본문에 명시적으로 정의되어야 합니다. 일반적으로 UDF는 생성된 후 모든 쓰기 작업에 대해 자동으로 실행할 수 없습니다.

각 후크에 대해 선택적 실행 속성을 지정할 수 있습니다. 다음은 그 예입니다.

POST /app/transactions?collectionId=myCollection
{
  "contents": "myValue", 
  "preHooks": [ 
    { 
        "functionId": "myPreHook", 
        "properties": { 
            "arguments": [ 
                "arg1",
                "arg2"
            ], 
            "exportedFunctionName": "myMainFunction", 
            "runtimeOptions": { 
                "max_heap_bytes": 5,
                "max_stack_bytes": 1024,
                "max_execution_time_ms": 5000,
                "log_exception_details": true,
                "return_exception_details": true
            } 
        } 
    } 
  ], 
  "postHooks": [ 
    { 
        "functionId": "myPostHook", 
        "properties": { 
            "arguments": [ 
                "arg1"
            ], 
            "exportedFunctionName": "myMainFunction", 
            "runtimeOptions": { 
                "max_heap_bytes": 5,
                "max_stack_bytes": 1024,
                "max_execution_time_ms": 5000,
                "log_exception_details": true,
                "return_exception_details": true
            } 
        } 
    }
  ] 
} 

요청 본문에 최대 5개의 사전 후크 및 후처리 후크를 모든 조합으로 지정할 수 있습니다. 후크는 항상 요청 본문에 제공된 순서대로 실행됩니다.

사전 후크 또는 사후 후크가 실패하면 전체 트랜잭션이 중단됩니다. 이 경우 응답에는 오류의 원인과 함께 오류 메시지가 포함됩니다.

{
    "error": {
        "code": "InternalError",
        "message": "Error while executing function myPreHook: Error: MyCustomError\n    at myMainFunction (myPreHook)\n",
    }
}

비고

여러 후크가 성공적으로 실행되더라도, 정의된 사전 후크 또는 사후 후크가 성공적으로 끝까지 실행되지 않으면 트랜잭션이 실패할 수 있습니다.

팁 (조언)

UDF는 동일한 요청에서 사전 후크 및 후크로 다시 사용하고 여러 번 호출할 수 있습니다.

예시

이 섹션에서는 Azure 기밀 원장에서 UDF를 사용하는 방법에 대한 몇 가지 실용적인 예를 안내합니다. 다음 예제 시나리오에서는 Azure 기밀 원장을 사용하여 다른 은행 사용자에 대한 은행 거래를 저장하는 것으로 가정합니다.

컨텍스트

사용자에 대한 뱅킹 트랜잭션을 저장하기 위해 기존 원장 쓰기 API를 사용할 수 있습니다. 트랜잭션 값은 원장 항목의 콘텐츠이고 사용자 ID는 콘텐츠가 기록되는 컬렉션 또는 키일 수 있습니다.

POST /app/transactions?collectionId=John
{
    "contents": "10"
}

HTTP/1.1 200 OK

입력 내용에 대한 유효성 검사가 없으므로 숫자가 아닌 값을 내용으로 작성할 수 있습니다. 예를 들어 콘텐츠 값이 숫자가 아니더라도 이 요청은 성공합니다.

POST /app/transactions?collectionId=Mark
{
    "contents": "This is not a number"
}

HTTP/1.1 200 OK

데이터 유효성 검사를 위한 전처리 훅

트랜잭션 내용이 항상 숫자인지 확인하기 위해 입력 내용을 확인하기 위해 UDF를 만들 수 있습니다. 다음 프리 훅에서는 내용 값이 숫자인지 확인하고, 그렇지 않을 경우 오류를 발생시킵니다.

PUT /app/userDefinedFunctions/validateTransaction
{
    "code": "export function main() { if (isNaN(context.contents)) { throw new Error('Contents is not a number'); } }"
}

HTTP/1.1 201 CREATED

쓰기 요청에서 사전 후크를 사용하면 입력 데이터가 예상 형식에 해당하도록 적용할 수 있습니다. 이제 이전 요청이 예상대로 실패합니다.

POST /app/transactions?collectionId=Mark
{
    "contents": "This is not a number",
    "preHooks": [
        {
            "functionId": "validateTransaction"
        }
    ]
}

HTTP/1.1 500 INTERNAL_SERVER_ERROR
{
  "error": {
    "code": "InternalError",
    "message": "Error while executing function validateTransaction: Error: Contents is not a number\n    at main (validateTransaction)\n"
  }
}

숫자 값을 포함하는 유효한 요청은 예상대로 성공합니다.

POST /app/transactions?collectionId=Mark
{
    "contents": "30",
    "preHooks": [
        {
            "functionId": "validateTransaction"
        }
    ]
}

HTTP/1.1 200 OK

데이터 보강을 위한 포스트 훅

사용자가 새 은행 거래를 수행할 때 감사 이유로 트랜잭션이 특정 임계값보다 높은 경우를 기록하려고 합니다. 사후 후크는 쓰기 작업 후 원장에 사용자 지정 메타데이터를 작성하여 트랜잭션이 특정 임계값보다 높은지 여부를 나타내는 데 사용할 수 있습니다.

예를 들어 UDF를 만들어 트랜잭션 값을 확인하고 값이 50보다 큰 경우 입력 사용자 아래에 더미 메시지(높은 값에 대한 "경고", 그렇지 않으면 "정상")를 사용자 지정 원장 테이블(payment_metadata)에 쓸 수 있습니다.

PUT /app/userDefinedFunctions/detectHighTransaction
{
    "code": "export function main() { let value = 'Normal'; if (context.contents > 50) { value = 'Alert' } ccf.kv['public:payment_metadata'].set(ccf.strToBuf(context.collectionId), ccf.strToBuf(value)); }"
}

HTTP/1.1 201 CREATED

UDF가 성공적으로 만들어지면 후크 후크를 새 쓰기 요청에 사용할 수 있습니다.

POST /app/transactions?collectionId=Mark
{
    "contents": "100",
    "preHooks": [
        {
            "functionId": "validateTransaction"
        }
    ],
    "postHooks": [
        {
            "functionId": "detectHighTransaction"
        }
    ]
}

HTTP/1.1 200 OK
POST /app/transactions?collectionId=John
{
    "contents": "20",
    "preHooks": [
        {
            "functionId": "validateTransaction"
        }
    ],
    "postHooks": [
        {
            "functionId": "detectHighTransaction"
        }
    ]
}

HTTP/1.1 200 OK

사용자 지정 쿼리에 대한 독립 실행형 UDF

후크 이후에 사용자 지정 테이블 payment_metadata에 기록된 최신 값을 검사하려면, 입력된 사용자 ID를 기준으로 테이블에서 값을 읽기 위한 UDF를 생성할 수 있습니다.

PUT /app/userDefinedFunctions/checkPaymentMetadataTable
{
    "code": "export function main(user) { const value = ccf.kv['public:payment_metadata'].get(ccf.strToBuf(user)); if (value === undefined) { throw new Error('UnknownUser'); } return ccf.bufToStr(value); }"
}

HTTP/1.1 201 CREATED

UDF를 직접 실행하면 지정된 사용자에 대한 사용자 지정 메타데이터 테이블에 기록된 최신 값을 확인할 수 있습니다.

최근 높은 트랜잭션이 있는 사용자의 경우 UDF는 예상대로 "경고" 값을 반환합니다.

POST /app/userDefinedFunctions/checkPaymentMetadataTable:execute
{
    "arguments": [
        "Mark"
    ]
}

HTTP/1.1 200 OK
{
  "result": {
    "returnValue": "Alert"
  },
  "status": "Succeeded"
}

최근 낮은 트랜잭션을 사용하는 사용자의 경우 UDF는 대신 값 "Normal"을 반환합니다.

POST /app/userDefinedFunctions/checkPaymentMetadataTable:execute
{
    "arguments": [
        "John"
    ]
}

HTTP/1.1 200 OK
{
  "result": {
    "returnValue": "Normal"
  },
  "status": "Succeeded"
}

사용자 지정 테이블에 항목이 없는 사용자의 경우 UDF는 UDF 코드에 정의된 대로 오류를 throw합니다.

POST /app/userDefinedFunctions/checkPaymentMetadataTable:execute
{
    "arguments": [
        "Jane"
    ]
}

HTTP/1.1 200 OK
{
  "error": {
    "message": "Error while executing function checkPaymentMetadataTable: Error: UnknownUser\n    at main (checkPaymentMetadataTable)\n"
  },
  "status": "Failed"
}

고려 사항

  • 트랜잭션 후크는 현재 원장에 POST /app/transactions 새 항목을 추가할 때 API에 대해서만 지원됩니다.

  • 사용자 정의 함수들(UDF) 및 후크는 항상 원장의 주 복제본에서 실행되어 트랜잭션 순서 지정 및 강력한 일관성 기능을 보장합니다.

  • UDF 코드 실행은 항상 단일 원자성 트랜잭션으로 래핑됩니다. UDF의 JavaScript 논리가 예외 없이 완료되면 UDF 내의 모든 작업이 원장에 커밋됩니다. 예외가 발생하면 전체 트랜잭션이 롤백됩니다. 마찬가지로, 사전 후크 및 사후 후크는 등록된 쓰기 작업과 동일한 컨텍스트에서 실행됩니다. 사전 후크 또는 사후 후크가 실패하면 전체 거래가 중단되며 원장에 항목이 추가되지 않습니다.

  • UDF는 CCF 애플리케이션 테이블에만 액세스할 수 있으며 보안상의 이유로 원장의 내부 및 거버넌스 테이블 또는 기타 기본 제공 테이블에 액세스할 수 없습니다. 항목이 기록되는 원장 테이블(public:confidentialledger.logs 공용 원장과 private:confidentialledger.logs 개인 원장)은 읽기 전용입니다.

  • 단일 쓰기 트랜잭션에 등록할 수 있는 사전 후크 및 사후 후크의 최대 개수는 5개입니다.

  • UDF 및 후크 실행은 5초로 제한됩니다. 함수를 실행하는 데 5초보다 오래 걸리면 작업이 중단되고 오류가 반환됩니다.