Azure Functions & Azure Service Bus(MQ)로 서버리스 아키텍처 구축하기
클라우드의 개념과 Azure를 활용한 서버리스 아키텍처 구축에 대한 포스팅입니다.
📌 Overview
요즘 클라우드 플랫폼은 개발자의 필수 스킬 중 하나로 자리 잡은 거 같다. 그만큼 수요도 많고 또 중요하다는 의미일 것이다. 나는 정보처리기사 취득 과정에서 IaaS, PaaS, SaaS 등 클라우드 이론을 처음 공부하게 되었다. 당시에 습득한 이론은 단편적이었는데 시간이 지나면서 클라우드 서비스를 실제로 어떻게 생성하고 어떻게 배포하는지 호기심이 생기게 되었다.
그래서 Microsoft Azure의 무료 크레딧으로 무작정 독학을 시작하였는데 생각보다 참고할 만한 자료가 적었다. 공식 문서에 이론적인 내용은 많았지만 Portal에서 어떻게 설정해야 하는지 찾기 어려웠달까,, 어찌 되었든 삽질 끝에 서버리스 아키텍처 구축까지 마칠 수 있었고 나름대로의 학습 내용과 과정을 기록한다.
📌 Azure 클라우드 플랫폼이란?
클라우드 플랫폼은 사용자에게 컴퓨팅 자원(서버, 스토리지, 네트워크 등)을 빌려주고 사용한 만큼 돈을 받는다. 사용자는 클라우드 환경에서 서버, 플랫폼, 단일 소프트웨어 등을 가상화하여 서비스로 제공할 수 있다. 실제 물리적 서버와 가상 인프라는 여전히 존재하지만 그 운영 및 관리 책임의 많은 부분을 공급자(Azure, AWS 등)가 대신해 주기 때문에 개발자는 애플리케이션과 비즈니스 로직에 더 집중할 수 있다.
Azure는 마이크로소프트에서 제공하는 클라우드 플랫폼이다. 클라우드 서비스는 제공 범위에 따라 IaaS, PaaS, SaaS로 구분되는데 Azure는 PaaS 형태로 서비스를 지원한다.
각각 어떤 차이점이 있는지 알아보자.

