자습서: Azure App Service에서 CORS를 통해 RESTful API 호스팅

Azure App Service는 확장성 높은 자체 패치 웹 호스팅 서비스를 제공합니다. 또한 App Service에는 RESTful API에 대한 CORS(Cross-Origin Resource Sharing)의 지원이 기본적으로 제공됩니다. 이 자습서에서는 CORS 지원을 사용하여 ASP.NET Core API 앱을 App Service에 배포하는 방법을 보여 줍니다. 명령줄 도구를 사용하여 앱을 구성하고, Git을 사용하여 앱을 배포합니다.

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

  • Azure CLI를 사용하여 App Service 리소스 만들기
  • Git을 사용하여 Azure에 RESTful API 배포
  • App Service CORS 지원을 사용하도록 설정

이 자습서의 단계는 macOS, Linux, Windows에서 수행할 수 있습니다.

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

필수 조건

이 자습서를 완료하려면 다음이 필요합니다.

로컬 ASP.NET Core 앱 만들기

이 단계에서는 로컬 ASP.NET Core 프로젝트를 설정합니다. App Service는 다른 언어로 작성된 API에 대해 동일한 워크플로를 지원합니다.

샘플 애플리케이션 복제

  1. 터미널 창에서 cd를 사용하여 작업 디렉터리로 이동합니다.

  2. 샘플 리포지토리를 복제하고 리포지토리 루트로 변경합니다.

    git clone https://github.com/Azure-Samples/dotnet-core-api
    cd dotnet-core-api
    

    이 리포지토리에는 Swagger를 사용한 ASP.NET Core Web API 도움말 페이지 자습서에 따라 만든 응용 프로그램이 포함되어 있으며, Swagger 생성기를 사용하여 Swagger UI와 Swagger JSON 엔드포인트를 제공합니다.

  3. 기본 분기가 main인지 확인합니다.

    git branch -m main
    

    App Service에는 분기 이름 변경이 필요하지 않습니다. 그러나 많은 리포지토리가 기본 분기를 main으로 변경(배포 분기 변경 참조)하고 있으므로 이 자습서에서는 main에서 리포지토리를 배포하는 방법도 보여 줍니다.

애플리케이션 실행

  1. 다음 명령을 실행하여 필요한 패키지를 설치하고 데이터베이스 마이그레이션을 실행하고 애플리케이션을 시작합니다.

    dotnet restore
    dotnet run
    
  2. 브라우저에서 http://localhost:5000/swagger로 이동하여 Swagger UI를 실행해 봅니다.

    ASP.NET Core API running locally

  3. http://localhost:5000/api/todo로 이동하여 ToDo JSON 항목 목록을 확인합니다.

  4. http://localhost:5000으로 이동하여 브라우저 앱을 실행해 봅니다. 나중에 CORS 기능을 테스트하기 위해 브라우저 앱을 App Service의 원격 API로 지정할 것입니다. 브라우저 앱에 대한 코드는 리포지토리의 wwwroot 디렉터리에 있습니다.

  5. 언제든지 ASP.NET Core를 중지하려면 터미널에서 Ctrl+C를 누릅니다.

Azure Cloud Shell

Azure는 브라우저를 통해 사용할 수 있는 대화형 셸 환경인 Azure Cloud Shell을 호스트합니다. Cloud Shell에서 Bash 또는 PowerShell을 사용하여 Azure 서비스 작업을 수행할 수 있습니다. 로컬 환경에 아무 것도 설치할 필요 없이 Azure Cloud Shell의 미리 설치된 명령을 사용하여 이 문서의 코드를 실행할 수 있습니다.

Azure Cloud Shell을 시작하려면 다음을 수행합니다.

옵션 예제/링크
코드 또는 명령 블록의 오른쪽 상단에서 시도를 선택합니다. 시도를 선택해도 코드 또는 명령이 Cloud Shell에 자동으로 복사되지 않습니다. Screenshot that shows an example of Try It for Azure Cloud Shell.
https://shell.azure.com으로 이동하거나 Cloud Shell 시작 단추를 선택하여 브라우저에서 Cloud Shell을 엽니다. Button to launch Azure Cloud Shell.
Azure Portal의 오른쪽 위에 있는 메뉴 모음에서 Cloud Shell 단추를 선택합니다. Screenshot that shows the Cloud Shell button in the Azure portal

Azure Cloud Shell을 사용하려면:

  1. Cloud Shell을 시작합니다.

  2. 코드 블록(또는 명령 블록)에서 복사 단추를 선택하여 코드 또는 명령을 복사합니다.

  3. Windows 및 Linux에서 Ctrl+Shift+V를 선택하거나 macOS에서 Cmd+Shift+V를 선택하여 코드 또는 명령을 Cloud Shell 세션에 붙여넣습니다.

  4. Enter를 선택하여 코드 또는 명령을 실행합니다.

