5장. Trac On Windows - Trac을 실제로 사용해 보자 지금까지 Trac과 서브버전 설정에 대해서 알아보았습니다. 그럼 실제로 Trac을 어떻게 사용하는지 알아보아야 겠죠? 1. 서브버전 저장소에 소스 올리고 수정하기 일단 서브버전 Test 저장소에 파일들을 올리고 갱신해 보도록 하겠습니다. 이 작업은 Trac을 사용하는데는 상관이 없지만 Trac이 서브버전 저장소와 연계가 되는지라 아무것도 없으면 뭐 할게 없어서... 제가 어떤 작업을 수행했는지만 나열하겠습니다. - 저장소에 trunk/src, branches, tags 등의 디렉토리를 만든다. - 아래의 helloworld.c 파일을 만들어 tags 디렉토리에 올린다. - tags디렉토리는 작업 디렉토리가 아니므로 다시 trunk/src 디렉토리에 올리고 tags디렉토리에서는 삭제한다. 우선 여기까지 작업을 합니다. 다음은 현재의 helloworld.c 파일의 내용입니다. #include int main(int argc, char *argv[]) { printf("Hello World... Hi %s!\n", argv[1]); return 0; } 자 여기까지 했다면 Trac의 Test프로젝트(http://localhost:88/projects/Test)에 접속해 보도록 하겠습니다. 메인 화면의 메뉴에서 "시간이력"을 클릭해 봅니다. 그러면 다음과 같이 나옵니다.
((-------IMAGE-------))

보시면 서브버전에 Commit할때마다 적었던 메모가 보여집니다. 각 변경사항을 클릭하면 다음과 같이 상세 내용이 나타납니다.

((-------IMAGE-------))

((-------IMAGE-------))

메뉴에서 "소스 브라우져" 클릭하셔서 보시면 서브버전의 저장소를 그대로 보여줍니다. trunk/src로 들어가셔서 helloworld.c 파일을 클릭하면 소스의 내용도 볼 수가 있네요. 참 한가지 말씀드릴 것은 소스에 한글을 사용하셨고 UTF-8로 작성하시지 않았다면 한글이 깨져 보일 수 있습니다. 이럴때는 trac저장소/conf/trac.ini 파일에서 default_charset을 찾아서 euc-kr이나 cp949로 바꿔 주세요. 2. 티켓과 로드맵 사용하기 Trac은 서브버전과 연동되고 버그리포트나 작업관리를 하는 것이 목적입니다. 여기에 사용하는 것이 티켓과 로드맵 기능입니다. 문제점이 생겼거나 개선사항이 생겼다면 해당 프로젝트에 티켓을 생성합니다. 그런 다음 프로젝트 참가자 중 한사람이 그 티켓을 받아 자신의 작업리스트에 추가하여 처리하거나 다른 사람에게 건내주거나 하면서 일을 처리하게 됩니다. "백문이불여일견"!!! 예를 들어 설명하고 사용방법을 보이도록 하겠습니다. 위의 helloworld.c가 오픈 프로젝트 였고 누군가가 사용해 보고 문제점이 생겨 이 Trac에 접근하는 시나리오로 이야기를 풀어 보겠습니다. 어떤 사람이 helloworld.c를 이용하다가 프로그램 실행시 아큐먼트를 입력하지 않으면 오류가 발생하는 것을 보고 이 Trac에 접속하였습니다. 그리고는 메뉴 중 "새로운 티켓"을 선택하고 다음과 같이 입력합니다.

((-------IMAGE-------))

위와 같이 작성하고 "새로운 티켓 추가" 버튼을 눌러 글을 올렸습니다. 이로써 하나의 티켓이 만들어 졌습니다. "nicholas"라는 사람이 이 시스템에 들어와 "시간이력"이나 "티켓들보기"메뉴를 클릭하여 새로운 티켓이 만들어 졌음알아고 열람해 봅니다.

((-------IMAGE-------))

한가지... 작성자가 nicholas라고 되어 있는데.. 이부분은 제가 예제를 만들면서 nicholas로 로그인하고 혼자 글올리고 수정하고 답변올리고 해서 그런 것입니다. 상황에 맞게 해석해 주세요. (죄송합니다.) 사실 지금까지 설명한 배경으로는 작성가 익명이 되어야 할 것입니다. 그건 그렇고 여기서 자세히 보아야 할 부분은 담당자 입니다. 이 티켓을 처리할 담당자가 somebody로 되어 있습니다. 이는 티켓을 누가 처리해야 할지 모른다는 뜻도 되고 티켓의 처리가 누군가에게 맞겨지지 않았다는 뜻도 됩니다. 아무튼 이 nicholas라는 프로젝트 참여자가 이 티켓을 보고 자신이 처리하는게 맞을 것 같아서 "응답하기"버튼을 클릭해서 답변을 작성합니다.

((-------IMAGE-------))

답글을 쓰고 아래쪽 중간에 마일스톤과 version을 바꾸었습니다. 마일스톤은 제품명이나 프로젝트명 쯤으로 생각하시면 될 것 같습니다. 사실 마일스톤 명을 먼저 수정해야 하는 것이 정상이지만 여기서는 Trac저장소를 생성하면 기본으로 생기는 마일스톤과 버전을 가지고 그대로 사용하고 있습니다. 어찌됐든 제일 아래 쪽에 "티켓에 대한 동작" 부분에서 자신이 직접 처리하겠다는 의사로 "티켓 수락하기"를 선택하였습니다. 이 티켓의 변경사항을 보면 다음과 같음을 알 수 있습니다.

((-------IMAGE-------))

보면 owner가 somebody에서 nicholas로 변경된 것을 보실 수 있습니다. 그래서 nicholas라는 사람은 helloworld.c파일을 수정하고 있었는데... 또 다른 사람이 이번에는 개선사항으로 "사용법을 보여 출력하게 해 달라는" 티켓을 또 추가합니다. 그래서 또 다시 nicholas 티켓을 확인 하고 이것 역시 답변을 쓰고 마일스톤1로 가져옵니다.

((-------IMAGE-------))

이제 "로드맵" 메뉴의 "마일스톤1"에 다음과 같이 두개의 티켓이 접수된 것(활성화된 티켓들)이 보여지게 됩니다.

((-------IMAGE-------))

nicholas는 이 두개의 티켓을 처리하는데 힘을 써서 다음과 같이 helloworld.c의 소스를 변경하였습니다. #include int main(int argc, char *argv[]) { if(argc\n", argv[0]); return 0; } printf("Hello World... Hi %s!\n", argv[1]); return 0; } 이 소스를 서브버전의 저장소에 체크인시키고 Trac에서 "시간이력"을 보면 다음과 같이 나타납니다.

((-------IMAGE-------))

자 이제 열린 티켓을 닫아야 겠군요.

((-------IMAGE-------))

받았던 두개의 티켓으로 가서 각각 하나씩 제일 하단에 "다음 방법으로 해결됨"을 선택하고 확인항 티켓을 닫습니다. 해당 티켓의 변경사항을 보면 티켓이 발생한 시기부터 경과한 내용 및 시간을 모두 알 수 있습니다.

((-------IMAGE-------))

다시 메뉴의 "시간이력"으로 들어가 보면 마일스톤1의 활성화 되었던 티켓들이 모두 닺쳐 진척도가 100%가 된 것을 알 수 있습니다.

((-------IMAGE-------))

메뉴 중 "소스브라우져"에 가서 소스를 보고 비교하기를 누르면 다음과 같이 변경된 내용이 무엇인지도 비교하여 보여줍니다.

((-------IMAGE-------))

그림이 많아서 조금 보기 힘드신가요? 대충 글만 읽어보고 그냥 실습을 한번 해보세요. 어렵지 않습니다. 금방 따라하실 수 있습니다. 3. 웹어드민으로 Trac관리하기 우리가 이미 설치한 웹어드민 플러그인이 있었습니다. 이번에는 이것의 사용법에 대해서 알아 보도록 하겠습니다. Trac 저장소에 우리는 admin이라는 계정에 관리자 권한을 주었었습니다. 그래서 Trac에 admin으로 로그인을 하면 Admin이라는 메뉴를 메뉴바의 맨 오른쪽에서 찾을 수 있습니다.

((-------IMAGE-------))

이 Admin이라는 메뉴를 클릭하면 현재 프로젝트의 Trac을 관리할 수 있는 화면이 나타납니다. Trac의 관리란 주로 저장소의 권한 설정이나 플러그 인을 설치 및 제거 그리고 티켓의 컴퍼넌트, 마일스톤 등을 관리하는 것을 이야기 합니다. 특이한 사항은 없으므로 그냥 몇가지 캡쳐된 화면만을 보이도록 하겠습니다.

((-------IMAGE-------))

Admin의 메뉴인데 메뉴의 각 항목을 관리할 수 있는 기능이 있다고 생각하시면 되겠네요. 크게 Trac의 일반 관린 모드인 "General"과 Ticket을 관리할 수 있는 "Ticket System"으로 구성되어 있습니다.

((-------IMAGE-------))

기본 설정(Basic Settings)에서 나오는 화면입니다. 프로젝트 이름이나 URL, 프로젝트에 대한 설명을 수정할 수 있습니다. 여기서 URL은 Trac의 웹페이지 좌측 상단의 trac로고를 클릭했을 때 가야할 URL을 설정하는 부분입니다.

((-------IMAGE-------))

로깅(Logging)메뉴에서 나오는 화면입니다. 로그를 어디에 남길지 어느 정도의 수준으로 남길지등을 설정할 수 있습니다. 또한 Log파일의 이름 및 경로를 지정할 수도 있습니다.

((-------IMAGE-------))

권한(Permissions)메뉴에서 나오는 화면 중 하나입니다. 현재 저장소에 설정된 권한을 표시하며 선택해서 해당 권한을 삭제할 수 있습니다.

((-------IMAGE-------))

권한(Permissions)메뉴에서 나오는 화면 중 하나입니다. 현재 저장소에 특정한 사용자 혹은 그룹으로 권한을 설정할 수 있습니다.

((-------IMAGE-------))

권한(Permissions)메뉴에서 나오는 화면 중 하나입니다. 특정한 사용자를 존재하는 특정 그룹에 포함 시킬 수 있습니다.

((-------IMAGE-------))

플러그인(Plugins)메뉴에서 나오는 화면 중 하나입니다. 현재 설치된 Trac플러그인의 리스트를 볼 수 있습니다. "Uninstall"버튼을 클릭해서 설치된 플러그인을 삭제할 수 있습니다.

((-------IMAGE-------))

플러그인(Plugins)메뉴에서 나오는 화면 중 하나입니다. 다운받은 플러그인 파일을 이 화면에서 선택하여 설치할 수 있습니다. 참고로 http://www.trac-hacks.org/ 에서 다양한 Trac 플러그 인들을 찾을 수 있습니다. 다음으로 Ticket을 관리하기 위한 메뉴들입니다.
((-------IMAGE-------))

컴포넌트(Components)메뉴에서 나오는 화면 중 하나입니다. 티켓 생성시 표시될 컴포넌트의 종류를 보여줍니다. 또한 해당 컴포넌트의 소유자를 표시합니다. 리스트에서 특정 컴포넌트를 선택해서 삭제할 수도 있습니다.

((-------IMAGE-------))

컴포넌트(Components)메뉴에서 나오는 화면 중 하나입니다. 티켓 생성시 표시될 컴포넌트에 소유자를 정하여 추가할 수 있습니다.

((-------IMAGE-------))

마일스톤(Milestones)메뉴에서 나오는 화면 중 하나입니다. 티켓 생성시 표시될 마일스톤의 현재 리스트를 보여줍니다. 특정 마일스톤을 선택하여 삭제할 수 있습니다.

((-------IMAGE-------))

마일스톤(Milestones)메뉴에서 나오는 화면 중 하나입니다. 티켓 생성시 표시될 새로운 마일스톤을 추가할 수 있습니다. 또한 추가하는 마일스톤의 완료일을 YYYY-MM-DD의 형식으로 날짜로 지정할 수 있습니다.

((-------IMAGE-------))

우선순위(Priorities)메뉴에서 나오는 화면 중 하나입니다. 티켓 생성시 표시되는 우선순위의 목록을 보여줍니다. 우선순의 값을 변경할 수도 있으며 특정 우선순위를 선택하여 제거할 수도 있습니다.

((-------IMAGE-------))

우선순위(Priorities)메뉴에서 나오는 화면 중 하나입니다. 티켓 생성시 표시될 새로운 우선순위를 추가합니다.

((-------IMAGE-------))

Severities 메뉴에서 나오는 화면 중 하나입니다. 티켓 생성시 표시되는 Severity의 목록을 보여주게 되는데 초기에는 아무 Severity가 설정되지 않아 보여지지 않고 위와 같은 화면이 나타납니다. Severity가 추가되면 다음과 같은 화면이 표시됩니다.

((-------IMAGE-------))

우선순위와 화면 및 기능이 같습니다.

((-------IMAGE-------))

티켓 종류(Ticket Types)메뉴에서 나오는 화면 중 하나입니다. 티켓 생성시 표시되는 티켓 종류의 목록을 보여줍니다. 표시될 순서 값을 변경할 수도 있으며 특정 티켓 종류를 선택하여 제거할 수도 있습니다.

((-------IMAGE-------))

티켓 종류(Ticket Types)메뉴에서 나오는 화면 중 하나입니다. 티켓 생성시 표시될 새로운 티켓 종류를 추가할 수 있습니다.

((-------IMAGE-------))

버전(Versions)메뉴에서 나오는 화면 중 하나입니다. 티켓 생성시 표시되는 버전의 목록을 보여줍니다. 특정 버전을 선택하여 제거할 수도 있습니다.

((-------IMAGE-------))

버전(Versions)메뉴에서 나오는 화면 중 하나입니다. 티켓 생성시 표시될 버전을 추가할 수 있습니다. 지금까지 Trac Web Admin의 사용법을 알아보았습니다. 뭐 그리 어려운 내용이 아니므로 그냥 한번 읽어보시고 실습을 한번 해보시기 바랍니다. 이렇게 해서 Trac, 서브버전, 아파치 등의 설치 및 설정 방법 그리고 사용법 등을 알아보았습니다. 서브버전 클라이언트를 사용하기 위한 구체적인 명령들에 대해서는 살펴보지 않았지만 윈도우즈에서 작업할 것이라면 "TortoiseSVN"이라는 GUI툴을 사용할 수도 있으므로 어렵지 않게 사용할 수 있을 것입니다. 참고로 TortoiseSVN은 http://tortoisesvn.net/ 에서 다운받아 사용할 수 있습니다. 한글 언어팩도 있으므로 그것도 다운받아 설치하셔서 사용하십시오. 지금까지의 글이 여러분의 프로젝트 관리에 많은 도움이 되었길 바랍니다.
by Anna 안나 2008. 7. 11. 21:19
이번 장의 내용은 Trac을 사용하는데는 꼭 하지 않아도 상관없는 내용입니다. 하지만 Trac이 서브버전을 사용하고 있고 서브버전 설치에 대해서도 알아봤으니 서브버전을 설정하는 법에 대해서도 알아보도록 하겠습니다. Trac만을 사용하겠다시는 분은 이번장을 넘어가셔도 상관없습니다. 4장. Trac On Windows - Subversion 설정 및 실행해보기 3장까지 해서 Trac을 설정해 보았습니다. 프로젝트 참여자 각자 모두가 어차피 서브버전을 설치해서 사용할 거니깐... 서브버전을 꼭 웹과 연동되어야 하는 것은 아닙니다. 하지만 웹과 연동을 시켜 놓으면 좋은 점이 있습니다. 먼저 웹에서 소스를 열람해 볼 수 있습니다. 그런데 이부분은 Trac을 통해서도 가능합니다. 또 다른 장점은 서브버전으로 소스를 체크아웃 할때 svn 프로토콜 뿐만이 아니라 http프로토콜도 사용할 수 있습니다. http프로토콜을 사용할 수 있다는 것은 일반적으로 방화벽 때문에 생기는 문제들을 해결할 수 있습니다. 왜냐면 80포트는 일반적으로 풀려 있기 때문이죠. 설정할 필요가 있던 없던 설명은 하도록 하겠습니다. 자 우리 한번 해봅시다. 1. 서브버전과 아파치와의 연동 이미 3장에서 아파치에 서브버전에 관한 Test프로젝트 저장소도 만들었고 서브버전 모듈들도 이미 설정했고 가상디렉토리도 설정해서 간단한 테스트도 해 보았습니다. 서브버전 가상디렉토리 설정 이야기는 여기서 부터 시작합니다. 3장에서 httpd.conf에 설정했던 내용을 다시 한번 보겠습니다. DAV svn SVNPath "D:/Trac/SvnRepo/Projects/Test" ▲ dav_svn_module 테스트 페이지 설정 위에서 DAV라는 것은 "Distributed Authoring and Versioning"(분산 저작 및 버전관리)의 약자로 여기서는 정확히 "Web-based Distributed Authoring and Versioning"(웹 기반 분산 저작 및 버전 관리)가 되겠습니다. 즉 Web을 이용해서 웹페이지를 올리는 것 같지 한다는 이야기죠. 일반 웹브라우저는 WebDAV를 지원하지는 않구요. 특수한 클라이언트가 있어야 합니다. 여기서는 아파치에 서브버전의 DAV모듈을 로드하여 서브버전 클라이언트와 WebDAV프로토콜을 사용하여 소스를 받고 올리게 한다는 것입니다. WebDAV에 대한 자세한 사항은 http://www.webdav.org/ 를 참조하세요. 1) 인증 처리 추가 위의 설정의 가상디렉토리를 접근하는 URL은 http://localhost:88/svn/Test입니다. 그런데 이곳은 아무나 접근을 해서 볼 수 있게 되어 있습니다. 그래서 인증을 추가해 보겠습니다. 인증 파일은 3장에서 만들었던 D:\Trac\.htpasswd 파일을 사용하도록 하겠습니다. DAV svn SVNPath "D:/Trac/SvnRepo/Projects/Test" AuthName "Subversion Test repository" AuthType Basic Require valid-user AuthUserFile "D:/Trac/.htpasswd" ▲ 서브버전 인증 추가 설정 이제 다시 웹으로 접속해 보면 아까와는 달리 BASIC인증 대화상자가 나타납니다.
((-------IMAGE-------))