출처: WhaTap
✨ IaaS / PaaS / SaaS
🌊 IaaS(Infrastructure as a Service)
IaaS는 인프라를 서비스로 제공한다. 서버, 스토리지, 네트워크 등 하드웨어 인프라를 가상화하여 제공하기 때문에, 사용자는 편리한 인프라 구매, 관리, 유지보수가 가능하다. 이를 통해 인건비 및 임대료를 절감할 수 있다.
기존 시스템을 마이그레이션하는 경우 사용하며 AWS EC2, Azure Virtual Machines가 IaaS에 해당한다.
🌊 PaaS(Platform as a Service)
PaaS는 개발 및 배포에 필요한 플랫폼을 서비스로 제공한다. 소프트웨어와 하드웨어 환경을 지원하기 때문에, 사용자는 개발에 필요한 인프라만 구매하여 편리한 운영 및 배포가 가능하다. 애플리케이션 서비스만 집중하여 개발하면 되지만, 플랫폼에 종속되어 이후 이관이 어렵다는 단점이 있다.
애플리케이션 개발에 집중하여 빠른 배포와 스케일링이 필요할 때 사용하며 Azure Functions, Google App Engine가 PaaS에 해당한다.
🌊 SaaS(Software as a Service)
SaaS는 완전한 애플리케이션을 클라우드 서비스 형태로 제공한다. 사용자는 최소한의 설정만으로 애플리케이션을 사용할 수 있다. 메일 서비스나 MS 오피스 같은 유형이다. 애플리케이션을 포함한 소프트웨어 단까지 클라우드 서비스로 제공되기 때문에 일반적으로 웹 브라우저를 통해 사용한다.
표준화된 업무 시스템을 도입할 때 사용하며 Gmail이나 MS-Office가 SaaS에 해당한다.
✨ Azure만의 차별점
그렇다면 Azure만의 차별점은 무엇일까? 🤔
Azure는 사용자(기업)의 온 프레미스 레거시를 클라우드 환경과 원할하게 통합 가능한 서비스를 제공한다. 이것을 하이브리드 클라우드 솔루션이라고 한다.
온 프레미스 레거시는 중요한 데이터를 직접 관리하는 환경이다.
클라우드는 필요시 리소스를 빌려 쓸 수 있는 환경이다.
하이브리드는 온 프레미스 + 클라우드를 통해 중요한 데이터를 인증과 모니터링을 통해 클라우드 환경으로 이관하는 것이다.
연동 절차는 다음과 같다.
- 네트워크 연결 구축: ExpressRoute/VPN으로 온 프레미스와 Azure 연결
- ID 동기화: Azure AD Connect로 사용자 계정과 그룹 연동
- 데이터 연동: Azure File Sync 또는 DB 복제/Managed Instance로 데이터 복제
- 관리·모니터링 통합: Azure Monitor 에이전트로 로그 및 성능을 중앙에서 확인
- 보안 정책 적용: Azure Policy와 보안 센터로 컴플라이언스 적용
- 장애 대응 준비: Site Recovery로 장애 시 빠른 복구 경로 구성
무조건 하이브리드 방식이 좋다는 건 아니다. 트래픽의 변동으로 일시적 확장이 필요하거나, 클라우드 네이티브 기능을 도입하고 싶을 때 등등 온 프레미스 환경에서 레거시 운영의 한계가 발생할 때 Azure와 같은 클라우드를 도입하는 것이 적합하다고 생각한다. 목적에 따라 AWS, GCP를 고려하는 것도 중요하다.
📌 서버리스 아키텍처란?
✨ 내가 해석한 서버리스 아키텍처
서버리스 아키텍처(Serverless Architecture)는 모순적이게도 서버가 없다는 뜻이 아니다.
나무위키에 정의된 의미
서버 프로그래머는 함수 서비스에다가 함수를 구현해 놓고, 필요한 만큼의 메모리 캐시 서비스를 예약하고, 데이터베이스 서비스에 들어갈 데이터 구조를 정의하면 끝.
따라서 서버리스 아키텍처는 그 명칭과 달리 서버가 전혀 없는 아키텍처가 아니다. 그 뒷단에는 여전히 실제 서버들이 구성되어 돌아가고 있으며 단지 서버를 구성하는데 필요한 부분들을 서비스화하고 단순화시켜 직접 서버를 구축함으로써 발생하는 애로사항 등을 줄이고자 하는 것이다.
내가 해석한 서버리스 아키텍처는 엔지니어가 서버 인프라를 신경 쓰지 않고 애플리케이션 개발에만 집중할 수 있도록 관리 포인트를 줄여주는 아키텍처이다.
예를 들어, 일반적인 방법으로 서버를 개발하고 운영한다면 정상 동작을 확인하기 위해 주기적인 Health-Check를 해주어야 하지만 클라우드 환경에서 서버 관리는 공급자(Azure)의 몫이기에 엔지니어가 인터벌마다 Health-Check를 하지 않아도 된다.
또한 병목 구간이 발생했을 때 코드에서 문제점을 찾기 보다 클라우드 환경에서 해결이 가능하다. (장기적으로는 코드 개선이 올바른 해결 방법이다.) 서버의 인스턴스를 추가하는 스케일 아웃(Scale-out)과 장치 리소스를 업그레이드하는 스케일 업(Scale-up)을 통해 독립적인 성능 확장이 가능하기 때문이다.
결국 서버리스 아키텍처의 서버는 클라우드에 존재한다. 관리해야 하는 서버나 컨테이너가 없기 때문에 엔지니어가 클라우드 환경에서 네이티브 개발을 진행해야 한다. 제공자(Azure)에게 애플리케이션을 실행하는데 필요한 리소스와 스토리지를 동적으로 할당하고, 엔지니어는 그 부분에서 발생하는 비용을 지불한다.
서버리스 아키텍처는 역할과 제공 범위에 따라 FaaS와 BaaS로 구분되는데 아래에서 살펴보자.
✨ FaaS(Function as a Service)
FaaS는 특정 이벤트에 반응하여 실행되는 단일 함수와 같은 작은 코드 조각을 실행하는 클라우드 서비스입니다. 엔지니어는 실행에 필요한 최소 단위의 코드(함수)만 작성 및 배포한다. 공급자(Azure)는 해당 코드를 실행하는 데 필요한 모든 인프라를 관리한다.
이벤트란 HTTP 요청이 Request 받거나, 데이터베이스의 Trigger가 발생하거나, Message Queue에 데이터가 쌓이는 등의 액션을 의미하며, 특정 이벤트가 발생했을 때 코드를 실행하도록 설정할 수 있다.
FaaS는 Stateless이다. 함수는 상태를 유지하지 않으며 각 호출은 독립적으로 처리된다. 함수의 상태를 유지할 수 있지만 그렇게 되면 서버리스 아키텍처를 배반하게 된다.
일시적인 트래픽 증가 시 워커 프로세스와 같은 함수 인스턴스가 자동으로 확장되고, 유휴 시에는 축소된다. 코드가 실제로 실행되는 시간에만 비용이 발생한다.
Azure Functions, AWS Lambda, Google Cloud Functions가 FaaS에 해당한다.
✨ BaaS (Backend as a Service)
BaaS는 백엔드 기능을 서비스 형태로 제공하는 것이다. 인증/인가, 푸시 알림 등 애플리케이션을 개발할 때 자주 사용되는 기능들을 제공한다. 엔지니어는 기능 구현에 시간을 할애하지 않고 이미 만들어진 기능을 API를 통해 사용하여 클라이언트 측 개발에만 집중할 수 있다.
FaaS와 마찬가지로 백엔드 인프라를 관리하지 않아도 된다. 앞서 설명한 것처럼 제공되는 기능들은 애플리케이션에서 API 호출을 통해 쉽게 통합할 수 있다. 요청 수, 저장 용량에 따라 과금되는 BaaS가 체감상 FaaS 보다 가격이 더 나오는 거 같다.
Azure AD B2C, Firebase Authentication, Firebase Cloud Messaging(FCM)가 BaaS에 해당한다.
✨ FaaS와 BaaS의 비교 및 관계
| 구분 | FaaS | BaaS |
|---|---|---|
| 제공 | 엔지니어가 작성한 코드 조각(함수)의 실행 | 백엔드 기능(인증/인가, 푸시 알림 등) |
| 주요 타겟 | 이벤트에 따른 코드 실행 | 백엔드 기능 재사용 및 추상화 |
| 엔지니어 역할 | 비지니스 로직 구현 | API로 백엔드 기능 호출 및 사용 |
| 예시 | Azure Functions, AWS Lambda | Azure AD B2C, Firebase Authentication |
FaaS와 SaaS는 역할이 다름에 따라 제공하는 항목이 다르며 이 두 가지를 상호 보완적인 관계로 사용할 수 있다. 예를 들어, 사용자가 이미지를 업로드하면 이벤트 발생가 발생하게 되고, Azure Functions(FaaS)이 이미지를 받아 Azure Storage(BaaS)에 저장하거나 Azure Cosmos DB(BaaS)에 이미지 정보를 기록하는 방식으로 사용할 수 있다.
결론적으로 두 항목 모두 인프라 관리보다는 비즈니스 로직과 사용자 경험 제공에 집중할 수 있도록 돕는 역할이다. 개발 및 운영 상황에 따라 어떻게 적용할지 적합한 의사결정을 할 줄 아는 것이 중요하다고 생각한다. 😎
📌 Azure 플랫폼으로 서버리스 아키텍처 구축하기

