IBM®
메인 컨텐츠로 가기
    Korea [국가변경]    이용약관
 
 
   
        제품    서비스 & 솔루션    고객지원 & 다운로드    회원 서비스    
메인 컨텐츠로 가기

한국 developerWorks  >  Rational  >

IBM Rational Purify의 향상된 기능: Purify를 소프트웨어 개발과 테스트 과정에 통합하기

Purify 사용을 자동화하기 위한 변환 심볼과 옵션 활용

developerWorks
문서 옵션

JavaScript가 필요한 문서 옵션은 디스플레이되지 않습니다.

샘플 코드

영어원문

영어원문


제안 및 의견
피드백

난이도 : 중급

Satish Chandra Gupta, 프로그래머, RAD/PurifyPlus, Rational Software, IBM Japan
Anand Gaurav, 프로그래머, PurifyPlus, IBM Japan

옮긴이: 박재호 이해영 dwkorea@kr.ibm.com

2008 년 7 월 22 일

IBM® Rational® Purify®는 정확하게 메모리 손상 오류를 감지하는 도구입니다. 이 도구가 없다면 분석과 수정이 아주 어려워집니다. Purify를 소프트웨어 개발 생명 주기의 모든 단계에서 조직적이고 체계적으로 사용하면 초기에 메모리 오류를 감지할 수 있습니다. 이 기사에서는 자동화와 더불어 소프트웨어 개발과 테스트 과정에 통합함으로써 Purify를 최대로 활용하는 방법을 소개합니다.

IBM® Rational® Purify®는 찾아내기 힘든 메모리 손상 오류를 정확하게 발견하도록 도와주는 첨단 메모리 오류 감지 도구다. Purify로 응용 프로그램에 보조 코드를 집어 넣고 나서, 이 프로그램을 수행하면, Purify는 메모리 접근을 면밀히 검토해 메모리 손상 오류가 발생하기 전에 이를 보고한다.

Purify는 소프트웨어 개발 생명 주기 전반에 걸쳐 사용가능한 아주 쓸모있는 도구다. 개발자는 Purify를 사용해 새로 작성한 코드가 부주의하게 메모리 손상 오류나 누수를 일으키지 않는지 확인할 수 있다. 테스트 엔지니어는 Purify를 사용해 기능 검증이나 시스템 통합 테스트 과정에서 메모리 오류를 찾아낼 수 있다. 현장 엔지니어와 지원 엔지니어는 Purify를 사용해 소프트웨어 배포 이후에 발생하는 메모리 문제점을 진단할 수 있다. 결함 감지와 수정에 들어가는 비용은 소프트웨어 개발 생명 주기 초반 동안 최소로 유지되므로, 개발과 테스트 단계 동안 최대한 많은 문제점을 잡아 수정하는 편이 가장 바람직하다. 소프트웨어 개발 생명 주기를 통틀어 체계적이고 조직적인 Purify 활용은 이런 이상을 달성하는 데 도움을 준다. 이렇게 하려면 Purify 자동화와 함께 소프트웨어 개발과 테스트 공정으로 Purify를 통합하는 방법이 최선이다.

도구 자동화는 부하를 줄이고 사용에 들어가는 노력을 줄이는 효과가 있다. 이는 공정의 일부로 도구 채택에 대한 거부감을 줄여준다. 따라서 자동화는 공정을 매끄럽게 만드는 핵심이다. 예를 들어, Purify를 단위 테스트나 동작 테스트 스위트에 포함시켜 개발자가 코드 변경을 체크인하기 전에 반드시 Purify가 보고한 새로운 메모리 오류를 수정해야 한다는 지침을 제공할 수 있다. 이런 방식을 따르면 메모리 오류가 생겨도 쉽게 수정할 수 있다. 변경한 코드 내역이 아직 개발자 머리 속에 남아있기 때문이다. 비슷하게 기능과 시스템 검증 테스트 스위트에 Purify를 통합해 야간이나 주간 단위로 돌릴 수 있다. 테스터는 Purify가 보고하는 메모리 오류를 분석해 결함 보고서를 만들어낸다. 이렇게 하면 테스트 직후 하루나 일주일 내에 새로운 메모리 버그를 찾아내므로 소프트웨어 배포 이후에 추적하는 경우보다 상황이 훨씬 나아진다.

