애저 펑션은 자체적으로 애저 이벤트그리드에 대한 바인딩 확장 기능을 제공하고 있어서 이벤트그리드로 이벤트 데이터를 아주 손쉽게 주고 받을 수 있습니다. 하지만, 현재 바인딩 확장 기능은 아직 정식으로 CloudEvents 형식을 지원하지 않는데, 그 이유는 현재 버전의 SDK가 아직 CloudEvents 형식을 지원하지 않기 때문입니다. 아마도 새 버전의 SDK가 GA되는 시점이 되면 애저 펑션 확장 기능에서도 이를 지원하지 않을까 예상하는데요, 따라서, 그 때 까지는 CloudEvents 형식을 사용해서 이벤트그리드에 데이터를 주고 받기 위해서 별도의 작업을 해 줘야 합니다. 이 포스트에서는 그 방법에 대해 간단히 정리해 보도록 하겠습니다.

이 포스트에서는 .NET SDK를 대상으로 기술합니다. 다른 언어로도 SDK를 지원하고 있으니, 아래 링크를 클릭해서 새 SDK를 확인해 보세요!

애저 이벤트그리드 SDK 프리뷰 버전 설치

이 글을 쓰는 현재 새 버전의 애저 이벤트그리드 SDK 버전은 4.0.0-beta.4로, 아직 프리뷰 상태입니다. 이 프리뷰 버전의 SDK를 사용하면 CloudEvents 형식의 이벤트 데이터를 활용할 수 있죠. 먼저 아래 명령어를 통해 프리뷰 버전의 SDK를 설치합니다.

dotnet add package Azure.Messaging.EventGrid --version 4.0.0-beta.4

이제 본격적으로 이벤트그리드 데이터를 작업해 보도록 하겠습니다!

CloudEvents 형식으로 이벤트 데이터 보내기

애저 CLI에서 이벤트그리드 명령어를 사용하기 위해서는 먼저 아래와 같이 확장 기능을 설치해야 합니다.

az extension add -n eventgrid

확장 기능을 설치하고 나면, 아래 애저 CLI 명령어를 통해 이벤트그리드 커스텀 토픽의 엔드포인트를 확인합니다.

endpoint=$(az eventgrid topic show \
-g <resource_group_name> \
-n <eventgrid_topic_name> \
--query "endpoint" -o tsv)

그리고, 접속 키는 아래 명령어를 이용해서 확인합니다.

id=$(az eventgrid topic show \
-g <resource_group_name> \
-n <eventgrid_topic_name> \
--query "id" -o tsv)
key=$(az rest \
-m POST \
-u "https://management.azure.com$id/listKeys?api-version=2020-06-01" \
--query "key1" -o tsv)

위에서 확인한 엔드포인트와 접속 키를 이용해서 아래와 같이 애저 펑션 안에 이벤트그리드용 데이터 퍼블리셔 인스턴스를 만듭니다.

var topicEndpoint = new Uri("<endpoint>");
var credential = new AzureKeyCredential("<key>");
var publisher = new EventGridPublisherClient(topicEndpoint, credential);

CloudEvents 형식의 데이터를 보내기 위해서는 이벤트 데이터 뿐만 아니라 몇 가지 다른 메타 데이터 정보가 필요합니다.

  • source: 이벤트 발생자. 보통 URL 형식으로 작성합니다.
  • type: 이벤트 타입. 이 값을 이용해서 이벤트를 구분합니다. 형식은 com.example.someevent와 비슷한 형태가 됩니다.
  • datacontenttype: 항상 application/cloudevents+json 값으로 지정해 주면 됩니다.

이외에도 다른 메타 데이터 정보가 필요하지만, 나머지는 SDK에서 자동으로 처리해주니 여기서는 크게 상관하지 않습니다.

CloudEvents 데이터 형식에 대한 내용을 좀 더 확인하고 싶다면, 이 링크를 읽어보세요!

위 메타 데이터 정보를 이용해 아래와 같이 CloudEvents 데이터를 작성한 후 이벤트그리드로 보내면 됩니다.

var source = "<source>";
var type = "<type>";
var dataContentType = "application/cloudevents+json";
var data = new MyData() { Hello = "World" };
var @event = new CloudEvent(source, type, data, dataContentType);
var events = new List<CloudEvent>() { @event };
await publisher.SendEventsAsync(events).ConfigureAwait(false);

위의 방법을 통해 CloudEvents 형식의 이벤트 데이터를 이벤트그리드 커스텀 토픽으로 보낼 수 있게 됐습니다.

CloudEvents 형식으로 이벤트 데이터 받기

앞서 애저 펑션에서는 이벤트그리드 바인딩 확장 기능의 제약이 있다고 했습니다. 때문에 CloudEvents 형식의 데이터를 받아 처리하기 위해서는 HTTP 트리거를 이용해야 하는데요, 이 트리거는 두 가지 요청을 동시에 처리해야 합니다.

  • 이벤트 핸들러 엔드포인트 검증 요청
  • 이벤트 데이터 처리

이벤트 핸들러 엔드포인트 검증 요청

CloudEvents의 웹훅 스펙에 따르면 검증 요청은 OPTIONS 메소드를 이용하고 요청 헤더에 반드시 WebHook-Request-Origin를 포함합니다 (line #8). 따라서 이 검증 요청에 응답하기 위해서는 이 요청 헤더 값을 응답 헤더의 WebHook-Allowed-Origin 값에 실어 보내야 합니다 (line #9).

[FunctionName("HttpTrigger")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "POST", "OPTIONS", Route = "handler")] HttpRequest req,
ILogger log)
{
if (HttpMethods.IsOptions(req.Method))
{
string header = req.Headers["WebHook-Request-Origin"];
req.HttpContext.Response.Headers.Add("Webhook-Allowed-Origin", header);
return new OkResult();
}
...
}

이벤트 데이터 처리

위와 같이 이벤트 핸들러로서 애저 펑션 엔드포인트 검증에 성공했다면, 애저 이벤트그리드는 앞으로 이벤트 데이터를 계속해서 동일한 엔드포인트로 POST 메소드를 이용해 보냅니다. 이 때, CloudEvents 이벤트 데이터 전체를 사용하고 싶다면 아래 코드의 @event 인스턴스를 이용하면 되고 (line #18), data 부분만 사용하려면 아래 코드와 같이 비직렬화해서 사용합니다 (line #19).

[FunctionName("HttpTrigger")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "POST", "OPTIONS", Route = "handler")] HttpRequest req,
ILogger log)
{
if (HttpMethods.IsOptions(req.Method))
{
...
}
using (var reader = new StreamReader(req.Body))
{
var payload = await reader.ReadToEndAsync().ConfigureAwait(false);
var events = CloudEvent.Parse(payload);
foreach (var @event in events)
{
var timestamp = @event.Time;
var data = await @event.GetDataAsync<MyData>().ConfigureAwait(false);
...
}
}
return new OkResult();
}

위와 같이 CloudEvents 형식의 이벤트 데이터를 이벤트 토픽에서 애저 펑션으로 받아 처리할 수 있게 됐습니다.


지금까지 애저 펑션에서 애저 이벤트그리드CloudEvents 형식의 이벤트 데이터를 보내고 받는 방법에 대해 알아 보았습니다. 이 방법은 포스트 안에서 계속 언급했다시피, 현재 바인딩 확장 기능은 아직 CloudEvents 형식을 지원하지 않기 때문에, 일종의 임시방편으로 사용하는 방법입니다. 조만간 새 버전의 확장 기능이 릴리즈 되기를 기대합니다.

더 궁금하다면...