자습서: Azure Functions 및 Azure Web PubSub 서비스를 사용하여 서버리스 알림 앱 만들기

Azure Web PubSub 서비스를 사용하면 WebSocket을 사용하여 실시간 메시징 웹 애플리케이션을 쉽게 빌드할 수 있습니다. Azure Functions는 인프라를 관리하지 않고 코드를 실행할 수 있는 서버리스 플랫폼입니다. 이 자습서에서는 Azure Web PubSub 서비스 및 Azure Functions를 사용하여 알림 시나리오의 실시간 메시징과 함께 서버리스 애플리케이션을 빌드하는 방법을 알아봅니다.

이 자습서에서는 다음을 하는 방법을 알아볼 수 있습니다.

  • 서버리스 알림 앱 빌드
  • Web PubSub 함수 입력 및 출력 바인딩 작업
  • 로컬로 샘플 함수 실행
  • Azure Function 앱에 함수 배포

필수 조건

Azure를 구독하고 있지 않다면 시작하기 전에 Azure 체험 계정을 만듭니다.

Azure에 로그인

Azure 계정을 사용하여 https://portal.azure.com/에서 Azure Portal에 로그인합니다.

Azure Web PubSub 서비스 인스턴스 만들기

애플리케이션이 Azure의 Web PubSub 서비스 인스턴스에 연결됩니다.

  1. Azure Portal의 왼쪽 위에 있는 새로 만들기 단추를 선택합니다. 새 화면의 검색 상자에서 Web PubSub를 입력하고 Enter를 누릅니다. (Web 범주에서 Azure Web PubSub를 검색할 수도 있습니다.)

    Screenshot of searching the Azure Web PubSub in portal.

  2. 검색 결과에서 Web PubSub를 선택한 다음, 만들기를 선택합니다.

  3. 다음 설정을 입력합니다.

    설정 제안 값 설명
    리소스 이름 전역적으로 고유한 이름 새 Web PubSub 서비스 인스턴스를 식별하는 전역적으로 고유한 이름입니다. 유효한 문자는 a-z, A-Z, 0-9-입니다.
    구독 구독 이 새 Web PubSub 서비스 인스턴스가 생성되는 Azure 구독입니다.
    리소스 그룹 myResourceGroup Web PubSub 서비스 인스턴스를 만들 새 리소스 그룹의 이름입니다.
    위치 미국 서부 가까운 지역을 선택합니다.
    가격 책정 계층 Free 먼저 Azure Web PubSub 서비스 평가판을 사용할 수 있습니다. Azure Web PubSub 서비스 가격 책정 계층에 대해 자세히 알아보세요.
    단위 수 - 단위 수는 Web PubSub 서비스 인스턴스가 허용할 수 있는 연결 수를 지정합니다. 각 단위는 최대 1,000개의 동시 연결을 지원합니다. 표준 계층에서만 구성할 수 있습니다.

    Screenshot of creating the Azure Web PubSub instance in portal.

  4. 만들기를 선택하여 Web PubSub 서비스 인스턴스 배포를 시작하세요.