직전 기사인 "누수되는" 보트에서 "C" 탐험이라? Purify를 써보자에서는 Purify를 사용하는 방법과 빌드 makefiles에 통합하는 방법을 설명했다. 이미 Purify에 익숙하다면, 이 기사를 건너뛰거나 슬쩍 훑고 넘어가자. 이 기사에서는 빌드와 테스트 환경을 바꿔 Purify와 통합하는 방법을 먼저 설명하고 계속해서 자동화 목적으로 Purify 옵션을 위한 변환 심볼을 설명한다. 그리고 Purify 오류를 웹 페이지에 게시하는 작업을 자동화하도록 이 모든 기능을 활용하는 예제를 살펴보겠다.

빌드와 테스트 환경에 Purify를 통합하기

소프트웨어 개발과 테스트 공정에 Purify를 통합하기 위한 첫 단계로 빌드와 테스트 시스템을 변경하자. 빌드 시스템은 응용 프로그램을 빌드하고, 테스트 시스템은 테스트 스위트로 응용 프로그램을 돌린다(그림 1에서 푸른색 표시). 일반적으로 응용 프로그램 빌드와 테스트 과정은 자동화되어 있으며 밤이나 주간 단위로 일정이 잡혀 있다.

빌드 시스템을 변경해 일반 프로그램을 Purify로 처리한 응용 프로그램 형태로 바꿔야 한다. 일반적으로 디버그 정보 없이 응용 프로그램을 빌드하게 된다(릴리스 모드). Purify로 처리한 응용 프로그램을 만들려면 (필수는 아니라도) 디버그 정보와 함께 응용 프로그램을 빌드하고(디버그 모드) Purify로 처리하는 방식이 바람직하다. 또한 테스트 시스템을 변경해 일반 응용 프로그램은 물론이고 Purify로 처리한 응용 프로그램을 테스트 스위트를 돌릴 필요가 있다. 이런 추가 빌드와 테스트 단계는 그림 1에서 초록색으로 표시했다. 이런 변경 이후에, Pufify로 처리한 응용 프로그램을 빌드하고 밤이나 주간 작업으로 자동화된 테스트 스위트로 돌리도록 규칙을 추가한다. 이 기사 후반부에는 Purify가 메모리 오류나 누수를 감지했을 때 행동을 통제하고 자동화하는 다양한 방법을 살펴볼 것이다.


그림 1. 빌드와 테스트 시스템 변경
변경 전후 비교

변환 심볼 활용하기

Purify는 -view-file 이나 -log-file (두 옵션은 각각 Purify 출력을 뷰 파일과 아스키 로그 파일로 보낸다) 같은 여러 옵션 값 설정에 도움을 주는 변환 심볼을 제공한다. Purify는 이런 심볼을 의미있는 확장을 통해 대체하며, 자료 저장을 위한 유일한 파일 이름을 만들어낸다. 예를 들어, 프로그램 이름과 프로세스 ID를 로그 파일 이름으로 사용할 수 있다.

$ purify -log-file=./purifyerrors_%v_%p.plog cc -o progname foo.c bar.c
      

이 명령은 보조 코드가 삽입된 실행 파일을 progname이라는 이름으로 생성한다. 명령을 내릴 때 실행 프로세스 ID가 1234이면, 모든 Purify 오류는 purifyerrors_progname_1234.plog라는 이름이 붙은 파일로 기록된다. 로그 파일 이름에서 Purify는 %v를 프로그램 실행 파일로, %p를 프로세스 ID로 확장한다.

Purify 실행 후에 연산 추가하기

가공된 응용 프로그램을 동작시키기 앞서 일어나는 작업을 자동화하기란 쉽다. 여러분이 통제권을 쥐고 있기 때문이다. 여러분은 후처리 작업 추가를 지원하는 다양한 Purify 기능을 활용해 응용 프로그램 수행 직후 어떤 일이 일어났는지를 통제하고 자동화할 수 있다. Purify는 가공된 응용 프로그램이 종료한 다음에 스크립트를 돌리도록 허용한다. 가공된 프로그램이 끝난 다음에 보고된 모든 오류를 요약해 보고하도록 스크립트를 활용할 수 있다. 이렇게 하려면 Purify에 -run-at-exit 옵션을 붙여 실행한다. 예를 들면, 가공된 응용 프로그램이 test.pure일 경우에, 발견된 오류 요약 보고서를 출력하려면 이 옵션을 다음과 같이 사용한다.

