Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 
(연관된 글이 1개 있습니다.)
(시리즈 글이 9개 있습니다.)
.NET Framework: 90. XmlSerializer 생성자의 실행 속도를 올리는 방법
; https://www.sysnet.pe.kr/2/0/511

.NET Framework: 92. XmlSerializer 생성자의 실행 속도를 올리는 방법 - 두 번째 이야기
; https://www.sysnet.pe.kr/2/0/521

.NET Framework: 100. XML Serializer를 이용한 값 복사
; https://www.sysnet.pe.kr/2/0/577

.NET Framework: 122. XML Serializer를 이용한 값 복사: 성능은 어떨까!
; https://www.sysnet.pe.kr/2/0/653

.NET Framework: 648. Dictionary<TKey, TValue>를 deep copy하는 방법
; https://www.sysnet.pe.kr/2/0/11157

.NET Framework: 660. Shallow Copy와 Deep Copy
; https://www.sysnet.pe.kr/2/0/11220

.NET Framework: 1141. XmlSerializer와 Dictionary 타입
; https://www.sysnet.pe.kr/2/0/12942

.NET Framework: 2078. .NET Core/5+를 위한 SGen(Microsoft.XmlSerializer.Generator) 사용법
; https://www.sysnet.pe.kr/2/0/13196

.NET Framework: 2080. C# - Microsoft.XmlSerializer.Generator 처리 없이 XmlSerializer 생성자를 예외 없이 사용하고 싶다면?
; https://www.sysnet.pe.kr/2/0/13198





XmlSerializer 생성자의 실행 속도를 올리는 방법 - 두 번째 이야기


우선, 이 글을 읽으시기 전에 문맥의 이해를 돕기 위해 다음의 토픽을 먼저 읽어주십시오.

XmlSerializer 생성자의 실행 속도를 올리는 방법 
; https://www.sysnet.pe.kr/2/0/511

참고로, 오늘 이야기는 "MSBuild"와 함께 진행됩니다. 이 부분에서... 벌써 "아하... 어떤 내용인지 알겠다" 하실 분이 있을 것 같군요. ^^ 어쨌든, MSBuild에 대한 이해가 부족하신 분들은 다음의 토픽을 미리 읽어두시는 것도 좋겠습니다.

Inside MSBuild 
; https://learn.microsoft.com/en-us/archive/msdn-magazine/2006/june/msdn-magazine-june-2006




지난 토픽을 보신 분들은, sgen.exe로 생성된 "[파일명].XmlSerializers.dll"과 같은 형식을 어디선가 본 적이 있으실 것입니다. 어디였을까???

명확한 재현 예를 들어볼까요? ^^

우선, "Class Library" 형식의 프로젝트를 하나 만든 후 다음과 같은 정의를 포함하는 "Class1.cs" 파일을 추가합니다.

using System;
using System.Collections.Generic;
using System.Text;

namespace ClassLibrary1
{
  public class MyClass
  {
    public string Prop;
  }
}

다음, 위의 MyClass 타입을 반환하는 아래의 코드와 같은 웹 메서드를 구현한 웹 애플리케이션을 만듭니다.

[WebMethod]
public MyClass GetData()
{
  return new MyClass();
}

이제 간단한 독립실행형 응용 프로그램 프로젝트(Console 또는 WinForm)를 만들어서 위의 웹 서비스를 참조한 다음, "Debug" 모드가 아닌, "Release" 모드로 빌드를 합니다.

아래는 제가 테스트 한 "XmlSerializerTest" 프로젝트의 빌드 결과물이 있는 bin\release 폴더의 내용입니다.

XmlSerializerTest.exe
XmlSerializerTest.exe.config
XmlSerializerTest.pdb
XmlSerializerTest.vshost.exe
XmlSerializerTest.vshost.exe.config
XmlSerializerTest.XmlSerializers.dll