Azure에 앱 배포

이 단계에서는 .NET Core 애플리케이션을 App Service에 배포합니다.

로컬 Git 배포 구성

FTP 및 로컬 Git는 배포 사용자를 통해 Azure 웹앱에 배포할 수 있습니다. 일단 배포 사용자를 구성하면 모든 Azure 배포에 사용할 수 있습니다. 계정 수준 배포 사용자 이름 및 암호는 Azure 구독 자격 증명과 다릅니다.

배포 사용자를 구성하려면 Azure Cloud Shell에서 az webapp deployment user set 명령을 실행합니다. <사용자 이름> 및 <암호>를 배포 사용자 이름 및 암호로 바꿉니다.

  • 사용자 이름은 Azure 내에서 고유해야 하고, 로컬 Git 푸시의경우 ' @' 기호를 포함하면 안 됩니다.
  • 암호는 글자, 숫자, 기호의 세 가지 요소 중 두 가지를 사용하고 8자 이상이어야 합니다.
az webapp deployment user set --user-name <username> --password <password>

JSON 출력에는 암호가 null로 나옵니다. 'Conflict'. Details: 409 오류가 발생하면 사용자 이름을 변경합니다. 'Bad Request'. Details: 400 오류가 발생하면 더 강력한 암호를 사용합니다.

웹앱을 배포할 때 사용할 수 있도록 사용자 이름 및 암호를 기록해 둡니다.

리소스 그룹 만들기

리소스 그룹은 웹앱, 데이터베이스, 스토리지 계정과 같은 Azure 리소스가 배포되고 관리되는 논리적 컨테이너입니다. 예를 들어 나중에 간단한 단계 하나만으로 전체 리소스 그룹을 삭제하도록 선택할 수 있습니다.

Cloud Shell에서 az group create 명령을 사용하여 리소스 그룹을 만듭니다. 다음 예제에서는 서유럽 위치에 myResourceGroup이라는 리소스 그룹을 만듭니다. 체험 계층에서 App Service에 지원되는 모든 위치를 확인하려면 az appservice list-locations --sku FREE 명령을 실행합니다.

az group create --name myResourceGroup --location "West Europe"

일반적으로 사용자와 가까운 지역에서 리소스 그룹 및 리소스를 만듭니다.

명령이 완료되면 JSON 출력이 리소스 그룹 속성을 보여줍니다.

App Service 플랜 만들기

Cloud Shell에서 az appservice plan create 명령을 사용하여 App Service 요금제를 만듭니다.

다음 예에서는 체험 가격 책정 계층에서 myAppServicePlan이라는 App Service 계획을 만듭니다.

az appservice plan create --name myAppServicePlan --resource-group myResourceGroup --sku FREE

App Service 계획을 만든 경우 Azure CLI는 다음 예제와 비슷한 정보를 표시합니다.

{ 
  "adminSiteName": null,
  "appServicePlanName": "myAppServicePlan",
  "geoRegion": "West Europe",
  "hostingEnvironmentProfile": null,
  "id": "/subscriptions/0000-0000/resourceGroups/myResourceGroup/providers/Microsoft.Web/serverfarms/myAppServicePlan",
  "kind": "app",
  "location": "West Europe",
  "maximumNumberOfWorkers": 1,
  "name": "myAppServicePlan",
  < JSON data removed for brevity. >
  "targetWorkerSizeId": 0,
  "type": "Microsoft.Web/serverfarms",
  "workerTierName": null
} 

웹앱 만들기

myAppServicePlan App Service 계획에서 웹앱을 만듭니다.

Cloud Shell에서 az webapp create 명령을 사용할 수 있습니다. 다음 예에서 <app-name>을 전역적으로 고유한 앱 이름으로 바꿉니다(유효한 문자는 a-z, 0-9-).

az webapp create --resource-group myResourceGroup --plan myAppServicePlan --name <app-name> --deployment-local-git

웹앱이 만들어지면 Azure CLI는 다음 예제와 비슷한 출력을 표시합니다.

Local git is configured with url of 'https://<username>@<app-name>.scm.azurewebsites.net/<app-name>.git'
{
  "availabilityState": "Normal",
  "clientAffinityEnabled": true,
  "clientCertEnabled": false,
  "clientCertExclusionPaths": null,
  "cloningInfo": null,
  "containerSize": 0,
  "dailyMemoryTimeQuota": 0,
  "defaultHostName": "<app-name>.azurewebsites.net",
  "deploymentLocalGitUrl": "https://<username>@<app-name>.scm.azurewebsites.net/<app-name>.git",
  "enabled": true,
  < JSON data removed for brevity. >
}

참고 항목

