[프로그래밍 노트] 실전 성능분석 (준비 편): 테스트 자동화


변경 내역

  1. 2006.08.04 작성.


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

일찍이 Donald Ervin. Knuth는 "Premature optimization is the root of all evil."(너무 이른 최적화는 모든 악의 근원이다.)라고 말했다. Kent Beck과 Ken Auer가 Lazy Optimization(최적화 미루기)를 제창한 것도 같은 맥락에서 이해할 수 있다. 20%의 코드가 전체실행시간의 80%를 차지하므로, 세부적인 성능 문제까지 모두 예측하여 일일이 대처하기 보다는 대범하게 성능분석과 최적화 작업을 미루는 게 낫다.

최재훈 | 필자는 복학을 앞두고 전에 없이 평온한 생활을 즐기고 있다. 패턴이나 소프트웨어 공학 책을 읽고, 자전거 산책을 즐긴다. 아마도 대부분의 독자는 일상에 치여 바쁘게 살아가고 있을 테지만, 책 한 권을 가방에 넣고 지하철에서 짬짬이 읽는다면 마음의 여유를 찾을 수 있으리라 믿는다.


로버트 L. 글래스는 『소프트웨어 공학의 사실과 오해』에서 효율에 관해 이렇게 말했다. 설계를 최소화하고 빨리 코딩하는 접근방법을 버리지 않으면, 복잡성이 큰 문제는 다루지 못할 것이다. 그는 자신의 견해와 상충되는 Extreme Programming 진영의 주장도 함께 제시했다. XP는 "설계를 단순하게 하고 코딩을 빨리 시작하는 것을 지지"한다. 그에 따라 발생하는 "설계상의 비효율과 오류를 수정하기 위한 지속적인 "리팩토링"을 강조한다. 앞서 소개한 Lazy Optimization는 이러한 XP의 철학을 반영한다. 필자는 개인적으로 XP의 주장을 지지한다. 프로파일링 기법은 "최적화 미루기"를 지지해주는 훌륭한 버팀목이다.

앞으로 두 차례에 걸쳐 필자는 다음과 같은 상황을 가정하고, 실전적인 성능분석 사례를 제시할 생각이다. 여러분은 Gif, jpeg 이미지 파일을 업로드할 수 있는 XML 웹 서비스를 구축하는 개발자이다. 운 좋게도 훌륭한 팀과 함께 일하게 된 덕분에 벌써 서비스의 주요 기능이 완성됐다. 다만 최적화 미루기 원칙에 따라 성능보다는 기능 구현에 초점을 맞춰 왔기 때문에 잠재적인 문제가 발생할 수도 있다. 이러한 이유로 팀은 내부 시범 서비스와 성능 분석 작업을 통해 잠재적인 문제를 조기에 발견하고, 리팩토링을 통해 만족할만한 성능 요구치를 달성하기로 결정했다.



예제 ASP.NET 어플리케이션 설치하기

Web Methods Make it Easy to Publish Your App’s Interface over the Internet라는 글과 함께 제공되는 예제 소스코드(통칭하여 WebMethods라 부르자)를 성능 분석할 생각이다. 필자가 이 WebMethods를 선택한 데에는 몇 가지 이유가 있다. 첫째, 이전 기사에서 다루지 못했던 ASP.NET 어플리케이션을 접할 수 있다. 둘째, 이 MSDN 매거진의 기사는 매우 실용적인 기술을 다루고 있다. 실제로 필자는 MMS(Multimedia Messaging Service)를 개발할 때, 위의 기사를 참고로 삼았다.

WebMethods.exe 파일을 다운로드 받아서 압축을 푼다. 이때 경로는 C:\로 잡아야 한다. 만약 다른 경로를 선택한다면, 소스코드와 설정파일(web.config)에 하드 코딩된 경로 값을 모두 수정해야 한다. "C:\Content Web Service"라는 폴더가 생성되어 있을 것이다. 이 폴더의 이름을 "ContentWebService"로 바꿔야 한다.

