서브버전으로 관리되던 소스들을 git으로 이전하기로 마음 먹었다.

기존에 운용되던 서브버전 서버를 제거하고 git서버를 구축하려다가 그냥 남들 사는대로 살자 싶어서 비트버킷으로 결정했다.


 내친김에 비트버킷을 이용하여 자동화된 코드배포를 구현하고 싶었다.

crontab을 사용해 주기적으로 git clone을 하려다 너무 세련되 보이지 않아 찾다보니 webhooks 라는게 보였다.


이때쯤 김주원님의 "Envoy를 이용한 코드 배포 자동화" 글을 보았다.

envoy를 쓰는 방식도 끌리긴 했지만 나는 그다지 큰 시스템도 아니라... 

그냥 간단하게 써도 될듯했고... webhook을 써보고 싶었다... ^^;;;


 그래서 간단하게 반응할 php를 만들었다...

다른 서버에 복사하기도 귀찮고 정리좀 할까 싶다가 composer package 를 만들어야겠다 싶어서 만들었다.

할꺼 많은데 자꾸 일을 벌인다...


계획 

  • master 브랜치만 적용
    - Git Flow 을 쓸때 master만 배포하고 이 외 develop 이나 feature 등은 배포시에 제외한다. 

  • bitbucket의 webhook 접근만 허용하자.
    - 공개된 주소이므로 아무나 접근이 가능했다. 그래서 bitbucket의 IP인 104.192.143.1~255 까지만 허용하기로 했다.

  • 로그를 남기자
    - 나중을 위해 뭐라도 남겨야....

  • 알림같은게 되었으면 좋겠는데...
    Pushbullet을 사용했다. slack쓸껄 후회도 했다... ㅡ,.ㅡa

  • 가능하면 수정된 파일만 바꾼다.
    - git으로 관리되지 않은 설정파일 혹은 업로드된파일 등은 건드리지 않는다.

이런 계획을 세웠다.



이제부턴 존칭으로.. ^^;;



준비


테스트 및 포스팅 편의를 위해 node1, node2, node3, node4 를 셋팅하여 가상의 상황을 만들었습니다.

주소는 아래와 같습니다. 

 http://node1.wbp.co.kr

 http://node2.wbp.co.kr

 http://node3.wbp.co.kr 

 http://node4.wbp.co.kr

※ 테스트로 셋팅한 환경은 일정시간 유지하겠지만 언제 없어질지 모릅니다. 


비트버킷에 배포가될 코드의 repository를 생성하겠습니다.

git@bitbucket.org:DexterPark/test-site.git 이고...

https://bitbucket.org/DexterPark/test-site 로 접근 가능합니다.

예제로 쓸꺼라 공개 repository이지만 실제는 비공개로 하셔야 겠지요?? ^^ 


요약




요약 하자면 이러한 구조입니다.


1. 개발장비에서 비트버킷에 push를 한다. 

2. master로 push가 들어오면 지정한 url로 hook을 날린다. (브렌치는 master외 다른 것으로 설정 가능)


3. hook을 받은 서버는 비트버킷으로 pull을 요청한다...

사실 pull은 아니고 fetch이다...


4. 그리고 결과를 Pushbullet 을 통해 알려준다.



설치


node1 에 설치해보겠습니다.



$ mkdir /home/git

$ cd /home/git

$ git clone --mirror git@bitbucket.org:DexterPark/test-site.git


/home/git를 만들고 

repository를 clone mirror하였습니다.




/home/git/test-site.git 로 생성되었네요... 

해당 경로가 필요하니 기억해 둡니다.


그리고 node1 으로 이동합니다.



구조는 간단합니다. 


웹 루트는 /home/node1/public_html 이지만 /home/node1 전체를 관리하겠습니다.

왜냐면 저는 프레임웍을 사용하니까요... ^^;

여기도 경로도 아래 설정할때 사용되니 기억해 주세요...


아까 composer로 만들었다던 bitdepoly를 설치하겠습니다.

