class Student
{
// 다른 속성들 생략
public int? UserId { get; private set; }
}
Id 중에서도 UserId 는 보안 사고날까봐 함부로 노출시키지는 못하겠고, 그렇다고, 이 속성 하나 때문에 Dto 를 줄줄이 만들기는 너무 귀찮습니다.
public record StudentDto(...) // UserId 만 뺀 다른 속성들 잔뜩
또한 C# 단일 언어 시스템에서, 로직이 프론트엔드(데스크탑 앱 포함)에 있는 경우, 백엔드로부터 Dto 를 받아서, 다시 도메인 객체로 변환하는 코드를 일일이 적자니, 쿨해 보이지 않습니다.
static class DtoConversions
{
public static Student? ToStudent(this StudentDto dto) => // ...
public static StudentDto? ToDto(this Student obj) => // ...
public static Teacher? ToTeacher(this TeacherDto dto) => // ...
public static TeacherDto? ToDto(this Teacher obj) => // ...
public static Staff? ToStaff(this StaffDto dto) => // ...
public static StaffDto? ToDto(this Staff obj) => // ...
}
요럴 때 쓸 수 있는 꼼수입니다.
public class UserIdConverter : JsonConverter<int?>
{
// 역직렬화 시에는 그냥 읽지만
public override int? Read(ref Utf8JsonReader r, Type t, JsonSerializerOptions o)
=> r.TryGetInt32(out var id) ? id : default(int?);
// 직렬화 시에는 null => null, n => 0
public override void Write(Utf8JsonWriter w, int? v, JsonSerializerOptions o)
{
if (v.HasValue)
w.WriteNumberValue(0);
else
w.WriteNullValue();
}
}
그리고, 이 컨버터를 UserId 에 적용시킵니다.
public class Student
{
// ...
[JsonInclude, JsonConverter(typeof(UserIdConverter))]
public int? UserId { get; private set; }
}
public class Teacher
{
// ...
[JsonInclude, JsonConverter(typeof(UserIdConverter))]
public int? UserId { get; private set; }
}
public class Staff
{
// ...
[JsonInclude, JsonConverter(typeof(UserIdConverter))]
public int? UserId { get; private set; }
}
// ...
수많은 Dto 와 수 많은 컨버젼 코드를 이 클래스 하나로 퉁칠 수 있게 됐습니다. ^^
사용 코드
var path = // ...
var students = await httpClient.GetFromJsonAsync<Student[]>(path, token);
var userIdsHidden = students.All(s => s.UserId == null || s.UserId == 0);
Console.WriteLine(userIdsHidden); // true;