Git 원격의 URL은 https://<username>@<app-name>.scm.azurewebsites.net/<app-name>.git 형식으로 deploymentLocalGitUrl 속성에 표시됩니다. 나중에 필요하므로 이 URL을 저장합니다.

Git에서 Azure에 푸시

  1. main 분기를 배포하고 있으므로 App Service 앱의 기본 배포 분기를 main로 설정해야 합니다(배포 분기 변경 참조). Cloud Shell에서 az webapp config appsettings set 명령으로 DEPLOYMENT_BRANCH 앱 설정을 지정합니다.

    az webapp config appsettings set --name <app-name> --resource-group myResourceGroup --settings DEPLOYMENT_BRANCH='main'
    
  2. 로컬 터미널 창으로 돌아와서 로컬 Git 리포지토리에 Azure 원격을 추가합니다. <deploymentLocalGitUrl-from-create-step>웹앱 만들기에서 저장한 Git 원격의 URL로 바꿉니다.

    git remote add azure <deploymentLocalGitUrl-from-create-step>
    
  3. 다음 명령을 사용하여 Azure 원격에 푸시하여 앱을 배포합니다. Git Credential Manager에서 자격 증명을 묻는 메시지가 표시되면 Azure Portal에 로그인하는 데 사용하는 자격 증명이 아니라 배포 사용자 구성에서 만든 자격 증명을 입력해야 합니다.

    git push azure main
    

    이 명령을 실행하는 데 몇 분 정도 걸릴 수 있습니다. 실행 시 다음 예와 유사한 정보를 출력합니다.

   Enumerating objects: 83, done.
   Counting objects: 100% (83/83), done.
   Delta compression using up to 8 threads
   Compressing objects: 100% (78/78), done.
   Writing objects: 100% (83/83), 22.15 KiB | 3.69 MiB/s, done.
   Total 83 (delta 26), reused 0 (delta 0)
   remote: Updating branch 'master'.
   remote: Updating submodules.
   remote: Preparing deployment for commit id '509236e13d'.
   remote: Generating deployment script.
   remote: Project file path: .\TodoApi.csproj
   remote: Generating deployment script for ASP.NET MSBuild16 App
   remote: Generated deployment script files
   remote: Running deployment command...
   remote: Handling ASP.NET Core Web Application deployment with MSBuild16.
   remote: .
   remote: .
   remote: .
   remote: Finished successfully.
   remote: Running post deployment command(s)...
   remote: Triggering recycle (preview mode disabled).
   remote: Deployment successful.
   To https://<app_name>.scm.azurewebsites.net/<app_name>.git
   * [new branch]      master -> master
   

Azure 앱 찾아보기

  1. 브라우저에서 http://<app_name>.azurewebsites.net/swagger로 이동하여 Swagger UI를 실행해 봅니다.

    ASP.NET Core API running in Azure App Service

  2. http://<app_name>.azurewebsites.net/swagger/v1/swagger.json으로 이동하여 배포된 API에 대한 swagger.json을 확인합니다.

  3. http://<app_name>.azurewebsites.net/api/todo로 이동하여 배포된 API가 작동하는지 확인합니다.

CORS 기능 추가

다음으로, API에 대한 App Service에서 기본 제공되는 CORS 지원을 사용하도록 설정합니다.

