반응형

Bash를 이용한 쉘 스크립팅 완전 가이드

Mendel Cooper

Brindlesoft

thegrendel (at) theriver.com



 

차례

Part 1. 소개
1. 왜 쉘 프로그래밍을 해야 하죠?
2. #! 으로 시작하기
2.1. 스크립트 실행하기
2.2. 몸풀기 연습문제(Preliminary Exercises)
Part 2. 기초 단계
3. 종료와 종료 상태(Exit and Exit Status)
4. 특수 문자
5. 변수와 매개변수 소개
5.1. 변수 치환(Variable Substitution)
5.2. 변수 할당(Variable Assignment)
5.3. Bash 변수는 타입이 없다(untyped)
5.4. 특수한 변수 타입
6. 쿼우팅(quoting)
7. 테스트
7.1. 테스트(Test Constructs)
7.2. 파일 테스트 연산자
7.3. 비교 연산자(이진)
7.4. 중첩된 if/then 조건 테스트
7.5. 여러분이 테스트문을 얼마나 이해했는지 테스트 해보기
8. 연산자 이야기(Operations and Related Topics)
8.1. 연산자(Operators)
8.2. 숫자 상수(Numerical Constants)
Part 3. 중급 단계(Beyond the Basics)
9. 변수 재검토(Variables Revisited)
9.1. 내부 변수(Internal Variables)
9.2. 문자열 조작
9.3. 매개변수 치환(Parameter Substitution)
9.4. 변수 타입 지정: declaretypeset
9.5. 변수 간접 참조
9.6. $RANDOM: 랜덤한 정수 만들기
9.7. 이중소괄호(The Double Parentheses Construct)
10. 루프와 분기(Loops and Branches)
10.1. 루프
10.2. 중첩된 루프
10.3. 루프 제어
10.4. 테스트와 분기(Testing and Branching)
11. 내부 명령어(Internal Commands and Builtins)
11.1. 작업 제어 명령어
12. 외부 필터, 프로그램, 명령어
12.1. 기본 명령어
12.2. 복잡한 명령어
12.3. 시간/날짜 명령어
12.4. 텍스트 처리 명령어
12.5. 파일, 아카이브(archive) 명령어
12.6. 통신 명령어
12.7. 터미널 제어 명령어
12.8. 수학용 명령어
12.9. 기타 명령어
13. 시스템과 관리자용 명령어
14. 명령어 치환(Command Substitution)
15. 산술 확장(Arithmetic Expansion)
16. I/O 재지향
16.1. exec 쓰기
16.2. 코드 블럭 재지향
16.3. 응용
17. Here Documents
18. 쉬어가기
Part 4. 고급 주제들(Advanced Topics)
19. 정규 표현식(Regular Expressions)
19.1. 정규 표현식의 간략한 소개
19.2. Globbing
20. 서브쉘(Subshells)
21. 제한된 쉘(Restricted Shells)
22. 프로세스 치환(Process Substitution)
23. 함수
23.1. 복잡 함수와 함수의 복잡성(Complex Functions and Function Complexities)
23.2. 지역 변수와 재귀 함수(Local Variables and Recursion)
24. 별칭(Aliases)
25. 리스트(List Constructs)
26. 배열
27. 파일들
28. /dev 와 /proc
28.1. /dev
28.2. /proc
29. 제로와 널(Of Zeros and Nulls)
30. 디버깅
31. 옵션
32. 몇 가지 지저분한 것들(Gotchas)
33. 스타일 있게 스크립트 짜기
33.1. 비공식 쉘 스크립팅 스타일시트
34. 자질구레한 것들
34.1. 대화(interactive)형 모드와 비대화(non-interactive)형 모드 쉘과 스크립트
34.2. 쉘 래퍼(Shell Wrappers)
34.3. 테스트와 비교: 다른 방법
34.4. 최적화
34.5. 팁 모음(Assorted Tips)
34.6. 괴상한 것(Oddities)
34.7. 이식성 문제(Portability Issues)
34.8. 윈도우즈에서의 쉘 스크립팅
35. Bash, 버전 2
36. 후기(Endnotes)
36.1. 저자 후기(Author's Note)
36.2. 저자에 대해서
36.3. 이 책을 만드는데 쓴 도구들
36.3.1. 하드웨어
36.3.2. 소프트웨어와 프린트웨어
36.4. 크레딧
서지사항
A. 여러분들이 보내준 스크립트들(Contributed Scripts)
B. Sed 와 Awk 에 대한 간단한 입문서
B.1. Sed
B.2. Awk
C. 특별한 의미를 갖는 종료 코드
D. I/O와 I/O 재지향에 대한 자세한 소개
E. 지역화(Localization)
F. 샘플 .bashrc 파일
G. 도스(DOS) 배치 파일을 쉘 스크립트로 변환
H. 연습문제
I. Copyright
예 목록
2-1. cleanup: /var/log 에 있는 로그 파일들을 청소하는 스크립트
2-2. cleanup: 위 스크립트의 향상되고 일반화된 버전.
3-1. 종료/종료 상태
3-2. !으로 조건을 부정하기
4-1. 코드 블럭과 I/O 재지향
4-2. 코드 블럭의 결과를 파일로 저장하기
4-3. 최근 하루동안 변경된 파일들을 백업하기
5-1. 변수 할당과 치환
5-2. 평범한 변수 할당
5-3. 평범하고 재미있는 변수 할당
5-4. 정수? 문자열?
5-5. 위치 매개변수
5-6. wh, whois 도메인 네임 룩업
5-7. shift 쓰기
6-1. 이상한 변수를 에코하기
6-2. 이스케이프된 문자들
7-1. 무엇이 참인가?
7-2. [ ]test 의 동일함
7-3. (( ))로 산술식 테스트 하기
7-4. 산술 비교와 문자열 비교
7-5. 문자열이 인지 테스트 하기
7-6. zmost
8-1. 산술 연산자 쓰기
8-2. && 와 || 를 쓴 복합 조건 테스트
8-3. 숫자 상수 표기법:
9-1. $IFS 와 빈 칸
9-2. 타임 아웃 처리 입력
9-3. 타임 아웃 처리 입력, 한 번 더
9-4. 내가 루트인가?
9-5. arglist: $* 과 $@ 로 인자를 나열하기
9-6. 일관성 없는 $*$@의 동작
9-7. $IFS 가 비어 있을 때 $*$@
9-8. 밑줄 변수(underscore variable)
9-9. 그래픽 파일을 다른 포맷 확장자로 이름을 바꾸면서 변환
9-10. 매개변수 치환과 : 쓰기
9-11. 변수의 길이
9-12. 매개변수 치환에서의 패턴 매칭
9-13. 파일 확장자 바꾸기:
9-14. 임의의 문자열을 파싱하기 위해 패턴 매칭 사용하기
9-15. 문자열의 접두, 접미어에서 일치하는 패턴 찾기
9-16. declare를 써서 변수 타입 지정하기
9-17. 간접 참조
9-18. awk에게 간접 참조를 넘기기
9-19. 랜덤한 숫자 만들기
9-20. RANDOM 으로 주사위를 던지기
9-21. RANDOM 에 seed를 다시 지정해 주기
9-22. C 형태의 변수 조작
10-1. 간단한 for 루프
10-2. 각 [list] 항목이 인자를 두 개씩 갖는 for
10-3. Fileinfo: 변수에 들어 있는 파일 목록에 대해 동작
10-4. for 문에서 파일 조작하기
10-5. in [list]가 빠진 for
10-6. for 문의 [list]에 명령어 치환 쓰기
10-7. 이진 파일에 grep 걸기
10-8. 특정 디렉토리의 모든 바이너리 파일에 대해 원저작자(authorship)를 확인 하기
10-9. 디렉토리에 들어 있는 심볼릭 링크들을 나열하기
10-10. 디렉토리에 들어 있는 심볼릭 링크들을 파일로 저장하기
10-11. C 형태의 for 루프
10-12. 배치 모드로 efax 사용하기
10-13. 간단한 while 루프
10-14. 다른 while 루프
10-15. 다중 조건 while 루프
10-16. C 형태의 문법을 쓰는 while 루프
10-17. until 루프
10-18. 중첩된 루프
10-19. 루프에서 breakcontinue의 영향
10-20. 여러 단계의 루프에서 탈출하기
10-21. 더 상위 루프 레벨에서 계속하기(continue)
10-22. case 쓰기
10-23. case로 메뉴 만들기
10-24. case용 변수를 만들기 위해서 명령어 치환 쓰기
10-25. 간단한 문자열 매칭
10-26. 입력이 알파벳인지 확인하기
10-27. select로 메뉴 만들기
10-28. 함수에서 select를 써서 메뉴 만들기
11-1. printf가 실제로 쓰이는 예제
11-2. read로 변수 할당하기
11-3. read로 여러줄의 입력 넣기
11-4. read파일 재지향과 같이 쓰기
11-5. 현재 작업 디렉토리 변경하기
11-6. let으로 몇 가지 산술 연산을 하기.
11-7. eval의 효과 보여주기
11-8. 강제로 로그 아웃 시키기
11-9. "rot13" 버전
11-10. 위치 매개변수와 set 쓰기
11-11. 변수를 "언셋"(unset) 하기
11-12. export를 써서, 내장된 awk 스크립트에 변수를 전달하기
11-13. getopts로 스크립트로 넘어온 옵션과 인자 읽기
11-14. 데이타 파일 "포함하기"
11-15. exec 효과
11-16. 작업을 계속 해 나가기 전에 프로세스가 끝나길 기다리기
12-1. CDR 디스크를 구울 때 ls로 목차 만들기
12-2. Badname, 파일 이름에 일반적이지 않은 문자나 공백 문자를 포함하는 파일을 지우기.
12-3. inode 로 파일을 지우기
12-4. 시스템 로그 모니터링용 xargs 로그 파일
12-5. copydir. xargs로 현재 디렉토리를 다른 곳으로 복사하기
12-6. expr 쓰기
12-7. date 쓰기
12-8. 스크립트에서 두 파일을 비교하기 위해 cmp 쓰기.
12-9. 낱말 빈도수 분석
12-10. 10자리 랜덤한 숫자 만들기
12-11. tail로 시스템 로그를 모니터하기
12-12. 스크립트에서 "grep"을 에뮬레이트 하기
12-13. 목록에 들어 있는 낱말들의 유효성 확인하기
12-14. toupper: 파일 내용을 모두 대문자로 바꿈.
12-15. lowercase: 현재 디렉토리의 모든 파일명을 소문자로 바꿈.
12-16. du: 도스용 텍스트 파일을 UNIX용으로 변환.
12-17. rot13: 초허접(ultra-weak) 암호화, rot13.
12-18. "Crypto-Quote" 퍼즐 만들기
12-19. 파일 목록 형식화.
12-20. column 으로 디렉토리 목록을 형식화 하기
12-21. nl: 자기 자신에게 번호를 붙이는 스크립트.
12-22. cpio로 디렉토리 트리 옮기기
12-23. rpm 아카이브 풀기
12-24. C 소스에서 주석을 제거하기
12-25. /usr/X11R6/bin 둘러보기
12-26. basenamedirname
12-27. 인코드된 파일을 uudecode하기
12-28. 저당에 대한 월 상환액(Monthly Payment on a Mortgage)
12-29. 진법 변환(Base Conversion)
12-30. 다른 방법으로 bc 실행
12-31. seq로 루프에 인자를 만들어 넣기
12-32. 키보드 입력을 갈무리하기
12-33. 파일을 안전하게 지우기
12-34. m4 쓰기
13-1. 지움 글자(erase character) 세팅하기
13-2. 비밀스런 비밀번호: 터미널 에코 끄기
13-3. 키누름 알아내기
13-4. pidof 로 프로세스를 죽이기
13-5. CD 이미지 확인하기
13-6. 한 파일에서 한번에 파일 시스템 만들기
13-7. 새 하드 드라이브 추가하기
13-8. killall, /etc/rc .d/init.d 에서 인용
16-1. exec으로 표준입력을 재지향 하기
16-2. 재지향된 while 루프
16-3. 다른 형태의 재지향된 while 루프
16-4. 재지향된 until 루프
16-5. 재지향된 for 루프
16-6. 재지향된 for 루프(표준입력, 표준출력 모두 재지향됨)
16-7. 재지향된 if/then 테스트
16-8. 이벤트 로깅하기
17-1. dummyfile: 두 줄짜리 더미 파일 만들기
17-2. broadcast: 로그인 해 있는 모든 사람들에게 메세지 보내기
17-3. cat으로 여러 줄의 메세지 만들기
17-4. 탭이 지워진 여러 줄의 메세지
17-5. Here document에서 매개변수 치환하기
17-6. 매개변수 치환 끄기
17-7. upload: "Sunsite" incoming 디렉토리에 파일 한 쌍을 업로드
17-8. "아무개"(anonymous) Here Document
20-1. 서브쉘에서 변수의 통용 범위(variable scope)
20-2. 사용자 프로파일 보기
20-3. 프로세스를 서브쉘에서 병렬로 돌리기
21-1. 제한된 모드로 스크립트 돌리기
23-1. 간단한 함수
23-2. 매개변수를 받는 함수
23-3. 두 숫자중 큰 수 찾기
23-4. 숫자를 로마 숫자로 바꾸기
23-5. 함수에서 큰 값을 리턴하는지 테스트하기
23-6. 큰 두 정수 비교하기
23-7. 사용자 계정 이름에서 실제 이름을 알아내기
23-8. 지역 변수의 영역(Local variable visibility)
23-9. 지역 변수를 쓴 재귀 함수
24-1. 스크립트에서 쓰이는 별칭(alias)
24-2. unalias: 별칭을 설정, 해제하기
25-1. "and list"를 써서 명령어줄 인자 확인하기
25-2. "and list"를 써서 명령어줄 인자를 확인하는 다른 방법
25-3. "or lists""and list"를 같이 쓰기
26-1. 간단한 배열 사용법
26-2. 배열의 특별한 특성 몇 가지
26-3. 빈 배열과 빈 원소
26-4. 아주 오래된 친구: 버블 정렬(Bubble Sort)
26-5. 복잡한 배열 어플리케이션: 에라토스테네스의 체(Sieve of Erastosthenes)
26-6. 복잡한 배열 어플리케이션: 기묘한 수학 급수 탐색(Exploring a weird mathematical series)
26-7. 2차원 배열을 흉내낸 다음, 기울이기(tilting it)
28-1. 특정 PID와 관련있는 프로세스 찾기
28-2. 온라인 연결 상태
29-1. 쿠키 항아리를 숨기기
29-2. /dev/zero로 스왑 파일 세팅하기
29-3. 램디스크 만들기
30-1. 버그 있는 스크립트
30-2. test24, 버그가 있는 다른 스크립트
30-3. "assert"로 조건을 테스트하기
30-4. exit 잡아채기(Trapping at exit)
30-5. Control-C 가 눌렸을 때 깨끗이 청소하기
30-6. 변수 추적하기
32-1. 서브쉘 함정(Subshell Pitfalls)
34-1. 쉘 래퍼(shell wrapper)
34-2. 조금 복잡한 쉘 래퍼(shell wapper)
34-3. awk 스크립트 쉘 래퍼(shell wrapper)
34-4. Bash 스크립트에 내장된 펄
34-5. 하나로 묶인 Bash 스크립트와 펄 스크립트
34-6. 자신을 재귀적으로 부르는 스크립트
35-1. 문자열 확장
35-2. 간접 변수 참조 - 새로운 방법
35-3. 배열과 약간의 트릭을 써서 한 벌의 카드를 4명에게 랜덤하게 돌리기
A-1. manview: 포맷된 맨 페이지를 보는 스크립트
A-2. mailformat: 이메일 메세지를 포맷해서 보기
A-3. rn: 간단한 파일이름 변경 유틸리티
A-4. encryptedpw: 로컬에 암호화 되어 있는 비밀번호로 ftp 사이트에 파일을 업로드하는 스크립트
A-5. copy-cd: 데이타 CD를 복사하는 스크립트
A-6. days-between: 두 날짜 사이의 차이를 계산해 주는 스크립트
A-7. behead: 메일과 뉴스 메세지 헤더를 제거해 주는 스크립트
A-8. ftpget: ftp에서 파일을 다운로드 해 주는 스크립트
A-9. password: 8 글자짜리 랜덤한 비밀번호 생성 스크립트
A-10. fifo: 네임드 파이프를 써서 매일 백업해 주는 스크립트
A-11. 나머지 연산자로 소수 생성하기
A-12. tree: 디렉토리 구조를 트리 형태로 보여주는 스크립트
A-13. 문자열 함수들: C 형태의 문자열 함수
A-14. 객체 지향 데이타 베이스
F-1. 샘플 .bashrc 파일
G-1. VIEWDATA.BAT: 도스용 배치 파일
G-2. viewdata.sh: VIEWDATA.BAT 의 스크립트 버전

출처 : http://wiki.kldp.org/HOWTO//html/Adv-Bash-Scr-HOWTO/index.html

반응형
최적화 툴입니다.
반응형

Mook

파이어폭스와 썬더버드, 모질라를 트레이에 넣을 수 있게 하는 확장 기능입니다.

트레이에 넣기
최소화 버튼을 오른쪽 클릭하거나 [도구]-[Minimize to Tray]를 선택, 혹은 Ctrl+Shift+M을 하면 트레이에 넣을 수 있습니다. 최소화 버튼을 왼쪽 클릭하여 트레이에 넣고 싶으면 확장 기능 설정에서 [Always minimize to tray]를 선택하면 됩니다. 그리고 최소화 버튼을 가운데 클릭하거나 Ctrl+오른쪽 클릭하면 열려 있는 창을 모두 트레이에 넣을 수 있습니다.

빠른 실행
모질라처럼 파이어폭스와 썬더버드의 창을 열지 않고 바로 트레이에 넣을 수 있습니다. 파이어폭스나 썬더버드를 실행할 때 -turbo를 추가로 입력하면 됩니다.

참고
- 윈도우즈에서만 사용할 수 있습니다.
- 프로그램이 설치된 폴더나 윈도우즈의 시스템 폴더에 mscvcr71.dll 파일이 있어야만 사용할 수 있습니다.


http://minimizetotray.mozdev.org/

반응형
OpenSource/Free Softwares for Windows
윈도우용 오픈소스/무료 소프트웨어

MS-Windows 에서 내가 사용하는 오픈소스 혹은 무료 소프트웨어들을 정리한다.
대부분 업무상 사용해도 문제되지 않는 것들이다.(일부 제외)

일반 도구

* VirtuaWin : 데스크탑 분할 프로그램. 모니터가 한 개인 상태에서 여러개의 창을 분류별로 각 데스크탑에 나눠 두면 작업 효율성이 향상된다. 유닉스 계열의 X-Window에 기본적으로 있는 기능인데, 윈도우에서 그것을 흉내낸 프로그램이다. 이것은 내가 개발을 위해서 거의 필수적으로 까는 프로그램이다.
* NcFTP : 콘솔기반 멀티 플랫폼 FTP 클라이언트
* Filezilla : GUI FTP/SFTP 클라이언트/서버
* PuTTY(한글판) : SSH/Telnet 클라이언트
* RocketDock : 아이콘 독. 화면 좌우/상하에 아이콘 독을 둘 수 있다.
* Windows XP PowerToys : 특히 Open command prompt here화 Tweak UI, Task Switch를 꼭 설치한다.
* AtNotes : 포스트 잇
* 빵집 : 압축 프로그램. Alz 지원.
* 7Zip : 압축 프로그램. 오픈 소스. 매우 뛰어난 압축률의 7z 지원.
* 다음 툴바 : 바이러스/스파이웨어 백신
* Speedswitch XP : WIndowsXP 노트북 CPU 속도 저절
* KMPlayer : 동영상 재생기
* Foobar 2000 : 음악 재생기
* eMule : P2P 공유
* Adobe Acrobat Reader : PDF 리더
* Nate On : 메신저
* Google Talk : 메신저
* XNews : Usenet 을 이용한 파일 다운로드. binnews.kornet.net
* 바닥 : 동영상 변환(iRiver H320 용)
* VirtualDub : 동영상 변환
* 꿀뷰2 : 그림과 만화 보기
* 오픈캡쳐 : 화면 캡쳐

사무/그래픽

* OpenOffice.org : 오픈 소스 오피스 스위트
* Paint .Net : 오픈 소스 PhotoShop
* photo WORKS : 이미지 편집 도구

웹 브라우저/EMail

* FireFox : 최강의 멀티 플랫폼 웹 브라우저
* Opera : 가볍고 빠르고 웹 표준을 아주 잘 준수하는 브라우저
* Thunder Bird : 모질라 재단의 이메일/뉴스 클라이언트

Database 관련

* SQLTools(한국) : 오라클 쿼리/관리 도구. 우리나라사람이 만든 것.
* SQLTools.Net(sf.net) : 오라클 쿼리/관리 도구. SF.net 에 있는 것. Oracle Client 필수.
* SQuirrel SQL : Java 기반 데이터베이스 관리도구. JDBC 드라이버가 있는 모든 DB.
* Aqua Data Studio : Java 기반 데이터베이스 관리도구. JDBC 드라이버가 있는 모든 DB. 상용이지만 개인 사용은 무료.

텍스트 편집기

* Vim : VI Improved. 멋진 멀티 플랫폼 에디터. 내가 주로 사용하는 에디터.
* Notepad++ : Windows 용 프로그래머의 에디터
* JEdit : Java 기반 프로그래머용 에디터. 매우 강력한 플러그인 기능 지원.

개발 도구

* Eclipse : Java IDE.
* NetBeans : Sun 이 만든 Eclipse의 경쟁자. NetBeans Profiler 등을 이용.
* JSwat : GUI 자바 디버거
* WinMerge : 텍스트 비교(Diff) 툴
* WinTail : 로그 파일을 볼 때 사용하는 tail 명령과 같은 기능. 매우 단순.
* Tail For Win32 : 다양한 로그 파일을 한꺼번에 등록해서 볼 수 있는 tail 기능.
* Follow : Java로 만들어진 tail. 여러 파일 처리 가능.
* Tortoise CVS : CVS 클라이언트
* Tortoise SVN : Subversion 클라이언트
* Cygwin : MS-Windows 에서 Unix 환경 구축
* IE용 DevToolbar
반응형
CVS를 이용한 프로젝트 관리
반응형

 SCJP 자격증을 취득하고 싶은데요. SCJP 자격증 취득 방법에 대해서 알려주세요.

자바는 3가지 계열로 볼 수 있습니다.

J2SE, J2EE, J2ME...

J2SE 계열은->개발자 계열이며,

J2EE 계열은-> 웹 어플리케이션 계역

J2ME 계열은->모바일 계열로 생각하시면 됩니다.

물론 이에 따라 각 각의 자격증이  존재합니다.

자바 자격증중 가장 기본이 되는 자격증은 SCJP자격증 입니다.

SCJP란?

SCJP 자격증은 J2SE (Java 2 Platform, Standard Edition) 기반의 Java Programming 언어에 대한 기본적인 지식을 기반으로 Java의 능률성을 실행하는데 관심이 있는 프로그래머들을 위한 자격증입니다.

자바 프로그래머라면 기본적으로 본 자격증을 취득하시기를 권장해 드립니다.


J2SE   -> SCJD

SCJD는 자바 개발자로서의 능력을 검증해 주는 국제공인 자격증으로 웹프로그래머나 기업자바개발자(EJB) 등 다양한 유형으로 경력을 쌓을 수 있는 보증수표라 할 수 있습니다
 

SCJD를 취득하기 위해서는 크게 2가지의 시험을 치뤄야 합니다.
우선 프로그래밍 과정(Programming Assignment)을 거쳐야 합니다.

이는 자바 전반에 관한 지식과 이해력을 바탕으로 직접 프로그래밍해 업 로드하는 방식으로 치뤄지며, 프로그램 디자인의 분석 능력과 실무 능력을 검증한다.
자바 실무능력 검증에 주안점을 둬 데이터베이스(DB)와 네트워크에 관련된 기술과의 연관성도 측정한다.

두 번째는 필기 시험으로 본인이 구성한 프로그램을 바탕으로 이론과 실무에 대한 종합적인 자바 개발능력을 검증한다.


J2ME   -> SCMAD

SCMAD는 J2ME 어플리케이션을 개발하는 Programmer와 Developer를 위한 시험으로서, CLDC (Connected, Limited Device Configuration) 1.0 및 1.1, MIDP(Mobile Information Device Profile) 2.0 스펙을 기반으로 하고 있으며, WMA(Wireless Messaging Application) API(어플리케이션 프로그래밍 인터페이스)와 MMAPI(Mobile Media API) 등 ‘무선 산업을 위한 자바 기술(JTWI)’이나 기타 무선 자바 기술들을 능숙하게 사용하고 있는지를 객관적으로 검증해주는 시험입니다.


J2EE   ---> SCWCD, SCBCD


SCWCD란?

SCWCD 자격증은 Java Technology Servlet과 JSP(JavaServer Pages) APIs(Application Program Interface) 웹 어플리케이션을 사용하는 Java 2 Platform 개발자들의 능력을 검증하기 위한 자격증입니다.

이에 따라 본 자격증은 Java Servlet과 JSP APIs를 이용하여 Web-tier를 프로그래밍하는 능력을 검증하여 기술 경쟁력을 제공합니다.

이 자격시험에 응시하기 위해서 반드시 Java 2 Platform(버전무관)의 썬 공인 프로그래머 자격(SCJP)을 취득한 상태여야 한다. 시험에 응시하기 전에 웹 어플리케이션 개발을 위해 Servlet과 JSP (JavaServer Pages)를 사용하여 최소 6개월 이상의 개발경험이 있는 개발자들에게 추천해 드립니다.

SCBCD란? 

SCBCD (Sun Certified Business Component Developer for the Java 2 Platform, Enterprise Edition 1.3) 자격증 시험은 EJB (Enterprise JavaBeans) 어플리케이션을 설계, 개발, 테스트, 구현 및 통합하는 업무를 담당하는 프로그래머나 개발자들을 위한 자격증 시험입니다.

또한, J2EE 플랫폼 (Java 2 Platform, Enterprise Edition) 기술을 도입하여 어플리케이션의 비즈니스 로직을 캡슐화하는 서버 사이드 컴포넌트를 개발하는 전문성을 가진 개발자들을 위한 자격증 시험입니다.

SCBCD 자격증 응시를 위한 선수조건은 반드시 SCJP를 취득하셔야 합니다.


SCEA란?

SCEA는 높은 확장성과 유연성, 보안성을 자랑하는 Compliant applications인 J2EE Technology (Java 2 Platform, Enterprise Edition)를 설계하고 디자인하는 업무를 담당하는 엔터프라이즈 설계자들을 위한 자격증입니다.

본 자격증은 1단계 객관식 시험과 2단계 설계 및 디자인 프로젝트 프로그래밍 시험, 그리고 3단계 논술시험으로 구성되어 있으며 3단계까지 Pass 하셔야 자격인증이 부여됩니다.

시험은 프로메트릭 센터에서 보실수 있으며,

집에서 가까운 프로메트릭 센터를 검색하셔서 시간과 날짜를 정하시면 됩니다.

응시료는 바우처값이 20만원으로 바우처를 구입하신 후 응시하시면 됩니다.

혹시라도 도움이 되실까 해서 자바 실무강의 및 자격증 대비 교육도 진행되고,

시험도 학원자체에서 볼수 있는 센터 남겨드리니 알아보시는데 참고하세요^^


자바 강의 진행 및 프로메트릭 시험센터-> http://www.webwill.co.kr

 

re: SCJP 자격증을 취득하고 싶은데요. SCJP 자격증 취득 방법에 대해서 알려주세요.

my_lady6 (2007-10-18 10:03 작성)

신고

1.자바의 종류는 무엇 무엇인가요 ?


자바의 종류는 크게 기업형(Enterprise), 표준형(Standard), 모바일형(Mobile) 형태로 각각의 목적에 따라 세가지로 나누고 다른점은 간단하게 본다면 지원되는 기능의 많고 적음정도로 보시면 됩니다.


다르게는 자바라고 보기는 그렇지만 자바를 개발하기위한 도구로 회사별로 자바가 있다고 생각할 수 있습니다. 이것들은 위의 기본적인 자바의 기능에 회사별로 특별한 기능들이 더 추가되어있다고 보면 됩니다.


Microsoft 사의 Visaul J++, Sybase 사의 PowerJ, Borland 사의 Jbuilder, 어디회사진 잘 모르겠지만 Visal CAFE라는 것들이 있습니다.

 

2.저는 자바,c언어c++ 를배우고 싶습니다.. 그러나 보지도 못하고,알지도 못합니다..

 

일단 한가지를 먼저 배우시기 바랍니다.

우선 순서는 C-> JAVA->JSP->EJB 이렇게 배우시는게 도움이 되실것 같습니다.

 

다만, 한언어를 이해하기 위해서 책 한권을 사서 거기에 있는 내용을 10번쯤 처음부터 끝까지 의문을 가지지 말고 책에서 실습해보라는 내용은 다 해보시기 바랍니다.(조금은 지루해질수 있습니다.) 예전에 했던 실습이라고 하지 않으면 효과가 없습니다.

 

프로그래밍은 반복입니다.  자신의 손에서 익숙해질 때까지 해보다 보면 대략적으로 이해를 할 수 있습니다. 일부(저를 포함해서)사람들은 프로그래밍을 머리로 50%, 손으로 50%를 공부한다고 말하곤 합니다.

 

3.알지도 못하는데 학원 다닐 여건이 안돼어 독학만으로도 가능할까요?

 

위에 말하고 중복이 되겠네요. 학원을 안다녀도 가능합니다.

다만 학원다닌것 보다는 시간이 더 걸릴수 있겠지요.  독학은 가능합니다.

초기에 프로그래밍 언어를 배운 사람들은 다 독학을 했다는점이 그런 증명인 셈이죠~!

(^^; 물론 혼자하는건 무척이나 힘든일이기는 합니다. 도와주는 사람이 없다면 학원을 다니시는것 또한 추천할 만한 내용이죠 ... ) 

 

4.저런 종류들의 프로그램을 알고싶다면 추가할 목록은 머가있나요

 

프로그래밍 언어는 단순히 컴퓨터에게 어떤 일을 시키기 위해서 컴퓨터가 알아들을수 있는 말을 만들기 위한 수단에 불과합니다.

어떻게 컴퓨터에게 효과적이고 제대로 일을 시킬것인가 하는 방법에 대한것들을 배워야 합니다.

그래서 대학에서는 이런것을 프로그래밍 언어를 배우기전에 배웁니다.

 

- 운영체제(윈도우XP같은것들을 이렇게 부릅니다.)개론

- 자료구조론

- 컴퓨터 알고리즘

- 이산수학

- 프로그래밍 언어론

- 확률/통계학

 

그리고 부가적으로 어떤일을 하느냐에 따라 필요한 내용은

 

-  데이터베이스 개론

- 시스템 프로그래밍

- 네트워크 개론

 

등이 있습니다. (중학생이 이해하기에는 말들이 다소 어렵지만 그래도 다 필요한것들입니다.)

 

5.위에 프로그램들을 알때, 소스가 알수 없는 영어와 숫자로 돼어있던데요

함수고 뭐고 모르는데 가능할까요.. 쉽게말하면 수학을 전혀 모릅니다

 

무엇을 위해 프로그래밍 언어를 배워야 하는지를 그 목적을 먼저 알  필요가 있습니다.

수학을 전혀 모른다면 문제는 되겠지만 프로그래밍 언어에서 말하는 함수는 어떤 특정한 일을 하는 기능을 부르는 말이라고 보셔야 합니다. (예를 들면, ClearSrc()라는 함수 있다면 이 함수는 화면을 지우는 기능을 한다..라는 식으로 말이죠)

 X=y+z와 같은 식을 수학에서는 함수라고 하지만요.

 

그리고 소스내용은 기계가 이해할 수 있는 말들을 만들기 위해서 프로그래밍 언어별로 약간씩은 다른 표현으로 말하는 방법을 적어놓은 것입니다.

따라서 우리가 영어를 하기 위해서 영어를 배워야 하듯이 기계하고 말을 하려면 기계와 말을 할 수 있는 언어를 배워야 합니다. 

 

순수하게 기계언어는 0과 1로되어있습니다만 사람도 그런 언어를 이해하기 힘들기때문에 프로그래밍 언어가 사람들이 이해하기 편한 영어형태로 언어를 제공하고 있습니다.

하지만 영어는 아닌거죠.  따라서 이 언어도 배워야 써먹을 수 있습니다.


6. JAVA전문교육기관 바로가기

 

위에서 말한것 처럼 책만으로 독학이 가능합니다. 허나 시간이 많이 걸립니다.

해서 단기간에 전문인으로 거듭 나려면 학원을 다니는것도 하나의 지름길입니다.

프로그래머로 출발하려면 바로가기 클릭하세요

 

7. 자격증??

 

자격증은 특별히 C언어 자격증 이런식으로는 되어있지 않습니다.

정보처리기능사/산업기사/기사에서 C언어로 시험을 치는 방법은 있지만...

자바는 다음 자격증으로 구분되어있습니다. 프로그래밍 방법이나 능력에 대한것입니다.

 SCJP

SCMAD

SCJD

SCWCD

SCBCD

SCEA
SCDJWS


부가적으로 프로그래밍 언어를 배우시려하시니 적겠습니다.

이건 안날아갔으면 좋겠네요.


먼저 프로그래밍 언어를 하시려면 왜 프로그래밍 언어를 배우려는지 그 목적을 명확하게 할 필요가 있습니다.

막연히 누군가가 한다고 해서 아니면 하면 좋다고 해서 무작정 해봐야지 한다면 후회하게 됩니다. 어려운 길입니다.  프로그래밍을 한다는 것은 말이죠.

학원에서도 프로그래밍 언어를 가르칠 때 기본적인 내용을 가르치면서도 한가지 프로그래밍 목적에 맞게 가르칩니다.

예를들면 채팅프로그램을 만든다면 채팅프로그램을 만들기 위한 기능위주로 프로그래밍 언어를 가르치게 되는거지요. (모든 프로그래밍 언어의 기능을 가르치지는 않습니다.)


그러니 우선적으로 프로그래밍 언어를 하시려면 목적을 명확하게 가지시는게 중요합니다.  그리고 제가 부가적으로 적어놓은 개론들은 실제로 그 공부를 하고 있을 때는 왜 이런걸 공부해야 하는지 모두가 의문을 가지는 것들입니다.


나중에 프로그래밍을 하다보면 왜 했어야 했는지 이해가 되는 지식들입니다.

열심히 공부하시고 단기간내에 승부를 내려고 하지 마시고 적어도 3년에서 6년정도는 공부해야지 하는 인내심을 가지고 하시기 바랍니다.


아마도 대학을 가시거나 성인이 되는 나이시점에는 무엇인가가 되어계실것 같네요.

(그리고 프로그래밍 언어를 배우신 후 대충 1~2년 정도만 자신의 프로그램을 짜다보면 일상적인 대부분의 프로그램을 경험하실 수 있으실겁니다.)


만약 혼자 공부 하기가 힘이 든다면  자바전문교육원 에서 상담받아보세요

친절히 안내해 줄겁니다.


도움이 되길 바랍니다.

re: SCJP 자격증을 취득하고 싶은데요. SCJP 자격증 취득 방법에 대해서 알려주세요.

ausakstp5 (2007-10-18 15:46 작성)

신고

자바교육의 경우 실무교육은 기본입니다.

물론 자격증취득만을 희망한다면 덤프를 보고 딸수도 있겠죠...

하지만~ 나중에 취업을 하시거나 실무에서 응용하실때 다시 배우셔야 합니다.

지금이든~ 나중이든~~ 어차피 학원교육은 받으셔야 된다는거죵...

전문학원을 기준으로 봤을때... 취업까지 생각하신다면 보통 4개월에서 5개월정도는 배우셔야 합니다.

자바 기본문법부터 JSP, EJB, PROJECT까지요~

각각 한달정도씩으로 생각하시면 되는대요~ EJB와 프로젝트가 난이도가 좀 있으니...

한달정도씩을 더 생각하셔도 될거 같습니다.
 


자격증시험 난이도는 덤프만 본다면 무난하게 합격하실 수 있습니다.


자격증취득은 덤프보고 준비하시구요.... 교육은  실무교육을 받으세요..

SCJP자격증의 경우 자바 기본문법과정에 대한 인증시험이구요~

실무능력,,, 프로젝트까지 생각하신다면,, JSP,EJB까지는

공부를 하셔야 합니다.

물론 그 두 과정역시 자격증이 있구요~~~~(SCWCD/SCBCD)


자격증 시험은 프로매트릭 시험센터에서 보실수가 있습니다.

시험날짜가 따로 정해진건 아니구요~ 원하시는 날짜에 예약하시고 보시면 됩니다.


자격증시험 가능하고 실무교육진행하는 학원 추천해드립니당...^^



☞ 자바(SCJP)전문학원 바로가기

http://www.webyahoo.co.kr/02_curri/curri_java.htm

re: SCJP 자격증을 취득하고 싶은데요. SCJP 자격증 취득 방법에 대해서 알려주세요.

woduddlgkgk (2007-10-22 19:02 작성)

신고

SCJP 자격증은 J2SE (Java 2 Platform, Standard Edition) 기반의 Java Programming 언어에
 대한 기본적인 지식을 기반으로 Java의 능률성을 실행하는데 관심이 있는
프로그래머들을 위한 자격증입니다.
 
Java 기술을 직접 개발한 썬 마이크로시스템즈에서 Java Programming 언어에 관련된 지식을
표준화된 방식으로 검증해 주는 시험이며, 또한 Java 기반의 모든 툴 개발에 필요한 기초
지식을 인정 받을 수 있는 자격증입니다.

자바 프로그래머라면 기본적으로 본 자격증을 취득하시기를 권장해 드립니다.
 
SCJP는 Java Programming 언어의 기본적인 syntax와 구조를 사용해 본 경험이 있는 프로그래머들
에게 적합한 시험으로 자바 프로그래머라면 반드시 취득하셔야
할 기본이 되는 자격증입니다.
 
시험요강은 아래와 같습니다.
 
 
 
 
반응형

 

1단계=======(3회 독파)


@연필(또는 샤프 펜슬)로 모르는 단어나 중요 단어(핵심어) 밑줄 긋기를 하며 책을 편하게 읽어나간다.
@책 내용이 이해되건 말건,외워지건 말건,아둥바둥하지 않고 그냥 가벼운 마음으로 물흐르듯 룰루랄라 앞으로 앞으로 전우의 시체를 넘고넘어 앞으로만 진격하라.
읽다가 이해가 되지 않는다고 뒤돌아가면,구약성서의 소돔과 고모라의 이야기처럼 "소금기둥"이 되리라.^^
@그래야 두뇌가 스트레스를 받지 않고 밑줄 친 핵심어들이 머리에 정리된다.
---재래식 공부법처럼 책 내용을 이해하려고 파고들었다간 3회는커녕 1회 독파에도 지치고만다는 것이다.
--1단계의 연반추 학습은 즐거운 마음으로 책 내용 전부가 자연스럽게 두뇌에 저장될 때까지 놀아 가면서 하는 단계이다.
@이렇게 세 번 읽는다.그 책이 수험서이건 교양서적이건 상관없다.
@연반추 학습의 3차례 독파 시간 재래식 공부법의 1차례 독파시간보다 짧다.
@책 내용을 머리 속에 자연스럽게 부드럽게 저장하는 과정이다.
@가장 중요한 주의사항-이해하려 들지 말라!!!(그러면 두뇌에 주름잡힌다-주름잡히면 머리가 피곤해져 정보 받기를 거부한다-바로 이 점이 연반추 학습의 포인트:두뇌학!!!)
@밑줄 세차례로 책은 밑줄긋기로 사태가 날 지경이 된다-이 밑줄핵심어가 자신도 모르게 두뇌에 깊이 새겨진다:두뇌학!!!!

 

2단계=========(2회 독파)


@형광펜으로 핵심어 중의 핵심어,즉 '진핵어'를 표시해나가며 책 읽는다."이 단계에서 책을 읽는 속도는 기존 공부법의 3~4배 더 빨라진다"고 한다.
@이 단계에선 웬만한 교양서적(시오노 나나미의 로마인이야기,유시민의 경제학카페 등)은 그냥 이해가 되고 머리 속에서 정리가 된다.
@고백컨대,필자는 아무리 좋아하는 책도 3차례 이상 읽은 적이 없다.공부 책은 꾸역꾸역 3번 가까이 보았지만,제발 빨리 끝나기를 학수고대하며 읽어내기에 바빴다.
@이렇게 형광펜으로 진핵어 표시하는 학습으로 책을 2차례 독파한다.
@하긴 이렇게 책을 읽으면 책 내용이 이해가 되지 않을래야 않을 수 없겠다 싶다:바로 이 점도 이 학습법의 포인트이다.재래식 방법으론 지쳐서 가지 못할 비포장 도로를,편하게 주행할 수 있도록 고속도로 포장도로를 놓은 셈이기 때문이다.학습에 대한 심리적 거부감을 대폭 줄여가며 10회 독파의 고속도로를 달리게 하는 것이다.

 

3단계======(5회 독파)


@연필로 밑줄 그은 핵심어와 형광펜으로 표시한 진핵어를 중심으로 책을 5차례 속독속해한다.
"이 단계가 되면 공부의 즐거움에 빠지며 책을 읽어나가는 속도가 얼마나 빨라지는지 스스로 놀라게 된다.만약 이 학습법을 체득한 뒤 자녀들에게 전수하면 공부하는 재미에 푹 빠지게 돼 스스로 학습이 가능해진다"고 한다.
@이렇게 책 한 권을 10회 독파하는 속도는 기존 공부법의 3회 독파 속도보다 더 빠르다고 한다.그런데 더욱 중요한 것은 재래식 공부법으론 3차례 독파조차 의지력이 강한 극소수만이 수행할 수 있는 끔찍한 일이지만,이 학습법으론 "웬만하면" 10차례 독파가 즐겁게 진행될 수 있다는 점이다.이 학습법의 핵심 포인트이다.
@이 단계에선 책 한권을 읽는 속도가 어마어마하게 빠르다.저자 임성룡씨는 "7~8회 독파를 넘어 9~10회가 되면 책 내용이 그 밑바닥 영양가까지 전부 머리에 정리되는 것을 느낄 수 있다.10회 독파면 웬만한 책은 몇시간이고 대중강연도 할 수 있다.완벽하게 그 책의 내용을 흡수했기 때문이다."고 말한다

반응형

경고문에 XXXX.dll 이라고 뜰 경우 아래파일에서 찾아서

해당폴더에 다운받으시면 됩니다.

dll-files downloads


반응형

자바 뉴스그룹/Java Community

  comp.lang.java.programmer

  국내

  han.comp.lang.java

  Java Lobby (A Java Developers' community)

 

국내 자바 사이트

  JavaCafe.or.kr 사이트

  yoonforh의 자바 묻고 답하기 게시판

  김덕태 님의 자바 1.1 한글 관련 내용

  한국 자바 개발자 컨퍼런스

  자바 문서 한글화 사이트(자바 문서 한글화에 참여할 뜻있는 분들을

                                    기다리고 있습니다.)

  김필호 님의 자바와 한글 게시판 (유료 자바 게시판이며 유료인만큼

  답변이 신속, 성실하게 올라옵니다.)

  김필호 님의 자바 Q&A 게시판

  강신동 님의 Korea Java Developer Connection

  지니에 흥미를 가지고 있는 그룹

  Whang's Java List Version 2.0

  Java FAQ - Korean Version

  광운대 자바랜드(JavaLand:자바네 동네)

  홍종진 님의    자바의 한글 처리 문제에 관한 리소스

  이경하 님의    자바 페이지

 

자바 온라인 매거진

  JavaWorld - IDG's magazine for the Java community

  Java Developer's Journal

  Javology: Contents

 

자바 리소스 디렉토리

   The Community Resource for Jini™ Technology

   Dr.Dobb's Java

   Java Directory of dmoz-open directory project  

   JavaShareWare.Com

   KPCB: Keiretsu

   Gamelan: Earthweb's Java Directory

   JARS Java Resource Listings

   artima.com - a resource for Java and Jini developers

   Jini Links

 

자바 기술

  Threads/Concurrency

      package util.concurrent (by Doug Lea)

      (Book) Concurrent Programming in Java

   JINI

      The JINI COMMUNITY

      Jan Newmarch's Guide to JINI Technologies(A tutorial)

      지니에 흥미를 가지고 있는 그룹(Jini Korea Interesting Group)

    JNDI

      Java(TM) Naming & Directory Interface (JNDI(TM))

    Internationalization

      Writing Global Programs

    RMI

      rmi-users.logfile by thread

    JNI

      Frequently Asked Questions - JavaTM 2 SDK v1.2 and JNI

    JDBC

     JDBC Frequently Asked Questions

     http://splash.javasoft.com/jdbc/jdbc.drivers.html

     George Reese's Java Pages(JDBC-mSQL 홈페이지)

     The JDBC(tm) database access API.

     MsqlJava(mSQL.class) Home Page

     오러클 JDBC 드라이버 다운로드

    자바 빈즈

      JavaBeans - Component APIs for Java

      Java Beans White Paper자바 빈즈 개발자를 위한 Coffee Grinder

      BeanContext Examples

      BeanContext Development Kit (BcDK) Homepage

      Java 1.2 Unleashed chapter 29 - Glasgow Developments

    자바 컴파일러

      IBM 사의 Jikes 컴파일러

      GCJ, The GNU Compiler fo Java Programming Language

    자바 역컴파일러

      Jad - the fast JAva Decompiler

      IceBreaker : A "Visual" Java decompiler/disassembler

      Mocha, the Java Decompiler

    자바 C 번역기

      Toba: A Java-to-C Translator

    오피스 웨어

       ThinkFree Office(trying to be MS Office compatible)

       로터스 e_Suite

       Download Marimba Software

       Unixoid Shell of Java(자바 셸)

     메일 프로그램

       피카소 메일

     자바 파운데이션 클래스 - Swing

       Java Foundation Classes

       Swing by Matthew Robinson and Pavel Vorobiev -

       Netscape IFC: Overview

       Netscape DevEdge - IFC

       마이크로소프트의 자바 사이트(AFC)

     자바 Plugins

       Java Plug-in Product Homepage

       Java Plug-in 1.2 Software FAQ

       JAVA PLUG-IN SCRIPTING (자바 플러그인을 스크립트와 연동)

     자바 Collections API

       Download JDK 1.1 Collections Package

     Reference objects

       a Javaworld article

       Reference Objects and Garbage Collection

       Package summary of java.lang.ref

       Chapter 9 of Inside the Java 2 VM Garbage Collection

    자바 개발 환경

        시맨텍 까페

        비주얼 까페 팁 페이지

        시맨텍 비주얼 까페

        JBuilder Home page

        썬 자바 워크숍

        넷빈즈

        JDE - Java Development Environment for Emacs

       

        마이크로소프트 (비주얼 J++/C++)

        Visual J++ Web site

        Using MSVC for Java

        Using JDK1.1 in Visual J++

        Microsoft SDK for Java Download

    자바 CGI

       Java CGI HOWTO: Executing a Java CGI Program

       Java CGI HOWTO

    자바 웹 서버/JSP/서블릿

       Jeeves Home Page

       The JSDK-Apache Project

       The JavaServer Pages

       The Jakarta Project(tomcat)
       
Tomcat을 Apache 서버에 연결시키는 HOWTO

       GNUJSP

       allaire 사의 JRun

       Java Servlet API(Sun)

       Hrl's Servlet Express

       New Atalanta Comm.'s ServletExec

       The X- Jeeves Server and Servlets FAQ

    Security

       Frequently Asked Questions - Applet Security

       Signed Applet Example (JDK 1.1.x)

       아래의 빈번한 질문과 답 사이트에 좀더 많은 정보가 있습니다.

    Performance / Benchmark

       Java Performance Tuning

       Jack Shirazi's Java Performance Tuning website

       Java Benchmark

       Volano Report

       Java Versus C/C++ Benchmarks

       Java Memory Management Performance

    자바 버그

       The Unofficial Java Spec Report

    XML 관련

        REC-DOM-Level-1-19981001 Java Language Binding

        Working with XML - The JavaTM Api for XML Parsing (JAXP)

    UML

        OMG UML 1.3 specification

        UML 툴

        플라스틱 소프트웨어(국산)

        Together/J(free whiteboard version avail.)

        Rational Rose

        Argo/UML(free and open source)

 

 자바 튜토리얼

   JDC Training and Tutorials

   Server-side computing with XML, , JDBC, Swing and Servlets

   JRE Usage Example

   Programming in Java - A Tutorial by David Mitchell

   자바 1.1 이벤트 모델 요약 설명

   Java Documentation in Windows Help Format

  ->추천:윈도우 help 형식으로 된 자바 도큐먼트

   yoonforh의 자바 튜토리얼

   Trail Map: The Java Tutorial     추천:자바 공식 튜토리얼

   자바 클라이언트/서버 강의

   Be a Java Master with Examples

   The Java Developer: How Do I?

   Java Table of Contents by Richard G Baldwin

   넷스케이프의 ViewSource

   Enterprise JavaBeans Tutorial

 

자바 빈번한 질문과 답(FAQ)

  SUNSITE's JAVA Frequently Asked Questions

                                (FAQ list of newsgroup comp.lang.java)

   The Java(TM) FAQ  (a good FAQ also published as a book)

   자바 프로그램을 윈도우 NT의 서비스로 만드는 방법

   (1) 링크 모음

   (2) JNI 사용하여 구현한 예

    애플릿 서명 예제 - 자바 홈페이지의 보안 문제 페이지 참고

   (1) JDK 1.1.x의 경우

   (2) JDK 1.2.x의 경우

   넷스케이프 사의 서명 도구 페이지

   VeriSign 사의 객체 서명 FAQ

   익스플로러의 자바 서명(Trust-Based Security for Java)

   익스플로러의 자바 서명 FAQ

   Microsoft Java VM version check

 

자바 공개 소스

   Tim Macinta's Java Swapware

   Metrochat

   LazloFont

   How to access a RS-232 port from Java?

   JavaChat 1.0, a distributed Java applet

   Jim Buzbee's Hershey Font Page

   The Java Cup International Contest Winners Circle

   Simple Java Servers

   Java Distributed TCP/IP Message Handling System

   Radu Sion의 소스

   Rich Burridge Home Page

   Joerg Meissner - priv@te szene - JAVA(CreditRoll.java)

   Typewriter Applet

 

자바 포팅 이슈

   Java-Linux

   jGuru Java on Linux FAQ

   Platforms Supporting Java

   IBM 자바 개발 센터

   IBM's Java-related Technology

   KAFFE - A virtual machine to run Java(tm)* code

   alphaWorks

   OSF Java Program Page

   넷스케이프의 JDK 1.1.x 지원

 

썬의 자바 공식 페이지

   자바 홈페이지

   Documentation Index

   White Papers

   Using HotJava(tm)

   Java(tm) IDL

   Java API Overview

   Java: The Inside story

   Java Coding Conventions(자바 코딩 관례)

   Examples of Using the JDK 1.1 AWT

   Java Platform Debugger Architecture

   JIMI Software Development Kit - a class library for managing

 

자바 스크립트

   JavaScript Developer Central(DevEdge Online)

   JavaScript Documentation

   Microsoft Scripting Technologies(JScript and VBScript)

   WEBFX Javascript Examples

   JavaScript Library

   The JavaScript Index v2.0

   Squatt Java/JavaScript Net-Zine

   About Kyle's JavaScript Navigator Window

오픈지엘 - 오픈지엘 공식 사이트, 생각외로 뉴스가 잘 올라온다.
NeHe Tutorial - OpenGL Tutorial. 내용도 좋지만, 각 플랫폼, 언어별로 구현된 소스가 유용.
OpenGL man page - OpenGL MAN 페이지. 일종의 매뉴얼.
OpenGL Manual - SGI 의 OpenGL 1.2 매뉴얼
GL4Java - 자바를 위한 OpenGL
OpenGL Win32 Tutorial - OpenGL Win32 Toturial
GLUT3 - glut3 소스 및 다운로드.
glut for win32 - Glut for Win32 DLL Download
OpenGL Gamedev - OpenGL Gamedev FAQ & Mailing list
cannon_smash - OpenGL 로 만든 오픈소스 탁구게임
OpenGL FAQ - OpenGL FAQ
OpenGL Reference Manual - 온라인북
OpenGL Programming Guide - 온라인북
OpenGL 수퍼 바이블 - 온라인북
OpenGL 튜토리얼 링크 - OpenGL 튜토리얼 링크
OpenGL 게임개발 FAQ - OpenGL 게임개발 FAQ
GLVelocity - OpenGL 관련 소스 및 뉴스, 토론 등등
DelphiGL - Delphi 로 하는 OpenGL
Yindo - 차세대 웹 애플리케이션
Dip2K's OpenGL - 여러 GL 관련 예제가 있는 국내 OpenGL 관련 사이트
GLScene - OpenGL Solution for Delphi
FLTK - OpenGL 그래픽 유저인터페이스 라이브러리(LGPL)
Quesa - Apple Quick Draw 3D Open-source Implementation
GLdomain - 파티클, 그라비티 등에 관한 튜토리얼
게임튜토리얼 - 오픈지엘 튜토리얼 및 MP3 라이브러리, 쓰레드 예제 등등이 있다.
Delphi3D - 델파이와 OpenGL를 사용한 최신 그래픽 기술에 관한 정보
DirectX Dev - Developer-only Forum
DirectX8 FAQ - Microsoft DirectX 8 개발자 FAQ
DirectX 개발 - DirectX Developer Center (MSDN)
XBOX - Microsoft XBOX
Mr.Snow's column - DirectX8 튜토리얼
Gamedev DirectX - Gamedev.net's DirectX resources
DrunkenHyena - Direct3D 8.0 Tutorial
Meltdown2001 - DirectX Meltdown2001 presentation
ShaderStudio - Vertex/Pixel Shader 를 쉽게 쓸 수 있는 툴.
Pixel/Vertex Shader - 톰스하드웨어의 픽셀/버텍스 쉐이더에 대한 간단한 정리
GDC2002-DirectX - Game Developer's Conference 2002 - MS DirectX Tutorial
STEEL 프로그래밍 리소스 - 200여개의 그래픽 관련 문서 및 튜토리얼 모음
그래픽스문서찾기 - 다운로드는 안되고 문서명만 검색되네.
파일포맷 - 모든 파일 포맷에 대해서.
게임프로그래밍뉴스그룹 - comp.games.development.progrmming.algorithms
GPGStudy.com - Game Programming Gems 스터디 사이트
KGDA - 한국 게임 개발자 협의회
FlipCode - 게임개발관련 뉴스와 자료들
GameDev.Net - game developer network
가마수트라 - game developer webzine.
nVidia Developer - nVidia 사의 개발자 네트워크
ATI Developer - ATI 사의 개발자 네트워크
Matrox Developer - 매트록스 사의 개발자 네트워크
게임-디벨로퍼 - 게임 개발 관련 자료 검색 전용
쿼터니온 - 가마수트라의 쿼터니온 설명
문서들 - 가마수트라의 프로그래밍 관련 문서
gdconf - 게임 디벨로퍼 컨퍼런스
디벨로퍼 코너 - 개발자들이 떠드는 곳
인텔 - 개발자 네트워크 게임부문
게임도큐먼트 - 심형근님의 게임개발 관련 각종 문서들
게임튜토리얼 - 게임관련 튜토리얼, 문서, 토론 등등
씽크존 - 가상 온라인 월드 개발자 네트워크
Genesis3D - 소스가 공개된 게임엔진. 유명.
Jet3D - Genesis3D를 기반으로 하는 차기 버전(소스공개)
타이탄프로젝트 - 퀘이크3의 맵을 읽는 엔진(소스공개)
AfterShock - GPL 퀘이크 렌더러
PolyCount - 퀘이크3/2, 언리얼, 하프라이프 등의 모델데이타.
소스포지의 3D 파운드리 - 소스포지의 공개된 여러 3D 관련 프로젝트 및 컨텐츠
3D Engine List - 3D Engine List - 우와 많다.
DDG Toolkit - Digital Dawn Graphics Toolkit (C++ OpenGL Terrain Toolkit)
OpenFX - 오픈소스 공개 모델러, 렌더러, 애니메이터(게임엔진아님)
FLY3D - FLY3D 게임 엔진. 책도 있음.
Crystal Space - 크리스탈 스페이스-멀티플랫폼 3D 게임 엔진
매직소프트웨어 - 3D Game Engine Design 이라는 책 소스 및 유용한 공개코드
Nevrax - GPL 게임 엔진(AI, 3D, 네트웍 포함. 베이어패치 및 아웃도어용)
OGRE - 객체지향 그래픽 렌더링 엔진(베지어패치 및 퀘이크 렌더러 등)
RadonLabs - Nebula Device 라는 공개엔진. Nomads 라는 게임에 사용.
NeoEngine - 네오엔진. 멀티 플랫폼 LGPL 게임엔진
크리스 헤커 - 크리스 헤커의 게임물리학 및 텍스쳐매핑관련 PDF 문서가 있다.
피터 린드스트롬 - 꽤 읽어봄직한 논문들
UNC GEOM - UNC 의 물리시뮬레이션, 기하학관련 리서치 그룹
casManG's 3D 게임 아트 - 3D 게임관련 모델링 튜토리얼 자료 많다.
ingava - Q3A Prgramming/Modeling Tutorial
Motion - 캐릭터 애니메이션에 관한 자료 및 문서
MathEngine - 실시간 물리엔진, 데모
Art Of Assembly - Art Of Assembly : 어셈블리 책 - 웹에 책 내용 다 있음.
게임인공지능 - 게임관련 인공지능
블랙북 - 마이클 애브래쉬의 블랙북 PDF
게임 모델링 툴 링크 - 모델링 툴, 스키닝 툴, 튜토리얼 및 리소스 링크
Creative - 크리에이티브사의 게임 개발 관련 자료
Amit's Link - Amit's Game Programming Information
리스텍 - 리스텍(lithtech) 게임엔진
NetImmerse - NDL사의 NetImmerse 엔진
Audiere - 오디에르. ogg, wav, mod, s3m 등을 지원하는 오디오 라이브러리(LGPL)
GDZine - 게임 개발자를 위한 웹진
Torque엔진 - Tribes 2 에 쓰였다는 Torque 게임 엔진. 개발자 당 $100 이란다.
HighEnd3D - 하이엔드 쓰리디. 마야, XSI, Shake 커뮤니티
씨쥐링크 - 컴퓨터 그래픽 관련
3D Links - 3D 관련 링크 및 뉴스
디지탈 프로듀서 - 디지털 영상 편집 및 3D 관련 웹진. 공짜 3D 모델도 있음.
아발론.뷰포인트 - 뷰포인트사의 공개 모델링 데이타들이 있는 곳.아발론
디지탈에이젼트 - 실사와 비슷한 3D 캐릭터가 말을 한다.(국내)
모델뱅크 - 뷰포인트사의 모델뱅크-상용
Steven Stahlberg 갤러리 - 여자 3D 캐릭터 죽~인다
3D Artisan - 국내 3D 관련 잡지
드림모션 - 국내 모션캡쳐 장비 회사
OpenEYE - 3DS MAX 사용자 그룹
3D 캐릭터 - 나잘난박사의 이상호님 홈
더-포즈 - Poser 애니메이션을 판다.
포져 - 3D 인체 포즈 및 애니메이션 툴. 훌륭...짝짝짝.
3D필름메이커 - 3D 영상 및 컨텐츠 개발 관련
밀크쉐이프3D - 쉐어웨어 로우 메쉬 에디터, 애니메이터(하프-라이프모델 지원빵빵)
안드로이드월드 - 안드로이드 관련 정보
소다플레이 - 마치 애완동물을 괴롭히는 것 같은...
메타모션 - 실시간 모션캡쳐 시스템
제니류님의 홈 - 마야 3D 케릭터 모델링과 애니메이션
CGLand - 다수의 CG 갤러리 및 링크
Digital Art Zone - Poser Map & Model 파는 곳
Motion Analysis - Motion Analysis : Motion Capture System/Software
Zygote - 상용 3D 모델 데이타
Famous3D - Facial motion-capture 솔루션
3D Artists - 세계적인 3D Artists 들의 작품 감상 및 포럼
소아나라 - 3D 아티스트. 실사와 거의 흡사한 이미지. 국산.
Kludge3D - 3D Modeling Tool (LGPL)
니트정보통신 - FilmBox, Toonz, Vicon 등의 디스트리뷰터
GMAX - 게임 MOD 개발용 gmax (공짜)
HCIKorea - 인간과 컴퓨터 상호작용 연구회
JoyCG - 여러 아티스트들의 3D 작품을 구경하고 싶을 때
KBS 기술연구소 - 방송에 관계된 여러 기술 정보
모캡코리아 - 모션캡쳐관련 서비스
Terragen - 공개용 지형 생성 툴
두모션 - 국산 기계식 모션캡쳐 시스템
Quest3D - 실시간 3D 멀티미디어 툴
소프트 쉐도우 - Michael Herf & Paul Heckbert 의 소프트쉐도우에 관한 페이지
쉐도우 - 쉐도우에 관한 여러가지 자료
Jeff Lander - Jeff Lander의 게임개발관련 문서
리얼타임렌더링 - 리얼타임렌더링책선전 및 각종 링크
Math3D - The 3D Computer Graphics Math Library Homepage
New York Univ. - New York Univ. Media Research Lab
Red3D - Non-photorealistic Rendering Links
쿼터니온 - 쿼터니온링크
SIGGRAPH PROCEEDINGS - ACM Digital Library(SIGGRAPH Proceedings Listing)
WhyQuat - Do you really need quaternion?
Nurbs++ - C++ Nurbs Library
매직소프트웨어링크 - 매직소프트웨어의 그래픽관련 링크
gdconf 2000 - Game Devel. Conf. 2000 procedings
gdconf 2001 - Game Devel. Conf. 2001 procedings
CFXWEB - 데모와 게임 개발에 관한 문서, 소스 및 갤러리
I_COLLIDE - 인터액티브하며 정확한 충돌 감지 라이브러리
ACM JGT - Journal of Graphics Tools
RTRT - RealTime RayTracer
피에르의 링크 - 3D, 수학, 물리관련 각종 링크
GeometricModeling - On-Line Geometric Modeling Notes. 맘에드는 주제들.
Heriot-Watt - Heriot-Watt University Graphics Notes. 설명양호.
미시간주립대 - 미시간주립대 그래픽스 노트. 프레젠테이션
Don Lancaster's Guru's Lair - Cubic Spline Library (PDF 서비스)
Paul Bourke - Curve 및 그래픽스 관련 노트 및 C 소스코드
Journal of Graphics Tools - 실제 현장에 바로 응용할 수 있는 그래픽스 연구
RayTracing - RayTracing Tutorial
TomasMöller - Real Time Rendering 의 저자
CassEveritt - nVidia 사 개발자, Per-Pixel Lighting 관련 연구
DavidBarraff - 현 Pixar (전 CMU). 강체시뮬레이션관련
Jos Stam - Alias|Wavefrot 사 근무. FFT 에 기반한 간단한 Fluid Solver
efg's 링크 - 시뮬레이션과 모델링에 관한 링크모음
그래픽스젬스 - 차례 및 소스 다운로드
BOID - BOID(새들의 무리?) 관련 정보(인공지능)
NatureWizard - 그래픽으로 자연을 표현하기 위한 정보들
오픈넙스 - 공개용 넙스(NURBS) 파일 포맷
Jan Kautz - BRDF, Shadow Bumpmap, Realtime Bumpmap 등등
ColDet - 공개 충돌 검사 라이브러리
University of Leeds - 여러가지 그래픽스 관련 기본적인 튜토리얼.
스탠포드대학 - 컴퓨터 그래픽스를 위한 수학적 방법(웨이블릿, 몬테카를로...)
Water - Rendering and Animation of Liquid
인텔3D 문서 - 인텔의 3D 소프트웨어 개발 관련 문서들
GTS - GNU Triangulated Surface Library
LargeModels - 공짜로 다운받는 무지 큰 3D 모델 파일들.
GDAlgorithm - Game Development Algorithm 메일링 리스트
PocketMovies - PocketMovies
Projectmessiah - Projectmessiah
Anzovin Studio - Anzovin Studio
AtomFilims - AtomFilims
4CSCG - 컴퓨터 그래픽 아트 관련
PepeLand - Daniel Martinez Lara
M. Gleicher 의 홈페이지 - 캐릭터 애니메이션 수업 및 모션 에디팅 논문 자료
캐릭터 애니메이션 강의자료 - 위스콘신
Thalmann - Thalmann's Home
CG KAIST - KAIST COMPUTER GRAPHICS LAB
Motion Editing Lecture - KAIST Motion Editing & Manipulation Class
서울대 그래픽스 랩 - 서울대 대학원 그래픽스 & 미디어 랩
Postech VR lab - 포항공대 VR 랩
H-Anim - Humanoid Animation Working Group
Virtual Humans - ben's Virtual Humans Page
Bio Virtual - 3차원 얼굴 뷰어
모션캡쳐 - 모션캡쳐 리서치
한국생산기술연구원 - 메카트로닉스 연구실(로봇관련정보)
Robotics - Mathematical Methods for Robotics and Vision
Linear Algebra - Computation Methods in Linear Algebra
MotionEditing Web - M.Gleicher 의 모션에디팅 메일링리스트
애니큐브 - 자신의 아바타를 만든다.
Heloli.com - 멋진 일본 캐릭터 애니. 카툰렌더링. 치마펄럭. 머리약간 펄럭.
경사면 걷기 - 경사면에서 걷기동작 생성에 관한 논문
스프링인간 - 플래쉬로 만든 고무줄 꼭두각시
DX8스키닝 - DX8 버텍스 쉐이더에 대한 간단설명 및 스키닝 등에 대한 상세한 설명
게임데브넷 - Character Animation with DirectX 8.0
캐릭터애니관련책 - Programming Dynamic Character Animation
CoderCorner - Pierre Terdiman의 Realtime Cloth, Flexporter, Z-Collide 등등 구경
Advanced Character Physics - Thomas Jakobsen의 GDC 2001 강연자료
ESC - 가상 나이트클럽.
메타휴먼 - 메타휴먼? 재미있는 프로젝트들을 연구하는 그룹.
Cal3D - 공개 캐릭터 엔진
Watt - 3D게임, 실시간렌더링 및 캐릭터 애니 강연자료
Expression - 공개 페이셜 애니메이션 툴킷(TTS, 맥스익스포터, 스크립팅)
LinuxGames - 리눅스게임에 관한 뉴스
리눅스 게임 톰 - 리눅스 게임에 관한 많은 뉴우스들.
OpenAL - Loki 사의 오디오 라이브러리
SDL - 쓸만한 멀티플랫폼 멀티미디어 라이브러리
인드리마 오픈소스 프로젝트 - 인드리마 콘솔을 위한 오픈소스 프로젝트 네트워크
Broodcast2000 - 리눅스시스템을 위한 실시간, 비선형 오디오 비디오 편집기 (소스공개)
Inovation3D - 오픈소스 3D 모델링 툴, 키프레임 애니메이션과 본도 지원(간단하게...)
Linux Game Dev. in GDC2001 - Linux Game Dev. in GDC2001
리눅스3D - 리눅스 3D 게임 뉴스
리눅스 3D (org) - 리눅스 3D 뉴스
LinuxVideo - Linux Video and DVD Project
인드리마 개발자 네트워크 - 리눅스 게임 콘솔 인드리마 개발자 네트워크
g-Matrix - kaswan 님의 게임 프로그래머를 위한 3차원 엔진 만들기
울트라 감자 - 뜨거운 감자 김성수님의 개인 홈 - 3D 의 몇몇 강좌와 게시판이 있슴당.
ALTOZ - 인공생명을 연구하는 (주)오즈 인터미디어의 인공생명팀
허영준씨개인홈 - 캐릭터 애니메이션에 관한 자료
GTC - 성균관대학교 게임 기술 개발 지원 센터
RedPixel - ACE, STL, Lua, OpenGL 등에 대한 정보.
Kano - 실시간그래픽스에 관심있는 일본개발자. GPG 일본판 번역자
Ádám Moravánszky - ShaderX 에 Bump Mapped BRDF Rendering 파트집필
Thomas Jakobsen - Game Engine, Physics, Character Animation, Denmark
Pion - 파연님 개인홈. 게임 개발 및 번역.
Cass Everitte - nVidia 사에서 일하는 카스 에버릿. 몇몇 오픈지엘 예제
Hoppe - 메쉬 최적화 및 기타 유용한 자료들. MS 사 댕김.
Newtype - MaxScript, D3D 등등의 여러 개발관련 정보.
Kaliver - 이주형님 개인홈. ASE Viewer 및 Qu Engine
쥐그라운드 - 게임 그라운드. 게임에 관련된 여러가지 정보들.
브렌더 - 최고의 공개 3D 모델러/애니메이터
demonews - 3D 게임 데모 정보
DPlayer - 댄스 플레이어. 무지 보기 좋다..
게임스팟 - 게임스팝 - 게임관련웹진
게임디벨로퍼코리아 - 게임디벨로퍼지 한국판
뷰포인트 - 뷰포인트사의 웹3D (메타스트림)
iWorld3D - 국내 Web3D 개발업체
와일드 탄젠트 - Web3D, Web Streaming, ....
sumea - 환상적인 자바 3D 영상
WEB3D 콘소시움 - 웹3D 콘소시움
Blaxxun3D - X3D 포맷을 지원하는 자바로 만든 3D 클라이언트
펄스3D - 꽤 괜찮은 캐릭터 애니메이션
슈퍼스케이프 - 워크맨 등의 가전제품에 써먹을만한...질은 별루...
샤우트3D - 자바 기반으로 만들어짐. 툴킷도 제공
사이코어 - Cult3D, CultEffect, PuppetTime 등의 프로덕트
드림스케이프 - 국내 웹3D 관련
GIBLE3D - 국내 웹 3D 뷰어 개발 업체
QEDSoft - 3D 웹 에이전트
오락닷컴 - 실시간 스트리밍 3D 춤 강좌 서비스
디지털에이전트 - 3D 캐릭터가 채팅 내용을 따라 읽어주는 등...
리눅스사랑넷 - 리눅스사용자라면 꼭 가봐야 할 사이트
KLDP - 리눅스사용자라면 꼭 가봐야 할 사이트
리눅스시스템관리 - 리눅스 시스템 관리자를 위한 홈페이지
Debian-KR 메일링 리스트 - 데비안-KR 메일링 리스트 아카이브
Trinux - 디스켓 3장에 들어가는 리눅스
linux-firewall - linux-firewall
certcc.or.kr - 한국정보보호센터
쉘프로그래밍 - 쉘프로그래밍매뉴얼
SAINT - 보안분석툴
securityfocus.com - 보안관련정보
정규표현식설명 - 정규표현식에 대한 설명
Thinkpad Tool - Thinkpad Notebook Linux Configuration Tool
네트워크프로그래밍 - BeeJ's Guide to Network Programming
webalizer - 웹 로그 분석툴
MRTG - 서버 네트워크 통계 프로그램
VirtualPC - 윈도우에서 리눅스를 깔 수 있다는
IBM Linux - 한국 IBM의 리눅스 기술문서 번역 및 자료
Linux C - Linux C 프로그래밍에 관한
헤커되기 - 헤커가 되는 법(에릭.S.레이몬드)
데비안사용자 - 국내 데비안 사용자 그룹
리눅스 가제트 - 리눅스관련 웹진
프렉 - 헤킹관련 웹진
CygWin - GNU + Cygnus + Windows
GNU GPL FAQ - GPL 에 관련된 빈번한 질문과 답
phpBB - PHP, MySQL 로 만드는 커뮤니티. 디자인이 깔끔.
KTUG - Korea TeX Users Group
KLDP 닷넷 - 소스포지와 같은 국내의 오픈소스 프로젝트 서비스 제공
Emacs-KR 홈페이지 - 최고의 에디터인 Emacs 의 사용자 모임
Emacs 설치 - 이맥스 윈도우즈용/유닉스용 설치에 관한...
정재목씨의 Emacs - 정재목씨의 Emacs 관련 페이지
NT Emacs - Windows 95/98/NT 용 Emacs 설명
Elisp Manual - Emacs Lisp Programming
Elisp intro page - Elisp Introduction & link
Elisp Reference - Elisp Reference Manual
VisEmacs - Visual Studio 내장 에디터로 Emacs 를 사용하게 해 줌
Doxygen - C++ 개발 문서화 도구
HeaderDoc - C/C++ 헤더파일을 HTML 로 문서화(펄)
CVS Home - 버젼 컨트롤 시스템
RCS - GNU Revision Control System
CS-RCS - 윈도우용 RCS. 1인용은 공짜
ViewCVS - CVS 레파지터리를 웹상에서 보기
ActiveState - ActivePerl, ActiveTCL 등등
Cetus링크 - 프로그래밍관련 방대한 링크
ZipArchive - C++ Zip 압축 라이브러리(소스동봉, zlib 사용)
zlib - zip 라이브러리
Data Compression Lib - 데이터 압축 관련 정보(인덱스, 링크, 소스)
GNU Win32 - Win32 용 각종 GNU 라이브러리 소스(libjpeg, crypt, freetype, zlib 등등)
루아 - 엘레강트하고, 심플하며, 빠르고, 가벼우며, 확장성이 용이한 스크립트 언어
공개 컴파일러들 - 각종 언어에 대한 공개 컴파일러 목록
CGShaders - C for Graphics. 사용자그룹을 가장한 공식홈
UPX - 실행 프로그램 압축 유틸리티. 여러분의 프로그램이 작아집니다.
oggvorbis - 오그 보비스 플러그인, SDK 및 소스 다운로드
ACM - The ABCs of Writing C++ Classes
Guru of the Week - Guru of the Week
STL - Standard C++ Library Tutorial 한글
STL - Standard C++ Library (SGI)
STL - Visual C++ 의 STL Sample
C++ FAQ - C++ FAQ
MSJ - Microsoft Systems Journal
VC++ STL Reference - VC++ STL Reference
Thinking in C++ - Thinking in C++ 온라인 북
코드 구루 - 코드 샘플이 많은 개발관련 사이트
OpenIL - Open Image Library
Win32ASM - Iczelion's Win32 Assembly Homepage
Priority Que & STL - by Mark Nelson (Dr. Dobb's Journal)
STLPort - 범용, 공개, 오픈소스 STL
데이타 압축 - 데이타 압축 관련 링크 모음
C++ Optimize - C++ 최적화 방법에 대한 내용
STL newbie - STL 초보자를 위한 문서
코드프로젝트 - 다양한 장르의 프로그래밍 강좌
MTL - Matrix Template Library
몇몇책들 - Effective C++, More Effective C++, Design Patterns
CPlusPlus - C++ Tutorial
AssemRef - Assembler Programmer's Reference
공짜 C/C++ 컴파일러들 - 공개 C/C++ 컴파일러들에 대한 상세한 목록
어셈러브 - 국내 어셈블리 관련 홈페이지
C++ Online Books - C++ 관련 공짜 온라인 북 링크
STL Document - RogueWave Software 의 STL 튜토리얼 및 레퍼런스
Blitz++ - 객체지향 공학용 수치계산 라이브러리(C++)
행렬 라이브러리 비교 - C/C++ 용 행렬 라이브러리 비교평가
Math Fun Facts - 수학의 재미있는 사실들
AKROWNE's Home - 수학관련 유용한 정보(책 미러 많다)
Math of DFD - Mathematics of DFD(Discrete Fourier Transform)
그리스문자 읽는법 - 그리스 문자를 읽는 방법
Numerican Recipes in C - Numerican Recipes in C 온라인 북
Mech-World - 기계공학관련(동역학,수치해석) 강의노트 및 관련정보
Forgodot - 미적분학 원격 강의
NetLib - 수학관련 문서 및 소프트웨어 모음집
MathBook - Online Books and Lecture Notes in Mathematics
물리의 이해 - 경상대학교의 물리학 노트. 플래쉬 및 자바애플릿.
수학사랑 - 수학교사들의 연구단체
대한 수학회 - 대한민국 수학 학회.
PlanetMath - Wikipedia+MathWorld+Slashdot
MathWorld - 수학 백과 사전. 방대한 자료 상세한 설명.
GIF math - 각종 수학 식에 대한 GIF 이미지 모음
Physically Based Modeling - (Online Siggraph Course notes)
Rigid-Body Link - Rigid-Body Link
RigidTutorial - Rigid Body Simulation Tutorial
ODE - Open Dynamics Engine, 공개 다관절 강체 동역학 엔진
Gene's RealtyRoad - ODE 로 만든 사람 계단에서 굴러 떨어지기
Dynamo - Dynamic Motion library
3DPhysics - 3D Physics 관련 설명
옥스포드다이나믹스 - 자동차 시뮬레이션 라이브러리. (상용)
BuggyDemo - ODE 를 사용한 3D 자동차 시뮬레이션 데모. 소스 제공
TJ의 리소스 - Thomas Jakobsen의 인터액티브 물리 시뮬레이션 관련 리소스들
하복 - 하복(havok) 물리 엔진(상용)
Math 엔진 - 물리 엔진 까르마(Karma) (상용)
GamePhysics Book - David H. Eberly 의 게임 피직스 북. 아직 출간 안됨.
SOLID - 상용 3D 충돌 라이브러리. 개인사용은 공짜.
OpCode - 삐에르 테디맨의 공개 충돌 라이브러리. RAPID나 SOLID와 비슷한.
반응형

디버깅은 프로그래밍을 시작하는 사람이면 누구나 동시에 시작하는 작업입니다. 디버깅을 시작하는 사람들을 위해 프로그래밍을 시작하는 처음부터 어떤 태도를 가져야 할 지, 어떤 것을 알아야 할 지 알아보도록 하겠습니다. 대중적인 플랫폼을 크게 두 개로 보았을 때, 디버깅은 윈도우 계열과 유닉스 계열의 큰 차이는 없습니다. 다만, 그 툴이 현저히 달라서 두 계열 동시에 비슷한 기능을 하는 디버깅 툴을 소개한다는 것은 불가능한 일입니다. 하지만 프로그램을 작성할 때부터 디버깅을 염두에 두고 프로그램을 작성하는 면에서는 크게 다르지 않습니다. 이 글에서는 프로그램이 만들어지는 순간들을 살펴보면서, ‘디버깅을 위한 프로그래밍 습관’에 대해 모든 환경에서 주의해야할 디버깅 기법에 대해 정리하고자 합니다. 참고로, 필자는 유닉스 기반의 서버 프로그램을 작성 및 포팅하는 것을 전문으로 하고 있습니다. 디버깅을 너무 일반적으로 할 수는 없는 것이므로, 연재를 통틀어 C/C++를 기반으로 전개할 것입니다.

언어와 개발 환경
다음의 몇 가지 이야기는 디버깅을 잘하는 것은 올바른 배움의 자세에서 나온다는 것을 전제로 생각해 보기 위한 글입니다.
반쪽 프로그래머
프로그래밍을 처음 배우는 사람은 C++, 자바, HTML 등을 배우게 됩니다. 즉, 언어를 배우게 되는 것이죠. 여기에 디버깅을 생각하면, 참으로 뛰어넘기 어려운 커리큘럼의 한계에 부딪히게 됩니다. 배울 때는 언어만 배우면 될 것 같지만 언어의 문법을 익히는 것만으로는 50점입니다. 그와 쌍벽을 이루는 것은 환경이라 할 수 있습니다. 즉 OS, 프로토콜, 라이브러리 등을 말합니다. 실전에서 부딪히는 ‘디버깅의 문제’는 항상 언어와 환경에 대한 이해도를 동시에 측정하는 문제와 같습니다. 어느 하나만 물어보는 문제는 사실 그리 많지 않습니다. 문법을 익히는 것으로도 어려운 사람에게 OS와 원하는 환경에 대한 API들(소켓, DB, 멀티미디어, MAPI, IPC 등)을 익혀야 한다는 사실은 프로그래머의 길을 참으로 멀게 느껴지게 하는 요소가 됩니다. 하지만 진실을 알아야 제대로 길을 가겠지요.
창을 제어하는 것을 만들 때, MFC를 사용할 것이냐와 볼랜드의 OWL을 사용하느냐는 ‘라이브러리’의 문제가 됩니다. 물론 컴파일러와 동반된 라이브러리라는 점에서는 ‘컴파일러’의 문제일 수도 있습니다만, 좀더 멀리 윈도우와 유닉스 계열에 동시 사용되는 Qt 라이브러리를 사용하느냐, 유닉스 상에서 사용되는 구식의 Motif 라이브러리를 사용할 것이냐 등의 문제까지 확장한다면, 이들을 사용할 때의 언어는 C/C++를 통해 비슷한 문법을 사용하지만 ‘사용되는 OS 플랫폼’과 ‘라이브러리’의 문제가 됩니다.
중요한 것은 언급한 OS와 컴파일러 등에 따라 라이브러리를 선택해야만 하지만, 이들 라이브러리들이 만들어지게 된 동기나 UI 및 처리 방식 등은 비슷하다는 것입니다. OS, 컴파일러, 라이브러리는 상호 호환되지 않지만 시작과 끝은 비슷한 인터페이스와 구현 개념으로 시작해 C/C++라는 언어까지 비슷한 체계를 만들어 냅니다. 이런 유사성이 많은 라이브러리가 있다는 것은 알지만 그것을 공부하는 것은 어렵습니다. 그럴지라도 빠지지 말아야 할 오류 중의 하나는, 언어와 라이브러리의 정확한 경계를 파악하면서 배우자입니다.
흔히 잘못 알려진 예를 들어보기로 합시다. 웹 프로그래밍을 할 때, ASP는 사실 IIS에서 CGI를 잘 구현하기 위한 몇 가지 객체에 대한 정의입니다. 그 객체는 언어와 독립된 존재입니다. 따라서 VBScript는 ASP와 분리되어 생각해야 하지요. JScript로 ASP를 작성할 수도 있습니다. 그러나 ‘ASP로 만들었어’라는 말에는 ‘VBScript로 만들었어’를 함축하여 사용하게 됩니다. 언어와 환경을 분리해 공부하는 학습을 해 두는 것이 앞서 언급한 언어와 환경을 동시에 묻는 ‘디버깅 문제’를 잘 해결하는 지름길입니다.

언어와 표준 라이브러리
주로 C, C++를 배울 때 나오는 문제입니다. 사실 C 언어와 동시대에 만들어지고 살아남은 언어는 없다고 해도 과언이 아닐 정도입니다. C 언어는 그간 많은 정제 작업을 거쳐 표준화되었고, 어떤 플랫폼이 새로 나올 때 어셈블리 다음으로 가장 먼저 포팅이 되는 언어라 할 수 있습니다. 그만큼 언어를 통해 이루어 놓은 재원이 풍부하다는 것이지요. 요즘에 새로이 나오는 언어들은 대개 표준화된 라이브러리를 포함해 배포가 됩니다. 하지만 C는 표준화 작업에서 ‘C 라이브러리’라는 표준화된 라이브러리를 선택하게 되었고, 컴파일러는 표준 C 라이브러리와 OS 자체 라이브러리를 동시에 배포하게 됩니다. 그러다보니 처음 언어를 배우는 사람들이 문법과 라이브러리의 차이를 알면서 배우기란 참 힘듭니다.
for와 printf 예를 들어 봅시다. for는 C 언어를 이루는 구문이며, printf는 라이브러리 함수라는 큰 차이가 있습니다. 이러한 차이는 처리 관점에서 볼 때, for는 컴파일러가 해석해 루프 코드를 만들어 내고, printf에 대해서는 심볼을 찾아 호출(call)할 수 있는 방법으로 처리됩니다. 링커는 for에 대해서는 아무것도 하지 않으며, printf에 대해서는 외부 라이브러리에서 심볼을 찾아다가 점프 테이블을 갱신해 줍니다. printf는 C 언어 문법 명세에 있는 것이 아닙니다. 표준 라이브러리에 들어 있는 것입니다. sizeof는 함수인가요? 연산자입니다. 의심스러운 분은 찾아보기 바랍니다. printf의 구현은 DOS에서 다르고, MS 윈도우에서 다르고, 모바일 폰에서 다릅니다. 하지만 C 표준 문서는 printf의 선언에 대한 명확한 정의를 하고 있습니다. C가 놀라운 이식성을 가진다는 것은 플랫폼이 기본적으로 지원하는 언어이며, 지원시 표준 라이브러리 명세에 들어 있는 것을 해당 플랫폼에 맞게 구현해 놓았다는 것에 있습니다. 다시 한번 디버깅을 위한 기본 자세는 언어와 환경을 잘 구별하는 것에 있음을 강조하고 싶습니다.

#구문과 함수
#include
int main( void ) /* main : 라이브러리 함수 */
{
int i;
for( i=0; i<10; i++ )
{
printf("%d Hello, world? %d", i, sizeof( i ) );
} /* printf : 라이브러리 함수, sizeof : 연산자) */
return 0; /* return : 구문 */
}

요즘의 에디터는 함수와 구문/연산자에 대해 색깔을 다르게 표시해 줍니다. 구문 컬러링(syntax coloring)은 90년대 초반 볼랜드의 터보 C++ 이후로 프로그램 소스 에디터의 거의 필수적인 요소가 되어 있습니다. 유닉스에서도 Emacs와 vim을 쓰는 분들도 구문 컬러링을 위해 컬러가 지원되는 터미널 혹은 GUI 버전을 사용하는 것이 추세입니다. <리스트 1>을 보면, 구문 컬러링이 main에 대해서는 printf와 같이 하는 것을 보기도 할 텐데요. main은 단순한 콜백 함수일 뿐입니다. C 언어는 C start-up object가 있어서 OS에서 프로세스를 실행할 때 초기화하는 코드가 먼저 불려지고, 이 코드는 main이라는 외부 함수를 호출하게 되어 있습니다. 따라서 외부 프로그램은 항상 main부터 시작하게 되는 것이지요. 이것은 링커의 규약이 아니며, 단지 C start-up object에서 그것을 요구하기 때문일 뿐입니다. 링커는 C 언어와는 상관없이 객체간에 undefined symbol에 대해 다른 라이브러리나 객체에서 익스포트 심볼을 찾아 채워주는 일을 합니다.
<리스트 1>에서 sizeof를 잘 살펴보면, sizeof가 함수일 경우 그것은 링커에 의해 관심 대상이 될 것입니다. 하지만 sizeof는 컴파일 타임에서 그 값이 결정되는 단항 연산자이며, 결과는 상수입니다. 즉, 프로그램 중간에 바뀌지 않는다는 것이지요.


  OS 편애 금지
모름지기 프로그래머라면, 플랫폼과 언어 선택에 있어서 운신의 폭을 좁히는 것은 ‘깊은 이해’를 끊는 것이나 다름없습니다. 종종 비아냥 투로 이런 류의 얘기를 많이 듣습니다. “앞으로도 성공할 리 없는 리눅스는 관심 없어. 난 윈도우 프로그래머니까”, “툭하면 파란 화면 뜨는 것이 OS냐?” 이런 플랫폼 고착적인 자세는 프로그래머로서의 도리가 아닙니다. 프로그래머는 플랫폼을 가리지 말고 도전할 때 운용체계와 라이브러리에 대한 깊은 이해가 생기게 됩니다. 모든 OS는 나름대로의 위치가 있습니다. 나름대로의 노하우를 정리해 두는 것이 앞으로 30년 뒤에 나올 OS도 문제없이 프로그래밍할 대상이 될 수 있을 것입니다. 앞으로 20년 뒤, “내가 xxx와 yyy에서 20년간 프로그래밍을 해보니 yyy는 OS로서는 미흡하다”는 말을 할 수 있기를 바랍니다. 디버깅을 잘하는 사람의 특징은 플랫폼과 라이브러리에 대한 겸손과 섬세한 이해라고나 해야 할까요?

API에 대한 경외심과 답답함
디버깅 초보가 겪는 문제 중의 하나는 널리 알려진 API에 대한 경외심 혹은 그 반대의 답답함에 있습니다. API라는 것은 말 그대로 Application Programming Interface입니다. 블랙박스를 사용하는 방법에 대한 문서라고 할 수 있습니다. API에 대한 경외심은 개발자가 잘 모르는 영역에 대한 API인 경우가 많습니다. 파일 시스템 핸들링 API, 윈도우 메시지 API, 프로세스간 데이터 교환을 돕기 위한 IPC 등 시스템 레벨인 경우에는 안정적일 것이라는 막연한 생각에서 인정하고 사용합니다.
반면, 답답함은 버그가 발생하고, 문제가 없을 듯해 보이는 방법이 전혀 해결될 기미가 보이지 않을 때 일단 자신의 실력을 의심하다가 나중에는 API에 버그가 있을지도 모른다는 생각을 하게 됩니다. 특히, 최근에 나온 소프트웨어에 대한 것일수록 그런 의심을 하게 됩니다. “소스를 알면 쉽게 디버깅을 할 텐데”라는 체념은 많은 프로그래머의 공통적인 경험입니다만 대개의 공인된 API에 대한 답답함은 소스를 모르는 것에 있지 않고, 제대로 되어 있지 않은 샘플 없는 문서에 있을 것입니다. 이런 경우는 어쩔 수 없이 사용자 포럼의 도움 혹은 검색 엔진을 통한 도움을 받아야 합니다. 모르는 것일수록 샘플을 수집해 사용 예를 구해야 하고, 충분한 샘플 이해 없이 빈약한 문서만으로 시간을 낭비하지 맙시다.
API는 처음부터 완벽하지 않습니다. 그리고 문서도 완벽하지 않습니다. 아직도 수많은 OS의 Undocumented API들이 존재합니다. 어떤 경우든지(문서가 없든지, 문서를 이해 못했든지) API를 자신이 만든 수준으로 이해하지 못한 경우에는 버그가 존재하기 마련입니다.

몇 가지 바른 생활 - 딴짓하는 프로그래머
디버깅은 종합 예술 행위입니다. 전체적이고 섬세한 감각을 소유하지 않으면, 디버깅의 깊이가 그만큼 줄어들게 됩니다. 메모, 프로세스, 파일 I/O, 소켓, UI 등등 체계적인 지식이 없이는 해결되지 않는 경우가 많습니다. 소켓 문제인줄 알고 소켓 관련된 책만 읽다가 나중에는 쓰레드 문제로 판명되는 경우도 있습니다. 대개 디버깅은 의외로 사소한 것을 많이 알고 있을 때 쉽게 해결됩니다. 디버깅을 잘하는 사람은 사소한 것을 꼼꼼히 알고 있는 사람입니다. 그런 면에서 업무 외에 재미로 하는 ‘프로그래밍 딴짓(?)’은 그 사람의 잠재적인 문제해결력을 증강시키는 효과가 있습니다. 이는 결코 측정될 수 없는 능력입니다.
딴짓은 본디 체계가 없는 것이긴 하지만, 배움을 동기로 하는 딴짓은 딴짓 이상의 딴짓입니다. 다음은 필자가 해 보았거나, 쓸만한(?) 딴짓 목록을 적어 놓은 것입니다. 모두 유틸리티와 그것을 사용하는 스크립트입니다. 유명한 유틸리티와 스크립트 언어를 사용하는 것은 프로그래머의 자유도를 높여 줍니다.

【유닉스 계열】
1 접속 후 아무 일도 하지 않은 채 24시간 이상된 사용자 끊는 스크립트 작성해 보기 - w, awk, kill
2 수시로 디스크의 사용량을 확인하여 80% 이상 되었을 때 자동으로 메일 보내기 - df, awk, mail
3 스포츠 신문 만화를 긁어 친구들에게 메일로 보내기 - wget, perl, mail
4 주기적으로 내 특정 디렉토리 전체를 다른 서버로 복사하기 - rsync

【윈도우 계열】
1 조카들이 바꿔 놓는 IE의 시작 페이지를 부팅 후 레지스트리에 원래대로 해 놓기 - VBScript
2 회사내 로컬 IP - 호스트명 테이블 만들어 보기 - nbtstat, perl
3 회사내 공유 폴더 리스트 만들어 보기 - net, perl
* - 뒤는 사용됨 직한 유틸리티입니다

사용자의 눈, 개발자의 눈
여러분이 프로그래머라면 일반 사용자와 눈이 달라져야 합니다. 컴퓨터에서 일어나는 모든 세세한 일까지 호기심을 가지고, 나름대로의 추측을 가지고 있어야 하고, 나중에 문서를 통해 혹은 트레이서 등을 통해 추측을 확인해야 하고, 궁극적으로는 필요한 때에 정확히 재현할 수 있는 코드를 작성할 줄 알아야 합니다.
디버깅은 전문 디버거로 알려진 도구들만의 전유물이 아닙니다. 디버깅은 정상으로 실행되는 프로그램에 대한 이해부터 시작합니다. 응답 시간이 길어지는 프로그램을 잘 살펴보면,
으로 눌러 다른 화면에서 돌아올 때 창이 새로 그려지지 않는 경우가 있습니다. 또 자세히 보면 윈도우의 맨 가장자리 프레임은 항상 그려지게 됩니다. 왜 그럴까요? 일반 사용자의 관점과 달리 프로그래머의 관점에서 보면, 현재 응답을 기다리는 쓰레드가 내부 창을 그리는데 사용되는 것과 같다는 것을 추측할 수 있습니다. 윈도우 맨 가장자리를 다루는 쓰레드는 OS에 소속된 것이지, 응용 프로그램에 소속된 것이 아닐 것 같다는 생각도 해 볼 수 있습니다.
조금 더 얘기하면, 디스플레이 등록정보에는 ‘마우스로 끄는 동안 창 내용 표시’ 같은 기능이 있습니다. 또, MSN Plus에서 제공하는 광고 창 감추기 기능이 있습니다. 이런 것들은 Spy++ 같은 윈도우 메시지 트레이서 기능을 이용해 평소에 눈여겨 두면, 알고 있는 지식과 구현된 기능에 대한 실 예를 통해 폭넓은 이해가 가능합니다. 유닉스의 경우 리눅스의 strace, 솔라리스의 truss, hpux의 tusc 등을 이용해 평소에 inetd 같은 데몬이 어떻게 돌아가는지(option -p) 알아 볼 수 있습니다. 이들은 실행중인 프로그램에 큰 영향을 주지 않으면서, 엿보기 기능을 이용해 구현을 짐작해 보는 것들입니다. API가 아무리 블랙박스처럼 보여도, 평소에 이런 류의 툴을 이용해 시스템 레벨의 입출력을 덤프해 보는 것만으로 API의 내부를 어느 정도 짐작해 볼 수 있습니다. 물론, 리버스 엔지니어링은 많은 소프트웨어에서 금지되어 있다는 사실도 염두에 두면서 들여다보기 바랍니다.

재현 가능성
으례 들을 수 있는 말이지만, 디버깅은 사건을 추적하는 형사가 하는 일과 같습니다. 크게 다른 것은 디버깅은 언제든지 같은 상황을 재현할 수 있는 데 있으며, 형사가 하는 일은 단 한 번의 사건에 국한되어 비슷한 상황을 연출하는 데 그 한계가 있다고 볼 수 있습니다. 우리로서는 참으로 다행이지 않을 수 없습니다. 수만 번 프로세스가 죽고, core dump, watson log 같은 시체만 남는다 해도 윤리적인 가책을 전혀 느끼지 않으니까요. 디버깅을 하는 사람들은 형사처럼 조심스럽게 그 프로세스의 시체들을 디버거를 통해 부검하겠지요.
디버깅을 위한 전제 조건으로 ‘재현 가능성’을 생각해 보겠습니다. 누구한테 디버깅에 대한 조언을 구할 때에도 재현을 하기 위한 방법이 모호하고, 심지어 말을 들어 주는 사람도 증상을 유추하기 어렵다면 별 도움을 받을 수 없을 것입니다. 증상을 제대로 설명하지 않았는데도 답변을 바로 준다면, 그 사람은 아마 여러분의 그룹에서 경외의 대상일 것입니다. 비단, 프로그램뿐만 아니라 전화나 메신저를 통해 컴퓨터의 이상을 호소하는 사람에게 조차 처음 듣는 현상인 경우 그대로 재현할 수 있는 방법에 대해 들어야 올바른 답을 줄 수 있는 것입니다.
또 다른 측면에서 다른 사람에게 설명하기 위해 재현하는 방법을 차근차근 설명하다가 해결책을 아는 경우가 종종 있습니다. 끝까지 설명하지 않았는데 말이죠. 이전까지는 문제의 현상에만 집중한 나머지 처음부터 생각을 하지 않았던 것입니다. 아니, 문제가 다른 부분에 있을 것이라고는 생각하지 않았던 것입니다. 그만큼 어떤 문제가 ‘재현 가능한지’에 대한 것과 ‘어떻게 재현할 수 있는지’에 대한 것은 디버깅을 위한 전제 조건이 됩니다.
개발자와 QA가 분리되어 있는 개발 그룹의 경우, QA의 버그 리포트는 재현 순서에 대한 상세한 설명을 수반하게 됩니다. 또한, 고객 상담실이 운영되어 출시한 프로그램의 사용자 지원이 이뤄질 때도 버그 재현에 대한 상세한 문서가 먼저 선행 조건이 됩니다. ‘재현되지 않는 버그는 고칠 수 없습니다’ - 개발자가 좋아하는 문구입니다. 디버깅을 위한 다음과 같은 공동의 작업 환경이 있다면 훌륭한 팀이 됩니다.

1 소스 버전 컨트롤 : Visual Source Safe, CVS, WinCVS, TortoiseCVS
2 버그 게시판 혹은 회람용 문서 : 배포 버전 번호/테스트 수트, 방법/ 버그 재현 순서/개발자 의견/조치 이력
3 잦은 배포 : 수시로 (2주 이내) 소스 묶음과 설치본을 QA에 넘깁니다

코드 리뷰
디버깅은 아니지만 꼼꼼한 관리자는 개발이 어느 정도 완료된 후 코드 리뷰(code review)를 하자고 합니다. 개발자로서는 참으로 쑥스러운 시간입니다. 한 사람당 한두 시간 정도 들어 발표하는 동안, 지켜보는 모든 사람은 인공지능 컴파일러가 되어 올려지는 모든 소스를 날카롭게 보게 됩니다. 코딩 규칙이나 명료하지 않는 부분, 주석 없는 것이 들키는 시간이지요. 이 컴파일러는 경고가 친절하지 않습니다. 간혹 인간성이 안 좋은 컴파일러로부터 심한 말도 듣게 됩니다. 한 사람 때문에 팀 전체 소스의 신뢰도를 떨어뜨릴 수 있기 때문이죠. 코드 리뷰는 여러 가지 이점이 있지만, 중요한 것은 준비하면서 코드를 다듬게 되며 발표 중에는 개발자조차 간과했던 버그를 발견하는 것입니다.
리턴 값을 확인하지 않고 지나는 경로가 있는지, assert 조건이 있음에도 assert문이 빠져 있다든지, 배열에 대한 boundary 확인이 되지 않은 채 최대 인덱스를 넘어 사용하는 부분이 있다든지, 재현되지 않은 버그까지 발견할 수 있는 이점을 가져다 줍니다. 이런 내용은 다음에 다시 설명 드리겠습니다. 디버깅을 위?코드 리뷰는 다음과 같이 합니다.

【발표하는 경우】
1 구문 컬러링이 되어 있는 에디터를 통해 소스를 보여줍니다(ViewCVS를 사용할 경우 enscript 기능 추가)
2 에디터에서 직접 수정하거나 메모장을 이용해 논의사항을 추후 반영합니다
3 설계 문서를 간단히 준비해 보여줍니다
4 설계상 가장 중요한 구조체/클래스에 대한 헤더를 먼저 소개합니다
5 메인 루프, 즉 함수들을 호출하는 중심이 되는 함수를 먼저 소개합니다
6 설계 문서를 번갈아 가며 구현되어 있는 함수를 보여줍니다.

【듣는 경우】
1 코딩 규칙을 살펴봅니다
2 알고 있는 것과 반대되는 것, 특이한 구현 방식에 대해 질문합니다
3 컴파일러가 그러하듯 질문 내용을 바로 질문해 토의가 일방적이지 않게 합니다
4 추궁하여 당황하게 만들지 말고, 충분히 소개할 수 있는 편안한 자세를 만들어 줍니다

코드 리뷰는 팀 내에서만 이뤄지는 것이 아닙니다. 오픈소스 진영에서는 발표하는 순간부터 코드 리뷰가 이뤄지고 있습니다. 소스에 대한 접근 권한이 있다는 것은 코드리뷰가 진행 중이라는 것이며, 개발자는 다른 사람의 리뷰 결과에 대해 겸손한 피드백을 해주어야 합니다. 코드 리뷰는 디버깅과 튼튼한 코드를 위한 가장 매력적이며 가장 확실한 방법입니다.

남의 코드를 많이 보라
소스는 마치 책과 같아 좋은 소스와 나쁜 소스에 대한 구별법이 없이는 잘못된 습관을 만들 수 있습니다. 디버깅하기 좋은 소스와 코딩하기 좋은 소스는 분명 구별됩니다. 어떤 것이 과연 디버깅하기 좋으냐에 대한 생각은 다를 수 있지만, 아무 소스나 보면서 그 소스에 대한 깔려 있는 생각을 읽을 수 없다면 습관이 잘못 들어 디버깅은 더 어려워질 수 있습니다. 그럴지라도 다른 사람의 소스를 많이 보십시오. 소스포지나 코드구루 등은 공개된 소스를 얻을 수 있는 좋은 사이트입니다. 특히, 팀으로 작업하는 프로젝트의 소스를 보십시오. 그 팀에서 코딩 가이드를 제시하고 있다면 더더욱 소스에 대한 질이 높아집니다.
나중에 다른 API를 사용해 연동할 일이 생긴다면, 그 소스를 볼 수 있다는 것은 디버깅에 큰 도움을 주게 됩니다. 한 페이지의 매뉴얼보다는 한 페이지의 소스가 더 도움이 되는 법이지요. 소스에 대한 경험이 많을수록 소스에서 느끼는 부드러움과 안정감, 위태로움, 불안함에 대한 감각이 자라나게 됩니다. 다른 사람의 소스를 읽는다는 것은 그 사람과 대화하는 것입니다. 그렇게 되면 코딩 스타일이라는 굴레를 벗어나게 되는 것입니다. 감상하는 법을 아는 사람만이 예술 작품세계에 대한 평을 할 수 있는 것입니다. 감상하는 법을 아는 사람이 설계 패턴과 설계 철학을 읽을 수 있습니다. 그리고 자연스럽게 자신의 소스에 대한 섬세한 손질을 할 수 있습니다.

표준 문서 숙지 - 네트워크 프로그래밍 디버깅
네트워크 프로그래밍은 필수 요소로 프로토콜이라는 전송 규약이 수반됩니다. 디버깅의 1차 목표는 프로토콜에서 정의한 패킷들이 필드 규격에 맞게 전송되고 있는지를 확인하는 것입니다. 그리고 다음으로는 필드의 내용이 프로토콜에 맞게 제 값을 가지고 다니는지를 확인하는 것입니다. 이를 위해 수반되는 것은 TCP/IP에 대한 명세를 확실히 하는 것입니다. TCP/IP 기반 네트워크 프로그래밍에서는 필수적으로 MAC 어드레스에 대한 개념과 IP 어드레스, 네트워크 어드레스, 브로드캐스팅 어드레스, 넷마스크, 디폴트 루트에 대한 개념을 책을 보며 익히되 패킷을 캡처해 가면서 공부하는 것을 권합니다. 더미 허브(스위칭 허브가 아닙니다)를 사용하면 네트워크에서 돌아다니는 모든 패킷을 다 읽을 수 있으므로, 패킷 캡쳐 툴을 사용해 가만히 들여다 보는 것만으로 책안의 내용이 살아나게 됩니다. 이런 툴 하나 정도는 꼼꼼히 옵션 찾아가며 익힐 것을 권합니다. tcpdump를 권하며, 윈도우에서는 같은 류의 windump가 있습니다. 둘의 옵션이 비슷하므로 하나를 익히면 다른 것도 쉽게 사용할 수 있습니다. 그 외에 GUI로 제공하는 많은 툴이 있으므로 찾아서 익히기 바랍니다(검색어 packet capture, sniffing).
인터넷 필수 기본 프로토콜에 대한 것은 문서를 익히는 것에서 패킷 캡처를 통한 확인, 그리고 많은 커맨드 라인 방식의 프로토콜(SMTP, HTTP, POP, NNTP, FTP)에 대해서는 telnet을 이용한 테스트까지 완전히 자기 것으로 만들어야 합니다. 물론 샘플을 구해 클라이언트를 만들어 본다면 더 없이 훌륭합니다. 네트워크 프로그래밍 개발자들이여, 문서를 읽어 용어를 아는 정도는 비개발자들도 하는 것입니다. 하물며 개발자는 패킷 캡처까지 하여 프로토콜 확인은 할 줄 알아야 합니다.

깊은 프로그래밍을 위한 첫 발걸음
소스를 많이 보면서 구체적인 점을 이야기하지 않았습니다만, 전반적으로 프로그래밍과 디버깅을 시작하는 사람들이 가져야 할 모습을 다루어 보았습니다. 시작부터 튼튼한 사람은 없습니다. 처음에는 버그를 잡았지만, 소 뒷걸음에 쥐를 잡은 듯이 넘어가는 일이 많습니다. 프로그래밍과 디버깅을 따로 뗄 수는 없는 것입니다. ‘코딩 끝 디버깅 시작’이라는 말같이 디버깅을 염두에 두지 않은 코딩은 그 깊이가 얕을 수밖에 없습니다. 프로그래밍이라는 작업은 만만치 않지만, 설계부터 코딩, 디버깅이 끝난 프로그램이 잘 돌아가는 것을 보는 것은 예술가적인 안목에서 참 흐뭇한 일입니다. 짧은 연재이지만, 뒤 이어지는 연재들을 같이 나누며 깊은 프로그래밍을 위한 발걸음을 차근차근 내딛어 봅시다.

프로그래머는 그 고집만큼 습관이 고착되어 있습니다. 여러 습관 중에서 가장 강조하고 싶은 것은 다음과 같습니다.

◆ 코딩 규칙 준수
◆ 로그 API 먼저 작성하기
◆ Assert문 이용하기
◆ 자원 관리 철학 갖기
◆ 다중 if문 갖지 않기

디버깅에 관한 코딩 습관
코딩 규칙을 준수합시다

개발이 시작되면 대개의 경우 팀으로 프로그래밍을 하게 되며, 설계가 끝나고 코딩에 들어가기 전에 항상 코딩 규칙을 만들게 됩니다. 혹은 회사에 전부터 정해진 코딩 규칙이 있다면 그것을 개발에 적용하게 됩니다. 이런 코딩 규칙은 통일을 기하기 위해 만들어집니다. 파일 명명법, 함수․변수 명명법, 괄호의 위치, 파일 주석, 함수 주석, 선언 주석, 들여쓰기 방법 등에 대한 것을 기술하며, 훈련이 잘 되어 있는 개발팀이라면 이런 코딩 방법에 대한 통일을 이루게 됩니다.

코딩 규칙은 미래의 자신과 개발 중인 다른 사람과의 협업을 위해서는 반드시 지켜야만 하는 것입니다. 이런 코딩 규칙과 디버깅과의 상관 관계는 일부 코딩 규칙이 버그 발생을 예방하기 위해 만드는 것이 있다는 것입니다. 필자가 권하는 것은 컴파일러다운 관용의 자세를 가지라는 것입니다. 이 말은 아무렇게나 작성하라는 것이 아니라 코딩 규칙이 프로젝트가 바뀔 때마다 변할지라도 자신을 능동적으로 맞춰가라는 것입니다. 코딩 규칙 중에서 많은 프로젝트에서 사용하는 두 가지를 소개하겠습니다.

◆ 단일 실행문을 갖는 if, while, for문이라 할지라도 중괄호(‘{’, ‘}’)를 기입한다(<리스트 1>).
<리스트 1>과 같은 규칙은 처음 작성할 때는 문제가 되지 않지만, if 안의 블럭에 실행문을 하나 더 추가할 일이 생길 경우 중괄호가 없는 예에서는 간혹 실수하여 if의 참, 거짓에 상관없이 다음에 실행되는 문장으로 인식될 수 있는 경우가 발생합니다. 특히 printf(“Check %s:%d”, __FILE__, __LINE__);과 같이 중간 중간 현재 진행되는 위치를 출력하려고 중요한 위치(함수 시작, 조건 판단, 함수 종료 등의 위치)에 마구 복사해 넣다 보면 <리스트 1>과 같은 경우가 흔히 발생합니다. 다른 예를 들어 보겠습니다.

<리스트 1> 코딩 규칙 1
규칙 준수 예 :
if( pTemp != NULL ) {
*pTemp = ‘x’;
}

규칙 미준수 예 :
if( pTemp != NULL )
*pTemp = ‘x’;


◆ ++, -- 연산자는 함수 호출 인자 내에 쓰지 않고 호출 앞 혹은 뒤에 따로 쓴다(<리스트 2>).
-- 위치에 따라 ‘사용 후 감소’ 또는 ‘감소 후 사용’이라는 모호성과 함수 호출시 인자로 넘어갈 값을 결정하는 순서(<리스트 2>에서는 두 번째 인자 --count와 세 번째 인자 score[count])가 섞이면 상당히 골치 아픈 일이 발생합니다. <리스트 2>에서는 count가 printf에 넘어간 뒤 --가 수행될지, --가 먼저 되고 printf에 넘어갈 지에 대해 생각을 합니다. 또한 printf 함수에 넣기 위해 --count를 먼저 할지, score[count]를 먼저 계산할 지에 따라 score 배열의 인덱스가 달라지는 문제가 발생합니다. --에 대한 것은 책을 찾아 명확하다고 할지라도 함수에 넘길 인자의 정확한 값을 구하기 위한 순서는 컴파일러마다 다를 수 있습니다. 모든 컴파일러가 같다고 할지라도 가독성을 떨어뜨리므로 좋은 코딩이라고 볼 수 없습니다.

<리스트 2> 코딩 규칙 2
--count;
printf( “Last index %d, Last value: %d”, count, score[count] );

규칙 미준수 예 :
printf( “Last Index: %d, Last value: %d”, --count, score[count] );

간단히 코딩 규칙의 예 중에서 버그 방지를 위한 것들로 자주 사용되는 것을 살펴봤습니다. 디버깅과 상관없이 코딩 규칙에 대해 말하자면, 코딩 규칙을 따르지 않고 자신만의 습관을 사용하는 것은 프로다운 모습이 아닙니다. 오히려 전문가는 프로그램 설계, 즉 구조에 중점을 두어야 합니다. 자신만의 코딩 규칙보다는 팀의 규칙을 따르는 것이 도움을 주고받을 때에도 시간을 단축할 수 있습니다. 필자가 속한 그룹에서는 들여쓰기와 괄호 위치, 선언 위치 등에 대한 것을 정해 놓고, code beautifier(GNU indent)를 사용하여 표준을 따르도록 고쳐주는 옵션을 정한 뒤 팀원들이 공유하여 코딩 규칙 일부에 대해 자동화합니다.

Log API 만들기
사실 printf를 디버거라고 부르기에는 적당하지 않습니다. 디버깅을 위한 값을 추적하는 방법에 불과하기 때문이지요. 여기서는 printf로 대표되는 ‘실행 중 값 출력’에 대해 말하고자 합니다. 프로그램을 시작하는 모든 사람이 오류가 발생하면 관심 있는 변수의 추이를 보고 싶어하고, 그런 변수가 실행 중 어떻게 변하는지를 살펴보는 것으로 처음 디버깅을 경험하게 됩니다. 이 방법이 정형화된 것이 바로 다단계 로그입니다. 잘 되어 있는 프로그램은 로그의 단계를 조절할 수 있는 기능(최소한 남길지 말지에 대한 기능)이 있어서 사용자가 종류별, 단계별로 로그를 원하는 파일에 심지어 원하는 포맷으로 남길 수 있습니다. 프로그래밍을 할 때 처음 구현을 위해 남기는 로그를 printf로 남기다가 나중에는 모조리 지웁니다. 왜냐하면 주로 이런 모습이기 때문입니다.

!!!! temp file name: gHie88009.dat
-------------- CHECK 1
---------------CHECK 2
client: 192.168.10.1 2890 8 17:30:13

아무 의미 없어 보이지만 실제 구현되기 전까지 만든 사람에게는 중요한 정보가 됩니다. 구현되고 나면 당연히 주석 처리가 되거나 삭제되는 코드입니다. 체계적인 로그 관리는 참으로 중요합니다. 나중에 문제가 생길 경우, 심지어 고객에게 배포된 것에 문제가 생길 경우에는 로그를 보내주고, 그 로그를 받아오면 좋은 경우가 많기 때문입니다. 앞과 같은 로그를 남기는 데 그냥 줄 수 있습니까? 애만 태우게 됩니다. 앞과 같은 로그 대신 프로젝트가 사용할 로그 API를 이용합니다. 이때 최상위 레벨, 즉 가장 자세한 상황으로 로그를 남기는 옵션일 때만 남기도록 함수를 하나 만들어 앞 로그를 보기 좋게 수정하여 코드에 넣는다면, 나중에 로그를 자세히 남길 필요가 있을 때(팀장의 협박하에 또는 고객지원을 위해)에도 많은 수고를 덜 수 있게 됩니다.
윈도우 프로그래머들은 TRACE와 TRACEn(n은 string을 제외한 인자의 개수)으로 대표되는 디버그 모드 추적기가 있습니다. 이 경우에 있어서도 될 수 있으면 팀에서 로그 포맷을 정하고, 개발 후에도 삭제하지 않고 유용한 정보로 사용하는 것이 좋습니다.

 

시공 감리 assert
‘assert를 잘 쓰면 기본은 뗐다’고 칭찬해 줄 정도입니다. 잘 쓴다는 얘기는 남발한다는 것이 아니라 필요한 부분에는 꼭 쓰고, 쓰지 말아야 할 곳에는 안 쓴 코드를 말합니다. assert는 중요하지만 많은 실전 경험 없이는 기술이 완성되는 것이 아니므로 몇 가지 예를 들어 설명하겠습니다. assert 사용이야말로 버그를 줄일 수 있는 가장 중요한 습관입니다. assert는 #include 과 같은 헤더를 포함해야 쓸 수 있습니다. 사용 방법은 단지 assert( <평가식> );과 같은데, 함수 호출이 있으면 <평가식>은 0이 아닌 값, 즉 참 값을 가져야만 합니다.

/* #define NDEBUG */
#include

#include

int main()
{
int i = 0;
assert( i );
}

앞과 같은 코드는 반드시 assert문에서 오류를 발생시킵니다. 오류에는 파일 이름과 행, 그리고 어떤 값이 오류를 일으켰는지에 대한 정보를 보여주게 됩니다. 다시 앞 코드를 컴파일할 때 #define NDEBUG의 주석을 푼 뒤 실행하면 오류가 나지 않음을 알 수 있습求? NDEBUG라는 매크로가 선언되어 있으면 모든 assert문은 빈 명령어가 되는 것입니다. assert.h는 ANSI C 표준에 들어 있으므로 ANSI C를 지원하는 라이브러리가 있다면 어떤 플랫폼에서도 사용할 수 있을 것입니다.

◆ assert는 내부 설계에 대하여 변수가 원하는 내용을 가지고 있는지 확인하는 데 사용한다(<리스트 3>).
<리스트 3>을 보면 두 함수가 등장하는데, process 함수를 SMTP의 ‘서버 메시지 같은 꼴’의 응답 메시지를 처리하는 데 사용하는 함수라 생각해 봅시다. SMTP 결과 메시지의 간단한 모양은 다음과 같습니다.

220 mail.test.com ESMTP Ready

<리스트 3> SMTP 적용 예제
const char szLastMessage[1024];
void saveLastMessage ( const char * szMessage )
{
strncpy( szLastMessage, szMessage, sizeof( szLastMessage ) );
/* sizeof는 배열 szLastMessage의 최대 크기 */
}

/* szLine은 <세 자리 숫자><공백><서버 메시지> */
void process( const char * szLine )
{
int code;
code = atoi( szLine );
saveLastMessage( szLine + 4 );
/* 이하 생략 */
}

SMTP에서는 세 자리의 응답 코드만 가지고 대부분 처리되지만, 사람이 읽을 수 있는 메시지는 한 칸의 공백을 두고 쓰게 되어 있습니다. 이 정도 규약이 있다고 가정합니다. process 함수는 내부에 saveLastMessage 함수를 부르고 있으며, saveLastMessage는 그 인자인 szMessage를 서버의 응답을 처리하는 데 맨 앞에 있는 함수가 아니라는 것을 알고 있습니다. 즉, saveLastMessage는 process라는 선처리 함수 뒤에서 작용하는 함수라는 설계가 반영된 것입니다. saveLastMessage는 적법한 메시지일 경우에 불리운다고 가정합니다. 이런 상황을 두고 적당한 assert 위치와 assert 내용을 살펴봅니다. 일단 함수에 들어오는 szLine, szMessage에 대한 상황을 생각해 봅니다.

saveLastMessage :
ꊱ szMessage는 널 포인터가 아니어야 한다.
ꊲ szMessage는 실제 내용이 있어야 한다.

process :
ꊱ szLine은 널 포인터가 아니어야 한다.
ꊲ szLine[0], szLine[1], szLine[2]는 숫자이어야 한다.
ꊳ szLine[3]은 공백이어야 한다.
ꊴ szLine + 4 위치에 메시지가 들어 있어야 한다.

<리스트 4> assert의 적용 예
const char szLastMessage[1024];
void saveLastMessage ( const char * szMessage )
{
assert( szMessage ); /* null pointer가 아님 */
assert( szMessage[0] ); /* 실제 내용이 있음 */
strncpy( szLastMessage, szMessage, sizeof( szLastMessage ) );
/* sizeof는 배열 szLastMessage의 최대 크기 */
}

/* szLine은 <세 자리 숫자><공백><서버 메시지> */
void process( const char * szLine )
{
int code;
assert( szLine ); /* null pointer 아님 */
assert( isdigit(szLine[0]) && isdigit(szLine[1]) && isdigit(szLine[2]) ); /* 세 자리 숫자 */
assert( szLine[3] == ‘ ’ ); /* 공백 */
assert( szLine[4] ); /* 실제 메시지 내용 있음 */
code = atoi( szLine );
saveLastMessage( szLine + 4 );
/* 이하 생략 */
}

앞과 같은 사항을 반영한 assert가 들어간 코드는 <리스트 4>와 같습니다. assert와 관련해 szLine과 szMessage의 가장 큰 차이는 설계상 szLine은 처음으로 서버 즉 외부의 데이터를 받는 부분이고, szMessage는 한번 걸러진 변수라는 것입니다. szLine이 준수해야 하는 규칙은 서버에서 응답을 이상하게 준다면 충분히 깨질 수 있는 상황이며, 그런 상황이 벌어진다면 saveLastMessage는 적법한 경우가 아니므로 불리우지 않는 것이 설계의 내용입니다. 그러므로 적법한 상황에서 불리우는 szMessage는 알 수 없는 오류가 있지 않는 한 널 포인터일리 없고 내용이 없을리도 없습니다. 따라서 설계상 서버의 오작동과 같이 외부 입력에 대한 것을 처리할 수 있는 것은 assert로 하는 것이 아니라 if문으로 처리하여 적절한 오류 처리 루틴을 따라야 합니다. 외부 데이터의 변화와 상관없는 assert를 정리하면 다음과 같습니다.

saveLastMessage :
ꊱ szMessage는 널 포인터가 아니어야 한다.
ꊲ szMessage는 실제 내용이 있어야 한다(szMessage 길이가 0으로 saveLastMessage 함수가 호출되지는 않는다).

process :
ꊱ szLine은 널 포인터가 아니어야 한다.
ꊲ szLine에 코드를 비롯한 메시지가 들어 있어야 한다(szLine 길이가 0으로 process 함수가 호출되지는 않는다).

<리스트 5> 설계와 구현에 대한 assert의 용법
void process( const char * szLine )
{
int code;
assert( szLine ); /* null pointer 아님 */
assert( szLine[0] );
/* 서버의 응답의 적합성을 파악하기 위해서는 길이가 적어도 4바이트가 되어야
배열 참조 인덱스가 유효하므로 먼저 길이 조사를 한다. */
if( strlen( szLine ) < 5 ||
! (isdigit(szLine[0]) && isdigit(szLine[1]) && isdigit(szLine[2]) ) ||
szLine[3] != ' ' )
{
/* 오류 로그 */
return;
}
code = atoi( szLine );
saveLastMessage( szLine + 4 );
/* 이하 생략 */
}

<리스트 5>는 설계와 구현에 대한 assert의 용법에 대해 알아 본 것입니다. 함수의 모든 인자에 대한 것은 함수 첫 부분에서 assert를 해줘야 합니다. 클래스 멤버 함수에 관해서는 인자뿐 아니라 사용하는 멤버 변수에 대한 것도 포함됩니다. 다음은 대표적인 assert문이 사용되는 방법입니다.

ꊱ 포인터의 경우 널인지 여부
ꊲ 일반 변수의 설계상의 범위 혹은 정확한 값 준수 여부
ꊳ 다중 if, else if, switch 등의 복잡한 판단 후 처리에 대한 결과 확인



assert를 사용하지 말아야 할 대표적인 곳은 다음과 같습니다.

ꊱ 외부 데이터 입력 변수
ꊲ 메모리 할당 결과
ꊳ 파일 열기 결과

이번에는 assert를 프로그래밍 습관보다는 디버깅에 사용하는 방법을 생각해 봅시다. 디버깅할 때 심지어는 멤버 함수의 경우 this 포인터가 널이 아닌지 확인해야 하는 경우도 있습니다. 버그가 발견되었을 때 문제가 발생한 곳을 중심으로 의심가는 곳에 assert를 심어 넣습니다. 이 경우에는 assert가 오류를 내고 프로그램이 멈추어야 되므로, 앞에서 사용하지 말아야 할 상황까지 일부러 넣어가면서 프로그램을 임시로 지저분하게 가져가야 합니다. 사용하지 말아야 할 곳에 넣은 assert는 나중에 다시 빼야 하므로 아예 들여쓰기를 하지 않고 넣는 것도 한 방법일 것입니다. 이 방법은 if로 조건을 벗어나는 것에 대한 로그를 남기는 것보다 확실합니다.
정리하면 assert는 설계의 흐름을 제대로 구현하고 있는가에 대한 감리 역할을 하고 있는 것입니다. 많은 경우 아주 가끔씩 일어나는 에러의 경우에도 assert를 충분히 해주었다면 쉽게 잡을 수 있는 경우가 있습니다.

 

 

자원 미제거에 대한 방어
자원 할당 또는 제거라 함은 메모리 할당, 파일 open, socket accept, close 등을 말합니다. 시스템 자원(메모리 포함)의 새는 것(resource leakage)에 대한 추적은 디버깅의 어떤 언어든 끝없는 주제일 것입니다. 메모리 및 시스템 자원에 대한 것은 다음과 같은 설계 철학을 공유하지 않으면 체계적이 될 수 없습니다. 물론 메모리 새는 것과 자원 새는 것들에 대해 추적할 수 있는 도구 혹은 추적 가능하게 해주는 라이브러리를 사용하는 방법이 있습니다만, 소스 수준에서 올바를 습관을 기르면 디버깅에 도움이 될 수 있기에 정리해 봅니다.

ꊱ 자원 할당과 제거를 동일한 계층에서 일어나도록 한다.
ꊲ 자원 할당과 제거를 논리적으로 시작 모듈과 끝 모듈에 맞추어 일어나도록 한다.

둘은 서로 반대되는 얘기입니다만 일종의 패턴이라고 생각하면 됩니다. 첫 번째, 자원 할당과 제거가 동일한 계층에서 일어난다는 것은 같은 모듈 내에 생성 소멸을 두라는 이야기입니다. 다른 말로 하면, 가능하면 자원을 할당한 함수에서 해제하라는 것입니다. 또는 그것이 불가능할 경우 같은 클래스 안에서 혹은 동일한 파일 내에서 할당, 제거할 수 있는 설계 방법을 취하는 것입니다. 두 번째는 생성되는 곳과 소멸되는 곳을 특정한 두 개 정도의 함수, 클래스 혹은 파일로 모으라는 것입니다. 만약 쓰레드 등을 써서 소켓을 accept하는 곳과 close하는 곳이 분리돼야만 한다면, 여러 곳에서 close하지 말고 모아두라는 것입니다. 즉, 프로세스의 시작과 끝이 다른 경우에는 자원이 생성되고 소멸되는 위치가 되도록 모여 있도록 하라는 것입니다.

이런 패턴을 따르지 않을 경우 할당 제거에 대한 명확한 문서화가 되어 있어야 합니다. 되도록 그런 문서를 만들지 않아도 알기 쉽게 앞 패턴을 따르는 것이 좋습니다. 다음은 같은 함수 내에서 제거하는 모습입니다. 나쁜 예는 process 함수 내의 주석 처리한 부분에서 pConfig 객체가 소멸되는 것입니다.

void process( Config * pConfig )
{
/*... 처리 ... */
/* delete pConfig; */
}

void run()
{
Config * pConfig = new Config("/etc/test.cfg"); /* pConfig가 널인지 확인하는 코드 생략 */
process( pConfig );
delete pConfig;
}

◆ 자원 할당 전에 변수가 비어 있는지 확인해야 한다.
◆ 자원 제거 후에는 변수를 초기 값으로 환원시켜야 한다.

자원 할당 전에 할당한 자원을 받을 변수에 어떤 의미있는 내용이 있는지 확인해야 합니다. 또한 자원 제거 후에는 반드시 그 변수를 초기 값으로 환원시켜서 다음에 해제되었는지를 확실히 해야 합니다.

/* 메모리 할당, 해제 전에 확인 */
if( pBuffer )
{
free( pBuffer );
}
pBuffer = (char *) malloc( BUFFER_SIZE );
/* 처리 */
if( pBuffer )
{
free( pBuffer );
pBuffer = NULL;
}

/* 파일 닫은 후에 초기화 */
if( fd >= 0 )
{
close( fd );
fd = -1;
}

좀더 상세하게 살펴봅시다. 다음의 예에서 g_pBuffer와 fd 변수는 초기 값으로 각각 NULL, -1을 가지고 있다고 합시다.

/* --- MEMORY --- */
if( !g_pBuffer ) {
g_pBuffer = (char &) malloc( BUFFER_SIZE );
}
/* --- FILE --- */
if( fd >= 0 ) {
if( !close(fd) ) {
printf(“close error”);
}
}
fd = open( “/tmp/log.txt”, O_RDONLY );

이제 자원 할당 문제와 assert를 이용한 디버깅을 알아봅시다. 앞의 예에서 g_pBuffer 값이 논리적으로 프로그램 흐름상 g_pBuffer는 초기화되어 있어야 한다면, 또한 open하기 전에 fd 값이 초기 값(-1)을 유지할 수밖에 없다는 것이 확실하다면, 즉 모든 실행 경로에서 if 조건들이 결코 참이 될 수 없다면 if 위에 assert를 넣어 다음과 같이 만들어 자신의 논리를 굳히는 프로그래밍을 할 수 있어야 합니다. 문법 오류를 컴파일러가 잡아내듯 논리 오류를 잡아내는 데 사용됩니다. 실행 도중 논리적인 설계 외의 행위가 발생한다면 그것은 디버깅감입니다.

assert( g_pBuffer );
assert( fd < 0 );

◆ 불필요하게 파일 기술자를 두 번 이상 close하지 않는다.
뭔가 확실히 해두려고 두 번 이상 파일 기술자를 닫는 경우가 있습니다. 이 경우 두 번째의 close는 당연히 닫힌 파일에 대한 close이므로 오류를 일으키며, 일반적으로 프로그래머는 close의 오류 확인을 하지 않는 경우가 많습니다. 물론 앞의 습관이 제대로 들어 close 후에 -1(혹은 Handle의 경우 NULL)로 초기화하고, close할 때는 반드시 0보다 크거나 같은지에 대해 확인을 하겠지만 그것은 안전을 위한 방법이며, 그것보다 먼저 점검할 것은 생각할 수 있는 모든 경로에서 정확히 close를 한번만 하는지 확인하는 것입니다. close 전에 assert를 넣어 두 번 close를 하는지 점검해 보는 것도 좋습니다.

다중 if문 피하기
다중 if를 최대한 줄일 수 있도록 만들면 그만큼 가독성을 높게 합니다. <리스트 5>와 <리스트 6>을 비교하면 처음 만나는 if문을 바꿔 씀으로써 이중 if문을 단일 if로 바꾸었습니다. <리스트 5>와 같은 코드는 나름대로 정상적인 흐름을 머릿속에 생각하고 정상적인 것만 처리하는 데 집중하여 나온 것입니다. 습관을 바꾸면 비정상적인 것을 먼저 판단하되 비정상적인 것이 잘 일어나지 않는 상황입니다.

게다가 로그를 남겨야 하는 등 많은 일을 처리해야 한다면 assert문을 넣어 그 함수 안에 들어오면 반드시 오류가 나도록 처리해 두고, 그 아래에 계속 생각의 흐름을 진행시키는 방향으로 코드를 작성하는 것이 좋습니다. 이런 습관은 일석이조의 효과를 거두게 됩니다. 코드의 가독성을 높이고, 구현을 미루어 놓아도 나중에 까먹지 않게 되지요. 다음과 같은 방법으로 간단히 처리하고 나중에 assert에 걸릴 때 적절한 코드를 넣어도 정상적인 것을 우선 작성하는 데 큰 어려움이 없을 것입니다.

if( 0 >= (size=recv( s, buf, 1024, 0 )) ) {
assert( 0 && “TODO: You should process error”);
}

<리스트 5> if문 사용 예 1
int check( s )
{
char buf[1024];
int size = 0;
if( 0 < (size=recv( s, buf, 1024, 0 )) ) {
buf[size] = ‘\0’;
if( ‘2’ == buf[0] ) {
return 0;
}
return 1;
}
printf(“Socket closed.”);
return -1;
}

<리스트 6> if문 사용 예 2
int check( s )
{
char buf[1024];
int size = 0;
if( 0 >= (size=recv( s, buf, 1024, 0 )) ) {
printf(“Socket closed.”);
return -1;
}
buf[size] = ‘\0’;
if( '2' == buf[0] ) {
return 0;
}
return 1;
}

빌드 과정 파악하기
지난 호에서 필자는 ‘언어와 환경’이라는 주제로 언어 명세와 라이브러리를 분리할 줄 알아야 한다고 했습니다. 이번에는 언어와 라이브러리를 조작하는, 흔히 말하는 컴파일러를 분석해 보겠습니다. 범용성을 가진 언어로서 C/C++는 그만큼 많은 제작사가 있으며, 많은 컴파일러를 만들어 내놓았습니다. 그 중에는 상용도 있으며(비주얼 C++, 볼랜드 C++ 빌더, 솔라리스 cc 등), 상용에 못지 않은 공개용(gcc/g++, djgpp, mingw)도 있고, 상용이었다가 이제는 공개용(터보 C)으로 된 것도 있습니다. 이들은 일부는 순수한 컴파일러만을 가지고 있으며, 일부는 어셈블러와 링커까지 포함된 것도 있습니다. 또 어떤 것은 통합 환경을 제시하는 것도 있고, 어떤 것은 커맨드라인 실행만을 지원합니다.

이런 구분을 잘 이해하려면 어떤 언어든지 다음을 이해해야 합니다. 디버깅이 어려운 것은 이런 구분 없이 오류를 해결하려고 하기 때문입니다. 한 번의 빌드 중에는 다음과 같은 일이 발생합니다. 간단한 명령 하나를 내리는 것 같지만 소스는 전처리 과정을 거쳐 컴파일러에 들어가고, 컴파일되어 나온 어셈블 코드 혹은 메타 언어 코드는 어셈블러를 통해 목적 파일이 생기고, 여러 목적 파일들을 합하여 하나의 실행 파일을 만들게 됩니다. 이 과정에서 중간에 에러 메시지가 나오게 됩니다. 그 에러 메시지가 다음 중 어떤 과정에서 일어나는지를 이해하는 것이 디버깅을 돕는 빠른 길입니다.

ꊱ 전처리기(pre-processor)
ꊲ 컴파일러(compiler)
ꊳ 어셈블러(assembler)
ꊴ 링커(linker)

이런 구분을 몇 가지 에러 메시지를 통해 이해해 봅시다. 전처리기와 링커의 경우를 살펴보겠습니다. 처음에는 숨겨 있기 때문에 바로 알 수 없는 경우가 많거든요.



<그림 1> 빌드 순서 개요




전처리기
전처리기, 즉 컴파일러에 들어가기 전에 처리하는 대표적인 것은 다음과 같습니다.

ꊱ #ifdef/#else
ꊲ #define
ꊳ #include

컴파일러는 사실 앞의 구문을 이해하지 못합니다. 앞의 내용을 바탕으로 컴파일러에 소스 중 일부만을 넘긴다거나 치환하여 넘긴다거나 다른 소스를 포함시켜 넘기는 것입니다. 컴파일러가 보는 것은 앞에서 제외한 모든 것이라 보면 되겠습니다. 그 중 유의할 것은 ꊱ typedef, ꊲ #pragma이지요. typdef와 #define을 많이 비교하는데, 사실은 처리하는 위치가 다르다는 것을 기억해 두기 바랍니다. pragma는 표준화된 것이 아니므로 컴파일러마다 다르다는 것을 이해해야 합니다. 따라서 컴파일러마다 공통적인 것이 아니라면 #pragma 앞뒤로 컴파일러 특유의 매크로가 정의되었는지 확인하는 #ifdef가 오게 됩니다. 다음은 C++ 코드입니다.

/* filename: a.cpp */
int main()
{
printf("Hello, world\n");
return 0;
}

앞의 코드를 컴파일하면 제대로 되지 않습니다. “implicit declaration of function ‘int printf(...)’”라는 오류를 내면서 멈추게 됩니다. printf를 암묵적으로 선언하여 사용했기 때문이죠. 즉, 정확한 선언 없이 사용했다는 것입니다. 이것이 C라면 암묵적인 선언도 무사하겠지만, C++라면 반드시 선언해야만 함수를 사용할 수 있으므로 컴파일이 더 이상 진행되지 않습니다. 선언을 제대로 하는 것은 어떻게 보면 컴파일러 문제겠지만, 우리는 여기에서 #include 라는 헤더 파일이 빠져 있음을 알 수 있습니다. stdio.h를 열어 보면, 어딘가 printf 함수의 원형이 선언되어 있음을 알 수 있을 것입니다. 이것은 전처리되어 앞에서 헤더가 포함되어 함수를 선언해 주는 것이 빠져 있기 때문에 생긴 것입니다. 물론 #include하지 않고 해당 printf를 앞에 그대로 복사해 놓아도 상관없습니다. 그것은 전처리기를 통하지 않고 컴파일러 안에서 처리한 것이죠.
  


 전처리의 특성 파악
전처리의 특성을 알면 유용할 때가 많습니다. 컴파일 옵션 중에 미리 선언한 값을 넘기는 경우가 있습니다. 그 값에 따라 #ifdef를 만나면 소스의 특정 부분을 선택적으로 컴파일할 수 있게 되지요. 디버깅을 하다 보면, 헤더 파일을 열었을 때 두 가지 선택 중 어떤 것이 선택되었을까 궁금할 때가 있게 됩니다. 이런 경우를 대비해서라도 컴파일 전에 들어가는, 즉 전처리된 소스를 한번 보기로 합시다.
앞 코드를 #include 를 넣어 제대로 돌아가도록 한 뒤 전처리기만 통과하여 나온 소스를 보기로 합시다. 유닉스용 g++의 경우 -E 옵션을 넣어 주면(g++ -E a.cpp) 컴파일러에 들어가기 전의 코드를 구할 수 있습니다.

비주얼 C++ 6.0의 경우 도스 명령 창을 실행하여 해당 소스가 있는 곳으로 이동한 뒤 cl /E a.cpp라고 명령을 내려주면 됩니다. cl 명령이 실행되지 않을 경우 경로가 잡혀 있지 않기 때문입니다. cl은 Visual Studio\VC98\Bin 디렉토리에 있습니다. cl이 바로 gcc/g++과 같은 역할을 하는 컴파일러인 것이지요. 정확히 cl과 gcc/g++ 등은 컴파일러를 부르는 구동기입니다.

링커
링커는 컴파일되어 나온 목적 파일들을 서로 묶어 주는 역할을 하는 것입니다. 흔히 이런 오류를 많이 보게 됩니다. 다음은 앞의 소스에서 main을 test로 이름을 바꾼 것입니다. 즉, 전체 프로젝트에 main 함수가 없는 상황이죠.

<리스트 7> a.cpp
#include
int test()
{
printf("h");
return 0;
}

g++ a.cpp -o a
/usr/lib/crt1.o: In function ‘_start’:
/usr/lib/crt1.o(.text+0x18): undefined reference to ‘main’
collect2: ld returned 1 exit status

VC++
Compiling...
a.cpp
Linking...
LIBCD.lib(crt0.obj) : error LNK2001: unresolved external symbol _main
Debug/a.exe : fatal error LNK1120: 1 unresolved externals
Error executing link.exe.

<리스트 7>을 보면 컴파일러가 다름에도 불구하고 undefined reference, undefined external symbol이라는 비슷한 오류가 발생했습니다. 그리고 g++에서는 crt1.o, VC에서는 crt0.obj이라는 것을 보게 됩니다. 마지막으로 g++에서는 collect2(ld)가, VC에서는 link.exe가 오류를 내는 것을 확인할 수 있습니다. 흔히 처음 이런 오류를 만났을 때에는 고민하다가 main 함수가 없어서 발생하는 것을 알게 되지요. 좀더 살펴보면 오류를 낸 것은 링커입니다.

오류의 위치는 main이라는 reference 혹은 external symbol을 찾지 못했다는 것이고 공교롭게도 crt 뭔가를 처리하다가 발생했습니다. 즉, 컴파일은 모두 성공적으로 끝났으며, 그 다음 단계인 링킹에서 오류가 발생한 것이지요. VC의 경우 main 대신 _main을 찾다가 생긴 오류로 나오는데 이것은 윈도우에서 흔히 사용하는 오브젝트 내의 심볼 표현 방법으로 C언어에서 만든 함수 이름 앞에 “_”을 붙이기 때문입니다. 메시지의 정확한 의미는 C++ 컴파일러가 crt라는 런타임 오브젝트를 먼저 처리하며, 그 오브젝트 안에는 다른 프로그램 어딘가로부터 main 함수를 필요하도록 만들어져 있습니다. 따라서 그것을 연결시켜야 하는데 전체 프로젝트 안에 main이라는 오브제트가 없는데서 발생한 것입니다.

비슷한 오류는 특정 라이브러리를 추가로 넣어줘야 하는데 빠졌을 경우 발생합니다. 윈도우 프로그램을 작성할 때는 소켓 라이브러리를 따로 프로젝트 환경 설정에 넣어야 하는 경우가 있고, 유닉스의 경우에도 수학 관련 함수를 사용할 때나 쓰레드 관련 프로그램을 할 때 항상 추가적인 라이브러리를 지시해 주어야 하는 경우가 있습니다. 모두 undefined reference, undefined external symbol 오류입니다. 이것은 컴파일러(어셈블러 포함)를 통과한 소스가 최종적으로 생성되어 나온 오브젝트는 내부에 포함된 함수와 외부로부터 필요한 함수가 어딘가에 기록되어 있다는 뜻입니다. 물론 변수도 그와 비슷하게 기록되어 있습니다. 이런 것을 알아보는 것이 부속 프로그램으로 따라 다니게 됩니다.

디버깅을 잘하는 것에 대하여 지난 호에 덧붙이자면, 언어와 환경(라이브러리, OS, 프로토콜)을 구별할 줄 알아야하며, 현재 오류가 난 부분이 전처리에서 난 것인지 컴파일에서 난 것인지 링커에서 난 것인지 구별할 줄 알아야합니다.

빌드 과정 정리
전처리기를 통하여 나온 소스는 모든 변수와 함수는 사용 전에 선언되어 있어야 하며, 컴파일러 명세에 있는 문법구문이 아닌 것은 모두 typedef되어야 합니다. 따라서 전처리를 통과하여 나온 것에는 주석이 모두 제거되고, 모든 #define문이 치환되며, include된 것들은 통째로 하나의 파일로 되어 전달됩니다. 이 소스를 볼 수 있다면 여러분이 소스를 깊게 이해하고, 디버깅하는 데 도움이 될 것입니다.
컴파일러를 통해 나온(정확히는 컴파일러와 어셈블러를 거쳐 나온) 목적 파일은 C 런타임 라이브러리(crt)와 결합하여 실행 파일을 만들게 됩니다. 따라서 모든 목적 파일과 crt에는 undefined symbol된 것이 어딘가에는 존재해야만 합니다. 만일 존재하지 않는다면 그것은 DLL(혹은 유닉스의 .so, .sl 등)과 같은 동적 연결 라이브러리 파일명을 적어 두고, 로더에 의해 실행 도중에 바인드되도록 만들어집니다.

진정한 전문가의 자세
지금까지의 설명을 토대로 생각하면 디버깅 과정은 빌드 과정을 깊이 이해하는 것처럼 보입니다. 사실 디버깅은 빌드 과정에 대한 이해는 기본으로 하고, 빌드 이후에 있는 어셈블리어를 통한 디버깅이나 System call trace를 통한 프로그램의 흐름을 상상하며 문제의 위치를 찾아내는 것을 주로 지칭합니다. 중요한 것은 시행착오를 남길 때마다 표면적인 문제가 해결된 것에 만족하지 않고, 내부 동작을 좀더 음미해가면서 문제를 해결하려는 태도입니다.

따라서 디버깅은 고도의 좋은 의미의 해킹과도 관계 있는 것입니다. 더불어 좋은 코딩 습관은 고집스런 뭔가를 고수하는 것보다 보다 유연하게 팀 작업을 돕기 위한 모습으로 자신의 활동 범위를 넓히는 것이 좋습니다. 진정한 전문가는 코딩 스타일보다 아키텍처에 관한 얘기를 하는 것입니다. 아무쪼록 깊은 이해를 위해 좋은 습관과 컴파일러와 그 주위를 둘러싼 유틸리티와 친해지길 바라며, 세부 옵션도 수시로 확인하고 정교하게 도구들을 사용할 수 있는 여러분이 되길 바랍니다.

 어떻게 하면 버그를 빨리 발견할 수 있을 것인가? 이것은 모든 프로그래머의 공통적인 관심사입니다. 많은 언어들은 이것에 부응하기 위해서 문법적인 장치들을 고안해 넣었습니다. 문법에 그런 장치를 넣었다는 것은 컴파일러에 의해 사용자의 의도 중에 잘못될 소지가 있는 것을 지적할 수 있는 이점이 있습니다. C/C++가 어려운 이유는 변수 타입이 다양하며, 심지어 각 타입에 signed/unsigned가 추가되고, 포인터에는 const이냐 아니냐에 따라 생각해야 할 많은 성질들이 들어가기 때문입니다. 현존하는 언어 중에서 변수에 대한 가장 섬세(?)한 조절 기능이 있다고 해도 과언이 아닙니다. 어쨌든 버그를 컴파일 타임으로 끌어 올려 발견할 수 있도록 하는 것이 문법이 가지는 목적 중 하나이며, 그런 문법의 의도를 충분히 이해하고 언어를 사용할 수 있다면 컴파일러가 단순히 컴파일만을 목적으로 하는 것이 아니라 디버깅 툴(?)로도 사용될 수 있음을 알 수 있습니다. 세 가지 예를 들어 프로그래밍의 깊이를 조금 깊게 느껴보는 시간을 갖기로 하겠습니다.

의도를 나타내는 ‘const’
함수는 부르는 자와 불리는 자의 주고받는 행위입니다. 뭘 주고 뭘 받을지에 대한 것을 프로그래머가 의도한 대로 반영하게 되는데, 몇 가지 함수 call을 살펴보면서 이해해 봅시다.

// 함수 선언과 포인터의 의미
1 int strlen( const char * str );
- str이 가리키는 내용을 건드리지 말라.
2 void strncpy( char * buf, const char * source, int max );
- source가 가리키는 내용을 건드리지 말라, buf가 가리키는 내용은 바꾸어도 좋다.
3 int strcmp( const char * s1, const char * s2 );
- s1, s2가 가리키는 것을 건드리지 말라.
4 FILE * fopen( const char * filename, const char * mode );
- fopen에서 넘어 오는 포인터가 가리키는 것을 맘대로 바꾸어도 좋다.
5 int fclose( FILE * fstr );
- fstr가 가리키는 내용은 바꾸어도 좋다.

앞에 잘 알려진 표준 C 라이브러리의 문자열 처리와 파일 개폐 함수를 나열하였습니다. 선언된 내용 중 포인터에 대한 것을 말로 서술하여 표현해 보았는데, 자세히 읽어보고 원래 함수가 하는 일과 비교하여 이해하기 바랍니다. const가 꾸미는 내용은 변수가 아니라 변수가 가리키는 것의 속성입니다.

// const 선언의 예
6 const char * buf;
7 char const * buf;
8 char * const buf;
9 const char * const buf; 또는 char const * const buf;

1번의 의미는 “ *buf = ‘a’; ”와 같은 방법으로 가리키는 내용을 바꾸는 일로는 사용될 수 없다는 것입니다. 2번의 의미는 정확히 1번과 같습니다. 그것은 const가 “*” 앞에 있어 “*”를 수식하는 것이며, 변수가 가리키는 내용이 상수라는 것입니다.
3번의 의미는 1, 2번과 다릅니다. 즉 가리키는 내용을 바꾸는, “ *buf = ‘a’; ”는 허용되지만 “ buf++ ” 같이 포인터 값 자체를 바꾸는 것은 허용되지 않습니다. 4번의 예는 앞 두 제약을 모두 가지고 있는 것이 됩니다. 이런 생각을 염두에 두고 앞 코드의 1번 문자열 길이를 구하는 함수 strlen이 내부적으로 할 수 있는 예를 생각해 봅시다. 그것은 포인터가 가리키는 값, *str 값이 ‘\0’인지 비교하면서 str++를 수행하며 원하는 결과를 구하게 될 것입니다.
함수를 호출할 때 변수의 포인터를 원하는 함수가 있다고 가정합시다. 만약 그 함수가 const 포인터(const *)를 원한다면 호출 이후 그 내용이 전혀 변하지 않을 것이 확실합니다. 하지만 일반 포인터를 인자로 원한다면 설사 그 함수가 내용을 고치지 않는다고 문서에 씌어 있어도 사실상 보장할 수 없게 됩니다. 이것이 “프로그래머의 의도”입니다. 변하느냐 변하지 않느냐, API 상에 데이터 변형에 대한 의도를 나타내어 버그가 생길 수 있는 소지를 막는 것이 컴파일러가 디버깅을 도와 줄 수 있는 방법입니다. 다음은 const가 있어야 함에도 뺀 함수에 대해 컴파일러는 다음과 같은 오류를 반환하는 예입니다.

#include

void welcome( char * username ){
printf(“Welcome! %s\n”, username );
}
int main() {
const char * name = “hojin”;
welcome( name );
return 0;
}
결과 - compile warning:
warning: passing arg 1 of `welcome’ discards qualifiers from pointer target type

welcome은 분명 username이 가리키는 것을 사용만 할 뿐 바꾸는 것이 없습니다. 그런데도 선언이 const *가 아니므로, welcome 함수를 사용하기 위해 const를 무시하겠다는 내용입니다. 이것에 대한 해결책으로 name에 대한 const까지 없앤다구요? (char * name = “hojin”;) 제발 그러지 마세요. 꼬이는 지름길입니다.

의도를 나타내는 ‘const’ 멤버 함수
C++의 const 사용에 대한 예를 들기 전에 C++의 멤버 함수에 대해 살펴보기로 할까요. 모든 멤버 함수는 하나의 인자가 숨겨 있다고 생각해야 합니다. 바로 그 클래스에 대한 this 포인터입니다. 따라서 아무 것도 인자를 받지 않는 함수라 할지라도, 항상 this 포인터라는 최소 한 개의 인자를 받는다는 사실입니다. this 포인터가 모든 인자보다 먼저 전달된다고 생각합시다. 그러면, 이 this 포인터는 숨겨 있으므로 “const” 수식을 할 수 없지 않겠느냐고 생각할 수 있겠지만 그렇지 않습니다. 우선 C++의 const 사용에 대한 예를 들어 보겠습니다(<리스트 1>).

<리스트 1> C++ const 멤버 함수
#include
#include

class ZConfig {
protected:
char _strFileName[256];

public:
ZConfig(){};
~ZConfig(){};

void setFileName( const char * strFileName ) {
strncpy( _strFileName, strFileName, sizeof _strFileName );
}
const char * getFileName() const {
return _strFileName;
}
};

int main() {
ZConfig conf;
conf.setFileName("test.cfg");
cout << conf.getFileName() << endl;
return 0;
}

<리스트 1>은 getFileName 함수 뒤에 붙은 const의 예를 보여줍니다. 이것이 this 포인터에 대한 const 여부를 나타낸다고 생각하면 됩니다. 이것은 C로 해석하면 다음과 같은 형태로 이해할 수 있습니다. 컴파일될 코드는 아니지만 살펴보면,

setFileName( ZConfig * this, const char * strFileName ) {
strncpy( this->_strFileName, strFileName, sizeof _strFileName );
}

const char * getFileName( const ZConfig * this ) {
return this->_strFileName;
}

getFileName의 선언이 “const char * getFileName() const”가 아닌 “const char * getFileName()”으로 바꾸고 다음의 예에서 사용된다고 생각해 봅시다.

// getFileName의 const 여부에 따른 컴파일

class ZConfig {
..중략..
const char * getFileName() {
return _strFileName;
}
};

void run( const ZConfig * pConfig ) {
cout << pConfig->getFileName() << endl;
}

int main() {
ZConfig conf;
conf.setFileName(“test.cfg”);
run( & conf );
return 0;
}
결과 - Error:
In function `void run (const ZConfig *)’:
passing `const ZConfig’ as `this’ argument of `const char *ZConfig::getFileName ()’ discards qualifiers

C++에서는 const에 대한 처리를 warning으로 다루지 않고 error로 다루는 강력한 검사 기능을 제공하는데, 앞에 기록된 error는 “const char * getFileName()”와 같이 맨 뒤의 const를 제거한 형태로 컴파일할 경우에 발생합니다. 자, 여기서 개발자의 의도를 생각해 봅시다. 설계를 한다면 setFileName은 분명히 클래스 멤버 변수를 변경하는 의도가 있음을 알 수 있습니다. 따라서 이 함수 뒤에는 const가 따라오지 않을 것이라는 것이 분명합니다. 또한 run 함수의 의도는 ZConfig 객체가 읽기 전용으로만 사용될 것임을 예상할 수 있습니다. 그리고 그 안에서 사용된 getFileName도 멤버 변수를 읽는 함수라 생각되어 사용한 것입니다.
그런데 ZConfig에서 getFileName 함수 선언시 맨 뒤에 const를 넣어 두지 않으면, run 함수와 같이 객체의 const 포인터를 사용하여 멤버 함수를 호출하는 경우 const 포인터가 내용을 변화시키는 의도를 나타내므로 컴파일러는 오류를 내는 것입니다. 다음을 잘 생각해 보면 getFileName을 C 형태로 표현한 것이며, this 포인터가 const가 제거되는 상황임을 알 수 있습니다.

const char * getFileName( ZConfig * this ) {
return this->_strFileName;
}
void run( const ZConfig * pConfig ) {
cout << getFileName( pConfig ) << endl;
}

내부적으로는 this의 const 여부를 나타내는 것으로 해석하면, C++의 멤버 함수에 대한 것을 C의 표현법으로 해석할 수 있으므로 C++에서 const 함수라는 것이 왜 필요하게 되었는지 생각하는 데 도움이 될 것입니다. 이런 의도를 알게 되었다면, 다음부터는 컴파일러에게 객체의 변형에 대한 의도를 충분히 반영하여 장차 생길 수 있는 오류를 컴파일 타임 때 잡아내도록 노력합시다.

 의도를 나타내는 ‘static’ 함수
C에서 static 함수의 표면적 의미는 ‘외부에서 불러 쓸 수 없는 그 파일 내부에서만의 함수’로 알려져 있습니다. 도대체 이게 왜 필요할까요? 이런 문법이 갖는 의도를 알지 못하는 이상 어떤 함수에 static을 줄지 말지를 심각하게 고민하지 않고, static은 전혀 사용하지 않는 편리한(?) 프로그램을 작성하게 됩니다. 잠시 링커를 생각해 봅시다. 링커가 하는 일은 오브젝트 파일들을 엮어서 실행 파일을 만드는데 있습니다. 엮는다는 의미는 외부에서 사용할 것이므로 어디엔가 함수 이름을 대비해 두고, 전 오브젝트에서 이름이 충돌하지 않게 관리된다는 추가적인 행동을 요구합니다. 안에서만 쓸 것이라 생각한 것들도 말이죠. 다른 말로는 이름 공간을 더럽힌다고 말합니다. 사용자 의도를 잘 해석하는 컴파일러는 <리스트 2>와 같은 경고를 보내줍니다.

VC++의 경고를 해석하면 “local function으로 만들었는데, 어느 곳에서도 사용하지 않으니 제거하겠다”는 뜻입니다. 사용자의 의도를 정확히 해석한 것이죠. 만약 함수 앞에 static이 없었다면 이런 경고를 내지 않을 것입니다. “다른 곳에서 사용하는가 보다”라고 해석하는 것이죠. static 함수라고 명시해 놓으면 나중에 프로그램을 고치다가 더 이상 쓸모없는 함수가 될 경우 즉시 컴파일러로부터 경고가 나올 것입니다. 그것에 따라 그 함수를 제거하면 프로그램 흐름에 대한 가독성을 높여 줄 것입니다. 이 static과 관련하여 헤더 파일 작성법도 약간의 영향을 받습니다.

◆ static이 아닌 것은 extern이 생략된 것이며, 이런 함수들은 모두 외부에서 사용되는 것이므로 헤더 파일에 선언을 한다.

◆ static인 것들은 .c 파일 전방에 선언한다. 혹은 사용하는 함수는 항상 뒤에 작성한다.

의도를 나타내는 클래스의 ‘static’ 멤버 함수
클래스의 static 멤버 함수와 일반 멤버 함수의 가장 큰 차이는 앞에서 언급한 this 포인터가 암묵적으로 전달되는지의 여부에 따라 다릅니다. C++ 책에도 나오듯이 static 멤버 함수는 일반 멤버 변수/함수에 접근하지 못하며, 단지 static 멤버 변수/함수만을 접근 가능하다고 알고 있을 것입니다. 이것은 static 멤버 함수는 this 포인터가 넘어가지 않는 함수이기 때문입니다. 따라서 this 포인터가 있어야만 하는 일, 앞과 같이 일반 멤버 변수/함수를 사용하는 호출에 대해 컴파일 오류가 생길 것입니다.

쓰레드를 만들 때 쓰레드의 입구 함수에 대한 형을 맞추는 것을 주의해야 합니다. 윈도우의 경우 CreateThread 함수를 사용하며, 유닉스의 Posix 쓰레드의 경우 pthread_create 함수를 사용합니다. 이 함수들은 인자에 입구 함수를 요구하며 각각 다음과 같은 형태의 함수 포인터여야 합니다.

Windows 쓰레드: DWORD func(void *);
Posix 쓰레드: void * func( void * );

문제는 다음과 같이 쓰레드 입구 함수에 클래스의 멤버 함수를 넣고 싶을 때 발생합니다. <리스트 3>은 posix 쓰레드를 사용한 것으로, XThread 클래스를 만드는 예제라고 가정하고 그 클래스에 두 개의 멤버 함수를 넣어 봅시다. 하나는 static 멤버 함수이며, 다른 하나는 일반적인 함수입니다.

<리스트 3>에서 사용된 pthread_create의 세 번째 인자로 들어가는 쓰레드 입구 함수는 앞의 코드와 같은 C 함수만 가능하다고 생각할 수 있습니다. 하지만 XThread에서 static 멤버로 선언된 thread_entrance1은 해당 요구사항을 만족하게 됩니다. 바로 this 포인터가 넘어가지 않기 때문에 가능한 것이죠. 직접 컴파일해 확인해 보기 바랍니다. 윈도우의 경우도 비슷하게 테스트해 볼 수 있습니다. 자, 이렇게 만들었을 때의 이점은 어떻게든 this 포인터를 받을 수 있다면(void *로 받는 param에 넘겨서) XThread의 protected 변수까지 접근 가능하다는데 있습니다.

const, static이 C++에서 많이 확장된 것을 확인해 보았습니다. 이런 내용은 오류 메시지를 잘 분석해 얻어질 수 있는 개념들입니다. C++ 명세에서 나왔다기보다는 C용 라이브러리와 같이 써야만 하는 환경해서 생각하다 보면 에러 메시지를 통해 얻을 수 있는 것입니다. 디버깅을 하면서 발생하는 컴파일 에러 메시지는 어떤 예제보다도 더 머릿속에 쏙 들어오는 문제집이자 참고서입니다.

헤더 파일 열어 보기
헤더 파일을 자주 열어 봐야 합니다. 즉, 유닉스 계열에서는 /usr/include 디렉토리, 비주얼 C++ 6.0에서는 VC98/include 디렉토리에 들어 있는 파일들의 내용을 이해한다는 것입니다. 이 헤더 파일은 사실 프로그래밍 초기에는 별로 보고 싶지 않은 내용으로 가득 차 있습니다. 그런 마음으로 프로그래밍을 하다가도 가끔씩 열어보곤 합니다. 하지만 여전히 신기한 문법을 사용하는 것 같아 보입니다. 헤더 파일이 복잡하게 보이는 것은 이식성을 고려해 만들기 때문입니다.

① WIN32와 MAC을 고려한 선언 부분을 발견할 수 있습니다.
② 32비트와 64비트에 따라 파일을 다루는 함수 등에서 파일 크기 등을 고려하여 달라지는 부분이 있습니다.
③ C와 C++ 동시에 사용되는 헤더 파일이 대부분이고, C용 헤더 파일의 경우에는 extern “C” 등이 항상 보입니다.
④ CPU의 endian에 따라 달라지는 부분이 있습니다.
⑤ ANSI C 이전의 컴파일러를 고려해 선언되는 인자를 없애고 선언해야 하는 경우도 있습니다.
⑥ 표준화(ANSI, X/Open, POSIX, BSD 등)에 따라 선언이 안되는 경우도 있습니다.
⑦쓰레드에 따라 달라지는 부분이 있습니다.
⑧예외(Exception)를 지원하는 않는 컴파일러를 고려해 달라지는 부분이 있습니다.
⑨디버깅(NDEBUG, DEBUG)에 따라 달라지는 부분이 있습니다.
⑩CPU 등의 아키텍처에 따라 달라지는 부분이 있습니다.

이렇게 많은 경우의 수를 하나의 헤더로 해결해야 하는 것이지요. 디버깅을 잘하려면 때때로 헤더 파일들을 뒤져가면서 보는 것이 좋습니다. 또 헤더 파일들을 잘 보면 앞과 같은 복잡한 상황에 대처하는 방법도 익히게 되며, 알지 못했던 상황도 고려하게 되는 경우가 많습니다. 디버깅을 위해서라도 헤더 파일 읽는 것을 소홀히 하지 마세요. 또, 한 가지 방법만 고려해 프로그램을 작성하다 보면 나중에 미처 생각지 못한 부분들이 발생하게 됩니다. 따라서 평소에 어떤 이식성 문제들이 있는지를 염두에 두면 디버깅할 때 입체적인 접근을 할 수 있습니다.

메모리에 대한 이해
메모리에 대한 이해는 코드를 작성할 때 데이터가 어떤 부분에 들어가는지에 대한 이해도입니다. 많이 접하는 네 가지의 예를 들어 보면 다음과 같습니다.
◆ 데이터
◆ static 변수(BSS)
◆ 자동 변수(stack)
◆ 힙

이중에서 데이터 영역과 BSS라 알려진 static 데이터 영역은 컴파일된 코드 내에 그 영역이 설정됩니다. 간단히 설명하면 데이터들은 static 변수에 들어가는 초기 값과 변수들이 저장되는 곳이며, BSS는 초기 값이 없는 static 변수가 들어가는 것이라고 보면 됩니다. 초기 값이 없고 변수 길이 정보만 잡힙니다(검색어 : data bss stack 세 개를 동시에 주면 자세한 내용을 더 알 수 있습니다). 다음 코드를 통해 익혀 봅시다(유닉스에서는 nm과 objdump를 통해서 확인해 볼 수 있습니다).

#include
const char * B;
const char * D = “R”;
int main() {
static int B2;
char * S = (char *) malloc( 32 );
S[0] = ‘H’;
return 0;
}

BSS 영역 : B, B2
데이터 영역 : D, “R”
스택 영역 : S
힙 영역 : S가 가리키는 곳, ‘H”가 저장되는 곳

앞 내용에 대한 것은 익숙해질 때까지 symbol 확인 유틸리티를 사용하고, disassemble 코드를 분석해 가면서 이해해야 합니다.

힙에서 흔히 발생하는 오류 형태
힙과 스택을 사용하는 데 생길만한 오류는 거의 바운더리 체크(boundary check)입니다. 먼저 GNU에서 제공하는 GNU C 라이브러리가 구현된 것의 개념만으로 설명하자면, malloc이 호출될 때 C 라이브러리는 요구한 크기의 연속된 메모리 공간(세그먼트라고 합시다)을 반환하여 줍니다. 그것은 다음과 같이 표현됩니다.

malloc에 의해 요청되는 크기에 8바이트 정도 추가 정보(A,B)가 먼저 기록되고 P 값이 리턴되어 넘어 옵니다. 커널에 요청하는 최초 메모리(A)가 넘어오는 것이 아니군요! 그리고 free로 해제할 때도 마찬가지로 P 값을 받아 8바이트를 뺀 위치에서 현재 해재돼야 할 길이를 구해 삭제 표시를 하게 되는 것입니다. 이런 이유로 free에서는 해제될 크기를 인자로 받지 않습니다. C++의 new, delete에서도 배열을 할당한 경우 delete[]를 호출할 때 원 배열의 크기를 넣지 않는 것도 같은 구현 방법으로 되어 있으며, 어떤 C++의 STL 구현에서 string에 대한 것도 내부 정보를 앞과 같은 방법으로 구현하는 것을 본 적이 있습니다. 중요한 것은 힙에서 버퍼 오버플로우가 일어날 때, 다음 블럭의 크기 정보를 망가뜨려 해제하거나 추가 할당할 때, 빈 공간에 대해 추정할 때 오동작하게 됩니다. 만약 오동작이 일어날 경우 이런 힙의 구조를 이해하면 추적하는 데 도움이 될 것입니다. new, delete도 결국에는 malloc, free를 이용하는 것입니다. 즉, 메모리를 할당받은 후 할당된 메모리에서 생성자나 소멸자가 불리는 것으로 이해하면 됩니다. 참고로 유닉스 계열에서 커널에 힙을 요청할 때는 내부적으로 brk 함수를 사용합니다.

스택에서 흔히 발생하는 오류 형태
스택은 흔히 번지 값이 감소하며 할당된다고 알려져 있습니다. 다음의 예와 같은 경우 <그림 2>와 같은 형태의 주소 배열을 갖습니다.

oid func() {
int x;
int y;
char buf[32];
}
buf가 나중에 선언되었지만 그 영향은 x, y에게 미친다는 것입니다. 또 스택이 사용되는 곳은 함수 호출과 관련되어 있습니다. 함수 호출은 calling convention이나 CPU 아키텍처마다 다릅니다. calling convention에 따라 함수에 넘어간 변수를 불린 함수가 해제하는 것이 있기도 하고, 부른 쪽에서 책임지고 해제하는 경우도 있습니다. 어떤 calling convention이나 CPU 아키텍처에 따라 함수 호출 인자의 몇 개까지는 레지스터에 넘기는 것도 있습니다.

함수가 호출될 때 일어나는 일은 돌아올 리턴 주소가 먼저 저장되며, 다음으로 프레임 포인터라는 것이 저장됩니다. 프레임은 각 함수의 단위로 생각하면 되는데, 그 함수 내에서는 모든 자동 변수들이 같은 베이스 포인터를 가지고 베이스 포인터로부터 얼마나 떨어져 있느냐로 표현됩니다. 스택 포인터는 늘 변하기 때문에 함수에 들어오자마자 프레임 포인터를 현재의 스택 포인터 값으로 바꿉니다. 예를 들어 <그림 2>를 살펴보면, frame pointer = 136이라 하고, frame pointer - 0을 x, frame pointer - 4를 y frame pointer - 36을 buf라 사용하는 것입니다. 따라서 하나의 함수가 시작하면 이전 함수의 프레임 포인터를 저장해 두어야 돌아가기 전에 복원됩니다(x86에서는 어셈블리어 leave 명령도 참고하세요).

스택 버퍼 오버플로우 공격이라 일컬어지는 것의 원리도 여기에 있습니다. buf에 쓰여지는 내용을 buf의 범위를 벗어난 리턴 어드레스까지 덮어 쓰게 만들고, 원래 호출한 함수로 돌아가는 것이 아니라 buf 내용 중간으로 점프하게 만들어 원하는 일을 하게 만드는 공격법이지요. 이런 이유 때문에 buffer 범위를 확인하지 않는 함수는 절대 사용 금지입니다(두 함수가 대표적입니다 : strcpy, sprintf). 정리하면 스택이 잘못될 경우 다음과 같이 영향을 미칠 수 있습니다.

① 갑자기 어떤 변수 내용이 바뀌었을 때, 그보다 아래 선언된 변수에서 영향을 받았을 가능성
② call stack이 이상하게 바뀌었을 경우에는 버퍼에 쓴 내용이 저장된 프레임 버퍼와 리턴 주소까지 영향을 준다.

원리를 알면 왜 그렇게 사용하지 말아야 하는가를 쉽게 이해할 수 있습니다. 원리를 모른 채 경고 메시지 제거나 프로그래밍 가이드를 따른다면 재미가 덜하겠죠? 디버깅은 경험한 만큼 언어와 컴파일러를 구현 원리까지 생각하도록 유도합니다.

C++의 함수 이름 꾸미기
C++에서 가장 초반에 공부하는 것 중의 하나는 함수 인자가 다르면, 같은 이름의 함수를 여러 개 만들 수 있다는 함수 오버로딩에 대한 것입니다. 그리고 C++에서 C에 대한 코드를 사용하려면 항상 extern “C” 지시자를 선언문에 넣어 두어야 제대로 사용되는 것을 배우게 됩니다. <리스트 4>를 보면 a1.c의 코드를 b1.cpp에서 사용하려면, a1.c에서 만든 함수에 extern “C”를 선언하는 것을 볼 수 있습니다.

이렇게 하는 이유는 C에 의해 생성되는 심볼과 C++에 의해 생성되는 심볼이 다르기 때문입니다. C에 의해 생기는 것은 함수명 그대로 심볼이 생기는 반면, C++에 의해 생기는 것은 함수명 뒤에 함수 인자에 따른 장식 문자들이 들어갑니다. <리스트 4>와 <리스트 5>의 a1.c, a2.cpp가 컴파일된 다음에 생기는 오브젝트의 심볼들을 살펴보면(<리스트 6>) C++에 의해 생성된 테스트에 대한 것은 “__Fii”라는 문자들이 붙게 됩니다. 아마 integer, integer라는 뜻이 맨 뒤에 붙었나 봅니다.

이런 문자들은 함수 인자에 따라 혹은 클래스 멤버에 따라 다양하게 준비되어 있습니다(윈도우에서는 OBJ 파일들에 대해 dumpbin /symbols라는 명령과 옵션으로 확인해 볼 수 있습니다). 이렇게 예상한 함수 뒤에 붙는 것을 함수 이름 꾸미기(function name mangling 혹은 function name decoration이라 하는데 적절한 번역이 없습니다)라고 합니다. 이렇게 하는 주된 이유는 함수 중첩 선언(function overloading)을 하기 위해 사용되는 오브젝트 파일 상에서의 기법입니다. 여기서 잘 생각해 볼 것은 C로 됐든 C++로 됐든 오브젝트 파일이 생기면 내부적으로는 사실상 같이 취급되는 것인데, 마찬가지로 포트란이나 파스칼도 오브젝트가 되면 링커에 의해 결합되는 것만을 남게 됩니다.

이런 원리를 알면 링커가 혼동하지 않도록 소스에서 오브젝트에 포함되는 심볼들의 이름을 일치시키는 것이 프로그래머가 해야 할 일입니다. 바로 extern “C”라는 선언문 앞 지시자의 역할이 함수 이름 꾸미기를 하지 말 것을 지시하는 것이지요.

nm a1.o
00000000 t gcc2_compiled.
U printf
00000000 T test

nm a2.o
00000000 t gcc2_compiled.
U printf
00000000 T test__Fii

재미로 한 가지 해 봅시다. 앞의 소스를 보면 b2.c라는 소스를 볼 수 있습니다만, 이것은 C++로 되어 있는 코드를 C에서 사용한 예를 나타낸 것입니다. 대개의 경우와 반대가 되겠지만, C에서 C++의 함수 이름 꾸미는 규칙에 대해 알고 일부러 없는 함수를 선언하여 링크되도록 유도한다고 해서 안 될 것이 없습니다. 다만 b2.c 같은 예제는 철저하게 재미로 만든 소스입니다. 결코 실전에서는 사용되어서는 안 됩니다. 왜냐하면 C++의 함수 이름 꾸미기 규칙은 컴파일러마다 완벽(?)하게 다르기 때문에 이름 규칙을 알고 소스를 작성하는 것은 무의미한 일입니다. b2.c 코드 또한 gcc에서 만드는 이름 규칙을 생각해서 만든 것이므로 다른 환경에서는 되지 않을 것입니다. 따라서 서로 다른 컴파일러에 의해 만들어진 C++ 코드는 이름 꾸미는 방식이 다른 것으로 인해 쉽게 같이 쓸 수 없는 단점이 있고, C++ 라이브러리는 컴파일러마다 독립적으로 존재한다는 것을 나중에 확인해 볼 수 있을 것입니다.

디버깅=프로그래밍 수련 과정
이번 호에서는 서로 관련 없는 사항이긴 하나 세 가지 정도를 들어 디버깅을 하면서 내부에서 돌아가는 원리를 알게 되는 일에 중점을 두어 설명하였습니다. 디버깅은 해킹과 같은 고도의 입체적인 접근과 연결되어 있는 개발 행위(?)입니다. 따라서 디버깅은 단순한 문제 해결 관점보다는 좀더 테스트 코드를 만들어 보게 하고, 언어와 환경에 대한 깊은 이해를 돕는 프로그래밍 수련회와 같은 것이라고나 할까요?

세 번에 걸쳐 디버깅에 대한 감각이 잡히려는 사람들을 대상으로 어떤 관점을 가져야 될지 주제를 골라 나열하였습니다만, 아는 것을 말로 표현하는 것이 쉽지만은 않다는 것을 느꼈습니다. 필자에게 따로 연락해도 도움을 드리겠지만, 되도록 지적인 공유를 위해 필자가 자주 이용하는 http://bbs.kldp.org/에 질문하면 여러 사람으로부터 다양한 도움을 얻을 수 있을 것입니다. 개발자들이 모두 디버깅에 자신을 갖고, 명랑한 코딩을 하기를 상상해 보면서 이만 연재를 마칩니다.

+ Recent posts