토큰 캐싱은 애플리케이션 성능, 안정성 및 사용자 환경을 향상시킵니다. Microsoft. Identity.Web은 성능, 지속성 및 운영 안정성의 균형을 맞추는 유연한 캐싱 전략을 제공합니다.
개요
이 섹션에서는 Microsoft.Identity.Web이 어떤 토큰을 캐시하는지와 캐싱이 애플리케이션에 왜 중요한지를 설명합니다.
캐시되는 토큰은 무엇인가요?
Microsoft. Identity.Web은 다음과 같은 여러 유형의 토큰을 캐시합니다.
| 토큰 형식 | Size | Scope | 퇴거 |
|---|---|---|---|
| 액세스 토큰 | ~2KB | (사용자/앱, 테넌트, 리소스) | 자동(수명 기반) |
| 토큰 새로 고침 | 변수 | 사용자 계정당 | 수동 또는 정책 기반 |
| ID 토큰 | ~2-7KB | 사용자당 | 자동 |
토큰 캐싱이 적용되는 위치:
- API를 호출하는 웹앱 - 위임된 액세스를 위한 사용자 토큰
- 다운스트림 API를 호출하는 웹 API - OBO 토큰(신중한 제거 정책 필요)
- 디먼 애플리케이션 - 서비스 대 서비스 호출에 대한 앱 전용 토큰
토큰을 캐시하는 이유
성능 이점:
- Microsoft Entra ID와의 데이터 전송 횟수 감소
- 빠른 API 호출(L1: <10ms vs L2: ~30ms vs 네트워크: >100ms)
- 최종 사용자의 대기 시간 단축
안정성 이점:
- 임시 Microsoft Entra 중단 시 작업을 계속합니다.
- 네트워크 이상 현상에 대한 복원력
- 우아한 성능 저하: 분산 캐시 실패 시
비용 혜택:
- 인증 요청 줄이기 (제한 방지)
- 인증 작업에 대한 Azure 비용 절감
퀵 스타트
환경에 따라 다음 캐시 구성 중 하나를 사용하여 빠르게 시작합니다.
개발 - 메모리 내 캐시
다음 예제에서는 개발 및 샘플에 적합한 메모리 내 토큰 캐시를 추가합니다.
using Microsoft.Identity.Web;
builder.Services.AddMicrosoftIdentityWebAppAuthentication(builder.Configuration, "AzureAd")
.EnableTokenAcquisitionToCallDownstreamApi()
.AddInMemoryTokenCaches();
장점:
- 간단한 설정
- 빠른 성능
- 외부 종속성 없음
단점:
- 앱을 다시 시작할 때 캐시가 손실되었습니다. 웹앱에서 사용자는 쿠키를 통해 로그인 상태를 유지하지만 액세스 토큰을 가져오고 캐시를 다시 채우려면 다시 로그인해야 합니다.
- 프로덕션 다중 서버 배포에 적합하지 않음
- 애플리케이션 인스턴스 간에 공유되지 않음
프로덕션 - 분산 캐시
프로덕션 애플리케이션, 특히 다중 서버 배포의 경우 Redis 또는 다른 공급자가 백업하는 분산 캐시를 사용합니다.
using Microsoft.Identity.Web;
builder.Services.AddMicrosoftIdentityWebAppAuthentication(builder.Configuration, "AzureAd")
.EnableTokenAcquisitionToCallDownstreamApi()
.AddDistributedTokenCaches();
// Choose your cache implementation
builder.Services.AddStackExchangeRedisCache(options =>
{
options.Configuration = builder.Configuration.GetConnectionString("Redis");
options.InstanceName = "MyApp_";
});
장점:
- 앱 재시작 후에도 지속됩니다.
- 모든 애플리케이션 인스턴스에서 공유
- 자동 L1+L2 캐싱
단점:
- 외부 캐시 인프라 필요
- 추가 구성 복잡성
- 캐시 작업에 대한 네트워크 대기 시간
캐시 전략 선택
다음 의사 결정 순서도 및 행렬을 사용하여 배포에 가장 적합한 캐시 전략을 선택합니다.
flowchart TD
Start([Token Caching<br/>Decision]) --> Q1{Production<br/>Environment?}
Q1 -->|No - Dev/Test| DevChoice[In-Memory Cache<br/>AddInMemoryTokenCaches]
Q1 -->|Yes| Q2{Multiple Server<br/>Instances?}
Q2 -->|No - Single Server| Q3{App Restarts<br/>Acceptable?}
Q3 -->|Yes| DevChoice
Q3 -->|No| DistChoice
Q2 -->|Yes| DistChoice[Distributed Cache<br/>AddDistributedTokenCaches]
DistChoice --> Q4{Cache<br/>Implementation?}
Q4 -->|High Performance| Redis[Redis Cache<br/>StackExchange.Redis<br/>⭐ Recommended]
Q4 -->|Azure Native| Azure[Azure Cache for Redis,<br/>Azure Cosmos DB,<br/>or Azure Database for PostgreSQL]
Q4 -->|On-Premises| SQL[SQL Server Cache<br/>AddDistributedSqlServerCache]
Q4 -->|Testing| DistMem[Distributed Memory<br/>Not for production]
Redis --> L1L2[Automatic L1+L2<br/>Caching]
Azure --> L1L2
SQL --> L1L2
DistMem --> L1L2
L1L2 --> Config[Configure Options<br/>MsalDistributedTokenCacheAdapterOptions]
DevChoice --> MemConfig[Configure Memory Options<br/>MsalMemoryTokenCacheOptions]
style Start fill:#e1f5ff
style DevChoice fill:#d4edda
style DistChoice fill:#fff3cd
style Redis fill:#d1ecf1
style L1L2 fill:#f8d7da
의사 결정 행렬
다음 표에서는 일반적인 배포 시나리오에 권장되는 캐시 유형을 요약합니다.
| 시나리오 | 권장 캐시 | 이유 |
|---|---|---|
| 로컬 개발 | In-Memory | 단순성, 인프라 필요 없음 |
| 샘플/데모 | In-Memory | 간단한 데모 설정 |
| 단일 서버 프로덕션(다시 시작 확인) | In-Memory | 세션을 다시 설정할 수 있는 경우 허용 |
| 다중 서버 프로덕션 | 레디스 | 공유 캐시, 고성능, 신뢰할 수 있는 |
| Azure 호스팅 애플리케이션 | Azure Cache for Redis (Azure용 Redis 캐시) | Azure의 고유 통합, 관리형 서비스 |
| 온-프레미스 엔터프라이즈 | SQL Server | 기존 인프라 활용 |
| PostgreSQL 환경 | PostgreSQL | 기존 PostgreSQL 데이터베이스, 친숙한 SQL 의미 체계 사용 |
| 높은 보안 환경 | SQL Server + 암호화 | 데이터 주권, 정지 상태의 데이터 암호화 |
| 분산 시나리오 테스트 | 분산 메모리 | 인프라 없이 L2 캐시 동작 테스트 |
캐시 구현
Microsoft. Identity.Web은 여러 캐시 구현을 지원합니다. 인프라 및 가용성 요구 사항과 일치하는 항목을 선택합니다.
메모리 내 캐시
사용 시기:
- 개발 및 테스트
- 허용되는 다시 시작 동작을 사용하는 단일 서버 배포
- 샘플 및 프로토타입
구성:
다음 코드는 메모리 내 토큰 캐시를 기본 설정으로 등록합니다.
builder.Services.AddMicrosoftIdentityWebAppAuthentication(builder.Configuration, "AzureAd")
.EnableTokenAcquisitionToCallDownstreamApi()
.AddInMemoryTokenCaches();
사용자 지정 옵션 사용:
옵션을 전달하여 만료 및 크기 제한을 사용자 지정할 수 있습니다.
builder.Services.AddMicrosoftIdentityWebAppAuthentication(builder.Configuration, "AzureAd")
.EnableTokenAcquisitionToCallDownstreamApi()
.AddInMemoryTokenCaches(options =>
{
// Token cache entry will expire after this duration
options.AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1);
// Limit cache size (default is unlimited)
options.SizeLimit = 500 * 1024 * 1024; // 500 MB
});
자동 L1 지원을 사용하는 L2(분산 캐시)
사용 시기:
- 프로덕션 다중 서버 배포
- 다시 시작할 때 캐시 지속성이 필요한 애플리케이션
- 고가용성 시나리오
핵심 기능: Microsoft.Identity.Web v1.8.0 이후, 분산 캐시에는 성능 및 안정성이 개선되도록 인메모리 L1 캐시가 자동으로 포함되었습니다.
Redis 캐시(권장)
Redis 연결 문자열을 appsettings.json에 추가합니다.
{
"ConnectionStrings": {
"Redis": "localhost:6379"
}
}
그런 다음 분산 토큰 캐시 및 Redis 공급자를 Program.cs 등록합니다.
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.TokenCacheProviders.Distributed;
builder.Services.AddMicrosoftIdentityWebAppAuthentication(builder.Configuration, "AzureAd")
.EnableTokenAcquisitionToCallDownstreamApi()
.AddDistributedTokenCaches();
// Redis cache implementation
builder.Services.AddStackExchangeRedisCache(options =>
{
options.Configuration = builder.Configuration.GetConnectionString("Redis");
options.InstanceName = "MyApp_"; // Unique prefix per application
});
// Optional: Configure distributed cache behavior
builder.Services.Configure<MsalDistributedTokenCacheAdapterOptions>(options =>
{
// Control L1 cache size
options.L1CacheOptions.SizeLimit = 500 * 1024 * 1024; // 500 MB
// Handle L2 cache failures gracefully
options.OnL2CacheFailure = (exception) =>
{
if (exception is StackExchange.Redis.RedisConnectionException)
{
// Log the failure
// Optionally attempt reconnection
return true; // Retry the operation
}
return false; // Don't retry
};
});
Azure Cache for Redis (Azure용 Redis 캐시)
Redis용 Azure Cache를 사용하려면 Azure의 연결 문자열을 사용하여 캐시를 등록합니다.
builder.Services.AddStackExchangeRedisCache(options =>
{
options.Configuration = builder.Configuration.GetConnectionString("AzureRedis");
options.InstanceName = "MyApp_";
});
연결 문자열 형식
<cache-name>.redis.cache.windows.net:6380,password=<access-key>,ssl=True,abortConnect=False
SQL Server 캐시
다음 예제에서는 SQL Server 분산 캐시 백 엔드로 구성합니다.
builder.Services.AddDistributedSqlServerCache(options =>
{
options.ConnectionString = builder.Configuration.GetConnectionString("TokenCacheDb");
options.SchemaName = "dbo";
options.TableName = "TokenCache";
// Set expiration longer than access token lifetime (default 1 hour)
// This prevents cache entries from expiring before tokens
options.DefaultSlidingExpiration = TimeSpan.FromMinutes(90);
});
Azure Cosmos DB 캐시
다음 예제에서는 Azure Cosmos DB 분산 캐시 백 엔드로 구성합니다.
builder.Services.AddCosmosCache((CosmosCacheOptions options) =>
{
options.ContainerName = builder.Configuration["CosmosCache:ContainerName"];
options.DatabaseName = builder.Configuration["CosmosCache:DatabaseName"];
options.ClientBuilder = new CosmosClientBuilder(
builder.Configuration["CosmosCache:ConnectionString"]);
options.CreateIfNotExists = true;
});
PostgreSQL 캐시
Microsoft.Extensions.Caching.Postgres NuGet 패키지가 필요합니다.
appsettings.json:
{
"ConnectionStrings": {
"PostgresCache": "Host=localhost;Database=mydb;Username=myuser;Password=mypassword"
},
"PostgresCache": {
"SchemaName": "public",
"TableName": "token_cache",
"CreateIfNotExists": true
}
}
그런 다음 , Program.cs PostgreSQL 캐시를 등록합니다.
builder.Services.AddDistributedPostgresCache(options =>
{
options.ConnectionString = builder.Configuration.GetConnectionString("PostgresCache");
options.SchemaName = builder.Configuration["PostgresCache:SchemaName"];
options.TableName = builder.Configuration["PostgresCache:TableName"];
options.CreateIfNotExists = builder.Configuration.GetValue<bool>("PostgresCache:CreateIfNotExists");
options.DefaultSlidingExpiration = TimeSpan.FromMinutes(90);
});
세션 캐시(권장되지 않음)
주의
세션 기반 캐싱에는 상당한 제한 사항이 있습니다. 대신 분산 캐시를 사용합니다.
다음 예제에서는 참조에 대한 세션 기반 토큰 캐싱을 보여줍니다.
using Microsoft.Identity.Web.TokenCacheProviders.Session;
// In Program.cs
builder.Services.AddSession();
builder.Services.AddMicrosoftIdentityWebAppAuthentication(builder.Configuration, "AzureAd")
.EnableTokenAcquisitionToCallDownstreamApi()
.AddSessionTokenCaches();
// In middleware pipeline
app.UseSession(); // Must be before UseAuthentication()
app.UseAuthentication();
app.UseAuthorization();
Limitations:
- 쿠키 크기 문제 - 클레임이 많은 큰 ID 토큰으로 인해 문제가 발생합니다.
-
Scope 충돌 - 싱글톤
TokenAcquisition에서는 사용할 수 없습니다 (예: Microsoft Graph SDK). - 세션 선호도 필요 - 부하 분산 시나리오에서 제대로 작동하지 않음
- 권장되지 않음 - 대신 분산 캐시 사용
고급 구성
이러한 옵션을 사용하면 성능, 보안 및 제거 정책에 대한 캐시 동작을 미세 조정할 수 있습니다.
L1 캐시 제어
L1(메모리 내) 캐시는 분산 캐시를 사용할 때 성능을 향상시킵니다. 다음 코드는 L1 캐시 크기 및 동작을 구성합니다.
builder.Services.Configure<MsalDistributedTokenCacheAdapterOptions>(options =>
{
// Control L1 cache size (default: 500 MB)
options.L1CacheOptions.SizeLimit = 100 * 1024 * 1024; // 100 MB
// Disable L1 cache if session affinity is not available
// (forces all requests to use L2 cache for consistency)
options.DisableL1Cache = false;
});
L1을 사용하지 않도록 설정하는 경우:
- 부하 분산 장치에 세션 선호도 없음
- 캐시 불일치로 인해 MFA를 묻는 메시지가 자주 표시되는 사용자
- 절충점: L2 액세스 속도가 느립니다(~30ms 대 ~10ms)
캐시 제거 정책
제거 정책은 캐시된 토큰이 제거되는 시기를 제어합니다. 다음 코드는 절대 및 슬라이딩 만료를 설정합니다.
builder.Services.Configure<MsalDistributedTokenCacheAdapterOptions>(options =>
{
// Absolute expiration (removed after this time, regardless of use)
options.AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(72);
// Sliding expiration (renewed on each access)
options.SlidingExpiration = TimeSpan.FromHours(2);
});
appsettings.json통해 제거를 구성할 수도 있습니다.
{
"TokenCacheOptions": {
"AbsoluteExpirationRelativeToNow": "72:00:00",
"SlidingExpiration": "02:00:00"
}
}
builder.Services.Configure<MsalDistributedTokenCacheAdapterOptions>(
builder.Configuration.GetSection("TokenCacheOptions"));
권장 사항:
- 토큰 수명보다 더 긴 만료 설정(토큰은 일반적으로 1시간 안에 만료됨)
- 기본값: 90분 슬라이딩 만료
- 메모리 사용량과 사용자 환경 간의 균형
- 고려 사항: 72시간 절대 + 양호한 UX를 위한 슬라이딩 2시간
정지 상태의 데이터 암호화
분산 캐시에서 중요한 토큰 데이터를 보호하려면 ASP.NET Core Data Protection을 통해 암호화를 사용하도록 설정합니다.
단일 기계
단일 컴퓨터에서 기본 제공 데이터 보호 공급자를 사용하여 암호화를 사용하도록 설정합니다.
builder.Services.Configure<MsalDistributedTokenCacheAdapterOptions>(options =>
{
options.Encrypt = true; // Uses ASP.NET Core Data Protection
});
분산 시스템(여러 서버)
중요합니다
분산 시스템은 기본적으로 암호화 키를 공유 하지 않습니다 . 키 공유를 구성해야 합니다.
Azure Key Vault(권장):
다음 코드는 Azure Blob Storage 키를 유지하며 Azure Key Vault 사용하여 키를 보호합니다.
using Microsoft.AspNetCore.DataProtection;
builder.Services.AddDataProtection()
.PersistKeysToAzureBlobStorage(new Uri(builder.Configuration["DataProtection:BlobUri"]))
.ProtectKeysWithAzureKeyVault(
new Uri(builder.Configuration["DataProtection:KeyIdentifier"]),
new DefaultAzureCredential());
인증서 기반:
다음 코드는 파일 공유에 대한 키를 유지하며 X.509 인증서를 사용하여 키를 보호합니다.
builder.Services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\keys"))
.ProtectKeysWithCertificate(
new X509Certificate2("current.pfx", builder.Configuration["CertPassword"]))
.UnprotectKeysWithAnyCertificate(
new X509Certificate2("current.pfx", builder.Configuration["CertPassword"]),
new X509Certificate2("previous.pfx", builder.Configuration["PrevCertPassword"]));
캐시 성능 고려 사항
다음 예상을 사용하여 애플리케이션에 대한 캐시 용량을 계획합니다.
토큰 크기 예측
| 토큰 형식 | 일반적인 크기 | 당 | Notes |
|---|---|---|---|
| 앱 토큰 | ~2KB | 테넌트 × 리소스 | 자동 제거 |
| 사용자 토큰 | ~7KB | 사용자 × 테넌트 × 리소스 | 수동 제거 필요 |
| 토큰 새로 고침 | 변수 | 사용자 | 수명이 긴 |
메모리 계획
3개 API를 호출하는 동시 사용자 500명:
- 사용자 토큰: 500 × 3 × 7KB = 10.5MB
- 오버헤드: ~15-20MB
동시 사용자 10,000명:
- 사용자 토큰: 10,000 × 3 × 7KB = 210MB
- 오버헤드: ~300-350MB
추천: 예상 동시 사용자에 따라 L1 캐시 크기 제한을 설정합니다.
모범 사례
안정적이고 효율적인 토큰 캐싱을 보장하려면 다음 지침을 따르세요.
프로덕션에서 분산 캐시 사용 - 다중 서버 배포에 필수
적절한 캐시 크기 제한 설정 - 바인딩되지 않은 메모리 증가 방지
제거 정책 구성 - UX 및 메모리 사용량 분산
중요한 데이터에 암호화 사용 - 미사용 토큰 보호
캐시 상태 모니터링 - 적중률, 오류 및 성능 추적
L2 캐시 오류를 정상적으로 처리 - L1 캐시는 복원력을 보장합니다.
테스트 캐시 동작 - 다시 시작 시나리오 및 장애 조치 확인
프로덕션 환경에서 분산 메모리 캐시를 사용하지 않음 - 영구 또는 분산되지 않음
세션 캐시 사용 안 함 - 상당한 제한 사항
토큰 수명보다 짧은 만료를 설정하지 마세요 . 불필요한 다시 인증을 강제로 적용합니다.
암호화 키 공유를 잊지 마세요 . 분산 시스템에는 공유 키가 필요합니다.