명령줄 도구를 사용하여 Azure Functions를 Azure Storage에 연결

이 문서에서는 이전 빠른 시작 문서에서 만든 함수 및 스토리지 계정과 Azure Storage 큐를 통합합니다. 이 통합은 데이터를 HTTP 요청에서 큐의 메시지에 쓰는 출력 바인딩을 사용하여 달성할 수 있습니다. 이 문서를 완료하면 이전의 빠른 시작에서 소요된 몇 USD 센트를 초과하는 추가 비용이 발생하지 않습니다. 바인딩에 대해 자세히 알아보려면 Azure Functions 트리거 및 바인딩 개념을 참조하세요.

참고 항목

이 문서에서는 현재 Functions용 Node.js v3만 지원합니다.

로컬 환경 구성

시작하기 전에 빠른 시작: 명령줄에서 Azure Functions 프로젝트 만들기 문서를 완료해야 합니다. 해당 문서의 끝에서 리소스를 이미 정리한 경우, 해당 단계를 다시 수행하여 Azure에서 함수 앱 및 관련 리소스를 다시 만듭니다.

시작하기 전에 빠른 시작: 명령줄에서 Azure Functions 프로젝트 만들기 문서를 완료해야 합니다. 해당 문서의 끝에서 리소스를 이미 정리한 경우, 해당 단계를 다시 수행하여 Azure에서 함수 앱 및 관련 리소스를 다시 만듭니다.

시작하기 전에 빠른 시작: 명령줄에서 Azure Functions 프로젝트 만들기 문서를 완료해야 합니다. 해당 문서의 끝에서 리소스를 이미 정리한 경우, 해당 단계를 다시 수행하여 Azure에서 함수 앱 및 관련 리소스를 다시 만듭니다.

시작하기 전에 빠른 시작: 명령줄에서 Azure Functions 프로젝트 만들기 문서를 완료해야 합니다. 해당 문서의 끝에서 리소스를 이미 정리한 경우, 해당 단계를 다시 수행하여 Azure에서 함수 앱 및 관련 리소스를 다시 만듭니다.

시작하기 전에 빠른 시작: 명령줄에서 Azure Functions 프로젝트 만들기 문서를 완료해야 합니다. 해당 문서의 끝에서 리소스를 이미 정리한 경우, 해당 단계를 다시 수행하여 Azure에서 함수 앱 및 관련 리소스를 다시 만듭니다.

시작하기 전에 빠른 시작: 명령줄에서 Azure Functions 프로젝트 만들기 문서를 완료해야 합니다. 해당 문서의 끝에서 리소스를 이미 정리한 경우, 해당 단계를 다시 수행하여 Azure에서 함수 앱 및 관련 리소스를 다시 만듭니다.

Azure Storage 연결 문자열 검색

이전에 함수 앱을 사용하기 위해 Azure Storage 계정을 만들었습니다. 이 계정의 연결 문자열은 Azure의 앱 설정에 안전하게 저장됩니다. 이 설정을 local.settings.json 파일에 다운로드하면 함수를 로컬로 실행할 때 연결을 사용하여 동일한 계정의 Storage 큐에 쓸 수 있습니다.

  1. 프로젝트의 루트에서 다음 명령을 실행하고 <APP_NAME>을 이전 단계의 함수 앱 이름으로 바꿉니다. 이 명령은 파일의 기존 값을 덮어씁니다.

    func azure functionapp fetch-app-settings <APP_NAME>
    
  2. local.settings.json 파일을 열고 Storage 계정 연결 문자열인 AzureWebJobsStorage라는 값을 찾습니다. 이 문서의 다른 섹션에서 AzureWebJobsStorage 이름과 연결 문자열을 사용합니다.

Important

local.settings.json 파일에는 Azure에서 다운로드한 비밀이 포함되어 있으므로 항상 이 파일을 소스 제어에서 제외하세요. 로컬 함수 프로젝트를 사용하여 만든 .gitignore 파일은 기본적으로 해당 파일이 제외됩니다.

바인딩 확장 등록

