리눅스 명령 OS/Linux 2008. 6. 2. 16:01


파일 다복사  방법
-----------------------------------------
[root@linux /root]# cd /home
[root@linux /home]# mkdir copy
[root@linux /home]# cd work
[root@linux work]# find . -depth -print | cpio -pmdvl /home/copy
------------------------------------------

yum install [패키지 이름]
yum remove [패키지 이름]
yum search [키워드]


d 옵션은 하위 디렉토리를 조사하지 않는다는 것입니다. 예를 들어

ls test*

이렇게 했는데 디렉토리 이름중에 test_001 이런게 있고 그안에 파일이 있다면 그것도 다 보여주게 됩니다. 하지만

ls -d test*

이렇게 하면 test_001 이라는 디렉토리 이름만 보여주고 그 안은 안보여줍니다.

whitelazy님이 진짜로 원하시는 것을 저는

  • ls -la | grep ^d


    ----------------
    find . -type d
    ---------------------

    ㅇ 서버관리 차원에서 다양한 이유들로 재부팅 및 종료를 하게 됩니다.
    ㅇ 부팅과 종료를 위한 프로세스 및 명령어들
    init, shutdown, reboot, halt, poweroff등
    ㅇ 부팅레벨 6종류 (관련파일 : /etc/inittab, 관련프로세스 : init)
    - 레벨 0 : 시스템 종료(init 0, halt, shudown –h now)
    - 레벨 1 : 싱글모드(관리모드), 콘솔로만 접근가능, root만 허용(init 1)
    - 레벨 2 : NFS를 지원하지 않는 멀티유저 모드(init 2)
    - 레벨 3 : NFS를 지원하는 멀티유저 모드(init 3, full multiuser mode)
    - 레벨 4 : 현재 사용하지 않는 부팅모드(설정하여 사용가능함)
    - 레벨 5 : X윈도우 환경으로 실행된 멀티유저 모드(init 5)
    - 레벨 6 : 서버 재부팅 모드(init 6, reboot, shutdown –r now)
    ㅇ 부팅과 종료관련 파일들
    - init 프로세스
    - /etc/inittab 파일(부팅시 init프로세스에 의해 참조되는 파일)
    - /etc/rc.d/rcN.d 디렉토리내의 K로 시작하는 파일들(N : 0~6)
    - /etc/rc.d/init.d/ 디렉토리내의 스크립트파일들
    - /var/log/wtmp, /var/log/messages, /var/log/dmesg, /var/log/boot.log 로그파일

원작은 방망이 깍던 노인인데 페러디 한것인데 재밌어서 올립니다.
데브피아에서 퍼왔습니다.
***************************************

코딩하던노인

원작 : 尹五榮의 '방망이 깎던 노인'

벌써 3-4년 전이다. 내가 갓 취업 한 지 얼마 안 돼서 구로공단에서 일 하던 때다.
이른 아침. 찜질방에서 잔 뒤 출근 하러가는 길에, 게임한판 하고 가기위해 근처 PC방으로 향했다.

리듬안마 맞은편 PC방에 구석에 앉아 비쥬얼 스튜디오를 들여다 보는 노인이 있었다.
밤새 잡히지 않는 버그에 대한 조언도 구할겸 소스를 봐달라고 부탁을 했다.
값을 굉장히 비싸게 부르는 것 같았다.

“좀 싸게 해줄 수 없습니까?”했더니,

“소스 하나 고쳐주는걸 가지고 에누리 하겠소? 비싸거든 자네가 고쳐.”

대단히 무뚝뚝한 노인이었다. 더 값을 흥정하지도 못하고 버그나 잡아달라고 부탁했다.

그는 잠자코 열심히 들여다 보고 잇었다. 처음에는 대충 보는 것 같더니, 저물도록 이리 스크롤해 보고 저리 스크롤 해보고 굼뜨기 시작하더니, 마냥 늑장이다. 내가 보기에는 그만하면 다 고친것 같은데, 자꾸만 더 고치고 있었다.
인제 잘 돌아는 가는것 같으니 그냥 달라고 해도 통 못 들은 척 대꾸가 없다. 사실 출근 시간이 빠듯해 왔다.
갑갑하고 지루하고 인제는 초조할 지경이었다.

“더 고치지 않아도 좋으니 그만 주십시오.”

라고 했더니, 화를 버럭 내며,

“끓을 만큼 끓어야 밥이 되지, 생쌀이 재촉한다고 밥 되나.”

한다. 나도 기가 막혀서,

“맡긴 사람이 좋다는데 무얼 더 고친다는 말이오? 노인장, 외고집이시구먼, 출근 시간 늦었다니까요.”

노인은 퉁명스럽게.

“다른 데 가 고치우. 난 소스 지우겠소.”

하고 내뱉는다. 지금까지 기다리고 있다가 그냥 갈 수도 없고, 출근 시간은 어차피 틀린 것 같고 해서, 될 대로 되라고 체념할 수밖에 없었다.

“그럼, 마음대로 고쳐 보시오.”

“글쎄, 재촉을 하면 점점 지저분해지고 늦어진다니까. 코드란 제대로 짜야지, 짜다가 놓치면 되나.”

좀 누그러진 말씨다. 이번에는 고치던 것을 숫제 새로 처음부터 태연스럽게 곰방대에 담배를 담아 피우며 짜고 있지 않은가.

나도 그만 지쳐 버려 구경꾼이 되고 말았다. 얼마 후에야 단축기를 눌러 이렇게 저렇게 컴파일 하고 돌려 보더니 다 됐다고 내준다. 다 되기는 아까부터 다 돼 있던 코드다.

출근 놓치고 지각 해야 하는 나는 불쾌하기 짝이 없었다. ‘그 따위로 코딩을 해 가지고는 장사가 될 턱이 없다. 손님 본위가 아니고 제 본위다. 그래 가지고 값만 되게 부른다. 상도덕도 모르고 불친절하고 무뚝뚝한 노인이다.’

생 각할수록 화증이 났다. 그러다가 뒤를 돌아보니 노인은 태연히 허리를 펴고 리듬안마 지붕 추녀를 바라보고 섰다. 그때, 그 바라보고 섰는 옆 모습이 어딘지 모르게 노인다워 보이고, 부드러운 눈매와 흰 수염에 내 마음은 약간 누그러졌다. 노인에 대한 멸시와 증오도 감쇄된 셈이다.