[제어판/관리도구/인터넷 정보 서비스]를 선택한다. 개인 컴퓨터라면 [기본 웹사이트]를 고르고 마우스 오른쪽 버튼을 눌러서 컨텍스트 메뉴를 연다. [새로 만들기/가상 디렉토리]를 선택한 후, ‘가상 디렉토리 별칭’과 ‘웹 사이트 컨텐트 디렉터리’에 각각 ContentManager, C:\ContentWebService\ContentManager 을 입력한다. [기본 웹사이트]에 ContentManager라는 응용프로그램이 구성되면, [속성] 창을 열고 [ASP.NET] 메뉴를 확인한다. ASP.NET 버전이 2.x 로 선택되었는지 확인한다.


예제 소스코드 설치


<화면 1> 빌드 구성


Visual Studio 2005에서 C:\ContentWebService\ContentManager.sln 파일을 열면 변환 마법사가 나타난다. 기본값으로 변환시키면 된다. 변환이 완료되고 솔루션이 열리면 Content.asmx 파일을 선택하고 "시작 페이지로 설정"한다. 이제 마지막으로 web.config 파일만 수정하면 된다. <compilers> 섹션과 <httpHandlers> 섹션만 주석처리하면 작업 완료다.

아마도 상당수 독자는 이런 작업이 귀찮을 것이다. 그래서 필자가 솔루션 변환, 설정 값 수정 등을 마친 예제 소스코드를 웹에 올려놨다. 다음 주소에서 파일을 다운로드 받아서 C:\에 설치한 후, [인터넷 정보 서비스]의 설정 작업만 적용하면 된다.



WebMethods의 샘플 데이터

예제 소스코드는 기본적인 테스트 환경을 제공하고 있다. "C:\ContentWebService\TheLittleSOAPClient"에 저장된 두 개의 VB 스크립트 파일을 실행시키면, "C:\ContentWebService\SoapEnvelopes\MIMEMessage.txt" 파일을 읽어서 ContentManager ASP.NET 어플리케이션에 전송한다. ContentManager 웹 응용프로그램은 수신 받은 데이터를 분석한 후, MIME(Multipurpose Internet Mail Extensions) 메시지에 BASE64 인코딩되어 있는 파일을 "C:\ContentWebService\PublishedContent"에 저장한다.

"MIMEMessage.txt "의 기본값이 <리스트 1>에 제시되어 있다. VB 스크립트를 실행시키면 "C:\ContentWebService\PublishedContent"에 "MIMEtesttext.txt", "MIMEtestdoc.doc" 두 개의 파일이 생성된다.


<리스트 1> 샘플 데이터

--MIME_boundary
Content-Type: text/xml; charset=UTF-8
Content-Transfer-Encoding: 8bit
Content-ID: <soapenvelope.xml@paulsoftware.com>

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<Publish xmlns="urn:webservices-paulsoftware-com-content">
<contentItems>
<Document>
<Author>Paula Paul</Author>
<Title>The Little Text Document</Title>
<Abstract>This is a test text document</Abstract>
<Categories>
<string>WebServices</string>
<string>Content</string>
</Categories>
<FileName>MIMEtesttext.txt</FileName>
<Data href="cid:MIMEtesttext.txt@paulsoftware.com"></Data>
</Document>
<Document>
<Author>Paula Paul</Author>
<Title>The Little Word Document</Title>
<Abstract>This is a test Word document</Abstract>
<Categories>
<string>Webservices</string>
<string>Binary Content</string>
</Categories>
<FileName>MIMEtestdoc.doc</FileName>
<Data href="cid:MIMEtestdoc.doc@paulsoftware.com"></Data>
</Document>
</contentItems>
</Publish>
</soap:Body>
</soap:Envelope>