로컬로 함수 만들기 및 실행

  1. Azure Functions Core Tools가 설치되어 있는지 확인합니다. 이제 프로젝트에 대한 빈 디렉터리를 만듭니다. 이 작업 디렉터리 아래에서 명령을 실행합니다. 아래의 지정된 옵션 중 하나를 사용합니다.

    func init --worker-runtime javascript --model V4
    
  2. 다음 단계에 따라 Microsoft.Azure.WebJobs.Extensions.WebPubSub을 설치합니다.

    Web PubSub 지원을 가져오려면 host.json의 extensionBundle을 확인하거나 버전 4.* 이상으로 업데이트합니다. host.json을 업데이트하려면 편집기에서 파일을 연 다음, 기존 버전 extensionBundle을 버전 4.* 이상으로 바꿉니다.

    {
        "extensionBundle": {
            "id": "Microsoft.Azure.Functions.ExtensionBundle",
            "version": "[4.*, 5.0.0)"
        }
    }
    
  3. 클라이언트에 대한 정적 웹 페이지를 읽고 호스팅하는 index 함수를 만듭니다.

    func new -n index -t HttpTrigger
    
    • src/functions/index.js를 업데이트하고 다음 코드를 복사합니다.
      const { app } = require('@azure/functions');
      const { readFile } = require('fs/promises');
      
      app.http('index', {
          methods: ['GET', 'POST'],
          authLevel: 'anonymous',
          handler: async (context) => {
              const content = await readFile('index.html', 'utf8', (err, data) => {
                  if (err) {
                      context.err(err)
                      return
                  }
              });
      
              return { 
                  status: 200,
                  headers: { 
                      'Content-Type': 'text/html'
                  }, 
                  body: content, 
              };
          }
      });
      
  4. 클라이언트가 액세스 토큰과 함께 서비스 연결 URL을 얻을 수 있도록 negotiate 함수를 만듭니다.

    func new -n negotiate -t HttpTrigger
    
    • src/functions/negotiate.js를 업데이트하고 다음 코드를 복사합니다.
      const { app, input } = require('@azure/functions');
      
      const connection = input.generic({
          type: 'webPubSubConnection',
          name: 'connection',
          hub: 'notification'
      });
      
      app.http('negotiate', {
          methods: ['GET', 'POST'],
          authLevel: 'anonymous',
          extraInputs: [connection],
          handler: async (request, context) => {
              return { body: JSON.stringify(context.extraInputs.get('connection')) };
          },
      });
      
  5. TimerTrigger를 사용하여 알림을 생성하도록 notification 함수를 만듭니다.

    func new -n notification -t TimerTrigger
    
    • src/functions/notification.js를 업데이트하고 다음 코드를 복사합니다.
      const { app, output } = require('@azure/functions');
      
      const wpsAction = output.generic({
          type: 'webPubSub',
          name: 'action',
          hub: 'notification'
      });
      
      app.timer('notification', {
          schedule: "*/10 * * * * *",
          extraOutputs: [wpsAction],
          handler: (myTimer, context) => {
              context.extraOutputs.set(wpsAction, {
                  actionName: 'sendToAll',
                  data: `[DateTime: ${new Date()}] Temperature: ${getValue(22, 1)}\xB0C, Humidity: ${getValue(40, 2)}%`,
                  dataType: 'text',
              });
          },
      });
      
      function getValue(baseNum, floatNum) {
          return (baseNum + 2 * floatNum * (Math.random() - 0.5)).toFixed(3);
      }
      
  6. 프로젝트 루트 폴더에서 클라이언트 단일 페이지(index.html)를 추가하고 콘텐츠를 복사합니다.

    <html>
        <body>
        <h1>Azure Web PubSub Notification</h1>
        <div id="messages"></div>
        <script>
            (async function () {
                let messages = document.querySelector('#messages');
                let res = await fetch(`${window.location.origin}/api/negotiate`);
                let url = await res.json();
                let ws = new WebSocket(url.url);
                ws.onopen = () => console.log('connected');
    
                ws.onmessage = event => {
                    let m = document.createElement('p');
                    m.innerText = event.data;
                    messages.appendChild(m);
                };
            })();
        </script>
        </body>
    </html>
    
  7. Azure 함수 앱을 구성하고 실행합니다.

    • 브라우저에서 Azure Portal을 열고 이전에 배포한 Web PubSub 서비스 인스턴스가 성공적으로 만들어졌는지 확인합니다. 인스턴스로 이동합니다.
    • 를 선택하고 연결 문자열을 복사합니다.

    Screenshot of copying the Web PubSub connection string.

    함수 폴더의 명령을 실행하여 서비스 연결 문자열을 설정합니다. 필요에 따라 <connection-string>을 해당 값으로 바꿉니다.

    func settings add WebPubSubConnectionString "<connection-string>"
    

    참고 항목

    샘플에 사용된 TimerTrigger에 Azure Storage에 대한 종속성이 있지만 함수가 로컬로 실행될 때 로컬 스토리지 에뮬레이터를 사용할 수 있습니다. There was an error performing a read operation on the Blob Storage Secret Repository. Please ensure the 'AzureWebJobsStorage' connection string is valid.와 같은 오류가 표시되면 스토리지 에뮬레이터를 다운로드하고 사용하도록 설정해야 합니다.

    이제 명령으로 로컬 함수를 실행할 수 있습니다.

    func start --port 7071
    

    실행 중인 로그를 확인하고 http://localhost:7071/api/index를 방문하여 로컬 호스트 정적 페이지를 방문할 수 있습니다.

    참고 항목

    일부 브라우저는 잘못된 URL로 연결되는 https로 자동 리디렉션됩니다. Edge 사용을 제안하고 렌더링이 성공하지 못한 경우 URL을 다시 확인합니다.