회사에 와서 소스를 내놨더니, 팀장은 완벽하게 코딩했다고 야단이다. 퇴사한 박대리(주1)가 코딩한 것보다 참 좋다는 것이다. 그러나 나는 전의 것이나 별로 다른 것 같지가 않았다. 그런데 팀장의 설명을 들어 보니, 코드가 너무 지저분하면 버그가 생기기 쉽고 같은 코드라도 성능이 떨어지며, 변수 이름이 제멋대로이면 다른 사람에게 코드를 넘겨주어도 쪽팔리기 쉽단다. 요렇게 꼭 알맞은 소스는 좀체로 만나기가 어렵다는 것이다. 나는 비로소 마음이 확 풀렸다. 그리고 노인에 대한 내 태도를 뉘우쳤다. 참으로 미안했다.

옛날부터 내려오는 開作(개작-Open Source)은 혹 컴파일이 안되면 자료형을 바꿔 컴파일 하고 파일이 누락되어 있으면 구글에서 찾아 넣고 컴파일 하면 좀체로 에러를 내지 않는다. 그러나 요새 소스는 에러가 한번 튀어나오기 시작하면 걷잡을 수가 없다. 예전에는 오래된 開作(개작-Open Source)코드를 갈아엎을때, 깔끔한 최신 배포판으로 잘 받아서 갈아치우기만 해도 컴파일이 되었다. 이것을 최신 리빌드라고 한다. 이렇게 하기를 세 번 한 뒤에 비로소 배포한다. 이것을 '최신 버전을 릴리즈 한다'라고 한다. 물론 날짜가 걸린다. 그러나 요새는 소스코드를 그냥 통채로 복사해서 붙여넣는다. 금방 붙는다. 그러나 왠지 찝찝하다. 그렇지만 요새 남이 보지도 않는 것을 며칠씩 걸려 가며 리빌드 할 사람이 있을 것 같지 않다.

外注(외주)만 해도 그렇다. 옛날에는 복사한 코드(Copy&Paste Code)는 얼마, 직접 짠 코드는 얼마, 값으로 구별했고, 구디구빌(NDNB:Nine-Debug, Nine-Build)한 것은 세 배 이상 비싸다. '구디구빌(NDNB)'란 아홉 번 디버깅하고 아홉번 리빌드 한 것이다. 눈으로 봐서는 다섯 번을 했는지 열 번을 했는지 알 수가 없다. 단지 말을 믿고 사는 것이다. 신용이다. 지금은 그런 말조차 없다. 어느 누가 남이 클레임 걸지도 않는데 아홉 번씩 디버깅 하고 리빌드 할 이도 없고, 또 그것을 믿고 세 배씩 값을 줄 사람도 없다.

옛날 사람들은 코딩은 코딩이요, 생계는 생계지만, 코드를 만드는 그 순간만은 오직 아름다운 코드를 만든다는 그것에만 열중했다. 그리고 스스로 보람을 느꼈다. 그렇게 순수하게 심혈을 기울여 어플리케이션을 만들어 냈다.

이 소스코드도 그런 심정에서 만들었을 것이다. 나는 그 노인에 대해서 죄를 지은 것 같은 괴로움을 느꼈다. “그 따위로 해서 무슨 코더를 해 먹는담.”하던 말은 “그런 노인이 나 같은 젊은이에게 멸시와 증오를 받는 세상에서, 어떻게 아름다운 코드가 탄생할 수 있담.”하는 말로 바뀌어졌다.

나는 그 노인을 찾아가서 삼겹살에 소주라도 대접하며 진심으로 사과해야겠다고 생각했다. 그래서 그 다음 월요일에 퇴근하는 길로 그 노인을 찾았다. 그러나 그 노인이 앉았던 자리에 노인은 있지 아니했다. 나는 그 노인이 앉았던 자리에 멍하니 서 있었다. 허전하고 서운했다. 내 마음은 사과드릴 길이 없어 안타까웠다. 맞은편 리듬안마의 지붕 추녀를 바라다보았다. 푸른 창공에 날아갈 듯한 추녀 끝으로 섹시한 포스터가 걸려있었다. 아, 그때 그 노인이 저 포스터를 보고 있었구나. 열심히 코딩 하다가 우연히 추녀 끝의 포스터를 바라보던 노인의 거룩한 모습이 떠올랐다. 나는 무심히 ‘採菊東籬不(채국동리불)다가 悠然見南山(유연견남산)!’ 도연명의 시구가 새어 나왔다.

오늘, 회사에 출근했더니 후배가 MFC(Microsoft Foundation Classes)와 리소스 편집기로 코딩을 하고 있었다. 전에 커맨드라인과 배치파일로 힘겹게 코딩하고 컴파일 하던 생각이 난다. 도스를 구경한 지도 참 오래다. 요새는 까만 화면은 볼 수도 없다. '왓콤씨' 이니, '어셈블러'이니 애수를 자아내던 그 개발툴들도 사라진지 이미 오래다. 문득 3-4년 전 코딩 하던 노인의 모습이 떠오른다.

주1) "퇴사한 박대리" - 필자가 자기자신을 희화한 인물.

1.설치 기본값으로
2. 디렉토리설정
3.temp 설정
4. 포트추가
5.패스 설정
6. 계정테스트

set cvsroot=:sspi:<computername>:/TEST
(ex--> set cvsroot=:sppi:127.0.0.1:/work)
서버 경로 완료
안돼면 (set CVSROOT=:pserver:IP:/cvsroot <- 위쪽 name 과 같게)

cvs passwd -a <account name>
(ex--> cvs passwd -a administrator ) account name 는 지금 윈도우 접속 아이디(계정)를 말한다.

서버  패스워드 입력하여 로그인

7. 계정 추가
cvs passwd -r <real accountname> -a <cvs login name>

계정에 공백이 포함되어 있다면 쌍따옴표로 감싸도록 한다.

cvs passwd -r "system admin" -a "new user"




(ex --> cvs passwd -r administrator -a layers )
<real accountname 윈도우 계정>
<cvs login name 만들 cvs 아이디>