--MIME_boundary
Content-Type: application/msword; name="MIMEtestdoc.doc"
Content-Transfer-Encoding: Base64
Content-ID: MIMEtestdoc.doc@paulsoftware.com

0M8R4KGxGuEAAAAAAAAAAAAAAAAAAAAAPgADAP7/CQAGAAAAAAAAAAAAAAABAAAAKgAAAAAA
AAAAEAAALAAAAAEAAAD+////AAAAACkAAAD/////////////////////////////////////

중략….

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

--MIME_boundary
	



성능분석의 전제조건 ? 샘플의 중요성

아직 한번도 언급한 적이 없지만 올바른 성능분석을 위해서 반드시 선행되어야 할 요건이 있다. 필자의 개인적인 생각에는 가비지 콜렉터의 작동 방식이나 CLR Profiler의 사용법을 아는 것 보다 10배는 중요한 것이다. 적절한 테스트 환경의 구축이 전제되고서야 여태까지 논의해왔던, 그리고 앞으로 논의할 모든 기법이 의미를 지닐 수 있다. 이제 무엇이 "적절한" 것인지 알아보자.

  1. 적절한 샘플의 선택

    실제 환경을 가능한 한 재현할 수 있어야 한다. 만약 출시 또는 서비스 개시 이후에 성능분석을 하는 경우라면 실제 데이터를 수집할 수 있을 것이다. 하지만 프로젝트 중간에 성능 개선이 필요하다면 여러분은 뒤이어 제시될 항목에 주의를 기울여야 한다.

  2. 오류를 발생시킬 수 있는 부적절한 데이터

    price viagra thailand 같은 사진 업로드 서비스를 생각해보자. 개발자인 여러분은 수십 가지의 jpg, gif 파일을 가지고 테스트를 진행했고, 별다른 문제를 발견하지 못했다. 기능 구현은 진작에 마쳤기 때문에 안심하고 서비스를 개시했다. 그러나 이틀도 지나지 않아 반응속도가 느리다는 사용자의 불만이 접수되기 시작했다. 문제를 해결하라는 상부의 재촉에 시달리며 야근까지 하고서야 원인을 발견할 수 있었다. 아직 서비스 지원이 안 되는 psd 파일을 업로드 하는 사용자가 많았던 것이다. Psd 파일을 업로드하려고 시도하면 예외가 발생하는데, 이 예외가 스택의 최상단에 도달하기까지 처리되지 않고 있었다. 개발자인 여러분은 설마 psd 파일을 업로드하려는 사람은 없겠지.라고 지레짐작할 수도 있다.

  3. 실제 상황과 유사한 각 샘플 빈도

    20가지 종류의 jpg, gif 파일 등을 준비했다. 거기에 psd 파일 등도 갖췄다. 각각의 이미지마다 100개의 샘플을 준비했다. 무엇이 문제인가? "심슨"이라는 키워드로 구글 이미지 검색을 해보자. 필자의 의도와는 달리 대부분 제시카 심슨의 사진이다. 최초의 100개의 이미지만 살펴보니 83개가 jpg 파일이었고, 11개가 gif였다. 만약 제품이 출시되었거나 서비스가 시작된 이후라면 잘못된 데이터의 입력 빈도나 데이터 자체를 수집할 수도 있다. 그렇지 않은 경우라면 방어적인 입장에서 적어도 전체 샘플 중 5% ~ 10%를 오류를 발생시킬 수 있는 부적절한 데이터로 채워 넣으면 된다.

  4. 자동화

    테스트는 지루한 작업이다. 샘플 선택과정에는 어느 정도의 창의성이 필요하기는 하지만 나머지는 단순반복 작업이다. 성능 분석 및 개선 작업 내내 똑 같은 테스트를 반복해야 하는데, 사람이 하면 아무래도 실수하기 마련이다. ASP.NET 웹 어플리케이션의 경우에는 WAS(Microsoft Web Application Stress)나 ACT(Microsoft Application Center Test)와 같은 스트레스 테스트 도구를 사용하면 된다. 이와 관련해서는 이번 칼럼에서 알아볼 예정이다.

  5. 문서화

    문서화가 되어 있어야 다른 개발자가 분석과정을 재현할 수 있고, 경우에 따라서는 잘못된 부분을 지적할 수 있다. 서비스가 지원되지도 않는 psd 파일을 왜 테스트하는지 모른다면, 여러분이 실수했다고 생각할지도 모른다. 보는 눈만 많다면, 어떤 문제라도 쉽게 잡을 수 있다.

