실전! 지속적인 통합 10편: MSBuild 따라하기

  • Post author:
  • Post category:칼럼
  • Post comments:2 Comments
  • Post last modified:October 21, 2012

이 글은 월간 마이크로소프트웨어(일명 마소) 2008년 10월호에 기고한 글입니다. 물론 구성이나 내용 상의 차이가 있을 수 있습니다.

비주얼 스튜디오 같은 IDE가 주를 이루는 개발 환경에서 빌드 스크립트는 찬밥 신세를 면치 못한 적도 있다. 그러나 빌드 스크립트는 IDE가 해주지 못하는 것. 빌드 자동화나 세밀한 빌드 구성을 지원한다. “왜 필요한가?” 싶다가도 막상 써보면 “이것 없이는 못 살아!”하게 된다.

최재훈 | SK 아이미디어의 게임 서버 팀에서 일한다. 요즘은 스크립트 엔진을 개발하는 데 전념하며, 새로운 도전을 즐긴다. 직업 외적인 측면에선 배철수의 음악 캠프를 15년째 즐겨 듣고, U2가 최고의 밴드라 생각한다.

예제 소스 코드

이번 시간엔 제목 그대로 실제 사례를 놓고 따라해보면서 MSBuild의 사용법을 익혀볼 생각이다. 그러나 MSBuild를 본격적으로 다루는 첫 칼럼인 만큼 무리하게 복잡한 사례를 다룰 생각은 없다. MSBuild가 본래 목표로 삼은 프로그래밍 언어인 C#의 경우만 우선 다뤄보려 한다. csproj, 즉 C# 프로젝트 파일 자체가 MSBuild 스크립트에 불과하다는 사실만 봐도 MSBuild와 C# 프로젝트의 궁합은 정말 잘 맞는다. 그런 만큼 최소한 이번 칼럼 중에서 “너무 어려워서 못 따라하겠어!” 같은 내용은 없을 것이다. 약속한다.

가능하면 실제 코드를 가지고 실습하면 좋겠단 생각이 든다. 원고 쓰느라 억지로 예제를 만들다 보면 현실 세계와 동떨어진 완성물이 나오는 탓이다. 우리가 프로그래밍을 할 땐 책에는 없는 문제와 부딪히기 마련인데, 원고를 위한 예제는 그런 면, 그러니까 예측 불가능함이 부족하다. 그래서 C#으로 개발한 오픈 소스 라이브러리를 하나 골라 실습해보기로 했다.

Program#(http://aimlbot.sourceforge.net/)은 AIML(Artificial Intelligence Markup Language) 엔진 라이브러리다. AIML은 말 그대로 인공지능을 XML로 구현할 수 있게 만든 언어인데 채팅봇을 만들 때 쓴다(AIML이나 채팅봇의 기본 개념을 알고 싶다면 “디지털 생명체 연구: 채팅로봇(Chatterbot) 기술”, http://kidbs.itfind.or.kr/WZIN/jugidong/1115/111501.htm를 읽어보길 바란다). 최근에 Program#을 한국어 환경에 맞춰 고치는 일을 짬짬이 하는 탓에 이 라이브러리를 자동화할 겸 이번 칼럼의 예제로 쓰기로 한다.

예제에서 쓸 Program#은 Release Version 2.5(http://aimlbot.svn.sourceforge.net/viewvc/aimlbot/tags/2.5/)다. 한데 원래 소스 코드는 엔진 라이브러리, 윈폼 구현 예제, 웹 서비스 구현 예제, 테스트 코드 등이 각자 별도의 솔루션으로 구성되었기 때문에 빌드 자동화하기 불편하다. 그래서 원래 소스 코드를 AIMLProjects.vs2008.sln 라는 하나의 솔루션으로 묶었다. 이 소스 코드는 imaso-200810.zip에서 다운로드 받으면 된다.

여기서 잠깐! 솔루션 파일 이름이 뭐라고? AIMLProjects.vs2008.sln? 비주얼 스튜디오 2008? 이 칼럼은 비주얼 스튜디오 2005를 다룬다 하지 않았던가? 맞다. 실은 지난 몇 달 동안 비주얼 스튜디오 2005로 개발된 프로젝트를 비주얼 스튜디오 2008로 이전하는 작업을 해왔다. 그 기간 내내 똑같은 소스 코드를 비주얼 스튜디오 2005와 비주얼 스튜디오 2008 양쪽에서 돌려보며 컴파일은 되는지, 테스트는 잘 통과하는지 쭉 지켜봤다. 이러한 점진적인 이전 작업은 처음 하는 탓에 까다로운 문제가 많았는데 가능하면 이런 경험도 칼럼을 통해 전달하고 싶다. 물론 이러한 이전 작업을 이해하려면 사전 지식이 더 필요하다. 그래서 이번 칼럼에선 비주얼 스튜디오 2008을 기준으로 삼고자 한다.