HTTP 및 타이머 트리거를 제외하고는 바인딩이 확장 패키지로 구현됩니다. 터미널 창에서 다음 dotnet add package 명령을 실행하여 프로젝트에 스토리지 확장 패키지를 추가합니다.

dotnet add package Microsoft.Azure.Functions.Worker.Extensions.Storage.Queues --prerelease

이제 프로젝트에 스토리지 출력 바인딩을 추가할 수 있습니다.

함수에 출력 바인딩 정의 추가

함수에는 하나의 트리거만 있을 수 있지만 여러 개의 입력 및 출력 바인딩이 있을 수 있으므로 사용자 지정 통합 코드를 작성하지 않고도 다른 Azure 서비스 및 리소스에 연결할 수 있습니다.

이러한 바인딩은 함수 폴더의 function.json 파일에서 선언합니다. 이전 빠른 시작에서 HttpExample 폴더의 function.json 파일에 있는 bindings 컬렉션에는 두 개의 바인딩이 포함되어 있습니다.

Python v2 프로그래밍 모델을 사용하는 경우 바인딩 특성은 function_app.py 파일에 데코레이터로 직접 정의됩니다. 이전의 빠른 시작부터 function_app.py 파일에는 데코레이터 기반 바인딩이 이미 포함되어 있습니다.

import azure.functions as func
import logging

app = func.FunctionApp()

@app.function_name(name="HttpTrigger1")
@app.route(route="hello", auth_level=func.AuthLevel.ANONYMOUS)

route 데코레이터는 HttpTrigger 및 HttpOutput 바인딩을 함수에 추가하여 http 요청이 지정된 경로에 도달하면 함수를 트리거할 수 있습니다.

이 함수에서 Azure Storage 큐에 쓰려면 함수 코드에 queue_output 데코레이터를 추가합니다.

@app.queue_output(arg_name="msg", queue_name="outqueue", connection="AzureWebJobsStorage")

데코레이터에서 arg_name은 코드에 참조되는 바인딩 매개 변수를 식별하고, queue_name은 바인딩이 쓰는 큐의 이름이며, connection은 스토리지 계정에 대한 연결 문자열을 포함하는 애플리케이션 설정의 이름입니다. 빠른 시작에서는 local.settings.json 파일의 AzureWebJobsStorage 설정에 있는 함수 앱과 동일한 스토리지 계정을 사용합니다. queue_name이 없으면 바인딩에서 처음 사용할 때 만들어집니다.

"bindings": [
    {
        "authLevel": "function",
        "type": "httpTrigger",
        "direction": "in",
        "name": "req",
        "methods": [
            "get",
            "post"
        ]
    },
    {
        "type": "http",
        "direction": "out",
        "name": "res"
    }
]
"bindings": [
  {
    "authLevel": "function",
    "type": "httpTrigger",
    "direction": "in",
    "name": "Request",
    "methods": [
      "get",
      "post"
    ]
  },
  {
    "type": "http",
    "direction": "out",
    "name": "Response"
  }
]

컬렉션의 두 번째 바인딩에는 res라는 이름이 지정됩니다. 이 http 바인딩은 HTTP 응답을 작성하는 데 사용되는 출력 바인딩(out)입니다.

이 함수에서 Azure Storage 큐에 쓰려면 아래 코드와 같이 이름이 msgqueue 형식의 out 바인딩을 추가합니다.

    {
      "authLevel": "function",
      "type": "httpTrigger",
      "direction": "in",
      "name": "req",
      "methods": [
        "get",
        "post"
      ]
    },
    {
      "type": "http",
      "direction": "out",
      "name": "res"
    },
    {
      "type": "queue",
      "direction": "out",
      "name": "msg",
      "queueName": "outqueue",
      "connection": "AzureWebJobsStorage"
    }
  ]
}

컬렉션의 두 번째 바인딩에는 res라는 이름이 지정됩니다. 이 http 바인딩은 HTTP 응답을 작성하는 데 사용되는 출력 바인딩(out)입니다.