로그인이 되면 소스의 내용을 볼 수 있습니다. 2) 다중 프로젝트 설정 Trac에서도 그랬지만 서브버전이 하나의 프로젝트만 관리하지는 않겠죠? 그래서 여러 프로젝트를 보기 위한 설정을 하도록 해보겠습니다. D:\Trac\SvnRepo\Projects 디렉토리에 Test 서브버전 저장소 뿐만 아니라 ProjectA, ProjectB라는 저장소도 생성하십시오. 귀찮으면 Test를 복사해서 이름이라도 바꾸십시오. 그런 다음 httpd.conf파일에서 아까 설정했던 부분을 다음과 같이 변경합니다. DAV svn SVNParentPath "D:/Trac/SvnRepo/Projects" AuthName "Subversion Projects" AuthType Basic Require valid-user AuthUserFile "D:/Trac/.htpasswd" ▲ 다중 프로젝트 설정 여기서 조금 특이한 부분은 SVNParentPath입니다. 이게 한단계 상위의 Path를 맵핑시키고 각각의 프로젝트를 http://localhost:88/svn/ProjectA, http://localhost:88/svn/ProjectB, http://localhost:88/svn/Test URL로 접근할 수 있도록 해줍니다. 웹브라우져 뿐만이 아니라 서브버전 클라이언트로도 같은 URL로 접근할 수 있습니다. 예를 들면 다음과 같이 해서 체크아웃 할 수 있습니다. C:\testsvn>svn co http://localhost:88/svn/Test Test 체크아웃된 리비전 0. ▲ 아파치와 연동하여 저장소 소스 체크아웃하기 위에서 보면 서버를 지정하기 위해 http프로토콜을 사용하고 있음을 알 수 있습니다.
만약 웹브라우저로 소스를 열람하는 것과 체크아웃하는 것은 아무나 할 수 있도록 하고 커밋은 지정된 사용자만 할 수 있게 하려면 위의 내용을 다음과 같이 바꾸면 됩니다. DAV svn SVNParentPath "D:/Trac/SvnRepo/Projects" AuthName "Subversion Projects" AuthType Basic AuthUserFile "D:/Trac/.htpasswd" Require valid-user ▲ 모두에게 열람 및 체크아웃하게 하고 지정된 사용자만 커밋 가능 설정 여기서 커밋 가능한 사용자는 따로 서브버전 저장소에 설정하여야 합니다. 2. 서브버전 서버(svnserve) 사용 어찌 어찌 하다보니깐 순서가 바뀐 것 같은 느낌이 듭니다. ㅎㅎ 원래 서브버전의 다른 강좌에서는 서브버전으로 우선 서비스를 하는 것을 모두 설명한 다음에 아파치와 연동하는 것을 이야기 하는데 이야기를 Trac에 촛점을 맞춰서 시작하다 보니 거꾸로 온 듯한 느낌은 들지만 뭐 이해하시는 대는 문제가 없을 듯합니다. ㅎㅎ 서브버전을 설치하면 svn.exe라는 파일을 사용할 수 있는데 이것이 바로 서브버전의 클라이언트에 해당합니다. 그럼 서버는 어떤 것이냐면... svnserve.exe라는 파일입니다. 물론 로컬 컴퓨터에서 혼자만 사용할 거라면 svnserve.exe으로 서버를 동작시키지 않고도 로컬 파일 시스템은 사용할 수 있습니다. 이번 장에서는 svnserve.exe에 의해 서버를 띄우고 이를 통해(아파치와 연동하지 않고) 서브버전을 사용하는 것에 대해서 설명하도록 하겠습니다. 1) svnserve 서버 띄우기 svnserve는 커맨드 창에서 다음과 같이 실행할 수 있습니다. C:\>start svnserve --daemon --root D:\Trac\SvnRepo\Projects\Test ▲ 윈도우즈에서 서브버전 서버 실행하기 /home>svnserve --daemon --root /Trac/SvnRepo/Projects/Test ▲ 유닉스/리눅스에서 서브버전 서버 실행하기 위와 같이 실행하면 아무 메시지도 나타나지 않는 새로운 창이 뜨게 됩니다. 무소식이 희소식이라고 아무 메시지도 안나오는게 정상적으로 동작하는 것입니다. 사실은 문제가 있더라도 아무 메시지가 출력되지 않습니다. 다음과 같은 명령으로 서브버전 서버에서 소스를 체크아웃 할 수 있습니다. C:\testsvn>svn co svn://localhost/ Test 체크아웃된 리비전 0. ▲ 서브버전 서버와 연동하여 저장소 소스 체크아웃하기 이번에는 서버를 지정하기 위해 svn프로토콜을 사용하고 있군요. 포트번호는 특별히 지정하지 않았는데요. 서브버전은 기본으로 3690번 포트를 사용합니다. 만약 포트를 7000번으로 바꾸고 싶다면 svnserve를 실행할 때 --listen-port 7000 와 같은 옵션을 추가해 줍니다.
위의 명령에서 start는 새로운 창으로 띄워서 실행하라는 의미입니다. 유닉스나 리눅스의 경우는 start라는 명령은 사용하지 않습니다. --daemon은 백그라운드로 돌아라는 명령인데 그렇다고 --daemon을 생략하면 실행됐다가 바로 죽어버립니다. 그래서 반듯이 줘야 합니다. --root는 서브버전 저장소가 어디인지를 알려주기 위해 사용합니다. 위의 설정으로는 Test 프로젝트에만 접근 가능합니다. 위의 명령에서 서브버전 저장소를 Test까지 주지 않고 Projects까지 주면 Test뿐만 아니라 ProjectA, ProjectB 모두를 꺼내볼 수 있습니다. 새창이 떠있을 때는 사실 문제의 소지가 있습니다. 뭐 창이 하나 떠 있어서 귀찮다는거 말고도 누군가 실수로 창을 닫아 버리거나 컴퓨터를 재 부팅 했을 경우 다시 띄워줘야 정상적인 서비스가 가능하다는 것이죠. 물론 시작프로그램에 등록하면 재 부팅 문제는 해결할 수 있겠네요. 어찌 되었건 창이 띄는게 맘에 안든다면 다음과 같이 할 수 있습니다. C:\>start /B svnserve --daemon --root D:\Trac\SvnRepo\Projects\Test ▲ 윈도우즈에서 새창 띄우지 않고 서브버전 서버 실행하기 보시면 /B옵션을 추가 했다는 것을 아시겠죠? 이런 경우는 svnserve를 죽이기 위해 작업관리자를 사용해서 작업종료를 해야 합니다. 하지만 가장 중요한 문제가 하나 더 남아 있는데 서브버전 서버를 컴퓨터에 로그인 하지 않으면 실행되지 않는 다는 것이 큰 문제입니다. 즉, 컴퓨터가 시작되면서 바로 서비스가 실행되지 않는다는 것이죠. 이문제를 해결하기 위해서는 svnserve를 서비스로 등록하여야 합니다. 2) svnserve 서버 서비스로 등록하기 서비스로 등록하는 방법으로는 여러가지가 있습니다. 또한 이런 걸 도와주는 툴들도 인터넷 상에 돌아다닙니다. 툴을 사용하실려면 다음의 URL을 참고하세요. http://dark.clansoft.dk/~mbn/svnservice/ http://www.pyrasis.com/main/SVNSERVEManager 여기서는 이런 툴 없이 그냥 맨손으로 서비스에 등록하도록 하겠습니다. 우선 서비스에 등록하기 위해 필요한 준비물은 sc.exe라는 프로그램입니다. 일반적으로 Windows의 system32디렉토리에 존재합니다. 이 프로그램은 서비스 제어 프로그램의 윈도우즈에서 제공하고 있습니다. sc.exe의 기본 사용법은 다음과 같습니다. sc [command] [service name] ... ▲ sc.exe의 사용법 사용법은 command에 따라 다양하시만 여기서는 서비스 등록, 제거, 일부 값 변경에 관한 내용만 알아보도록 하겠습니다. <server>는 \\MyCom 과 같이 서비스를 제어하고 싶은 컴퓨터의 이름을 적는 부분인데 자신의 로컬 컴퓨터를 대상으로 한다면 생략하여도 됩니다. command는 다음의 값을 사용하면 됩니다. - 서비스의 등록: create - 서비스의 제거: delete [service name]은 서비스로 등록할 때 등록자가 임의로 그 서비스의 이름을 지정하는 것입니다. <option>은 각 command마다 다른데 우리가 필요한 몇가지만 보도록 하겠습니다. binpath= : 서비스를 제공할 파일의 전체 경로를 적어 줍니다. start= : 서비스를 시작 유형을 자동(auto), 수동(demand), 사용안함(diabled)으로 지정할 수 있습니다. displayname= : 서비스를 확인시키기 위해 사용될 이름을 지정합니다. 뭐 이정도만 알면 될 것 같습니다. 옵션을 지정할 때 주의 해야 할 점이 하나 있는데 "옵션명= 옵션값"을 지정하되 = 다음에는 꼭 스페이스가 있어야 합니다. 그렇지 않으면 오류가 납니다. 자 그럼 이제 서브버전을 서비스에 등록해 보도록 하겠습니다. svnserve에 지정하는 옵션 중 서비스에 등록할 때는 --deamon 대신에 --service를 사용해야 합니다. sc create "Subversion Serve" binpath= "C:\Program Files\Subversion\bin\svnserve.exe --service --root D:\Trac\SvnRepo\Projects" ▲ sc를 이용한 svnserve 서비스 등록 서비스 관리자에 가서 보면 "Subversion Serve"라는 이름으로 서비스가 등록되어 있는 것을 확인 하실 수 있을 것입니다.
((-------IMAGE-------))

시작시키면 시작도 되구요. 정지 시키면 정지도 됩니다. 물론 정상적으로 설치 했을 경우에 말이죠. ㅎㅎ 만약에 잘못 등록해서 서비스에서 제거해야 한다면 다음과 같이 합니다. sc delete "Subversion Serve" ▲ sc를 이용한 svnserve 서비스 제거 자 이제 서비스에도 등록했겠다. 이제 서브버전에 더 상세한 설정을 해 보도록 하겠습니다. 3. 서브버전 권한 설정 이미 해보셨겠지만 현재 상태에서 서브버전의 저장소는 읽기 전용으로 동작합니다. 즉, 체크아웃은 되지만 체크인은 되지가 않습니다. 좀 이상하다고 생각하실지 모르겠습니다. 이전에 아파치에서는 인증을 통해서 체크인도 했었는데 svnserve로는 커밋을 못한다니... 아파치와 연결해서 사용할 때는 httpd.conf에 설정한 값에 의해 서브버전이 행동을 합니다. 저장소의 설정과는 상관없이 말이죠. 하지만 svnserve는 아파치에 설정했던 것과 아무 상관없이 동작합니다. 사실 이게 설정했던 것과는 상관없습니다. svnadmin으로 저장소를 생성하면 기본적으로 읽기 전용으로 만들어 집니다. 이제 권한을 변경해 보겠습니다. 1) svnserve.conf 설정 및 인증 추가하기 각 서브버전의 저장소를 들여다 보면 conf라는 디렉토리가 있습니다. 우리가 실습하고 있는 예 중 하나로 보면 D:\Trac\SvnRepo\Projects\Test\conf 디렉토리가 되겠죠? 거기로 가서 svnserve.conf라는 파일을 열어보세요. 보시면 아시겠지만 대부분은 #으로 주석처리되어 있습니다. 그래서 읽기 전용인 것입니다. 그럼 12라인쯤에 보면 anon-access = read 라는 부분이 주석 처리되어 있습니다. 이 의미는 익명(로그인하지 않고)으로 서브버전 저장소에 저장하는 사용자에게 어떤 권한을 부여할지를 결정합니다. 사용할 수 있는 값으로는 read, write, none 중에 하나를 설정할 수 있습니다. read는 읽기전용이구요, write는 읽고 쓸수 있게 권한을 부여하는 것이구요. none의 경우는 읽지도 못하게 할때 사용합니다. 우선 #을 지우시고 값을 read로 그대로 놔 둡니다. 즉, 익명의 사용자는 읽어볼 수 만 있게 하는 것이죠. 그 다음 라인은 auth-access = write 라고 적혀 있는데 역시 주석처리 되어 있군요. auth-access는 로그인 한 사용자에 대한 권한을 지정하는 것입니다. 이 것도 그냥 주석만 풀어 주세요. 즉, 인증이 된 사용자는 읽고 쓰기가 모두 가능하게 하는 것이죠. 그 다음은 18라인 쯤 보면 passwd-db = passwd 라고 적혀 있는 라인이 보입니다. 뭐 주석처리 되어 있지만 인증을 하기 위한 사용자 명과 암호를 설정한 파일이 어디 있는지 지정하는 곳입니다. 지금 현재는 같은 디렉토리에 passwd라고 지정되어 있군요. 다른 디렉토리에서 사용자 계정을 관리하고 싶다면 해당 파일의 경로와 이름을 적어 주면 됩니다. 우선은 이것도 값을 그대로 두고 주석만 제거하세요. 그리고 제일 마지막 라인의 realm = My First Repository 부분의 주석을 제거하고 "My First Repository"라고 되어 있는 부분을 여러분이 쓰고 싶은 문자열을 쓰세요. 이 값은 인증을 요구할 때 표시되는 인증을 요청하는 곳의 명칭으로 사용됩니다. 저는 여기에 "Test Repository"라고 쓰도록 하겠습니다. 그리고 같은 디렉토리에 보면 passwd파일이 하나 있습니다. 이 파일은 위의 svnserve.conf파일의 passwd-db항목에 설정한 파일이죠? 이 파일을 열어 보시면 다 주석으로 처리되어 있네요. 사용자명과 암호를 =로 구분하여 적으면 됩니다. 즉, pooh라는 사용자명과 1234라는 암호를 부여하려면 파일의 제일 아래에 pooh=1234 라고 적어주면 됩니다. 아직 서브버전은 암호화된 인증 파일을 지원하고 있지 않습니다. 그래서 그냥 암호자체를 적어야 합니다. 좀 찝찝하기는 하지만 현재로선 별수가 없습니다. 어찌되었건 실습을 위해 passwd에 pooh = 1234 와 scott = 1234 를 지정하시기 바랍니다. 참고로 위의 설정 값들은 라인의 첫번째 컬럼부터 시작해야 합니다. 제일 앞에 공백과 같은 것이 있을때는 설정파일 오류가 납니다. 자 여기까지 했으면 svn으로 다시 한번 해 보죠. D:\testsvn>svn co svn://localhost/Test Test 체크아웃된 리비전 0. D:\testsvn>cd Test D:\testsvn\Test>copy con readme.txt 읽어보세요 ^Z 1개 파일이 복사되었습니다. D:\testsvn\Test>svn import -m "readme 추가" svn://localhost/Test 인증 영역(realm): Test Repository 'administrator'의 암호: 인증 영역(realm): Test Repository 사용자명:pooh 'pooh'의 암호:**** 무시함 '.svn' 추가 readme.txt 커밋된 리비전 1. D:\testsvn\Test> 위의 예를 보면 svn://localhost/Test 에서 체크아웃하고 거기에 readme.txt 파일을 하나 생성한 후 이 파일을 import시키고 있습니다. 그런데 체크아웃할 때는 암호를 물어보지 않지만 import할때는 저장소에 쓰기작업이기 때문에 암호를 물어보고 있습니다. 맨 처음에는 윈도우즈의 계정인 administrator의 암호를 물어 보게 됩니다. 하지만 우리는 pooh라는 사용자만 만들었죠? 그래서 그냥 앤터를 칩니다. 그럼 다시 사용자명 부터 물어보게 됩니다. 그리고 위에서 svnserve.conf에서 realm에 설정했던 값이 어디에 출력되는지도 한번 확인해 보시기 바랍니다. 2) 디렉토리, 파일별 권한 설정하기 위의 예에서는 한번의 인증으로 저장소의 모든 파일을 읽고 쓸 수 있는 상태가 되었음을 알 수 있습니다. 이런 경우라면 특별히 보호되어야 하는 파일이나 디렉토리를 지키기가 힘들겠죠? 그래서 이번에는 각 디렉토리 혹은 파일에 사용자별로 권한을 설정하는 방법에 대해서 알아 보겠습니다. svnserve.conf에서 우리가 아직 건드리지 않은 값이 하나 있죠? 약 25번째 라인 쯤에 authz-db = authz 라는 부분입니다. authz-db 항목은 파일이나 디렉토리별로 권한을 부여할 수 있는 설정을 저장하고 있는 파일을 설정하는 부분입니다. 뭐 또 주석만 제거하세요. 그리고 한가지만 더 수정할게 있는데요. authz-db를 사용하려면 anon-access = none 으로 설정되어야 합니다. 이 값이 설정되어 있으면 authz-db의 설정값과 충돌이 생겨서 읽거나 쓸수있는 상황이 생깁니다. 역시 authz라는 파일도 같은 디렉토리에 샘플이 있습니다. [groups] 세션에서 그룹을 대표하는 이름을 만들 있습니다. 예를 들어 ATeam = pooh, scott 과 같이 지정합니다. 이 예는 pooh와 scott이라는 사용자를 ATeam이라고 정하는 것입니다. 팀명을 사용할 때는 @ATeam 이라고 하면 됩니다. 그리고 나머지 세션은 제한을 두고 싶은 실제 저장소내 디렉토리 혹은 파일의 경로입니다. 각 디렉토리 혹은 파일별 권한을 설정하기 전에 우선 모든 사용자가 저장소의 / 디렉토리는 읽고 쓰게 해 주어야 합니다. 다음과 같이 해서 모든 사용자가 읽고 쓸 수 있게 해 줍니다. [/] * = rw 여기서 [/]는 저장소의 /디렉토리에 대한 접근 권한을 지정한다는 의미로 사용되며 *는 누구나 라는 의미이며 r은 읽기 가능 w는 쓰기 가능의 의미를 가지고 있습니다. 즉, rw는 읽고 쓰기가 모두 가능하게 지정하는 것입니다. 만약 읽지도 쓰지도 못하게 하려면 = 다음에 아무값도 적지 않으면 됩니다. 그럼 pooh 사용자가 readme.txt 파일을 읽지 못하게 하려면 다음과 같이 설정하면 됩니다. [/readme.txt] pooh = 이렇게 설정하면 됩니다. 위에서 이야기 한 내용을 모두 한번에 표시하면 다음과 같은 식이겠죠? [groups] ATeam = pooh, scott [/] * = rw [/readme.txt] pooh = [/temp] @ATeam = r 내용을 보면 이제 이해가 가실 거라 생각됩니다. 한가지 꼭 명심하셔야 할 것은 이렇게 설정했다고 해서 아파치를 통해 WebDAV로 접근하는 사용자에게도 통하는게 아니라는거 꼭 명심하시기 바랍니다.
by Anna 안나 2008. 7. 11. 20:56
이제 2장에서 설치한 아파치, 서브버전, 파이썬, Trac을 이용하기 위해 설정방법을 알아보고 실제로 구동 시켜 보도록 하겠습니다. 3장. Trac On Windows - Trac 설정 및 실행해보기 설명하기 앞서 각각의 디렉토리를 다음의 표기로 변경하도록 하겠습니다. 좀 길어서요. 아파치 설치 경로(C:\Program Files\Apache Software Foundation\Apache2.2)를 $Apache로 표기하겠습니다. 서브버전 설치 경로(C:\Program Files\Subversion)를 $Subversion으로 표기하겠습니다. 파이썬 설치 경로(C:\Program Files\Python24)를 $Python으로 표기하겠습니다. 1. 프로젝트 저장소 만들고 Trac Standalone으로 띄워 보기 우선 svn-admin으로 프로젝트의 소스를 저장하고 버전관리를 할 저장소를 생성합니다. svnadmin create D:\Trac\SvnRepo\Projects\Test Test 저는 다음과 같이 D:\Trac\SvnRepo\Projects\Test 라는 디렉토리에 저장소를 만들었습니다. 자 이번에는 이 서브버전 저장소와 연계된 Trac 저장소를 만들어 보도록 하겠습니다. python "C:\Program Files\Python24\Script-xs\trac-admin" "D:\Trac\TracRepo\Projects\Test" initenv 이번에는 "D:\Trac\TracRepo\Projects\Test" 라는 데렉토리에 저장소를 만들고 있습니다. 이렇게 입력하면 저장소 생성시 필요한 내용을 사용자에게 하나씩 물어 보면서 진행하게 됩니다. 제시한 값이 맞다면 그냥 엔터를 치시면 됩니다. 다음은 실제로 실행시의 화면 캡쳐 내용입니다. C:\>python "C:\Program Files\Python24\Script-xs\trac-admin" "D:\Trac\TracRepo\Projects\Test" initenv Creating a new Trac environment at D:\Trac\TracRepo\Projects\Test Trac will first ask a few questions about your environment in order to initalize and prepare the project database. Please enter the name of your project. This name will be used in page titles and descript-xions. Project Name [My Project]> Test Please specify the connection string for the database to use. By default, a local SQLite database is created in the environment directory. It is also possible to use an already existing PostgreSQL database (check the Trac documentation for the exact connection string syntax). Database connection string [sqlite:db/trac.db]> Please specify the type of version control system, By default, it will be svn. If you don't want to use Trac with version control integration, choose the default here and don't specify a repository directory. in the next question. Repository type [svn]> Please specify the absolute path to the version control repository, or leave it blank to use Trac without a repository. You can also set the repository location later. Path to repository [/path/to/repos]> D:/Trac/SvnRepo/Projects/Test Please enter location of Trac page templates. Default is the location of the site-wide templates installed with Trac. Templates directory [C:\Program Files\Python24\share\trac\templates]> Creating and Initializing Project Installing default wiki pages C:\Program Files\Python24\share\trac\wiki-default\CamelCase => CamelCase C:\Program Files\Python24\share\trac\wiki-default\checkwiki.py => checkwiki.py C:\Program Files\Python24\share\trac\wiki-default\InterMapTxt => InterMapTxt C:\Program Files\Python24\share\trac\wiki-default\InterTrac => InterTrac C:\Program Files\Python24\share\trac\wiki-default\InterWiki => InterWiki C:\Program Files\Python24\share\trac\wiki-default\RecentChanges => RecentChanges C:\Program Files\Python24\share\trac\wiki-default\SandBox => SandBox C:\Program Files\Python24\share\trac\wiki-default\TitleIndex => TitleIndex C:\Program Files\Python24\share\trac\wiki-default\TracAccessibility => TracAccessibility C:\Program Files\Python24\share\trac\wiki-default\TracAdmin => TracAdmin C:\Program Files\Python24\share\trac\wiki-default\TracBackup => TracBackup C:\Program Files\Python24\share\trac\wiki-default\TracBrowser => TracBrowser C:\Program Files\Python24\share\trac\wiki-default\TracCgi => TracCgi C:\Program Files\Python24\share\trac\wiki-default\TracChangeset => TracChangeset C:\Program Files\Python24\share\trac\wiki-default\TracEnvironment => TracEnvironment C:\Program Files\Python24\share\trac\wiki-default\TracFastCgi => TracFastCgi C:\Program Files\Python24\share\trac\wiki-default\TracForKorean => TracForKorean C:\Program Files\Python24\share\trac\wiki-default\TracGuide => TracGuide C:\Program Files\Python24\share\trac\wiki-default\TracImport => TracImport C:\Program Files\Python24\share\trac\wiki-default\TracIni => TracIni C:\Program Files\Python24\share\trac\wiki-default\TracInstall => TracInstall C:\Program Files\Python24\share\trac\wiki-default\TracInterfaceCustomization => TracInterfaceCustomization C:\Program Files\Python24\share\trac\wiki-default\TracLinks => TracLinks C:\Program Files\Python24\share\trac\wiki-default\TracLogging => TracLogging C:\Program Files\Python24\share\trac\wiki-default\TracModPython => TracModPython C:\Program Files\Python24\share\trac\wiki-default\TracNotification => TracNotification C:\Program Files\Python24\share\trac\wiki-default\TracPermissions => TracPermissions C:\Program Files\Python24\share\trac\wiki-default\TracPlugins => TracPlugins C:\Program Files\Python24\share\trac\wiki-default\TracQuery => TracQuery C:\Program Files\Python24\share\trac\wiki-default\TracReports => TracReports C:\Program Files\Python24\share\trac\wiki-default\TracRevisionLog => TracRevisionLog C:\Program Files\Python24\share\trac\wiki-default\TracRoadmap => TracRoadmap C:\Program Files\Python24\share\trac\wiki-default\TracRss => TracRss C:\Program Files\Python24\share\trac\wiki-default\TracSearch => TracSearch C:\Program Files\Python24\share\trac\wiki-default\TracStandalone => TracStandalone C:\Program Files\Python24\share\trac\wiki-default\TracSupport => TracSupport C:\Program Files\Python24\share\trac\wiki-default\TracSyntaxColoring => TracSyntaxColoring C:\Program Files\Python24\share\trac\wiki-default\TracTickets => TracTickets C:\Program Files\Python24\share\trac\wiki-default\TracTicketsCustomFields => TracTicketsCustomFields C:\Program Files\Python24\share\trac\wiki-default\TracTimeline => TracTimeline C:\Program Files\Python24\share\trac\wiki-default\TracUnicode => TracUnicode C:\Program Files\Python24\share\trac\wiki-default\TracUpgrade => TracUpgrade C:\Program Files\Python24\share\trac\wiki-default\TracWiki => TracWiki C:\Program Files\Python24\share\trac\wiki-default\WikiDeletePage => WikiDeletePage C:\Program Files\Python24\share\trac\wiki-default\WikiFormatting => WikiFormatting C:\Program Files\Python24\share\trac\wiki-default\WikiHtml => WikiHtml C:\Program Files\Python24\share\trac\wiki-default\WikiMacros => WikiMacros C:\Program Files\Python24\share\trac\wiki-default\WikiNewPage => WikiNewPage C:\Program Files\Python24\share\trac\wiki-default\WikiPageNames => WikiPageNames C:\Program Files\Python24\share\trac\wiki-default\WikiProcessors => WikiProcessors C:\Program Files\Python24\share\trac\wiki-default\WikiRestructuredText => WikiRestructuredText C:\Program Files\Python24\share\trac\wiki-default\WikiRestructuredTextLinks => WikiRestructuredTextLinks C:\Program Files\Python24\share\trac\wiki-default\WikiStart => WikiStart Indexing repository --------------------------------------------------------------------- Project environment for 'Test' created. You may now configure the environment by editing the file: D:\Trac\TracRepo\Projects\Test\conf\trac.ini If you'd like to take this new project environment for a test drive, try running the Trac standalone web server `tracd`: tracd --port 8000 D:\Trac\TracRepo\Projects\Test Then point your browser to http://localhost:8000/Test. There you can also browse the documentation for your installed version of Trac, including information on further setup (such as deploying Trac to a real web server). The latest documentation can also always be found on the project website: http://trac.edgewall.org/ Congratulations! C:\> WEB Admin이 동작할 수 있도록 D:\Trac\TracRepo\Projects\Test\conf\trac.ini 을 열어 다음을 추가합니다. [components]
webadmin.* = enabled
여기까지 했으면 우선 trac을 Standalone 데몬으로 띄워 보도록 하겠습니다. 명령창에 다음과 같이 입력하세요. C:\>python "C:\Program Files\Python24\Script-xs\tracd
" --port 8000 "D:\Trac\TracRepo\Projects\Test" 그런 다음 웹브라우져를 열어서 다음의 URL로 접속해 보세요. http://localhost:8000/Test
다음의 화면을 보실 수 있었다면 지금까지 잘 따라 오신 겁니다.
((-------IMAGE-------))