빌드 스크립트의 기본적인 모양새

다운로드 받은 소스 코드를 “D:\workspace\imaso-2008-10”에 놓았다고 가정하자. 이 폴더를 열면 두 개의 파일과 6개의 폴더가 보인다. 이 칼럼에선 파일이 중요한데 하나는 앞서 설명한 AIMLProjects.vs2008.sln이고 다른 하나는 빌드 스크립트인 msbuild.xml이다. 물론 이 빌드 스크립트는 완성판이다. 말인즉, 복잡하다. 처음 MSBuild를 접하는 사람에겐 복잡하게 느껴질지 모른다. 그러니 이 칼럼에선 완성된 빌드 스크립트를 분해해 하나씩 다시 조립해나가자. 우선 [목록 1]을 보자.

[목록 1] 빌드 스크립트
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

<Target Name="Clean">
	<Message Text="타겟: Clean" />
</Target>

<Target Name="Build">
	<Message Text="타겟: Build" />
</Target>

<Target Name="Rebuild" DependsOnTargets="Clean; Build">
	<Message Text="타겟: Rebuild" />
</Target>

</Project>
		

보통 이렇게 코드를 짜는 것부터 한다. 어떤 빌드 스크립트를 짜든 이걸 뼈대로 삼고 살점을 붙이는 방식으로 일하면 된다. 이 스크립트엔 타겟(Target)이 세 개 있다. 하나는 Clean. 글자 그대로 정리하는 역할을 하게 된다. 빌드 산출물을 삭제하고 소스 코드를 처음 상태로 돌려놓는 역할을 하게 될 텐데 보통 bin, obj 폴더를 지우는 일이다. Build는 앞으로 소스 코드를 빌드하는 일을 하게 될 테고, Rebuild는 “다시 빌드”를 맡는다. 그리고 Rebuild는 보통 Clean과 Build를 순서대로 실행하는 것에 불과하다.

타겟?

MSDN에선 Target을 “작업”이라 부르기도 한다. MSBuild의 빌드 스크립트를 구성하는 XML 구성요소는 크게 Item, Property, Target이 있다. 그런데 그 이름에서 짐작하듯 Item과 Property는 어떤 “값”을 나타내는 반면, Target만이 유일하게 “행동”을 정의한다. MSDN 라이브러리에서 “MSBuild – 작업 참조” 항목을 보면, Copy, Delete, Exec, ReadLinesFromFile, VCBuild 같은 타겟이 있는데 글자 그대로 파일이나 디렉터리를 복사하고 지우거나 어떤 프로그램을 실행시키는 등의 일을 한다. 타겟은 약 30개 정도 되는데 프로그래밍을 통해 확장 가능하다. 몇 차례 소개한 MSBuildCommunityTask도 이러한 확장 기능을 활용한 사례라 하겠다.

MSBuild 바이너리 래퍼

첫 번째 빌드 스크립트를 만들었으니 실행시켜볼 차례다. 그런데 msbuild.exe의 경로가 참 사람 피곤하게 만든다. “C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\msbuild.exe”, 이런 경로를 어느 세월에 치고 있겠나! 다른 건 몰라도 버전 번호는 참 고약하다.

그래서 우리는 프로젝트 폴더, 즉 “D:\workspace\imaso-2008-10”에 MsBuild_WIN32.bat란 배치 파일을 만들 생각이다. 실은 이 배치 파일은 지난 시간에 소개했는데, 이번에는 비주얼 스튜디오 2005를 쓰는 경우와 2008을 쓰는 경우를 비교해보자.

[목록 2] [VS2005 & x86]를 위한 MSBuild_Win32.bat
@echo off
SETLOCAL
call "%VS80COMNTOOLS%\..\..\VC\vcvarsall.bat" x86
"C:\WINDOWS\Microsoft.NET\Framework64\v3.5\msbuild.exe" %*
ENDLOCAL
SET ERR_LEVEL=%errorlevel%
exit /b %ERR_LEVEL%


		
[목록 3] [VS2008 & x86]를 위한 MSBuild_Win32.bat
@echo off
SETLOCAL
call "%VS90COMNTOOLS%\..\..\VC\vcvarsall.bat" x86
"C:\WINDOWS\Microsoft.NET\Framework64\v3.5\msbuild.exe" %*
ENDLOCAL
SET ERR_LEVEL=%errorlevel%
exit /b %ERR_LEVEL%


		