이 함수에서 Azure Storage 큐에 쓰려면 아래 코드와 같이 이름이 msgqueue 형식의 out 바인딩을 추가합니다.

    {
      "authLevel": "function",
      "type": "httpTrigger",
      "direction": "in",
      "name": "Request",
      "methods": [
        "get",
        "post"
      ]
    },
    {
      "type": "http",
      "direction": "out",
      "name": "Response"
    },
    {
      "type": "queue",
      "direction": "out",
      "name": "msg",
      "queueName": "outqueue",
      "connection": "AzureWebJobsStorage"
    }
  ]
}

이 경우 msg가 출력 인수로 함수에 제공됩니다. queue 형식의 경우 queueName에는 큐 이름을 지정하고, connection에는 local.settings.json 파일에 있는 Azure Storage 연결의 이름을 제공해야 합니다.

C# 프로젝트에서 바인딩은 함수 메서드의 바인딩 특성으로 정의됩니다. 특정 정의는 앱이 프로세스 내에서 실행되는지(C# 클래스 라이브러리) 또는 격리된 작업자 프로세스에서 실행되는지에 따라 달라집니다.

HttpExample.cs 프로젝트 파일을 열고 다음 MultiResponse 클래스를 추가합니다.

public class MultiResponse
{
    [QueueOutput("outqueue",Connection = "AzureWebJobsStorage")]
    public string[] Messages { get; set; }
    public HttpResponseData HttpResponse { get; set; }
}

MultiResponse 클래스를 사용하면 outqueue라는 스토리지 큐와 HTTP 성공 메시지에 쓸 수 있습니다. QueueOutput 특성이 문자열 배열에 적용되므로 여러 메시지를 큐로 보낼 수 있습니다.

Connection 속성은 스토리지 계정의 연결 문자열을 설정합니다. 이 경우 기본 스토리지 계정을 이미 사용하고 있으므로 Connection를 생략할 수 있습니다.

Java 프로젝트에서 바인딩은 함수 메서드의 바인딩 주석으로 정의됩니다. 그런 다음, function.json 파일은 이러한 주석을 기반으로 자동으로 생성됩니다.

src/main/java에서 함수 코드의 위치로 이동하여 Function.java 프로젝트 파일을 열고, 다음 매개 변수를 run 메서드 정의에 추가합니다.

@QueueOutput(name = "msg", queueName = "outqueue", connection = "AzureWebJobsStorage") OutputBinding<String> msg

msg 매개 변수는 문자열 컬렉션을 나타내는 OutputBinding<T> 형식입니다. 이러한 문자열은 함수가 완료될 때 출력 바인딩에 메시지로 기록됩니다. 이 경우 출력은 outqueue라는 스토리지 큐입니다. 스토리지 계정에 대한 연결 문자열은 connection 메서드로 설정됩니다. 연결 문자열 자체를 전달하는 대신 스토리지 계정 연결 문자열이 포함된 애플리케이션 설정을 전달합니다.

이제 run 메서드 정의는 다음 예와 같아야 합니다.

@FunctionName("HttpTrigger-Java")
public HttpResponseMessage run(
        @HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.FUNCTION)  
        HttpRequestMessage<Optional<String>> request, 
        @QueueOutput(name = "msg", queueName = "outqueue", connection = "AzureWebJobsStorage") 
        OutputBinding<String> msg, final ExecutionContext context) {
    ...
}

바인딩 세부 정보에 대한 자세한 내용은 Azure Functions 트리거 및 바인딩 개념큐 출력 구성을 참조하세요.

출력 바인딩을 사용하는 코드 추가

큐 바인딩이 정의된 상태에서 이제 msg 출력 매개 변수를 받고 메시지를 큐에 쓰도록 함수를 업데이트할 수 있습니다.

다음 코드와 일치하도록 HttpExample\function_app.py 를 업데이트하여 msg 매개 변수를 함수 정의에 추가하고 if name: 문 아래 msg.set(name)를 추가합니다.

import azure.functions as func
import logging

app = func.FunctionApp(http_auth_level=func.AuthLevel.ANONYMOUS)

