블레이저에서는 페이지에 기능을 추가하고, 페이지의 요소가 너무 복잡하지 않도록 기능을 쪼개 자식 요소에게 위임하는 경우가 많습니다.
이때, 부모(페이지) 요소가 가진 데이터를 자식 요소로 전달하는 수단은 잘 발달되어 있습니다.
- 요소 파라미터
- Cascading 파라미터
// SchoolsPage.razor
@page "/schools"
<PageTitle>학교기관</PageTitle>
<!--파리미터 전달-->
<ShoolList Model="_schools" />
<!--캐스케이딩-->
<CascadingValue Value="_selectedSchool">
<SchoolDetails />
<SchoolInput />
</CasCadingValue>
@code {
// ...
}
그런데, 페이지 요소의 기능이 복잡하다면, 자식 요소로 파편화하는 게 한계가 있기 마련입니다.
이 경우, 자식 요소 성격의 새로운 페이지 요소를 도입하게 되는 게 일반적인데, 문제는 페이지 요소간 데이터 전달 수단이 매우 한정적이라는 점입니다.
- 파라미터
- 쿼리 스트링
// StudentsPage.razor
@page "/schools/{shooldId}/students"
<h2>@_shoolName 학생들</h2>
// ...
@code {
[Parameter]
public int SchooldId { get; set; }
[SupplyParameterFromQuery]
public string? Gender { get; set; }
string _schoolName = "";
}
그런데, _schoolName
은 SchoolsPage
에서 이미 획득한 데이터입니다.
아래와 같이, 파라미터로 받을 수도 있지만,
[SupplyParameterFromQuery]
public string? SchoolName { get; set; }
이 것이 항상 쉽게 가능하지는 않습니다.
예를 들면, 비 영어 문자열 데이터인데, 이 데이터들은 엔코딩 문제를 해결해야 한다는 제한이 있습니다. 쿼리 엔코딩은 어렵다면 어렵고, 쉽다면 쉬우나, 언제나 마음을 조리게 하는 문제입니다.
사설이 길었습니다.
우선, 페이지 간 데이터 전달을 위해, 캐시서비스(UserDataCache)를 정의합니다.
이 글의 목적은 캐시가 아니라서, 구현은 생략하지만, Duration 과 Sliding 을 지원하는 캐시라고 가정합니다.
데이터 송신자
다른 페이지에게 데이터를 전달하고자 하는 페이지 요소는 아래와 같이 네비게이션이 일어 날 때, 캐시를 등록하게 끔 처리합니다.
// SchoolsPage.razor
@page "/schools"
@inject NavigationManager Navigation
@inject UserDataCache DataCache
// ..
@code {
Shool[] _schools = [];
IDisposable? _navigatingHandler;
public static string CachKey = "schoolsPageCache";
protected override async Task OnInitializedAsync()
{
_navigatingHandler = Navigation.RegisterLocationChangingHandler(
async (context) => {
await DataCache.Set(CacheKey, _shools);
_navigatingHandler?.Dispose();
});
// ...
보시다시피, 캐싱의 시점은 이 페이지에서 나가는 순간입니다.
참고로, 블레이저에는 브라우저 창이 닫히는 이벤트를 받는 수단은 없습니다.
따라서,나가기 전에 이뤄진 데이터에 대한 조작은 캐시에 그대로 반영됩니다.
또한, 캐시된 데이터의 Duration은 이 페이지를 빠져 나가는 순간부터 시작됩니다.
데이터 수신자
캐시된 데이터를 받고자 하는 페이지는 아래와 같이 처리합니다.
// StudentsPage.razor
@page "/schools/{shooldId}/students"
@inject UserDataCache DataCache
@inject NavigationManager Navigation
@inject IMessageBox MessageBox
// ..
@code {
// ...
[Parameter]
public int SchooldId { get; set; }
string? _schoolName;
protected override async Task OnInitializedAsync()
{
var schools = await DataCache.Get<School[]>(SchoolsPage.CacheKey);
_schoolName = schools?.FirstOrDefault(s => s.Id == SchoolId)?.Name;
if (_schoolName is null)
{
await MessageBox.AlertAsync("잘못된 접근입니다.");
Navigation.NavigateTo("/schools");
}
// ...
이 페이지는 브라우저에 아래와 같이 주소를 입력해도 접근을 하지 못합니다.
https://schoolsystem.net/schools/11/students
반드시 schools 를 먼저 방문해야 하는데, 이는 이 페이지가 SchoolsPage 의 자식 요소처럼 동작하기 원했기 때문입니다. (이러한 행태가 필요없다면, 그렇게 하지 않아도 됩니다.)
데이터 송신자의 캐시 이용
참고로, 데이터 송신자도 캐시 고유의 장점을 향유할 수 있습니다.
// SchoolsPage.razor
// ...
@inject ISchoolService SchoolService
@inject UserDataCache DataCache
// ..
@code {
School[] _schools = [];
// ...
protected override async Task OnInitializedAsync()
{
//...
_schools = (await DataCache.Get<School[]>(CacheKey)) ??
(await SchoolService.GetallAsync()) ?? [];
// ...
이 페이지에서 나갔다가 금방 돌아오는 경우나 사용자가 의미 없이 네비게이션을 반복하는 경우, 외부 자원에 대한 접근이 불필요하게 일어나는 것을 예방할 수 있는 장점이 있습니다.