두 배치 파일의 차이점은 세 번째 줄과 네 번째 줄에 있다. 우선 세 번째 줄에선 비주얼 스튜디오 공통도구의 경로가 쓰이는데 당연히 버전에 따라 경로도 달라진다(%VS80COMNTOOLS% 와 %VS90COMNTOOLS%).

네 번째 줄에선 MSBuild를 호출한다. 두말하면 잔소리지만, 비주얼 스튜디오 2005는 닷넷 프레임워크 2.0을 위해, 그리고 비주얼 스튜디오 2008은 닷넷 프레임워크 2.0부터 3.5 사이를 위해 개발됐다. 그러니 MSBuild 바이너리의 버전도 그에 맞춰야 한다.

그러고 보니 이 배치 파일이 하는 일에 대해 자세히 설명한 적이 없다. 배치 파일을 쓸 일이 많지 않다 보니 이쯤에서 간략하게 알아두자. 우선 두 번째 줄에선 기본 경로 값을 저장한다. 명령창에서 “set” 명령을 실행시키면 PATH 값이 나오는데, 세 번째 줄에 있는 vcvarsall.bat 배치 파일이 이 값을 변경한다. 그러니 변경되기 전의 PATH 값을 미리 저장해뒀다가 모든 일이 다 끝나고 나서 복구하는데 쓸 생각이다. Vcvarsall.bat 는 비주얼 스튜디오에 필요한 환경설정을 구성한다.

네 번째 줄에서 MSBuild를 실행시키는데 주목할 부분은 “%*”다. 이 기호는 배치 파일에 넘긴 모든 인자를 뜻한다. 예를 들어 이런 명령어를 쳤다고 해보자.

Msbuild_win32.bat msbuild.xml /t:build

그러면 네 번째 줄은 다음과 같이 해석된다.

"C:\WINDOWS\Microsoft.NET\Framework\v3.5\msbuild.exe"  msbuild.xml /t:build

다섯 번째 줄에서 우리는 msbuild의 실행결과 값을 저장해놨다가 마지막 줄에서 반환한다. 그리고 여섯 번째 줄에서 세 번째 줄에서 바뀐 경로를 원래대로 돌려놓는다. 이로써 MSBuild의 래퍼 배치파일이 완성됐다.

빌드 스크립트 실행해보기

우선 Clean부터 실행해보자. 지면상의 이유로 불필요한 출력 메시지는 삭제했으니 실제 실행결과는 좀더 복잡할 것이다.

D:\workspace\imaso-2008-10> Msbuild_win32.bat msbuild.xml /t:Clean
Build started 2008-09-17 오전 9:53:48.
Project "D:\workspace\imaso-2008-10\msbuild.xml" on node 0 (Clean target(s)).
  타겟: Clean
Done Building Project "D:\workspace\imaso-2008-10\msbuild.xml" (Clean target(s)).

Build succeeded.
    0 Warning(s)
    0 Error(s)

예상대로 ‘타겟: Clean’만 찍힌다. 명령창에 타겟 이름만 출력할 뿐 아직은 어떤 일도 하지 않는다. 이제 Build 타켓을 실행시켜보자.

D:\workspace\imaso-2008-10> Msbuild_win32.bat msbuild.xml /t:Build
Build started 2008-09-17 오전 9:53:52.
Project "D:\workspace\imaso-2008-10\msbuild.xml" on node 0 (Build target(s)).
  타겟: Build
Done Building Project "D:\workspace\imaso-2008-10\msbuild.xml" (Build target(s)).

Build succeeded.
    0 Warning(s)
    0 Error(s)

DependsOnTargets 애트리뷰트에 명시한 대로 Clean과 Build 타겟이 순서대로 실행됐다. DependsOnTarget(그리고 설명 안 한 CallTarget)은 코드 중복을 줄이는데 도움이 된다. 가능하면 DependsOnTarget을 써먹자.

여기까진 예상했던 대로다. 그런데 좀 색다르게 타겟 이름을 명시하지 않으면 어떻게 될까?