뭐 한글 Trac으로 설치를 했으니 한글로 해석된 글이 나오고 좋네요. 2. 아파치에 Trac 설정하기 이미 Trac을 웹을 통해 보았지만 궁극적인 목적은 아파치를 통해 많은 사람들이 프로젝트에 참여하도록 하는 것이 목적이므로 아파치를 설정해서 띄워 보도록 하겠습니다. 1) 필요한 모듈 설정 아파치와 서브버전을 연동 시키기 위해 다음과 같이 수행합니다. 단 이 과정은 Subversion을 인스톨본으로 설치하신 분은 대부분이 자동으로 되어 있을 수 있습니다. 그런분은 그냥 확인만 하십시오. - $Subversion\bin 디렉토리에 있는 mod_dav_svn.so파일과 mod_authz_svn.so파일을 $Apache\modules 디렉토리에 복사합니다. 또한 mod_*_svn.so 에서 필요한 dll파일(libdb44.dll와 intl3_svn.dll)도 같이 복사합니다. - $Apache\conf\httpd.conf 파일에서 다음 부분의 주석을 제거합니다. LoadModule dav_module modules/mod_dav.so
LoadModule dav_fs_module modules/mod_dav_fs.so - $Apache\conf\httpd.conf 파일에 다음을 추가합니다. LoadModule dav_svn_module modules/mod_dav_svn.so
LoadModule authz_svn_module modules/mod_authz_svn.so LoadModule python_module modules/mod_python.so 위의 모듈들은 인증을 처리하기 위한 모듈들과 서브버전과 연동을 위한 모듈 그리고 파이썬과 연동을 위한 모듈입니다. Trac은 cgi, fastcgi, 그리고 python_module로 연동이 가능한데... cgi는 성능이 많이 떨어지므로 사용하는 것을 권장하지 않습니다. 사용하길 권장하는 부분은 fastcgi와 python_module인데 위에서 mod_python.so을 로드하는 부분을 추가하고 있습니다. 2) Trac 가상 페이지 만들기 이 부분이 잘 설정되었는지 확인하기 위해 다음과 같이 $Apache\conf\httpd.conf파일에 다음을 추가합니다. SetHandler mod_python PythonHandler mod_python.testhandler ▲ python_module이 테스트 페이지 설정 이렇게 입력한 후 아파치를 재 시작시킵니다. 그런 다음 웹브라우져로 http://localhost:88/mpinfo 를 실행하여 다음과 유사한 화면이 나타나는지 확인합니다.
((-------IMAGE-------))((-------IMAGE-------))

정상적으로 표시가 되었다면 mod_python 은 정상적으로 설치가 되었습니다. 이 설정은 보안에 문제가 있을 수 있으므로 정상적으로 동작했다는 것을 확인하면 지우세요. 이번에는 서브버전과의 아파치와의 연동이 정상적으로 이루어 지는지를 다음의 내용을 추가하여 확인합니다. DAV svn SVNPath "D:/Trac/SvnRepo/Projects/Test" ▲ dav_svn_module 테스트 페이지 설정 httpd.conf파일을 적용하기 위해서는 아파치를 다시 시작시켜야 합니다. 웹으로는 http://localhost:88/svn/Test 로 접속합니다. 화면에 다음과 유사한 값이 찍히면 정상입니다. Revision 0: / Powered by Subversion version 1.4.4 (r25188). 상태나 버전에 따라 좀 다르게 나올 수도 있습니다. 이제 Trac을 띄워볼 차례입니다. $Apache\conf\httpd.conf파일에 다음을 추가합니다. SetHandler mod_python PythonHandler trac.web.modpython_frontend PythonOption TracEnv "D:/Trac/TracRepo/Projects/Test" PythonOption TracUriRoot /projects/Test ▲ Trac 테스트를 위한 페이지 설정 httpd.conf파일을 적용하기 위해서는 아파치를 다시 시작시켜야 합니다. 웹으로는 http://localhost:88/projects/Test 로 접속합니다. 위에 캡쳐(trac standalone deamon)된 것과 같은 화면이 출력되면 정상적으로 설정된 것입니다. 만약 Trac을 설치 할때 기본 디렉토리인 파이썬 디렉토리가 아니라 별도의 인스톨 디렉토리를 지정하였다면 위의 설정에 다음과 같은 구문을 추가해서 설치한 Trac의 경로를 파이썬에게 알려 주어야 합니다. PythonPath "sys.path + ['C:/Program Files/Trac']" 위의 설정은 Trac을 C:\Program Files\Trac 에 설치했다고 했을 때 설정입니다. 3) 로그인 처리 http://localhost:88/projects/Test 에서 보여진 화면에서 오른쪽 상단 부근에 "로그인"이라는 부분이 있습니다. 지금 상태에서는 "로그인"을 누르면 다음과 같은 오류 메시지가 나타납니다.
((-------IMAGE-------))

아직 로그인 페이지 처리를 하지 않아서 그런 것 입니다. 로그인을 처리하여 페이지에 접근 권한을 부여기 위해서 먼저 사용자 암호 파일을 만들어야 합니다. 암호 파일은 D:/Trac/.htpasswd 에 만들기로 하겠습니다. 이 파일은 추후 Subversion에서도 사용할 꺼거든요. 그래야 계정 하나로 서브버전도 그리고 Trac도 모두 사용할 수 있겠죠? 이 암호 파일은 아파치에서 제공하는 htpasswd.exe파일로 만들고 관리할 수 있습니다. 명령창으로 $Apache\bin으로 이동해서 다음과 같이 입력합니다. $Apache\bin>htpasswd -c D:\Trac\.htpasswd admin Automatically using MD5 format. New password: ****** Re-type new password: ****** Adding password for user admin ▲ 암호 파일 생성 및 계정 추가 위의 내용은 새로운 암호 파일(D:\Trac\.htaccess)을 만들면서 admin이라는 계정의 추가하고 있습니다. 암호를 정하고 다시 한번 암호를 확인하면 입력이 끝납니다. Trac에 인증절차를 추가하기 위하여 다음을 httpd.conf에 추가합니다. AuthType Basic AuthName "Test Project" AuthUserFile "D:/Trac/.htpasswd" Require valid-user ▲ Trac 인증 페이지 설정 당연한 이야기지만 httpd.conf를 변경하면 아파치 다시 띄워야 하는 거 아시죠? http://localhost:88/projects/Test 에 접속해서 "로그인"버튼을 클릭하면 다음과 같은 BASIC인증 창이 나타납니다.
((-------IMAGE-------))

이제 아까 입력했던 ID와 암호를 입력하여 들어가 보도록 하겠습니다. 이제는 오류가 나지 않고 로그인이 되었다는 메시지가 출력되는 것을 보실 수 있습니다.
((-------IMAGE-------))

로그인 되었을 때 할 수 있는 일에 대해서는 추후 이야기 하도록 하고 설정을 더 진행해 보도록 하겠습니다. 4) 다중 프로젝트 적용하기 위의 예제는 설정의 예를 보이기 위한 것일 뿐입니다. 그렇다 보니 Trac으로 Test라는 하나의 프로젝트만 서비스 하고 있습니다. 실제 상황에서는 여러 프로젝트를 여러 사람이 접근하여 하게 되겠죠? 즉, D:/Trac/TracRepo/Projects 디렉토리에 Test라는 프로젝트가 있는데 사실은 같은 디렉토리에 여러 프로젝트의 Trac 저장소가 있을 수 있다는 겁니다. 물론 D:/Trac/SvnRepo/Projects에도 서브버전 저장소가 여러개 있을 수 있겠죠? 뭐 구성하기 나름이겠지만... 이럴 경우 /projects/Test라는 가상디렉토리를 D:/Trac/TracRepo/Projects/Test에 직접 맵핑 하지 않고 가상디렉토리 /projects를 D:/Trac/TracRepo/Projects 디렉토리에 맵핑해서 저장소의 리스트를 보여주고 선택해서 들어갈 수 있게 하는 방법을 이야기 하려고 합니다. SetHandler mod_python PythonHandler trac.web.modpython_frontend PythonOption TracEnvParentDir "D:/Trac/TracRepo/Projects" PythonOption TracUriRoot /projects ▲ Trac에서 프로젝트 리스트 보기 설정 좀 달라진 것으로는 PythonOption 중에 TracEnvParentDir 이라는게 있네요. 어찌 되었건 저렇게 설정하고 http://localhost:88/projects 에 들어가 보면 다음과 같이 나타납니다.
((-------IMAGE-------))

물론 위의 경우는 제가 ProjectA와 ProjectB의 Trac저장소를 더 만들었을 때의 이야기 입니다. 어찌 되었건 프로젝트 리스트가 보여지죠? 여기서 Test를 클릭해서 들어가면 또 아까의 화면으로 들어가 집니다. 그런데... 프로젝트의 리스트는 보여주기는 싫고 프로젝트 URL을 직접 링크를 한다거나 혹은 프로젝트 URL을 아는 사람만 접속하게 하고 위의 설정을 다음과 같이 설정합니다. SetHandler mod_python PythonHandler trac.web.modpython_frontend PythonOption TracEnvParentDir "D:/Trac/TracRepo/Projects" PythonOption TracUriRoot /projects ▲ Trac에서 프로젝트 리스트 보이 않게 설정 위의 설정으로는 http://localhost:88/projects 경로로는 접속할 수 없습니다. 하지만 http://localhost:88/projects/Test 로는 접속할 수 있습니다. 또 한가지 프로젝트가 여러개라면 로그인을 처리하는 설정도 여러개여야 합니다. 왜냐면 위의 로그인 처리를 하는 설정의 가상 디렉토리가 /projects/Test/login 이라서 다른 프로젝트에는 사용할 수 없기 때문이죠. 각 프로젝트마다 사용자가 모두 다르다면 별수 없이 이렇게 해야 하겠지만 하나의 계정 정보만으로 모든 프로젝트에 동일하게 적용하고 싶다면 위의 로그인을 위하여 설정한 내용을 다음과 같이 바꿉니다. AuthType Basic AuthName "Test Project" AuthUserFile "D:/AppData/Trac/.htpasswd" Require valid-user ▲ Trac의 모든 프로젝트에 같은 계정으로 로그인 설정 위의 예는 /projects/로 시작되고 끝이 /longin인 모든 가상디렉토리는 인증처리를 하라는 의미입니다. Trac설정의 마지막으로 여러분이 localhost밑에 프로젝트 경로가 아닌 도메인 자체가 projectA.company.com, test.company.com, test.company.com 과 같이 만들고 싶을 수도 있겠네요. 물론 hosts파일이든지 직접 도메인을 추가하든지 해서 이 도메인이 아파치가 설치된 웹서버의 주소로 변경될 수 있어야 겠죠? 어찌되었건 이럴때는 아파치의 가상호스트 설정을 해야 합니다. DocumentRoot "D:/Trac/TracRepo/Projects/Test" ServerName test.mycompany.com SetHandler mod_python PythonHandler trac.web.modpython_frontend PythonOption TracEnv "D:/Trac/TracRepo/Projects/Test" PythonOption TracUriRoot / AuthType Basic AuthName "MyCompany Trac Server" AuthUserFile "D:/AppData/Trac/.htpasswd" Require valid-user ▲ 가상 호스트를 이용한 Trac 서비스 제공 만약 여기서도 다중 프로젝트를 서비스 하려면 위 설정 중 PythonOption TracEnv "D:/AppData/Trac/TracRepo/Projects/Test" 부분을 다음과 같이 변경하시면 되겠습니다. PythonOption TracEnvParentDir"D:/Trac/TracRepo/Projects" 이 부분은 필요에 따라 직접 한번 해보세요. 5) WebAdmin 적용하기 Trac을 관리하기 위해서는 Trac 저장소를 만들때 해본것과 같이 trac-admin을 사용해야 합니다. 그런데 이게 커맨드 도구여서 좀 사용이 불편합니다. 그래서 Web상에서 Trac을 관리할 수 있게 우리는 이미 2장에서 Web Admin 플러그인을 설치하였습니다. 그럼 어떻게 사용하는 한번 알아보도록 하겠습니다. 우선 Web Admin을 수행하기 위해서는 Trac저장소에 관리 권한이 부여되어야 합니다. 다음과 같은 명령으로 저장소에 관리권한을 부여합니다. python "C:\Program Files\Python24\Script-xs\trac-admin" D:/Trac/TracRepo/Projects/Test permission add admin TRAC_ADMIN 그런 다음 다시 http://localhost:88/projects/Test를 들어가서 로그인을 admin으로 하게 되면 다음 그림과 같이 제일 오른쪽에 Admin이라는 탭이 생긴것을 확인할 수 있습니다.
((-------IMAGE-------))