보시는 것처럼, "[파일명].XmlSerializers.dll"이 자동으로 생성된 것을 확인할 수 있습니다. 이 때문에, 웹 서비스를 참조한 프로젝트를 Release 모드로 빌드해서 배포하는 경우에는 "XmlSerializer" 내부에서 "System.IO.FileNotFoundException" 예외가 발생하지 않게 됩니다.

Visual Studio IDE에서는, 위의 Sgen.exe 결과물이 생성되지 않도록 하는 옵션도 제공해 줍니다. 아래와 같이 "Generate serialization assembly" 옵션을 "Off"로 주면 됩니다.

sgen_msbuild_task_use_1.png




특이한 점이 있다면, "[파일명].XmlSerializers.dll"의 자동 생성은 "웹 서비스 참조"에 한해서 발생할 뿐, 위의 예제에서 응용 프로그램이 "MyClass"가 정의된 DLL을 직접 참조해서 XmlSerializer를 이용하는 경우에는 자동 생성되지 않습니다. (이 이유에 대해서는 잠시 후에 답이 나옵니다.)

어쨌든, 이쯤 되면 답은 거의 나온 것이나 다름없습니다.
왜냐하면, VS.NET 2005 역시 MSBuild로 빌드를 하는 것이기 때문에 틀림없이 그 안에 sgen.exe가 동작되도록 하는 그 무엇인가 있을 것이기 때문입니다.

이를 위해, 자신의 빌드 옵션에서 불려지는 아래와 같은 "Microsoft.Common.targets" 파일들 중의 하나를 열어서 살펴보면 해답을 발견할 수 있습니다. (물론, 변경까지 하게 되면 모든 프로젝트의 빌드에 영향을 줄 수 있습니다.)

"C:\Windows\Microsoft.NET\Framework\v2.0.50727\Microsoft.Common.targets"
"C:\Windows\Microsoft.NET\Framework\v3.5\Microsoft.Common.targets"
"C:\Windows\Microsoft.NET\Framework64\v2.0.50727\Microsoft.Common.targets"
"C:\Windows\Microsoft.NET\Framework64\v3.5\Microsoft.Common.targets"

찾아낸 내용은 다음과 같습니다.

<SGen
    BuildAssemblyName="$(TargetFileName)"
    BuildAssemblyPath="$(IntermediateOutputPath)"
    References="@(ReferencePath)"
    ShouldGenerateSerializer="true"
    UseProxyTypes="true"
    KeyContainer="$(KeyContainerName)"
    KeyFile="$(KeyOriginatorFile)"
    DelaySign="$(DelaySign)"
    ToolPath="$(SGenToolPath)">

    <Output TaskParameter="SerializationAssembly" ItemName="SerializationAssembly"/>
</SGen>

보시면, "UseProxyTypes" 옵션이 "true"로 되어 있는 것을 확인할 수 있는데요. 바로 이 옵션 떄문에 "웹 서비스 참조"에 대해서만 "[파일명].XmlSerializers.dll" 파일들이 생성되는 것입니다. 간단하게는, 이 옵션을 "false"로 바꿔주면 모든 참조 DLL 들에 대해서 "SerializationAssembly"를 얻을 수 있습니다.




하지만 개인적으로는, 위와 같은 방법을 선호하지는 않습니다. 왜냐하면, 팀 개발에서 모든 구성원에게 "Microsoft.Common.targets" 파일을 변경하라고 하는 것은 그다지 바람직하지 않기 때문입니다. 하긴, 이런 부분은 "빌드 서버"에만 적용하면 된다는 현실적인 이유로 인해 묵인될 수는 있습니다.

하지만, 또 다른 이유로, 쓸데없는 타입들에 대해서도 sgen 작업이 이뤄진다는 점을 들 수 있습니다. 사실, 설계가 잘된 솔루션 구조라면 특정 라이브러리 프로젝트에 모든 데이터 엔티티 클래스들이 구현되어져 있을 것이므로 해당 프로젝트에만 적용하는 것이 더 바람직하다고 봅니다.

그렇다면, 프로젝트 파일을 수정해야 할 텐데요. 이에 대해서는 다음의 토픽을 간단하게 살펴 보십시오.

