Athena.Cache.Redis
1.1.9
dotnet add package Athena.Cache.Redis --version 1.1.9
NuGet\Install-Package Athena.Cache.Redis -Version 1.1.9
<PackageReference Include="Athena.Cache.Redis" Version="1.1.9" />
<PackageVersion Include="Athena.Cache.Redis" Version="1.1.9" />
<PackageReference Include="Athena.Cache.Redis" />
paket add Athena.Cache.Redis --version 1.1.9
#r "nuget: Athena.Cache.Redis, 1.1.9"
#:package Athena.Cache.Redis@1.1.9
#addin nuget:?package=Athena.Cache.Redis&version=1.1.9
#tool nuget:?package=Athena.Cache.Redis&version=1.1.9
๐๏ธ Athena.Cache
Smart caching library for ASP.NET Core with automatic query parameter key generation and table-based cache invalidation.
Athena.Cache๋ ASP.NET Core ์ ํ๋ฆฌ์ผ์ด์ ์ ์ํ ์ง๋ฅํ ์บ์ฑ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋๋ค. ์ฟผ๋ฆฌ ํ๋ผ๋ฏธํฐ๋ฅผ ์๋์ผ๋ก ์บ์ ํค๋ก ๋ณํํ๊ณ , ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ ์ด๋ธ ๋ณ๊ฒฝ ์ ๊ด๋ จ ์บ์๋ฅผ ์๋์ผ๋ก ๋ฌดํจํํฉ๋๋ค.
โจ ์ฃผ์ ๊ธฐ๋ฅ
- ๐ฏ Source Generator: ์ปดํ์ผ ํ์์ ์บ์ ์ค์ ์๋ ์์ฑ, AOT ํธํ
- ๐ ์๋ ์บ์ ํค ์์ฑ: ์ฟผ๋ฆฌ ํ๋ผ๋ฏธํฐ โ SHA256 ํด์ ํค ์๋ ๋ณํ
- ๐๏ธ ํ ์ด๋ธ ๊ธฐ๋ฐ ๋ฌดํจํ: ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ ์ด๋ธ ๋ณ๊ฒฝ ์ ๊ด๋ จ ์บ์ ์๋ ์ญ์
- ๐๏ธ Convention ๊ธฐ๋ฐ ์ถ๋ก : ์ปจํธ๋กค๋ฌ๋ช ์์ ํ ์ด๋ธ๋ช ์๋ ์ถ๋ก , ์ค๋ณต ์ ์ธ ๋ถํ์
- ๐ ๋ค์ค ๋ฐฑ์๋ ์ง์: MemoryCache, Redis, Valkey ์ง์
- ๐จ ์ ์ธ์ ์บ์ฑ:
[AthenaCache]
,[CacheInvalidateOn]
์ดํธ๋ฆฌ๋ทฐํธ - โก ๊ณ ์ฑ๋ฅ: ๋์ฉ๋ ํธ๋ํฝ ํ๊ฒฝ์ ์ต์ ํ, Primary Constructor ์ฌ์ฉ
- ๐ง ์ ๋ก ๋ฉ๋ชจ๋ฆฌ ํ ๋น: 5๋จ๊ณ ์ต์ ํ๋ก ๋ฉ๋ชจ๋ฆฌ ํ ๋น 90-98% ๊ฐ์
- ๐ ์๋ ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ: ์ค์๊ฐ GC ๋ชจ๋ํฐ๋ง ๋ฐ ์๋ ์บ์ ์ ๋ฆฌ
- ๐ง ์ฌ์ด ํตํฉ: ๋จ์ผ ํจํค์ง ์ค์น๋ก ๋ชจ๋ ๊ธฐ๋ฅ ์ฌ์ฉ ๊ฐ๋ฅ
- ๐งช ์์ ํ ํ ์คํธ: ํฌ๊ด์ ์ธ ๋จ์ ๋ฐ ํตํฉ ํ ์คํธ
๐ ๋น ๋ฅธ ์์
์ค์น
# ๊ธฐ๋ณธ ํจํค์ง (MemoryCache + Source Generator ํฌํจ)
dotnet add package Athena.Cache.Core
# Redis ์ง์ (์ ํ์ฌํญ)
dotnet add package Athena.Cache.Redis
๐ฏ ํตํฉ ํจํค์ง:
Athena.Cache.Core
๋ง ์ค์นํ๋ฉด Source Generator๊ฐ ์๋์ผ๋ก ํฌํจ๋์ด ์ปดํ์ผ ํ์์ ์บ์ ์ค์ ์ ์๋ ์์ฑํฉ๋๋ค.
๊ธฐ๋ณธ ์ค์
// Program.cs
using Athena.Cache.Core.Extensions;
var builder = WebApplication.CreateBuilder(args);
// ๊ฐ๋ฐ ํ๊ฒฝ (MemoryCache)
builder.Services.AddAthenaCacheComplete(options => {
options.Namespace = "MyApp_DEV";
options.DefaultExpirationMinutes = 30;
options.Logging.LogCacheHitMiss = true; // ์บ์ ํํธ/๋ฏธ์ค ๋ก๊น
});
// ์ด์ ํ๊ฒฝ (Redis)
builder.Services.AddAthenaCacheRedisComplete(
athena => {
athena.Namespace = "MyApp_PROD";
athena.DefaultExpirationMinutes = 60;
},
redis => {
redis.ConnectionString = "localhost:6379";
redis.DatabaseId = 1;
});
var app = builder.Build();
// ๐ง ๋ฏธ๋ค์จ์ด ์ถ๊ฐ (์ค์: ๋ผ์ฐํ
ํ, ์ปจํธ๋กค๋ฌ ์ ์ ์ถ๊ฐ)
app.UseRouting();
app.UseAthenaCache(); // ๋ผ์ฐํ
ํ์ ์ถ๊ฐ
app.MapControllers();
app.Run();
์ปจํธ๋กค๋ฌ์์ ์ฌ์ฉ
using Athena.Cache.Core.Attributes;
using Athena.Cache.Core.Enums;
[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
private readonly IUserService _userService;
public UsersController(IUserService userService)
{
_userService = userService;
}
[HttpGet]
[AthenaCache(ExpirationMinutes = 30)]
[CacheInvalidateOn("Users")]
public async Task<ActionResult<IEnumerable<UserDto>>> GetUsers(
[FromQuery] string? search = null,
[FromQuery] int page = 1)
{
// ๐ Source Generator๊ฐ ์ปดํ์ผ ํ์์ ์ด ์ดํธ๋ฆฌ๋ทฐํธ๋ฅผ ์ค์บํ์ฌ
// ์บ์ ์ค์ ์ ์๋ ์์ฑํฉ๋๋ค. ๋ณ๋ ์ค์ ๋ถํ์!
var users = await _userService.GetUsersAsync(search, page);
return Ok(users);
}
[HttpGet("{id}")]
[AthenaCache(ExpirationMinutes = 60)]
[CacheInvalidateOn("Users")]
[CacheInvalidateOn("Orders", InvalidationType.Pattern, "User_*")]
public async Task<ActionResult<UserDto>> GetUser(int id)
{
var user = await _userService.GetUserByIdAsync(id);
if (user == null) return NotFound();
return Ok(user);
}
[HttpPost]
public async Task<ActionResult<UserDto>> CreateUser([FromBody] User user)
{
var createdUser = await _userService.CreateUserAsync(user);
// Users ํ
์ด๋ธ ๊ด๋ จ ์บ์๊ฐ ์๋ ๋ฌดํจํ๋จ
return CreatedAtAction(nameof(GetUser), new { id = createdUser.Id }, createdUser);
}
// ์บ์ ๋นํ์ฑํ ์์
[HttpGet("no-cache")]
[NoCache] // ์ด ์ก์
์ ์บ์ฑํ์ง ์์
public async Task<ActionResult<IEnumerable<UserDto>>> GetUsersWithoutCache()
{
var users = await _userService.GetUsersAsync();
return Ok(users);
}
}
๐ ์บ์ ์ํ ํ์ธ
Athena.Cache๋ HTTP ํค๋๋ฅผ ํตํด ์บ์ ์ํ๋ฅผ ํ์ธํ ์ ์์ต๋๋ค:
# ์ฒซ ๋ฒ์งธ ์์ฒญ (์บ์ ๋ฏธ์ค)
curl -v http://localhost:5000/api/users
# ์๋ต ํค๋: X-Athena-Cache: MISS
# ๋ ๋ฒ์งธ ์์ฒญ (์บ์ ํํธ)
curl -v http://localhost:5000/api/users
# ์๋ต ํค๋: X-Athena-Cache: HIT
๐ฏ Convention ๊ธฐ๋ฐ ์บ์ฑ (๊ถ์ฅ)
Convention ๊ธฐ๋ฐ ์บ์ฑ์ ์ฌ์ฉํ๋ฉด CacheInvalidateOn
์ดํธ๋ฆฌ๋ทฐํธ๋ฅผ ๋ฐ๋ณต ์ ์ธํ ํ์ ์์ด, ์ปจํธ๋กค๋ฌ๋ช
์์ ํ
์ด๋ธ๋ช
์ ์๋์ผ๋ก ์ถ๋ก ํฉ๋๋ค.
๊ธฐ๋ณธ ์ค์ (Convention ํ์ฑํ)
// Program.cs
builder.Services.AddAthenaCacheComplete(options => {
options.Namespace = "MyApp";
options.DefaultExpirationMinutes = 30;
// Convention ๊ธฐ๋ฐ ํ
์ด๋ธ๋ช
์ถ๋ก ํ์ฑํ (๊ธฐ๋ณธ๊ฐ: true)
options.Convention.Enabled = true;
options.Convention.UsePluralizer = true; // UsersController โ Users
});
๊ฐ๋จํ ์ฌ์ฉ๋ฒ
[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase // ์๋์ผ๋ก "Users" ํ
์ด๋ธ๊ณผ ์ฐ๊ฒฐ
{
// โ
Convention ์ฌ์ฉ: CacheInvalidateOn ์ค๋ณต ์ ์ธ ๋ถํ์
[HttpGet]
[AthenaCache(ExpirationMinutes = 30)]
public async Task<ActionResult<IEnumerable<UserDto>>> GetUsers()
{
// ์๋์ผ๋ก Users ํ
์ด๋ธ ๋ณ๊ฒฝ ์ ์บ์ ๋ฌดํจํ
return Ok(await _userService.GetUsersAsync());
}
[HttpPost]
public async Task<ActionResult<UserDto>> CreateUser([FromBody] User user)
{
// ์๋์ผ๋ก Users ํ
์ด๋ธ ๊ด๋ จ ์บ์ ๋ฌดํจํ
var createdUser = await _userService.CreateUserAsync(user);
return CreatedAtAction(nameof(GetUser), new { id = createdUser.Id }, createdUser);
}
// ์ถ๊ฐ ํ
์ด๋ธ์ด ํ์ํ ๊ฒฝ์ฐ์๋ง ๋ช
์์ ์ ์ธ
[HttpGet("{id}")]
[AthenaCache(ExpirationMinutes = 60)]
[CacheInvalidateOn("Orders", InvalidationType.Pattern, "User_*")] // Users๋ ์๋, Orders๋ง ๋ช
์
public async Task<ActionResult<UserDto>> GetUser(int id)
{
return Ok(await _userService.GetUserByIdAsync(id));
}
}
๊ณ ๊ธ Convention ์ค์
// ๋ค์ค ํ
์ด๋ธ ๋งคํ
builder.Services.AddAthenaCacheComplete(options => {
options.Convention.ControllerTableMappings = new Dictionary<string, string[]>
{
["UsersController"] = ["Users", "UserProfiles"],
["OrdersController"] = ["Orders", "OrderItems", "Users"]
};
// ์ปค์คํ
์ถ๋ก ํจ์
options.Convention.CustomMultiTableInferrer = controllerName =>
{
// UsersOrdersController โ ["Users", "Orders"]
if (controllerName.Contains("Users") && controllerName.Contains("Orders"))
return ["Users", "Orders"];
return [controllerName.Replace("Controller", "")];
};
});
Convention vs ๋ช ์์ ์ ์ธ ๋น๊ต
// โ ๊ธฐ์กด ๋ฐฉ์: ๋ชจ๋ ๋ฉ์๋์ ์ค๋ณต ์ ์ธ
public class UsersController : ControllerBase
{
[HttpGet]
[AthenaCache(ExpirationMinutes = 30)]
[CacheInvalidateOn("Users")] // ์ค๋ณต
public async Task<IActionResult> GetUsers() { ... }
[HttpPost]
[CacheInvalidateOn("Users")] // ์ค๋ณต
public async Task<IActionResult> CreateUser() { ... }
[HttpPut("{id}")]
[CacheInvalidateOn("Users")] // ์ค๋ณต
public async Task<IActionResult> UpdateUser() { ... }
}
// โ
Convention ๋ฐฉ์: ์๋ ์ถ๋ก ์ผ๋ก ๊ฐ์ํ
public class UsersController : ControllerBase
{
[HttpGet]
[AthenaCache(ExpirationMinutes = 30)]
public async Task<IActionResult> GetUsers() { ... } // Users ํ
์ด๋ธ ์๋ ์ฐ๊ฒฐ
[HttpPost]
public async Task<IActionResult> CreateUser() { ... } // Users ํ
์ด๋ธ ์๋ ๋ฌดํจํ
[HttpPut("{id}")]
public async Task<IActionResult> UpdateUser() { ... } // Users ํ
์ด๋ธ ์๋ ๋ฌดํจํ
}
๋ง์ด๊ทธ๋ ์ด์ ๊ฐ์ด๋
๊ธฐ์กด ํ๋ก์ ํธ์์ Convention์ผ๋ก ์ ํํ๋ ๋ฐฉ๋ฒ:
- Convention ํ์ฑํ
options.Convention.Enabled = true;
- ์ค๋ณต ์ ์ธ ์ ๊ฑฐ (์ ํ์ฌํญ)
// ์ ๊ฑฐ ๊ฐ๋ฅ: ์ปจํธ๋กค๋ฌ๋ช
๊ณผ ๋์ผํ ํ
์ด๋ธ์ CacheInvalidateOn
[CacheInvalidateOn("Users")] // UsersController์์ ์ ๊ฑฐ ๊ฐ๋ฅ
- ์ ์ง์ ๋ง์ด๊ทธ๋ ์ด์
- Convention๊ณผ ๋ช ์์ ์ ์ธ์ด ํจ๊ป ์๋
- ๋ช ์์ ์ ์ธ์ด ์ฐ์ ์ ์ฉ๋์ด ๊ธฐ์กด ์ฝ๋ ์ํฅ ์์
Convention ๋นํ์ฑํ
ํน์ ์ปจํธ๋กค๋ฌ์์ Convention ์ถ๋ก ์ ๋นํ์ฑํํ๊ณ ์๋์ผ๋ก ์บ์๋ฅผ ์ ์ดํ๋ ค๋ ๊ฒฝ์ฐ:
๋ฐฉ๋ฒ 1: NoConventionInvalidation ์ดํธ๋ฆฌ๋ทฐํธ
[ApiController]
[NoConventionInvalidation] // Convention ์ถ๋ก ๋นํ์ฑํ
public class ReportsController : ControllerBase
{
private readonly ICacheInvalidator _cacheInvalidator;
[HttpGet]
[AthenaCache(ExpirationMinutes = 120)]
public async Task<IActionResult> GetReport()
{
// ์บ์ฑ๋ง ํ๊ณ , ์๋ ๋ฌดํจํ ์์
return Ok(data);
}
[HttpPost("refresh")]
public async Task<IActionResult> RefreshData()
{
// ์๋์ผ๋ก ์บ์ ๋ฌดํจํ
await _cacheInvalidator.InvalidateByPatternAsync("*GetReport*");
return Ok();
}
}
๋ฐฉ๋ฒ 2: ์ ์ญ ์ค์ ์ผ๋ก ์ ์ธ
builder.Services.AddAthenaCacheComplete(options => {
options.Convention.Enabled = true;
options.Convention.ExcludedControllers.Add("ReportsController");
options.Convention.ExcludedControllers.Add("AnalyticsController");
});
๋ฐฉ๋ฒ 3: ์ ์ฒด Convention ๋นํ์ฑํ
builder.Services.AddAthenaCacheComplete(options => {
options.Convention.Enabled = false; // ๋ชจ๋ ์ปจํธ๋กค๋ฌ์์ ๋นํ์ฑํ
});
์๋ ์บ์ ์ ์ด ํจํด
public class ManualCacheController : ControllerBase
{
private readonly ICacheInvalidator _invalidator;
[HttpGet]
[AthenaCache]
[NoConventionInvalidation]
public async Task<IActionResult> GetData() { ... }
[HttpPost]
[NoConventionInvalidation]
public async Task<IActionResult> UpdateData()
{
// ๋น์ฆ๋์ค ๋ก์ง ์คํ
var result = await _service.UpdateAsync();
// ์ ํ์ ์บ์ ๋ฌดํจํ
if (result.ShouldInvalidateCache)
{
await _invalidator.InvalidateByPatternAsync("GetData*");
}
return Ok(result);
}
}
๐ ๏ธ ๊ณ ๊ธ ๊ธฐ๋ฅ
์ปค์คํ ์บ์ ํค
[AthenaCache(
ExpirationMinutes = 45,
CustomKeyPrefix = "UserStats",
ExcludeParameters = new[] { "debug", "trace" }
)]
public async Task<IActionResult> GetUserStatistics(int userId, bool debug = false)
{
// debug ํ๋ผ๋ฏธํฐ๋ ์บ์ ํค ์์ฑ์์ ์ ์ธ๋จ
return Ok(stats);
}
ํจํด ๊ธฐ๋ฐ ๋ฌดํจํ
[CacheInvalidateOn("Users", InvalidationType.All)] // ๋ชจ๋ Users ๊ด๋ จ ์บ์
[CacheInvalidateOn("Orders", InvalidationType.Pattern, "User_*")] // User_* ํจํด ์บ์
[CacheInvalidateOn("Products", InvalidationType.Related, "Categories")] // ์ฐ๊ด ํ
์ด๋ธ๊น์ง
public async Task<IActionResult> GetUserOrders(int userId) { ... }
์๋ ์บ์ ๊ด๋ฆฌ
public class UserService
{
private readonly IAthenaCache _cache;
private readonly ICacheInvalidator _invalidator;
public UserService(IAthenaCache cache, ICacheInvalidator invalidator)
{
_cache = cache;
_invalidator = invalidator;
}
public async Task InvalidateUserCaches(int userId)
{
// ํน์ ์ฌ์ฉ์ ๊ด๋ จ ์บ์๋ง ์ญ์
await _invalidator.InvalidateByPatternAsync($"User_{userId}_*");
}
public async Task<CacheStatistics> GetCacheStats()
{
return await _cache.GetStatisticsAsync();
}
}
๐ ์ฑ๋ฅ
๐ ๊ธฐ๋ณธ ์ฑ๋ฅ ์งํ
- ๋์ ์ฒ๋ฆฌ๋: Redis ๊ธฐ์ค 10,000+ requests/second
- ๋ฎ์ ์ง์ฐ์๊ฐ: ์บ์ ํค ์์ฑ 1ms ๋ฏธ๋ง
- ๋ฉ๋ชจ๋ฆฌ ํจ์จ์ฑ: ์ต์ ํ๋ ์ง๋ ฌํ ๋ฐ ์์ถ
- ํ์ฅ ๊ฐ๋ฅ: ๋ค์ค ์ธ์คํด์ค ๋ถ์ฐ ๋ฌดํจํ ์ง์
๐ง ์ ๋ก ๋ฉ๋ชจ๋ฆฌ ํ ๋น ์ต์ ํ ๊ฒฐ๊ณผ
์ต์ ํ ์์ญ | ๊ฐ์ ์จ | ์ฃผ์ ๊ธฐ๋ฒ |
---|---|---|
๋ฉ๋ชจ๋ฆฌ ํ ๋น | 90-98% ๊ฐ์ | ์ปฌ๋ ์ ํ๋ง, Span/Memory, ์บ์ฑ |
GC ์๋ฐ | ~90% ๊ฐ์ | ๊ฐ ํ์ , ๋ฌธ์์ด ์ธํฐ๋, ์๋ ์ ๋ฆฌ |
๋ฌธ์์ด ์ฒ๋ฆฌ | ~98% ๊ฐ์ | StringBuilder ํ๋ง, ์ฝํ ์ฐธ์กฐ ์ธํฐ๋ |
์ปฌ๋ ์ ์ฐ์ฐ | ~95% ๊ฐ์ | LINQ ์ ๊ฑฐ, ์๋ ๋ฃจํ, ArrayPool |
๋ฐ์ฑ/์ธ๋ฐ์ฑ | 100% ์ ๊ฑฐ | ๊ฐ ํ์ ๊ตฌ์กฐ์ฒด, ์ ๋ค๋ฆญ ํต๊ณ |
๐ ์ฑ๋ฅ ํ ์คํธ ๋ฐ๋ชจ
# ์ฑ๋ฅ ๋น๊ต ํ
์คํธ ์คํ
curl -X POST "http://localhost:5000/api/ZeroMemoryDemo/performance-comparison?iterations=10000"
# ๋ฉ๋ชจ๋ฆฌ ์ํ ํ์ธ
curl "http://localhost:5000/api/ZeroMemoryDemo/memory-status"
# ๋ฉ๋ชจ๋ฆฌ ์๋ฐ ์๋ฎฌ๋ ์ด์
curl -X POST "http://localhost:5000/api/ZeroMemoryDemo/memory-pressure-test"
๐ ์์ธ ๊ฐ์ด๋: ์ ๋ก ๋ฉ๋ชจ๋ฆฌ ์ต์ ํ ๊ธฐ๋ฒ์ ๋ํ ์์ธํ ์ค๋ช ์ docs/ZeroMemoryOptimization.md๋ฅผ ์ฐธ์กฐํ์ธ์.
๐ง ์ค์ ์ต์
์ ์ญ ์ค์
services.AddAthenaCacheComplete(options => {
options.Namespace = "MyApp"; // ๋ค์์คํ์ด์ค (ํ๊ฒฝ ๋ถ๋ฆฌ)
options.VersionKey = "v1.0"; // ๋ฒ์ ํค (๋ฐฐํฌ ์ ์บ์ ๋ถ๋ฆฌ)
options.DefaultExpirationMinutes = 30; // ๊ธฐ๋ณธ ๋ง๋ฃ ์๊ฐ
options.MaxRelatedDepth = 3; // ์ฐ์ ๋ฌดํจํ ์ต๋ ๊น์ด
options.StartupCacheCleanup = CleanupMode.ExpireShorten; // ์์ ์ ์ ๋ฆฌ ๋ฐฉ์
// ๋ก๊น
์ค์
options.Logging.LogCacheHitMiss = true; // ํํธ/๋ฏธ์ค ๋ก๊น
options.Logging.LogInvalidation = true; // ๋ฌดํจํ ๋ก๊น
// ์๋ฌ ์ฒ๋ฆฌ
options.ErrorHandling.SilentFallback = true; // ์กฐ์ฉํ ํด๋ฐฑ
});
Redis ์ค์
services.AddAthenaCacheRedisComplete(
athena => { /* Athena ์ค์ */ },
redis => {
redis.ConnectionString = "localhost:6379";
redis.DatabaseId = 1;
redis.KeyPrefix = "MyApp";
redis.BatchSize = 1000;
redis.ConnectTimeoutSeconds = 5;
redis.RetryCount = 3;
});
๐งช ํ ์คํธ
# ๋ชจ๋ ํ
์คํธ ์คํ
dotnet test
# ์ปค๋ฒ๋ฆฌ์ง ํฌํจ
dotnet test --collect:"XPlat Code Coverage"
# ํตํฉ ํ
์คํธ (Redis ํ์)
docker run -d -p 6379:6379 redis:7-alpine
dotnet test --filter Category=Integration
๐๏ธ ์ํคํ ์ฒ
ํต์ฌ ์ปดํฌ๋ํธ
- ๐ฏ Source Generator: ์ปดํ์ผ ํ์์ Controller ์ดํธ๋ฆฌ๋ทฐํธ๋ฅผ ์ค์บํ์ฌ ์บ์ ์ค์ ์๋ ์์ฑ
- ICacheConfigurationRegistry: ์์ฑ๋ ์บ์ ์ค์ ์ ๊ด๋ฆฌํ๋ ๋ ์ง์คํธ๋ฆฌ
- ICacheKeyGenerator: ์ฟผ๋ฆฌ ํ๋ผ๋ฏธํฐ โ ์บ์ ํค ๋ณํ
- ICacheInvalidator: ํ ์ด๋ธ ๊ธฐ๋ฐ ์บ์ ๋ฌดํจํ ๊ด๋ฆฌ
- IAthenaCache: ์บ์ ์ ๊ณต์ ์ถ์ํ (Memory/Redis/Valkey)
- AthenaCacheMiddleware: HTTP ์์ฒญ ๊ฐ๋ก์ฑ๊ธฐ ๋ฐ ์บ์ฑ (Primary Constructor ์ฌ์ฉ)
๋์ ์๋ฆฌ
- ๐ง ์ปดํ์ผ ํ์: Source Generator๊ฐ Controller๋ฅผ ์ค์บํ์ฌ ์บ์ ์ค์ ํด๋์ค ์๋ ์์ฑ
- ๐ ๋ฐํ์ ์ด๊ธฐํ: ์์ฑ๋ ์ค์ ์ด ์์ผ๋ฉด ์ฌ์ฉ, ์์ผ๋ฉด Reflection ๋ฐฑ์ ์ฌ์ฉ
- ๐ก ์์ฒญ ๊ฐ๋ก์ฑ๊ธฐ: ๋ฏธ๋ค์จ์ด๊ฐ GET ์์ฒญ์ ๋ผ์ฐํ ์ ๋ณด์ ํจ๊ป ๊ฐ๋ก์ฑ
- ๐ ํค ์์ฑ: ์ฟผ๋ฆฌ ํ๋ผ๋ฏธํฐ๋ฅผ ์ ๋ ฌํ์ฌ SHA256 ํด์ ์์ฑ
- ๐พ ์บ์ ํ์ธ: ์์ฑ๋ ํค๋ก ์บ์์์ ์๋ต ์กฐํ
- ๐ฆ ์๋ต ์บ์ฑ: ์บ์ ๋ฏธ์ค ์ ์๋ต์ ์บ์์ ์ ์ฅ ํ ํ ์ด๋ธ ์ถ์ ๋ฑ๋ก
- ๐ ๋ฌดํจํ: ํ ์ด๋ธ ๋ณ๊ฒฝ ์ ๊ด๋ จ ์บ์ ์๋ ์ญ์
๐ฏ Source Generator ์ฅ์
- โก ์ฑ๋ฅ: ๋ฐํ์ Reflection ๋ถํ์, ์ปดํ์ผ ํ์ ์ต์ ํ
- ๐ก๏ธ ํ์ ์์ ์ฑ: ์ปดํ์ผ ํ์์ ์ดํธ๋ฆฌ๋ทฐํธ ๊ฒ์ฆ
- ๐ AOT ํธํ: Native AOT ๋ฐฐํฌ ์ง์
- ๐ง ์๋ ๊ด๋ฆฌ: ๋ณ๋ ์ค์ ํ์ผ ๋ถํ์, ์ดํธ๋ฆฌ๋ทฐํธ๋ง์ผ๋ก ์์ฑ
๐ ์บ์ ๋ฌดํจํ ์ ๋ต
1. ์ฆ์ ๋ฌดํจํ (Immediate)
[CacheInvalidateOn("Users", InvalidationType.All)]
2. ํจํด ๋ฌดํจํ (Pattern-based)
[CacheInvalidateOn("Users", InvalidationType.Pattern, "User_*")]
3. ์ฐ๊ด ๋ฌดํจํ (Related)
[CacheInvalidateOn("Users", InvalidationType.Related, "Orders", "Profiles")]
๐ฆ ์ถ๊ฐ ํจํค์ง
# Source Generator (์ ํ์ - Core์ ํฌํจ๋์ง๋ง ๋
๋ฆฝ ์ค์น ๊ฐ๋ฅ)
dotnet add package Athena.Cache.SourceGenerator
# ๋ถ์ ๋ฐ ๋ชจ๋ํฐ๋ง (์ ํ์ )
dotnet add package Athena.Cache.Analytics
๐ ๋ฆด๋ฆฌ์ฆ ์ ๋ณด: ์์ธํ ๋ฆด๋ฆฌ์ฆ ํ๋ก์ธ์ค์ ๋ฒ์ ๊ด๋ฆฌ ์ ๋ต์ RELEASE.md๋ฅผ ์ฐธ์กฐํ์ธ์.
๐ค ๊ธฐ์ฌํ๊ธฐ
- ์ ์ฅ์ ํฌํฌ
- ๊ธฐ๋ฅ ๋ธ๋์น ์์ฑ (
git checkout -b feature/amazing-feature
) - ๋ณ๊ฒฝ์ฌํญ ์ปค๋ฐ (
git commit -m 'Add amazing feature'
) - ๋ธ๋์น์ ํธ์ (
git push origin feature/amazing-feature
) - Pull Request ์คํ
๊ฐ๋ฐ ํ๊ฒฝ ์ค์
git clone https://github.com/jhbrunoK/Athena.Cache.git
cd Athena.Cache
dotnet restore
dotnet build
dotnet test
๐ ๋ผ์ด์ผ์ค
์ด ํ๋ก์ ํธ๋ MIT ๋ผ์ด์ผ์ค ํ์ ๋ฐฐํฌ๋ฉ๋๋ค. ์์ธํ ๋ด์ฉ์ LICENSE ํ์ผ์ ์ฐธ์กฐํ์ธ์.
๐ ๊ฐ์ฌ์ ๋ง
- ์ ๋ต๊ณผ ์งํ์ ์ฌ์ ์ํ ๋์์ ์๊ฐ์ ๋ฐ์์ต๋๋ค
- ASP.NET Core ์ปค๋ฎค๋ํฐ๋ฅผ ์ํด ์ ์๋์์ต๋๋ค
- ๋ชจ๋ ๊ธฐ์ฌ์๋ถ๋ค๊ป ๊ฐ์ฌ๋๋ฆฝ๋๋ค
๐ ์ง์ ๋ฐ ๋ฌธ์
- ๐ ๋ฒ๊ทธ ๋ฆฌํฌํธ: GitHub Issues
- ๐ก ๊ธฐ๋ฅ ์์ฒญ: GitHub Discussions
- ๐ง ์ด๋ฉ์ผ: bobhappy2000@gmail.com
โค๏ธ ๊ณ ์ฑ๋ฅ ASP.NET Core ์ ํ๋ฆฌ์ผ์ด์ ์ ์ํด ์ ์๋์์ต๋๋ค
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- Athena.Cache.Core (>= 1.1.9)
- Microsoft.Extensions.DependencyInjection (>= 9.0.7)
- StackExchange.Redis (>= 2.8.58)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
v1.1.9: Redis provider updates and improvements