해봤더니 안 뜬다구요? 글을 자세히 읽지 않았군요. ^^; 위에서 다음과 같은 설명을 했었는데... ========================================= WEB Admin이 동작할 수 있도록 D:\Trac\TracRepo\Projects\Test\conf\trac.ini 을 열어 다음을 추가합니다. [components]
webadmin.* = enabled ========================================= 이 설정을 해야 한다는 거 잊지 마세요. Web Admin에 대한 사용법은 다음번에 알아보도록 하겠습니다. 여기까지 Trac의 설정을 마치구요. 다음은 서브버전에 대한 설정을 알아보도록 하겠습니다.
by Anna 안나 2008. 7. 11. 20:53
실제 설치는 내일 작성하도록 하겠습니다. 2장. Trac On Windows - Trac 실제로 설치하기 이제 앞장에서 살펴본대로 버전에 맞게 모든 프로그램 및 모듈들을 다운로드 받았다면 설치를 시작해 보도록 하겠습니다. 1. 아파치 설치 윈도우즈용 아파치 바이너리 파일은 현재 모두 인스톨본으로 제공하고 있기 때문에 뭐 별 문제없이 설치하실 수 있겠죠? 뭐 아파치 설치는 그냥 실행시켜서 Next누르고 도메인명, 호스트명, 관리자 이메일 정도 적어주면 쉽게 설치할 수 있으니까 설명하지 않도록 하겠습니다. 단, 저의 경우는 IIS와 톰캣이 모두 설치되어 있어서 C:\Program Files\Apache Software Foundation\Apache2.2\conf\httpd.conf 파일에서 "Listen 80" 부분을 "Listen 88"로 바꾸어 주었습니다. 웹서버 포트 변경하는 건지는 다 들아시죠? 2. 서브버전 설치 서브버전은 그냥 압축된 형태의 것과 설치본 두가지 모두를 제공합니다. 저는 그냥 압축본을 사용하도록 하겠습니다. 압축본의 압축을 풀어서 C:\Program Files\Subversion이라는 디렉토리에 복사하였습니다. 위의 C:\Program Files\Subversion\bin디렉토리를 환경변수 PATH에 등록하여 주세요. 또한 추후 svn 사용시 한글 문제를 해결하기 위해서 환경변수 APR_ICONV_PATH 을 만들고 값으로 C:\Program Files\Subversion\iconv 를 입력하세요. 나중에 커밋을 위해 메시지를 입력할 수 있는 에디터를 정하기 위해 환경변수에 SVN_EDITOR을 만들고 값을 notepad.exe 나 원하는 에디터를 입력하세요. 이런 설정을 별도로 하는 것이 귀찮으면 압축본이 아닌 설치본을 받아서 설치하세요. 3. 파이썬 설치 파이썬은 인스톨본 밖에 없기 때문에 그냥 그걸로 설치합니다. 설치 중 설치과정이 멈춰져 있는듯 하다면 웹브라우져가 열려있는 경우는 닫아 주세요. 저는 C:\Program Files\Python24 디렉토리에 설치하였습니다. 위의 경로는 환경변수 PATH에 등록하여 주세요. 또한 C:\Program Files\Python24\Script-xs디렉토리도 환경변수 PATH에 등록하여 주세요. 다음 작업을 좀 편하게 하기 위해서 입니다. 4. 파이썬과 서브버전의 연계 모듈 설치 이 모듈 역시 그냥 설치본으로 설치하셔도 상관은 없구요. 다음은 압축파일로 설치할 경우는 svn-win32-1.4.4_py24.zip 파일의 압축을 풀고 C:\Program Files\Python24\Lib\site-packages 디렉토리에 압축을 푼 파일 중 libsvn과 svn 디렉토리를 복사합니다. 5. 아파치와 파이썬 연계 모듈 설치 다운 받은 mod_python-3.3.1.win32-py2.4-Apache2.2.exe 파일을 그냥실행해서 설치하시면 됩니다. 설치 중간에 아파치가 설치된 디렉토리를 물어보게 되는데... C:\Program Files\Apache Software Foundation\Apache2.2 디렉토리를 선택해 주시면 됩니다. 6. 닥유틸스 설치 다운 받은 파일 docutils-snapshot.tgz의 압축을 알집 같은 걸로 풉니다. 명령창을 열어 풀린 디렉토리들 중 docutils-snapshot\docutils 디렉토리로 이동합니다. python ./setup.py install 위와 같은 명령으로 설치를 하시면 됩니다.
7. PySQLite 설치 다운 받은 pysqlite-2.3.5.win32-py2.4.exe파일을 실행해서 설치를 합니다.
8. 클리어실버 설치 다운 받은 clearsilver-0.9.14.win32-py2.4.exe파일을 실행해서 설치를 합니다. 9. Trac을 설치 다운받은 Trac파일 (영문일 경우 trac-0.10.4.tar.gz, 한글일 경우 trac-0.10.4-ko.tar.gz)의 압축을 해제 합니다. 뭐 한글이나 영문이나 설치 방법이 다르지는 않습니다. 저는 한글로 하겠습니다. 명령창을 열어서 압축이 풀린 trac-0.10.4-ko.tar\trac-0.10.4-ko 디렉토리로 이동합니다. python ./setup.py install 위와 같은 명령으로 설치할 수 있습니다. python ./setup.py install --prefix=/path/to/installdir 위와 같이 하여 설치될 디렉토리를 지정할 수 도 있습니다. 10. WebAdmin 설치 우선 ez_setup을 먼저 설치합니다. 다운 받은 ez_setup.py 파일을 C:\Program Files\Python24\Script-xs 디렉토리에 복사해 놓습니다. 그런 다음 명령창을 열어서 다음과 같이 실행합니다. 참 인터넷에 컴퓨터가 연결되어 있어야 합니다. 파일을 다운 받거든요. python "C:\Program Files\Python24\Script-xs\ez_setup.py" 위의 명령을 실행시키면 다음과 같은 화면이 나타납니다. C:\>python "C:\Program Files\Python24\Script-xs\ez_setup.py" Downloading http://cheeseshop.python.org/packages/2.4/s/setuptools/setuptools-0.6c6-py2.4.egg Processing setuptools-0.6c6-py2.4.egg creating c:\program files\python24\lib\site-packages\setuptools-0.6c6-py2.4.egg Extracting setuptools-0.6c6-py2.4.egg to c:\program files\python24\lib\site-packages Adding setuptools 0.6c6 to easy-install.pth file Installing easy_install-script-x.py script-x to C:\Program Files\Python24\Script-xs Installing easy_install.exe script-x to C:\Program Files\Python24\Script-xs Installing easy_install-2.4-script-x.py script-x to C:\Program Files\Python24\Script-xs Installing easy_install-2.4.exe script-x to C:\Program Files\Python24\Script-xs Installed c:\program files\python24\lib\site-packages\setuptools-0.6c6-py2.4.egg Processing dependencies for setuptools==0.6c6 Finished processing dependencies for setuptools==0.6c6 C:\> http://cheeseshop.python.org 에 접속해서 setup tool을 다운로드 받아서 설치하는 것을 알 수가 있을 것입니다. 최종적으로 위의 실행 결과에 의해 C:\Program Files\Python24\Script-xs\easy_install.exe 파일이 생성됩니다. 자 이제 WebAdmin을 설치해 보도록 하지요. WebAdmin은 설치하는 방법으로 두가지 있습니다. 하나는 다운 받은 파일을 이용하는 방법이 있구요. svn서버에서 소스를 다운 받아 설치하는 방법이 있습니다. 우선 첫번째 방법으로 다운 받은 TracWebAdmin-0.1.2dev_r4240-py2.4.egg.zip 파일의 압축을 해제 해서 생성된 TracWebAdmin-0.1.2dev_r4240-py2.4.egg 디렉토리와 easy_install을 이용해서 다음과 같이 설치합니다. easy_install.exe TracWebAdmin-0.1.2dev_r4240-py2.4.egg 또 다른 방법으로는 다음과 같이 입력하여 서브버전을 이용해서 소스를 다운받아 설치하는 방법입니다. easy_install http://svn.edgewall.com/repos/trac/sandbox/webadmin 저는 이 방법으로 하도록 하겠습니다. 다음은 설치시에 나타나는 화면입니다. 참고하십시오. C:\>easy_install http://svn.edgewall.com/repos/trac/sandbox/webadmin Downloading http://svn.edgewall.com/repos/trac/sandbox/webadmin Doing subversion checkout from http://svn.edgewall.com/repos/trac/sandbox/webadmin to c:\temp\easy_install-p8q8nb\webadmin Processing webadmin Running setup.py -q bdist_egg --dist-dir c:\temp\easy_install-p8q8nb\webadmin\egg-dist-tmp-psawjc zip_safe flag not set; analyzing archive contents... webadmin.plugin: module references __file__ Adding tracwebadmin 0.1.2dev-r5753 to easy-install.pth file Installed c:\program files\python24\lib\site-packages\tracwebadmin-0.1.2dev_r5753-py2.4.egg Processing dependencies for tracwebadmin==0.1.2dev-r5753 Finished processing dependencies for tracwebadmin==0.1.2dev-r5753 C:\> 자 여기까지 해서 CygwinSSHD만 빼고 모두 설치하였네요. 저는 CygwinSSHD는 설치하지 않도록 하겠습니다. 설치가 무지 복잡하죠? 어찌되었건 설치는 했으니 다음장에서 설정과 실행을 시켜보도록 하겠습니다.
by Anna 안나 2008. 7. 11. 20:42
회사에서 프로그램 버전 관리의 필요성이 생겨서 CVS를 할까 Subversion을 할까 아니면 SourceSafe를 할까 고민을 하다가... SourceSafe는 좋긴하지만 상용에다가 윈도우즈 외의 플랫폼에서는 쥐약이라 포기하고.... CVS를 검토하다가 알게된 Subversion이 CVS의 기능을 보강한 새로운 버전관리 프로그램이라고 해서 Subversion을 사용하기 맘 먹었습니다. 서브버전을 설치하고 이것 저것 쓰다가 보니깐... 검색을 할 수가 없다는 단점이 있어서... 또 뒤적거리다가 Trac이라놈이 있다는 걸 알게 되었습니다. 1장. Trac On Windows - 설치 파일 버전맞추기 Trac은 서브버전에 위키 웹을 합쳐놓은 형태라서 게시판처럼 사용도 하고 버전도 관리하도록 되어 있더 군요. 그런데 다 좋은데... 설치가 너무 어려웠습니다. Python을 잘 모르는 저에게는 더욱 어려웠습니다. 사실은 아직 완전한 설치를 하질 못했습니다. 이글은 지금까지 삽질 했던 부분과 앞으로의 삽질 할 부분을 정리하기 위해 작성하고 있습니다. 이것 저것 보면서 삽질을 하다가 보니 나중에 다시 설치하라고 하면 못 할것 같아서.. ㅠㅠㅠ 그럼 이제 시작해 봅도록 합시다. 1. 설치해야 할 프로그램 목록 제가 설치하면 삽질한 원인 중 가장 큰 원인이 설치를 해야할 것이 너무 많은데... 이것들이 서로 버전을 맞춰주지 않으면 안된다는 것이었습니다. 될 수 있으면 최신버전이 반듯이 설치에 성공할 수 있다는 보장을 할 수 없습니다.아마도 Trac을 설치해보셨던 분들은 모두 마찬가지 이유로 삽질을 하셨을 겁니다. 그래서 설치하기 전에 필요한 버전의 모듈이 모두 있는지 확인하는 과정이 필요합니다. 1) 반듯이 필요한 프로그램 혹은 모듈들... - Apache Web Server
- Subversion
- Python
- Subversion for the Python bindings (파이썬과 서브버전의 연계를 위함)
- Apache Module fro the Python (파이썬과 아파치 연동을 위함)
- DocUtils
- PySQLite
- ClearSilver
- TRAC 위에서 PySQLite는 Trac이 DB를 사용해야 하는데 SQLite를 사용할 때 필요합니다. 참고로 SQLite 대신에 MySQL을 사용할 수도 있습니다. 2) 필요하다면 설치할 수 있는 모듈들... - WebAdmin (Trac의 Plug-In) - 한글 Trac Template (한글화를 위함) - CygwinSSHD (보안 강화를 위함) 설치해야 할 항목만 봐도 기가 확 죽죠? ㅠㅠ 저도 답답해 옵니다. 거기다가 이것들이 버전을 가린다니 더욱 그렇습니다. 모두 오픈 소스기 때문에 버전이 안맞다면 컴파일을 해서 사용해도 되겠지만 다른 놈은 괜찮은데... ClearSilver라는 놈을 윈도우즈에서 컴파일하려면 윈도우즈에 리눅스 환경을 만들어야 해서 그거 하나 컴파일 하려고 더 복잡하게 만들 수는 없지 않겠습니까? ㅎㅎ 그래서 될 수 있으면 바이너리를 구해서 사용하려다 보니깐... 해당 버전의 파일들의 바이너리를 제공하고 있는지 확인을 꼭 해야 합니다. 2. 설치할 프로그램 다운로드 및 버전 체크 문제는 각각의 프로그램을 연결해 주는 모듈들입니다. 버전이 맞는 놈(바이너리 파일)이 있는지 주로 체크해야 할 것은 다음과 같습니다. - 아파치와 서브버전 - 아파치와 파이썬 - 서브버전과 파이썬 - 파이썬과 클리어실버 현재 제가 글을 쓰고 있는 시점을 기준으로 이야기 하도록 하겠습니다. 지금은 버전을 맞추는 방법을 설명하고 있으므로 바로 다운로드 하지 말고 우선 종이에다가 버전들을 적으면서 살펴만 보세요.
1) 아파치 웹서버 아파치 웹서버를 다운로드 할 수 있는 곳 http://httpd.apache.org/ 현재 최신 Release버전은 2.2.4입니다. 그리고 2.0.59버전도 있네요. 2) 서브버전 서브버전을 다운로드 받을 수 있는 곳 http://subversion.tigris.org/project_packages.html 이 페이지의 맨 아래에는 윈도우즈 바이너리를 다운받을 수 있는 링크가 있습니다. 보면 아파치 버전에 따라서 다운 받을 수 있는 경로가 구분되어 있습니다. Win32 packages built against Apache 2.0 Win32 packages built against Apache 2.2 다행이도 현재 아파치 웹서버의 최신버전이 2.2.4에서 돌아갈 수 있는 서브버전이 있네요. 그래서 Win32 packages built against Apache 2.2 에 가보면 여러 서브버전이 있는데... 1.4.4가 최신 버전이 구요. 1.4.3도 있습니다. 한가지 더 확인할 것은 파이썬과의 연동을 위해 파이썬 연동 모듈이 파이썬의 어떤 버전까지를 지원하는지 확인합니다. 보니깐.. py23, py24, py25 까지 지원하는 모듈이 있군요. 3) 파이썬 파이썬을 다운로드 받을 수 있는 곳 http://www.python.org/download/ 파이썬은 2.5.1, 2.4.4, 2.3.6 버전을 다운 받을 수 있습니다. 4) 파이썬과 서브버전의 연계 모듈 이 모듈은 아까 2)번 항목에서 이미 살펴 보았습니다. py23, py24, py25가 있었죠. 즉, 파이썬 2.3.x 부터 파이썬 2.5.x까지 모두 연동이 가능하군요. 5) 아파치와 파이썬 연동 모듈 이 모듈은 http://httpd.apache.org/modules/python-download.cgi 에서 확인합니다. 이 모듈은 파이썬 스크립트가 CGI방식이 아니라 아파치의 기능확장 모듈(Fast-CGI)로 동작할 수 있도록 해 줍니다. 최신 버전은 3.3.1인데 윈도우즈용 바이너리가 있는 링크를 가보면.. 파이썬과 아파치 연동 버전관계를 파일명을 통해서 알 수 있는데... 현재는 다음과 같은 쌍으로 연동할 수 있는 바이너리를 제공하고 있습니다. py2.3.x - Apache2.0.x
py2.3.x - Apache2.2.x
py2.4.x - Apache2.0.x
py2.4.x - Apache2.2.x
py2.5.x - Apache2.0.x
py2.5.x - Apache2.2.x 6) 닥유틸스 이 모듈은 http://docutils.sourceforge.net/ 에서 다운 받을 수 있습니다. 현재 버전은 0.4인데 파이썬 스크립트로만 되어 있고 파이썬으로 설치할 거기 때문에 특별히 버전을 체크할 필요는 없습니다. 7) PySQLite 이 모듈은 http://initd.org/tracker/pysqlite/wiki/pysqlite 에서 다운 받을 수 있습니다. 현재 버전은 2.3.5 이고 Python 2.3, 2.4. 2.5 를 지원하는 바이너리를 받을 수 있습니다. 8) 클리어실버 이 모듈은 http://www.clearsilver.net/downloads/ 에서 다운 받을 수 있습니다. 현재 버전은 0.10.5 인데... 윈도우즈용 바이너리로 제공하는 놈은 0.9.14까지만 제공하고 있으며 게다가 Python은 2.3과 2.4만을 지원하네요. 지금까지는 좋았는데... 여기서 문제가 생겼네요. 즉, 이놈 때문에 Python 2.5버전을 사용할 수 없다는 것입니다. 고로 파이썬의 버전은 2.4이하로 되어야 겠네요. 9) 트랙(Trac) 이 모듈은 http://trac.edgewall.org/wiki/TracDownload 에서 다운 받을 수 있습니다. 현재 버전은 0.10.4이고 특별하게 버전을 맞출 필요는 없습니다. 참고로 아래의 11)에 의해 한글 트랙을 사용하실 분은 영문 트랙을 굳이 다운 받으실 필요가 없습니다. 10) 웹어드민 웹어드민은 트랙의 관리를 웹상에서 해 줄 수 있는 모듈입니다. 이 모듈은 SetupTool을 다운 받아 설치하고 이 SetupTool을 이용해서 설치해야 합니다. 우선 SetupTool을 다운 받는 곳은 http://trac.edgewall.org/wiki/TracPlugins 입니다. 여기에서 http://peak.telecommunity.com/dist/ez_setup.py 파일을 다운 받습니다. 그 다음 웹어드민은 http://trac.edgewall.org/wiki/WebAdmin 에서 다운 받습니다. 이놈은 트랙 버전과 파이썬버전을 동시에 체크해야 합니다. 보니깐 최근버전이 0.11인데 트랙 0.93이상을 요구하며 파이썬 2.3과 2.4를 지원하는 군요. 11) 한글 Trac Trac을 한글화 하고 있는 사이트로 http://kldp.net/projects/trac-ko/ 이 있으며 http://kldp.net/frs/?group_id=724 에서 다운 받을 수 있습니다. 현재 버전은 0.10.4네요... 다행히도 최신버전의 영문 Trac이 한글화 되어 있습니다. 12) CygwinSSHD 이 프로그램은 Secure Shell Daemon으로 트랙의 보안을 강화 시킬 수 있습니다. http://ist.uwaterloo.ca/~kscully/CygwinSSHD_W2K3.html 에서 다운받을 수 있습니다. 13) 버전체크 결과 자 여기까지 해서 버전체크를 마쳤습니다. 결과적으로 다음의 조합이 현재 상태에서 컴파일 없이 윈도우즈에 가장 최신 모듈들을 이용해서 트랙을 설치할 수 있는 조합입니다. 아파치는 2.0.59, 서브버전 1.4.4, 파이썬 2.4.4 그리고 나머지는 이에 맞는 최신버전들을 사용하면 되겠습니다. 이제 버전체크 결과가 나왔으니 해당 사이트에서 다운로드를 받아 한 디렉토리에 담아 놓으세요. 바이너리의 경우 인스톨본을 제공하는 경우도 있고 그냥 zip이나 tar로 묶어져 있는 경우도 있습니다. 저는 zip이나 tar로 묶여 있는 놈들을 주로 받을 생각입니다. 고생한 김에 좀 더 고생해 보려구요. 여러분들은 인스톨본을 받으세요. 그러면 설정이 좀 자동화 되는 부분이 있습니다. 마지막으로 한가지 더 이야기 하자면... 윈도우즈 상에서 트랙을 돌려보기가 넘 어려워서 인지 다음의 웹사이트에서 압축만 풀어서 트랙을 사용할 수 있도록 묶음 버전을 만들고 있습니다. http://sourceforge.net/projects/traconwindows 이 묶음 파일에서의 각 파일들의 버전은 다음과 같습니다. Python 2.4.3 for win32
Apache 2.2 for win32
mod_python
clearsilver 0.9.14 for win32
Subversion 1.4.2
svn-python 1.4.2 for win32, Python 2.4
pysqlite 2.3.2 for win32, Python 2.4
Trac 0.10.3
이 TracOnWindows(ALLinOne)은 현재 0.1 alpha 2가 최신버전입니다. 단, C:\Trac이라는 디렉토리 고정되어 있습니다. 다운 받은 후 압축을 풀고 C:\Trac디렉토리에 가서 run-apache.bat 파일을 실행시킨 후 웹브라우저로 http://localhost:8080/projects 에 접속하면 됩니다. 관련내용은 http://yeoupooh.us.to:8080/wiki/display/pu/TracOnWindows 을 참조하세요.




