빠른 시작: JavaScript를 사용하여 Azure Functions 및 SignalR Service로 서버리스 앱 만들기

이 문서에서는 SignalR Service, Azure Functions 및 JavaScript를 사용하여 서버리스 애플리케이션을 빌드하고 클라이언트에 메시지를 브로드캐스트합니다.

참고 항목

문서에 사용된 모든 코드는 GitHub에서 가져올 수 있습니다.

필수 조건

이 빠른 시작은 macOS, Windows 또는 Linux에서 실행할 수 있습니다.

필수 요소 설명
Azure 구독 구독이 아직 없는 경우 Azure 무료 계정을 만듭니다.
코드 편집기 Visual Studio Code와 같은 코드 편집기가 필요합니다.
Azure Functions Core Tools Python Azure Function 앱을 로컬로 실행하려면 버전 2.7.1505 이상이 필요합니다.
Node.JS Azure Functions JavaScript 개발자 가이드에서 지원되는 node.js 버전을 참조하세요.
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 명령을 참조하세요.

  1. Azure Portal에 로그인합니다.
  2. 페이지의 왼쪽 상단에서 + 리소스 만들기를 선택합니다.
  3. 리소스 만들기 페이지의 검색 서비스 및 마켓플레이스 텍스트 상자에 signalr을 입력한 다음, 목록에서 SignalR Service를 선택합니다.
  4. SignalR Service 페이지에서 만들기를 선택합니다.
  5. 기본 사항 탭에서 새 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 자습서의 네트워킹태그 탭에서 설정을 변경할 필요가 없습니다.

  1. 기본 탭의 아래쪽에서 검토 + 만들기 단추를 선택합니다.
  2. 검토 + 만들기 탭에서 해당 값을 검토한 다음, 만들기를 선택합니다. 배포가 완료되는 데 몇 분 정도 걸립니다.
  3. 배포가 완료되면 리소스로 이동 단추를 선택합니다.
  4. SignalR 리소스 페이지의 Settings 아래 왼쪽 메뉴에서 Keys를 선택합니다.
  5. 기본 키의 연결 문자열을 복사합니다. 이 자습서의 뒷부분에서 앱을 구성하려면 이 연결 문자열이 필요합니다.

함수 프로젝트 설정

Azure Functions Core Tools가 설치되어 있는지 확인합니다.

  1. 명령줄을 엽니다.
  2. 프로젝트 디렉터리를 만든 다음 프로젝트 디렉터리로 변경합니다.
  3. Azure Functions func init 명령을 실행하여 새 프로젝트를 초기화합니다.
# Initialize a function project
func init --worker-runtime javascript

프로젝트 함수 만들기

프로젝트를 초기화한 후에는 함수를 만들어야 합니다. 이 프로젝트에는 다음 세 함수가 필요합니다.

  • index: 클라이언트에 대한 웹 페이지를 호스트합니다.
  • negotiate: 클라이언트가 액세스 토큰을 가져올 수 있습니다.
  • broadcast: 시간 트리거를 사용하여 메시지를 모든 클라이언트에 주기적으로 브로드캐스트합니다.

프로젝트의 루트 디렉터리에서 func new 명령을 실행하는 경우 Azure Functions Core Tools는 함수 이름이 있는 폴더에 저장하는 함수 원본 파일을 만듭니다. 필요에 따라 파일을 편집하여 기본 코드를 앱 코드로 대체합니다.

index 함수 만들기

  1. 다음 명령을 실행하여 index 함수를 만듭니다.

    func new -n index -t HttpTrigger
    
  2. index/function.json을 편집하고 콘텐츠를 다음 json 코드로 바꿉니다.

    {
      "bindings": [
        {
          "authLevel": "anonymous",
          "type": "httpTrigger",
          "direction": "in",
          "name": "req",
          "methods": [
            "get",
            "post"
          ]
        },
        {
          "type": "http",
          "direction": "out",
          "name": "res"
        }
      ]
    }
    
  3. index/index.js를 편집하고 콘텐츠를 다음 코드로 바꿉니다.

    var fs = require('fs').promises
    
    module.exports = async function (context, req) {
        const path = context.executionContext.functionDirectory + '/../content/index.html'
        try {
            var data = await fs.readFile(path);
            context.res = {
                headers: {
                    'Content-Type': 'text/html'
                },
                body: data
            }
            context.done()
        } catch (err) {
            context.log.error(err);
            context.done(err);
        }
    }
    

