.NET Framework에서는 NuGet으로 받아야만 쓸 수 있는 3rd party 프레임워크에 불과한 Newtonsoft.Json이 워낙 이런저런 패키지들에 깊숙히 침투해서 중구난방으로 사용되고 있었기 때문에 .NET Core로 마이그레이션할 때 System.Text.Json으로 바꾸고 Newtonsoft의 잔재를 걷어내는 일은 참으로 지난한 일이 되었다...
즐겨 사용하는 패키지인 PushSharp을 역시 .NET Core에서도 사용하고 있는데, 내부적으로 Newtonsoft를 쓰고 있어서 하는 수 없이 최신 소스코드를 GitHub에서 내려 받은 다음 일일이 System.Text.Json으로 수정해서 써야 했고...
PushSharp에서 아직까지 제대로 지원하지 않는 APNS P8 토큰 기반 HTTP/2 통신 관련 기능을 직접 구현할 때 필요한 패키지인 Jose.JWT도 역시 내부적으로 Newtonsoft를 쓰고 있어서 마찬가지로 소스코드 째로 받아 수정해야 했으며...
기타 등등.
수정하고 테스트하면서 알게 된 것들 몇 가지를 적어 본다.
1. ASP.NET Core 프로젝트의 Startup.cs에서 전역 설정한 JsonOptions는... JsonSerializer를 직접 사용하는 경우에는 적용되지 않는다. 전역 설정이라서 당연히 여기저기 다 적용되겠거니 싶었지만, 아니었다. 해당 전역 설정은 Controller로 요청하거나 응답하는 경우에만 적용되는 설정이다! (Controller 응답을 클래스 객체로 리턴할 때, 그리고 요청을 Json 객체로 받을 때 내부적으로 자동 Serialize/Deserialize되는데 이 때에만 적용된다.)
services.AddControllersWithViews()
.AddJsonOptions(options =>
{
// Json 날짜 변환 형식 추가
options.JsonSerializerOptions.Converters.Add(new DateTimeConverterEx());
options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
options.JsonSerializerOptions.Converters.Add(new ObjectToInferredTypesConverter());
// Json 속성이 소문자로 강제 변환되는 것을 방지(클래스 정의대로)
options.JsonSerializerOptions.PropertyNamingPolicy = null;
// null Property는 생략함
options.JsonSerializerOptions.IgnoreNullValues = true;
// 한글이 인코딩(깨짐)되지 않게
options.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All);
});
보통 이런 식으로 전역 설정을 해놓고 쓰는데...
MyClass cls = ...;
String objString = JsonSerializer.Serialize(cls);
MyClass cls2 = JsonSerializer.Deserialize<MyClass>(objString);
이렇게 코드에서 직접 JsonSerializer를 사용할 때는 위 JsonOptions 전역 설정이 적용되지 않는다. 왜냐하면, JsonSerializer.Serialize()와 JsonSerializer.Deserialize() 함수를 잘 들여다 보면 알 수 있지만 두 번째 인수인 JsonSerializerOptions 값이 생략되면 그 기본값은 null이 된다! ㄷㄷㄷ 프레임워크 기본값으로 초기화되는 것.
즉, Startup.cs의 전역 설정 외에 따로 기본값을 정의해서 매번 JsonSerializer 코드를 쓸 때마다 적용하지 않으면 프레임워크 초기값이 적용되는 것이다. 중복이지만... 매번 코드를 작성해서 적용해야 한다. 어쩔 수 없다.
나는 고민 끝에 가장 간단한 방법으로 JsonSerializerOptions에서 끝에 s만 뺀 정적static 클래스를 만들고 정적static 읽기 전용 속성Property인 Default를 하나 정의해서 사용하는 방법을 썼다. (JsonSerializerOption이 .NET Core 내장 클래스인 것처럼 속이기? ㅎㅎ)
public static class JsonSerializerOption
{
public static JsonSerializerOptions Default
{
get
{
return new JsonSerializerOptions
{
// Json 속성이 소문자로 강제 변환되는 것을 방지(클래스 정의대로)
PropertyNamingPolicy = null,
// null Property는 생략함
IgnoreNullValues = true,
// 한글이 인코딩(깨짐)되지 않게
Encoder = JavaScriptEncoder.Create(UnicodeRanges.All)
};
}
}
}
JsonSerializerOptions 객체는 Singleton으로 쓸 수 없고 Serialize/Deserialize 할 때마다 매번 new 해서 새로 만들어야 한다. 이 또한 어쩔 수 없다...
전역 설정에서의 중복을 피하기 위해 Startup.cs 코드는 아래와 같이 바뀌었다.
services.AddControllersWithViews()
.AddJsonOptions(options =>
{
// Json 날짜 변환 형식 추가
options.JsonSerializerOptions.Converters.Add(new DateTimeConverterEx());
options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
options.JsonSerializerOptions.Converters.Add(new ObjectToInferredTypesConverter());
// JsonSerializerOptions 기본값 사용자 정의
var serializerOptions = JsonSerializerOption.Default;
options.JsonSerializerOptions.PropertyNamingPolicy = serializerOptions.PropertyNamingPolicy;
options.JsonSerializerOptions.IgnoreNullValues = serializerOptions.IgnoreNullValues;
options.JsonSerializerOptions.Encoder = serializerOptions.Encoder;
});
2. Newtonsoft와 달리 System.Text.Json으로 Serialize/Deserialize할 때는 클래스 멤버 변수(Field)가 아닌 속성(Property)을 사용해야 한다. 즉, get; set;이 없는 클래스 멤버 변수(Field)는 비록 public이라 하더라도 지원되지 않는다.
public class MyClass
{
public string id;
public string name;
}
이렇게 변수(Field)로 되어 있으면 전혀 Serialize/Deserialize되지 않는다. 그냥 텅 빈 값만 덩그러니...
public class MyClass
{
public string id { get; set; }
public string name { get; set; }
}
이렇게 속성(Property)으로 수정해서 써야 한다. 처음엔 이 내용을 몰라서... 왜 정상적으로 동작하지 않는지 디버깅하느라 눈 빠질 뻔 했다. 알고보니 Microsoft 공식 문서에 버젓이 명시되어 있는 내용이었다. 진작 문서를 들여다 볼 걸...
위 두 부분에서 모두 클래스 멤버 변수(Field)가 지원되지 않는다고 명시되어 있었던 것! ㅆㅂ
(그 밖에도 아주 유용한 정보가 많이 들어 있다. 위 링크 페이지는 책갈피에 꽂아두고 필독!)
끝.
'Tech: > Server·IIS' 카테고리의 다른 글
[docker] MariaDB 10.4 my.cnf 인식 오류 (0) | 2022.08.03 |
---|---|
ARR: URL 재작성(rewrite) 팁 (0) | 2020.12.15 |
Docker 컨테이너 실행순서 조정하기 (0) | 2020.08.12 |
Docker 컨테이너 시간을 UTC+09:00로 설정하기 (0) | 2020.08.11 |
Windows Server Core 유용한 팁 (0) | 2020.07.09 |