출처 : http://www.nicklib.com/menu/study/
by Anna 안나 2008. 7. 11. 20:24
시작하기 전에 이미지 다운로드 - 문단이나 항목 정리에 있어서, Unorderd List 태그인 <ul>과 항목 리스트를 나타내는 <li>태그는 매우 유용한 태그입니다. 다만, 웹브라우저에 따라 여백이나 패딩의 및 불릿 이미지를 표기하는 방식이 꽤나 다르기 때문에 사용상 주의도 필요합니다. 또한, 그동안 웹페이지의 레이아웃을 유지하기 위해 그동안 테이블<table>을 많이 이용했으나, 리스트 태그와 CSS를 이용한 방법이 바람직하다고 여깁니다. 기본적으로, 여백과 패딩값은 설정해 주어야 IE7과 FireFox2사이의 레이아웃 차이를 없앨 수 있습니다. ul{ margin: 0; padding: 0;}/* li태그는 해당 내용 상속됨 */ <li> 및 display속성값이 [list-item]인 경우 다음과 같은 스타일을 적용할 수 있습니다. * <ol> <ul> 에 다음의 속성을 지정하면, 해당 태그의 서브 <li>요소에는 모두 상속됩니다. [ 리스트 속성의 일괄 지정 ] list-style: type image position; 리스트의 글머리표의 종류를 지정(type), 글머리표의 이미지를 지정(image) 글머리표의 표시위치 지정(position)을 지정합니다. 속성값인 type, image, position은 아래에서 설명할 속성값을 따릅니다. [ 글머리표의 종류 지정 ] list-style-type: disc(초기값); 글머리표의 이미지가 있는 경우에는, 이미지가 우선적으로 표시됩니다. 이미지가 없거나(none) 지정한 이미지를 표시할 수 없는 경우, 이 설정값이 유효하게 됩니다. 구버전의 IE에서는 인식되지 않으나, IE7에서는 인식이됩니다. 다음과 같은 속성값을 이용할 수 있습니다. none : 글머리표를 표시하지 않습니다. disc : 검은색 원(기본 값) circls : 선으로 그려진 원 square : 선으로 그려진 사각형 lower-roman : 로마숫자 소문자 upper-roman : 로마숫자 대문자 lower-greek : 그리스 문자 소문자 decimal : 일반 숫자 decimal-leading-zero : 첫 자리에 0을 붙인 숫자 lower-latin, lower-alpha : 알파벳 소문자 upper-latin, upper-alpha : 알파벳 대문자 hebrew : 히브리 숫자 armenian : 아르메니아 숫자 georgian : 그로지아 숫자 <ul style="list-style-type: lower-roman;"> index content postscripe [ 글머리표 이미지 지정 ] list-style-image: url(); 디폴트 값은 none입니다. 아래와 같이 사용할 수 있습니다. <ul style="list-style-image: url(ico_note.gif);"> index content postscripe [ 글머리표 이미지 표시 위치 지정 ] list-style-position: outside(초기값); 리스트 항목의 박스에 대한 글머리표의 위치를 설정하는 속성입니다. 사용할 수 있는 속성값으로는 inside 와 outside가 있으며 디폴트 값은 outside입니다. <ul style="border: 1px solid; list-style-image: url(ico_note.gif);"> index content postscripe <ul style="border: 1px solid; list-style-position: inside; list-style-image: url(ico_note.gif);"> index content postscripe 위 예의 경우, margin: 0; padding: 0; width: 200px로 지정되어 있습니다. 테두리를 1px로 설정하였으므로, 박스의 레이아웃은 가로 202px가 됩니다. 글머리표 이미지를 inside로 설정한 경우는 이 202px안에 모두 표기가 됩니다. outside의 경우는 해당 박스의 여백이나 패딩값에 관계없이 박스의 외곽에 표기됩니다. 심지어 외곽 박스의 영역 을 침범해서라도 표기가 됩니다. 다만, 외곽 박스가 <body>인 경우에는, IE7의 경우에는 패딩영역에만 표기됩니다. 즉, FireFox2경우에는 패딩영역이든 여백(margin)영역이든 표기할 공간이 있으면 표기해 주나, IE7의 경우에는 여백(margin)영역만 존재하고 패딩영역이 없는 경우 글머리표 이미지는 표기가 되지 않습니다. <ul style="border: 1px solid; list-style-position: inside; list-style-image: url(ico_note.gif);"> <li style="list-style-position: outside;">index</li> index content postscripe 이 상, 리스트 항목의 글머리표에 대한, 속성설정에 대하여 살펴보았습니다. 이 항목들만을 가지고 글머리표 레이아웃을 설정하는 방법에는 한계가 있습니다. 글머리표로 사용한 이미지의 크기에 따라, 상하 높이를 설정하는 부분이 없기 때문입니다. ul{ list-style: none; margin: 0; padding: 0;} 따라서, 레이아웃을 위한 일반적인 사용에는 위와 같이 사용할 뿐입니다. http://blog.naver.com/einmong/10011586136
by Anna 안나 2008. 7. 9. 19:25
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <htmlxmlns="http://www.w3.org/1999/xhtml"> <head> <metahttp-equiv="Content-Type"content="text/html; charset=euc-kr"/> <title>Tabs</title> <!-- Description : XHTML Tabs style code [ IE6 , IE7 , FF2] --> <!-- Author : blueb(http://blueb.net/blog) --> <!-- Date : 2008-05-08 --> </head> <body> <scripttype='text/javascript'> function tabs(idx){ for(i = 1; i <= 3 ; i++ ){ document.getElementById('tab'+i).className = ""; document.getElementById('content'+i).className = "content hide"; } document.getElementById('tab'+idx).className = "active"; document.getElementById('content'+idx).className = "content show"; } </script> <!--[if IE 6]> <styletype='text/css'> /* IE 6 */ div.tabs-area { position:relative; z-index:2; width:100%; height:48px; padding:0;margin:0;overflow:hidden; } div.tabs-line { position:relative; z-index:1; width:100%; height:0; top:-1px; border-top:1px solid #D3D9E6; } </style> <![endif]--> <!--[if IE 7]> <styletype='text/css'> /* IE 7 */ div.tabs-area { position:relative; z-index:2; height:48px;width:100%;padding:0;margin:0 } div.tabs-line { position:relative; z-index:1; width:100%; height:1px; top:-2px; border-bottom:1px solid #D3D9E6; } </style> <![endif]--> <!--[if !IE]>--> <styletype='text/css'> div.tabs-area { position:relative; z-index:2; width:100%; height:45px; padding:0;margin:0;overflow:hidden; } div.tabs-line { position:relative; z-index:1; width:100%; height:1px; top:-1px; border-top:1px solid #D3D9E6; } </style> <!--<![endif]--> <styletype='text/css'> ul.tabs { padding:0px;margin:0px; z-index:2; } ul.tabs li { list-style:none; display:inline; height:50px; } ul.tabs li a { padding:5px 20px 3px 20px; border:1px solid #D3D9E6; text-decoration:none; font-size:9pt; line-height:30px; color:#4B69AF; background:#E9ECF2; } ul.tabs li a:hover { background:#E8FFFF; } ul.tabs li a.active { background:#fff; color:#4B69AF; border:1px solid #D3D9E6; border-bottom: 1px solid #ffffff; padding:10px 20px 3px 20px; font-weight:bold; line-height:70px; } div.content { /* 컨텐츠 영역 박스 스타일 지정 하는곳 */ } div.show { display:block;} div.hide { display:none; } </style> <h3>IE6, IE7, FF2 Tabs</h3> <div> <divclass='tabs-area'> <ulclass='tabs'> <li></li> <li><aid='tab1'title="Tab1 Desc"href="javascript:tabs('1');"class='active'>Tab 1</a></li> <li><aid='tab2'title="Tab2 Desc"href="javascript:tabs('2');">Tab 2</a></li> <li><aid='tab3'title="Tab3 Desc"href="javascript:tabs('3');">Tab 3</a></li> </ul> </div> <divclass='tabs-line'></div> </div> <divid='content1'class='content show'> <h1>Content 1</h1> </div> <divid='content2'class='content hide'> <h1>Content 2</h1> </div> <divid='content3'class='content hide'> <h1>Content 3</h1> </div> </body> </html> http://blueb.net/blog/1224
by Anna 안나 2008. 7. 9. 19:21
<head>
<STYLE type='text/css'>
li.1 { list-style-type : lower-roman } /* 로마숫자 소문자으로된 목록 */
li.2 { list-style-type : upper-roman } /* 로마숫자 대문자으로된 목록 */li.3 { list-style-type : lower-alpha } /* 알파벳 소문자으로된 목록 */
li.4 { list-style-type : upper-alpha } /* 알파벳 대문자으로된 목록 */
li.5 { list-style-type : disc } /* 점으로 된 목록 */
li.6 { list-style-type : circle } /* 속이 하얀색 원으로 된 목록 */li.7 { list-style-type : square } /* 사각형으로 된 목록 */li.8 { list-style-type : decimal } /* 숫자로 된 목록 */li.9 { list-style-type : none } /* 아무 표시 없음 */</STYLE>
</head> 리스트 태그(ul, ol, li)와 같은 기능을 CSS로 만들기.(list-style-type)

<ol>
<LI class='1'>리스트 항목 lower-roman</LI>
<LI class='2'>리스트 항목 upper-roman</LI>
<LI class='3'>리스트 항목 lower-alpha</LI>
<LI class='4'>리스트 항목 upper-alpha</LI>
<LI class='5'>리스트 항목 upper-alpha</LI>
<LI class='6'>리스트 항목 disc</LI>
<LI class='7'>리스트 항목 circle</LI>
<LI class='8'>리스트 항목 square</LI>
<LI class='9'>리스트 항목 none</LI>
</ol> 출처 : http://bbs.freecount.net/bbs/bbs.php?m=view&bid=study_2&id=21&page=1&cate=&q=
by Anna 안나 2008. 7. 9. 19:20
IT계열에 종사하시고 계신, 또는 관심이 있으신 방문자님들은..

한번쯤 보셔도 될 좋은 포스트입니다.^^

http://www.dal.co.kr/blog/archives/000502.html

'개인 > freeboard' 카테고리의 다른 글

반응속도 테스트  (0) 2008.09.21
가치평가에 이어..상대방평가?를 해보았다.^^  (0) 2008.09.21
가치평가  (0) 2008.09.21
CSS로 만든 Layout 소개  (1) 2008.07.12
jQuery 관련 링크  (0) 2008.07.12
구글 스타일 로고 만들기  (0) 2008.07.07
재미있는 페이지.  (0) 2008.07.07
산돌폰트패키지 뷰어(viewer)  (0) 2008.06.29
온라인 촛불 문화제 이미지들..  (0) 2008.06.21
제목  (0) 2008.05.07
by Anna 안나 2008. 7. 7. 19:14
다음은 맨아래 '구글 스타일 로고 만들기' 웹프로그램(?)으로 만들어진 구글스타일로고 예시입니다.

((-------IMAGE-------))((-------IMAGE-------))((-------IMAGE-------))((-------IMAGE-------))((-------IMAGE-------))((-------IMAGE-------))((-------IMAGE-------))((-------IMAGE-------))((-------IMAGE-------))((-------IMAGE-------))((-------IMAGE-------))

구글 스타일 로고 만들기 : http://www.littlefunny.com/Google.aspx

'개인 > freeboard' 카테고리의 다른 글

가치평가에 이어..상대방평가?를 해보았다.^^  (0) 2008.09.21
가치평가  (0) 2008.09.21
CSS로 만든 Layout 소개  (1) 2008.07.12
jQuery 관련 링크  (0) 2008.07.12
구글 매니아가 구글에 환호하는 이유  (0) 2008.07.07
재미있는 페이지.  (0) 2008.07.07
산돌폰트패키지 뷰어(viewer)  (0) 2008.06.29
온라인 촛불 문화제 이미지들..  (0) 2008.06.21
제목  (0) 2008.05.07
하핫..-_-;  (1) 2008.03.27
by Anna 안나 2008. 7. 7. 19:11
링크 : http://www.shibumi.org/eoti.htm


The End of the Internet Congratulations! This is the last page.Thank you for visiting the End of the Internet. There are no more links.

You must now turn off your computer and go do something productive.

Go read a book, for pete's sake.




한번..읽어보세요^^

'개인 > freeboard' 카테고리의 다른 글

가치평가에 이어..상대방평가?를 해보았다.^^  (0) 2008.09.21
가치평가  (0) 2008.09.21
CSS로 만든 Layout 소개  (1) 2008.07.12
jQuery 관련 링크  (0) 2008.07.12
구글 매니아가 구글에 환호하는 이유  (0) 2008.07.07
구글 스타일 로고 만들기  (0) 2008.07.07
산돌폰트패키지 뷰어(viewer)  (0) 2008.06.29
온라인 촛불 문화제 이미지들..  (0) 2008.06.21
제목  (0) 2008.05.07
하핫..-_-;  (1) 2008.03.27
by Anna 안나 2008. 7. 7. 18:39
((-------IMAGE-------))((-------IMAGE-------))((-------IMAGE-------))((-------IMAGE-------))


브러쉬 다운로드 : Japanese_Foliage.abr.zip

'디자인_포토샵 > p_brush' 카테고리의 다른 글

Elegant Brushes (테두리, 언더라인)  (0) 2008.03.08
귀여운 말풍선  (0) 2008.03.08
say it nicely 고풍스런글씨2  (0) 2008.03.08
say some 고풍스런글씨1  (0) 2008.03.08
handwriting 2  (0) 2008.03.08
handwriting 1  (0) 2008.03.08
valentine brushes  (0) 2008.03.08
random brushes  (0) 2008.03.08
man  (0) 2008.03.08
stars  (0) 2008.03.08
by Anna 안나 2008. 7. 7. 17:33
버젼 관리의 필요성

여러 사람이 함께 작업할 경우 작업 결과를 모아두는 곳이 필요하다. 이때 쉽게 떠올릴 수 있는 것이 FTP 서버나 공유 서버에 작업 결과물을 모아둘 저장소를 만들어 두는 것이다. 그 저장소에는 프로그램의 소스 코드도 있을 것이고, 개발 과정을 정리한 문서, 프로그램에서 사용하는 라이브러리 등도 있을 것이다.

각각의 개발자는 작업을 하기 위해서 그 저장소에 저장된 파일을 가져와서 자신의 로컬 PC에 복사본을 만들어서 작업을 수행한다. 그런 다음 작업이 끝나면 다시 그 결과물을 저장소에 넣어둘 것이다. 이런 상황에서는 몇 가지 문제점이 발견된다.

우선 서로 다른 두 명의 개발자가 동시에 같은 파일을 복사해서 작업한 다음 저장소에 올리는 경우를 생각해보자. 열심히 작업을 해서 한 명이 먼저 작업을 끝내서 저장소에 올렸다. 잠시 후에 그 사실을 모르고 있는 또 다른 개발자가 저장소에 자신의 작업 결과물을 저장소에 보내는 순간 덮어쓰기가 일어나며 앞선 개발자의 작업은 덧없이 날아가 버린다.

물론 저장소에 올리기 전에 내가 내려 받은 파일의 최종 수정일을 기록해뒀다가, 올리기 전에 서버의 최종수정일이 변하지 않았는지를 확인하면 되지만 너무나 귀찮은 일이다.

혹 그런 귀찮음을 무릅쓰고 철저하게 확인을 한다 하더라도, 날짜가 변경되어 있을 때 도대체 누가 어떤 목적으로 그 파일의 어떤 부분을 수정했는지 알아내려면 모든 개발팀에게 “누가 언제 왜 어떤 목적으로 어떤 부분을 수정했나요?”하고 일일이 물어봐야 한다.

물론 이러한 확인이 이뤄진 후에도 앞서 작업한 개발자와 상의해서 서로 다른 두 개의 결과물을 조정해서 합의된 최종 결과물을 만든 후에 서버에 올리는 것도 여러분의 몫이다.

또 다른 경우는 며칠 전의 상태로 작업 결과를 되돌려야 하는 상황이 발생할 때이다. 1차 개발을 완료한 시점에서 추가적인 요구 사항을 처리하기 시작했다고 가정해보자. 이 때 잘못된 판단으로 도저히 회복하기 힘든 큰 실수를 저질러서 차라리 1차 개발이 완료된 시점에서 다시 개발하는 것이 좋을 때도 있을 것이다.

단순한 공유 서버를 사용했을 때 이런 상황을 해결하기 위한 방법은 전체 작업 결과물을 특정 날짜 별로 복사해서 따로 관리하는 것이다.

이 방법은 물론 특정 날짜의 작업으로 되돌릴 수 있지만, 작업 결과물의 덩치가 크고 개발기간이 길수록 불필요한 디스크의 낭비가 심해진다. 만일 2시간 전의 상태로 저장소를 되돌리고 싶다면 어떻게 할까?

매 시간마다 또 특정 태그를 붙인 복사본을 만들면서 작업을 할 것인가? 또 어떤 내용은 그대로 두고 특정 파일만을 이틀 전의 상태로 되돌리고 싶을 때는 어떻게 할 것인가?

소프트웨어의 큰 특징 중 하나는 프로젝트가 진행되는 기간 동안 그 소프트웨어는 계속해서 변경된다는 것이다. 이 때 변경되는 것은 여러분이 작성한 코드 뿐 아니라, 관련된 문서나 적용하는 라이브러리를 포함한 프로젝트의 모든 산출물이다.

여러 명이 동시에 작업하는 프로젝트라면 그러한 변경으로 인해 모든 개발자의 작업이 영향을 받게 된다. 바로 이런 문제들을 해결하기 위해 등장한 것이 바로 버전관리 시스템이다.


요약하자면


개발 버전과 릴리즈 버전이 섞이지 않고 쉽게 관리 할 수 있습니다.
소스를 잘못 수정 했더라도 기록이 남고 되돌리기가 쉽습니다.(많은 파일의 경우 유용)
수정, 추가, 삭제 등의 기록이 모두 남고 변경 사항을 추적하기 쉽습니다.
개발자들이 따로 따로 백업을 하지 않아도 됩니다.

버전 관리 시스템의 종류
현재 나와 있는 소프트웨어 버전 관리 시스템은 여러 종류가 있습니다. 각각 장단점이 있습니다.


- CVS (Concurrent Version System) : 가장 널리 사용되며 역사가 깊은 버전 관리 시스템입니다. http://www.cvshome.org

- Subversion : CVS의 단점을 개선하고 CVS를 대체할 목적으로 개발 되었습니다. 이 문서에서 설명할 버전 관리 시스템입니다. http://subversion.tigris.org

- Visual Sourcesafe : Microsoft에서 만든 버전 관리 시스템입니다. CVS와는 버전 관리 관점에서 조금의 차이점이 있습니다. 윈도우 기반 소프트웨어의 버전 관리를 할 때 자주 사용됩니다. http://msdn.microsoft.com/ssafe/

- Clear Case : Rational이라는 회사에서 만든 버전 관리 시스템입니다. 지금은 IBM에 합병되었습니다. 상용 소프트웨어입니다. http://www-306.ibm.com/software/rational

- BitKeeper : 리눅스 커널이 BitKeeper를 이용해서 개발 하고 있습니다. 상용 소프트웨어입니다. http://www.bitkeeper.com


SVN 과 CVS의 차이점

- 소스 코드 뿐 아니라 바이너리 파일, 문서 지원 - 커밋의 단위가 파일이 아니라 변경된 작업 단위
- 디렉토리, 파일별로 세밀한 접근 가능
- CVS에 비해서 빠르다.



SVN 기본 용어 정리

리비전 (rivision) : 변경들의 논리적인 단위(?), 변경 집합이라고 해석한 책도 있음 리비전 번호 (rivision %d) : 변경들의 논리적인 증가, 0부터 새로운 변경 집합이 발생하면 1 증가

저장소 (repository) : 여러 가지 버전들을 저장하는 하나의 장소 작업본 (working copy) = 작업 디렉토리 (working directory) = 작업장 (workspace) : 저장소의 파일들은 직접 변경할 수 없다. 변경하기 위해서는 저장소의 파일들을 작업하는 컴퓨터 시스템(즉, 로칼)으로 받아 사용해야 한다. 이런 저장소의 파일들이 로칼에 저장되는 공간을 workspace라고 한다. 체크 아웃 (check out) : 작업장을 처음 사용하기 위해서는 저장소에서 필요한 파일들을 받아야 한다. 처음으로 작업장에 파일을 채우는 과정을 "체크 아웃"이라 한다. 체크 아웃(쉽게 다운로드)을 통해 작업장에 최신 파일이 저장된다. 체크 아웃의 주어를 "파일들"이라고 생각하면 이해가 쉽다. 파일이 호텔(=저장소)에 들어가 숙박(=저장) 후 호텔을 나와 직장(=작업장)에 일하러 가기 위해 프런트에서 "체크 아웃"한다고 생각해보자. 그러면 이해가 쉽다. 커밋 (cummit) : 작업 후에 작업장의 파일은 변경된다. 이 변경된 파일들을 다시 저장소에 저장하고 싶을 때, 저장소에 다시 올려 보내는 것(쉽게 업로드)을 '커밋'이라 한다. 커밋하게 되면 리비전의 번호가 증가하게 된다. 갱신 (update) : 지난 작업을 마치고 커밋한 이후에 다른 사람이 저장소의 파일을 변경(커밋)할 경우가 있다. 있든 없든, 변경 여부를 확인하기 위해(=변경 여부를 작업장에 반영하기 위해) 작업장의 파일들을 갱신 하는 과정을 거친다. 이를 체크 아웃이라고 설명하는 책, 문서 등등도 있다. 태그 (tag) : 특정 시점(리비전)에 태크(=이름)을 부여하여 기억하기 쉽게 할 수 있다. 그 태크를 이용해서 체크 아웃할 수 있다. 체크 인 (check in) : commit Import : 저장소에 파일들을 집어 넣는다. svn import 명령 차이 (diff) : 리비전 간의 코드의 차이점, 작업중인 버전과 HEAD(=저장소의 최신버전)을 비교할 때 [ + ] 기호는 작업장에서 변경된 부분, [ - ] 기호는 다른 사용자에 의해 변경된 부분을 표시하는데 사용 svn diff 명령 패치 (patch) : svn diff 명령의 출력을 파일에 저장하면 그 자체가 패치다. 패치를 이용한 갱신은 patch 명령


윈도우에서 SVN을 사용하기 위해 필요한것들

1. SVN

2. 아파치

3. Tortoise http://beehone.egloos.com/1945767
by Anna 안나 2008. 7. 6. 23:07
<input type="text" value="id" > 위와 같은 소스를 적용하면, 박스에 id가 입력되어 있는 상태가 됩니다. 우리는 사용자가 저 박스에 마우스를 클릭하면 박스에 id 라는 글자가 없어지게 하려고 합니다. 다음은 해결책입니다. 자바스크립트의 onFocus 를 사용하시면 됩니다. onFocus 는 포커스가 폼이나 창에 주어졌을 때의 이벤트를 판단하는 이벤트 헨들러입니다. 폼을 이동하거나 포커스가 없는 창을 클릭했을 때를 이벤트로 판단하고 설정한 것을 처리합니다. onfocus="this.value='';" 위 소스를 input소스 내에 첨가하시면 되겠습니다. inpyt 소스 내에 첨가한 예는 다음과 같습니다. <input type="text" value="id" onfocus="this.value='';">
by Anna 안나 2008. 7. 6. 22:13
readonly 와 disabled 둘 다, input tag사용시 text필드의 어떤값을 정해두고 고정하여 수정을 못하게 합니다. ● disabled 를 사용할 경우 disabled 필드를 사용하면, 이 필드를 사용하지 않는다고 정의가 내려져 이 값이 다음페이지에 넘어가지 않습니다. ● readonly 를 사용할 경우 이 필드값을 수정하지 못한 체로 다음페이지에 이 값을 넘깁니다. 요약 :: 다음페이지에 넘길수 있느냐/없느냐의 차이점입니다.
by Anna 안나 2008. 7. 6. 22:08
input box 위에 마우스 올리면 위 텍스트가 작은 창같이 떠서 그중에서 클릭하면 그 값이 input box 에 들어가도록 해주는 자바스크립트입니다. ※셀렉트박스가 아니지만, 셀렉트박스와 비슷한 기능을 내게 하는 것이랍니다.
-다른소스 <HTML>
<HEAD>
<TITLE> </TITLE>
<script>
function test(){
html = " <table width='200' cellpadding='1' cellspacing='1' bgcolor='#58aeca'>"
+ " <tr> "
+ " <td valign='bottom' bgcolor='#e0f6fd'><a href='javascript:setup(1);'>사과</a> | <a href='javascript:setup(2);'>바나나</a> | <a href='javascript:setup(3);'>수박</a></td>"
+ " </tr>"
+ " <tr> "
+ " <td align='right' valign='bottom' bgcolor='#e0f6fd'><a href='javascript:divClose();'>[닫기]&nbsp</a></td>"
+ " </tr>"
+ " </table>"

oElement = document.elementFromPoint(event.x, event.y); //마우스가 움직일때의 대상객체

prevcontent.style.display=''; //레이어를 보이고
prevcontent.style.visibility = "visible";
prevcontent.style.pixelLeft=event.x-100; //레이어의 가로 위치지정
prevcontent.style.pixelTop=document.body.scrollTop+event.y; //레이어의 세로위치 지정
prevcontent.innerHTML=html; //레이어의 내용 출력
}
function divClose(){
prevcontent.style.display='none';
prevcontent.style.visibility = "hidden";
} function setup(tmp){
//값을 셋업하는 부분입니다. 사실 위쪽에 html부분을 수정하면 바로 값을 받아서 셋팅할수도 있겠지요.
var f = document.f1;
if(tmp==1){
f.text1.value="사과";
}else if(tmp==2){
f.text1.value="바나나";
}else if(tmp==3){
f.text1.value="수박";
} }
</script> </HEAD> <BODY>
<div id="prevcontent" style="visibility:hidden;position:absolute;left:10;top:10;background:white;filter:alpha(opacity=90);border: 1px solid;padding:2 2 2 2"></div>
<form name="f1">
<a href="#" onmouseOver="test();"><input type="text" name="text1"></a>
</form>
</BODY>
</HTML>
http://kin.naver.com/detail/detail.php?d1id=1&dir_id=10105&eid=B3IGxpGX3apB9hTTyNDUHgufjkLCy6B0&qb=aW5wdXQgdGV4dCDA2rW/
by Anna 안나 2008. 7. 6. 22:03
11:11.11 이와같이 11분 11초 11 과 같이..타임단위로(형식으로) 자동변경합니다. 예를 들면 사용자가 텍스트 박스안에 111를 넣었을경우 00:01.11이 자동으로 반환되게 하는 자바 스크립입니다. 또다른 예로 1111를 입력받았을경우 00:11.11
만약 사용자가 00:11.11 를 입력받았을경우 이건 제대로 입력됐기 때문에 그냥 그 값을 반환하게 되는것이죠. http://kin.naver.com/detail/detail.php?d1id=1&dir_id=10105&eid=HeqLjkhKuPVbrCGeXv9UJ/mSA3BMErM6&qb=aW5wdXQgdGV4dCDA2rW/
by Anna 안나 2008. 7. 6. 22:01
Version Control System에서 공통적으로 사용하는 용어들 저의 잘못된 번역으로 인한 오해와 잘못된 지식의 전달을 우려해서 원문과 함께 번역문을 기술합니다. ! NOTICE ! This article is excerpted from http://en.wikipedia.org/wiki/Revision_control Baseline (베이스 라인) An approved revision of a document or source file from which subsequent changes can be made. 계속 변경된 문서 또는 소스파일의 승인된 리비전 Branch (가지,분기,브랜치) A set of files under version control may be branched or forked at a point in time so that, from that time forward, two copies of those files may be developed at different speeds or in different ways independently of the other. 시간상 어떤 시점에서 버전 관리하의 한 셋트의 파일들은 분기될 수 있다. 그래서 그 분기 시점 이후 이러한 파일들의 두 복사본들은 서로 독립적으로 다른 속도로 또는 다른 방법으로 개발될 수 있다. Check-out (체크아웃) A check-out (or checkout or co) creates a local working copy from the repository. Either a specific revision is specified, or the latest is obtained. 체크 아웃은 저장소로 부터 로컬 작업본을 생성한다. 특정 리비전이 명시되거나 최근 리비전이 획득된다. Commit (커밋, 체크인) A commit (check-in, ci or, more rarely, install or submit) occurs when a copy of the changes made to the working copy is written or merged into the repository. 작업본에 변경이 있거나 저장소로 병합되었을 때 커밋이 일어난다(발생한다). Conflict (충돌) A conflict occurs when two changes are made by different parties to the same document, and the system is unable to reconcile the changes. A user must resolve the conflict by combining the changes, or by selecting one change in favour of the other. 충돌은 동일한 문서에 다른 부서에 의해 두개의 변경있을 때 그리고 시스템이 변경사항들을 조정, 일치시킬수 없을 때 발생한다. 사용자는 변경사항들을 결합하거나 하나의 변경을 선택함으로써 충돌을 해결해야만 한다. Change (변경) A change (or diff, or delta) represents a specific modification to a document under version control. The granularity of the modification considered a change varies between version control systems. 변경은 버전 관리하에 문서상에 특정 수정을 말한다. Change list (변경 리스트) On many version control systems with atomic multi-change commits, a changelist, change set, or patch identifies the set of changes made in a single commit. This can also represent a sequential view of the source code, allowing source to be examined as of any particular changelist ID. 극소 다중 변경 커밋을 지원하는(?) 많은 버전 관리 시스템에서, 변경 리스트, 변경 셋, 패치는 단일 커밋에 있어서의 변경사항들의 한 셋트이다. 이는 소스코드의 순차적인 관점을 나타내며, 어느 특정한 변경 리스트 ID로서 소스가 검사되도록 한다. Dynamic stream (동적 스트림) A stream (a data structure that implements a configuration of the elements in a particular repository) whose configuration changes over time, with new versions promoted from child workspaces and/or from other dynamic streams. It also inherits versions from its parent stream. 스트림(특정 저장소에서 요소들의 구성을 수행하는 데이타 구조)의 구성은 자식 워크스페이스 또는 다른 동적 스트림에 의해 시간에 따라 변경된다. 이는 부모 스트림으로 부터 버전을 상속한다(물려받는다). Export (엑스포트) An export is similar to a check-out except that it creates a clean directory tree without the version control metadata used in a working copy. Often used prior to publishing the contents. 엑스포트는 작업본에서 사용되는 버전 관리 메타데이타 없이 순수한 디렉토리 트리를 생성한다. 종종 항목들을 공표하기에 앞서 사용된다. Head (헤드, 최신 리비전) The most recent commit. 가장 최근의 커밋 Import (임포트) An import is the action of copying a local directory tree (that is not currently a working copy) into the repository for the first time. 임포트는 처음에 (현재의 작업본이 아닌) 로컬 디렉토리 트리를 저장소로 복사하는 작업이다. Mainline (메인 라인) Similar to Trunk, but there can be a Mainline for each branch. Trunk와 유사하지만 각 브랜치에 대한 메인라인이 있다. Merge (병합) A merge or integration brings together two sets of changes to a file or set of files into a unified revision of that file or files. 병합 또는 통합은 파일 또는 파일 세트상의 두 변경 세트를 파일 또는 파일들의 단일 리비전으로 만든다. This may happen when one user, working on those files, updates their working copy with changes made, and checked into the repository, by other users. Conversely, this same process may happen in the repository when a user tries to check-in their changes. It may happen after a set of files has been branched, then a problem that existed before the branching is fixed in one branch and this fix needs merging into the other. It may happen after files have been branched, developed independently for a while and then are required to be merged back into a single unified trunk. Repository (저장소) The repository is where the current and historical file data is stored, often on a server. 저장소는 현재 그리고 옛 파일 데이타가 서버상에 저장되는 곳이다. Reverse integration (역 통합) The process of merging different team branches into the main trunk of the versioning system. 다른 팀 분기를 버전 시스템의 주요 본체(trunk)로 병합하는 프로세스이다. Revision (리비전, 버전) A revision or version is one version in a chain of changes. 리비전 또는 버전은 일련의 변경사항들에 있어서 한 버전이다. Tag (태그) A tag or release refers to an important snapshot in time, consistent across many files. These files at that point may all be tagged with a user-friendly, meaningful name or revision number. 태그 또는 릴리즈는 일관된 많은 파일들과 시간상의 중요한 스냅샷을 참조로 한다. 이 시점에서의 파일들은 친숙한, 의미있는 이름 또는 리비전 넘버로 태그될 수 있다. Trunk (트렁크, 본체) The unique line of development that is not a branch (sometimes also called Baseline or Mainline) 분기가 아닌 개발의 유일한 라인이다. (때때로 베이스 라인 또는 메인 라인으로 불려진다.) Resolve (해결) The act of user intervention to address a conflict between different changes to the same document. 동일 문서상에 다른 변경사항들 사이의 충돌을 설명하기 위한 사용자 중재 활동이다. Update (갱신) An update (or sync) merges changes that have been made in the repository (e.g. by other people) into the local working copy. 갱신은 (다른 사람에 의해) 저장소에서의 변경 사항을 로컬 작업본에 병합한다. Working copy The working copy is the local copy of files from a repository, at a specific time or revision. All work done to the files in a repository is initially done on a working copy, hence the name. 작업본은 저장소로 부터 특정 시간 또는 리비전의 파일 로컬 복사이다. 저장소에서 파일들에 모든 작업의 완료는 초기에 작업본에 완료된다. Version Control System에서 알아두어야 할 용어
by Anna 안나 2008. 7. 6. 17:27
버그 관리의 중요성 팀 단위로 개발을 할 경우 서로가 서로의 버그를 발견하는 경우가 있는데, 많은 경우 구두로 이러한 과정이 이루어진다. 간단하면서도 심각한 버그라면 발견시 바로 해결하기도 하지만, 많은 버그들이 뒤로 미루거나 다른일과 겹치거나 하면서 잊혀져 버진다. 그 결과 버그가 계속적으로 누적되고, 발생했던 버그가 또 다시 발생하고 이 버그가 해결했던 버그인지 아닌지도 헷갈리고 해결해야 되는건지 아닌건지도 헷갈리는 최악의 상황에 도달하게 된다. 때에 따라서는 소모적인 책임공방도 벌어지게 된다. 메일로 발견된 버그를 보고하면 그나마 좀 낳긴 하지만 임시방편일 뿐이라는 건 사용해본 사람은 안다. 결국 프로젝트는 이런저런 자잘한 버그들 때문에 문제가 계속되고 어찌어찌해서 급하게 출시를 하더라도 완성도가 떨어지는 제품을 고객에게 내놓게 된다. 버그 관리 버그를 관리하고 감시하기 위해서는 버그에 대해서 심각도와 우선순위등을 할당하게 된다. 그래서 아래의 용어에 대해서 명확히 이해를 하고 있어야 버그관리가 제대로 이루어질 수 있을 것이다. 버그 심각도 (Severity) Blocker :개발 혹은 테스트 작업을 진행할 수 없게 만듦 버그 처리 결과 버그에 어떤 일이 발생했는지 나타낸다. FIXED (해결됨) : 테스트가 완료되었으며 버그 트리에 해결되었다고 표시된다. INVALID (버그아님) : 기술된 문제는 버그가 아니었음. WONTFIX (해결불가) : 기술된 문제는 해결될 수 없는 버그이다. LATER (나중에) : 기술된 문제는 본 제품에서는 수정될 수 없다. 차후 제품에 수정이 가능할 수 있다. REMIND (기억할 것) : 기술된 문제는 본 제품의 현재 버전에서는 수정할 수 없는 버그이지만, 앞으로 계속 영향을 끼칠 것으로 예상된다. DUPLICATE (중복됨) : 기술된 문제는 기존의 버그와 중복되었다.(혹은 유사 버그가 이미 존재함) 버그를 중복되었다고 표시하기 위해서는 기존의 버그 번호를 필요로 한다. WORKSFORME (파악할 수 없음) : 버그를 재현해 보려고 많은 노력을 기울였지만 실패했으며, 생성된 코드를 분석해봐도 왜 보고된 문제가 발생했는지를 파악할 수 없는 상태이다. 이 문제를 해결하기 위해서는 추가적인 정보가 필요하고, 그럴경우 이 버그는 다시 할당될 수 있다. 혹은 문제를 파악할 수 있을 만한 다른 개발자에게 할당할 수 있을 것이다. [출처] Bug Tracking System에서 알아두어야 할 용어|작성자 지연아빠 Critical : 프로그램이 깨지거나, 데이터 손상 및 메무리 누수가 발생함 Major : 기능상 중요한 결정이 발견됨 Normal : 일반적인 문제로 반드시 고쳐야할 버그 Minor : 기능상 그리 중요하지 않은 결점 혹은 쉽게 해결할 수 있는 문제 Trivial : 오탈자, 텟스트 정렬 문제와 같은 외형적인 문제 Enhancement : 기능 및 성능 개선 관련 사항 버그 처리 우선순위 (Priority) 즉시 (P1) : Prevents work from getting done, causes data loss, or BFI("Bad First Impression") 가장 먼저 처리해야함 긴급 (P2) : Workaround required to get stuff done. 보통 (P3) : Like P2, but rarely encountered in normal usage. 낮음 (P4) : Developer concern only, API stability ro cleanliness issue. 없음 (P5) : Nice to fix, but in a pinch we could live with it. 가장 나중에 처리해도 됨 버그 상태 정보 UNCONFIRMED (승인되지 않음) : 최근에 테이터베이스에 등록된 버그로, 아무도 이 버그에 대해서 확인을 해보지 않았다. 즉 아직 버그인지 아닌지도 검증이 되지 않은 상태다. 등록된 버그를 Confirm할 수 있는 사용자는 이 버그를 승인할 수 있으며, NEW(새로운 버그)로 하거나 해결될 경우 RESOLVED등의 상태로 변경이 가능하다. NEW (신규 버그) : 이 버그는 최근에 버그를 할당받은 담당자에 의해서 버그로 인정되었으며, 이에 대한 처리가 이루어져야 한다. 이 상태의 버그는 수락(accept)될 수 있으며, 필요한 경우 다른 사람에게 전달될 수(ASSIGNED)있다. 만약 다음 단계로 더 이상 진행이 안된다면 계속 NEW상태로 남아 있을 것이고, 문제가 해결된다면 RESOLVED 상태로 전이될 수 있을 것이다. ASSIGNED ( 할당됨 ) : 이 버그는 아직 해결되지 않았지만, 버그를 처리할수 있는 적합한 사람에게 할당되어져 있음을 의미한다. REOPENED (다시 오픈됨) : 이전에 해결(CLOSED)되었던 버그라고 하더라도 다시 재현될 수 있고, 혹은 담당자가 봤을 때 버그처리가 명확하게 되어있지 않음을 인지할 수도 있을 것이다. 이 경우 REOPENED 상태가 될수 있을 것이다. 이 상태의 버그들은 ASSIGNED 혹은 RESOLVED 상태로 전이될 수 있다. RESOLVED (처리됨) : 처리가 되었으며, 품질보증(QA) 담당자의 검증을 기다린다. 이 상태의 버그들은 REOPENED, VERIFIED 상태로 혹은 CLOSED 상태로 전이될 수 있다. VERIFIED (검증됨) : QA 담당자가 버그와 처리결과를 보고 적절하게 처리가 완료되었는지를 검증하게 된다. CLOSED (닫힘) : 버그가 완전히 사라졌다고 간주되는 상태다. 그러나 불행히도 다시 살아나는 경우가 발생할 수 있는데, 이때는 REOPENED 상태가 되어야 한다. http://blog.naver.com/eddykim72?Redirect=Log&logNo=40040460022
by Anna 안나 2008. 7. 6. 17:25
http://www.ajaxlessons.com/2006/02/19/ajax-workshop-3-shopping-cart-using-scriptaculous/ This workshop we will be building a shopping cart that’s Ajax powered. This will be a drag and drop shopping cart using the Script.aculo.us JavaScript library. We will also be using PHP on the back end to store the user’s shopping cart in sessions. We will start this workshop off with the XHTML and CSS for the shopping cart and its products. Let’s take a look at the XHTML. <div id="products">
<div style="float:left; font-weight:bold">Products</div>
<div id="clearCart" onclick="clearCart();">Clear Cart</div>
<div id="loading">Loading...</div>
<br style="clear:both" />
<div class="box" id="product_1">1</div>
<div class="box" id="product_2">2</div>
<div class="box" id="product_3">3</div>
<div class="box" id="product_4">4</div>
<div class="box" id="product_5">5</div>
<div class="box" id="product_6">6</div>
</div>
<div id="cart">
<div style="float:left; font-weight:bold">Shopping Cart</div>
</div>
We start out with a product div; this div will hold all of our products. Inside the product div we have a new div for the product box label “Products”, a div to hold the clear cart button (which we attached an event to that will be discusses further in the workshop) and the loading status image. After we have a break tag with a clear:both this is needed because we floated the previous divs. We then have the products with their class “box” and a unique id. The ID is what we will be passing to the server side to add the product to our shopping cart. We then have the cart div. This is the actual shopping cart when you drag your products into to be saved server side. Let’s take a look at the CSS now. #cart {
background-color:#FFFF66;
border:dashed gray 1px;
height:100px;
width:500px;
padding:5px;
margin-top:10px;
overflow: auto;
}
#products {
background-color:#FFF;
border:dashed gray 1px;
height:100px;
width:500px;
padding:5px;
}
.box {
background-color:#CCFF33;
border:solid gray 1px;
margin:10px;
padding:4px;
width:50px;
height:50px;
float:left;
cursor:pointer;
}
#loading {
display:none;
float:right;
}
#clearCart {
text-decoration:underline;
cursor:pointer;
float:right
}
#clearCart:hover {
background-color:#CCFFCC;
color:#000099;
}
Nothing spectacular going on here for the most part, we are giving all of our divs some style, setting the height and width colors etc. Some things to point out are in the box class we are floating left so that the divs are horizontally aligned also we are setting display of the loading ID to none so that it’s not shown unless we are communicating with the server. You should now have something that looks like this: We now need to add the code for Script.aculo.us to create the drag and drop functionality. Add this code right before the closing of your body tag. <script type="text/javascript">
var products = document.getElementsByClassName('box');
for (var i = 0; i < products.length; i++) {
new Draggable(products[i].id, {ghosting:true, revert:true})
}
Droppables.add('cart', {onDrop:addProduct})
</script>
The first thing we are doing is getting each product as an object so we can add the drag ability from Script.aculo.us. We do this by getting all elements with the class name of “box” in an array then looping through and creating a new Draggable with the ID of the element. There are also two parameters: ghosting:true and revert:true. Ghosting makes the element transparent while dragging and revert sends the element back to it’s starting position on mouse out. We then make the cart div a droppable. What this does is allow us to catch the ID of which element was dropped and call a function. We set the onDrop parameter to onDrop:addProduct which means that when a draggable (product) is dropped on the droppable (cart) we call the function addProduct. Let’s take a look at the JavaScript now that contains all of our functions. function addProduct(element, dropon, event) {
sendData(element.id);
}
function sendData (prod) {
var url = 'cart.php';
var rand = Math.random(9999);
var pars = 'product_id=' + prod + '&rand=' + rand;
var myAjax = new Ajax.Request( url, {method: 'get', parameters: pars, onLoading: showLoad, onComplete: showResponse} );
}
function clearCart () {
var url = 'cart.php';
var rand = Math.random(9999);
var pars = 'clear=true&rand=' + rand;
var myAjax = new Ajax.Request( url, {method: 'get', parameters: pars, onLoading: showLoad, onComplete: showResponse} );
}
function clearProduct (id) {
var url = 'cart.php';
var rand = Math.random(9999);
var pars = 'clearProduct=true&id=' + id + '&rand=' + rand;
var myAjax = new Ajax.Request( url, {method: 'get', parameters: pars, onLoading: showLoad, onComplete: showResponse} );
}
function showResponse (originalRequest) {
$('loading').style.display = "none";
$('clearCart').style.display = "block";
$('cart').innerHTML = originalRequest.responseText;
}
function showLoad () {
$('clearCart').style.display = "none";
$('loading').style.display = "block";
}
The first function we created addProduct is what is called when a product is added to the shopping cart. We then call another function sendData and pass the ID of the product that was dropped in the cart. The next function sendData we send the product ID to the server to store in a SESSION. For more information using this function visit the previous workshops where we went into greater detail. The clearCart and clearProduct functions are almost identical to the sendData function, but instead we are clearing the cart and clearing the product respectively in the server side SESSION. The showResponse function is updating the shopping cart with products in the server side SESSION as well as hiding the loading div and showing the clear cart button. The showLoad function hides the clear cart button and shows the loading status div. Again we have covered all of these functions earlier in previous workshops. Here is the complete XHTML, CSS and JavaScript <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Ajax Workshop 3: Shopping Cart using Script.aculo.us</title>
<script src="prototype.js" type="text/javascript"></script>
<script src="scriptaculous.js" type="text/javascript"></script>
<style media="screen" type="text/css">
body {
font-family:"Trebuchet MS";
font-size:12px;
}
#cart {
background-color:#FFFF66;
border:dashed gray 1px;
height:100px;
width:500px;
padding:5px;
margin-top:10px;
overflow: auto;
}
#products {
background-color:#FFF;
border:dashed gray 1px;
height:100px;
width:500px;
padding:5px;
}
.box {
background-color:#CCFF33;
border:solid gray 1px;
margin:10px;
padding:4px;
width:50px;
height:50px;
float:left;
cursor:pointer;
}
#loading {
display:none;
float:right;
}
#clearCart {
color:blue;
text-decoration:underline;
cursor:pointer;
float:right
}
#clearCart:hover {
background-color:#CCFFCC;
color:#000099;
}
</style>
<script language="javascript" type="text/javascript">
function addProduct(element, dropon, event) {
sendData(element.id);
}
function sendData (prod) {
var url = 'cart.php';
var rand = Math.random(9999);
var pars = 'product_id=' + prod + '&rand=' + rand;
var myAjax = new Ajax.Request( url, {method: 'get', parameters: pars, onLoading: showLoad, onComplete: showResponse} );
}
function clearCart () {
var url = 'cart.php';
var rand = Math.random(9999);
var pars = 'clear=true&rand=' + rand;
var myAjax = new Ajax.Request( url, {method: 'get', parameters: pars, onLoading: showLoad, onComplete: showResponse} );
}
function clearProduct (id) {
var url = 'cart.php';
var rand = Math.random(9999);
var pars = 'clearProduct=true&id=' + id + '&rand=' + rand;
var myAjax = new Ajax.Request( url, {method: 'get', parameters: pars, onLoading: showLoad, onComplete: showResponse} );
}
function showResponse (originalRequest) {
$('loading').style.display = "none";
$('clearCart').style.display = "block";
$('cart').innerHTML = originalRequest.responseText;
}
function showLoad () {
$('clearCart').style.display = "none";
$('loading').style.display = "block";
}
</script>
</head>