$ setenv PURIFYOPTIONS '-run-at-exit="if %z ; then \
 	echo \"%v : %e errors, %l bytes leaked.\" ; fi"'
      

-run-at-exit 옵션 뒤에 나오는 문자열은 프로그램이 종료한 다음에 셸이 수행한다. 변환 심볼 치환은 예를 들어 동작 중에 Purify 오류나 누수가 발생했다면 %z 값을 false로 만든다. 이런 이유로 인해 이 예제에서 if 구문은 다음과 같이 해석된다. "오류가 있을 경우에만 'echo'를 실행한다." 계속해서 echo 명령은 얼마나 많은 오류가 있었는지를 보고하기 위해 좀 더 많은 치환 문자열을 사용한다. 프로그램 종료 결과에 따라 Purify는 다음과 비슷한 메시지를 보낸다.

$ test.pure
test.pure : 2 errors, 10 bytes leaked
      

이 메시지는 간단한 예제다. 하지만 좀 더 복잡한 처리 과정을 스크립트나 심지어 프로그램 내부에 심어 다양한 변환 심볼을 스크립트나 프로그램에 전달할 수도 있다. 예는 다음과 같다.

$ setenv PURIFYOPTIONS '-run-at-exit="postprocess.csh %v %z %e %l "'
      

표 1표 2는 변환 심볼에 대한 좀 더 상세한 치환 문자열을 보여준다.


표 1. Purify 옵션으로 쓸 수 있는 변환 심볼
문자변환
%v프로그램 실행 파일 이름, 소문자 V(실행 중인, 가공한 실행 파일 이름)
%V프로그램 전체 경로, 대문자 V(/_로 대체된다)
%p프로세스 ID(pid, PID)


표 2. exit 명령에 사용되는 변환 심볼(-run-at-exit)
문자변환
%z true인지 false인지를 나타내는 문자열이며 오류 호출 체인이나 누수가 출력되었는지를 알려준다(Purify가 흥미를 보일만한 뭔가를 찾았는지에 따라 exit 스크립트가 다르게 행동할 때 필요하다).
%x프로그램 종료 상태(프로그램이 exit를 호출하지 않으면 0이다)
%e출력된 개별 접근 오류 개수
%E출력된 전체 오류 개수
%l누수된 메모리 바이트 개수(소문자 L)
%L잠재적으로 누수된 메모리 바이트 개수(대문자 L)

프로그램 종료 상태 활용

가공된 프로그램이 끝나고 스크립트를 돌리는 방법을 이미 익혔다. Purify는 또한 종료 상태를 통해 몇 가지 정보를 제공한다. 기본적으로 Purify는 프로그램의 정상적인 종료 상태를 변경하지 않는다. 하지만 Purify가 메모리 접근 오류나 메모리 누수를 찾으면 여러분 프로그램이 특수한 종료 상태를 반환하도록 선택할 수 있다. 이는 테스트 스위트에서 동작 실패를 알려주는 편리한 방법이다. -exit-status=yes 옵션을 켜면 Purify가 실행 오류 유형을 알리기 위해 플래그를 삽입한다. Purify 오류 출력을 활성화했다면, 현재 메모리 오류 유형에 따라 다음 값을 비트 단위로 OR하는 방법으로 상태 코드를 계산해 넘긴다.

  • 0x40: 메모리 접근 오류
  • 0x20: 메모리 누수
  • 0x10: 잠재적인 메모리 누수

다른 방법으로 코드 내부에서 exit(status) 를 호출하는 대신에 main() 함수에 있는 return 문을 purify_exit(status) 함수를 부르도록 변경할 수 있다. (Purify API 함수에 대한 기사는 참고자료 절을 참조하자.) 메모리 접근 오류에 대해서만 신경쓴다면, -leaks-at-exit=no 를 켜서 종료 시에 누수 감지를 꺼버리거나 메모리 누수와 잠재적인 메모리 누수 메시지를 보여주지 않도록 설정할 수 있다. 또한 종료 상태에서 적절한 비트를 무시할 수도 있다. 하지만 Purify 보고서에 나오는 프로그램 요약 메시지는 다른 Purify 결과 상태 비트를 종료 상태에 OR하기 앞서 원래 종료 상태를 항상 보여주게 되어 있다.

