Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (seongtaejeong at gmail.com)
홈페이지
첨부 파일
 

Directory.Build.props에 정의한 속성에 대해 Condition 제약으로 값을 변경하는 방법

지난 글을 통해,

(OneDrive, Dropbox 등의 공유 디렉터리에 있는) C# 프로젝트의 출력 경로 변경하기
; https://www.sysnet.pe.kr/2/0/13907

빌드 결과물을 프로젝트 경로의 하위가 아닌, 별도로 지정한 임시 디렉터리로 지정할 수 있었는데요, 아쉽게도 여기엔 문제가 좀 있습니다. ^^;

한 가지 사례로, Web Application 유형의 프로젝트라면 Visual Studio에서 F5 디버깅 시 기본적으로 ./[프로젝트]/bin 디렉터리를 사용하기 때문에 (별도 디렉터리에 생성된) dll 파일을 찾을 수 없어 오류가 발생합니다.

이 문제를 해결할 수 있는 한 가지 방법으로, Condition 속성을 고려할 수 있는데요,

MSBuild conditions
; https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild-conditions?view=vs-2022

그렇다면 이제 필요한 것은 Condition 식 내에 Web Application 프로젝트를 구분할 수 있는 적절한 속성을 찾아야 합니다. 음... 어떤 것이 좋을까요? ^^; 이를 위해 Web Application 프로젝트용의 csproj를 살펴보면,

  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProductVersion>
    </ProductVersion>
    <SchemaVersion>2.0</SchemaVersion>
    <ProjectGuid>{9F65F804-0A96-4FC4-9F32-6A8DC1A897D7}</ProjectGuid>
    <ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>WebApplication1</RootNamespace>
    <AssemblyName>WebApplication1</AssemblyName>
    <TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
    <MvcBuildViews>false</MvcBuildViews>
    <UseIISExpress>true</UseIISExpress>
    <Use64BitIISExpress />
    <IISExpressSSLPort />
    <IISExpressAnonymousAuthentication />
    <IISExpressWindowsAuthentication />
    <IISExpressUseClassicPipelineMode />
    <UseGlobalApplicationHostFile />
    <NuGetPackageImportStamp>
    </NuGetPackageImportStamp>
  </PropertyGroup>

ProjectTypeGuids, UseIISExpress 정도가 후보로 떠오릅니다. (아무거나 고른) ProjectTypeGuids의 경우 "349c5851-65df-11da-9384-00065b846f21" 값이 있다면 MVC 프로젝트 유형을 나타내므로 Directory.Build.props에 아래와 같이 올바른 문법으로 사용해 볼 수 있지만,

<Project>
    <PropertyGroup Condition="$(ProjectTypeGuids.IndexOf('349c5851-65df-11da-9384-00065b846f21')) == -1">
        <OutputPath>$(BaseOutputPath)\$(Platform)\$(Configuration)\</OutputPath>
        <BaseIntermediateOutputPath>$(BaseOutputPath)\temp</BaseIntermediateOutputPath>
        <IntermediateOutputPath>$(BaseIntermediateOutputPath)\$(Platform)\$(Configuration)\</IntermediateOutputPath>
        ...[생략]...
    </PropertyGroup>
</Project>

실제로 저렇게 해보면 의도한 대로 동작하지 않습니다. 그 이유는, Directory.Build.props의 평가가 너무 이른 시기에 완료되므로 ProjectTypeGuids의 값이 저 시점에는 비어 있기 때문입니다. (UseIISExpress를 사용해도 마찬가지입니다.)

아이러니하게도, Directory.Build.props 파일의 속성이 프로젝트에 대해 매우 이른 시기에 적용된다는 점은 장점이면서도 단점이 된 것입니다. 즉, OutputPath 등의 적용이 프로젝트 로딩 초기에 이뤄지므로 /bin, 또는 /obj 디렉터리가 생성되지 않을 수 있었던 건데요, 오히려 그런 이른 평가 시점으로 인해 Condition 조건에 여타 다른 속성을 사용할 수 없다는 제약으로 작용합니다.

아쉬운 대로, 이런 제약을 해결할 수 있는 방법이 Target을 경유해 속성 변경을 하는 것입니다. 가령, PrepareForBuild 이전에 실행하도록 Target을 정의하고, 그 안에서 CallTarget + Condition을 적용하는 식으로 우회할 수 있습니다.

<Target Name="RollbackProperties">
    <PropertyGroup>
        <BaseOutputPath>$(ProjectDir)bin\</BaseOutputPath>
        <OutputPath>$(ProjectDir)bin\</OutputPath>
        <BaseIntermediateOutputPath>$(ProjectDir)obj\$(Configuration)\</BaseIntermediateOutputPath>
        <IntermediateOutputPath>$(ProjectDir)obj\$(Configuration)\</IntermediateOutputPath>
    </PropertyGroup>
</Target>

<Target Name="CSharpUseTempDirectory" BeforeTargets="PrepareForBuild">
    <CallTarget Condition="'$(UseIISExpress)' == 'true'" Targets="RollbackProperties" />
</Target>

(물론, 시점의 변화로 인해 위와 같은 상황에서 의도했던 /obj, /bin 디렉터리 생성을 막을 수는 없습니다.)




참고로, Condition 등의 조건식에 문자열을 중복 사용할 때는 escape 처리를 해야 합니다. 가령, 다음과 같이 사용하게 되면,