8. 테스트
set cvsroot=:pserver:<user>@<computername>:/TEST
(ex --> set cvsroot=:pserver:layers@127.0.0.1:/work )
<user (cvs login name)만든아이디>
<computername 서버 아이피주소>
/test 만든 디렉토리이름

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

















 

모든지 하나를 쓸려면 알아야 하는게 많치만 자주 쓰는 명령어들만 익혀도 별 무리는 없는듯


* cvs help 사용

 - cvs command -- help

ex) cvs update[ui] -- help

Usage: cvs update [-APCdflRp] [-k kopt] [-r rev] [-D date] [-j rev]
    [-I ign] [-W spec] [files...]
        -A      Reset any sticky tags/date/kopts.
        -P      Prune empty directories.
        -C      Overwrite locally modified files with clean repository copies.
        -d      Build directories, like checkout does.
        -f      Force a head revision match if tag/date not found.
        -l      Local directory only, no recursion.
        -R      Process directories recursively.

...............

command 에 따른 옵션들을 쉽게 알아 찾아볼수가 있다.


* cvs 로그인 정보는 .profile 에 기록

 - 리눅스에서 같은 계정을 등록하여 사용할 경우 .bash_profile 을 자신만의 profile 로 카피하여 사용한다.

ex) cp .bash_profile .jhcho_profile

.jhcho_profile 내용

alias javacc='/export/www/jhcho/.javacc'

export CVSROOT=:pserver:jhcho@214.216.176.195/home/webmaste/webhard/source-forge

.........

........


* cvs history

cvs log 명령을 통해 저장된 소스 파일의 history 를 볼수 있다.

[webmaste@test controller]$ cvs log BaseAction.java

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

revision 1.2

date: 2004/11/08 04:41:49;  author: jhcho;  state: Exp;  lines: +1 -13

가상메소드변경

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

revision 1.1

date: 2004/11/04 01:44:35;  author: jdjang;  state: Exp;

기본 엑션 크랠스


* 잘못 checkin 하여 이전 버전의 소스를 가져오고 싶을 경우

cvs update 명령을 통해 버전에 맞는 파일을 가져 올수 있다.

> cvs update -f 1.2 BaseAction.java


* binary 파일을 저장소에 올릴때

> cvs add -kb *.gif

 

* update 를 통한 repository 디렉토리 생성

: 작업 디렉토리를 기준으로 저장소에 만들어져 있는 디렉토리가 생성이 되므로 원치않는 디렉토리가 생길수도 있다.

> cvs update -d



참고 사이트

-  cvs quick reference guide

cvs FAQ

cvs 사용

[출처] 몇가지 cvs 명령어|작성자 블즈

1. 트랜잭션 로그 Truncate
현재 트랜잭션 로그 백업을 받지 않는다면 다음의 명령을 수행해 log를 truncate합니다.
backup log [DB_NAME] with TRUNCATE_ONLY
go

2. LDF 파일 축소
DB의 LDF파일에 대한 이름을 확인한 후 DBCC SHRINKFILE 명령으로 원하는 SIZE만큼
줄입니다.
-- LDF 파일에 대한 logical name 확인
sp_helpdb [DB_NAME]
예를 들면 pubs가 디비명인 경우 pubs_log가 논리적 이름이 됩니다.

-- 아래 100은 MB단위로 target size입니다. (즉, 파일 size를 100MB로 줄임)
DBCC SHRINKFILE (pubs_log, 100)
GO

LDF 파일을 축소하는 작업은 운영중에도 가능하지만, 사용자가 적은 시간에 동작시키는
것이 현명할 듯 하네요~ 데이터베이스 복구 메델이 "단순"이 아니라면 정기적으로 트랜
잭션 로그를 백업 받아 주셔야 LDF파일이 무한정 커지는 것을 방지할 수 있습니다.

BACKUP LOG DB_NAME  WITH TRUNCATE_ONLY
DBCC SHRINKDATABASE ( DB_NAME , TRUNCATEONLY)

function useridcheek(pNum)
 {
  for (i = 0; i < pNum.value.length; i++)
  {   
   var ch = pNum.value.charCodeAt(i);
   
   if((ch>32&&ch<48)||(ch>57&&ch<127)) {
    alert('한글과 숫자만 입력할 수 있습니다');
    pNum.value = ""
    return false;
   }
  }
  return true;
 }



------------------ 숫자만 이력되게하는 코드 - -- - ------------
function onlyNumberInput()
 {
   var code = window.event.keyCode;

   if ((code > 34 && code < 41) || (code > 47 && code < 58) || (code > 95 && code < 106) || code == 8 || code == 9 || code == 13 || code == 46)
   {
     window.event.returnValue = true;
     return;
   }
   window.event.returnValue = false;
 }



onKeyDown = "javascript:onlyNumberInput()" style='IME-MODE: disabled'



-- 영어 소문자 대문자. 특수문자만 빼고 입력되게해놨음 ....

//읽어보지못했다 처음만보고 주말에 자세히 봐야겠다.
//퍼온곳 http://kwon37xi.egloos.com/2558053 
Ajax 기본 예제와 JSP 엔진에서 한글 인코딩 충돌 문제 처리
Ajax가 필요한 일이 발생해서, 급조해서 Ajax를 공부했다.
공부한 책은 Ajax 입문이며, 처음 약 5~60 페이지만 읽었다. 급조한 내용이니 너무 신뢰하지 말 것. 아.. 그리고 이 책, 중대한 오탈자가 은근히 있다. 혹시 이 책으로 공부하고자 한다면 오탈자를 확인한 뒤에 공부해서 불필요한 시간 낭비를 줄이는 것이 좋겠다.

아무튼, 책의 내용중 Ajax의 기본적인 사용에 관한 문제를 정리하고, 또 JSP/Servlet 엔진에서 Ajax사용시에 발생하는 한글 인코딩(encoding)문제의 처리방법도 정리해 둔다.


Ajax의 개념에 관한 설명은 인터넷 상에 차고 넘치므로 생략.

* A Simpler Ajax Path가 Ajax 입문에 좋은 글.

Ajax의 개발 순서