Azure에 함수 앱 배포

함수 코드를 Azure에 배포하기 전에 다음 세 가지 리소스를 만들어야 합니다.

  • 리소스 그룹 - 관련 리소스에 대한 논리 컨테이너입니다.
  • 스토리지 계정 - 함수에 대한 상태 및 기타 정보를 유지 관리합니다.
  • 함수 앱 - 함수 코드를 실행할 수 있는 환경을 제공합니다. 함수 앱은 로컬 함수 프로젝트에 매핑되며, 함수를 논리적 단위로 그룹화하여 리소스를 더 쉽게 관리, 배포 및 공유할 수 있습니다.

다음 명령을 사용하여 이러한 항목을 만듭니다.

  1. Azure에 로그인:

    az login
    
  2. 리소스 그룹을 만들거나, Azure Web PubSub 서비스 중 하나를 다시 사용하여 건너뛸 수 있습니다.

    az group create -n WebPubSubFunction -l <REGION>
    
  3. 범용 스토리지 계정을 리소스 그룹 및 지역에 만듭니다.

    az storage account create -n <STORAGE_NAME> -l <REGION> -g WebPubSubFunction
    
  4. Azure에서 함수 앱을 만듭니다.

    az functionapp create --resource-group WebPubSubFunction --consumption-plan-location <REGION> --runtime node --runtime-version 18 --functions-version 4 --name <FUNCIONAPP_NAME> --storage-account <STORAGE_NAME>
    

    참고 항목

    Azure Functions 런타임 버전 설명서를 확인하여 --runtime-version 매개 변수를 지원되는 값으로 설정하세요.

  5. 함수 프로젝트를 Azure에 배포합니다.

    Azure에서 함수 앱을 성공적으로 만들었으면 이제 func azure functionapp publish 명령을 사용하여 로컬 함수 프로젝트를 배포할 준비가 되었습니다.

    func azure functionapp publish <FUNCIONAPP_NAME> --publish-local-settings
    

    참고 항목

    여기서는 로컬 설정(local.settings.json)을 명령 매개 변수(--publish-local-settings)와 함께 배포합니다. Microsoft Azure Storage 에뮬레이터를 사용하는 경우 프롬프트 메시지(App setting AzureWebJobsStorage is different between azure and local.settings.json, Would you like to overwrite value in azure? [yes/no/show])에 따라 Azure에서 이 값의 덮어쓰기를 건너뛰려면 no를 입력할 수 있습니다. 또한 Azure Portal ->설정 ->구성에서 함수 앱 설정을 업데이트할 수 있습니다.

  6. 이제 https://<FUNCIONAPP_NAME>.azurewebsites.net/api/index URL로 이동하여 Azure Function App에서 사이트를 확인할 수 있습니다.

리소스 정리

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

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

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

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

다음 단계

이 빠른 시작에서는 서버리스 채팅 애플리케이션을 실행하는 방법을 알아보았습니다. 이제 자체 애플리케이션을 빌드할 수 있습니다.