1. Memory Cache Interface sample (web XML Data)
2. Memory Cache Interface sample (web Json Data)
- json 데이타 : https://auctionpro.co.kr/airlinecode.json
- json 데이타 이용한 메모리 캐싱 샘픔
- 1.에서 Models->Airline.cs , AirlineHttpClient.cs 만 수정
Web XML Data
UML
Program Files
AirlineController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
using Microsoft.AspNetCore.Mvc; using InMemoryCacheCore.Models; using InMemoryCacheCore.Services; namespace InMemoryCacheCore.Controllers { [ApiController] [Route("[controller]")] public class AirlineController : ControllerBase { private readonly ICodeService _codeService; private readonly ICacheService _cacheService; private readonly ILogger<AirlineController> _logger; public AirlineController(ICodeService codeService, ICacheService cacheService, ILogger<AirlineController> logger) { _codeService = codeService ?? throw new ArgumentNullException(nameof(codeService)); _cacheService = cacheService ?? throw new ArgumentNullException(nameof(cacheService)); _logger = logger; } [HttpGet(Name = "AirlineCode")] public async Task<IEnumerable<Code>> Get() { try { IEnumerable<Code> codes = _cacheService.GetCachedCode("_Codes"); if (codes == null || !codes.Any()) { codes = await _codeService.GetCodeAsync(); } return codes ?? new List<Code>(); //codes = codes.Where(s => s.Airlinecode.Equals("KE", StringComparison.Ordinal)) ?? new List<Code>(); //return codes; } catch (Exception ex) { _logger.LogError(ex, "정보를 가져오는 동안 오류가 발생했습니다."); throw; } } } } |
AirlineHttpClient
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
using InMemoryCacheCore.Models; using System.Text.Json; namespace InMemoryCacheCore.Infrastructure { public interface IHttpClient { Task<IEnumerable<Code>> Get(); } public class AirlineHttpClient : IHttpClient { private const string API_URL = "https://auctionpro.co.kr/airlinecode.json"; private readonly HttpClient _httpClient; public AirlineHttpClient(HttpClient httpClient) { _httpClient = httpClient; } public async Task<IEnumerable<Code>> Get() { HttpResponseMessage response = await _httpClient.GetAsync(API_URL); if (response.IsSuccessStatusCode) { await using var responseStream = await response.Content.ReadAsStreamAsync(); // JsonSerializer를 사용하여 JSON 응답을 역직렬화 var usersResponse = await JsonSerializer.DeserializeAsync<List<Code>>(responseStream) ?? new List<Code>(); return usersResponse; } else { // 예외 메시지에 실패 원인 추가 //throw new Exception($"API 호출 실패: {response.StatusCode}"); return Array.Empty<Code>(); } } } } |
CacheProvider
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
using Microsoft.Extensions.Caching.Memory; namespace InMemoryCacheCore.Infrastructure { public interface ICacheProvider { T? GetFromCache<T>(string key) where T : class; void SetCache<T>(string key, T value, MemoryCacheEntryOptions options) where T : class; void ClearCache(string key); } public class CacheProvider : ICacheProvider { private readonly IMemoryCache _cache; public CacheProvider(IMemoryCache cache) { _cache = cache; } public T? GetFromCache<T>(string key) where T : class { //_cache.TryGetValue(key, out T cachedResponse); //return cachedResponse as T; return _cache.Get<T>(key); } public void SetCache<T>(string key, T value, MemoryCacheEntryOptions options) where T : class { _cache.Set(key, value, options); } public void ClearCache(string key) { _cache.Remove(key); } } } |
Models->Airlines
1 2 3 4 5 6 7 8 9 10 11 12 |
namespace InMemoryCacheCore.Models { public class Code { public string? Airlinecode { get; set; } public string? Airline3code { get; set; } public string? Airlinekor { get; set; } public string? Airlineeng { get; set; } } } |
CachedCodeService
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
using InMemoryCacheCore.Infrastructure; using InMemoryCacheCore.Models; using Microsoft.Extensions.Caching.Memory; namespace InMemoryCacheCore.Services { public class CachedCodeService : ICodeService { private readonly CodeService _codeService; private readonly ICacheProvider _cacheProvider; private const int CacheTTLInSeconds = 100; private readonly MemoryCacheEntryOptions cacheEntryOptions = new MemoryCacheEntryOptions() .SetSlidingExpiration(TimeSpan.FromSeconds(CacheTTLInSeconds)); private static readonly SemaphoreSlim GetUsersSemaphore = new(1, 1); public CachedCodeService(CodeService codeService, ICacheProvider cacheProvider) { _codeService = codeService; _cacheProvider = cacheProvider; } public async Task<IEnumerable<Code>> GetCodeAsync() { return await GetCachedResponse("_Codes", GetUsersSemaphore, () => _codeService.GetCodeAsync()); } private async Task<IEnumerable<Code>> GetCachedResponse(string cacheKey, SemaphoreSlim semaphore, Func<Task<IEnumerable<Code>>> func) { var users = _cacheProvider?.GetFromCache<IEnumerable<Code>>(cacheKey); if (users != null) return users; try { await semaphore.WaitAsync(); users = _cacheProvider?.GetFromCache<IEnumerable<Code>>(cacheKey); // Recheck to make sure it didn't populate before entering semaphore if (users != null) return users; users = await func(); _cacheProvider?.SetCache(cacheKey, users, cacheEntryOptions); } finally { semaphore.Release(); } return users; } } } |
CacheService
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
using InMemoryCacheCore.Infrastructure; using InMemoryCacheCore.Models; namespace InMemoryCacheCore.Services { public interface ICacheService { IEnumerable<Code> GetCachedCode(string cacheKey); } public class CacheService : ICacheService { private readonly ICacheProvider _cacheProvider; public CacheService(ICacheProvider cacheProvider) { _cacheProvider = cacheProvider; } public IEnumerable<Code> GetCachedCode(string cacheKey) { return _cacheProvider.GetFromCache<IEnumerable<Code>>(cacheKey); } public void ClearCache(string cachekey) { _cacheProvider.ClearCache(cachekey); } } } |
CodeService
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
using InMemoryCacheCore.Infrastructure; using InMemoryCacheCore.Models; namespace InMemoryCacheCore.Services { public interface ICodeService { Task<IEnumerable<Code>> GetCodeAsync(); } public class CodeService : ICodeService { private readonly IHttpClient _httpClient; public CodeService(IHttpClient httpClient) { _httpClient = httpClient; } public Task<IEnumerable<Code>> GetCodeAsync() { return _httpClient.Get(); } } } |
Program.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
using InMemoryCacheCore.Infrastructure; using InMemoryCacheCore.Services; var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllers(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); // builder.Services.AddHttpClient(); builder.Services.AddSingleton<CodeService>(); //MainService builder.Services.AddSingleton<ICodeService, CachedCodeService>(); //User 연결 서비스 builder.Services.AddSingleton<ICacheService, CacheService>(); //캐싱 서비스 builder.Services.AddSingleton<ICacheProvider, CacheProvider>(); //infrastructure builder.Services.AddSingleton<IHttpClient, AirlineHttpClient>(); //infrastructure builder.Services.AddMemoryCache(); var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); app.Run(); |