물론 "성능 분석하기" 기사에서 사용할 예제 소스코드와 샘플은 실제 서비스에는 미치지 못한다. 앞서 제시한 조건이 무색해질 수도 있지만 필자의 경험에 바탕을 두고 가능한 한 현실을 재현해보겠다.



테스트 자동화는 왜 필요한가?

스트레스 테스트 도구가 왜 필요할까? CLR Profiler나 NProf와 같은 프로필러의 사용법을 익히기도 바쁜데 또 다른 도구까지 등장하니 벌써부터 골치 아프다. 필자가 워낙 잘난 척하기 좋아하는 인물이라 이상한 기술을 늘어놓고 자랑하는 것 아닌가? 그렇지는 않다. 필자는 여러분을 괴롭힐 생각이 없다. 오히려 그 반대다. 약속하건대 새로운 도구는 여러분의 인생을 편하게 해 줄 것이고, 사용법을 익히는 것도 오래 걸리지 않는다.

우선 스트레스 테스트 도구가 없는 상황에서 어떻게 성능분석이 이뤄지는지 살펴보자. NProf를 실행시킨다. [File/New] 또는 Ctrl+N 키를 누르고 새로운 ASP.NET 프로젝트를 생성한다. (사실 ASP.NET 어플리케이션을 성능분석하기 위해 선행되어야 할 작업이 있지만, 이에 대한 설명은 다음으로 미룬다.) [Project/Start project run] 또는 F5 키를 누른다. NProf는 인터넷 서비스(IIS)를 중지시켰다 실행시킬 것이다. 이제 테스트 데이터를 ContentManager 웹 서비스에 전송할 차례다. "C:\ContentWebService\TheLittleSOAPClient"의 VB 스크립트를 실행시켜보자. "Request sent. HTTP request status=200"라는 팝업 창이 열리면 전송 완료된 것이다. 이제 NProf로 돌아가서 "Stop Run" 버튼을 누르면 분석결과가 제시된다.

<필자 노트> NProf ? ASP.NET 분석 결과가 제시되지 않는 버그

NProf는 아직 성숙된 오픈 소스 프로젝트라고 할 수는 없다. 최신 버전이 0.9.1에 불과하다. 그런 탓에 상용 프로필러와 비교해서 기능이 부족하고, 눈에 띄는 버그도 상당하다. NProf 0.9b이 발표된 후부터 NProf 포럼에는 ASP.NET 어플리케이션의 성능분석기능이 작동하지 않는다는 이야기가 오가고 있다.

NProf bug

위의 화면은 실제 테스트 결과를 보여준다. "Stop Run" 버튼을 누른 후, "Profiling Completed"라는 메시지가 출력됐다. 그게 끝이다. 프로파일링이 종료됐다는데 분석결과가 제시되지 않는다. 참고로 machine.config의 <processModel> 섹션 설정이 문제라면 아예 "Profiling Completed"와 같은 메시지가 출력되지 않는다. 문제는 여기서 그치지 않는다. 이 상태에서 NProf를 종료시키면, 작업관리자에는 NProf.Application.exe 프로세스가 종료되지 않은 채 여전히 떠있다. 물론 오픈 소스 커뮤니티에 참여자들은 제 나름의 해결책을 내놓았다.