자동차 정비소를 예시로 들어보자. 일반적인 자동차 정비 프로세스는 아래와 같다.
- 고객은 정비소의 접수 직원에서 자동차 정비를 맡긴다.
- 접수 직원은 차량 정비가 가능 여부를 파악하고 정비사에게 요청한다.
- 정비사의 접수 완료 신호를 받은 접수 직원은 고객에게 접수 완료를 알린다.
- 정비사가 자동차 정비 결과를 정비 이력 장부에 작성하고 수납 직원에게 완료 여부와 가격을 알린다.
- 수납 직원은 고객에게 정비 완료를 알린다.
일반적인 서버 아키텍처에서는 위 프로세스 모두를 한 개 서버가 처리해야 한다. 당연하게도 정비 요청이 늘어남에 따라 서버의 부하도 증가할 것이다.
반면 서버리스 아키텍처는 위 이미지와 같이 기능을 처리하는 주체(접수 직원, 정비사, 수납 직원, 정비 이력 장부) 단위로 서비스를 분산할 수 있다.
만약 정비 요청이 한 번에 많이 들어오는 경우(병목 구간)에는, 접수 직원을 증원(스케일 아웃)하거나, 업무 처리가 빠른 경력 인원으로 대체(스케일 업)가 가능하다.
그렇다면 Azure Functions, Azure Service Bus, Azure Cosmos DB 서비스의 역할과 동작 방법을 살펴보자.
✨ Azure Functions / Azure Service Bus / Azure Cosmos DB
🌊 Azure Functions
Azure Functions는 이벤트 기반 트리거로 실행된다. HTTP 요청이 오는 등 유저가 설정해 놓은 이벤트가 발동되었을 때 Azure Functions에 작성된 코드가 실행된다.
Azure Functions의 최대 장점은 유연한 확장성이라고 생각한다. 앞서 말한 것처럼 병목 구간이 발생할 경우, 독립적인 스케일 아웃, 스케일 업이 가능하다. 이슈를 코드가 아닌 인프라 단에서 해결할 수 있는 것이다.
아래와 같은 트리거 종류를 지원한다.
| 트리거 | 설명 | 활용 예시 |
|---|---|---|
| HTTP 트리거 | HTTP 요청(GET, POST 등)이 들어올 때 함수를 실행 | 서버리스 REST API 구축, 웹훅(Webhook) 처리, 웹사이트 백엔드 연동 |
| 타이머 트리거 | 특정 시간 간격이나 예약된 시각에 함수를 주기적으로 실행 | 데이터베이스 백업, 주기적인 보고서 생성, 배치 작업 예약 |
| Service Bus 트리거 | Azure Service Bus 큐 또는 토픽에 메시지가 도착하면 함수를 실행 | 마이크로 서비스 간 안정적인 메시지 전달, 주문 처리 시스템, 알림 서비스 |
| Blob 트리거 | Azure Blob Storage에 파일이 추가되거나 변경될 때 함수를 실행 | 이미지/동영상 파일 업로드 시 자동 압축 및 변환, 문서 처리 |
| Event Hub 트리거 | Azure Event Hubs로 유입되는 대규모 이벤트 스트림에 반응하여 함수를 실행 | IoT 디바이스에서 실시간 데이터 수집 및 분석, 로그 데이터 처리 |
| Queue 트리거 | Azure Storage Queue에 메시지가 추가될 때 함수를 실행 | 비동기 작업 처리, 백그라운드 작업 분리, 긴 처리 시간 작업을 분산 처리 |
| Redis 트리거 | Redis Stream 또는 Pub/Sub 채널에 데이터가 추가되거나 메시지가 발행될 때 함수를 실행 (Consumption 플랜 등 특정 호스팅 플랜에서는 미지원) | IoT 센서 데이터 실시간 처리 (Stream), 채팅 메시지 발행 시 즉각적인 알림 (Pub/Sub) |
(Generic Webhook Trigger, GitHub Webhook Trigger, Preparation Trigger 등도 있는데 사용도가 낮다고 한다.)
🌊 Azure Service Bus
Azure Service Bus는 서비스 간 안정적인 비동기 메시지 교환을 가능하게 하는 완전 관리형 메시지 브로커 서비스이다. 서로 다른 서비스들이 데이터를 주고받을 때 직접적인 통신 방식이 아닌 Service Bus라는 중간 다리를 통해 메시지를 주고받게 하여, 시스템 전체의 안정성과 확장성을 향상시킨다.
메시지 생산자(Sender)와 소비자(Receiver)가 서로 직접 통신하지 않으므로, 각 서비스는 독립적으로 개발, 배포 및 확장 가능하다. 한 서비스에 문제가 발생해도 다른 서비스에 영향을 미치지 않는다.
Azure Service Bus는 두 가지 메시징 엔티티를 제공한다.
Queues: 메시지를 보내는 발신자(생산자)와 메시지를 읽는 수신자(소비자) 간의 일대일 (1:1) 통신을 위해 사용한다.Topics & Subscriptions: 일대다(1:N) 통신 시나리오에 사용한다. 발신자(게시자)는 토픽에 메시지를 발행하고, 토픽에 생성된 여러 ‘구독’을 통해 하나 이상의 수신자(구독자)들이 해당 메시지를 수신할 수 있다.
🌊 Azure Cosmos DB
Azure Cosmos DB는 NoSQL 데이터베이스 서비스이다.
다중 모델이라 다양한 NoSQL 데이터 모델을 지원한다.
Document Database: JSON 문서를 저장하는 Core (SQL) API 또는 MongoDB APIKey-Value Database: 간단한 키-값 형태의 데이터를 위한 Table APIGraph Database: 관계형 데이터보다 복잡한 연결 관계를 나타내는 데 적합한 Gremlin APIColumn-family Database: 대규모 쓰기와 넓은 컬럼을 가진 데이터를 위한 Cassandra API
✨ Azure 배포
Azure Functions, Azure Service Bus 등의 서비스 생성은 Azure Portal에서 가능하다. Functions 사용은 이번이 처음이었는데 배포에서 우여곡절이 정말 많았다.
처음에는 Portal에서 Functions을 생성하고 VS Code에서 코드를 배포하니 성공이라고 표시되었으나, Portal에서는 서비스가 반영되지 않았다. 다른 방법으로 시도하기 위해 Functions 서비스를 VS Code에서 생성 후 배포하니 서비스는 등록되지만 서버 실행이 되지 않았다. 두 번째 상황은 환경 변수 설정 미스로 명확한 내 잘못이었지만 원인을 파악하기 정말 어려웠고 시간도 많이 소요되었다. (VM처럼 콘솔 확인을 할 수 있는 것도 아니고.. 로그 파일 생성을 할 수 있는 것도 아니니 말이다.)
뭐 어찌 되었거나 구글과 GPT의 도움으로 VS Code를 통해 성공적인 배포를 할 수 있었다.
- 개발은 Python 3.13과 FastAPI를 사용
- 배포 시 파일명은 function_app.py로 고정
- 필요 라이브러리는 requirements.txt에 작성
🌊 사용자 요청을 Service Bus 푸시하기 위한 Functions
아래는 사용자의 요청을 Service Bus에 전달하기 위한 Funtions이다. 요청을 process-request-queue에 푸시한다. 웹으로부터 입력받는 것까지 시험하기 위해 Pub-Sub도 추가로 구성하였는데 생략해도 무방하다.
# function_app.py
from typing import Union
from fastapi import FastAPI
from dto.question import QuestionRequest
from azure.messaging.webpubsubservice.aio import WebPubSubServiceClient
from azure.core.credentials import AzureKeyCredential
from azure.servicebus.aio import ServiceBusClient
from azure.servicebus import ServiceBusMessage
from fastapi.middleware.cors import CORSMiddleware
from motor.motor_asyncio import AsyncIOMotorClient
import azure.functions as func
import os
import uuid
import json
fast_app = FastAPI()
fast_app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"]
)
# DB 연동
db_client = AsyncIOMotorClient(os.environ['DB_CONNECTION_URL'])
db = db_client['']
# Fast API 오픈
# Pub-Sub 연동
# Service Bus 연동
app = func.AsgiFunctionApp(app=fast_app, http_auth_level=func.AuthLevel.ANONYMOUS)
pubsub_client = WebPubSubServiceClient(endpoint=os.environ['PUBSUB_CONNECTION_URL'],
hub=os.environ['PUBSUB_HUB'],
credential=AzureKeyCredential(os.environ['PUBSUB_KEY']))
servicebus_client = ServiceBusClient.from_connection_string(conn_str=os.environ['SERVICEBUS_CONNECTION_URL'], logging_enable=True)
@fast_app.get("/channel-id")
async def get_channel_id():
return {"channel_id": str(uuid.uuid4())}
@fast_app.post("/question")
async def send_question(request: QuestionRequest):
question_data = {
"channel_id": request.channel_id,
"content": request.content,
"type": "question"
}
# 입력을 DB에 누적
result = await db.messages.insert_one(question_data)
question_data['_id'] = str(question_data['_id'])
# 요청을 process-request-queue에 푸시
async with servicebus_client:
sender = servicebus_client.get_queue_sender(queue_name="process-request-queue")
async with sender:
message = ServiceBusMessage(json.dumps(question_data))
await sender.send_messages(message)
return str(result.inserted_id)
# token 발행용 API
@fast_app.get("/pubsub/token")
async def read_root(channel_id: str):
return await pubsub_client.get_client_access_token(groups=[channel_id], minutes_to_expire=5, roles=['webpubsub.joinLeaveGroup.' + channel_id])
🌊 Service Bus에 푸시된 요청을 처리하기 위한 Functions
아래 Functions는 process-request-queue의 구독 트리거 방식으로 생성하였다. 따라서 위에서 요청이 푸시될 때마다 서버가 실행되어 요청을 처리할 수 있다. 이후 결과를 process-response-queue에 다시 푸시한다.
import azure.functions as func
import logging
from openai import OpenAI
from azure.servicebus.aio import ServiceBusClient
from azure.servicebus import ServiceBusMessage
import os
import json
app = func.FunctionApp()
servicebus_client = ServiceBusClient.from_connection_string(conn_str=os.environ['SERVICEBUS_CONNECTION_URL'], logging_enable=True)
# 트리거 방식
@app.service_bus_queue_trigger(arg_name="msg", queue_name="process-request-queue",
connection="SERVICEBUS_CONNECTION_URL")
async def process_request(msg: func.ServiceBusMessage):
message = json.loads(msg.get_body().decode('utf-8'))
answer_data = {
"channel_id": message['channel_id'],
"content": '결과 반환',
"type": "answer"
}
# 요청에 대한 처리 결과를 process-response-queue에 푸시
async with servicebus_client:
sender = servicebus_client.get_queue_sender(queue_name="process-response-queue")
async with sender:
message = ServiceBusMessage(json.dumps(answer_data))
await sender.send_messages(message)
logging.info(response.output_text)
🌊 처리된 요청을 사용자에게 전달하기 위한 Functions
요청에 대한 결과값이 process-response-queue에 푸시되면 해당 내용을 Pub-Sub으로 사용자에게 전달한다.
import azure.functions as func
import logging
import json
import os
from azure.messaging.webpubsubservice.aio import WebPubSubServiceClient
from azure.core.credentials import AzureKeyCredential
from motor.motor_asyncio import AsyncIOMotorClient
app = func.FunctionApp()
db_client = AsyncIOMotorClient(os.environ['DB_CONNECTION_URL'])
db = db_client['']
pubsub_client = WebPubSubServiceClient(endpoint=os.environ['PUBSUB_CONNECTION_URL'],
hub=os.environ['PUBSUB_HUB'],
credential=AzureKeyCredential(os.environ['PUBSUB_KEY']))
# 트리거 방식
@app.service_bus_queue_trigger(arg_name="msg", queue_name="process-response-queue",
connection="SERVICEBUS_CONNECTION_URL")
# 처리된 요청이 process-response-queue에 푸시되면
# 그 내용을 Pub-Sub으로 사용자에게 전달
async def process_response_function(msg: func.ServiceBusMessage):
response = json.loads(msg.get_body().decode('utf-8'))
response['_id'] = str(response['_id'])
await db.messages.insert_one(response)
await pubsub_client.send_to_group(group=response['channel_id'], message=response)
🌊 사용자용 프론트에서 Pub-Sub 호출
아래는 사용자용 프론트에서 Azure 서비스를 호출하기 위한 통신단 코드이다.
function getChannelId() {
return $.ajax({
url: API_URL + '/channel-id',
type: 'GET',
dataType: 'json'
})
}
function getPubSubToken(channelId) {
return $.ajax({
url: API_URL + '/pubsub/token?channel_id=' + channelId,
type: 'GET',
dataType: 'json'
})
}
function connectWebSocket(channelId, token) {
const WEB_SOCKET_URL = "pubsub 주소" + token;
const pubsubClient = new WebSocket(WEB_SOCKET_URL, 'json.webpubsub.azure.v1');
pubsubClient.onopen = function(event) {
console.log('Azure websocket connect');
pubsubClient.send(
JSON.stringify({
type: 'joinGroup',
group: channelId
}))
};
pubsubClient.onmessage = function(event) {
let message = JSON.parse(event.data);
if (!message.data || message.data.content === "") {
return;
}
$('#chat-box').append('<p class="other-message">' + message.data.content + '</p>');
$(this).val('');
$('#chat-box').scrollTop($('#chat-box')[0].scrollHeight);
}
}
$(document).ready(function() {
getChannelId().then(response => {
CHANNEL_ID = response.channel_id
console.log(CHANNEL_ID)
return getPubSubToken(CHANNEL_ID)
}).then(response => {
const token = response.token
connectWebSocket(CHANNEL_ID, token)
});
$('#message-input').keydown(function(event) {
if (event.isComposing || event.keyCode === 229) {
return;
}
if (event.key === 'Enter') {
var message = $(this).val();
if (message.trim() !== '') {
$('#chat-box').append('<p class="my-message">' + message + '</p>');
$(event.target).val('');
$('#chat-box').scrollTop($('#chat-box')[0].scrollHeight);
$.ajax({
url: API_URL + '/question',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({
channel_id: CHANNEL_ID,
content: message
}),
success: function(response) {
},
error: function(xhr, status, error) {
console.log(error)
}
})
}
}
});
});
📌 후기
Azure를 통해 서버리스 아키텍처를 구현해 보았다. 개발 및 구축을 진행하다 보니 처음 기획했던 아키텍처에서 변경사항이 발생하였다. 사용자용 UI를 추가하면서 프론트단과 Functions를 연동하기 위한 Pub-Sub이 추가되었고, 운용 및 관리 편의성을 고민하다 보니 Service Bus를 요청용, 응답용 두개로 구분하게 되었다. 사실 기존 아키텍처대로 구성했어도 육안으로 보았을 때 크게 달라진 점은 없었을 것이다. 단지 서비스를 관리해야 하는 엔지니어의 입장에서 더 나은 선택이지 않을까 라고 판단했을 뿐이다.
그리고 Functions나 Pub-Sub은 선수 지식 없이 맨땅에 헤딩하면서 구축하였는데 공식 문서에 이론 설명은 상세했지만 실제 구축을 어떻게 해야 하는지에 대한 설명이 부족하다고 느껴졌고 워낙 관련된 자료가 없어서 조금의 삽질과 어려움이 있었다. Pub/Sub 패턴이나 Functions의 동작 원리 같은 부분들을 직접 경험하며 배우는 게 최고인 거 같다!.. 또한 Azure는 전반적으로 구축이 효율적이고 시간 대비 성능이 좋다고 생각한다. 구성이나 모니터링하기도 편하다.
다만 IDE와 같은 툴에서의 Deploy는 개선되었으면 하는 바람이다. 오류에 대한 원인 파악이 너무 어렵다. 작업 중간에 오류가 나서 기존 서비스가 빠그라진다면 어느정도 숙달된 사람이 아니고서야 서비스를 처음부터 새로 구축하는 게 빠를 정도이다.
뭐.. 일부 아쉬운 점도 있었지만 Azure와 같은 클라우드는 개발자가 운영 부담을 줄이고 문제 해결에 집중할 수 있는 기회를 준다. 개인적으로 아직 경험하지 못한 클라우드 서비스 기능들이 많은데, 다음에는 Azure가 아닌 AWS를 사용해 보고 후기를 작성해 보고자 한다. 다른 이유는 없고 경험하지 못한 다양한 것들을 써보고 싶어서이다.
끝. 🫡