@app.route(route="HttpExample")
@app.queue_output(arg_name="msg", queue_name="outqueue", connection="AzureWebJobsStorage")
def HttpExample(req: func.HttpRequest, msg: func.Out [func.QueueMessage]) -> func.HttpResponse:
    logging.info('Python HTTP trigger function processed a request.')

    name = req.params.get('name')
    if not name:
        try:
            req_body = req.get_json()
        except ValueError:
            pass
        else:
            name = req_body.get('name')

    if name:
        msg.set(name)
        return func.HttpResponse(f"Hello, {name}. This HTTP triggered function executed successfully.")
    else:
        return func.HttpResponse(
             "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response.",
             status_code=200
        )

msg 매개 변수는 azure.functions.Out class의 인스턴스입니다. set 메서드는 큐에 문자열 메시지를 씁니다. 이 경우 URL 쿼리 문자열의 함수에 전달된 name입니다.

context.bindings에서 msg 출력 바인딩 개체를 사용하여 큐 메시지를 만드는 코드를 추가합니다. context.res 문 앞에 이 코드를 추가합니다.

// Add a message to the Storage queue,
// which is the name passed to the function.
context.bindings.msg = (req.query.name || req.body.name);

이때 함수는 다음과 같이 표시될 수 있습니다.

module.exports = async function (context, req) {
    context.log('JavaScript HTTP trigger function processed a request.');

    if (req.query.name || (req.body && req.body.name)) {
        // Add a message to the Storage queue,
        // which is the name passed to the function.
        context.bindings.msg = (req.query.name || req.body.name);
        context.res = {
            // status: 200, /* Defaults to 200 */
            body: "Hello " + (req.query.name || req.body.name)
        };
    }
    else {
        context.res = {
            status: 400,
            body: "Please pass a name on the query string or in the request body"
        };
    }
};

context.bindings에서 msg 출력 바인딩 개체를 사용하여 큐 메시지를 만드는 코드를 추가합니다. context.res 문 앞에 이 코드를 추가합니다.

context.bindings.msg = name;

이 시점에서 함수는 다음과 같이 표시됩니다.

import { AzureFunction, Context, HttpRequest } from "@azure/functions"

const httpTrigger: AzureFunction = async function (context: Context, req: HttpRequest): Promise<void> {
    context.log('HTTP trigger function processed a request.');
    const name = (req.query.name || (req.body && req.body.name));

    if (name) {
        // Add a message to the storage queue, 
        // which is the name passed to the function.
        context.bindings.msg = name; 
        // Send a "hello" response.
        context.res = {
            // status: 200, /* Defaults to 200 */
            body: "Hello " + (req.query.name || req.body.name)
        };
    }
    else {
        context.res = {
            status: 400,
            body: "Please pass a name on the query string or in the request body"
        };
    }
};

export default httpTrigger;

Push-OutputBinding cmdlet을 사용하여 msg 출력 바인딩을 통해 큐에 텍스트를 작성하는 코드를 추가합니다. if 문에서 확인 상태를 설정하기 전에 이 코드를 추가합니다.

$outputMsg = $name
Push-OutputBinding -name msg -Value $outputMsg

이 시점에서 함수는 다음과 같이 표시됩니다.

using namespace System.Net

# Input bindings are passed in via param block.
param($Request, $TriggerMetadata)

# Write to the Azure Functions log stream.
Write-Host "PowerShell HTTP trigger function processed a request."

# Interact with query parameters or the body of the request.
$name = $Request.Query.Name
if (-not $name) {
    $name = $Request.Body.Name
}

if ($name) {
    # Write the $name value to the queue, 
    # which is the name passed to the function.
    $outputMsg = $name
    Push-OutputBinding -name msg -Value $outputMsg

    $status = [HttpStatusCode]::OK
    $body = "Hello $name"
}
else {
    $status = [HttpStatusCode]::BadRequest
    $body = "Please pass a name on the query string or in the request body."
}

# Associate values to output bindings by calling 'Push-OutputBinding'.
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
    StatusCode = $status
    Body = $body
})

기존 HttpExample 클래스를 다음 코드로 바꿉니다.

    [Function("HttpExample")]
    public static MultiResponse Run([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req,
        FunctionContext executionContext)
    {
        var logger = executionContext.GetLogger("HttpExample");
        logger.LogInformation("C# HTTP trigger function processed a request.");

        var message = "Welcome to Azure Functions!";

        var response = req.CreateResponse(HttpStatusCode.OK);
        response.Headers.Add("Content-Type", "text/plain; charset=utf-8");
        response.WriteString(message);

        // Return a response to both HTTP trigger and storage output binding.
        return new MultiResponse()
        {
            // Write a single message.
            Messages = new string[] { message },
            HttpResponse = response
        };
    }
}