D:\workspace\imaso-2008-10> Msbuild_win32.bat msbuild.xml
Build started 2008-09-17 오전 9:53:48.
Project "D:\workspace\imaso-2008-10\msbuild.xml" on node 0 (Clean target(s)).
  타겟: Clean
Done Building Project "D:\workspace\imaso-2008-10\msbuild.xml" (Clean target(s)).

MSBuild 바이너리를 실행할 때 명령창에서 타겟 이름을 주지 않으면 처음 정의한 타겟을 실행한다. 이 경우엔 Clean 타겟을 실행했다. 만약 Build 타겟이 Clean 타겟보다 앞에 있었다면 Build 타겟이 실행됐을 것이다.

눈치 빠른 이는 벌써 알아차렸겠지만 당연히 기본 타겟을 바꾸는 방법도 있다.

[목록 4] DefaultTargets
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
중략…
</Project>
		

Project 엘레멘트의 DefaultTargets 애트리뷰트를 활용하면 된다. 이 경우엔 "Build"를 기본 타겟으로 삼았다. 이렇게 빌드 스크립트를 바꾸고 실행해보면 다음과 같은 결과를 얻는다.

D:\workspace\imaso-2008-10> Msbuild_win32.bat msbuild.xml
Build started 2008-09-17 오전 9:54:07.
Project "D:\workspace\imaso-2008-10\msbuild.xml" on node 0 (default targets).
  타겟: Build
Done Building Project "D:\workspace\imaso-2008-10\msbuild.xml" (default targets).

DefaultTargets 라는 이름에 주의하자. DefaultTarget이 아닌 DefaultTargets다. 말인즉, 여러 개의 타겟을 한번에 지정해도 된다는 뜻이다. 예를 들어, DefaultTargets="Clean; Build"라 지정하면 이런 결과가 나온다.

D:\workspace\imaso-2008-10> Msbuild_win32.bat msbuild.xml
Build started 2008-09-17 오전 10:21:30.
Project "D:\workspace\imaso-2008-10\msbuild.xml" on node 0 (default targets).
  타겟: Build
Clean:
  타겟: Clean
Done Building Project "D:\workspace\imaso-2008-10\msbuild.xml" (default targets).

여기서 좀더 나아가볼까? 아예 파일 이름도 주지 않는 것이다.

D:\workspace\cuckoo-trunk\server\src\Aiml> Msbuild_win32.bat
Build started 2008-09-18 오전 10:04:14.
Project "D:\workspace\cuckoo-trunk\server\src\Aiml\AIMLProjects.vs2008.sln"
on node 0 (default targets).
  Building solution configuration "Debug|Any CPU".

뭔가 달라졌다. "D:\workspace\imaso-2008-10\msbuild.xml"가 아닌 "D:\workspace\cuckoo-trunk\server\src\Aiml\AIMLProjects.vs2008.sln"을 실행시켰다. 만약 "D:\workspace\cuckoo-trunk\server\src\Aiml\AIMLProjects.vs2008.sln"을 지우거나 확장자를 “sln.txt”로 바꾼다면 어떨까? 그러면 “Msbuild_win32.bat msbuild.xml /t:Clean”과 같은 결과를 얻는다.

정리하면 이렇다. 만약 빌드 스크립트를 명시하지 않으면 해당 폴더에 있는 솔루션 파일부터 찾는다. 솔루션 파일이 없으면 "어라, 파일 이름은 어디 갔지? 에라 모르겠다. 아마 msbuild.xml이겠지. 아니면 말고." 이렇게 생각한다. 그러니 주 빌드 스크립트 파일의 이름은 msbuild.xml로 짓는 편이 좋다. 여기서 주 빌드 스크립트라 함은 부 빌드 스크립트도 있다는 뜻인데, 훗날 MSBuildCommunityTasks를 다룰 때 알아볼 것이다.

진짜 빌드해보기

이제 진짜 빌드를 해보자. 우선 Build 타겟부터 구현해보는 게 어떨까?

[목록 5] Build 타겟
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">Any CPU</Platform>
	<BuildCondition>$(Configuration)|$(Platform)</BuildCondition>
</PropertyGroup>

<PropertyGroup Condition=" '$(BuildCondition)' == 'Debug|AnyCPU' ">
</PropertyGroup>

<PropertyGroup Condition=" '$(BuildCondition)' == 'Release|AnyCPU' ">
</PropertyGroup>