샘플 앱에서 CORS 테스트

  1. 로컬 리포지토리에서 wwwroot/index.html을 엽니다.

  2. 51번 줄에서 apiEndpoint 변수를 배포된 API의 URL(http://<app_name>.azurewebsites.net)로 설정합니다. <appname>을 App Service의 앱 이름으로 바꿉니다.

  3. 로컬 터미널 창에서 샘플 앱을 다시 실행합니다.

    dotnet run
    
  4. http://localhost:5000에 있는 브라우저 앱으로 이동합니다. 브라우저에서 개발자 도구 창을 열고(Windows용 Chrome에서 Ctrl+Shift+i) 콘솔 탭을 검사합니다. 이제 No 'Access-Control-Allow-Origin' header is present on the requested resource 오류 메시지가 표시됩니다.

    CORS error in browser client

    브라우저 앱(http://localhost:5000)과 원격 리소스(http://<app_name>.azurewebsites.net) 간의 도메인 불일치는 브라우저에서 원본 간 리소스 요청으로 인식됩니다. 또한 App Service 앱의 REST API가 Access-Control-Allow-Origin 헤더를 보내지 않는다는 사실 때문에 브라우저에서 도메인 간 콘텐츠가 로드되지 않습니다.

    프로덕션 환경에서 브라우저 앱에는 localhost URL 대신 공용 URL이 있지만, CORS를 localhost URL로 사용하도록 설정하는 방법은 공용 URL과 동일합니다.

CORS를 사용하도록 설정

Cloud Shell에서 az webapp cors add 명령을 사용하여 CORS를 클라이언트의 URL로 사용합니다. <app-name> 자리 표시자를 바꿉니다.

az webapp cors add --resource-group myResourceGroup --name <app-name> --allowed-origins 'http://localhost:5000'

명령을 여러 번 실행하거나 --allowed-origins에 쉼표로 구분된 목록을 추가하여 허용되는 원본을 여러 개 추가할 수 있습니다. 모든 원본을 허용하려면 --allowed-origins '*'을(를) 사용합니다.

CORS 다시 테스트

http://localhost:5000에서 브라우저 앱을 새로 고칩니다. 콘솔 창의 오류 메시지가 사라지고, 배포된 API의 데이터를 보고 상호 작용할 수 있습니다. 이제 원격 API에서 로컬로 실행 중인 브라우저 앱에 CORS를 지원합니다.

CORS success in browser client

축하합니다! CORS 지원을 통해 Azure App Service에서 API를 실행하고 있습니다.

자주 묻는 질문

App Service CORS 및 사용자 고유 CORS

더 많은 유연성을 위해 App Service CORS 대신 자신의 CORS 유틸리티를 사용할 수 있습니다. 예를 들어 여러 경로 또는 메서드에 대해 허용되는 원본을 서로 다르게 지정할 수 있습니다. App Service CORS를 사용하면 모든 API 경로 및 메서드에 대해 허용되는 원본 집합을 하나만 지정할 수 있으므로 사용자 고유의 CORS 코드를 사용해야 합니다. ASP.NET Core에서 이를 수행하는 방법은 Enabling Cross-Origin Requests (CORS)(원본 간 요청(CORS) 사용)를 참조하세요.

기본 제공 App Service CORS 기능에는 지정한 각 원본에 대해 특정 HTTP 메서드 또는 동사만 허용하는 옵션이 없습니다. 정의된 각 원본에 대한 모든 메서드와 헤더를 자동으로 허용합니다. 이 동작은 옵션을 코드에서 사용할 때 .AllowAnyHeader()ASP.NET Core CORS.AllowAnyMethod() 정책과 유사합니다.

참고 항목

App Service CORS와 사용자 고유 CORS 코드는 함께 사용하지 마세요. 함께 사용하는 경우 App Service CORS가 우선적으로 적용되며, 사용자 고유의 CORS 코드는 아무런 영향을 주지 않습니다.

어떻게 허용된 원본을 와일드카드 하위 도메인으로 설정할까요?

*.contoso.com과(와) 같은 와일드카드 하위 도메인은 와일드카드 원본 *보다 더 제한적입니다. 그러나 Azure Portal 앱의 CORS 관리 페이지에서 와일드카드 하위 도메인을 허용된 원본으로 설정할 수 없습니다. 그러나 다음과 같이 Azure CLI를 사용하여 수행할 수 있습니다.

az webapp cors add --resource-group <group-name> --name <app-name> --allowed-origins 'https://*.contoso.com'

어떻게 응답에서 ACCESS-CONTROL-ALLOW-CREDENTIALS 헤더를 사용하도록 설정할까요?

앱에서 쿠키나 인증 토큰과 같은 자격 증명을 전송하도록 요구하는 경우 브라우저의 응답에 ACCESS-CONTROL-ALLOW-CREDENTIALS 헤더가 필요할 수 있습니다. App Service에서 사용하도록 설정하려면 properties.cors.supportCredentials을(를) true(으)로 설정합니다.

az resource update --name web --resource-group <group-name> \
  --namespace Microsoft.Web --resource-type config \
  --parent sites/<app-name> --set properties.cors.supportCredentials=true

허용된 원본에 와일드카드 원본 '*'이(가) 포함된 경우 이 작업은 허용되지 않습니다. AllowAnyOriginAllowCredentials를 지정하는 것은 안전하지 않은 구성이며 사이트 간 요청이 위조될 수 있습니다. 자격 증명을 허용하려면 와일드카드 원본을 와일드카드 하위 도메인으로 바꿔 보세요.

리소스 정리

이전 단계에서는 리소스 그룹에서 Azure 리소스를 만들었습니다. 나중에 이러한 리소스가 필요하지 않을 것 같으면 Cloud Shell에서 다음 명령을 실행하여 리소스 그룹을 삭제합니다.

az group delete --name myResourceGroup

이 명령을 실행하는 데 1분 정도 걸릴 수 있습니다.

다음 단계

학습한 내용은 다음과 같습니다.

  • Azure CLI를 사용하여 App Service 리소스 만들기
  • Git을 사용하여 Azure에 RESTful API 배포
  • App Service CORS 지원을 사용하도록 설정

사용자를 인증하고 사용자 권한을 부여하는 방법에 대해 알아보려면 다음 자습서를 진행합니다.