1. XMLHttpRequest 객체 생성
2. HTTP 요청을 발생시킴(open(), send())
3. 서버측에서 XMLHttpRequest를 통해 보낸 요청을 받아서 파라미터를 분석하고, 작업을 한 뒤에 결과를 XML이나 문자열로 리턴한다.
4. XMLHttpRequest로 서버가 리턴한 데이터를 받아서 처리(onreadystatechange, responseText, responseXML)

XMLHttpRequest에 의한 송수신 상세 예

JavaScript에서 XMLHttpRequest Object 생성하기

// XMLHttpRequest 오브젝트 생성 함수
// @sample oj = createHttpRequest();
// @return XMLHttpRequest 오브젝트
function createHttpRequest()
{
    if (window.ActiveXObject) {
        try {
            // IE6
            return new ActiveXObject("Msxml2.XMLHTTP");
        } catch (e) {
            try {
                // IE4, IE5
                return new ActiveXObject("Microsoft.XMLHTTP");
            } catch (e2) {
                return null;
            }
        }
    } else if (window.XMLHttpRequest) {
        // Mozilla, FireFox, Opera, Safari, Konqueror3
        return new XMLHttpRequest();
    } else {
        return null;
    }
}


HTTP 요청 발생

 1. open() 메소드 실행 (POST/GET, 요청URL, 동기/비동기지정)
var request = createHttpRequest();
request.open("GET", "/test.xml");
 
// param 1 : GET/POST
// param 2 : URL
// param 3 : 생략가능. 동기/비동기 여부. 기본 비동기.

 2. send() 메소드(데이터 송신)
 request.send(""); // 데이터 없이 전송할때 혹은
 request.send(null); // Konqueror에서는 오류 발생함. Konqueror를 제외하고 데이터 없이 전송할 때 사용가능

위 a,b가 기본적인 모양새이지만, 실제로 GET과 POST 방식에 따라 차이가 많이난다.

  * GET 방식 : GET 방식은 URL에 파라미터를 직접 붙여 보내지만, 한글 등의 문제로 인코딩이 필요하고, RequestHeader 설정도 필요하다. 일반적으로 다음과 같은 모양이 된다.
var paramName = encodeURIComponent("파라미터명"); // 파라미터이름을 UTF-8로 인코딩
var paramValue = encodedURIComponent("파라미터값"); // 파라미터 값을 UTF-8로 인코딩

// 파라미터 구분에 사용되는 ?와 &는 인코딩 되면 안된다. 그래서 따로 붙인다.
var fullParameter = '?' + paramName + '=' + paramValue; // URL에 사용할 파라미터 구성

request.open("GET", "/test.cgi" + data);

// setRequestHeader()는 open()보다 이후에 나와야만 한다.
// 아래는 파라미터 값을 UTF-8로 인코딩해서 보내겠다는 의미.
// GET방식에서는 필요 없고, POST방식에서는 필수이다.
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');

request.send(null);


  * POST 방식 : send() 메소드에 인수를 데이터로 넘긴다.
request.open("POST", "/test.cgi");
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
// POST 방식에서도 파라미터를 인코딩하여 send()메소드의 인자로 넘겨주면 된다.
request.send("name=test&data=123");

A Simpler Ajax Path에 보면 HTML폼에 입력된 값을 자동으로 파라미터 문자열로 변경해주는 메소드 예가 있다.

착신과 데이터 처리

 * onreadystatechange 이벤트(송수신 상태 변할때 기동) - IE 이외 부라우저에서는 콜백 스타일의 onload 이벤트 사용가능
 * readyState 프라퍼티 (송수신 상태를 나타내는 값. "4"가 송신 완료) - onload의 경우에는 불필요
onreadystatechange는 요청 처리 상태를 나타내는 readyState 프라퍼티의 값이 바뀔 때 발생한다.
 * 착신을 처리할 함수 지정은 request.open() 함수를 호출하기 전에 선언해야 정상 작동했다. 항상 요청을 보내기 전에 요청을 처리할 함수 지정을 해둔다.
request.onreadystatechange = callbackFunction; // callbackFunction() 함수가 호출된다.

function callbackFunction() {
    // readyState == 4는 착신 완료를 의미한다.
    if (request.readyState == 4) {
        // 착신시의 처리
    }
}

다른 방법으로, callback 함수를 인라인으로 정의하고, HTTP 상태 코드가 200일때만 작업하도록 할 수도 있다. 두가지를 한꺼번에 보면,
request.onreadystatechange = funcation() {
    if (request.readyState == 4 &&
            request.status == 200) {
        // 착신시의 처리
    }
}

onreadystatechange 대신 onload를 사용할 수 있다. Opera 8은 버그때문에 onload만 사용한다. (IE를 제외한 다른 브라우저에서 다 된다)
request.onload = function() {
    // 착신시의 처리
}

onload와 onreadystatechange를 동시에 이용하기 위해서 다음과 같이한다.
if (window.opera) {
    request.onload = function() { callback(request); }
} else {
    request.onreadystatechange = function() {
        if (request.readyState == 4) {
            callback(request);
        }
    }
}

function callback(request) {
    // 실제 착신시의 처리를 구현하는 부분
}

 * responseText 또는 responseXML (데이터를 텍스트 혹은 DOMDocument로 수신)
   * responseText : 텍스트로 받기
   * responseXml : XML로 받기
   * 여러줄의 CSV 텍스트를 받았을 때의 일반적 처리
var res = request.responseText;
var rows = res.split(" "); // 여러 줄을 한 줄씩 배열로 만든다.
var cols = rows[0].split(","); // 첫번째 줄을 쉼표 단위로 분리하여 배열로 만든다.

   * JSON 처리
eval("res = " + request.responseText)
//... 기타 처리

   * XML 처리
<?xml version="1.0"?>
<lists>
  <name>Toshiro Takahashi</name>
  <msg>hello</msg>
</lists>

var res = request.responseXML;
var msgs = res.getElementsByTagName("msg"); // DOM 객체 사용

alert(msg[0].firstChild.nodeValue);


서버측 스크립트