Do you know: editing VS project files made easy
; https://learn.microsoft.com/en-us/archive/blogs/nagarajp/do-you-know-editing-vs-project-files-made-easy

그럼, 우리가 원하는 데로 해볼까요? ^^
저라면, 다음과 같이 추가를 하겠습니다. ^^

<Target Name="AfterBuild">
  <SGen
   BuildAssemblyName="$(TargetFileName)"
   BuildAssemblyPath="$(OutputPath)"
   References="@(ReferencePath)"
   ShouldGenerateSerializer="true"
   KeyContainer="$(KeyContainerName)" KeyFile="$(KeyOriginatorFile)"
   DelaySign="$(DelaySign)"
   UseProxyTypes="false"
   ToolPath="$(SGenToolPath)">
    <Output TaskParameter="SerializationAssembly" ItemName="SerializationAssembly"/>
  </SGen>
</Target> 

이제 빌드를 해보면, 해당 DLL 에 대해서 "[파일명].XmlSerializers.dll"이 생성되는 것을 확인할 수 있습니다. 이 부분에서... "에이, 그 정도는 '빌드 후 이벤트' 기능으로도 얼마든지 구현이 가능한데!"라고 하시는 분이 계실지도 모르겠는데요. MSBuild의 장점은, 해당 DLL 프로젝트를 다른 프로젝트가 참조할 때 발생합니다. ^^

다른 프로젝트에 의해서 참조되는 경우, 해당 DLL만 복사되는 것이 아니라 자동적으로 "[파일명].XmlSerializers.dll"까지 복사되어 넘어갑니다. 우와~~~ ^^; (게다가 빌드 이벤트는 "TFS 팀 빌드" 시에는 효과가 없다는 점도 있지요.)

이제 배우셨으니, 여러분들의 클래스 라이브러리에 적용해 보십시오. 이전보다 "쪼끔 더" 빠른 초기 실행 속도를 경험하실 수 있습니다. (디버그 모드에서 실행시, System.IO.FileNotFoundException 예외가 발생하면서 동적 DLL들이 생성되는 경우가 있는지 확인해 보시면 됩니다. 하나도 발생하지 않도록 만드는 순간,,, 가벼운 희열을 느끼실 수 있습니다. ^^;)

마지막으로 아래는, 위의 내용과 비슷한 내용을 담고 있는 해외 블로거의 글입니다.

VS2005 - When SGEN Doesn't Work... 
; http://www.kiwidude.com/blog/2007/02/vs2005-when-sgen-doesnt-work.html





[이 토픽에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 12/15/2022]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 



2008-02-23 05시34분
Use of SGEN.exe to avoid common XmlSerializer performance pitfalls
; https://learn.microsoft.com/en-us/archive/blogs/billwert/use-of-sgen-exe-to-avoid-common-xmlserializer-performance-pitfalls

위의 기사를 읽어보고 한 가지 재미있는 사실을 알았습니다.
원래 XmlSerializer는 해당 타입에 대해서 런타임 시에 직렬화 코드를 만들어 낼 때, In-memory 상에서 코드를 생성해 내는데요. 이것을 %Temp% 파일에 생성하도록 바꿔주는 방법을 알려주고 있습니다. (직접 보세요. ^^)
kevin25
2008-06-18 09시47분
직렬화 코드를 담은 어셈블리를 특정 폴더로 지정하기 위한 방법을 아래의 토픽에서 설명하고 있습니다.

Serialization Temporary Assemblies
; https://learn.microsoft.com/en-us/archive/blogs/drnick/serialization-temporary-assemblies

system.xml.serialization/xmlserializer[@tempFilesLocation] 속성을 바꾸면 된다는.
kevin25
2009-11-04 10시54분
kevin25

