이 단계에서는 어시스턴트에게 앱의 비시각적 부분을 요청합니다. 이 변경 사항을 XAML과 별도로 유지하면 컴파일러 오류 및 디자인 문제를 더 쉽게 격리할 수 있습니다.
모델 및 스토리지 서비스 요청
다음 프롬프트를 사용합니다.
Add the data layer for TaskTally.
- Create a TaskItem model with an ID, title, and completion state.
- Use MVVM Toolkit observable properties.
- Create a TaskStorage service that serializes the task list to JSON.
- Store tasks in ApplicationData.Current.LocalFolder because this is
a packaged WinUI 3 app.
- Return an empty list when the file doesn't exist.
- Serialize save operations so rapid changes don't write the file concurrently.
- Surface real read and write errors to the caller.
- Build the project after the change.
생성된 모델을 다음 구현과 비교합니다.
using CommunityToolkit.Mvvm.ComponentModel;
namespace TaskTally.Models;
public partial class TaskItem : ObservableObject
{
public Guid Id { get; init; } = Guid.NewGuid();
[ObservableProperty]
public partial string Title { get; set; } = string.Empty;
[ObservableProperty]
public partial bool IsComplete { get; set; }
}
이 특성은 [ObservableProperty] 공용 속성 및 변경 알림을 생성합니다. 클래스는 partial MVVM 도구 키트 소스 생성기에서 해당 코드를 추가할 수 있도록 해야 합니다.
스토리지 서비스는 다음과 같습니다.
using System.Text.Json;
using System.Threading;
using TaskTally.Models;
using Windows.Storage;
namespace TaskTally.Services;
internal static class TaskStorage
{
private const string FileName = "tasks.json";
private static readonly SemaphoreSlim SaveLock = new(1, 1);
public static async Task<IReadOnlyList<TaskItem>> LoadAsync()
{
StorageFile? file =
await ApplicationData.Current.LocalFolder.TryGetItemAsync(FileName) as StorageFile;
if (file is null)
{
return [];
}
string json = await FileIO.ReadTextAsync(file);
return JsonSerializer.Deserialize<List<TaskItem>>(json) ?? [];
}
public static async Task SaveAsync(IEnumerable<TaskItem> tasks)
{
await SaveLock.WaitAsync();
try
{
string json = JsonSerializer.Serialize(tasks, new JsonSerializerOptions
{
WriteIndented = true
});
StorageFile file = await ApplicationData.Current.LocalFolder.CreateFileAsync(
FileName,
CreationCollisionOption.ReplaceExisting);
await FileIO.WriteTextAsync(file, json);
}
finally
{
SaveLock.Release();
}
}
}
ApplicationData.Current.LocalFolder 는 앱에 패키지 ID가 있기 때문에 여기에 적합합니다.
SaveLock 는 추가, 삭제 및 완료 작업이 동일한 파일을 동시에 작성하지 못하도록 방지합니다. 나중에 배포 모델을 변경하는 경우 이 스토리지 선택을 자동으로 복사하는 대신 패키지 ID 가정을 다시 확인합니다.
자세한 정보: 설정 및 기타 앱 데이터 및ApplicationData.LocalFolder를 저장하고 검색합니다.
뷰 모델 요청
다음으로, 다음 프롬프트를 사용합니다.
Add MainPageViewModel.
- Expose an ObservableCollection<TaskItem>.
- Add observable properties for the new-task text and status message.
- Add asynchronous commands to add and delete tasks.
- Disable AddTaskCommand when the title is blank.
- Load tasks once when the page starts.
- Save after add, delete, or completion changes.
- Expose a summary and empty-state property for x:Bind.
- Catch storage exceptions only where the UI can report them.
- Build and fix all warnings.
응답에서 검사할 주요 패턴은 다음과 같습니다.
public ObservableCollection<TaskItem> Tasks { get; } = [];
[ObservableProperty]
public partial string NewTaskTitle { get; set; } = string.Empty;
public string Summary =>
Tasks.Count == 0
? "No tasks yet"
: $"{Tasks.Count(task => !task.IsComplete)} of {Tasks.Count} remaining";
[RelayCommand(CanExecute = nameof(CanAddTask))]
private async Task AddTaskAsync()
{
string title = NewTaskTitle.Trim();
Tasks.Add(new TaskItem { Title = title });
NewTaskTitle = string.Empty;
RefreshTaskState();
await SaveAsync($"Added \"{title}\".");
}
[RelayCommand]
private async Task DeleteTaskAsync(TaskItem task)
{
Tasks.Remove(task);
RefreshTaskState();
await SaveAsync($"Deleted \"{task.Title}\".");
}
private bool CanAddTask() => !string.IsNullOrWhiteSpace(NewTaskTitle);
partial void OnNewTaskTitleChanged(string value)
{
AddTaskCommand.NotifyCanExecuteChanged();
}
어시스턴트가 .Wait() 또는 .Result로 비동기 작업을 차단하지 않는지 확인합니다. 스토리지 작업은 대기해야 합니다.
뷰 모델은 스토리지 예외를 페이지용 메시지로 변환할 때 이를 처리할 수 있습니다.
private async Task SaveAsync(string successMessage)
{
try
{
await TaskStorage.SaveAsync(Tasks);
ShowStatus(successMessage);
}
catch (Exception ex)
{
ShowStatus($"Changes couldn't be saved: {ex.Message}", hasError: true);
}
}
이는 자동 대체가 아닙니다. 사용자가 오류를 수신하고 보기 모델은 저장이 성공했다고 주장하지 않습니다.
명령 및 SaveAsync 두 개의 작은 도우미를 호출합니다.
RefreshTaskState 는 작업 목록이 변경된 후 UI가 업데이트되도록 계산된 Summary 속성에 대한 변경 알림을 발생시키고 ShowStatus 페이지가 바인딩하는 관찰 가능한 상태 속성을 설정합니다.
[ObservableProperty]
public partial string StatusMessage { get; set; } = string.Empty;
[ObservableProperty]
public partial bool IsStatusOpen { get; set; }
[ObservableProperty]
public partial bool HasError { get; set; }
private void RefreshTaskState()
{
OnPropertyChanged(nameof(Summary));
OnPropertyChanged(nameof(HasNoTasks));
}
private void ShowStatus(string message, bool hasError = false)
{
StatusMessage = message;
HasError = hasError;
IsStatusOpen = true;
}
[ObservableProperty]는 Summary가 아니라 계산된 속성이므로 Tasks가 변경될 때마다 해당 속성의 변경 알림을 수동으로 발생시켜야 합니다.
빌드 및 검사
x64용으로 빌드합니다. 경고를 작업 항목, 특히 바인딩 및 nullable 참조 경고로 처리합니다.
dotnet build -p:Platform=x64
도우미에게 추가된 모든 패키지를 설명하도록 요청합니다. 이 앱의 경우 템플릿의 Windows 앱 SDK 및 MVVM 도구 키트 참조 외에 새 패키지가 필요하지 않습니다.
Windows developer