Listing 1 -exit-status 옵션을 붙인 다음에 프로그램에 오류가 있는지를 결정하는 과정에서 exit 상태를 점검하는 예다.


Listing 1. 종료 상태 옵션 예제
                
$ cat prog.c
#include <stdio.h>

int main() {
    int i,j;
    i = j+1;  /* UMR: 초기화하지 않은 변수 j를 읽고 있다 */
    return 0;
} 

$ purify -exit-status=yes cc -g prog.c -o prog.pure
$ prog.pure
$ echo $?
64

main 함수에서 반환된 종료 값은 0이 아니다. 반환값은 64이며 16진수로는 0x40이다. 이렇게 값이 나온 이유는 Purify가 UMR(Uninitialized Memory Read) 메모리 접근 오류를 프로그램에서 감지했기 때문이다. Purify로 가공한 실행 파일을 돌린 다음에 받은 종료 상태를 점검한 후, 테스트 프로그램을 돌려 나온 결함 보고서나 Purify 로그에 남은 결과에 따라 오류에 대한 적절한 조치를 진행하도록 이 옵션을 손쉽게 스크립트에 포함할 수 있다.

보조 코드를 넣은 응용 프로그램이 첫 오류을 감지하자마자 바로 종료하기를 원한다면, -exit-on-error 옵션을 사용한다. 이 옵션을 사용할 때, 프로그램은 Purify가 오류를 인지하는 순간에 종료한다(suppresskill 지시자를 사용해 감춰진 오류는 포함하지 않는다).

Purify 결과를 전자편지로 보내 분석을 지원하기

Purify가 지원하는 -mail-to-user 옵션은 일간이나 주간 Purify 결과 보고를 자동화는 데 사용한다. 이 옵션을 사용하면 Purify는 오류 보고서를 특정 테스트와 개발자 전자편지 주소로 발송하므로, 전자편지를 읽고 결과를 검토할 수 있다. 예를 들어, 프로그램을 다음과 같이 Purify로 돌렸다고 가정하자.

$ purify -mail-to-user=yourid cc -g prog.c -o prog.pure
      

다음에 prog.pure를 동작하면, Purify는 yourid 전자편지 주소로 보고서를 자동으로 발송한다.

종종 오류를 분석할 때, 함수 이름뿐만 아니라 위치한 파일 전체 경로라든지 PC 값과 같은 기타 세부 정보를 살펴볼 필요가 있다. 다음과 같은 옵션을 사용해 Purify가 이런 정보를 출력하도록 활성화할 수 있다.

  • -show-pc는 전체 PC 값을 보여준다.
  • -show-pc-offset은 함수 시작 위치를 pc 변위로 보여준다.
  • -show-directory는 함수를 포함하는 파일 위치를 디렉터리 목록으로 보여준다(디버그 모드로 프로그램을 빌드해야 한다).

예제

이 절에서는 이번 기사에서 익힌 대다수 옵션을 활용하는 예제를 살펴보겠다. Listing 2는 변경된 빌드와 테스트 시스템을 포함한 GNUMakefile이다(이 기사에 사용한 원시 코드를 얻으려면 다운로드를 살펴본다). 응용 프로그램 이름이 memerrors라면, Purify로 가공할 응용 프로그램을 빌드하기 위해 추가할 새로운 목표 이름은 memerrors.pure다. 비슷한 방법으로 Purify로 가공할 응용 프로그램을 돌릴 테스트를 새로운 목표 이름으로 추가한다.

Purify 실행 옵션은 테스트에 앞서 설정한다. -log-file 옵션은 생성할 로그 파일 이름이며, 변환 심볼과 date 명령을 사용한다(파일 이름은 프로그램 이름, 프로세스 ID, 날짜와 시각을 포함한다). -run-at-exit 옵션은 프로그램이 끝나고 addsummary.sh 스크립트에 변환 심볼(다시 말해 로그 파일 이름, 오류 호출 체인을 출력할지 유무, 종료 상태, 발견한 메모리 오류 개수, 메모리 누수 크기, 잠재적인 메모리 누수)을 통해 지정한 인수를 넘긴 다음 돌리도록 지시하는 데 쓰인다. -exit-status=yes를 사용하지 않기에, Purify는 종료 상태값을 덮어쓰지 않고 프로그램의 원래 종료 상태를 유지한다.