XMLHttpRequest.send() 에 의해 요청을 받아 처리하게 되는 서버측 스크립트(JSP, Servlet, ASP, PHP 등)은 요청 파라미터를 분석하여 작업을 처리한 뒤에 결과를 Text나 XML로 리턴하면 된다.
 * 리턴시 문자 인코딩은 기본적으로 UTF-8로 한다.
 * 텍스트로 리턴할 경우, Opera 8, Konqueror 3, Safari 등은 UTF-8을 인식하지 못한다. 서버는 응답 문자열을 UTF-8기준으로 URI 인코딩을 해서(Java의 경우 java.net.URLEncoder.encode() 메소드 사용) 리턴하고, 받는 측(웹 브라우져)는 다음과 같이 해석하면 정상적인 문자열을 받게 된다.(실제로는 작동하지 않으므로 URLEncoder를 사용하지말고 받는 자바 스크립트 측에서도 아래와 같이 받지 말고 그냥 request.responseText를 받을 것)
// Mozilla FireFox와 IE에서는 Encode/Decode할 경우
//공백이 +로 바뀌는 현상이 발생했다. 그래서 안쓴다.
// JavaScript측에서 decodeURIComponent를 안하면 서버측에서도 URLEncoding을 하면 안된다.
var res = decodeURIComponent(request.responseText);


JSP/Servlet 에서 Ajax와 한글 인코딩 문제

Ajax는 기본적으로 UTF-8 만으로 통신을 한다고 보면된다. 그 이외의 Encoding을 지원하는 인코딩 함수가 없기 때문에 EUC-KR로 데이터를 전송하려는 꿈은 접어야만 한다.
헌데, 아직 우리나라는 EUC-KR 인코딩으로 된 웹 어플리케이션이 압도적으로 많은 상황이다(어서 빨리 UTF-8로 옮겨가길 바라마지 않는다).
거기다가 보통 JSP/Servlet 웹 어플리케이션은 Servlet 스펙 2.3이후부터 문자 인코딩 서블릿 필터를 사용해 모든 요청에 대해 일관된 문자 인코딩을 사용하는 것이 보편적인 방법으로 자리잡았다.

서블릿 필터는 일관성있게 모든 요청을 EUC-KR로 받아들이게 했는데, 몇몇 Ajax관련 요청만 UTF-8로 받아들여야만 하는 것이다.
필터를 적용할 URL-Pattern을 따로 줘보려 했으나, 너무 복잡해졌다.
그래서 HTTP 요청의 헤더를 이용해서 해결 했다.

아.. 한가지 더. 현재 한글 문제는 "XMLHttpRequest 요청 -> JSP/Servlet" 이 상황에서만 발생하는 것이다.
"JSP/Servlet -> XMLHttpRequest"의 상황(서버에서 클라이언트로 값을 리턴)에서는 이 문제가 발생하지 않는다.
서버가 리턴하는 문자열은 간단하게 다음처럼 하면 WAS가 자동으로 UTF-8로 값을 변경해서 전달하기 때문이다.
<%@ page contentType="text/plain; charset=utf-8" pageEncoding="EUC-KR"%>

contentType에서 text/plain은 텍스트나 JSON으로 값을 리턴할 때이다. XML로 리턴할 때는 text/xml.

아래는 Ajax 요청을 처리하기 위해서 만들어본 간단한 Encoding Filter 이다.
package ajax.filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * 어플리케이션 전체에 적용되는 필터이다.
 *
 * <ul>
 * <li>encoding 파라미터 : encoding 파라미터를 설정하면 request 객체에
 * setCharacterEncoding(encoding)을 실행한다.</li>
 * <li>ajaxFlag 파라미터 : Ajax요청임을 나타내는 HTTP 파라미터 이름이다. ajaxFilter로 지정한 HTTP 파라미터의
 * 값이 true 로 설정되면 인코딩을 무조건 UTF-8로 설정한다.</li>
 * </ul>
 *
 * @author 손권남(kwon37xi@yahoo.co.kr)
 *
 */
public class EncodingFilter implements Filter {

    private Log log = LogFactory.getLog(this.getClass());

    /** HTTP 요청 문자 인코딩 */
    private String encoding = null;

    /** Ajax 요청임을 나타내는 플래그 파라미터 이름 */
    private String ajaxFlag = null;

    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {

        if (ajaxFlag != null
                && "true".equals(((HttpServletRequest) request)
                        .getHeader(ajaxFlag))) {
            // Ajax 처리 요청일 경우 무조건 UTF-8 지정.
            request.setCharacterEncoding("UTF-8");
            if (log.isDebugEnabled()) {
                log.debug("요청 헤더에 " + ajaxFlag + "가 "
                        + ((HttpServletRequest) request).getHeader(ajaxFlag)
                        + "로 설정되어 있어 문자 인코딩에  UTF-8을 사용합니다.");
            }
        } else if (encoding != null) {
            // Ajax 플래그가 true가 아니면, 기본적인 인코딩을 적용한다.
            request.setCharacterEncoding(encoding);
            if (log.isDebugEnabled()) {
                log.debug("문자 인코딩에 " + encoding + "을 사용합니다.");
            }
        }

        chain.doFilter(request, response);
    }

    public void init(FilterConfig config) throws ServletException {
        encoding = config.getInitParameter("encoding");

        ajaxFlag = config.getInitParameter("ajaxFlag");

        if (log.isDebugEnabled()) {
            log.info("encoding : " + encoding + ", ajaxFlag : " + ajaxFlag);
        }
    }

    public void destroy() {
    }
}

이 필터를 적용하고서, web.xml에 다음 내용을 추가하면 필터가 작동한다.
<filter>
    <description>이중 인코딩 필터</description>
    <filter-name>EncodingFilter</filter-name>
    <filter-class>ajax.filter.EncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>euc-kr</param-value>
    </init-param>
    <init-param>
        <param-name>ajaxFlag</param-name>
        <param-value>Ajax</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>EncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

여기 내용을 보면, 기본적인 인코딩은 EUC-KR이고, 요청 헤더에 "Ajax" 헤더의 값이 "true"일 경우에는 강제로 UTF-8을 지정하라고 한 것이다. "ajaxFlag"의 값을 변경하면 헤더의 키을 "Ajax"가 아닌 다른 값으로도 지정할 수 있다. 하지만 아무튼 해당 헤더의 값을 "true"로 지정하면 Ajax로 인식하게 되는 것이다.