이제 새 msg 매개 변수를 사용하여 함수 코드에서 출력 바인딩에 쓸 수 있습니다. 성공 응답 앞에 다음 코드 줄을 추가하여 name 값을 msg 출력 바인딩에 추가합니다.

msg.setValue(name);

출력 바인딩을 사용하면 인증하거나, 큐 참조를 가져오거나, 데이터를 쓰는 데 Azure Storage SDK 코드를 사용할 필요가 없습니다. Functions 런타임 및 큐 출력 바인딩이 이러한 작업을 알아서 처리합니다.

이제 run 메서드가 다음 예와 같아야 합니다.

public HttpResponseMessage run(
        @HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) 
        HttpRequestMessage<Optional<String>> request, 
        @QueueOutput(name = "msg", queueName = "outqueue", 
        connection = "AzureWebJobsStorage") OutputBinding<String> msg, 
        final ExecutionContext context) {
    context.getLogger().info("Java HTTP trigger processed a request.");

    // Parse query parameter
    String query = request.getQueryParameters().get("name");
    String name = request.getBody().orElse(query);

    if (name == null) {
        return request.createResponseBuilder(HttpStatus.BAD_REQUEST)
        .body("Please pass a name on the query string or in the request body").build();
    } else {
        // Write the name to the message queue. 
        msg.setValue(name);

        return request.createResponseBuilder(HttpStatus.OK).body("Hello, " + name).build();
    }
}

테스트 업데이트

또한 원형은 테스트 세트를 만들기 때문에 run 메서드 시그니처에서 새 msg 매개 변수를 처리하도록 이러한 테스트를 업데이트해야 합니다.

src/test/java에서 테스트 코드의 위치로 이동하여 Function.java 프로젝트 파일을 열고, //Invoke 아래의 코드 줄을 다음 코드로 바꿉니다.

@SuppressWarnings("unchecked")
final OutputBinding<String> msg = (OutputBinding<String>)mock(OutputBinding.class);
final HttpResponseMessage ret = new Function().run(req, msg, context);

인증, 큐 참조 가져오기 또는 데이터 쓰기를 위한 코드를 작성할 필요가 없습니다. 이러한 모든 통합 작업은 Azure Functions 런타임 및 큐 출력 바인딩에서 편리하게 처리됩니다.

로컬에서 함수 실행

  1. LocalFunctionProj 폴더에서 로컬 Azure Functions 런타임 호스트를 시작하여 함수를 실행합니다.

    func start
    

    출력의 끝 부분에 다음 줄이 표시됩니다.

    함수를 로컬로 실행할 때 터미널 창 출력의 스크린샷

    참고 항목

    HttpExample이 위와 같이 표시되지 않으면 프로젝트의 루트 폴더 외부에서 호스트를 시작했을 가능성이 높습니다. 이 경우 Ctrl+C를 사용하여 호스트를 중지하고, 프로젝트의 루트 폴더로 이동하여 이전 명령을 다시 실행합니다.

  2. 이 출력에서 HTTP 함수의 URL을 브라우저에 복사하고 쿼리 문자열 ?name=<YOUR_NAME>을 추가하여 전체 URL을 http://localhost:7071/api/HttpExample?name=Functions와 같이 만듭니다. 브라우저는 쿼리 문자열 값을 다시 에코하는 응답 메시지를 표시해야 합니다. 프로젝트를 시작한 터미널에도 요청 시 로그 출력이 표시됩니다.

  3. 완료되면 Ctrl + C를 누르고 y를 입력하여 함수 호스트를 중지합니다.

시작하는 동안 호스트는 Storage 바인딩 확장 및 기타 Microsoft 바인딩 확장을 다운로드하여 설치합니다. 이 설치는 host.json 파일에서 기본적으로 다음 속성을 사용하여 바인딩 확장을 사용하도록 설정되어 있기 때문에 발생합니다.