<ItemGroup>
	<ProjectReferences Include="AIMLProjects.vs2008.sln">
		<Configuration>$(Configuration)</Configuration>
		<Platform>$(Platform)</Platform>
	</ProjectReferences>
</ItemGroup>

<Target Name="Clean">
	<Message Text="타겟: Clean" />
</Target>

<Target Name="Build">
	<Message Text="타겟: Build" />
	<Message Text="빌드 조건: '$(BuildCondition)'" Importance="high" />

	<MSBuild Projects="@(ProjectReferences)"
Properties="Configuration=%(ProjectReferences.Configuration);
Platform=%(ProjectReferences.Platform)"
StopOnFirstFailure="true" />
</Target>

<Target Name="Rebuild" DependsOnTargets="Clean; Build">
	<Message Text="타겟: Rebuild" />
</Target>
</Project>

		

MSBuild엔 크게 세 가지 구성요소가 있다 했다. 이 빌드 스크립트에서 그 구성요소를 모두 확인할 수 있는데, Item, Property, Target이다. Target은 이미 다뤘고, 우선 Property에 대해 알아보자. Property는 PropertyGroup 안에 정의하는데 PropertyGroup 자체는 Property를 묶는 역할만 할 뿐이다. Property는 프로그래밍 언어의 변수 정도를 떠올리면 이해하기 쉽다. Configuration이란 변수와 Platform이란 변수를 만들어 빌드 설정값을 저장하는 용도로 쓴다. Property 값은 명령어 인자로 넘길 수 있는데 이런 식이다.

Msbuild msbuild.xml /p:Configuration=Release

이렇게 빌드 스크립트를 돌리면 Configuration 프로퍼티의 값은 Release가 되고, Platform 프로퍼티의 값은 Any CPU가 된다.

Item은 일종의 배열이다. 특히, 파일 이름과 기타 정보를 저장하는 용도로 쓰는 배열이다. ProjectReferences란 배열이 보이는데, 이 배열엔 AIMLProjects.vs2008.sln 이란 파일만 들어있다. 그리고 이 요소는 Configuration과 Platform이란 두 개의 멤버 변수를 가진다. 사실, 이 경우엔 멤버 변수를 따로 정의할 필요 없이 Configuration, Platform 프로퍼티를 쓰면 된다. 하지만 프로젝트 구성이 복잡한 경우, 예를 들어 C++ 프로젝트의 플랫폼은 WIN32이고 C# 프로젝트의 플랫폼은 x86이 되어야 하는 경우라면 이렇게 Item마다 별도의 설정값을 가져야 한다.

이제 빌드 스크립트를 돌려보고 빌드가 잘 되는지 확인해보자.

Msbuild_win32.bat msbuild.xml /t:Build

Clean하기

[목록 6] Clean 타겟
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

중략…

<Target Name="Clean">
	<Message Text="타겟: Clean" />

	<MSBuild Projects="@(ProjectReferences)"
Targets="Clean"
Properties="
Configuration=%(ProjectReferences.Configuration);
Platform=%(ProjectReferences.Platform)"
StopOnFirstFailure="true" />
</Target>
</Project>
		

알다시피 비주얼 스튜디오 프로젝트나 솔루션에 이미 Clean 구성(Configuration)이 있다. 그러니 Clean 타겟에서 이를 호출하면 될 것 같다. 일단 정말 그런지 확인해볼까?

msbuild msbuild.xml /t:Clean

정말 깔끔하게 정리됐을까? 물론 명령창에 뜨는 빌드 정보는 성공했다(Build succeeded.)고 하지만 직접 확인해봐야 안심하겠다.

그림 1. Clean이 잘 안 됐다.

.\AIMLGUI\AIMLGUI\bin\debug 폴더(그림 1)에 가 보니 여전히 일부 파일이 남아 있다. CustomTags는 빌드 이벤트를 활용해 만든 폴더라 자동으로 정리(Clean)가 안 됐고 그 외에도 비주얼 스튜디오가 디버깅할 때 자동으로 만든 파일(*.vshost.*)도 있다. 역시 가장 좋은 방법은 bin 폴더를 통째로 지우는 것이다.

[목록 7] Clean 타겟 – 두 번째 시도
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

중략 …