Listing 2. 변경된 빌드와 테스트 시스템을 지정한 GNUMakefile
                
# Purify 변환 심볼과 date 명령을 사용한 로그 파일 이름 결정
DATEANDTIME     := `date +%Y_%b_%d_%H_%M_%S`
LOGFILENAME     := %v_pid%p_$(DATEANDTIME).plog

# Purify로 가공한 프로그램이 존재하면 스크립트를 수행한다.
PURIFYEXITSCRIPT:= \"addsummary.sh $(DATEANDTIME) $(LOGFILENAME) %z %x %e %l %L\"

# Purify 옵션
PURIFYOPTIONS   := -log-file=$(LOGFILENAME) -run-at-exit=$(PURIFYEXITSCRIPT)

# 목표와 규칙
all: runtest runpurifytest

# Clean
clean:
	$(RM) memerrors memerrors.pure

# 응용 프로그램 빌드
memerrors: memerrors.c
	$(CC) -o $@ $? 

# Purify로 가공한 응용 프로그램 빌드
memerrors.pure: memerrors.c
	purify $(CC) -g -o $@ $? 

# 테스트 스위트 수행
runtest: memerrors
	./memerrors

# Purify로 가공한 응용 프로그램으로 테스트 스위트 수행
runpurifytest: memerrors.pure
	echo Starting test at $(DATEANDTIME) .....
	env PURIFYOPTIONS="$(PURIFYOPTIONS)" ./memerrors.pure

# GNUMakefile 끝
      

Listing 3에 나오는 addsummary.sh 스크립트는 HTML 보고서를 생성한다. 보고서는 목록 파일로 지금까지 동작한 각 Purify 결과를 HTML 테이블 하나로 표시하며, 시간 역순으로 배열한다. Pufiry 동작이 끝나고 스크립트를 돌리면, 마지막 수행 결과를 직전 행이 담긴 파일에 덧붙여 HTML 테이블로 표시하는 새로운 목록 파일을 생성하고 예전 목록 파일을 새로운 목록 파일로 교체한다. 최종적으로 목록 파일을 HTML 태그로 감싸는 방법으로 HTML 파일을 생성한다.


Listing 3. addsummary.sh 셸 스크립트 내용
                
#!/bin/sh

DATEANDTIME=$1
LOGFILENAME=$2
LOGFULLNAME=`pwd`/$LOGFILENAME
ERRORFOUND=$3
EXITSTATUS=$4
ERRORCOUNT=$5
LEAKSIZE=$6
PLEAKSIZE=$7

PURIFYREPORT="purify_reports"
REPORTLIST="$PURIFYREPORT.list"
REPORTNEWLIST="$REPORTLIST.new"
REPORTHTML="$PURIFYREPORT.html"

# 시작
echo Processing $LOGFILENAME created at $DATEANDTIME

# 보고서 파일이 존재하지 않으면 생성한다.
touch $REPORTLIST

# 가장 마지막에 동작한 Purify를 위한 행 생성
echo "<tr>" >> $REPORTNEWLIST
echo "<td>$DATEANDTIME</td>" >> $REPORTNEWLIST
if ($ERRORFOUND == "true"); then
    echo "<td>FAILED</td>" >> $REPORTNEWLIST
else
    echo "<td>Pass</td>" >> $REPORTNEWLIST
fi
echo "<td>$EXITSTATUS</td>" >> $REPORTNEWLIST
echo "<td>$ERRORCOUNT</td>" >> $REPORTNEWLIST
echo "<td>$LEAKSIZE bytes</td>" >> $REPORTNEWLIST
echo "<td>$PLEAKSIZE bytes</td>" >> $REPORTNEWLIST
echo "<td><a href=\"$LOGFULLNAME\">$LOGFILENAME</a></td>" >> $REPORTNEWLIST
echo "</tr>\n" >> $REPORTNEWLIST

# 이 행을 테이블 처음에 추가한다.
cat $REPORTLIST >> $REPORTNEWLIST
mv $REPORTNEWLIST $REPORTLIST

