배포와 호스팅
Blazor 앱을 Azure, IIS, Docker에 배포하는 방법과 배포 환경 설정을 학습합니다.
📌 학습 목표
- Blazor WebAssembly와 Blazor Server의 배포 방식 차이를 명확하게 설명할 수 있습니다.
- Azure App Service에 Blazor 앱을 배포하고 환경 변수를 설정할 수 있습니다.
- IIS에 Blazor 앱을 호스팅하고 필요한 설정(.NET 호스팅 번들, MIME 타입 등)을 적용할 수 있습니다.
- Dockerfile 멀티 스테이지 빌드를 작성하고 Docker로 Blazor 앱을 컨테이너화할 수 있습니다.
- 환경별 appsettings.json을 분리하여 개발·운영 환경을 안전하게 관리할 수 있습니다.
| 구분 | Blazor WebAssembly | Blazor Server |
|---|---|---|
| 실행 위치 | 브라우저 (클라이언트) | 서버 (SignalR 연결) |
| 배포 형태 | 정적 파일 (.html/.js/.wasm) | ASP.NET Core 앱 |
| Azure 배포 | Static Web Apps / App Service | App Service |
| IIS 배포 | 정적 사이트 또는 역방향 프록시 | 역방향 프록시 필수 |
| Docker | Nginx + 정적 파일 | ASP.NET Core 런타임 이미지 |
| 서버 부하 | 낮음 (클라이언트에서 처리) | 높음 (서버에서 UI 처리) |
📝 개념 설명
1. 배포 전 준비 — dotnet publish
Blazor 앱을 어느 환경에 배포하든 공통적으로 dotnet publish 명령을 먼저 실행합니다. 이 명령은 소스 코드를 릴리스 모드로 컴파일하고, 의존 파일·정적 파일을 하나의 폴더에 모아 배포 패키지를 생성합니다.
# 릴리스 모드로 발행 (가장 기본적인 형태)
dotnet publish -c Release -o ./publish
# Self-contained 배포 — .NET 런타임을 함께 패키징 (서버에 .NET 불필요)
dotnet publish -c Release -o ./publish --self-contained true -r win-x64
# Framework-dependent 배포 — 서버에 .NET 런타임이 별도 설치되어야 함
dotnet publish -c Release -o ./publish --self-contained false
발행이 완료된 ./publish 폴더에는 배포에 필요한 모든 파일이 들어 있습니다. 이 폴더가 실제로 서버에 올라가는 배포 단위입니다.
2. 환경별 설정 분리 — appsettings.json
.NET 앱은 ASPNETCORE_ENVIRONMENT 환경 변수 값에 따라 설정 파일을 자동으로 계층화하여 로드합니다. 기본 appsettings.json 위에 환경별 파일의 값이 덮어씌워지는 방식입니다.
// appsettings.json — 모든 환경 공통 기본값
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
// appsettings.Production.json — 운영 환경 전용 (기본값 덮어씌움)
{
"ConnectionStrings": {
"DefaultConnection": "Server=prod-db;Database=MyApp;Integrated Security=True"
},
"AllowedHosts": "mydomain.com;www.mydomain.com"
}
// appsettings.Development.json — 개발 환경 전용
{
"DetailedErrors": true,
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=MyAppDev"
}
}
- Development — 로컬 개발 환경. 오류 상세 정보 표시 활성화
- Staging — QA·스테이징 환경. 운영과 유사하되 테스트 가능
- Production — 운영 환경. ASPNETCORE_ENVIRONMENT 미설정 시 기본값
연결 문자열, API 키, 비밀번호 등 민감한 값을 appsettings.json에 직접 넣으면 Git 리포지토리를 통해 유출될 수 있습니다. 개발 환경에서는 dotnet user-secrets를, 운영 환경에서는 Azure Key Vault 또는 환경 변수(Environment Variables)를 활용하세요.
3. 설정 우선순위 이해
.NET 설정 계층은 낮은 곳에서 높은 곳으로 덮어씌워집니다. 우선순위가 높을수록 나중에 로드되어 이전 값을 재정의합니다.
.json
(최저)
.{환경명}
.json
(개발 전용)
(Environment
Variables)
(최우선)
💡 예제 & 실습
실습 1 — Azure App Service 배포
Azure App Service는 서버 관리 없이 Blazor 앱을 클라우드에 호스팅할 수 있는 PaaS(Platform as a Service) 서비스입니다. Blazor Server와 Blazor WebAssembly(Hosted) 모두 지원합니다.
# 1. Azure 로그인
az login
# 2. 리소스 그룹 생성
az group create --name MyBlazorRG --location koreacentral
# 3. App Service 플랜 생성 (Free 티어)
az appservice plan create --name MyBlazorPlan --resource-group MyBlazorRG --sku FREE
# 4. Web App 생성 (.NET 8 런타임)
az webapp create --name my-blazor-app --resource-group MyBlazorRG --plan MyBlazorPlan --runtime "DOTNETCORE:8.0"
# 5. 환경 변수 설정
az webapp config appsettings set --name my-blazor-app --resource-group MyBlazorRG --settings ASPNETCORE_ENVIRONMENT=Production
# 6. 앱 발행 후 ZIP 배포 (PowerShell)
dotnet publish -c Release -o ./publish
Compress-Archive -Path ./publish/* -DestinationPath myapp.zip -Force
az webapp deploy --name my-blazor-app --resource-group MyBlazorRG --src-path myapp.zip
배포 후 Azure 포털에서 App Service → 구성(Configuration) → 애플리케이션 설정에서 환경 변수를 추가하거나 수정할 수 있습니다. 이 값들은 appsettings.json보다 우선순위가 높아 코드 수정 없이 설정을 변경할 수 있습니다.
Blazor WebAssembly는 정적 파일만으로 실행되므로 Azure Static Web Apps를 사용하면 더 저렴하고 빠릅니다. GitHub 리포지토리와 자동 연동되어 main 브랜치에 Push 시 자동 빌드·배포(CI/CD)가 구성됩니다. Free 티어에서도 HTTPS, 커스텀 도메인, CDN 가속이 제공됩니다.
실습 2 — IIS(인터넷 정보 서비스) 배포
사내 Windows 서버나 온프레미스 환경에 배포할 때 IIS를 사용합니다. IIS에서 .NET 앱을 실행하려면 반드시 .NET 호스팅 번들(Hosting Bundle)을 먼저 설치해야 합니다. 이 번들에는 ASP.NET Core 모듈(ANCM)이 포함되어 있습니다.
- .NET 호스팅 번들 설치 — Microsoft 공식 다운로드 센터에서 설치 후 IIS를 재시작합니다.
- IIS에서 새 사이트 추가 — IIS 관리자 → 사이트 → 웹 사이트 추가. 실제 경로를 발행 폴더(./publish)로 지정합니다.
- 애플리케이션 풀 설정 — .NET CLR 버전을 반드시 “관리 코드 없음(No Managed Code)”으로 설정합니다.
- 폴더 권한 부여 — IIS_IUSRS 계정에 발행 폴더의 읽기·실행 권한을 부여합니다.
- 바인딩 및 HTTPS 구성 — SSL 인증서를 바인딩하여 HTTPS를 활성화합니다.
dotnet publish 시 자동으로 생성되는 web.config 파일이 IIS와 ASP.NET Core 모듈 간의 연동을 담당합니다:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<location path="." inheritInChildApplications="false">
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*"
modules="AspNetCoreModuleV2"
resourceType="Unspecified" />
</handlers>
<aspNetCore processPath="dotnet"
arguments="./MyBlazorApp.dll"
stdoutLogEnabled="false"
stdoutLogFile="./logs/stdout"
hostingModel="inprocess">
<environmentVariables>
<environmentVariable name="ASPNETCORE_ENVIRONMENT" value="Production" />
</environmentVariables>
</aspNetCore>
<!-- Blazor WebAssembly 전용: .wasm MIME 타입 등록 (미등록 시 404 오류) -->
<staticContent>
<mimeMap fileExtension=".wasm" mimeType="application/wasm" />
</staticContent>
</system.webServer>
</location>
</configuration>
실습 3 — Docker 컨테이너 배포
Docker는 애플리케이션과 모든 의존성을 컨테이너에 패키징하여 어느 환경에서나 동일하게 실행할 수 있게 합니다. 멀티 스테이지 빌드(Multi-stage Build)를 사용하면 최종 이미지에 빌드 도구(SDK)가 포함되지 않아 이미지 크기를 크게 줄일 수 있습니다.
# ── Stage 1: SDK 이미지로 빌드·발행 ──────────────────────
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
# 프로젝트 파일 먼저 복사 → 의존성 복원 (Docker 레이어 캐시 활용)
COPY ["MyBlazorApp/MyBlazorApp.csproj", "MyBlazorApp/"]
RUN dotnet restore "MyBlazorApp/MyBlazorApp.csproj"
# 전체 소스 복사 → 릴리스 발행
COPY . .
WORKDIR "/src/MyBlazorApp"
RUN dotnet publish -c Release -o /app/publish
# ── Stage 2: 런타임 이미지 (SDK 제외 → 이미지 경량화) ────
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS final
WORKDIR /app
COPY --from=build /app/publish .
# .NET 8 기본 HTTP 포트 = 8080
ENV ASPNETCORE_ENVIRONMENT=Production
ENV ASPNETCORE_URLS=http://+:8080
EXPOSE 8080
ENTRYPOINT ["dotnet", "MyBlazorApp.dll"]
# Stage 1: WebAssembly 발행
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY . .
RUN dotnet publish -c Release -o /app/publish
# Stage 2: Nginx로 정적 파일 서빙 (SDK 완전 제외)
FROM nginx:alpine AS final
COPY --from=build /app/publish/wwwroot /usr/share/nginx/html
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
events {}
http {
include mime.types;
types {
application/wasm wasm; # .wasm MIME 타입 등록
}
server {
listen 80;
root /usr/share/nginx/html;
index index.html;
# SPA 라우팅: 존재하지 않는 경로는 index.html로 폴백
location / {
try_files $uri $uri/ /index.html;
}
# gzip 압축으로 전송 크기 최소화
gzip on;
gzip_types text/plain application/javascript application/wasm;
}
}
# 이미지 빌드
docker build -t my-blazor-app:latest .
# 단독 컨테이너 실행
docker run -d -p 8080:8080 -e ASPNETCORE_ENVIRONMENT=Production --name blazor-app my-blazor-app:latest
# 로그 확인
docker logs blazor-app
# Compose로 실행
docker-compose up -d
# docker-compose.yml — 앱 + SQL Server 통합 구성
services:
webapp:
build: .
ports:
- 8080:8080
environment:
- ASPNETCORE_ENVIRONMENT=Production
- ConnectionStrings__DefaultConnection=Server=db;Database=MyApp;User Id=sa;Password=Pass@word1!
depends_on:
- db
db:
image: mcr.microsoft.com/mssql/server:2022-latest
environment:
- ACCEPT_EULA=Y
- SA_PASSWORD=Pass@word1!
ports:
- 1433:1433
volumes:
- sqldata:/var/lib/mssql/data
volumes:
sqldata:
docker-compose.yml에서 중첩된 appsettings.json 경로는 콜론(:) 대신 이중 밑줄(__)로 구분합니다.
예: ConnectionStrings:DefaultConnection →
ConnectionStrings__DefaultConnection
실습 4 — GitHub Actions CI/CD 파이프라인
CI/CD 파이프라인을 구성하면 코드를 Push할 때마다 자동으로 빌드하고 배포합니다. 수동 배포 실수를 줄이고 배포 속도를 높일 수 있습니다.
# .github/workflows/deploy.yml
name: Deploy Blazor to Azure
on:
push:
branches: [ main ]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup .NET 8
uses: actions/setup-dotnet@v3
with:
dotnet-version: '8.0.x'
- name: Restore
run: dotnet restore
- name: Build
run: dotnet build -c Release --no-restore
- name: Publish
run: dotnet publish -c Release -o ./publish --no-build
- name: Deploy to Azure App Service
uses: azure/webapps-deploy@v2
with:
app-name: 'my-blazor-app'
publish-profile: ${{ secrets.AZURE_WEBAPP_PUBLISH_PROFILE }}
package: ./publish
Azure 포털의 App Service → 배포 센터 → 게시 프로필 다운로드에서 가져온 XML을 GitHub 리포지토리 Settings → Secrets에 AZURE_WEBAPP_PUBLISH_PROFILE로 등록하면 자동 배포가 완성됩니다.
⚠️ 자주 틀리는 것 / 주의사항
- .NET 8 기본 포트 변경: .NET 8부터 기본 HTTP 포트가 80에서 8080으로 변경되었습니다. Dockerfile이나 docker-compose.yml에서 ASPNETCORE_URLS 환경 변수로 포트를 명시적으로 지정하세요.
- 멀티 스테이지 빌드 생략 금지: SDK 이미지(약 900MB)와 aspnet 런타임 이미지(약 250MB)를 분리하지 않으면 최종 Docker 이미지가 1GB가 넘어 배포·Pull 속도가 크게 저하됩니다.
- Azure Free 티어의 Always On 미지원: App Service Free·Shared 티어는 일정 시간 비활성화 후 앱이 중지됩니다. Blazor Server처럼 지속적인 SignalR 연결이 필요한 앱은 Basic 이상 티어에서 Always On을 활성화해야 합니다.
- Self-contained 배포 주의: Self-contained 배포는 서버에 .NET 런타임이 없어도 실행되지만 배포 파일 크기가 수십 MB 이상으로 커집니다. 서버 환경을 제어할 수 있다면 Framework-dependent 배포가 더 효율적입니다.
- HTTPS 강제 리다이렉트: 운영 환경에서는 Program.cs에
app.UseHttpsRedirection();을 반드시 포함하고, 인증서 구성을 완료해야 합니다.
🎯 마무리
Blazor 앱 배포는 호스팅 모델 선택(WebAssembly vs Server)에서 전략이 갈립니다. WebAssembly는 정적 파일로 배포되어 CDN과 정적 호스팅의 장점을 최대한 활용할 수 있고, Server는 기존 ASP.NET Core 앱과 동일한 방식으로 배포합니다.
프로젝트 상황별 권장 배포 전략:
- 개인·소규모 프로젝트 → Azure App Service Free 티어로 빠르게 시작
- 팀 개발·지속 배포 → GitHub Actions + Azure로 CI/CD 자동화
- 사내 서버·온프레미스 → IIS + .NET 호스팅 번들로 기존 인프라 활용
- 마이크로서비스·클라우드 네이티브 → Docker + Kubernetes로 확장성 확보
- dotnet publish -c Release로 배포 패키지를 생성합니다. Self-contained는 런타임 포함(서버에 .NET 불필요), Framework-dependent는 서버에 .NET 런타임이 필요합니다.
- 환경별 설정은 appsettings.{환경명}.json으로 분리하고 ASPNETCORE_ENVIRONMENT 환경 변수로 활성 환경을 지정합니다. 설정 우선순위는 환경 변수 > 환경별 json > 기본 json 순입니다.
- Azure App Service는 Azure CLI 또는 ZIP 배포로 간단히 배포하며, 포털의 구성(Configuration) 탭에서 환경 변수를 관리합니다. Blazor WebAssembly는 Azure Static Web Apps가 더 경제적입니다.
- IIS 배포는 ① .NET 호스팅 번들 설치 → ② 애플리케이션 풀을 “관리 코드 없음” 설정 → ③ 폴더 권한 부여 순서로 진행합니다. Blazor WebAssembly는 web.config에 .wasm MIME 타입 등록이 필수입니다.
- Docker 배포는 SDK 이미지(빌드) → aspnet 런타임 이미지(실행) 멀티 스테이지 빌드를 사용합니다. Blazor WebAssembly는 Nginx 이미지로 서빙하며 SPA 라우팅 폴백(try_files)을 nginx.conf에 설정해야 합니다.
- Blazor Server는 SignalR WebSocket을 사용하므로 모든 역방향 프록시에서 WebSocket 업그레이드 헤더를 허용해야 앱이 정상 작동합니다.
- GitHub Actions CI/CD를 구성하면 main 브랜치 Push 시 자동 빌드·발행·배포 파이프라인이 실행되어 수동 배포 오류를 없앨 수 있습니다.