이를 위해서는 XMLHttpRequest에도 한가지 처리를 더 보태야 한다.
    request.open("GET", "AjaxProcessor.jsp" + fullParameter);
    request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
    request.setRequestHeader('Ajax', 'true');

당연히 헤더에 값 Ajax요청임을 명시하는 값을 설정하는 것이다.

그리고 다음 문제가 또 있는데, Tomcat 버전별로 문자 인코딩 설정이 달라질 수 있다는 것이다.
위 내용은 Tomcat 4.x에서는 정상 작동하지만, Tomcat 5.x 에서는 제대로 작동하지 않는다.
Tomcat 5.x에서는 server.xml 에 GET 방식의 요청에 대한 인코딩을 지정하기 때문이다.
여기서 URIEncoding="euc-kr" 을 사용하지 않고, useBodyEncodingForURI="true"을 사용하면 Tomcat 4.x 처럼 request.setCharacterEncoding()의 값을 따라가게 할 수 있다.
Tomcat과 한글에 대해서는 Tomcat/JSP와 한글문서를 참조한다.

또하나 Ajax임을 나타내는 플래그를 HTTP 요청 헤더에 설정하도록 했는데, 그러지 않고 요청 파라미터(request.getParameter()로 값을 얻어올 수 있는 것)으로 설정하면 안된다.
request.getParameter()가 실행되어 Ajax 플래그의 값을 감지하는 그 순간, 그 이후 호출되는 request.setCharacterEncoding()는 완전히 무시되어 버리기 때문이다.

예제

급조한 예제이다.
* AjaxCaller.jsp - Ajax 호출부(클라이언트) : 수식을 만들어서 서버측에 계산을 요청한다.
<%@ page language="java" contentType="text/html; charset=EUC-KR"
    pageEncoding="EUC-KR"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=EUC-KR">
<title>Insert title here</title>
<script type="text/javascript">
// XMLHttpRequest 오브젝트 생성 함수
// @sample oj = createHttpRequest();
// @return XMLHttpRequest 오브젝트
function createHttpRequest()
{
    if (window.ActiveXObject) {
        try {
            // IE6
            return new ActiveXObject("Msxml2.XMLHTTP");
        } catch (e) {
            try {
                // IE4, IE5
                return new ActiveXObject("Microsoft.XMLHTTP");
            } catch (e2) {
                return null;
            }
        }
    } else if (window.XMLHttpRequest) {
        // Mozilla, FireFox, Opera, Safari, Konqueror3
        return new XMLHttpRequest();
    } else {
        return null;
    }
}

// 계산을 수행한다.
function calc() {
    var request = createHttpRequest();
    var nameParam = encodeURIComponent("name");
    var nameValue = encodeURIComponent(document.getElementById("name").value);
   
    var oper1Param = encodeURIComponent("oper1");
    var oper1Value = encodeURIComponent(document.getElementById("oper1").value);
   
    var oper2Param = encodeURIComponent("oper2");
    var oper2Value = encodeURIComponent(document.getElementById("oper2").value);
   
    var operatorParam = encodeURIComponent("operator");
    var operatorValue = encodeURIComponent(document.getElementById("operator").value);
   
    var fullParameter =
        "?" + nameParam + "=" + nameValue
        + "&" + oper1Param + "=" + oper1Value
        + "&" + oper2Param + "=" + oper2Value
        + "&" + operatorParam + "=" + operatorValue;
   
    request.onreadystatechange = function() {
        if (request.readyState == 4) {
            alert("Response : " + request.responseText);
            eval("var result = " + request.responseText);
            alert(result.name + "님 계산결과는 " + result.value + "입니다.");
        }
    }
   
    request.open("GET", "AjaxProcessor.jsp" + fullParameter);
    request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
    request.setRequestHeader('Ajax', 'true');
    request.send("");

} // end of calc()
</script>
</head>
<body>
이름 : <input type="text" id="name" /> <br />
<input type="text" id="oper1" />
<select id="operator">
    <option value="+">+</option>
    <option value="-">-</option>
    <option value="*">*</option>
    <option value="/">/</option>
</select>
<input type="text" id="oper2" />
<input type="button" value="계산하기" onclick="calc()" />
</body>
</html>

* AjaxProcessor.jsp - Ajax 처리부(서버) : 수식을 계산한 결과를 JSON 형태로 리턴한다
<%@ page contentType="text; charset=utf-8" pageEncoding="EUC-KR"%>
<%
System.out.println("AjaxProcessor.jsp 시작");

String result = null;

String name = request.getParameter("name");
String oper1 = request.getParameter("oper1");
String oper2 = request.getParameter("oper2");
String operator = request.getParameter("operator");

double oper1Value = Double.parseDouble(oper1);
double oper2Value = Double.parseDouble(oper2);

double calcResult = 0;

if ("+".equals(operator)) {
    calcResult = oper1Value + oper2Value;
} else if ("-".equals(operator)) {
    calcResult = oper1Value - oper2Value;
} else if ("*".equals(operator)) {
    calcResult = oper1Value * oper2Value;
} else {
    calcResult = oper1Value / oper2Value;
}

result = "{  " +
    ""name" : "" + name + "", " +
    ""value" : "" + calcResult + "" " +
    "} ";
System.out.println("Result : " + result);

%><%= result%><%
System.out.println("AjaxProcessor.jsp 끝");
%>

이름 입력 부분에 한글을 입력하여 문제없이 처리되는지 확인해보기 바란다.

JSON

Ajax(JavaScript)는 데이터를 리턴 받는 방법으로 XML/Text/JSON을 지원한다. JSON은 텍스트 형태로 객체를 정의하는 방식이다. 이것은 XML과 1:1 매칭을 할 수도 있다. XML보다 훨씬 만들기 쉽고 사용법도 자바 객체를 사용하는 것과 유사하다. 그래서 이걸 사용해서 프로그램을 작성했다.
{
  "test1": "hello",
  "test2": "hello2"
}

위와 같은 메시지를 서버에서 응답으로 내보냈다고 할 때
// JSON 형태의 텍스트를 JavaScript 객체화 한다.
eval("res = " + request.responseText);