negotiate 함수 만들기

  1. 다음 명령을 실행하여 negotiate 함수를 만듭니다.

    func new -n negotiate -t HttpTrigger
    
  2. negotiate/function.json을 편집하고 콘텐츠를 다음 json 코드로 바꿉니다.

    {
      "disabled": false,
      "bindings": [
        {
          "authLevel": "anonymous",
          "type": "httpTrigger",
          "direction": "in",
          "methods": [
            "post"
          ],
          "name": "req",
          "route": "negotiate"
        },
        {
          "type": "http",
          "direction": "out",
          "name": "res"
        },
        {
          "type": "signalRConnectionInfo",
          "name": "connectionInfo",
          "hubName": "serverless",
          "connectionStringSetting": "AzureSignalRConnectionString",
          "direction": "in"
        }
      ]
    }
    
  3. negotiate/index.js를 편집하고 콘텐츠를 다음 JavaScript 코드로 바꿉니다.

    module.exports = async function (context, req, connectionInfo) {
        context.res.body = connectionInfo;
    };
    

broadcast 함수를 만듭니다.

  1. 다음 명령을 실행하여 broadcast 함수를 만듭니다.

    func new -n broadcast -t TimerTrigger
    
  2. broadcast/function.json을 편집하고 콘텐츠를 다음 코드로 바꿉니다.

    {
      "bindings": [
        {
          "name": "myTimer",
          "type": "timerTrigger",
          "direction": "in",
          "schedule": "*/5 * * * * *"
        },
        {
          "type": "signalR",
          "name": "signalRMessages",
          "hubName": "serverless",
          "connectionStringSetting": "AzureSignalRConnectionString",
          "direction": "out"
        }
      ]
    }
    
  3. broadcast/index.js를 편집하고 콘텐츠를 다음 코드로 바꿉니다.

    var https = require('https');
    
    var etag = '';
    var star = 0;
    
    module.exports = function (context) {
        var req = https.request("https://api.github.com/repos/azure/azure-signalr", {
            method: 'GET',
            headers: {'User-Agent': 'serverless', 'If-None-Match': etag}
        }, res => {
            if (res.headers['etag']) {
                etag = res.headers['etag']
            }
    
            var body = "";
    
            res.on('data', data => {
                body += data;
            });
            res.on("end", () => {
                if (res.statusCode === 200) {
                    var jbody = JSON.parse(body);
                    star = jbody['stargazers_count'];
                }
    
                context.bindings.signalRMessages = [{
                    "target": "newMessage",
                    "arguments": [ `Current star count of https://github.com/Azure/azure-signalr is: ${star}` ]
                }]
                context.done();
            });
        }).on("error", (error) => {
            context.log(error);
            context.res = {
              status: 500,
              body: error
            };
            context.done();
        });
        req.end();
    }
    

index.html 파일 만들기

이 앱의 클라이언트 인터페이스는 웹 페이지입니다. index 함수는 broadcast/index.html 파일에서 HTML 콘텐츠를 읽습니다.

  1. 프로젝트 루트 폴더에 content 폴더를 만듭니다.

  2. 파일 content/index.html을 만듭니다.

  3. 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 Functions를 사용하려면 스토리지 계정이 작동해야 합니다. Azure Storage Emulator를 설치하고 실행할 수 있습니다. 또는 다음 명령으로 실제 스토리지 계정을 사용하는 설정을 업데이트할 수 있습니다. bash func settings add AzureWebJobsStorage "<storage-connection-string>"

이제 거의 완료되었습니다. 마지막 단계는 Azure Function 앱 설정에 SignalR Service의 연결 문자열을 설정하는 것입니다.

  1. Azure Portal에서 이전에 배포한 SignalR 인스턴스로 이동합니다.

  2. SignalR Service 인스턴스의 연결 문자열을 보려면 를 선택합니다.

    Screenshot of Azure SignalR service Keys page.

  3. 기본 연결 문자열을 복사하고 명령을 실행합니다.

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에서 리포지토리에 별표를 표시하거나 별표 표시를 해제하면 몇 초마다 새로 고친 수가 표시됩니다.

문제가 있나요? 문제 해결 가이드를 사용해 보거나 알려주세요.

리소스 정리

이 앱을 계속 사용하지 않으려면 다음 단계에 따라 이 빠른 시작에서 만든 리소스를 모두 삭제하세요. 요금은 발생되지 않습니다.

  1. Azure Portal에서 맨 왼쪽에 있는 리소스 그룹을 선택한 다음, 만든 리소스 그룹을 선택합니다. 또는 검색 상자를 사용하여 이름으로 리소스 그룹을 찾을 수 있습니다.

  2. 열린 창에서 리소스 그룹을 선택한 다음, 리소스 그룹 삭제를 클릭합니다.

  3. 새 창에서 삭제할 리소스 그룹의 이름을 입력한 다음, 삭제를 클릭합니다.

다음 단계

이 빠른 시작에서는 localhost에서 실시간 서버리스 애플리케이션을 빌드하고 실행했습니다. 그런 다음, SignalR Service를 사용하여 클라이언트와 Azure Function 간 양방향 통신을 수행하는 방법을 자세히 알아봅니다.