( 참고 : https://packagist.org/packages/dexterys/bitdepoly )



$ composer require dexterys/bitdepoly


여느 composer 설치법과 같습니다.


이제 bitbucket에서 hook을 받을 파일을 생성합니다.


파일은 /home/node1/public_html/bitbucket-hook.php 에 아래와 같이 생성하겠습니다.

https://bitbucket.org/DexterPark/bitdepoly/src 로 가시면 샘플코드를 비롯한 모든 소스코드를 보실 수 있습니다.


<?php require_once __DIR__ . '/../vendor/autoload.php'; use BitDepoly\Hook; $depoly = new Hook(); $depoly->server_name = 'node1'; // 서버의 이름 $depoly->branch = 'master'; // master 브렌치만 적용합니다. $depoly->repo_dir = '/home/git/test-site.git'; // 아까 git clone한 경로를 적어줍니다. $depoly->web_root_dir = '/home/node1'; // 여기도 아까 기억해 달라던 적용될 경로입니다. $depoly->log_file = '/home/node1/deploy.log'; // 이건 로그를 기록할 위치예요. 적당한 위치로 설정하세요.

$depoly->pushbullet_token = ''; // pushbullet의 token 을 적어주세요. (아래설명) $depoly->pushbullet_user = array('xxxx@wbp.co.kr');// 알림 받을 사람 (아래설명)

 

$depoly->deployment();


저는 pushbullet 을 이용하여 알림 받도록 하였습니다.

slack 으로 하려다 다른곳에서도 사용하고 있어서 pushbullet으로 적용하였습니다.

알림을 받지 않으시려면 token 값을 비워두시면 됩니다.


아래 동영상에서 보이는 알림기능을 합니다.

https://pushbullet.com/ 이곳을 통해 가입하셔서 무료로 사용하실 수 있습니다.


또한 $depoly->pushbullet_user에는 배열로 array('xxxx@wbp.co.kr', 'xxxx222@wbp.co.kr'); 와같이 넘겨주시면 여러명에게 전송 가능합니다.

같은 방법으로 node2~4의 서버에도 동일한 방법으로 설치하였습니다.


/home/node1/public_html/bitbucket-hook.php 에 생성후 자신에 맞게 설정을 바꾸셨으면 이제 bitbucket 에서도 설정해주어야 합니다.



bitbucket repository로 이동후 webhook을 설정합니다.



add webhook 을 선택하시면 아래와 같이 나옵니다.




아까 생성한 bitbucket-hook.php 의 경로를 적어주세요.

비트버킷의 IP인 104.192.143.x 에만 반응합니다. 브라우져로 접속시 에러만 출력해요. 

http://node2.wbp.co.kr/bitbucket-hook.php  <- 에러메시지 구경해 보기


4대의 서버에서 테스트 할것이므로 node2~4까지 같은방법으로 등록했습니다.




이제 다 되었습니다!! 


이제 테스트를 해보겠습니다.

이미지로 설명하는것 보다 동영상이 나을것 같아서 동영상으로 만들어 보았습니다.

( 소리없음 )


이거 녹화한다고 떨리네요... ^^;;;


1. 일단 node1~4까지 열었습니다. 모두 "Hi Dexter" 라고 나오고 있네요.

2. local에 설치한 git bash에서 테스트 합니다. 

3. origin과 같도록 pull부터 하고 master브렌치의 index.php 에서 Hi Dexter를 Hello Dexter 로 바꾸었습니다.

4. 그리고 origin master로 push합니다.

5. 눈에 보이진 않지만 push받은 비트버킷에서 각 서버로 webhook을 날리고 각 서버들은 수정된 사항을 origin master에서 받아 옵니다.

6. 각 서버들은 받아온 수정사항을 적용한 후 설정된 사람에게 알림을 보냅니다. 

7. 4대가 모두 잘 배포되었다는 알림을 확인한 후 실제로 바뀌었는지 브라우져에서 확인 합니다.

8. 이번엔 master가 아닌 다른 브렌치에서 작업된것이 적용되진 ㅇ낳는지 테스트 해봅니다.

9. hotfix 브렌치를 생성하고 index.php 파일의  Hello Dexter 아래에다가 "hotfix" 라고 추가해 봅니다.

10. hotfix 브렌치를 origin에 push하고 반응을 기다려 봅니다. 

11. 의도한대로 반응이 없습니다. 

12. 그럼 master에서 merge를 한후 push를 해보겠습니다.

13. 배포되는데 조금 걸렸습니다만 정상적으로 알림이 오고 브라우져로 확인해보니 수정되었습니다.


잘 되네요... 


글 재주가 없어 잘 전달 되었는지 모르겠습니다.

긴글 읽어주셔서 감사합니다.


이것으로 마무리 하겠습니다...





'개발 > 옛날꺼' 카테고리의 다른 글

Bitbucket을 이용한 코드배포  (0) 2017.03.23
Docker image 배포  (0) 2017.03.07
재미있는 Docker  (0) 2017.03.07
자바스크립트 정규식  (0) 2012.01.19
제이쿼리를 이용한 타이머 기능  (0) 2011.10.10
jQuery 동영상 강의...  (0) 2010.09.24

 만들기로 했으니 뭘로 어떻게 만들고, 어떻게 진행할지의 계획이 필요했다.

팀원이 없으니 모두가 혼자 결정하고 계획 해야 했다.

독단적 이어서 편하기도 하지만 느리고 시행착오도 많을것이다...


일단 사장님이 외국에 계실때도 매출일보나 기타 물류상황을 알아야 했다.

리얼타임으로 현 상황의 판매상태를 실시간으로 볼수있게하여 매출을 확인할때 매장 마감까지 기다려야하는 경우를 없애자. 

그리고 확장이 유연하고 빠르며 저렴하게 구축을 해야 한다.

그렇다면 역시 웹기반이지....


모바일에서도 PC,태블릿 작동하고 외국 혹은 외부에서도 자유롭게 접속하는 것이 목표였다.

다른건 다 문제없지만....


포스를 웹으로 만들 수 있을까??

어차피 터치스크린은 "터치=클릭"이니 버튼을 크게 만들면 될 것 같고, 영수증을 프린터 해야하는데...

웹브라우져에서 출력이 진행되면 바로 출력은 안되고 인쇄 미리보기가 한번 나올텐데....

그건 다른 방법으로 우회 하는 법을 연구하자...


바코드건은 어차피 키보드로 치는거와 별반 다르지 않으니 아무동작 하지 않을땐 input text에 포커스만  잘 가져다 노으면 문제가 없을듯 하고... 

여유가 생긴다면 POS만은 나중에 C#이나 델파이같은걸로 다시 만들어도 되니까 DB쪽을 신경쓰자...

또 추후 RestAPI을 도입하더라도  일단은 웹으로 빠르게....


일단 바코드 규칙을 정하는게 우선이었다.


바코드 규칙을 정의하고, 의류의 품종을 정리하고 흠... 대충의 DB설계를 해가면서 틀을 잡아봐야겠다.

의류쪽을 몰라서 큰일이다...


저예산을 목표로 작업해야 하니까

PHP에 Codeigniter 를 쓰기로 하고 MySQL 쓸꺼니까 Workbench로 DB설계를 진행해야겠다.


영수증 프린터, 바코드건 주문해야하고...

와~~~ 할꺼 많타... ㅋㅋㅋㅋㅋ


차근차근 정리하며 하자....


'프로젝트 > 웹포스 물류관리' 카테고리의 다른 글

6편 각종 코드  (0) 2016.05.09
5편 바코드 테스트  (0) 2016.05.09
4편 품번 규칙  (0) 2016.05.09
3편 DB 설계 #1  (0) 2016.05.09
2편 환경구성과 계획  (0) 2016.05.09
1편 WEBPOS를 만들자!!  (0) 2016.05.09

프로그래머들에게 가장 짜증나게 하는게 바로 폼체크가 아닌가 생각됩니다.

사실 간단한듯 하면서도 짜증나서 예전에 만들어놓은거 대충 가져다 쓰는중이었습니다.

 

새로이 프로젝트를 진행하면서 기존 '아이디 중복체크' 버튼등을 빼고싶다는 생각이 들었습니다.

관련 자료를 찾아보니 J-Query Plugin 중에 Validation라는 것이 있더군요.

( 관련자료 : http://docs.jquery.com/Plugins/Validation )

 

그러나 한국에서는 약간 맞지 않은게 보였습니다.

맞지 않다기보다 지원하지 않는 것이랄까요?

그래서 필요한 기능을 플러그인에 추가해 넣기로 했습니다.

 

제가 필요한것은

 

  1. 한글만 입력가능
  2. 영문만 입력가능
  3. 한글 및 영문만 입력가능
  4. 주민번호 유효성 체크
  5. 사업자번호 유효성 체크


 

위 5가지 정도였습니다.

 

일단 구현 예제부터 확인해보시길 바랍니다..

 

http://test.love2u.be/20100408/

 

소스를 보시면 아시겠지만 굉장히 간단하게 구현이 되어있습니다.

한번의 작업으로 편하게 할수 있게 되었군요...

 

J-Query는 따로 받으시면 되구요.

수정된 플러그인은 이걸 받으시면 됩니다.

 

일단 사용하시려면 해당페이지에

J-Query와 Validation 플러그인을 넣으셔야 합니다.

 

이런식으로 넣으면 되요.. ^^;

<script type="text/javascript" src="/js/jquery-1.4.2.min.js"></script>
<script type="text/javascript" src="/js/jquery.validate.js"></script>


 

사용하실때는 기본적인 J-Query 문법을 따릅니다.

 

  $(document).ready(function() {

   $("#form").validate({
    rules: {
     id:{required: true,minlength: 4,remote: {type:"post", url:"/20100408/check_id.php"}}    },

    messages: {
     id: {required: "아이디를 입력하시오",minlength: jQuery.format("{0}자 이상"),remote: "이미 등록된 아이디"}
    }

   });
  });


 

rules에 기본적인 규칙들을 넣으시면 되요..

required: true 란 필수항목이란 것이구요.

minlength: 4 란건 최소길이 4자 라는 겁니다.

 

룰은 이것말고도 많이 있는데요.

여기선 다 다루지 않겠구요.

http://docs.jquery.com/Plugins/Validation

여기 가시면 자세하게 나와있습니다.

영문이긴 하지만 자세히보면 너무 쉬운 영어들이예요.

영어에 문외한인 저도 알아보니까요... ^^;

 

위 소스에서 유의해서 보실 부분은 remote 란 부분인데요.

remote 뒷부분인 url에 /20100408/check_id.php이곳으로 데이터를 post 방식으로 보내게 됩니다.

 

그리고 messages 은 해당 조건에 만족하지 못할때 뿌려지는 메세지 입니다..

쉽죠??

 

check_id.php 에서는 true 와 false 로 응답을 해주시면 되는데요...

 

<?
 if($_POST['id']=="dexter" or $_POST['id']=="love2u" or $_POST['id']=="guest") echo 'false';
 else echo 'true';
?>


 

일단 저는 이렇게 적용하였습니다.

Sql과 연동하셔서 처리하시면 실시간 중복 체크가 완성이 되겠지요??

POST로 보냈기 때문에 $_POST로 받았습니다...

 

음.... 역시 전 누구를 가르킬 체질이 안되나 봅니다... --;

어떻게 설명해야할지 감이 잘 안서네요.. ^^;;;

 

머 이런식으로 사용하심 되구요... ( 너무 대충 넘어가나요?? ^^; )

그냥 예제 소스를 올리겠습니다...

 

 

 

음...jquery.validate.js 파일의 수정 부분을 간단하게 설명해 볼께요...

사실 그냥 사용하시려면 아래는 안보셔도 되요....

우선 추가한 부분은 제가올린 소스 기준으로 245번째 줄을 보면

 

  kor: "한글만 입력가능합니다.",
  eng: "영문만 입력가능합니다.",
  kor_eng: "한글과 영문만 입력가능합니다.",
  ssn: "주민번호가 바르지 않습니다.",
  bssn: "사업자번호가 바르지 않습니다.",
  number: "번호만 입력 가능합니다.",


 

messages안의 부분이있습니다.

저 위의 rules에서 볼때  messages 에서 설정이 안되어있으면 나올 메세지 입니다.

 

724~728줄에 classRuleSettings 을 지정 하였구요...

 

1000번째 줄에 보면

kor: function(value, element) {
   return this.optional(element) || /^[가-힣]+$/.test(value);
  },

 

는 한글만 체크하기 위해 정규식을 썻구요...

그 아래 eng,kor_eng 도 마찬가지구요...

 

그리고 이건 주민번호 체크와 사업자번호를 체크하기위해 넣었습니다.


  ssn: function(value, element) {

   value = value.replace("-", "");
   
   if (/[^0-9-]+/.test(value))
    return false;

   var ssnCheck = 0;
   for (var i = 0; i < 12; i++) {
    ssnCheck += (i % 8 + 2) * value.charAt(i);
   }
   ssnCheck = (11 - ssnCheck % 11) % 10;

   if(ssnCheck != value.charAt(12)) {    
    return false;
   }

   return true;
  },      

  bssn: function(value, element) {

   value = value.replace("-", "");
   value = value.replace("-", "");
   
   var sum = 0;
   var getlist =new Array(10);
   var chkvalue =new Array("1","3","7","1","3","7","1","3","5");
   for(var i=0; i<10; i++) { getlist[i] = value.substring(i, i+1); }
   for(var i=0; i<9; i++) { sum += getlist[i]*chkvalue[i]; }
   sum = sum + parseInt((getlist[8]*5)/10);
   sidliy = sum % 10;
   sidchk = 0;
   if(sidliy != 0) { sidchk = 10 - sidliy; }
   else { sidchk = 0; }
   if(sidchk != getlist[9]) {
    return false;
   }
   return true;
  },

 

지금까지 설명드린 나중에 rules에 추가해서 사용하실 수 있도록 한것입니다.

 

일단 소스를 찬찬히 보시면 답이 튀어 나올듯 합니다...

제 글을 읽어주실 분들이 어떤분들인지 모르니 어떻게 설명을 드려야 할지 모르겠네요...

너무 깊숙히 접근하면 내용이 너무 길어질것같고 대충 적으면 성의 없어보이고...

 

그냥 사용하시다가 궁굼하거나 막히는게 있으시면 아래 댓글을 달아주세요...

그럼 답변 드리도록 하겠습니다.. 그게 좋겠져?? ^^;

 

대단한것도 아니고 어려운것도 아닙니다...

당장 모르신다고해도 천천히 보시면 다 이해 가시리라 믿습니다...

 

별것도 아닌걸 포스팅했는지도 모르겠네요...

어째든 어느분에겐 도움이 되었으면 합니다... ^^;

끝으로 포스팅을 해야겠다는 자극을 주신 일현님께도 감사를... ^^;

 

퍼가실땐 출처를 남겨주세요~~ ^^;

 

 

  1. defle 2010.05.04 22:23 신고

    꼭 필요한 건 다 말씀하신거 같네요. ^^

    좋은 내용 감사합니다.

  2. 드렁크수달 2010.05.10 04:51 신고

    좋은글 감사합니다. 정규식 내용만 퍼갑니다. ^^ 혹시 일현님이 그누보드에 일현님은 아시겠지요? ㅎㅎ;;

  3. 오호 2010.07.12 18:05 신고

    오호 감사합니다. ^^

  4. 질문자 2010.08.11 16:17 신고

    먼저 좋은글 감사드립니다
    validate 의 정규식에서 한글/한글_영문에 오류가 있어 문의를 드릴려고 합니다
    charset=euc-kr 에서 올려주신 파일을 적용하니
    "잘못된 범주의 문자집합입니다" 라고 뜨는데 혹시 해결 방법이 있는가 싶어
    글을 올립니다..;;
    글을 보시면 답변 부탁드립니다...

    • Dexter.Park 2010.08.17 21:25 신고

      euckr은 송수신시 문제가 생기는걸로 알고있습니다.
      euckr에서 사용하시려면 utf8로 변경하신후 보내셔야 합니다.

  5. 나이유미 2010.11.30 21:09 신고

    정말 감사합니다. 꾸벅 (__);

  6. 나그네 2011.03.09 17:33 신고

    그런데 jquery 1.5 에서는 잘 안되는군요..

    • Dexter Park 박용섭 2011.11.21 15:18 신고

      버젼 1.4.2때 만들었구요.
      1.6.2 에서 테스트 해보았으나 정상적으로 작동하고 있습니다.

  7. 누시 2011.12.08 20:36 신고

    감사합니다. 몇일을 고생 고생하다가 제가 원하는것을 찾았습니다. 꾸뻑
    이리도 자세히 적어주셔서 정말로 감사합니다.
    다름이 아니라 지금 아이디에 조건을 더 넣을려고 합니다.
    예를 들어 영문과 숫자만 그리고 중간에 공백 불허 등등을 더 넣을려고 하는데요 조건을 더 넣을려면 어떻게 해야 할까요?
    그리고 처음부터 조건을 명시하는 문구가 나와 있었음 하는데요.....
    마지막으로 성공을 했을때 오케이 아이콘 다음에 문구를 넣고 싶습니다.
    너무나 초보적인 질문 드려서 죄송합니다.
    다시금 좋은 소스 주셔서 정말로 감사히 잘 쓰겠습니다.

일단 소스 코드 입니다.

 

<?
function hide_email($email) {
 $character_set = '+-.0123456789@ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz';
 $key = str_shuffle($character_set);
 $cipher_text = '';
 $id = 'e'.rand(1,999999999);
 for ($i=0;$i<strlen($email);$i+=1)
 $cipher_text.= $key[strpos($character_set,$email[$i])];
 
 $script = 'var a="'.$key.'";var b=a.split("").sort().join("");var c="'.$cipher_text.'";var d="";';
 $script.= 'for(var e=0;e<c.length;e++)d+=b.charAt(a.indexOf(c.charAt(e)));';
 $script.= 'document.getElementById("'.$id.'").innerHTML="<a href=\\"mailto:"+d+"\\">"+d+"</a>"';
 $script = "eval(\"".str_replace(array("\\",'"'),array("\\\\",'\"'), $script)."\")";
 $script = '<script type="text/javascript">/*<![CDATA[*/'.$script.'/*]]>*/</script>';
 
 return '<span id="'.$id.'">[javascript protected email address]</span>'.$script;
}
?>


 

사용법은

 

echo hide_email('test@test.com');

이런식으로 사용하시면 됩니다...

소스보기를 하여 보면...

 

<SPAN id=e76655641>[javascript protected email address]</SPAN>
<SCRIPT type=text/javascript>/*<![CDATA[*/eval("var a=\"6tbK5NHCsqFGf12wP9oe@IcJBVkL3mOiTavSdMDg+.jA_RWY-8pyxzXZhErunl470UQ\";var b=a.split(\"\").sort().join(\"\");var c=\"nRun1nRunbAZz\";var d=\"\";for(var e=0;e<c.length;e++)d+=b.charAt(a.indexOf(c.charAt(e)));document.getElementById(\"e76655641\").innerHTML=\"<a href=\\\"mailto:\"+d+\"\\\">\"+d+\"</a>\"")/*]]>*/</SCRIPT>


이런 식으로 자동 수집기가 수집이 불가능한 형태로 보여집니다..

 

출처 : http://www.maurits.vdschee.nl/php_hide_email/

 

Pager 는 말그대로 페이징 클래스입니다.

설치방법은 php 설치 디렉토리에서

pear install Pager 엔터 하면 설치가 되고

사용은 require_once 'Pager/Pager.php'; 하시고 쓰시면 되겠습니다.

사용방법은 단순히 인스턴스 생성시 파라메타로 설정값을 넘기면

페이징 링크를 생성해서 사용할수 있는 아주~ 간단한 클래스입니다.

사용예제는 아래와 같습니다.

$params = array(
    'mode'      => 'Jumping',
  'extraVars' => array('sType' => $sType, 'sString' => $sString),
    'totalItems'  => (int)$total,
    'delta'  => 10,
    'perPage' => 30,
    'prevImg' => '[◀]',
    'nextImg' => '[▶]',
    'firstPageText' => '맨처음',
    'lastPageText' => '마지막',
    'curPageLinkClassName' => 'pagerStyle'
);
$pager =& Pager::factory($params);
$page =& $pager->getLinks();

이렇게 만들면 $page에 배열로된 링크가 만들어져 리턴됩니다.
그걸 뿌려주기만 하면 끝이죠. 옵션에 관한 설명은 아는거만 ^^;

itemData [array] -
페이징에 사용할 내용 전체를 배열로 넣을수 있습니다.
그럼 배열의 총수가 total 이 되는거죠.

totalItems [integer]-
itemData 에 내용전체를 넣지않고 페이징될 총 갯수만 설정할수 있습니다.

perPage [integer] -
한페이지에 글이 몇개인지.

delta [integer]-
페이지 번호를 몇개 보여줄지.

mode [string] "Jumping" or "Sliding"
점핑은 보통 사용하는 페이징 방식이고 슬라이딩은 일정페이지 이상이 되면
현재 페이지가 항상 페이지 번호 중간에 나오는 방식입니다.(구글 결과페이지 참고)

httpMethod [string] "get" or "post" -
페이징 변수를 get,post로 넘길건지.

linkClass [string] -
페이징에 걸 스타일 시트

urlVar [string] "pageID" -
페이지 번호로 사용할 변수이름

altFirst [string]
altPrev [string]
altNext [string]
altLast [string]
altPage [string]
- 마우스 올렸을때 나타낼 문자열

prevImg [string] "<<"
nextImg [string] ">>"
- 앞,뒤 링크 문자 혹은 이미지 아이콘으로 설정

separator [string] -
페이지 번호에 특정 문자,이미지를 추가할수 있습니다.

curPageLinkClassName [string]
- 현재페이지 번호에 적용할 스타일 시트

firstPageText [string]
lastPageText [string]
- 맨처음,맨끝 페이지 표시할때 사용할 문자열

extraVars [array]
- 페이징 링크에 추가할 변수들 넣을수 있습니다.

머 대충 이정도구요.

나머지 메소드들도 간단히 아는거만 ^^;

Pager::getCurrentPageID() -- 현재페이지 번호리턴

Pager::getLinks() --  생성된 링크를 배열로 리턴

Pager::getNextPageID() -- 다음페이지 번호 리턴

Pager::getOffsetByPageId() -- 페이지에 해당하는 limit 리턴

Pager::getPageData() -- itemData에 전체 내용을 배열로 넣었을때 페이지에 해당하는 배열내용을 가져울수 있습니다.

Pager::getPreviousPageID() -- 이전 페이지 번호

Pager::getperpageselectbox ([integer $start = 5 [, integer $end = 30 [, integer $step = 5 [, boolean $showAllData = false [, string $optionText = '%d']]]]]) -- 페이지 바로가기 selectbox html 을 리턴합니다.


대충 이정도구요.
아쉬운 점이라면 이전10개 다음10개 이기능이 없더군요.
페이징관련 소스를 찾고 있으시거나 공부해보실 분이라면
한번 봐두는 것도 좋을듯 하네요.

http://pear.php.net/manual/en/package.html.pager.php

출처 : PHPSCHOOL

바코드의 종류는 100여종에 이른다고 합니다. 이중에 Pear에서 code39, code128, int25, postnet, ean13, upca 6가지 바코드를 손쉽게 생성할수 있는 클래스가 있네요

Pear 패키지명 : Image_Barcode

예제)
<?
require_once "Image/Barcode.php";
$bar = new Image_Barcode;
$bar->draw('pear_Barcode128','code128','jpg');
?>

<?
require_once "Image/Barcode.php";
$bar = new Image_Barcode;
$bar->draw('pear_barcode39','code128','jpg');
?>

<?
require_once "Image/Barcode.php";
$bar = new Image_Barcode;
$bar->draw('2827491728372','ean13','jpg');
?>

<?
require_once "Image/Barcode.php";
$bar = new Image_Barcode;
$bar->draw('pear_image_barcode25','int25','jpg');
?>

<?
require_once "Image/Barcode.php";
$bar = new Image_Barcode;
$bar->draw('pearbarcode128','postnet','jpg');
?>

<?
require_once "Image/Barcode.php";
$bar = new Image_Barcode;
$bar->draw('192787364758','upca','jpg');
?>
룰(?) : upca 사용시 숫자열만 12자로 
PEAR메일은 어떠한 상황에서라도 개발자들이 메일을 보낼수 있는
편리한 클래스 래퍼를 제공합니다.

오늘은 PEAR메일 클래스로 메일을 보내는 3가지 방법에 대해서 알아봅시다.
첫번째로는 PHP에서 제공한 mail함수가 있습니다.
PEAR가 PHP함수를 호출하고 이는 다시 시스템에 연결된 데몬에 메일서비스를 호출하게됩니다.
두번째는 sendmail에 직접 연결하는 것입니다.
스쿨의 팁란에서도 PHP의 메일 함수가 문제가 있을때 sendmail을 직접 호출해서 사용하는 방법을
많은 개발자들이 선호하고 있습니다.
세번째는 소켓을 연결하셔 SMTP서버와 직접 연결하는 것입니다.
서버자체에 메일서비스가 없을때 유용하게 사용할 수 있습니다.

먼저 PEAR로 간단하게 메일을 보내 봅시다.
<?
require_once 'Mail.php';

// 아래의 구문은 클래스 생성과 메서드 호출을 동시에 처리할 수 있습니다.
// 따라서 이 문은 $mail = new Mail(); $mail->factory('mail'); 과 같습니다.
$mail =& Mail::factory('mail');

$headers = array (
'From' => '보내는사람@메일.com',
'To' => '받는사람@메일.com',
'Subject' => '메일 제목'
);
// 같이 받을사람을 ,로 구분해 적습니다. 생략가능
$recipients = '참조1@메일.com,참조2@메일.com';
$body = '메일 본문';

$mail->send($recipients, $headers, $body);
?>

간단 하지요?
클래스 사용이 난해하다구요? 저는 그렇게 생각지 않습니다.
PEAR를 자주 쓰시다보면 다 거기서 거기로 보입니다. 익숙해 지는것이죠.

자 이번에는 PEAR메일을 이용해서 파일 첨부도 해봅시다.
아래는 PEAR메일 클래스로 메일을 보내는 3가지 방법에 대해서 설명하고 있습니다.
또한 아래의 예제는 제대로 실행되지 않습니다.

<?php
// 아래 함수들은 PEAR가 설치된 디렉토리에서 불러들이는 것입니다.
require_once 'Mail.php';
require_once 'Mail/mime.php';

$txtBody = 'TXT형식의 메일의 내용을 적습니다.';
$htmlBody = '<h1>HTML형식의 메일의 내용을 적습니다.</h1>';
$file = '첨부할 파일.zip';
$headers = array(
'From' => 'free_hewon@yahoo.co.kr',
'Subject' => 'Test용 mime 메세지'
)

$mime = new Mail_mime();

// 같이 받을사람을 ,로 구분해 적습니다. 생략가능
$recipients = '참조1@메일.com,참조2@메일.com';

// 메일 보내는 환경을 set 합니다. set으로 시작하니까 세팅하는 메서드 이겠죠.
$mime->setTXTBody($txtBody); // 텍스트형식으로 메일을 보낼경우
$mime->setHTMLBody($htmlBody); // HTML형식으로 메일을 보낼경우

// 여러 파일을 보내고 싶을때에는 반복해서 입력하시면 됩니다. add하는 메서드 이니까요.
$mime->addAttachment($file); // 파일첨부의 기본값은 application/octet-stream 입니다.
$mime->addAttachment($file, 'text/plain'); // 텍스트형으로 보내고 싶을경우

$body = $mime->get(); // base64엔코딩된 몸체를 얻습니다.
$headers = $mime->headers($headers); // base64엔코딩된 헤더를 얻습니다.

// 여기까지는 모두 동일한 방법입니다.
// 이제 메일을 보내는 3가지 방법입니다.

// PHP기본 메일함수를 이용할경우는 기본 메일설정으로 인스턴스를 생성합니다.
$mail =& Mail::factory('mail');

// sendmail을 이용할때는 sendmail 경로를 설정해주어야 합니다.
$params['sendmail_path'] = '/usr/lib/sendmail';
$mail =& Mail::factory('sendmail', $params);

// SMTP를 이용할때에는 smtp주소를 설정해야 합니다.
// localhost를 제외한 타 서버에서 보낼때에는 아이디와 패스워드가 필요합니다.

$params = array (
'host' => 'smtp.mail.yahoo.co.kr',
'auth' => 'true',
'username' => 'free_hewon',
'password' => '*****'
)
       
$mail =& Mail::factory('smtp', $params);

// 메일 보내는것은 3가지 모두 같습니다.
$mail->send($recipients, $headers, $body);

?> 

'개발 > 옛날꺼' 카테고리의 다른 글

PEAR Barcode로 손쉽게 바코드 생성  (0) 2010.02.01
PEAR를 이용한 whois  (0) 2010.02.01
PEAR로 메일을 보내는 3가지 방법  (0) 2010.02.01
PEAR로 PHP 프로그램 캐싱하기  (0) 2010.02.01
[펌] jQuery 유용한 플러그인 모음.  (0) 2010.02.01
PHP 내장 함수 2  (0) 2010.01.15
구문 안에서의 캐싱 캐싱(caching)은 PHP 세계에서 현재 가장 빈번하게 언급되는 주제이다. PHP는 동적 웹 페이지를 생성하므로 웹 페이지가 요청 되어지면 매번 스크립트가 실행되어야 하며 실행 결과가 같다고 하더라도 그 결과는 매번 연산되어야 한다. 게다가 PHP는 스크립트가 요청될 때마다 컴파일한다. 이러한 오버헤드는 과중한 트래픽과 함께 사이트의 속도를 느리게 만들기도 한다. 다행히도 웹 요청 결과는 저장되거나 캐시될 수 있으며 스크립트를 재실행 하거나 컴파일 할 필요 없이 매칭 요청으로 제시될 수 있다. ZendCache같은 상품이나 Alternate PHP Cache같은 오픈 소스 솔루션은 바이트 코드인 PHP 스크립트의 컴파일된 버전을 캐시해준다. 'PHP land' 솔루션이 PHP디자인에 타격을 가하는 동안 'Userland'솔루션은 웹 애플리케이션 디자인과 프로그램에서 발생하는 일반적인 병목현상을 해결함으로써 한 단계 더 발전할 수 있다.(여기 사용된 'PHP land'는 PHP 4를 구동시키는 Zend Engine과 같은 PHP 언어 수준을 말하며 'Userland'는 PHP사용자에 의해 저작된 것을 말한다.) 데이터베이스 안에 저장된 거대한 카탈로그와 같은 상업적 응용 프로그램을 생각해 보자. 현실적으로 그 카탈로그의 정보는 하루 한 두 번 특정 시간대에 바뀔 것이다. 그리고 여전히 제품 페이지에 요청이 들어올 때마다 데이터베이스 쿼리는 수행된다. 이러한 오버로드는 쿼리 결과나 요청 페이지의 완벽한 HTML 출력을 캐싱함으로써 피할 수 있다. PEAR의 캐시 패키지는 동적인 내용, 데이터베이스 쿼리와 PHP 함수 호출을 캐싱하기 위한 프레임워크를 제공한다. 어디에서 PEAR 캐시를 얻을 수 있는가? 펄이 CPAN, TeX가 CTAN을 가지고 있듯이 PHP또한 클래스와 라이브러리 모듈을 위한 중앙 저장소를 가지고 있다. 이 중앙 저장소를 PEAR라고 부르며 이는 PHP Extension and Add-On Repository의 약자이다. 이 기사는 여러분이 PEAR 환경 설정을 이미 마쳤다는 가정 하에 기술된 것이다. 보기로 든 예제는 CVS에서 사용하는 PEAR Cache 개발 버전으로 만들어지고 테스트한 것이다. PEAR 캐시는 어떻게 작동하는가? PEAR 캐시 패키지는 일반 Cache 클래스, 함수 호출을 캐시하기위한 클래스, 스크립트 출력과 같은 몇 개의 특화된 하위 클래스로 구성된다. Cache 클래스는 실질적으로 캐시된 데이터를 저장하고 운영하는 Container 클래스라 불리는 다양한 종류의 클래스를 이용할 수 있다. 다음은 개개의 매개변수와 함께 PEAR 캐시의 현재 컨테이너 구현 목록을 정리해 놓은 것이다.
  • file - file 컨테이너는 캐시된 데이터를 파일 시스템 내에 저장한다. 이것은 가장 빠른 컨테이너이다.
  • Cache_dir - 이것은 컨테이너가 파일을 저장하는 디렉토리이다.
  • Filename_prefix - 캐시 파일을 위한 파일명 접두사, 예를 들어 "cache_"
  • shm - shm 컨테이너는 공유 메모리 내로 캐시된 데이터를 저장한다. 벤치마크는 이 컨테이너의 현재 구현이 file 컨테이너보다 훨씬 느리다는 것을 암시한다.
  • shm_key - 사용되는 공유 메모리 키
  • shm_perm - 공유 메모리 조각을 위한 퍼미션
  • shm_size - 할당된 공유 메모리의 크기
  • sem_key - 사용되는 새마포어 키
  • sem_perm - 세마포어를 위한 퍼미션
  • db - PEAR의 데이터베이스 분리 계층
  • dsn - DSN 사용된 데이터베이스 연결의 DSN. 세부 사항을 위해 PEAR DB 서류라고 명기해야 함.
  • Cache_table - 사용되는 테이블의 이름
  • phplib - phplib 컨테이너는 캐시된 데이터를 저장하기 위한 데이터베이스 분리 계층을 사용한다.
  • db_class
  • db_file
  • db_path
  • local_file
  • local_path
  • ext/dbx - PHP 데이터베이스 분리 계층 확장자. 이것은 여러분이 캐시된 데이터를 데이터베이스 내로 저장하고자 할 때 일반적으로 선택하는 컨테이너이다.
  • module
  • host
  • db
  • username
  • password
  • cache_table
  • persistent
PEAR 캐시를 사용함으로써 얻을 수 있는 수행상 이점은 여러분이 어떤 캐시 컨테이너를 사용하느냐에 크게 좌우된다. 예를 들어, 데이터베이스의 쿼리 결과를 다시 데이터베이스로 저장시키는 것은 아무 의미가 없다는 뜻이다. 함수 호출 캐싱 PEAR Cache의 Function Cache 모듈은 어떤 함수나 메소드의 결과와 출력이 내장 PHP함수나 사용자 정의 함수라 하더라도 캐시한다. 초기 설정값은 File 컨테이너를 사용하고 캐시 데이터를 function_cache 라 이름 붙여진 디렉토리로 갖다 놓는다. Cache_Function 클래스 생성자는 선택 가능한 모든 매개변수를 3개까지 받아들일 수 있다.
  • $container 사용할 캐시 컨테이너의 이름
  • $container_options 캐시 컨테이너를 위한 매개변수의 배열
  • $expires 캐시 개체가 만기된 후 시간을 나타내는 숫자
Cache_Function 클래스의 call()메소드를 사용하는 일반 함수 호출을 래핑함으로써 캐시된 함수 호출은 유발된다. Call()을 사용하는 것은 매우 쉽다. 첫번째 매개변수는 호출할 함수나 메소드의 이름을 지정하고 호출된 함수나 메소드의 매개변수가 그 뒤에 제시된다. Call()의 두 번째 매개변수는 호출된 함수나 메소드의 첫번째 매개변수이다. 이와 같은 방법으로 프로그램은 계속 작성된다. 그러면 이제 예제를 한 번 살펴보자. 예제 1: 캐싱 함수와 메소드 호출
<.;?php
// Load PEAR Cache's Function Cache

< 'Cache/Function.php';

// Define some classes and functions / methods
// for demonstration

class foo {
  function bar($test) {
    echo "foo::bar($test)<br>";
  }
}

class bar {
  function foobar($object) {
    echo '$'.$object.'->foobar('.$object.')<br>';
  }
}

$bar = new bar;

function foobar() {
  echo 'foobar()';
}

// Get Cache_Function object

$cache = new Cache_Function();

// Cached call to static method bar() of class foo
// (foo::bar())

$cache->call('foo::bar', 'test');

// Cached call to method foobar() of object bar
// ($bar->foobar())

$cache->call('bar->foobar', 'bar');

// Cached call to function foobar()

$cache->call('foobar');
?>
함수 호출의 캐싱은 일일 기준으로 변하는 XML 소스의 XSL 변형과 같이 시간을 많이 소비하는 상황에서 편리하게 사용된다. 출력 캐싱 상업 응용 프로그램의 도입 예제를 돌이켜 볼 때, 이제 여러분은 요구할 때마다 데이터베이스에서 꺼내 사용할 수 있는 카탈로그 정보나 파싱된 템플릿 요소를 캐시할 수 있다. 우리는 이제 Cache_Output 클래스와 함께 스크립트의 완벽한 출력을 캐시함으로서 한 단계 더 발전하는 것이다. 예제 2: 캐싱 스크립트의 출력
<?php 
// Load PEAR Cache's Output Cache 
 
require_once 'Cache/Output.php'; 
 
$cache = new Cache_Output('file', array('cache_dir' => '.') ); 
 
// Compute unique cache identifier for the page we're about  
// to cache. We'll assume that the page's output depends on 
// the URL, HTTP GET and POST variables and cookies. 
 
$cache_id = $cache->generateID(array('url' => $REQUEST_URI, 
	'post' => $HTTP_POST_VARS, 'cookies' => 

$HTTP_COOKIE_VARS) ); 
 
// Query the cache 
 
if ($content = $cache->start($cache_id)) { 
// Cache Hit 
 
echo $content; 
die(); 
} 
 
// Cache Miss 
 
// -- content producing code here -- 
 
// Store page into cache 
 
echo $cache->end(); 
?>
Cache_Output 클래스를 사용하면 쉽게 동적 데이터베이스 구동 웹 애플리케이션을 정적 애플리케이션으로 변화시킬 수 있다. 이것은 사이트의 수행 능력을 현저하게 개선시킬 수 있다. 점점 더 많은 웹 사이트가 HTML 컨텐트를 위해 GZIP를 사용한다. 이것은 서버의 대역폭을 감소시키며 트래픽 발생으로 인한 비용도 줄여준다. 게다가, 그것은 이러한 모뎀 연결을 이용하는 사용자의 경험을 증가시킨다. 내용을 압축하는데 필요한 CPU시간을 절약하기 위해 생성된 HTML의 GZIP 압축 버전을 캐시함에 따라 Cache_OutputCompressionCache_Output 클래스의 상관성을 확장시킨다. 맞춤식 솔루션 이전에 나는 PEAR Cache 프래임워크가 어떻게 맞춤식 캐싱 솔루션을 개발하기 위해 사용될 수 있는지에 대해 설명하였다. 그때 나는 SELECT 쿼리 결과를 캐시하는 MySQL_Query_Cache클래스를 선택하여 설명했다. 그렇다면 이제 클래스 변수인 생성자와 소멸자부터 시작해 보자. 생성자는 Cache_FunctionCache_Output클래스 앞에서 캐시 컨테이너 옵션을 옮기기 위해 사용된다. 소멸자는 MySQL 연결을 종료하고 필요하다면 캐시의 가비지 컬렉션을 작동시킨다.
<?php
require_once 'Cache.php';

class MySQL_Query_Cache extends Cache {
  var $connection = null;
  var $expires    = 3600;

  var $cursor = 0;
  var $result = array();

  function MySQL_Query_Cache($container  = 'file', 
      $container_options = array('cache_dir'=> '.', 
      'filename_prefix' => 'cache_'), $expires = 3600)
  {
    $this->Cache($container, $container_options);
    $this->expires = $expires;      
  }

  function _MySQL_Query_Cache() {
      if (is_resource($this->connection)) {
        mysql_close($this->connection);
      }

      $this->_Cache();
  }
}
?>
실제적으로 쿼리를 수행하고 캐시하는 핵심적인 부분을 살펴보기 이전에 좀더 도움을 주는 함수에 대해 살펴보자.
<?php 
function connect($hostname, $username, $password, $database) { 
  $this->connection = mysql_connect($hostname, $username, $password) 
  or trigger_error('Could not connect to database.', E_USER_ERROR); 
 
  mysql_select_db($database, $this->connection) 
  or trigger_error('Could not select database.', E_USER_ERROR); 
} 
 
function fetch_row() { 
  if ($this->cursor < sizeof($this->result)) { 
    return $this->result[$this->cursor++]; 
  } else { 
    return false; 
  } 
} 
 
function num_rows() { 
  return sizeof($this->result); 
} 
?>
우리는 MySQL 데이터베이스에 연결하거나 캐시된 결과로부터 열을 불러오고 세트 안에 있는 열의 숫자를 가져오는데 필요한 함수를 이미 알고 있다. 그렇다면 데이터베이스 쿼리가 어떻게 수행하고 캐시하는지 살펴보도록 하자.
<?php 
function query($query) { 
  if (stristr($query, 'SELECT')) { 
    // Compute unique cache identifier for this query 
 
    $cache_id = md5($query); 
 
    // Query the cache 
 
    $this->result = $this->get($cache_id, 'mysql_query_cache'); 
 
    if ($this->result == NULL) { 
      // Cache Miss 
 
      $this->cursor = 0; 
      $this->result = array(); 
 
      if (is_resource($this->connection)) { 
        // Use mysql_unbuffered_query(), if available 
 
        if (function_exists('mysql_unbuffered_query')) 
        {$result = mysql_unbuffered_query($query, $this->connection); 
        } else {$result = mysql_query($query, $this->connection); 
        } 
 
        // Fetch all result rows 
 
        while ($row = mysql_fetch_assoc($result)) {$this->result[] = $row; 
        } 
 
        // Free MySQL Result Resource 
 
        mysql_free_result($result); 
 
        // Store result set in cache 
 
        $this->save($cache_id, $this->result, $this->expires, 
        'mysql_query_cache'); 
      } 
    } 
  } else { 
    // No SELECT query, don't cache it 
 
    return mysql_query($query, $this->connection); 
  } 
} 
?>
예제 3: 활성중인 MySQL 쿼리 캐시
<?php
require_once 'MySQL_Query_Cache.php';

$cache = new MySQL_Query_Cache();
$cache->connect('hostname', 'username', 'password', 'database');
$cache->query('select * from table');

while ($row = $cache->fetch_row()) {
  echo '<p>';
  print_r($row);
  echo '</p>';
}
?>
이상 주어진 정보만으로도 여러분은 대부분의 응용 프로그램에서 PHP웹 페이지를 캐싱할 수 있을 것이다. 세바스찬 베르그만(Sebastian Bergmann)은 23세의 독일출신 컴퓨터 공학도이며 PHP 프로젝트에 참여하고 있다. 이외에도 온라인 및 오프라인의 자유기고가로 활동하고 있으며 독일 PHP 연합 고문단(www.php-ev.de)의 일원으로 phpOpenTracker 소프트웨어(www.phpOpenTracker.de)를 개발하고 있다.

'개발 > 옛날꺼' 카테고리의 다른 글

PEAR를 이용한 whois  (0) 2010.02.01
PEAR로 메일을 보내는 3가지 방법  (0) 2010.02.01
PEAR로 PHP 프로그램 캐싱하기  (0) 2010.02.01
[펌] jQuery 유용한 플러그인 모음.  (0) 2010.02.01
PHP 내장 함수 2  (0) 2010.01.15
J-Query 플러그인  (0) 2009.11.17

<?
   $char1 =  substr("abcdef",0);     //abcdef
   $char2 =  substr("abcdef",1);     //bcdef
   $char3 =  substr("abcdef",-1);    //뒤에서부터 하나만 자른다. f
   $char4 =  substr("abcdef",0,3);   //abc
   $char5 =  substr("abcdef",2,3);   //cde

   echo" $char1 <br> $char2 <br> $char3 <br> $char4 <br> $char5";
?>

 

-----------------------------------------------------------------------------

 

<?
   $char1 =  strrchr("php_sample","s");  //sample , strchr과 strrchr의 차이
   $char2 =  strrchr("php_sample","p");  //ple
   $char3 =  strrchr("image.gif",".");       //gif
  
   echo" $char1 <br> $char2 <br> $char3 ";
?>

 

-----------------------------------------------------------------------------

 

<?
   $char1 =  rand();           //0~32768 사이의 값
   $char2 =  rand(0,5);       //0~5 사이
   $char3 =  rand(1,100);    //1~100 사이의 임의의 값

   echo" $char1 <br> $char2 <br> $char3 ";
?>

-----------------------------------------------------------------------------

 

<?
   $char1 =  uniqid(2);             //중복되지 않은 고유한 아이디를 생성, 숫자 2는 prefix
   $char2 =  uniqid(2);
   $char3 =  uniqid(rand());

   echo" $char1 <br> $char2 <br> $char3  ";
?>

 

244012a36d59fb
244012a36d5a0a
1417344012a36d5a19

 

---------------------------------------------------------------------------------

 

 

<?
   $char1 =  md5(2);
   $char2 =  md5(rand());
   $char3 =  md5(uniqid(rand()));

   echo" $char1 <br> $char2 <br> $char3 ";
?>

string md5(string str[,bool raw_output]);

입력받은 값의 md5 해시를 계산한다. 여기서 해시는 16진수 형태의 32자리 문자를 뜻한다.

보통 고정된 크기의 고유한 값을 구하기 위한 방법으로 사용

 

c81e728d9d4c2f636f067f89cc14862c
74c4481c34e417ac39d1de6b4f854c4f
e28d70dca59c6615fd030b68ac3f9c54

 

------------------------------------------------------------------------------

[출처] PHP 내장 함수 2|작성자 연금술사

'개발 > 옛날꺼' 카테고리의 다른 글

PEAR로 메일을 보내는 3가지 방법  (0) 2010.02.01
PEAR로 PHP 프로그램 캐싱하기  (0) 2010.02.01
[펌] jQuery 유용한 플러그인 모음.  (0) 2010.02.01
PHP 내장 함수 2  (0) 2010.01.15
J-Query 플러그인  (0) 2009.11.17
jQuery를 시작하며...  (0) 2009.11.06

+ Recent posts

티스토리 툴바