<body>
<h1>Ajax Workshop 3: Shopping Cart using <a href="http://script.aculo.us">Script.aculo.us</a> </h1>
<h2>Drag the products into the shopping cart</h2>
<div id="products">
<div style="float:left; font-weight:bold">Products</div>
<div id="clearCart" onclick="clearCart();">Clear Cart</div>
<div id="loading"><img src="indicator.gif" alt="loading..." /></div>
<br style="clear:both" />
<div class="box" id="product_1">1</div>
<div class="box" id="product_2">2</div>
<div class="box" id="product_3">3</div>
<div class="box" id="product_4">4</div>
<div class="box" id="product_5">5</div>
<div class="box" id="product_6">6</div>
</div>
<div id="cart">
<div style="float:left; font-weight:bold">Shopping Cart</div>
</div>
<script type="text/javascript">
var products = document.getElementsByClassName('box');
for (var i = 0; i < products.length; i++) {
new Draggable(products[i].id, {ghosting:true, revert:true})
}
Droppables.add('cart', {onDrop:addProduct})
</script>
</body>
</html>
Time for the server side Cart.php. <?php
session_start();
function stringForJavascript($in_string) {
$str = ereg_replace("[\r\n]", " \\n\\\n", $in_string);
$str = ereg_replace('"', '\\"', $str);
Return $str;
}
if (isset($_GET['clearProduct'])) {
$_SESSION['cart'][$_GET['id']]--;
if ($_SESSION['cart'][$_GET['id']] == 0) {
unset($_SESSION['cart'][$_GET['id']]);
}
foreach ($_SESSION['cart'] as $key => $value) {
print "$key = $value <span style=\"color:blue; text-decoration:underline; cursor:pointer\" onclick=\"clearProduct('$key');\">DELETE</span><br />";
}
sleep(1);
die;
}
if (isset($_GET['clear'])) {
unset($_SESSION['cart']);
sleep(1);
die;
}
$prodid = $_GET['product_id'];
$_SESSION['cart'][$prodid] = 1 + $_SESSION['cart'][$prodid];
foreach ($_SESSION['cart'] as $key => $value) {
print "$key = $value <span style=\"color:blue; text-decoration:underline; cursor:pointer\" onclick=\"clearProduct('$key');\">DELETE</span><br />";
}
sleep(1);
?>
We start off by calling the session_start() function which allow us to access and modify the SESSION global variable. We then have created a function for returning a string that JavaScript can read. Next we are using a conditional statement to check whether we are adding a product, clearing the cart or removing a product. Depending on what action is called we manipulate the SESSION global variable and send back the contents of the shopping cart. I am not going to cover all the basics of the SESSION global variable but if you like you can read up on it at http://php.net/session. Here is what it looks like when complete
by Anna 안나 2008. 7. 6. 17:13
웹에서 리스트를 만들면 때로는 제목이나 기타등등이 길어질 때가 있다
이때 많은 사람들이 substring 을 해서 점을 붙이는 형태로 작업을 하는데, 영어나 한글 등의 언어에 따라 길이가 제각각이 되므로 보기에 상당히 안 좋다.