// 객체를 사용한다.
alert(res.test2); // 이 명령은 "hello2"를 출력한다.

배열은 다음과 같이 생성한다.
[
  ["test1", "test2"],
  ["test3", "test4"]
]

아래와 같이 사용한다.
eval("res = " + request.responseText);

// test4를 출력한다.
alert(res[1][1])

 * JSON 홈페이지 : http://www.json.org/
 * Java 객체를 이용해서 JSON 객체를 위한 텍스트 생성하기 : http://www.json.org/java/simple.txt 매우 단순한 방법으로 핵심 기능만 가지고 있다. 이것을 사용하길 권장한다. 라이브러리 다운로드는 http://www.JSON.org/java/json_simple.zip 에서 한다.
 * XML과 JSON간의 변환
 
[팁] XMLHttpRequest를 사용할 때 한글 파라미터의 인코딩 처리 방법
XMLHttpRequest에서 한글 파라미터를 전송할 때 인코딩을 처리하는 방법에 대해서 살펴본다.
프로바이더: 최범균
섹션 목록
  • XMLHttpRequest 사용시 한글 파라미터 전송 방법
XMLHttpRequest 사용시 한글 파라미터 전송 방법

자바캔에 실린 'XMLHttpRequest를 이용한 웹 채팅 구현'이란 글에서 한글 문제를 iframe을 사용하여 해결했는데, 그 방식 말고 자바캔의 댓글 추가에서 사용한 한글 처리 방식에 대해서 설명해보도록 하겠다.

XMLHttpRequest의 한글 파라미터 문제 해결 방법에 대해서 살펴보기 전에, 웹브라우저가 파라미터 값을 전송할 때 어떻게 인코딩하는 지 살펴보도록 하자. 대부분의 한글 사이트는 다음과 같이 캐릭터셋이 "euc-kr"인 HTML 문서를 사용할 것이다.

    <!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" xml:lang="ko" lang="ko">
    <head>
        <meta http-equiv="content-type" content="text/html; charset=euc-kr" />
        <title>test</title>
    </head>
    <body>
    <form name="test" action="form.jsp">
    <input type="text" name="name" /><input type="submit"/>
    </form>
    </body>
    </html>

위 문서는 name 파라미터를 form.jsp로 전송하는 FORM을 갖고 있는데, name 필드에 '한글'을 입력한 뒤 submit 버튼을 누르면 다음과 같이 파라미터 값이 변환되어 전송된다.

    euc-kr 캐릭터셋에 맞춰 '한글' 을 인코딩한 값
    
    http://..../form.jsp?name=%C7%D1%B1%DB

위에서 '%C7%D1%B1%DB'는 '한글'을 euc-kr 캐릭터셋에 맞춰서 인코딩한 결과이다. IE나 파이어폭스 등의 웹 브라우저는 문서의 캐릭터셋에 맞춰서 파라미터를 인코딩하여 전송하기 때문에, 만약 문서의 캐릭터셋이 utf-8 이면 '한글' 파라미터를 다음과 같이 인코딩하여 전송하게 된다.

    
    utf-8 캐릭터셋에 맞춰 '한글' 을 인코딩한 값
    
    http://..../form.jsp?name=%ED%95%9C%EA%B8%80

문서 캐릭터셋이 euc-kr인 경우와 utf-8인 경우 전송되는 파라미터의 인코딩된 값도 다른 것을 확인할 수 있다.

이제, 다시 본론으로 돌아와서 XMLHttpRequest가 전송하는 파라미터에 대해서 살펴보자. XMLHttpRequest도 파라미터를 웹서버에 전송하기 때문에 파라미터 값을 알맞게 인코딩 해 주어야 한다. 그런데, 아쉽게도 XMLHttpRequest 자체적으로 인코딩 처리를 지원해주지는 않는다. 따라서, 자바스크립트가 지원해주는 인코딩 처리 함수를 사용해야 한다. 자바 스크립트가 제공하는 인코딩 처리 함수는 escape()와 encodeURIComponent()의 두가지가 있다. 이 두가지는 동작 방식이 다른데, 다음표는 두 함수의 실행 결과를 보여주고 있다.

인코딩 처리 인코딩된 값 설명
두 함수의 실행 결과는 문서 캐릭터셋이 euc-kr 이거나 utf-8 인 경우 모두 동일하다.
escape('한글') %uD55C%uAE00 유니코드 값을 표현
encodeURIComponent('한글') %ED%95%9C%EA%B8%80 utf-8로 인코딩. encodeURI() 함수도 동일한 결과 출력

두 함수의 실행 결과를 보면 encodeURIComponent() 함수가 utf-8로 인코딩한 결과를 보여줌을 알 수 있다. 따라서, XMLHttpRequest로 한글 파라미터를 전송할 때에는 다음과 같은 방법을 사용하면 된다.

  • 웹브라우저에서: 자바스크립트 encodeURIComponent() 함수를 사용하여 파라미터 값을 utf-8로 인코딩하여 전송한다.
  • 서버에서: 파라미터 값을 utf-8로 디코딩하여 읽어온다.

웹브라우저의 코드를 작성하면 다음과 같을 것이다. (POST 방식으로 전송할 때에도 같은 방법으로 파라미터를 인코딩하면 된다.

)
    <script type="text/javascript">
    function sendData() {
        var xmlHttp = null;
        if( window.XMLHttpRequest ){
            xmlHttp = new XMLHttpRequest();
        }
        else{
            xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
        }
        
        if( xmlHttp ){
            var nameValue = document.memberForm.name.value;
            var idValue = document.memberForm.id.value;
            var param = "name="+encodeURIComponent(nameValue)+"&id="+encodeURIComponent(idValue);
            xmlHttp.open('GET', 'http://www.some.com/receive.jsp?'+param, true);
            
            xmlHttp.onReadyStateChange = function(){
                if( xmlHttp.readyState == 4 ){
                    alert(xmlHttp.responseText);
                }
            }
            xmlHttp.send(null);
        }
    }
    </script>

