C# & .NET

[C# UWP] UWP에서 설정값 저장하고 불러오기

카루-R 2022. 2. 23. 10:45
반응형

환영합니다, Rolling Ress의 카루입니다.

GGHS Time Table을 개발하면서 이제껏 설정값을 그냥 StorageFile 클래스로 걍 파일에 텍스트로 읽고 쓰며 저장했습니다. 그러다가 XmlWriter에 대해 알게되고 그 뒤로는 그걸 써왔는데, UWP에서 제공하는 아주 쉽고 간단한 방법이 있더군요.

// 설정 쓰기
SubjectList list = new(ttc.Korean.Selected, ttc.Math.Selected, ttc.Social.Selected, ttc.Language.Selected, ttc.Global1.Selected, ttc.Global2.Selected);
DataWriter<SubjectList> writeSubject = new(DataFile, list);
DataWriter<Settings> writeSettings = new(SettingsFile, Info.Settings);
DataWriter<Version> writeVersion = new(VersionFile, Info.Version);
DataWriter<int> writeClass = new(ClassFile, Info.User.Class);
await Task.WhenAll(writeKey, writeSubject.WriteAsync(), writeSettings.WriteAsync(), writeVersion.WriteAsync(), writeClass.WriteAsync());

// 설정 읽기
DataReader<Settings> readSettings = new(SettingsFile);
DataReader<SubjectList> readTuple = new(DataFile);
DataReader<Version> readVersion = new(VersionFile);
DataReader<int> readClass = new(ClassFile);

var settings = readSettings.ReadAsync();
var subject = readTuple.ReadAsync();
var version = readVersion.ReadAsync();
var cls = readClass.ReadAsync();

// Wait all until all files are read
await Task.WhenAll(settings, subject, version, cls);

기존에는 이런 식으로 저장했습니다. DataWriter<T>은 XmlWriter의 래퍼 클래스입니다. ㅋㅋㅋㅋㅋㅋㅋ참...지금 봐도 뭔가 정신 없긴 해요. 그래서, 새롭게 Setting을 다시 관리해보겠습니다.

// 설정 쓰기
ApplicationDataContainer localSettings = ApplicationData.Current.LocalSettings;
localSettings.Values[SettingValues.Subjects] = new SubjectList(ttc.Korean.Selected, ttc.Math.Selected, ttc.Social.Selected, ttc.Language.Selected, ttc.Global1.Selected, ttc.Global2.Selected);
localSettings.Values[SettingValues.Settings] = Info.Settings;
localSettings.Values[SettingValues.Version] = Info.Version;
localSettings.Values[SettingValues.Class] = Info.User.Class;
localSettings.Values[SettingValues.Level] = Info.User.ActivationLevel;

// 설정 읽기
ApplicationDataContainer localSettings = ApplicationData.Current.LocalSettings;
Version? version = Deserialize<Version?>(localSettings.Values[SettingValues.Version].ToString());
if (version is null)
{
    Info.User.Status = LoadStatus.NewlyInstalled;
    return;
}
(ttc.Korean.Selected, ttc.Math.Selected, ttc.Social.Selected, ttc.Language.Selected, ttc.Global1.Selected, ttc.Global2.Selected)
    = Deserialize<SubjectList>(localSettings.Values[SettingValues.Subjects].ToString()).Parse();
Info.Settings = Deserialize<Settings>(localSettings.Values[SettingValues.Settings].ToString());
Info.User.Class = (int)localSettings.Values[SettingValues.Class];
Info.User.ActivationLevel = Deserialize<ActivationLevel>(localSettings.Values[SettingValues.Level].ToString());
Info.User.Status = version != Info.Version ? LoadStatus.Updated : LoadStatus.Normal;

코드가 더 늘어난 것 같다면 기분탓입니다. 비교해보시면 아시겠지만, 사실 기존 코드가 훨씬 길었거든요. 지금은 기존 코드에 없던 부분까지 추가해서 그렇습니다. 대략 반 정도로 줄어든 것 같아요.

다만, 사용자 정의 클래스의 경우 DataContract를 지정해줘야 하고, 각각의 멤버에 DataMember 파라미터를 넣어줘야 한다는 번거로움이 생기긴 합니다. 그래도, 괜히 파일 읽고 쓰다가 생기는 오류를 만나는 것보단 이쪽이 훨씬 좋아보여요.

public string Serialize<T>(in T data)
{
    DataContractSerializer serializer = new(typeof(T));
    using MemoryStream stream = new();
    serializer.WriteObject(stream, data);
    stream.Position = 0;
    using StreamReader streamReader = new(stream);
    return streamReader.ReadToEnd();
}
public static T? Deserialize<T>(string xml)
{
    try
    {
        byte[] data = Encoding.UTF8.GetBytes(xml);
        using MemoryStream stream = new();
        stream.Write(data, 0, data.Length);
        stream.Position = 0;
        DataContractSerializer deserializer = new(typeof(T));

        T result = (T)Convert.ChangeType(deserializer.ReadObject(stream), typeof(T));
        return result;
    }
    catch
    {
        return default;
    }
}

참고로 저는 위와 같이 문자열로 직렬화/역직렬화를 해서 사용중입니다. 혹시 코드 복사를 원하신다면 위에 링크된 티스토리 블로그를 참고하시기 바랍니다.

정상적으로 뜨는군요. 좋습니다. 참고로 비동기 메서드처럼 실행하려면 각각 메서드의 내부를 await Task.Run(() => { 으로 감싼 뒤 async만 추가해주면 됩니다. 다만 큰 데이터가 아니면 오히려 코드만 길어질 것 같아서, 저는 전체 저장하는 부분을 따로 뽑아낸 뒤 거기에 async를 걸고 있어요.

여하튼, UWP는 여전히 손이 많이 가는군요.

반응형