<!-- Clean 할 대상 디렉터리 -->
<ItemGroup>
	<BinDirs Include=".\AIMLbot\AIMLbot\bin" />
	<BinDirs Include=".\AIMLGUI\AIMLGUI\bin" />
	<BinDirs Include=".\AIMLWebService\AIMLWebService\bin" />
	<BinDirs Include=".\ExampleCustomAIMLTags\ExampleCustomAIMLTags\bin" />
	<BinDirs Include=".\Shared\bin" />
	<BinDirs Include=".\Tests\Tests\bin" />

	<ObjDirs Include=".\AIMLbot\AIMLbot\obj" />
	<ObjDirs Include=".\AIMLGUI\AIMLGUI\obj" />
	<ObjDirs Include=".\AIMLWebService\AIMLWebService\obj" />
	<ObjDirs Include=".\ExampleCustomAIMLTags\ExampleCustomAIMLTags\obj" />
	<ObjDirs Include=".\Shared\obj" />
	<ObjDirs Include=".\Tests\Tests\obj" />
</ItemGroup>

<Target Name="Clean">
	<Message Text="타겟: Clean" />

	<MSBuild Projects="@(ProjectReferences)"
Targets="Clean"
Properties="
Configuration=%(ProjectReferences.Configuration);
Platform=%(ProjectReferences.Platform)"
StopOnFirstFailure="true" />

	<RemoveDir Directories="@(BinDirs);@(ObjDirs)" />
</Target>
</Project>

		

조금 번거롭긴 하지만 지워야 할 대상 폴더의 목록(BinDirs, ObjDirs)을 만들었다. 그리고 RemoveDir 을 써서 대상 폴더를 모두 삭제했다. 이 방법은 상당히 효과적이지만 귀찮은 점도 있다. 무엇보다 대상 폴더의 목록을 만들고 꾸준히 갱신해야 한다는 점이 성가시다. 다행히 Item에 와일드카드를 쓸 수 있다. 이를테면 [목록 8]처럼 말이다.

[목록 8] Clean 타겟 – 세 번째 시도
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

중략…

<!-- Clean 할 대상 디렉터리 -->
<ItemGroup>
	<FilesInBinDirs Include="**\**\bin$(Configuration)\*" />
	<FilesInBinDirs Include="**\bin$(Configuration)\*" />

	<FilesInObjDirs Include="**\**\obj$(Configuration)\*" />
	<FilesInObjDirs Include="**\obj$(Configuration)\*" />
</ItemGroup>

<!-- 타겟 시작 -->
<Target Name="Clean">
	<Message Text="타겟: Clean" Importance="high" />

	<MSBuild Projects="@(ProjectReferences)"
Targets="Clean"
Properties="Configuration=%(ProjectReferences.Configuration);
Platform=%(ProjectReferences.Platform)"
StopOnFirstFailure="true" />

	<Delete Files="@(FilesInBinDirs);@(FilesInObjDirs)" />
</Target>
</Project>

		

이 방법은 간단하고 효과적이지만 여전히 문제가 없진 않다. 이렇게 저렇게 테스트를 많이 해봤지만 Item에 와일드카드를 쓰면 아무래도 폴더 지정이 안 되는 듯 하다. 그러니까 bin\debug\ 에 있는 모든 파일을 지우라고 하는 것보단 bin\debug 폴더를 통째로 지우라고 하는 편이 직관적이고 확실하게 정리(Clean)하는 방법이다. 하지만 여러 차례 시도해봤지만 번번히 실패했다. 혹 이 문제를 해결할 방법이 있을지도 모른다. 만약 방법을 찾게 되면 블로그를 통해 전달하도록 하겠다.

끝마치는 말

자, MSBuild의 기초는 익혔다. 다음 시간엔 MSBuild로 복잡한 일을 다루는 방법에 대해 알아보겠다. 지면이 허락하고 진도가 충분히 빠졌다는 판단이 들면 아마 여러분이 어디서도 보지 못한 신선한 사례도 다룰 수 있을 것이다. 기대하시라.

Author Details
Kubernetes, DevSecOps, AWS, 클라우드 보안, 클라우드 비용관리, SaaS 의 활용과 내재화 등 소프트웨어 개발 전반에 도움이 필요하다면 도움을 요청하세요. 지인이라면 가볍게 도와드리겠습니다. 전문적인 도움이 필요하다면 저의 현업에 방해가 되지 않는 선에서 협의가능합니다.
0 0 votes
Article Rating
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

2 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
새벽커피향기
새벽커피향기
11 years ago

감사합니다 도움이 많이 되었습니다

CHOI, Jaehoon
11 years ago

아직도 도움이 된다니 다행이고 즐겁네요.