NProf 공식 사이트에서는 소스코드를 다운로드 받을 수 있다. 압축을 풀고 NProf.Glue\Profiler\Profiler.cs 파일을 연다. 133줄의 _run=null; 코드를 if (_pi.ProjectType!=ProjectType.AspNet) _run=null;로 수정한 다음 빌드하면 된다. 아마도 독자 여러분은 이런 작업이 번거롭게 느껴질 수 있을 것이다. 그래서 필자는 블로그를 통해 미리 빌드해 놓은 바이너리 파일을 제공한다. 하지만 NProf의 후속버전에서도 버그가 수정되지 않는다면, 독자 스스로 위에서 소개한 방법으로 문제를 해결해야 할 것이다.

성능분석결과(화면 2)는 의미 있는 정보를 제시하지 못하고 있다. ContentManager 네임스페이스에 관한 정보가 아예 보이지 않는다. 이는 단 한차례의 테스트로는 쓸모 있는 성능 통계를 측정하지 못하기 때문이다. 약 40차례에 걸쳐 VB 스크립트를 실행시키고 나서 제대로 된 측정값(화면 3)를 얻어낼 수 있었다.

이것은 자동화 테스트 도구가 없다면 여러분이 하루에도 수백 번씩 마우스를 움직여서 스크립트 파일을 더블 클릭해야 하다는 뜻이다. 수십?수백 개의 테스트 케이스가 있다는 사실을 고려해 볼 때 클릭 회수는 수천 번이 될 수도 있다. 한가지 대안은 여러분 스스로 일련의 테스트 과정을 자동으로 수행할 어플리케이션을 작성하는 것이다. 필자도 한동안 이런 접근 방식을 택했었다. 하지만 필자는 테스트만을 위한 어플리케이션을 작성하는데 지쳤고, 대안을 찾아 나섰다.


테스트가 충분하지 못할 때,


<화면 2> 테스트가 충분하지 못할 때,


충분한 테스트를 거친 후


<화면 3> 충분한 테스트를 거친 후



스트레스 테스트 도구 ? Microsoft Application Center Test

Visual Studio .NET 2003 Enterprise Edition에는 Microsoft Application Center라는 도구가 포함되어 있다. Microsoft Web Application Stress와 달리 라이센스 문제 때문에 독자 중 상당수는 사용하기 힘들 수 있다는 것이 단점이다. 하지만 사용하기 쉽고 강력하다. 한글화가 되어 있다는 점도 여러 사람에게 매력적인 요소가 될 수 있을 것이다.

[파일/새 프로젝트] 또는 Ctrl+N 키를 눌러서 새로운 프로젝트를 만든다. 프로젝트 이름이나 경로는 원하는 대로 설정한다. 이제 새 테스트를 추가한다. 왼쪽 창에서 "테스트"를 선택하고 컨텍스트 메뉴에서 "새 테스트"를 고르면 된다. <화면 4>의 첫 번째 그림과 같은 창이 뜨면 "새 테스트 기록하기"를 선택하면 된다. "빈 테스트 만들기"는 테스트 케이스를 수작업으로 만들 때 사용하는데, 그다지 사용할 일이 많지는 않다.

"새 테스트 기록하기"를 선택하면 뒤이어 <화면 4>의 두 번째 그림과 같은 창을 볼 수 있다. "기록 시작" 버튼을 눌러도 화면 상의 변화는 거의 없다. 하지만 "기록 중지" 버튼이 활성화되면 작동하고 있는 것이니 걱정하지 않아도 된다. 기록이 시작됨과 동시에 인터넷 익스플로러가 실행된다. 필자가 좋아하는 와이어드 한국판에 접속하니 요청 정보란(화면 4)에 와이어드 한국판 사이트와 필자 컴퓨터의 인터넷 익스플로러가 주고 받는 HTTP 프로토콜이 표시된다. ACT는 로컬 컴퓨터의 웹 검색 세션을 모두 기록하는 것이다.


