계약
contract 모듈은 원격 API, 워크플로우, 함수를 위한 타입화된 서비스 바인딩을 엽니다. 계약은 스키마 검증, 비동기 호출, 호출 컨텍스트 전파를 지원합니다. 이 페이지는 API 참조이며, current_user 같은 ID와 값은 애플리케이션 소유 엔트리 및 주변 핸들러 상태를 나타냅니다.
로딩
local contract = require("contract")
바인딩 열기
레지스트리 ID로 바인딩을 엽니다.
local greeter, err = contract.open("app.services:greeter")
if err then
return nil, err
end
local result, err = greeter:say_hello("Alice")
if err then
return nil, err
end
바인딩에는 스코프 값, 쿼리 파라미터 또는 호출 옵션도 전달할 수 있습니다.
-- With scope table
local svc, err = contract.open("app.services:user", {
tenant_id = "acme",
region = "us-east"
})
-- With query parameters (auto-converted: "true"→bool, numbers→int/float)
local api, err = contract.open("app.services:api?debug=true&timeout=5000")
-- With call options (third argument)
local inst, err = contract.open("app.services:flaky", nil, {
retry = { max_attempts = 5, initial_delay = 100 }
})
| 파라미터 | 타입 | 설명 |
|---|---|---|
binding_id |
string | 바인딩 ID, 쿼리 파라미터 지원 |
scope |
table | 컨텍스트 값 (선택적, 쿼리 파라미터 재정의) |
options |
table | 호출 옵션 (선택적) — 예: retry.max_attempts, retry.initial_delay |
반환: Instance, error
계약 가져오기
인트로스펙션을 위해 계약 정의를 검색합니다:
local c, err = contract.get("app.services:greeter")
if err then
return nil, err
end
print(c:id()) -- "app.services:greeter"
local methods = c:methods()
for _, m in ipairs(methods) do
print(m.name, m.description)
end
local method, err = c:method("say_hello")
if err then
return nil, err
end
메서드 정의
| 필드 | 타입 | 설명 |
|---|---|---|
name |
string | 메서드 이름 |
description |
string | 메서드 설명 |
input_schemas |
table[] | 입력 스키마 정의 (메서드가 선언하지 않으면 없음) |
output_schemas |
table[] | 출력 스키마 정의 (메서드가 선언하지 않으면 없음) |
구현 찾기
계약을 구현하는 모든 바인딩을 나열합니다:
local bindings, err = contract.find_implementations("app.services:greeter")
if err then
return nil, err
end
for _, binding_id in ipairs(bindings) do
print(binding_id)
end
또는 계약 객체를 통해:
local c, err = contract.get("app.services:greeter")
if err then
return nil, err
end
local bindings, err = c:implementations()
if err then
return nil, err
end
구현 확인
인스턴스가 계약을 구현하는지 확인합니다:
if contract.is(instance, "app.services:greeter") then
instance:say_hello("World")
end
메서드 호출
동기 호출 - 완료까지 블록:
local calc, err = contract.open("app.services:calculator")
if err then
return nil, err
end
local sum, err = calc:add(10, 20)
if err then
return nil, err
end
local product, err = calc:multiply(5, 6)
if err then
return nil, err
end
비동기 호출
비동기 실행을 위해 _async 접미사 추가:
local processor, err = contract.open("app.services:processor")
if err then
return nil, err
end
local future, err = processor:process_async(large_dataset)
if err then
return nil, err
end
-- Do other work...
-- Wait for result
local ch = future:response()
local _, open = ch:receive()
if not open then
return nil, errors.new("future response channel closed")
end
local payload, result_err = future:result()
if result_err then return nil, result_err end
local result, data_err = payload:data()
if data_err then return nil, data_err end
Future 메서드는 Futures를 참조하세요.
계약을 통해 열기
계약 객체를 통해 바인딩을 엽니다. 아래 호출은 대안입니다. 인스턴스를 사용하기 전에 contract.get()과 선택한 open() 호출에서 반환된 오류를 확인하세요.
local c, err = contract.get("app.services:user")
if err then
return nil, err
end
-- Default binding
local instance, err = c:open()
-- Specific binding
local instance, err = c:open("app.services:user_impl")
-- With scope
local instance, err = c:open(nil, {user_id = 123})
local instance, err = c:open("app.services:user_impl", {user_id = 123})
컨텍스트 추가
미리 구성된 컨텍스트로 래퍼를 생성합니다:
local ctx = require("ctx")
local c, err = contract.get("app.services:user")
if err then return nil, err end
local request_id, ctx_err = ctx.get("request_id")
if ctx_err then return nil, ctx_err end
local wrapped, err = c:with_context({
request_id = request_id,
user_id = current_user.id
})
if err then return nil, err end
local instance, err = wrapped:open()
호출 옵션
with_options를 통해 재시도 및 기타 호출 동작을 구성합니다:
local c, err = contract.get("app.services:flaky")
if err then return nil, err end
local configured = c:with_options({
retry = { max_attempts = 5, initial_delay = 100 }
})
local inst, err = configured:open("app.services:flaky_impl")
if err then return nil, err end
local result, err = inst:call()
옵션은 반환된 인스턴스의 모든 메서드 호출에 적용됩니다. 재시도 가능한 오류만 재시도를 트리거하며, 재시도 불가능한 오류는 즉시 반환됩니다. with_options는 with_context, with_actor, with_scope와 체이닝할 수 있습니다.
| 옵션 | 타입 | 설명 |
|---|---|---|
retry.max_attempts |
int | 첫 번째를 포함한 최대 시도 횟수 (1은 재시도 비활성화) |
retry.initial_delay |
int/duration | 첫 번째 재시도 전 지연 (ms 또는 duration 문자열), 기본값 100 |
retry.max_delay |
int/duration | 백오프 지연의 상한 (ms 또는 duration 문자열), 기본값 10s |
retry.backoff_factor |
number | 시도할 때마다 지연에 적용되는 배수, 기본값 2.0 |
retry.jitter |
number | 각 지연에 적용되는 무작위 지터 비율, 기본값 0.1 |
retry.retry_kinds |
string[] | 이 종류의 에러만 재시도; 기본적으로 Invalid, PermissionDenied, Internal을 제외한 모든 종류를 재시도 |
retry.skip_kinds |
string[] | 이 종류의 에러는 재시도하지 않음 |
보안 컨텍스트
인가를 위해 액터와 스코프를 설정합니다:
local security = require("security")
local c, err = contract.get("app.services:admin")
if err then return nil, err end
local secured, err = c:with_actor(security.actor())
if err then return nil, err end
secured, err = secured:with_scope(security.scope())
if err then return nil, err end
local admin, err = secured:open()
if err then return nil, err end
명시적인 with_actor/with_scope 없이 열린 계약은 호출자의 앰비언트 액터와 스코프를 상속합니다. 설정된 경우 바인딩된 구현 함수로 전파됩니다 — 인스턴스의 모든 메서드 호출이 해당 신원 아래에서 실행됩니다.
권한
| 권한 | 리소스 | 함수 |
|---|---|---|
contract.get |
계약 id | get() |
contract.open |
바인딩 id | open(), Contract:open() |
contract.implementations |
계약 id | find_implementations(), Contract:implementations() |
contract.call |
메서드 이름 | 동기 및 비동기 메서드 호출 |
contract.context |
"context" | Contract:with_context() |
contract.security |
"security" | Contract:with_actor(), Contract:with_scope() |
에러
| 조건 | 종류 |
|---|---|
| 잘못된 바인딩 ID 형식 | errors.INVALID |
| 계약을 찾을 수 없음 | errors.NOT_FOUND |
| 바인딩을 찾을 수 없음 | errors.NOT_FOUND |
| 메서드를 찾을 수 없음 | errors.NOT_FOUND |
| 기본 바인딩 없음 | errors.NOT_FOUND |
| 권한 거부됨 | errors.PERMISSION_DENIED |
| 호출 실패 | 구현체 에러의 종류가 보존됨; 디스패치 실패는 errors.INTERNAL |