# HTML 페이지 생성
# 헤더
echo "<html>"  > $REPORTHTML
echo "<body>" >> $REPORTHTML
echo "<table border=1>" >> $REPORTHTML
echo "<caption>Purify Test Summary</caption>" >> $REPORTHTML
echo "<tr>" >> $REPORTHTML
echo "<th>Date & Time</th><th>Result</th><th>Exit Status</th>"  >> $REPORTHTML
echo "<th>Errors</th><th>Leaks</th><th>Potential Leaks</th>" >> $REPORTHTML
echo "<th>Log File</th>"  >> $REPORTHTML
echo "</tr>\n" >> $REPORTHTML
# Add rows for Purify results
cat $REPORTLIST >> $REPORTHTML
# 풋터
echo "</table>" >> $REPORTHTML
echo "</body>" >> $REPORTHTML
echo "</html>" >> $REPORTHTML

# 완료
echo "Successfully updated $REPORTHTML"

# addsummary.sh 끝

그림 2는 세 가지 테스트 스위트 동작 후 생성된 HTML 페이지를 보여준다. 각각은 행으로 나와 있으며, 각 행은 Purify 로그 파일로 향하는 하이퍼링크를 포함한다. 연속적인 테스트 스위트 수행 결과는 테이블 처음에 새로운 행으로 나타난다.


그림 2. 브라우저에서 본 Purify 테스트 요약 보고서
화면은 실패한 테스트 세 개와 세부 사항을 보여준다

정리

이 기사에서는 Purify를 주기적이고 체계적으로 사용할 때 장점을 극대화한다는 사실을 설명했다. 지금쯤이면 Purify를 소프트웨어 개발과 테스트 공정에 통합하는 방법과 변환 심볼/옵션을 통해 이뤄낸 Purify 자동화 방법을 이해할 것이다.

이 기사에 소개한 예제가 간단하긴 하지만, Purify를 빌드와 테스트 환경으로 쉽게 통합하는 방법과 Purify 자동화에 따르는 가치를 보여준다. 테스트 스위트를 수행할 때마다 매번 자동으로 웹 페이지에 올라온 Purify 테스트 결과를 살펴보는 작업이 얼마나 단순한지 생각해보자. 현존하는 모든 로그 파일 또한 동일한 웹 페이지를 통해 접근할 수 있다. 여기 소개하는 예제는 의도적으로 간단하게 만들었으며, 단지 가능성만 보여줄 뿐이다. 결과를 비교해 추가적인 메모리 오류와 누수를 발견했을 때 세부 내역을 전자편지로 통보하는 상당히 복잡한 시스템도 만들 수 있다. 이를 통해 버그가 나오자마자 바로 수정할 수 있다. 이런 지식을 바탕으로 여러분은 Rational Purify를 최대로 활용할 준비가 끝났다.





위로


다운로드 하십시오

설명이름크기다운로드 방식
이 글에서 사용한 소스 코드automate-purify-sources.zip3KBHTTP
다운로드 방식에 대한 정보


참고자료

교육

제품 및 기술 얻기

토론


필자소개

Satish Chandra Gupta는 인도 방갈로르에 위치한 IBM Rational® PurifyPlus® 그룹의 개발자다. 관심 분야는 컴파일러, 프로그래밍 언어, 런타임 분석, 자바 메모리 누수, 유형 이론, 소프트웨어 엔지니어, 소프트웨어 개발 환경 등이다. 이와 관련한 연구가 ACM/IEEE 컨퍼런스에 출간됐다. 인도 칸푸르의 Indian Institute of Technology에서 학사 학위를, 미국 밀워키의 University of Wisconsin에서 석사 학위를 받았다.


Anand Gaurav는 인도 벵갈루루에 위치한 IBM Rational PurifyPlus 그룹의 개발자다. 관심 분야는 런타임 분석, 객체 지향 디자인, 데이터 구조, 알고리즘 등이다. 인도 카르나타카에 위치한 PESIT(VTU)를 졸업했다.




기사에 대한 평가


보다 나은 서비스를 제공하기 위함이오니 잠시 짬을 내어 이 양식을 제출하여 주십시오.



 


 


 


이 문서 북마킹 하기

mar.gar.in mar.gar.in naver naver eolin eolin del.icio.us del.icio.us





위로


developerWorks 콘텐트를 다른 사이트에 전재하기:
developerWorks 콘텐트에 대한 저작권은 IBM에 있습니다. IBM의 서면 허가나 원본 저자의 허락이 없이는 전재를 금합니다. 저희 콘텐트를 전재하시려면 IBM developerWorks 담당자 에게 문의하십시오.
    IBM 소개 개인정보 보호정책 문의