... [16]  17  18  19  20  21  22  23  24  25  26  27  28  29  30  ...
NoWriterDateCnt.TitleFile(s)
13246정성태2/6/20234400개발 환경 구성: 664. Hyper-V에 설치한 리눅스 VM의 VHD 크기 늘리는 방법 - 두 번째 이야기
13245정성태2/6/20234965.NET Framework: 2093. C# - PEM 파일을 이용한 RSA 개인키/공개키 설정 방법파일 다운로드1
13244정성태2/5/20234360VS.NET IDE: 179. Visual Studio - External Tools에 Shell 내장 명령어 등록
13243정성태2/5/20235186디버깅 기술: 190. windbg - Win32 API 호출 시점에 BP 거는 방법 [1]
13242정성태2/4/20234640디버깅 기술: 189. ASP.NET Web Application (.NET Framework) 프로젝트의 숨겨진 예외 - System.UnauthorizedAccessException
13241정성태2/3/20234037디버깅 기술: 188. ASP.NET Web Application (.NET Framework) 프로젝트의 숨겨진 예외 - System.IO.FileNotFoundException
13240정성태2/1/20234190디버깅 기술: 187. ASP.NET Web Application (.NET Framework) 프로젝트의 숨겨진 예외 - System.Web.HttpException
13239정성태2/1/20233884디버깅 기술: 186. C# - CacheDependency의 숨겨진 예외 - System.Web.HttpException
13238정성태1/31/20236035.NET Framework: 2092. IIS 웹 사이트를 TLS 1.2 또는 TLS 1.3 프로토콜로만 운영하는 방법
13237정성태1/30/20235728.NET Framework: 2091. C# - 웹 사이트가 어떤 버전의 TLS/SSL을 지원하는지 확인하는 방법
13236정성태1/29/20235197개발 환경 구성: 663. openssl을 이용해 인트라넷 IIS 사이트의 SSL 인증서 생성
13235정성태1/29/20234857개발 환경 구성: 662. openssl - 윈도우 환경의 명령행에서 SAN 적용하는 방법
13234정성태1/28/20235951개발 환경 구성: 661. dnSpy를 이용해 소스 코드가 없는 .NET 어셈블리의 코드를 변경하는 방법 [1]
13233정성태1/28/20237325오류 유형: 840. C# - WebClient로 https 호출 시 "The request was aborted: Could not create SSL/TLS secure channel" 예외 발생
13232정성태1/27/20234979스크립트: 43. uwsgi의 --processes와 --threads 옵션
13231정성태1/27/20234018오류 유형: 839. python - TypeError: '...' object is not callable
13230정성태1/26/20234401개발 환경 구성: 660. WSL 2 내부로부터 호스트 측의 네트워크로 UDP 데이터가 1개의 패킷으로만 제한되는 문제
13229정성태1/25/20235414.NET Framework: 2090. C# - UDP Datagram의 최대 크기
13228정성태1/24/20235542.NET Framework: 2089. C# - WMI 논리 디스크가 속한 물리 디스크의 정보를 얻는 방법 [2]파일 다운로드1
13227정성태1/23/20235184개발 환경 구성: 659. Windows - IP MTU 값을 바꿀 수 있을까요? [1]
13226정성태1/23/20234869.NET Framework: 2088. .NET 5부터 지원하는 GetRawSocketOption 사용 시 주의할 점
13225정성태1/21/20234065개발 환경 구성: 658. Windows에서 실행 중인 소켓 서버를 다른 PC 또는 WSL에서 접속할 수 없는 경우
13224정성태1/21/20234517Windows: 221. Windows - Private/Public/Domain이 아닌 네트워크 어댑터 단위로 방화벽을 on/off하는 방법
13223정성태1/20/20234652오류 유형: 838. RDP 연결 오류 - The two computers couldn't connect in the amount of time allotted
13222정성태1/20/20234341개발 환경 구성: 657. WSL - DockerDesktop.vhdx 파일 위치를 옮기는 방법
13221정성태1/19/20234537Linux: 57. C# - 리눅스 프로세스 메모리 정보파일 다운로드1
... [16]  17  18  19  20  21  22  23  24  25  26  27  28  29  30  ...