서버에서는 파라미터를 utf-8로 읽어오기만 하면 된다. 예를 들어, JSP를 사용한다면 다음과 같이 파라미터의 인코딩을 utf-8로 지정하면 된다.

    <%
        request.setCharacterEncoding("utf-8");
        ...
        String name = request.getParameter("name");
    %>

PHP나 ASP.net과 같은 다른 서버 프로그래밍 언어에서도 JSP와 같은 방식으로 파라미터 값을 utf-8로 디코딩하여 읽어오면 파라미터를 알맞게 처리할 수 있다.

프로바이더 최범균 ( madvirus@madvirus.net ) :
틀 프레임워크, 틀 자바스크립트 등을 개발하였으며, JSP 2.0 프로그래밍, JSP 실전 Know-how, Jakarta Project 등의 책을 집필하였다. 자바캔의 운영자이며, 현재 CVNet e-biz 팀에서 근무하고 있다.
Ian SelbyPrototypeScriptaculous를 이용한 에이젝스 활동 알리미(Ajax Activity Indicators)에 대한 문제점을 지적하고 자신이 개발한 사용법을 공개하였다. XMLHttpRequest들의 완료시점이 어정쩡한 프로토타입의 onComplete 옵션에 질려버린 개발자라면 Ian이 말하는 방식을 꼭 한번 살펴보자. 그의 데모 페이지에서 테스트해본 결과 XMLHttpRequest 요청이 일어나는 시점과 끝나는 시점을 정확히 잡아내며 여러번 클릭하여 다중으로 요청하는 경우에도 문제가 발생하지 않는다.

Ajax.Responders.register({
onCreate: function() {
if($('indicator') && Ajax.activeRequestCount > 0)
Effect.Appear('indicator',{duration: 0.25, queue: 'end'});
},
onComplete: function() {
if($('indicator') && Ajax.activeRequestCount == 0)
Effect.Fade('indicator',{duration: 0.25, queue: 'end'});
}
});
출처 : http://firejune.com/905
음 .. 여러군데서 막힌다 ^^ Tomcat 5 설정및 자바 커넥션 연구 .

대부분의 어플리캐이션에서 가장 부하가 많이 걸리는 부분은 DB 에 접속 하는 부분이라고 합니다. 그에따라 대부분의 상용 사이트 혹은 동시접속자가 생기는 솔루션의 경우에는 DataBase Pool 이라는 기법을 이용하여 Connection 되어있는 객체를 생성하고, 요구사항이 있을때 임대해 주는 방식을 많이 사용합니다. 이 부분을 직접 작성해 보는것 또한 많은 것을 학습할 수 있는 기회가 되지만, 실제 서비스되는 곳에 실험적으로 만들어진 코드를 사용하기는 현실적으로 많이 어려운것 같습니다.

따라서 apache 재단의 Java 서브 프로젝트인 jakarta 에서 DBCP 라는 프레임워크(?) 많이들 이용하고 있습니다. 물론, 학습시에 말이죠. :-)


자바를 배워 나아간다는것은, 영업직들의 화려한 활약으로 인해서 EJB 가 필수적인 스킬이 되어 가고 있는 상황이고 EJB 라는 녀석 자체가 초기 투자비용이 워낙 큰 녀석이다보니 WAS 에서 학습시에 세팅에 열을 올리는 대부분의 것들을 자체적으로 지원해 주기 마련입니다. 그래서 DBCP(DB Connection Pool) 에 대한 전반적인 지식이 없이 EJB 로 넘어가는 사례를 많이 보곤 합니다. 하지만 제가 항상 강조하듯이 지식을 끌어안지 못하고 과정을 학습하는것은, 차후에 그것을 기반으로 한 상위의 무언가를 배울때 걸림돌이 될 것이 확실하다고 생각합니다.

그래서 결론은, 'DBCP 에 대한 전반적인 지식을 끌어안고 상위로 나아가자' 가 되겠죠. :-)


항상 포스팅을 할 때마다 느끼는것이지만, 사설이 너무 길어지는거 같습니다.

1> 라이브러리 복사.
2> server.xml ==> <Resource /> 추가
3> context.xml 혹은 server.xml ==> <ResourceLink /> 추가
4> web.xml ==> <resource-ref> ... </resource-ref> 추가
5> TEST!!


1-1> DBCP 라이브러리 복사.
common-collections-3.2.jar
common-dbcp-1.2.1.jar
common-pool-1.3

위의 3개 파일을 <context>/WEB-INF/lib 디렉토리에 복사.


1-2> oracle JDBC 드라이버 파일 복사.
ojdbc14.jar
위 파일을 <context>/WEB-INF/lib 디렉토리에 복사.
[<TOMCAT_HOME>/common/lib 디렉토리에 복사]


2> server.xml 파일의 <GlobalNamingResources> 의 내부태그로 <Resource /> 추가

<Resource auth="Container" driverClassName="oracle.jdbc.driver.OracleDriver" maxActive="20" maxIdle="10" maxWait="-1" name="jdbc/project" password="tiger" type="javax.sql.DataSource" url="jdbc:oracle:thin:@127.0.0.1:1521:xe" username="scott" />


3> context.xml 혹은 server.xml 파일의 <Context> 의 내부태그로 <ResourceLink /> 추가

<ResourceLink global="jdbc/project" name="jdbc/project" type="javax.sql.DataSource" />


4> web.xml 파일에 <resource-ref> 추가.

 <resource-ref>
  <description>DB Connection</description>
  <res-ref-name>jdbc/project</res-ref-name>
  <res-type>javax.sql.DataSource</res-type>
  <res-auth>Container</res-auth>
 </resource-ref>


5> TEST!!

<%@ page contentType="text/html" pageEncoding="UTF-8"
         import="java.sql.*"
         import="javax.sql.*"
         import="javax.naming.*"
%>
<html>
<head>
 <title></title>
</head>


<body>
<%
  try {
   Context initCtx = new InitialContext();
   Context envCtx = (Context)initCtx.lookup("java:/comp/env");
   DataSource ds = (DataSource)envCtx.lookup("jdbc/project");

   Connection conn = ds.getConnection();
   out.write("DB Connection..");
 
   conn.close();
  }
  catch(Exception e) {
   e.printStackTrace();
  }
%>
</body>

</html>

퍼온곳 http://blog.rooine.com/trackback/8 입니다.