빠른 시작: Python에서 Azure Functions 및 Azure SignalR Service로 서버리스 앱 만들기
Azure Functions 및 Python으로 클라이언트에 메시지를 브로드캐스트하는 서버리스 애플리케이션을 빌드하여 Azure SignalR Service를 시작합니다. 로컬 환경에서 함수를 실행하여 클라우드의 Azure SignalR Service instance에 연결합니다. 이 빠른 시작을 완료하면 Azure 계정에서 USD 센트 이하의 작은 비용이 발생합니다.
참고 항목
이 문서의 코드는 GitHub에서 가져올 수 있습니다.
Important
원시 연결 문자열 데모용으로만 이 문서에 표시됩니다.
연결 문자열에는 애플리케이션이 Azure Web PubSub 서비스에 액세스하는 데 필요한 권한 부여 정보가 포함됩니다. 연결 문자열 내의 액세스 키는 서비스의 루트 암호와 비슷합니다. 프로덕션 환경에서는 항상 액세스 키를 보호합니다. Azure Key Vault를 사용하여 키를 안전하게 관리 및 회전하고 Microsoft Entra ID를 사용하여 연결 문자열 보호합니다.
액세스 키를 다른 사용자에게 배포하거나 하드 코딩하거나 다른 사용자가 액세스할 수 있는 일반 텍스트로 저장하지 않도록 합니다. 키가 손상되었다고 생각되면 키를 교체하세요.
필수 조건
이 빠른 시작은 macOS, Windows 또는 Linux에서 실행할 수 있습니다. 다음이 필요합니다.
필수 요소 | 설명 |
---|---|
Azure 구독 | Azure 구독이 없는 경우 Azure 무료 계정을 만들 수 있습니다. |
코드 편집기 | Visual Studio Code와 같은 코드 편집기가 필요합니다. |
Azure Functions Core Tools | Python Azure Function 앱을 로컬로 실행하려면 버전 2.7.1505 이상이 필요합니다. |
Python 3.7+ | Azure Functions에는 Python 3.7 이상이 필요합니다. 지원되는 Python 버전을 참조하세요. |
Azurite | SignalR 바인딩에는 Azure Storage가 필요합니다. 함수가 로컬로 실행 중일 때 로컬 스토리지 에뮬레이터를 사용할 수 있습니다. |
Azure CLI | 필요에 따라 Azure CLI를 사용하여 Azure SignalR Service 인스턴스를 만들 수 있습니다. |
Azure SignalR Service 인스턴스 만들기
이 섹션에서는 앱에 사용할 기본 Azure SignalR 인스턴스를 만듭니다. 다음 단계에서는 Azure Portal을 사용하여 새 인스턴스를 만들지만 Azure CLI를 사용할 수도 있습니다. 자세한 내용은 Azure SignalR Service CLI 참조에서 az signalr create 명령을 참조하세요.
- Azure Portal에 로그인합니다.
- 페이지의 왼쪽 상단에서 + 리소스 만들기를 선택합니다.
- 리소스 만들기 페이지의 검색 서비스 및 마켓플레이스 텍스트 상자에 signalr을 입력한 다음, 목록에서 SignalR Service를 선택합니다.
- SignalR Service 페이지에서 만들기를 선택합니다.
- 기본 사항 탭에서 새 SignalR Service 인스턴스에 대한 필수 정보를 입력합니다. 다음 값을 입력합니다.
필드 | 제안 값 | 설명 |
---|---|---|
구독 | 구독 선택 | 새 SignalR Service 인스턴스를 만드는 데 사용할 구독을 선택합니다. |
리소스 그룹 | SignalRTestResources라는 리소스 그룹 만들기 | SignalR 리소스에 대한 리소스 그룹을 선택하거나 만듭니다. 기존 리소스 그룹을 사용하는 대신 이 자습서에 대한 새 리소스 그룹을 만드는 것이 유용합니다. 자습서를 완료한 후 리소스를 확보하려면 리소스 그룹을 삭제합니다. 리소스 그룹을 삭제하면 해당 그룹에 속한 리소스도 모두 삭제됩니다. 이 작업은 취소할 수 없습니다. 리소스 그룹을 삭제하기 전에 유지하려는 리소스가 포함되어 있지 않은지 확인합니다. 자세한 내용은 리소스 그룹을 사용하여 Azure 리소스 관리를 참조하세요. |
리소스 이름 | testsignalr | SignalR 리소스에 사용할 고유한 리소스 이름을 입력합니다. 해당 지역에서 이미 testsignalr을 사용하고 있는 경우 이름이 고유해질 때까지 숫자나 문자를 추가합니다. 이름은 1~63자의 문자열로, 숫자, 영문자 및 하이픈( - ) 문자만 포함할 수 있습니다. 이름은 하이픈 문자로 시작하거나 끝날 수 없고 연속되는 하이픈 문자는 유효하지 않습니다. |
지역 | 지역 선택 | 새 SignalR Service 인스턴스에 적합한 지역을 선택합니다. Azure SignalR Service는 현재 일부 지역에서 사용할 수 없습니다. 자세한 내용은 Azure SignalR Service 지역 가용성을 참조하세요. |
가격 책정 계층 | 변경을 선택한 다음, 무료(개발/테스트 전용)를 선택합니다. 선택을 선택하여 선택한 가격 책정 계층을 확인합니다. | Azure SignalR Service에는 무료, 표준 및 프리미엄의 세 가지 가격 책정 계층이 있습니다. 자습서는 필수 구성 요소에 달리 명시되지 않는 한 무료 계층을 사용합니다. 계층과 가격 책정 간의 기능 차이에 대한 자세한 내용은 Azure SignalR Service 가격 책정을 참조하세요. |
서비스 모드 | 적절한 서비스 모드를 선택합니다. | 웹앱에서 SignalR 허브 논리를 호스팅하고 SignalR 서비스를 프록시로 사용하는 경우 기본값을 사용합니다. Azure Functions와 같은 서버리스 기술을 사용하여 SignalR 허브 논리를 호스팅하는 경우 서버리스를 사용합니다. 클래식 모드는 이전 버전과의 호환성만을 위한 것이므로 사용하지 않는 것이 좋습니다. 자세한 내용은 Azure SignalR Service의 서비스 모드를 참조하세요. |
SignalR 자습서의 네트워킹 및 태그 탭에서 설정을 변경할 필요가 없습니다.
- 기본 탭의 아래쪽에서 검토 + 만들기 단추를 선택합니다.
- 검토 + 만들기 탭에서 해당 값을 검토한 다음, 만들기를 선택합니다. 배포가 완료되는 데 몇 분 정도 걸립니다.
- 배포가 완료되면 리소스로 이동 단추를 선택합니다.
- SignalR 리소스 페이지의 Settings 아래 왼쪽 메뉴에서 Keys를 선택합니다.
- 기본 키의 연결 문자열을 복사합니다. 이 자습서의 뒷부분에서 앱을 구성하려면 이 연결 문자열이 필요합니다.
Azure 함수 프로젝트 만들기
로컬 Azure 함수 프로젝트를 만듭니다.
- 명령줄에서 프로젝트에 대한 디렉터리를 만듭니다.
- 프로젝트 디렉터리로 변경합니다.
- Azure Functions
func init
명령을 사용하여 함수 프로젝트를 초기화합니다.
# Initialize a function project
func init --worker-runtime python
함수 만들기
프로젝트를 초기화한 후에는 함수를 만들어야 합니다. 이 프로젝트에는 다음 세 함수가 필요합니다.
index
: 클라이언트에 대한 웹 페이지를 호스트합니다.negotiate
: 클라이언트가 액세스 토큰을 가져올 수 있습니다.broadcast
: 시간 트리거를 사용하여 메시지를 모든 클라이언트에 주기적으로 브로드캐스트합니다.
프로젝트의 루트 디렉터리에서 func new
명령을 실행하면 Azure Functions Core Tools가 function_app.py
파일에 함수 코드를 추가합니다. 기본 코드를 앱 코드로 바꿔 필요에 따라 매개 변수 광고 콘텐츠를 편집합니다.
index 함수 만들기
이 샘플 함수를 사용자 고유의 함수에 대한 템플릿으로 사용할 수 있습니다.
function_app.py
파일을 엽니다. 이 파일에는 함수가 포함됩니다. 먼저 필요한 import 문을 포함하도록 파일을 수정하고 다음 함수에서 사용할 전역 변수를 정의합니다.
import azure.functions as func
import os
import requests
import json
app = func.FunctionApp()
etag = ''
start_count = 0
- 다음 코드를 추가하여 함수
index
추가
@app.route(route="index", auth_level=func.AuthLevel.ANONYMOUS)
def index(req: func.HttpRequest) -> func.HttpResponse:
f = open(os.path.dirname(os.path.realpath(__file__)) + '/content/index.html')
return func.HttpResponse(f.read(), mimetype='text/html')
이 함수는 클라이언트에 대한 웹 페이지를 호스트합니다.
negotiate 함수 만들기
다음 코드를 추가하여 함수 negotiate
를 추가합니다.
원시 연결 문자열 데모용으로만 이 문서에 표시됩니다. 프로덕션 환경에서는 항상 액세스 키를 보호합니다. Azure Key Vault를 사용하여 키를 안전하게 관리 및 회전하고 Microsoft Entra ID를 사용하여 연결 문자열 보호합니다.
@app.route(route="negotiate", auth_level=func.AuthLevel.ANONYMOUS, methods=["POST"])
@app.generic_input_binding(arg_name="connectionInfo", type="signalRConnectionInfo", hubName="serverless", connectionStringSetting="AzureSignalRConnectionString")
def negotiate(req: func.HttpRequest, connectionInfo) -> func.HttpResponse:
return func.HttpResponse(connectionInfo)
이 함수를 사용하면 클라이언트가 액세스 토큰을 가져올 수 있습니다.
broadcast 함수를 만듭니다.
다음 코드를 추가하여 함수 broadcast
추가
@app.timer_trigger(schedule="*/1 * * * *", arg_name="myTimer",
run_on_startup=False,
use_monitor=False)
@app.generic_output_binding(arg_name="signalRMessages", type="signalR", hubName="serverless", connectionStringSetting="AzureSignalRConnectionString")
def broadcast(myTimer: func.TimerRequest, signalRMessages: func.Out[str]) -> None:
global etag
global start_count
headers = {'User-Agent': 'serverless', 'If-None-Match': etag}
res = requests.get('https://api.github.com/repos/azure/azure-functions-python-worker', headers=headers)
if res.headers.get('ETag'):
etag = res.headers.get('ETag')
if res.status_code == 200:
jres = res.json()
start_count = jres['stargazers_count']
signalRMessages.set(json.dumps({
'target': 'newMessage',
'arguments': [ 'Current star count of https://api.github.com/repos/azure/azure-functions-python-worker is: ' + str(start_count) ]
}))
이 함수는 시간 트리거를 사용하여 메시지를 모든 클라이언트에 주기적으로 브로드캐스트합니다.
Azure 함수 프로젝트 만들기
로컬 Azure 함수 프로젝트를 만듭니다.
- 명령줄에서 프로젝트에 대한 디렉터리를 만듭니다.
- 프로젝트 디렉터리로 변경합니다.
- Azure Functions
func init
명령을 사용하여 함수 프로젝트를 초기화합니다.
# Initialize a function project
func init --worker-runtime python --model v1
함수 만들기
프로젝트를 초기화한 후에는 함수를 만들어야 합니다. 이 프로젝트에는 다음 세 함수가 필요합니다.
index
: 클라이언트에 대한 웹 페이지를 호스트합니다.negotiate
: 클라이언트가 액세스 토큰을 가져올 수 있습니다.broadcast
: 시간 트리거를 사용하여 메시지를 모든 클라이언트에 주기적으로 브로드캐스트합니다.
프로젝트의 루트 디렉터리에서 func new
명령을 실행하는 경우 Azure Functions Core Tools는 함수 이름을 따서 명명한 폴더에 저장하는 기본 함수 원본 파일을 만듭니다. 필요에 따라 파일을 편집하여 기본 코드를 앱 코드로 대체합니다.
index 함수 만들기
이 샘플 함수를 사용자 고유의 함수에 대한 템플릿으로 사용할 수 있습니다.
- 다음 명령을 실행하여
index
함수를 만듭니다.
func new -n index -t HttpTrigger
- index/function.json을 편집하고 콘텐츠를 다음 json 코드로 바꿉니다.
{
"bindings": [
{
"authLevel": "anonymous",
"type": "httpTrigger",
"direction": "in",
"name": "req",
"methods": [
"get",
"post"
]
},
{
"type": "http",
"direction": "out",
"name": "$return"
}
]
}
- index/_init_.py를 편집하고 콘텐츠를 다음 코드로 바꿉니다.
import os
import azure.functions as func
def main(req: func.HttpRequest) -> func.HttpResponse:
f = open(os.path.dirname(os.path.realpath(__file__)) + '/../content/index.html')
return func.HttpResponse(f.read(), mimetype='text/html')
negotiate 함수 만들기
- 다음 명령을 실행하여
negotiate
함수를 만듭니다.
func new -n negotiate -t HttpTrigger
- negotiate/function.json을 편집하고 콘텐츠를 다음 json 코드로 바꿉니다.
{
"scriptFile": "__init__.py",
"bindings": [
{
"authLevel": "anonymous",
"type": "httpTrigger",
"direction": "in",
"name": "req",
"methods": [
"post"
]
},
{
"type": "http",
"direction": "out",
"name": "$return"
},
{
"type": "signalRConnectionInfo",
"name": "connectionInfo",
"hubName": "serverless",
"connectionStringSetting": "AzureSignalRConnectionString",
"direction": "in"
}
]
}
- negotiate/_init_.py를 편집하고 콘텐츠를 다음 코드로 바꿉니다.
import azure.functions as func
def main(req: func.HttpRequest, connectionInfo) -> func.HttpResponse:
return func.HttpResponse(connectionInfo)
broadcast 함수를 만듭니다.
- 다음 명령을 실행하여
broadcast
함수를 만듭니다.
func new -n broadcast -t TimerTrigger
# install requests
pip install requests
- broadcast/function.json을 편집하고 콘텐츠를 다음 코드로 바꿉니다.
{
"scriptFile": "__init__.py",
"bindings": [
{
"name": "myTimer",
"type": "timerTrigger",
"direction": "in",
"schedule": "*/5 * * * * *"
},
{
"type": "signalR",
"name": "signalRMessages",
"hubName": "serverless",
"connectionStringSetting": "AzureSignalRConnectionString",
"direction": "out"
}
]
}
- broadcast/_init_.py을 편집하고 콘텐츠를 다음 코드로 바꿉니다.
import requests
import json
import azure.functions as func
etag = ''
start_count = 0
def main(myTimer: func.TimerRequest, signalRMessages: func.Out[str]) -> None:
global etag
global start_count
headers = {'User-Agent': 'serverless', 'If-None-Match': etag}
res = requests.get('https://api.github.com/repos/azure/azure-signalr', headers=headers)
if res.headers.get('ETag'):
etag = res.headers.get('ETag')
if res.status_code == 200:
jres = res.json()
start_count = jres['stargazers_count']
signalRMessages.set(json.dumps({
'target': 'newMessage',
'arguments': [ 'Current star count of https://github.com/Azure/azure-signalr is: ' + str(start_count) ]
}))
index.html 파일 만들기
이 앱의 클라이언트 인터페이스는 웹 페이지입니다. index
함수는 broadcast/index.html 파일에서 HTML 콘텐츠를 읽습니다.
- 프로젝트 루트 폴더에
content
폴더를 만듭니다. - 파일 content/index.html을 만듭니다.
- content/index.html 파일에 다음 콘텐츠를 복사하고 저장합니다.
<html>
<body>
<h1>Azure SignalR Serverless Sample</h1>
<div id="messages"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/3.1.7/signalr.min.js"></script>
<script>
let messages = document.querySelector('#messages');
const apiBaseUrl = window.location.origin;
const connection = new signalR.HubConnectionBuilder()
.withUrl(apiBaseUrl + '/api')
.configureLogging(signalR.LogLevel.Information)
.build();
connection.on('newMessage', (message) => {
document.getElementById("messages").innerHTML = message;
});
connection.start()
.catch(console.error);
</script>
</body>
</html>
함수 앱 설정에 SignalR Service 연결 문자열 추가
마지막 단계는 Azure Function 앱 설정에 SignalR Service의 연결 문자열을 설정하는 것입니다.
Azure Portal에서 이전에 배포한 SignalR 인스턴스로 이동합니다.
SignalR Service 인스턴스의 연결 문자열을 보려면 키를 선택합니다.
기본 연결 문자열을 복사하고 명령을 실행합니다.
func settings add AzureSignalRConnectionString "<signalr-connection-string>"
Azure Functions 앱을 로컬로 실행
Azurite 스토리지 에뮬레이터를 시작:
azurite
로컬 환경에서 Azure Function 앱을 실행합니다.
func start
참고 항목
Blob Storage에서 읽기 오류를 보여 주는 오류가 표시되면 local.settings.json 파일의 'AzureWebJobsStorage' 설정이 UseDevelopmentStorage=true
로 설정되어 있는지 확인합니다.
Azure Function이 로컬로 실행되면 http://localhost:7071/api/index
로 이동합니다. 이 페이지에는 GitHub Azure/azure-signalr 리포지토리에 대한 현재 star 수가 표시됩니다. GitHub에서 리포지토리에 별표를 표시하거나 별표 표시를 해제하면 몇 초마다 새로 고친 수가 표시됩니다.
리소스 정리
이 앱을 계속 사용하지 않으려면 다음 단계에 따라 이 빠른 시작에서 만든 리소스를 모두 삭제하세요. 요금은 발생되지 않습니다.
Azure Portal에서 맨 왼쪽에 있는 리소스 그룹을 선택한 다음, 만든 리소스 그룹을 선택합니다. 또는 검색 상자를 사용하여 이름으로 리소스 그룹을 찾을 수 있습니다.
열린 창에서 리소스 그룹을 선택한 다음, 리소스 그룹 삭제를 클릭합니다.
새 창에서 삭제할 리소스 그룹의 이름을 입력한 다음, 삭제를 클릭합니다.
문제가 있나요? 문제 해결 가이드를 사용해 보거나 알려주세요.
다음 단계
이 빠른 시작에서는 로컬에서 실시간 서버리스 애플리케이션을 빌드하고 실행했습니다. 그런 다음, SignalR Service를 사용하여 클라이언트와 Azure Function 간 양방향 통신을 사용하는 방법을 자세히 알아봅니다.