{
    "version": "2.0",
    "extensionBundle": {
        "id": "Microsoft.Azure.Functions.ExtensionBundle",
        "version": "[1.*, 2.0.0)"
    }
}

바인딩 확장과 관련된 오류가 발생하면 위의 속성이 host.json에 있는지 확인하세요.

Azure Storage 큐의 메시지 보기

큐는 Azure Portal 또는 Microsoft Azure Storage Explorer에서 볼 수 있습니다. 또한 다음 단계에 설명된 대로 Azure CLI에서 큐를 볼 수도 있습니다.

  1. 함수 프로젝트의 local.setting.json 파일을 열고, 연결 문자열 값을 복사합니다. 터미널 또는 명령 창에서 다음 명령을 실행하여 AZURE_STORAGE_CONNECTION_STRING이라는 환경 변수를 만들고 <MY_CONNECTION_STRING> 대신 특정 연결 문자열을 붙여넣습니다. (이 환경 변수는 --connection-string 인수를 사용하여 연결 문자열을 각 후속 명령에 제공할 필요가 없음을 의미합니다.)

    export AZURE_STORAGE_CONNECTION_STRING="<MY_CONNECTION_STRING>"
    
  2. (선택 사항) az storage queue list 명령을 사용하여 계정의 Storage 큐를 봅니다. 이 명령의 출력에는 함수에서 첫 번째 메시지를 해당 큐에 쓸 때 만들어진 outqueue라는 이름의 큐가 포함되어야 합니다.

    az storage queue list --output tsv
    
  3. az storage message get 명령을 사용하여 이 큐의 메시지를 읽습니다. 이 메시지는 이전에 함수를 테스트할 때 제공한 값이어야 합니다. 명령은 큐에서 첫 번째 메시지를 읽고 제거합니다.

    echo `echo $(az storage message get --queue-name outqueue -o tsv --query '[].{Message:content}') | base64 --decode`
    

    메시지 본문은 Base64로 인코딩되어 저장되기 때문에 메시지를 표시하기 전에 먼저 디코딩해야 합니다. az storage message get을 실행하면 메시지가 큐에서 제거됩니다. outqueue에 메시지가 하나만 있는 경우 이 명령을 두 번째로 실행하면 메시지가 검색되는 대신 오류가 발생합니다.

Azure에 프로젝트 다시 배포

함수가 Azure Storage 큐에 메시지를 쓴 것을 로컬에서 확인했으면, 프로젝트를 다시 배포하여 Azure에서 실행되는 엔드포인트를 업데이트할 수 있습니다.

LocalFunctionsProj 폴더에서 func azure functionapp publish 명령을 사용하여 프로젝트를 다시 배포합니다. 여기서 <APP_NAME>을 앱 이름으로 바꿉니다.

func azure functionapp publish <APP_NAME>

로컬 프로젝트 폴더에서 다음 Maven 명령을 사용하여 프로젝트를 다시 게시합니다.

mvn azure-functions:deploy

Azure에서 확인

  1. 이전 빠른 시작에서와 같이 브라우저 또는 CURL을 사용하여 다시 배포된 함수를 테스트합니다.

    publish 명령의 출력에 표시된 호출 URL 전체를 브라우저 주소 표시줄에 복사하여 &name=Functions 쿼리 매개 변수를 추가합니다. 브라우저에서 함수를 로컬로 실행했을 때와 동일한 출력이 표시됩니다.

  2. 이전 섹션에서 설명한 대로 Storage 큐를 다시 검사하여 큐에 쓴 새 메시지가 포함되어 있는지 확인합니다.

리소스 정리

완료한 후 추가 비용이 발생하지 않도록 다음 명령을 사용하여 리소스 그룹 및 포함된 모든 리소스를 삭제합니다.

az group delete --name AzureFunctionsQuickstart-rg

다음 단계

Storage 큐에 데이터를 쓰도록 HTTP 트리거 함수를 업데이트했습니다. 이제 Core Tools 및 Azure CLI를 사용하여 명령줄에서 함수를 개발하는 방법에 대해 자세히 알아볼 수 있습니다.