제목이 긴경우 ... 으로 해준다
<div style="position:relative; width:100px; text-overflow:ellipsis; overflow:hidden; cursor:hand"><nobr>텍스트 어쩌구저쩌구 이러쿵 저러쿵...</nobr><div>


<table width="100%" border="1" class="v1" style='table-layout:fixed'>
<tr>
<td width="50"><img src="<?=$strImage?>" width="50" height="50"></td>
<td width="100%">
<div style='width:100%;height:16px;overflow:hidden;text-overflow:ellipsis'><nobr><b><?=$strTitle?></b></nobr></div>
</td>
</tr>
</table>
by Anna 안나 2008. 7. 6. 01:38
Browser-based file uploads, in particular those involving the HTML <input type="file"> tag, have always been rather lacking. As I am sure most of you are aware, uploading files exceeding 10MB often causes a very poor user experience. Once a user submits the file, the browser will appear to be inactive while it attempts to upload the file to the server. While this happening in the background, many impatient users would start to assume that the server is "hanging" and would try to submit the file again. This of course, only helps to make matters worse. In an attempt to make uploading of files more user-friendly, many sites display an indeterminate progress animation (such as a rotating icon) once the user submits the file. Although this technique may be useful in keeping the user distracted while the upload being submitted to the server, it offers very little information on the status of the file upload. Another attempt at solving the problem is to implement an applet that uploads the file to the server through FTP. The drawback with this solution is that it limits your audience to those that have a Java-enabled browser. In this article, we will take fresh approach and implement an AJAX-powered component that will not only upload the file to server, but also monitor the actual progress of a file upload request in "real time." The four stages of this component are illustrated below, in Figures 1, 2, 3, and 4: Figure 1. Stage 1: Selecting the file upload Figure 2. Stage 2: Uploading the file to the server Figure 3. Stage 3: Uploaded completed Figure 4. File upload summary Implementing the Component We will first walk through the process of creating the multipart filter that will allow us to handle and monitor the file upload. We will then move on to implementation of the JavaServer Faces component that will provide the user with continuous feedback in the form of an AJAX-enabled progress bar. The Multipart Filter: UploadMultipartFilter The responsibility of the multipart filter is to intercept the incoming file upload and write the file to a temporary directory on the server. At the same time, it will also monitor the amount of bytes received and determine how much of the file has been uploaded. Fortunately, there is an excellent Jakarta-Commons open source library available (FileUpload) that takes care of parsing an HTTP multipart request and writing a file upload to disk. We will be extend this library and add in the required "hooks" we need to monitor how many bytes have been processed. public class UploadMultipartFilter implements Filter{ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest hRequest = (HttpServletRequest)request; //Check whether we're dealing with a multipart request String contentHeader = hRequest.getHeader("content-type"); boolean isMultipart = ( contentHeader != null && contentHeader.indexOf("multipart/form-data") != -1); if(isMultipart == false){ chain.doFilter(request,response); }else{ UploadMultipartRequestWrapper wrapper = new UploadMultipartRequestWrapper(hRequest); chain.doFilter(wrapper,response); } ... } As you can see, the UploadMultipartFilter class simply checks to see whether the current request is a multipart request. If the request does not contain a file upload, the request is passed on to the next filter in the chain without any additional processing. Otherwise, the request is wrapped in an UploadMultipartRequestWrapper. The UploadMultipartRequestWrapper Class public class UploadMultipartRequestWrapper extends HttpServletRequestWrapper{ private Map<String,String> formParameters; private Map<String,FileItem> fileParameters; public UploadMultipartRequestWrapper(HttpServletRequest request) { super(request); try{ ServletFileUpload upload = new ServletFileUpload(); upload.setFileItemFactory(new ProgressMonitorFileItemFactory(request)); List fileItems = upload.parseRequest(request); formParameters = new HashMap<String,String>(); fileParameters = new HashMap<String,FileItem>(); for(int i=0;i<fileItems.size();i++){ FileItem item = (FileItem)fileItems.get(i); if(item.isFormField() == true){ formParameters.put(item.getFieldName(),item.getString()); }else{ fileParameters.put(item.getFieldName(),item); request.setAttribute(item.getFieldName(),item); } } }catch(FileUploadException fe){ //Request Timed out - user might have gone to other page. //Do some logging //... } ... In our UploadMultipartRequestWrapper class, we initialize the commons ServletFileUpload class that will parse our request and write the file to the default temporary directory on the server. The ServletFileUpload instance creates a FileItem instance for each field that is encountered in the request. These include both file uploads and normal form elements. A FileItem instance can then be used to retrieve the properties of a submitted field, or, in the case of a file upload, an InputStream to the underlying temporary file to which it has been saved. In summary, the UploadMultipartRequestWrapper basically parses the file and sets any FileItems that represent file uploads as attributes in the request. These can then be picked up by JSF components further down the line. The behavior of normal form fields remains the same. By default, the Commons FileUpload library use instances of the DiskFileItems class to handle file uploads. Although DiskFileItems are very useful in handling the whole temporary-file business, there is very little support for monitoring exactly how much of the file has been processed. Since version 1.1, the Commons FileUpload library provides developers with the ability to specify the factory that will be used to create the FileItem. We will use the ProgressMonitorFileItemFactory and ProgressMonitorFileItem classes to override the default behavior and monitor the progress file uploads. The ProgressMonitorFileItemFactory Class public class ProgressMonitorFileItemFactory extends DiskFileItemFactory { private File temporaryDirectory; private HttpServletRequest requestRef; private long requestLength; public ProgressMonitorFileItemFactory(HttpServletRequest request) { super(); temporaryDirectory = (File)request.getSession().getServletContext(). getAttribute("javax.servlet.context.tempdir"); requestRef = request; String contentLength = request.getHeader("content-length"); if(contentLength != null){ requestLength = Long.parseLong(contentLength.trim()); } } public FileItem createItem(String fieldName, String contentType, boolean isFormField, String fileName) { SessionUpdatingProgressObserver observer = null; if(isFormField == false) //This must be a file upload. observer = new SessionUpdatingProgressObserver(fieldName, fileName); ProgressMonitorFileItem item = new ProgressMonitorFileItem( fieldName, contentType, isFormField, fileName, 2048, temporaryDirectory, observer, requestLength); return item; } ... public class SessionUpdatingProgressObserver implements ProgressObserver { private String fieldName; private String fileName; ... public void setProgress(double progress) { if(request != null){ request.getSession().setAttribute("FileUpload.Progress." + fieldName, progress); request.getSession().setAttribute("FileUpload.FileName." + fieldName, fileName); } } } } The ProgressMonitorFileItemFactory uses the Content-Length header set by the browser and assumes it to be the accurate length of the upload file being sent. This method of determining the file length does limit you to uploading only one file per request--it's inaccurate if more than more file is encoded in the request. This is due to the fact that browsers only send one Content-Length header, regardless of the number of files in the upload. In addition to creating ProgressMonitorFileItem instances, the ProgressMonitorFileItemFactory also registers a ProgressObserver instance that will be used by the ProgressMonitorFileItem to send updates on the progress of the file upload. The implementation of the ProgressObserver used, SessionUpdatingProgressObserver, sets the progress percentage into the user's session under the id of the submitted field. This value can then be accessed by the JSF component in order to send updates to the user. The ProgressMonitorFileItem Class public class ProgressMonitorFileItem extends DiskFileItem { private ProgressObserver observer; private long passedInFileSize; ... private boolean isFormField; ... @Override public OutputStream getOutputStream() throws IOException { OutputStream baseOutputStream = super.getOutputStream(); if(isFormField == false){ return new BytesCountingOutputStream(baseOutputStream); }else{ return baseOutputStream; } } ... private class BytesCountingOutputStream extends OutputStream{ private long previousProgressUpdate; private OutputStream base; public BytesCountingOutputStream(OutputStream ous){ base = ous; } ... private void fireProgressEvent(int b){ bytesRead += b; ... double progress = (((double)(bytesRead)) / passedInFileSize); progress *= 100.0 observer.setProgress(); } } } The ProgressMonitorFileItem wraps the default OutputStream of the DiskFileItem in a BytesCountingOutputStream, which updates the associated ProgressObserver every time a certain number of bytes have been read. The AJAX-Enabled JavaServer Faces Upload Component This component is responsible for rendering the HTML file upload tag, displaying a progress bar to monitor the file upload, and rendering the components that need to be displayed once a file has been successfully uploaded. One of the main advantages to implementing this component using JavaServer Faces is the fact that most of the complexities are hidden from the page author. The page author only needs to add the component tag to the JSP and the component will take care of all of the AJAX and progress-monitoring details. Below is the JSP code snippet that is used to add the upload component to the page. <comp:fileUpload value="#{uploadPageBean.uploadedFile}" uploadIcon="images/upload.png" styleClass="progressBarDiv" progressBarStyleClass="progressBar" cellStyleClass="progressBarCell" activeStyleClass="progressBarActiveCell"> <%--Below are the components that will be visible once the file upload completes--%> <h:panelGrid columns="2" cellpadding="2" cellspacing="0" width="100%"> <f:facet name="header"> <h:outputText styleClass="text" value="File Upload Successful." /> </f:facet> <h:panelGroup style="text-align:left;display:block;width:100%;"> <h:commandButton action="#{uploadPageBean.reset}" image="images/reset.png"/> </h:panelGroup> <h:panelGroup style="text-align:right;display:block;width:100%;"> <h:commandButton action="#{uploadPageBean.nextPage}" image="images/continue.png"/> </h:panelGroup> </h:panelGrid> </comp:fileUpload> The value attribute of the file upload component needs to be bound to a bean with a property that holds a FileItem. The child components are only displayed once the file has been successfully received by the server. Implementing the AJAX File Upload Component The progress bar of the component was inspired by the " Progress Bar Using JavaServer Faces Component with AJAX" solution as detailed in the Java BluePrints Solution Catalog. In essence, the upload component either renders a complete version of itself, or in the case of an AJAX request, only a bit of XML to update the state of the progress bar on the page. In order to prevent JavaServer Faces from rendering the complete component tree (which would incur unnecessary overhead), we also need to implement a PhaseListener (PagePhaseListener) to abort the rest of the faces' request processing if an AJAX request is encountered. I have omitted all of the standard configuration (faces-config.xml and tag libraries) from the article, as these are very straightforward and have been covered before. Everything is, however, included in the source code download for this article (in the Resources section) if you wish to review them. The AJAX File Upload Component Renderer The implementation of the component and tag classes is rather straightforward. The bulk of the logic is contained in the renderer, which has the following responsibilities: Encode the full file upload component (complete with the HTML file upload tag), components to be displayed once a file has been uploaded, and the client-side JavaScript code to implement for the AJAX requests. Handle partial AJAX requests appropriately and send back the necessary XML. Decode a file upload and set it as a FileItem instance on the underlying value binding. Encoding the Full Upload Component As mentioned previously, the file upload component is composed of three stages. During the full encoding of the component, we will encode all three stages. Their visibility (using the CSS display property) on the page will then be controlled by the AJAX JavaScript. Stage 1 Figure 5 shows stage 1 of the upload component. Figure 5. Stage 1: Selecting the file upload In Stage 1, we need to render the HTML file upload tag and the button that will be responsible for starting off this process. Once the user clicks the upload button, the form is submitted through an IFRAME ( to prevent blocking on the page) and the second stage of the process is initiated. Below is an extract of the rendering code : //The File upload component writer.startElement("input", component); writer.writeAttribute("type", "file", null); writer.writeAttribute("name", component.getClientId(context), "id"); writer.writeAttribute("id", component.getClientId(context),"id"); if(input.getValue() != null){ //Render the name of the file if available. FileItem fileData = (FileItem)input.getValue(); writer.writeAttribute("value", fileData.getName(), fileData.getName()); } writer.endElement("input"); String iconURL = input.getUploadIcon(); //Render the image, and attach the JavaScript event to it. writer.startElement("div", component); writer.writeAttribute("style","display:block;width:100%;text-align:center;", "style"); writer.startElement("img", component); writer.writeAttribute("src",iconURL,"src"); writer.writeAttribute("type","image","type"); writer.writeAttribute("style","cursor:hand;cursor:pointer;","style"); UIForm form = FacesUtils.getForm(context,component); if(form != null) { String getFormJS = "document.getElementById('" + form.getClientId(context) + "')"; String jsFriendlyClientID = input.getClientId(context).replace(":","_"); //Sets the encoding of the form to be multipart required for file uploads and //to submit its content through an IFRAME. The second stage of the component is //also initialized after 500 milliseconds. writer.writeAttribute("onclick",getFormJS + ".encoding='multipart/form-data';" + getFormJS + ".target='" + iframeName + "';" + getFormJS + ".submit();" + getFormJS + ".encoding='application/x-www-form-urlencoded';" + getFormJS + ".target='_self';" + "setTimeout('refreshProgress" + jsFriendlyClientID + "();',500);", null); } ... writer.endElement("img"); //Now do the IFRAME we are going to submit the file/form to. writer.startElement("iframe", component); writer.writeAttribute("id", iframeName, null); writer.writeAttribute("name",iframeName,null); writer.writeAttribute("style","display:none;",null); writer.endElement("iframe"); writer.endElement("div"); writer.endElement("div"); //End of Stage1 Stage 2 Stage 2 of the component is the progress bar and the label that indicates the current percentage, as seen in Figure 6. The progress bar is implemented as a div tag with 100 embedded span tags. These will be set by the AJAX JavaScript based on the response received from the server. Figure 6. Stage 2: Uploading the file to the server writer.startElement("div", component); writer.writeAttribute("id", input.getClientId(context) + "_stage2", "id"); ... writer.writeAttribute("style","display:none", "style"); String progressBarID = component.getClientId(context) + "_progressBar"; String progressBarLabelID = component.getClientId(context) + "_progressBarlabel"; writer.startElement("div", component); writer.writeAttribute("id",progressBarID,"id"); String progressBarStyleClass = input.getProgressBarStyleClass(); if(progressBarStyleClass != null) writer.writeAttribute("class",progressBarStyleClass,"class"); for(int i=0;i<100;i++){ writer.write("<span> </span>"); } writer.endElement("div"); writer.startElement("div", component); writer.writeAttribute("id",progressBarLabelID,"id"); ... writer.endElement("div"); writer.endElement("div"); //End of Stage2 Stage 3 Finally, the components that need to be displayed once a file has been successfully uploaded, seen in Figure 7, are rendered as part of Stage 3. These are done in the encodeChildren method of the renderer. Figure 7. Stage 3: Uploaded completed public void encodeChildren(FacesContext context, UIComponent component) throws IOException { ResponseWriter writer = context.getResponseWriter(); UIFileUpload input = (UIFileUpload)component; //Do the children that will be shown once the //file has been successfully uploaded writer.startElement("div", component); writer.writeAttribute("id", input.getClientId(context) + "_stage3", "id"); //Stage3. if(input.getValue() == null){ writer.writeAttribute("style","display:none;",null); }else{ writer.writeAttribute("style","display:block",null); } List<UIComponent> children = input.getChildren(); for(UIComponent child : children){ FacesUtils.encodeRecursive(context,child); } writer.endElement("div"); //End of Stage3 } Handling AJAX Requests The rendering of AJAX requests is handled in the decode method of this component, in accordance with recommendations in the Java BluePrints Solution Catalog. We need to check whether this is in fact an AJAX request (to differentiate from normal decoding behavior) and then send back an XML response to the client based on the values that have been set in the session by the SessionUpdatingProgressObserver instance in the ProgressMonitorFileItemFactory class. public void decode(FacesContext context, UIComponent component) { UIFileUpload input = (UIFileUpload) component; //Check whether this is a request for the //progress of the upload, or if it is an actual // upload request. ExternalContext extContext = context.getExternalContext(); Map parameterMap = extContext.getRequestParameterMap(); String clientId = input.getClientId(context); Map requestMap = extContext.getRequestParameterMap(); if(requestMap.get(clientId) == null){ //Nothing to do. return; } if(parameterMap.containsKey(PROGRESS_REQUEST_PARAM_NAME)){ //This is a request to get the progress on the file request. //Get the progress and render it as XML HttpServletResponse response = (HttpServletResponse) context.getExternalContext().getResponse(); // set the header information for the response response.setContentType("text/xml"); response.setHeader("Cache-Control", "no-cache"); try { ResponseWriter writer = FacesUtils.setupResponseWriter(context); writer.startElement("progress", input); writer.startElement("percentage", input); //Get the current progress percentage from //the session (as set by the filter). Double progressCount = (Double)extContext.getSessionMap(). get("FileUpload.Progress." + input.getClientId(context)); if(progressCount != null){ writer.writeText(progressCount, null); }else{ //We haven't received the upload yet. writer.writeText("1", null); } writer.endElement("percentage"); writer.startElement("clientId", input); writer.writeText(input.getClientId(context), null); writer.endElement("clientId"); writer.endElement("progress"); } catch(Exception e){ //Do some sort of error logging... } }else{ //Normal decoding request. ... Normal Decoding Behavior During normal decoding, the file upload renderer retrieves the FileItem from the request attributes, where it has been set by the filter, and updates the component's value binding. The progress in the session is then updated to 100 percent so that the JavaScript on the page can move the component into Stage 3. //Normal decoding request. if(requestMap.get(clientId).toString().equals("file")){ try{ HttpServletRequest request = (HttpServletRequest)extContext.getRequest(); FileItem fileData = (FileItem)request.getAttribute(clientId); if(fileData != null) input.setSubmittedValue(fileData); //Now we need to clear any progress associated with this item. extContext.getSessionMap().put("FileUpload.Progress." + input.getClientId(context), new Double(100)); }catch(Exception e){ throw new RuntimeException("Could not handle file upload" + " - please configure the filter.",e); } } The client-side JavaScript is responsible for making progress requests to the server and for moving the component through the different stages. To cut out the usual boilerplate code associated with handling all of the browser-specific quirks of the XMLHttpRequest object, I've opted for the excellent AjaxRequest.js library provided by Matt Krause. This library allows us to considerably reduce the amount of JavaScript code we need to write to get this component working. Although it is probably best practice to package the JavaScript code as part of the component and then to render it from a PhaseListener (as detailed here ), I've tried to keep it simple by defining a link to the JavaScript library on the JSP page. The getProgressBarJavaScript method in the component is called to render the JavaScript. Getting the JavaScript correct is usually the most painful part of implementing any AJAX component; hopefully, the code below is clear enough to be easily understood. While the JavaScript in my example is embedded within the Java code, it is probably better practice to externalize it into a separate file. For the purposes of this article I wanted to keep it simple and to the point. Below is an example of the JavaScript that would be rendered by the component. It is assumed that fileUpload1 is the client-side JSF Id assigned to the file component, while uploadForm is the Id of the HTML form. function refreshProgress(){ // Assume we are entering stage 2. document.getElementById('fileUpload1_stage1').style.display = 'none'; document.getElementById('fileUpload1_stage2').style.display = ''; document.getElementById('fileUpload1_stage3').style.display = 'none'; // Create the AJAX post AjaxRequest.post( { //Specify the correct parameters so that //the component is correctly handled on //the server side. 'parameters':{ 'uploadForm':'uploadForm', 'fileUpload1':'fileUpload1', 'jsf.component.UIFileUpload':'1', 'ajax.abortPhase':'4' } //Abort at Phase 4. // Specify the callback method for successful processing. ,'onSuccess':function(req) { var xml = req.responseXML; if( xml.getElementsByTagName('clientId').length == 0) { setTimeout('refreshProgress()',200); return; } var clientId = xml.getElementsByTagName('clientId'); clientId = clientId[0].firstChild.nodeValue + '_progressBar'; //Get the percentage from the XML var percentage = xml.getElementsByTagName('percentage')[0].firstChild.nodeValue; var innerSpans = document.getElementById(clientId).getElementsByTagName('span'); document.getElementById(clientId + 'label').innerHTML = Math.round(percentage) + '%'; // Set the style classes of the spans based on the current progress. for(var i=0;i<innerSpans.length;i++){ if(i < percentage){ innerSpans[i].className = 'active'; }else{ innerSpans[i].className = 'passive'; } } // If the percentage is not 100, we need to carry // on polling the server for updates. if(percentage != 100){ setTimeout('refreshProgress()',400); } else { // The file upload is done - we now //need to move the component into stage 3. document.getElementById('fileUpload1_stage1').style.display = 'none'; document.getElementById('fileUpload1_stage2').style.display = 'none'; document.getElementById('fileUpload1_stage3').style.display = ''; } } }); } return builder.toString(); Conclusion Hopefully, this article offered you some insight on how to make file uploads more user-friendly, and on the possibilities of combining AJAX and JavaServer Faces for advanced user interface components. The solution in this article is by no means exhaustive and could probably be further improved. I would encourage you to look at the complete source code to gain a deeper understanding of the concepts discussed here. Resources Source code for this article Commons FileUpload library: The library we used process the file uploads Java BluePrints Solution Catalog: " Using JavaServer Faces Technology with AJAX" Ajax Toolbox for more information on the AjaxRequest API used in this article Jacobus Steenkamp has been developing in Java for the last 4.5 years and has worked in various sectors including the financial, pharmaceutical and energy industries. http://today.java.net/pub/a/today/2006/02/09/file-uploads-with-ajax-and-jsf.html
by Anna 안나 2008. 7. 6. 01:38
This tutorial is not to replace great photography, but to instead help those of you, like me, who are hobbyists and don't have the expensive equipment to get the photos we want to get. You will need at least Adobe Photoshop CS2. This tutorial is based on a landscape photo, but you can, with little imagination, apply it to any type of photograph. We will start with this base photo, a dull and plain shot of a river shore and some trees. Before we get started, this is the end result: First step is to crop. We want a little more impact and currently this photo has some extra elements and dead space that we can easily get rid of. This allows the viewer to concentrate more closely to the interesting pieces of the composition. Next step is most important when dealing with RAW formats, or if the photo was slightly out of focus. That's right, sharpening. I like to use a simple technique using a high pass filter. I do have other, more complicated and more accurate methods, but that is another tutorial altogether. First step is to duplicate the original layer. Then go to "filter > other > high pass". You may need to use a different setting than I did, depending on the photograph, but try to match the detail to the screen shot. Next, set the high pass layer to overlay like in the screen shot. You should notice the image get much sharper and likely too sharp. You may want to adjust the layers opacity. Here is a screen shot of my progress so far. Next we will create a new adjustment layer, to do this click the highlighted button in the screen shot. Select "Curves", when the dialogue comes up adjust it similar to what is shown in the screen shot. I do this to add impact through contrast, shadows and highlights. Now, once finished add another new adjustment layer like before only this time select "Hue/Saturation". The attempt here is to bring out the colors, now depending on the photograph, this can change dramatically, or even be skipped if there was no trouble with the colors to begin with. You can see what I chose to do in the screen shot. We have one more adjustment layer to add, this time select "Levels". The idea here is to again create a bigger impact by creating a little more contrast in tones. You may want to very your adjustments once again depending on the photograph you have selected, but you can see what I did by looking at the screen shot. In your layer pallete, select the original layer again. Next step is to create the illusion of depth of field. Click the button highlighted in the screen shot to enter quick mask mode, it is located on the tools palette. Next select the gradiant tool in the tools pallet, be sure to select a fill-to-transparent gradient from the gradient dialogue as shown in the screen shot. Next, use the gradient to fill the viewable atmosphere and any objects not part of the photos interest points. The basic concept is to have the gradient match up with elements that are furthest away and fade away as elements get closer. Same would go for element closest, fading to the subject. Take a look at the screen shot to see how I chose to do it for this photo. When satisfied, click the Edit in Standard Mode button, located just left of the Quick Mask Mode button. A selection will now replace the mask. Now go to "Select > Inverse". Then go to "Filter > Blur > Lens Blur" and match the settings in the screen shot. http://www.nicora.net//index.cfm/method/article/blogid/724D0CB0-9EE5-AD0B-89D3-32F24228AD7C/d/1/Add_life_to_your_dull_landscape_photos
by Anna 안나 2008. 7. 1. 00:03
This is a simple tutorial on how to create Web2.0 logos. I use Adobe Photoshop CS2 for this tutorial. Enjoy!! Step 1:
Create a new Image document by going to File > New. From the options window select the dimensions 1000 x 1000 72 resolution and white background. Step 2: Select the type tool and chose your font. For a real Web2.0 feel it would be wise to select either Trebuchet MS or something like Arial or Helvetica Rounded. Next select your text color, use something bright, I'll be using #0042ff. Type your Web2.0 logo text and set the size to 160pt, then center the text. Step 3: Next we need a logo mark, I'll do something really simple, but as you'll soon see any simple mark will work. Select the Custom Shape Tool, then select the "flower 5" default shape. Using the shift key to constrain proportions, make a shape about the same size in relation to the image example I have. Select the Path Selection Tool, then right click in the shape and select "Make Selection". Set the feather radius to 0 and check anti-aliased. Next go to Select > Modify > Expand. In the options window, expand by 8 pixels. Create a new layer and select a fill color. Again, choose a nice bright color. I'll be using #ff00ae. Fill the select, you can use the shortcut key CTL+Backspace. Use CTL+D to de-select. Label this layer "Logo Mark". Step 4: Arrange the two objects so they look similar to what I have shown in the image provided. The next step is to add the plastic wrap. Select the text object from the layer palette and go to Select > Load Selection and click OK from the options window. Create a new layer and label it "Bottom Text Gradient". Then select the Gradient Tool. In the Gradient Tool options window, select white to transparent like in the image provided. Use the Gradient Tool to create a mask similar to the image provided, when content, de-select. Create a new layer and label it "Top Text Gradient". Select the Ellipse Marquee Tool, then create an ellipse over the text like in the example image. Once that is done, select the Gradient Tool and match your mask to the one in the example image. De-select the ellipse Next select the text object layer, then go to Select > Load Selection and click OK in the options window. Use the shortcut key CTR+SHT+I to inverse the selection. Select the layer "Bottom Text Gradient" and press the backspace key to delete the selection. Select the layer "Top Text Gradient" and do the same to delete the selection. De-select. Step 5: Repeat step 4 on the Logo Mark and try to get the same effect as shown in the example image. You may need to use a different ellipse shape depending on your Marks shape. Step 6: Select the text object layer, right click and select Blending options from the context menu. Select Drop Shadow and change the settings to match the example image. Step 7: Next select stroke and change the setting to match that of the example image. When finished, right click the text object layer and select Copy Layer Style. Select the Logo Mark layer, right click and select Paste Layer Style. You will need to go into this layer Blending Options and change the stroke color to match the Logo Mark. At this point you should have something loosely resembling the example image. Next we put on the finishing touches. Step 8: Move all your objects into place, because the next step is to combine everything we have save the background into one layer. To do this, select all layers except for the background then use the shortcut CTL+E to combine the layers. Next, create a copy of the layer. You can do this by dragging the layer to the New Layer icon at the bottom of the layer palette Select the original layer and go to Select > Load Selection. Create a new layer and label it "Shadow". Select the Shadow layer and fill the selection with black, you can do this by using the shortcut CTL+Backspace. Select the Move Tool, then use the shortcut CTL+T to bring up the transform tool. Holding down the shift key, skew the transform box to match the example image, hit enter when done. Adjust the opacity on the Shadow layer to 15%. Step 9: Select the copied layer, then the Move Tool and again use the shortcut CTL+T to bring up the transform tool. Right click then select Flip Vertical, hit enter. Holding down the shift key, use the Move tool to move the copied layer down to look like the example image. Next, move the Shadow layer to the first layer, just above the background layer and just before the original layer. Change the copied layers layer opacity to 40%. Step 10: Create a new layer above the copied layer and then select the Gradient Tool. Use the Gradient Tool to create an effect to the finished image. Once this logo is placed on it's shiny table, you are done. Final Product: Put a few more final touches on and you are really smokin' now! http://www.nicora.net/index.cfm?method=article&blogID=C88F54CC-D998-BFC0-D995-3823E52FA483
by Anna 안나 2008. 6. 30. 23:58
Finished product Step 1
Before starting this tutorial, be sure you have already created your Web 2.0 logo, and keep in mind if you used different colors that you will want to coordinate them instead of using the colors I do. I will be using many of the same techniques too, so some of this should get easier. First things first, create a new canvas, make it 900px x 600px at 72 resolution and a white background. Now let's make some guides, I do this by bring up the rulers. Do this with the Ctrl+R shortcut. Now to create our first guide, left mouse click into the horizontal ruler and drag downward. You should see the guide, typically depicted as a blue line. Drag it down and line it up with the 120px mark. To stop dragging the guide, simple release the mouse button. If you want to move the guide again, be sure to select the move tool from the tools palette. Do the same thing again, this time dragging the guide to the 200px mark. And one more to the 202px mark, you may have to zoom way in to get this one aligned just right. We have 2 more guides to place, these will be vertical guides and to start dragging these, click into the vertical ruler. Drag the first to the 100px mark, and the second to the 800px mark. You should have something resembling the screenshot below. Step 2
Create a new layer by clicking the "Create new layer" icon in the layers palette then select the rectangular marquee tool from the tools palette create a rectangle starting at the farthest left and the 202px mark to the farthest right and to the very bottom. Set your background color to #fc2bbb in the tools palette and fill, I use the Ctrl+Backspace shortcut to do this. When finished use Ctrl+D to deselect. Next create a new layer and pick the gradient tool and the color white for the foreground color in the tools palette. Start from the bottom and create a gradient like in the screenshot. Step 3
Create a new layer and set the foreground color to #434343 and create a very slight and small gradient from the top and another from the 200px mark up, as in the screen shot. Then insert your Web 2.0 logo and resize accordingly, also depicted in the screenshot. Step 4
Create a new layer then select the rectangular marquee tool from the tools palette and make a selection starting from the vertical 200px mark up ~40px and across to the horizontal 800px mark. Fill with the color #fc2bbb, this will be the base of our nav. Now we want to round the corners. Select the elliptical marquee tool from the tools palette and set the feather to 0px. Look at the screenshot below to see where to make your selection. You can constrain proportions by holding Shift while sizing. Step 5
Now select the eraser tool from the tools palette, set the diameter to 25px and hardness to 0%. We need to inverse the selection, to do this I use the Ctrl+I shortcut. Now use the eraser tool to round the corner, see the screen shot for reference. Once finished, repeat the step on the right side of the rectangle. Step 6
Click on the thumb nail of the layer with the base nav element in the layers palette while holding Ctrl, this will make a selection around that element. Then go to "Select > Modify > Contract" contract by 1px. Select the gradient tool and set the foreground color to white, create a slight and subtle gradient starting at the bottom of the selection up. Use the screen shot as reference. Step 7
Create a new layer then select the elliptical marquee tool, draw a selection like the one in the screen shot. Step 8
Select the gradient tool and make a gradient starting from the top going down, use the screen shot as reference. Step 9
Make a selection around the base nav element using the same technique as before, then contract the selection by 1px. Inverse the selection and hit the Backspace, this deletes all the unwanted parts of the elliptical gradient. Step 10
Select the rectangular marquee tool and make a selection starting from the farthest left at the vertical 120px mark, down to the 200px mark and then all the way to the right. Fill this selection with the color #c9ccd6. You can add the 2px line at the top shown in the screen shot by using the Single Row Marquee tool and the color #3b3f4d. Step 11
At this point, you know how to create rectangles and round the corners like in Step 4. Select the rectangular marquee tool and make a selection starting from the horizontal 100px mark at the vertical 202px mark down to the ~500px mark and across to the 800px mark. Round the bottom corners. Using the screen shot, create 2 rectangle selections inside this rectangle and fill the top one with the color #bed2ff and the bottom with #545454. As you can see I used gradients with slightly darker colors starting from top to bottom on both rectangles. This will be the base content layer. Step 12
Create a new layer under the base content layer and use the method in Step 6 to create a selection around the object in the base content layer. Fill the selection with black. Then go to "Filter > Blur > Gaussian blur" and set the radius to 3.8px. Use the rectangle marquee tool to make a selection from the farthest left at the vertical 120px mark all the way up and over to the right, hit Backspace to delete this selection. You should have something like in the screen shot. Step 13
Next, lets put the menu items into the nav. Create a new layer and select the text tool from the tools palette. Select the desired font, keep in mind that Trebuchet is a wildly popular Web 2.0 font. Type out the items and set the font size to 17.88pt, you can use the move tool to position the type according to the screen shot. Once positioned, right click on the texts layer in the layers palette and select "Blending options". Step 14
Click on drop shadow and configure the setting to match the screen shot. Step 15
Now we add a few finishing touches like fake copy and some extra content pieces and we are done!
by Anna 안나 2008. 6. 30. 23:53
1. Make a rectangle with the Rounded Rectangle Tool (any color) and right click and Rasterize it. 2. Open the Layer Style and add these values: 3. Inner Glow 4. Bevel and Emboss 5. Gradient Overlay: #99b910 to #d1dc42 6. Stroke: #d1dc42 7. You should have something like this: 8. Over a New Layer, make a selection with the Elliptical Marquee Tool as shown below: 9. Make a Radial Gradient white to transparent and change the mode to Soft Light and Fill 40%. 10. Right click over the thumb while pressing Ctrl and go to Select/Modify/Contract and put 2 px, then go to Select/Inverse and press Del once. Ctrl + D to select. 11. Add text using Segoe 25 pt. 12. Then add it a Drop Shadow. 13. Now we're going to make a Rollover using the same button, duplicate it and change the Gradient Overlay: #2a2e2f to #4a5052 and the Stroke #454b4d. Note: If you want to have both separate, copy all the layers and put them each one into a group. 14. And you're done.
by Anna 안나 2008. 6. 30. 23:47
보통 css 에서 폰트 속성을 지정할 때 아래와 같이 하지요...

{font-family:굴림; font-size:9pt; color:black}

이것을 좀 더 간단하게 지정할 수 있습니다.

{font:9pt "굴림"; color:black}
by Anna 안나 2008. 6. 30. 18:13
산돌폰트패키지 뷰어(viewer)입니다.^^

폰트이름과 예시가 플래시형식으로 나와있어서 보기 편리합니다.

'개인 > freeboard' 카테고리의 다른 글

가치평가에 이어..상대방평가?를 해보았다.^^  (0) 2008.09.21
가치평가  (0) 2008.09.21
CSS로 만든 Layout 소개  (1) 2008.07.12
jQuery 관련 링크  (0) 2008.07.12
구글 매니아가 구글에 환호하는 이유  (0) 2008.07.07
구글 스타일 로고 만들기  (0) 2008.07.07
재미있는 페이지.  (0) 2008.07.07
온라인 촛불 문화제 이미지들..  (0) 2008.06.21
제목  (0) 2008.05.07
하핫..-_-;  (1) 2008.03.27
by Anna 안나 2008. 6. 29. 00:18
설치 방법에 대한 기본 준비물은 자신이 사용할 웹 서버 계정과 FTP 프로그램, 그리고 현 시점에서 배포중인 MM게시판 배포 압축파일 입니다. 아래 설명글은 미리내 웹 서버, 알 FTP, MMB 2.00 기준으로 작성했습니다. 1. 먼저 배포 포스트에 첨부되어 있는 mmb2-ion01.zip 파일을 다운로드 받습니다. 다운로드 받으시고 압축을 푸시면 사용하시는 압축 프로그램에 따라 다르지만 대략 mmb100-ion01 폴더 안에 파일들의 압축이 풀립니다.
((-------IMAGE-------)) 2. mmb2-ion01 폴더 안에는 mmb라는 폴더가 있으며, mmb 폴더 안에는 아래 그림과 같이 php 파일들과 폴더 세개가 있습니다. mmb 폴더의 이름은 원하시는대로 마음대로 바꾸셔도 좋지만 한글은 피하시는 편이 좋습니다. 아래 설명에서는 그대로 mmb 상태인 채로 설명합니다. ((-------IMAGE-------)) mmb 폴더안에서 env.php 파일을 "워드패드"로 엽니다. 파일 내용중에서 //---------------- 사이에 $cfg_admin_passwd = " ~ "가 있습니다. 큰 따옴표("") 사이에 띄어 쓰기 없이 원하시는 관리자 비밀번호를 바꿔줍니다. 그 줄 아래의 $cfg_member_passwd는 회원 인증 비밀번호입니다. 이것도 큰 따옴표 사이를, 관리자 비밀번호와 다르되, 간단한 비밀번호로 바꿔주신 후 저장하시고 env.php 파일을 닫습니다. 3. FTP 프로그램을 켜고 자신이 사용하는 계정에 접속합니다. 화면에 서 "접속하기" 버튼을 누르시만 아래와 같이 창이 하나 뜹니다. ftp 주소란에는 자신이 사용하는 계정의 ftp 주소를, (미리내 경우엔 아래에서 ***** 부분을 자신의 아이디나 계정명으로 바꾸시면 됩니다.) 사용자 아이디에 계정명을, 비밀번호란에 계정 비밀번호를 적으시고 반드시 Passive Mode 패시브 모드 에 체크하신 후 확인 버튼을 누릅니다. ((-------IMAGE-------)) 4. 아래는 정상적으로 자신의 웹 서버 계정에 접속한 모습입니다. 사용하시는 웹 서버마다 환경은 다르지만, 미리내 경우엔 / 아래에 html 폴더가 있는 모습이 보이실겁니다. ((-------IMAGE-------)) 5. html 폴더 안을 들어갑니다. 뭔가 잡다한 파일이 많습니다만.. 그냥 둡니다. ((-------IMAGE-------)) 6. 아래 그림에서, 윗 화면은 "웹 서버" 쪽의 상태를, 아래쪽은 "자신의 하드의 폴더" 를 나타냅니다. 아래쪽 화면에서, 아까 하드에 압축을 풀어 둔 mmb100-ion01 폴더 안으로 이동하신 후 mmb 폴더에서 마우스 오른쪽을 누르시고 "업로드" 를 누릅니다. ((-------IMAGE-------)) 7. 아래는 하드의 mmb폴더 내용물이, 웹 서버의 /html/mmb 폴더로 자동으로 업로드 되고있는 모습입니다. 죽~ 죽~ 다 올라갑니다.. 아래 빨간 화살표의 파일들이 전부 사라지면 다 올라간겁니다. ((-------IMAGE-------)) 8. 웹 서버에 하드의 mmb 폴더가 폴더 통째로 다 올라간 모습입니다. 웹서버 화면에서 mmb 폴더가 생겨있는것을 보실 수 있습니다. ((-------IMAGE-------)) 9. 웹서버의 mmb 폴더에서 마우스 오른쪽을 누르시고 권한설정을 누릅니다. (사용하시는 FTP 프로그램에 따라 "퍼미션" 이라고 되어있을 수 있습니다.) ((-------IMAGE-------)) 10. 권한 변경 창이 뜹니다. 거기에서 현재 Mode, 즉 권한의 숫자를 "755" 로 바꿔줍니다. 사용하시는 서버에 따라 다르지만 이미 755가 되어있을 수도 있습니다. 755가 맞으시면 "확인" 을 누릅니다. ((-------IMAGE-------)) 11. 웹 서버의 mmb 폴더안으로 들어가시면 폴더 3개와 php파일들이 쭉 보이실겁니다. 그 중에서, data 폴더와 image 폴더 두개를 선택하시고 마우스 오른쪽을 누르신 후 권한설정을 누릅니다. 그리고 권한을 "777" 로 바꾸시고 나서 "확인" 을 누릅니다. ((-------IMAGE-------)) 그리고 나서는 php 파일중에서, config_data.php 파일과 option_data.php 파일을 찾으시고 각각 마우스 오른쪽을 누르신 후 "권한설정"을 누르고 권한을 "666" 으로 바꾼 뒤 "확인"을 누릅니다. 다음으로는 data 폴더 안에 들어가셔서 blockw_data.txt, emote_data.txt 두 파일의 권한을 "666" 으로 바꾼 뒤 "확인"을 누릅니다. 즉, 11번 단계에서 폴더 2개와 파일 4개의 권한을 바꾸시는 겁니다. ((-------IMAGE-------)) 12. 게시판 설치가 끝났습니다. FTP 프로그램을 끄시고 익스플로러를 켜신 뒤 주소창에 자신이 비툴을 설치한 계정의 주소를 씁니다. 미리내 경우라면, 아래 그림에서 ***** 부분만 자신의 계정명을 쓰시면 됩니다. 다른 서버를 사용하시는 분은 http://웹서버주소/mmb(또는 바꾼 폴더이름) 입니다. ((-------IMAGE-------)) 13. 주소를 쓰신후 엔터를 치시면, 게시판 설치 후 최초로 접속 하셨을 때는 아래와 같은 메세지가 나타납니다. ((-------IMAGE-------)) 나타난 메세지를 따라서, 관리자 모드를 누르셔서 로그인 하신 뒤, 환경설정 란에 들어가셔야 합니다. ((-------IMAGE-------)) 환경설정 버튼을 누르면 아래와 같은 로그파일 생성 완료 메세지가 나타납니다. ((-------IMAGE-------)) 설치가 완료된 것입니다. 이후로는 사용하시기 편한 대로 게시판의 환경 설정을 바꾸시면 앞으로 mm 게시판을 사용하실 준비가 끝나신겁니다. btool 버튼을 누르시고 제대로 로그가 작성되는지 테스트 해보시길 바랍니다.
by Anna 안나 2008. 6. 22. 11:44
안녕하세요, 비밋 운영자의 자유개방 갠비랍니다.

왼쪽 "자유비툴"란에도 링크가 되어있으니..그쪽으로 오셔도 똑같아요~^^

언제든지 들려주세요^0^
by Anna 안나 2008. 6. 21. 23:38