Condition="'$(ProjectTypeGuids.IndexOf('349c5851-65df-11da-9384-00065b846f21'))' == '-1'"

2중으로 홑따옴표를 사용해 오류가 발생합니다.

error  : An unexpected token "..." was found at character position ... in condition 

이런 경우 내부에 중첩된 홑따옴표를 backtick(`)으로 escape 처리하면 됩니다.

Condition="'$(ProjectTypeGuids.IndexOf(`349c5851-65df-11da-9384-00065b846f21`))' == '-1'"




만약 특정 속성의 값이 궁금하다면 Message Task를 사용해 확인해 볼 수 있습니다. 가령 다음과 같이 속성을 정의했다면,

<Project>

    ...[생략]...

    <PropertyGroup>
        <TestProp>$(ProjectTypeGuids)</TestProp>
    </PropertyGroup>
</Project>

적절하게 Target을 정의한 후, Message Task를 추가하면 빌드 시에 Output 창을 통해 속성 값을 확인할 수 있습니다.

<Project>
    ...[생략]...

    <Target Name="mywork" AfterTargets="CoreBuild">
        <Message Importance="high" Text="[TEST] $(TestProp)"/>
    </Target>
</Project>




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







[최초 등록일: ]
[최종 수정일: 5/1/2025]

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

비밀번호

댓글 작성자
 




1  [2]  3  4  5  6  7  8  9  10  11  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
13893정성태2/27/20252184Linux: 115. eBPF (bpf2go) - ARRAY / HASH map 기본 사용법
13892정성태2/24/20252940닷넷: 2325. C# - PowerShell과 연동하는 방법파일 다운로드1
13891정성태2/23/20252473닷넷: 2324. C# - 프로세스의 성능 카운터용 인스턴스 이름을 구하는 방법파일 다운로드1
13890정성태2/21/20252288닷넷: 2323. C# - 프로세스 메모리 중 Private Working Set 크기를 구하는 방법(Win32 API)파일 다운로드1
13889정성태2/20/20253010닷넷: 2322. C# - 프로세스 메모리 중 Private Working Set 크기를 구하는 방법(성능 카운터, WMI) [1]파일 다운로드1
13888정성태2/17/20252466닷넷: 2321. Blazor에서 발생할 수 있는 async void 메서드의 부작용
13887정성태2/17/20253031닷넷: 2320. Blazor의 razor 페이지에서 code-behind 파일로 코드를 분리 및 DI 사용법
13886정성태2/15/20252559VS.NET IDE: 196. Visual Studio - Code-behind처럼 cs 파일을 그룹핑하는 방법
13885정성태2/14/20253211닷넷: 2319. ASP.NET Core Web API / Razor 페이지에서 발생할 수 있는 async void 메서드의 부작용
13884정성태2/13/20253479닷넷: 2318. C# - (async Task가 아닌) async void 사용 시의 부작용파일 다운로드1
13883정성태2/12/20253226닷넷: 2317. C# - Memory Mapped I/O를 이용한 PCI Configuration Space 정보 열람파일 다운로드1
13882정성태2/10/20252546스크립트: 70. 파이썬 - oracledb 패키지 연동 시 Thin / Thick 모드
13881정성태2/7/20252798닷넷: 2316. C# - Port I/O를 이용한 PCI Configuration Space 정보 열람파일 다운로드1
13880정성태2/5/20253140오류 유형: 947. sshd - Failed to start OpenSSH server daemon.
13879정성태2/5/20253354오류 유형: 946. Ubuntu - N: Updating from such a repository can't be done securely, and is therefore disabled by default.
13878정성태2/3/20253165오류 유형: 945. Windows - 최대 절전 모드 시 DRIVER_POWER_STATE_FAILURE 발생 (pacer.sys)
13877정성태1/25/20253220닷넷: 2315. C# - PCI 장치 열거 (레지스트리, SetupAPI)파일 다운로드1
13876정성태1/25/20253669닷넷: 2314. C# - ProcessStartInfo 타입의 Arguments와 ArgumentList파일 다운로드1
13875정성태1/24/20253122스크립트: 69. 파이썬 - multiprocessing 패키지의 spawn 모드로 동작하는 uvicorn의 workers
13874정성태1/24/20253519스크립트: 68. 파이썬 - multiprocessing Pool의 기본 프로세스 시작 모드(spawn, fork)
13873정성태1/23/20252952디버깅 기술: 217. WinDbg - PCI 장치 열거파일 다운로드1
13872정성태1/23/20252867오류 유형: 944. WinDbg - 원격 커널 디버깅이 연결은 되지만 Break (Ctrl + Break) 키를 눌러도 멈추지 않는 현상
13871정성태1/22/20253263Windows: 278. Windows - 윈도우를 다른 모니터 화면으로 이동시키는 단축키 (Window + Shift + 화살표)
13870정성태1/18/20253699개발 환경 구성: 741. WinDbg - 네트워크 커널 디버깅이 가능한 NIC 카드 지원 확대
13869정성태1/18/20253428개발 환경 구성: 740. WinDbg - _NT_SYMBOL_PATH 환경 변수에 설정한 경로로 심벌 파일을 다운로드하지 않는 경우
13868정성태1/17/20253081Windows: 277. Hyper-V - Windows 11 VM의 Enhanced Session 모드로 로그인을 할 수 없는 문제
1  [2]  3  4  5  6  7  8  9  10  11  12  13  14  15  ...