새 테스트 만들기 #1


새 테스트 만들기 #2


<화면 4> 새 테스트 만들기


흥미로운 잡지 기사를 보는 게 칼럼의 목적은 아니다. 방금 전의 기록을 무시하고 새로운 테스트를 만든다. 인터넷 익스플로러에는 신경 쓰지 말고 C:\ContentWebService\TheLittleSOAPClient의 VB 스크립트를 실행시키자. 전송할 메시지(MIMEMessage.txt)를 바꿔가면서 모든 샘플 데이터를 전송한다. 심슨네 가족, 귀여운 라와디 돌고래의 사진 데이터를 보내고 나서 "기록 중지" 버튼을 누른다. 이제 ContentManager와 VB 스크립트 웹 클라이언트가 주고 받은 웹 세션이 모두 기록됐고, 버튼만 누르면 언제라도 재현할 수 있다.

새 테스트를 선택하면, 기록된 웹 세션을 재현하기 위한 VB 스크립트 코드를 볼 수 있다. 이것은 WAS는 제공하지 않는 기능이다. 물론 필요에 따라 여러분 스스로 소스코드를 수정할 수도 있다. (참고로 WAS는 VB 스크립트 코드를 제공하지는 않지만, ACT와 마찬가지로 주고 받을 데이터를 직접 입력하거나 수정할 수는 있다.)


새 테스트의 VB 스크립트


<화면 5> 새 테스트의 VB 스크립트


새 테스트의 "속성" 창(<화면 6>)을 열어보자. 세 개의 주요 메뉴가 있다. "카운터"에서는 성능 카운터를 추가할 수 있다. 예를 들어 부하 수준에 따라 웹 서버의 CPU 사용률이 어떻게 변하는지 알고 싶을 때 성능 카운터가 유용하다. "사용자"에서는 인증정보 등을 설정하는데, ContentManager 웹 어플리케이션에는 사용자 인증기능이 없으니 당장은 사용할 일이 없다. 성능분석에서 가장 중요한 값은 "일반" 메뉴에 있다. "브라우저 동시 연결 수"는 크게 잡지 말자. 거듭 말하지만 프로필러를 작동시키면 응용프로그램의 성능이 수십?수백 배 느려진다. 테스트 로드 수준을 높게 잡으면 어떤 상황이 벌어질지 쉽게 짐작할 수 있다. 지정한 시간 또는 지정한 횟수만큼 테스트를 진행할 수 있다. 반면에 WAS는 시간만 지정할 수 있다.


테스트 속성 창


<화면 6> 테스트 속성 창


테스트를 마치고 나면 좌측 패널의 "결과" 메뉴를 선택해보자. 스트레스 테스트 결과(<화면 7>)를 확인할 수 있다. 그래프가 그려진 패널의 스크롤 바를 내리면 다양한 통계자료를 볼 수 있다. 모든 것을 설명하기에는 지면이 부족하지만, "참고 문헌"에 자세한 설명이 실려 있다.


스트레스 테스트 결과


<화면 7> 스트레스 테스트 결과



마치는 글

이번 칼럼에서는 샘플 데이터를 준비하고, 일련의 테스트 과정을 자동화하는 데 초점을 맞췄다. 지루하지만 앞으로의 즐거움을 위해선 반드시 선행되어야 할 작업이었다. 스트레스 테스트 도구와 같은 개발환경에 익숙하지 않은 독자라면, 지레 겁먹지 말고 한번만 기사의 내용을 실행보기 바란다.

마지막으로 다음 칼럼에서는 이번에 소개한 ACT 대신에 WAS를 사용할 것이다. 라이센스 등의 이유로 ACT를 사용하지 못하는 독자에겐 좋은 대안이 될 수 있다. 두 도구의 사용법은 대동소이하므로 차이점 등 필요한 부분만 짚고 넘어갈 생각이다.

This work, unless otherwise expressly stated, is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.