RSS구독하기:SUBSCRIBE TO RSS FEED
즐겨찾기추가:ADD FAVORITE
글쓰기:POST
관리자:ADMINISTRATOR
앞절에서 이미 언급한 부분입니다. Scope에 관한거죠. 못읽어 보신분이라면 언능가서 먼저 읽으시길 바랍니다. 이 강좌를 하면서 Scope의 개념이 없이는 절대 안되는 부분이거든요. 물론 깊게 설명하지는 않았습니다만,
이번 강좌를 이해하는데 많은 도움이 되리라 생각합니다.

//중첩함수
function A(){
var a = "1";
  function B(){
    var b = "2";
  }
}


이러한 함수가 정의되어 있다고 가정합시다. 함수 A는 a라는 지역변수를 가지고 있고 함수 B는 함수 A안에서
존재하고 b라는 지역변수도 가지고 있습니다.
깜짝 질문, 함수 B에서 a라는 변수를 참조할 수 있을까요? 없을까요? 앞장에서 했던 강좌를 유심히 보신분
이라면 쉽게 알 수 있을 겁니다. 답은 "참조할 수 있다" 입니다. 답은 앞장의 강좌를 다시 읽어서 확인하시기 바랍니다.

참고로 위의 구문은 "중첩함수"라고 부르며 Javascript에서의 private을 만들때 사용하는 방법중에 하나입니다. 실제로 해보시면 아시겠지만, 함수 B는 함수 A안에서밖에 호출할 수 없으며 밖에서 함수 A를 가지고 함수 B를 참조하려 해도 안되는걸 아실 수 있습니다. A.B() <- 이런식으로 절대 호출 할 수 없습니다.

그럼 다음 함수를 보시죠.

<html>
<script>
 function OutFunc(){
   var out = "i'm a out"
   function InFunc(){
     var inner = "i'm a in "+"-----"+ out;
     return inner;
   }
   return InFunc;
 }

 var test_1 = OutFunc();
 var test_2 = test_1();
 alert(test_2);
</script>
<body></body>
</html>

이 스크립트의 결과가 어찌 될거라고 예상이 됩니까? 사실 이 함수 자체를 이해 못하시는 분도 있으리라
생각합니다. 절대 무시하는게 아닙니다. 다만 우리는 알게모르게 쉬운 스크립트만을 접하고 이렇게 구조적으로 파헤친적이 없기때문에 어려울 수 밖에 없었던겁니다. 처음부터 Java 나 C#처럼 배웠다면 이렇게 Javascript가 우리를 힘들게 하지 않았을거라고 전 생각합니다. 일단 실행부터 시켜보세요. 어떻습니까? 여러분이 예상했던 답하고 일치합니까? 일치하다면 자신있게 설명을 하실 수 있습니까? 없다면 아래를 봐주세요 ^^ㅋ

먼저 정리부터 해보죠.
1. Javascript에서의 단위는 함수이다.
2. 함수의 지역변수는 함수밖에서는 절대 호출 할 수 없고, 함수내부에서는 전역변수보다 지역변수가 우선한다.
3. 변수는 각각의 Scope를 가지며 자신의 Scope를 벗어날 수 없다.


이제 소스를 해석해보겠습니다. 다들 청심환 하나씩 드세요. ^^
OutFunc는 자신의 지역변수 out을 가지고 있고 중첩함수인 InFunc를 가지고 있습니다. InFunc는 자신의
지역변수인 inner를 가지고 있고 중첩된 함수(nested function이라고 표현합니다) InFunc는 지역변수 inner를 리턴하는군요. OutFunc는 중첩함수인 InFunc를 리턴하구요. 여기까지 이해하셨죠?

그리고 OutFunc를 실행하고 test_1에 담습니다..여기엔 뭐가 담길까요? 그렇습니다. 함수 InFunc가 담깁니다. 그리고 test_2에는 test_1에 담긴 함수를 실행하고 그결과를 담습니다.

그럼 답을 발표하겠습니다. 답은 "i'm a in -------- i'm a out" 입니다.

제가 묻겠습니다. 변수 inner는 어디에 속해이어야 하는 거죠? 네. 함수 InFunc에 속해서 그 Scope안에서만
존재 해야하며 그함수가 종료되었을때, 같이 사라져야할 변수입니다. 그런데 지금의 모습은 어떻습니까?
alert()을 통해서 inner의 값을 찍고 있습니다. 도대체 어떻게 된 것일까요? 위에 정리한 1,2,3은 도대체
어쩌라고 이렇게 결과가 나오는 겁니까?

눈치가 빠르신분은 이미 알고 계셨을 겁니다. 그렇습니다. 이것이 바로 "클로져(Closure)" 입니다.
클로져의 정의는 "다른 함수내에서 내부객체로 생성된 함수 리터럴을 반환하여 호출 프로그램에서 이를 변수로 배정한 것" 이라고 정의하고 있으며 부연설명으로는 "함수가 동작하는데 필요한 데이터 영역 확장"이라고 하고 있습니다.[o'relly - Javascript for web 2.0 5장]

제가 더 덧붙여 설명하면 첫번째  OutFunc()를 실행함으로써 InFunc()함수를 OutFunc()의 안에서 바깥쪽으로
끄집어내어 영역을 변경(확장)하고 두번째 test_1()을 다시 한번 실행함으로써 inner의 값을 직접 호출 할 수 있도록 호출 프로그램으로 영역을 변경(확장)한 것입니다. 이로써 inner라는 변수는 더이상 OutFunc의
InFunc안에 갇혀있지 않고 어디서든지 호출 될 수 있는 전역변수화가 되어버린 것이죠.
여기까지 이해하셨다면 당신은 이미 중급의 문턱까지 온것입니다.
사실 클로져란것을 정확하게 알고 있는 사람은 드물뿐 아니라 "의도하지 않은 클로져의 생성"으로 고통받고
있는 개발자들도 많습니다. 그만큼 어렵고 이해하기 힘든 개념인 것입니다.

결과적으로만 보면 상당히 훌륭해보이고 멋져 보이지만, 사실 내면엔 "메모리 누수"라는 문제점을 안고
있습니다. 메모리 누수에 대해서는 다음 강좌인 "함수-3"에서 자세히 다뤄보도록 합시다.
오늘은 이만 접어야 겠네요..ㅋ 퇴근시간이 가까워 져서리..ㅋㅋ
그럼 다음 강좌까지 다들 건강조심세요.. 더위가 심하네요..


오늘은 저번 강좌에 이어 클로져에 대해서 이어가겠습니다. 클로져에 대해서 개념이 안잡히신 분들을 위해
간략하게 정리하겠습니다.

함수안에 존재하는 지역변수는 지역변수로써 살아야하며, 함수가 죽으면 따라죽을 운명인데, 클로져는 죽어
마땅한 함수의 지역변수를 밖으로 끄집어내어 영역을 변경(확장)하는 것
을 의미합니다. 물론 이한줄이 클로져를 전부 나타낼 수는 없습니다. 하지만 전 이렇게 설명하려 합니다. 다르게 설명해봤자 서로 헛깔릴게 분명
하거든요 ^^ 사람이든 변수이든 자기의 영역에서 벗어나면 안되나 봅니다..ㅎㅎ 왜냐구요? 클로져도 문제가
좀 심각하거든요... 이름하여 "Memory leak(메모리 누수)" 현상 때문입니다.

메모리 누수의 전체적 그림은 중급강좌정도에 보실 수 있겠네요. 여기서는 클로져에 의한 메모리 누수에
대해서만 다루겠습니다. 메모리 누수 모델이 몇가지 있는데 그걸 다 다루자면 제목에서 보듯이 [기초강좌]에서 범위가 벗어납니다. 우리가 좀전에 뭘 배웠습니까? 영역을 벗어나면 문제가 된다고 했지요 ㅎㅎㅎㅎ

자 다시 본론으로 가서, 클로져는 한 내부함수의 지역변수를 함수 리터럴을 통하여 밖으로 꺼내오는 것을
의미한다 했습니다. 그럼 제가 질문을 해보겠습니다. 그 한 내부함수가 종료되면 클로져에 의해 밖으로
튀어나온 변수는 어떻게 될까요? 그렇습니다... 계속 살아 있습니다. 계속 살아있으므로 부모함수가 죽었는데도 불구하고 계속 호출되어 사용됩니다. 나중에 다시 이야기 되겠지만 이것이 "IE에서의 메모리 누수 모델 1"
입니다. 보통 FireFox나 오페라 같은 브라우저는 클로져가 생성되어도 참조값이 0이면 브라우저메모리에서
GC에의해 소멸되게 됩니다. 허나, IE는 그렇지 못하여 문제가 되는거죠. 전 강좌를 열심히 보신분이라면 쉽게 이해하시리라 봅니다. 클로저의 메모리 누수는 이쯤으로 마치겠습니다. 사실 메모리 누수에 대해서는 할말이
무지 많습니다. 허나, 역시 범위를 넘기면 문제가 ㅎㅎㅎㅎ



posted by blankus

--------------------- 펌글 ---------------------


음냥 초보자들이 알기 딱좋네 !!

나두 넘 좋아 나도 초보거등 ~~~ ㅋㅋㅋ
2007/11/21 12:54 2007/11/21 12:54
이 글에는 트랙백을 보낼 수 없습니다
오늘도 무쟈게 덥군요 ^^; 좀전에 회사 사람들하고 잠시 밖에나가 잡담을 하며 바람좀 쐬고왔습니다.
역시 사무실에 일할때보단 밖에서 놀때가 더 좋군여..ㅎ

오늘은 By value와 By reference에 대해서 알아보겠습니다. 날도 덥고하니 조금은 짧게 이야기해봅시다.
사실 이부분은 제가 C를 공부할때 알게되었습니다만, Javascript에서는 크게 신경안쓰고 작업했던 부분입니다. C를 공부하신 분은 아시겠지만, C에는 포인터란 개념이 있습니다. 물론 Javascript에서는 없습니다.
하지만 표면적으로 C처럼 포인터를 핸들링 하지 않는것 뿐이지, 개념은 필요한 부분입니다.

포인터.. 그놈은 이런놈입니다. 보통 우리가 변수를 선언하고 무언가를 그 변수에 담을때, 그때 그 무언가는
메모리에 저장되게 됩니다. 메모리에 저장될때, "나는 메모리의 어디에 위치해있다."라고 알려주는데 그게 바로 "포인터" 입니다. 말그대로 무언가를 가리키는거죠. 그래서 C에선 일반변수와 포인터 변수를 구분해서
사용하는데, Javascript에서 사용하는 변수는 C에서의 일반 변수가 되겠습니다. 포인터 변수는 뭔가 저장된놈의
메모리 주소를 가지고 있는거구요. 일반 변수는 by value가 되고, 포인터 변수는 넓은 의미에서 by reference가 됩니다. 물론 Javascript에 100% 적용되는 설명은 아닙니다.
여기까지는 쉽게 이해가 가실거라 생각됩니다. 아직 부족하다구요? 그럼 더 쉽게 --;

by value의 참조는 변수에 값을 직접할당하는 방식이며 (변수를 박스라고 생각하시면) 박스안에 내용물이
중요한 놈이죠.
by reference의 참조는 변수에 값을 직접 할당하지 않고,(변수를 박스라고 생각하시면) 박스의 위치에
관심이 있는 놈입니다.
내용물에 뭐가 담겨져있고, 그 내용물이 어찌되던간에 그 박스의 위치만 잘 가지고 있으면 되는 놈이죠.
나중에 변수를 call하면 by reference의 참조는 "값을 가져오라고? 그래. 난 박스주소를 알고있으니까, 그박스에 뭐가있는지는 잘모르지만 내용물을 넘겨줄께"라고 행동하게 됩니다.

그럼 Javascript에서는 위의 개념을 어떻게 가지고 있을까요? 이제 소스를 보면서 이야기해보죠.

<html>
<script>
   //첫번째 구문
   var ref_1 = "string...";
   var test_1 = ref_1;
   ref_1 = "none";

   //두번째 구문
   var ref_2 = new String("hello world");
   var test_2 = ref_2;
   ref_2 = new String("world");

   //세번째 구문
   var ref_3 = new Array("one", "two", "three");
   var test_3 = ref_3;
   ref_3[0] = "1";

</script>
<body>
</body>
</html>


우선 '첫번째 구문'에 대해서 살펴보죠. 아주 쉽습니다. 단순히 문자열을 변수에 담고있고, 그것을 다시 다른 변수에 담았습니다.
그리고 원래 변수의 값을 변경했군요.

'두번째 구문'은 결과적으로 '첫번째 구문'과 동일하지만 다른점이 하나 있습니다. 바로 new String이란 것을 사용해서 변수에 담고있습니다.
'첫번째 구문'에서 ref_1의 타입은 Stirng이지만, '두번째 구문'에서 ref_2의 타입은 Object입니다. 역시 마지막에선 다른 String Object로
값을 변경하고 있네요.

'세번째 구문'은 많이 사용하는 배열입니다. 배열은 자체가 Object인건 아시죠? 역시 이것도 test_3이라는 변수를 만들고 ref_3을 담았습니다.

이상 소스설명은 드렸습니다. 여기까지 이해가 가시나요? 혹시 안가신다면 제게 질문을 하시거나, 구글링을통해 학습이 필요합니다.
이제 제가 질문을 드리겠습니다.

1. alert(test_1)를 실행하면 값이 어떻게 나올까요?
2. alert(test_2)를 실행하면 값이 어떻게 나올까요?
3. alert(test_3)를 실행하면 값이 어떻게 나올까요?

3개의 답을 모두 자신있게 말씀하셨다면, 거기에 답까지 맞으셨다면 축하합니다 ^^*

1번의 답은 "String..."이 출력됩니다. 이유는 String타입의 값들은 by value에 의해서 참조가 됩니다.
다시말해서, test_1은 ref_1의 주소값을 가지고 있는게 아니라 "String..." 자체의 값 즉, value를 가지고 있는겁니다. 그러므로 아무리 ref_1의 값을 변경해도 값에의한 참조(by value)를 한 test_1은 영향을 받지
않는거죠.

2번의 답은 3번을 설명하고 다시 이야기하겠습니다.
3번의 답은 "1, two, three"가 되겠습니다. 왜일까요? 그렇습니다. 둘중에 하나니까 그렇습니다 --;
위에서 값에의한 참조였으니, 이번엔 그반대의 경우겠지요 ^^; 물론 맞는 답입니다만, 정확한 설명을 드리면, ref_3은 Object를 담고있습니다. 그리고 test_3는 ref_3을 참조하지요. 그리고 Object는 by reference에 의해서 참조가 됩니다. 위에서 제가 말씀드린게 by reference가 되면 값을 가지고 있는게 아니라 참조되는 놈의 주소를 가진다고 말씀드렸습니다. 이해가 되시는지요?
만약 값에 의한 참조가 되었다면 test_3는 값이 변경되기전의 ref_3을 참조했기때문에 test_3는 ref_3의 값의 변동에 상관없이 동일한 값을 가지고 있어야함에도 불구하고, 결과는 변경된 값을 가지고 있습니다. 바로 by value가 아닌 by reference에 의해 참조가 되기 때문이지요. by reference로 참조된 변수는 그안에 값이 어떻게 바뀌든지 상관없이 주소값만을 가지고 있으므로 나중에 주소안에 들어있는 값만을 내뱉어 줍니다.
위에 설명 드렸던 부분입니다.

그럼 2번은 결과가 어떻게 될까요? 참으로 애매한 부분입니다. 답은 "hello world"가 출력됩니다
new String으로 생성했으므로 분명 Object입니다, 그럼 by reference에 의해서 참조가 일어나야 정상이지만
결과적으로 그렇지 못합니다 --;
왜그럴까요? 여기에도 무언가가 있기 때문입니다.

바로 Wrapper Object가 범인입니다. Wrapper를 잘모르시는 분을 위해서 잠깐 설명을 드리겠습니다.
Wrapper는 보통 타입을 변경하기위해 사용합니다. String으로 사용하지만 나중의 변환을 위해 new String으로 생성하여 Object로 만들어 내는것입니다. 물론 Javascript에서는 자동 형변환에 의해 크게 사용되지는
않습니다.
다시 본론으로 돌아가서, Wrapper Object는 Object이지만서도 by value에 의해 작동됩니다.
[O'reilly의 Javascript The Definitive Guide 5/E 3장 참고]
(Javascript에서의 Wrapper Object는 String, Number, Boolen이 있습니다. )
그러므로 예상대로 "world"가 출력되지 않고 "hello world"가 출력되는 것입니다.

오늘은 조금 짧게 써보려고 했는데, 말이 길어졌군요 ㅎㅎ 오늘의 주제는 "참조"였는데 만족할 만한
주제였는지 모르겠습니다.
기초강좌라서 약간은 부담이 되는게 사실입니다 ^^; 최대한  쉽게 풀어서 설명하고 싶은데 생각처럼 그렇게
쉽지만은 않군요ㅋ 포스팅을 하다보니 벌써 저녁먹을 시간이네요.. 오늘도 야근모드인
개발자의 비애ㅋ

자 기초강좌가 언제까지 연재 될지 모르겠지만 가끔은 중급강좌도 쓰도록 하겠습니다.
즐거운 저녁식사 되세요~~

posted by blankus


---------------- 트래백 http://www.blankus.net/trackback/5 ----------

위글을 봐서도 알겠지만 ㅡㅡ 뻔히 아는애기 왜하냐고 하면 ㅡㅡ

답이 없지만 서도 ;; 초보자들이 개념만충 하기엔 딱좋은 글이다.

그래서 퍼왔다 !
2007/11/21 12:51 2007/11/21 12:51
이 글에는 트랙백을 보낼 수 없습니다
http://mckoss.com/jscript/object.htm

위의곳을 참조하여보아라

JS 의 참맛을 알게 될것이니 ....

난 지금 js _ abstruct 를 만드는 중인데 .. 참고할만 하다.
2007/11/20 00:57 2007/11/20 00:57
이 글에는 트랙백을 보낼 수 없습니다

사용자 삽입 이미지
 


이 만화에는 놀라운 철학이 들어 있었뜸미다


엄청 단순하고 못그리려고 노력한 그림임에도 불구

눈의 크기와 코의 크기같은 사람의 얼굴 부위별 형태가 모두 다름으로써 서로간의 개성을 어필

비록 단순한 눈으로 보더라도 똑같다고 부를수 있는 사람은 결코 없음을 내포하고 있으며

그들의 대화는 한층 더 놀라운 탄성을 지르게 합니다.


 "너는 씨發 jot찐따" 부분에서 얼굴에 감정을 들어내지 않은채 말하는것으로 보아

상대방에 대한 견제와 그 인격을 알아보기위한 여러의미의 공격수단으로 사용하였으며

그 어려운 공격을 "헐 나 삐져뜸ㅋ"이라는 화난건지 아니면 이해하고

개그로 받아치는(얼굴색 변화 하나 없이)건지  알수 없는 저 반응은 서로 견재하는 상태에 놓여있음을

뜻하고 있습니다.


  이에 공격자는 그에 대응하여 수준이 지나친 사과의 자세와 그 지나친 사과의 자세와는 맞지 않는

"미안"이라는 간단한 말을 함으로써 상대방의 상태를 한번더 떠보는 행동을 하게 됩니다.


  서로 견재함을 뜻하는 만화답게 이에 대응해 수비자도 또 반격을 시작합니다

분명 자신을 공격을 받고 자신을 공격을 받아 좋지 않음을 전달했을 뿐임에도 수비자는

"내가 더 미안"이라는 말을 합니다.

고작 나 삐져뜸ㅋ라는 말 만으로도 자신이 "내가 더 미안"이라고 사과해야 할 수준이니

너의 "미안"이라는 말은 나에게 사과하기엔 매우 부족한 표현이다 라는 뜻을 가지고 있으며

수비자의 행동도 같은 뜻을 의미하고 있습니다.

고개를 90도를 더 넘게 숙임으로서 "이정도가 미안의 의미다"라는 말을 간접적으로 전달하고 있으며

고개를 숙인 상태로 공격자의 얼굴을 주시하고 있으므로 상대방이 얼굴로 미안해 하는정도를 확인하고 있는 것입니다. 


 이에 드디어 견재로 이어지던 서로간의 행동이 본격적인 공격성을 갖기 시작합니다.

간접적이 아닌 직접적으로 말이죠

공격자의 "질 수 없음"이라는 말은 자신이 분명 상대를 견재하기위해 상대방에게 그런 행동과

말을 했음을 감출수 없어 나타나게 되어버린 행동이며

수비자는 이 공격자의 뜻을 알고 있었다는듯 "질 수 없음"이라는 말을 합니다.

서로간의 견재는 이제 필요없고 본론으로 들어가 싸우고 싶다는뜻을 알리는 말이죠

이는 사회에 서로 자신을 알리고 싶어하면서 정작 자신의 생김새나 거의 모든것들을 감춘채

살아가는 인간들의 서로의 감춘 내면을 떠보려 하는 현대인의 본능적 행위를 묘사한 것입니다.


  그러나 이 만화의 주제는 서로간을 떠보려고 하는 그런 인간의 모습을 묘사하려는것

그것만은 아닙니다

이 만화의 진정한 주제가 드러나는 부분은

마지막 제 3자가 나타나 "우왕ㅋ굳ㅋ"의 대사가 진정한 이 만화의 주제를 드러내고 있습니다.

서로 견재를 하다 마침내 자신이 피해입는건 고려하지 않고 서로를 떠보려고 하는 두 인간들...

하지만 그 행위는 이미 자신이 드러내고 싶지 않은 추태를 보이고 마는 것으로써

제 3자는 별 노력없이 그 두명의 약점을 잡게 되었습니다.

시야를 넓게 가지지 않고 조그만 일에서 자아와 삶의 목적을 상실하게 되면

결국 피해를 입게되는건 자신뿐이라는 의미를 가지고 있으며

제3자의 "우왕ㅋ굳ㅋ"은 우리에게 충격의 메세지를 전하고 있습니다.


  일단 우왕ㅋ굳ㅋ은 만화로써 많은 감정상태를 우리에게 전달해 주고 있습니다.

'우왕ㅋ'와 '굳ㅋ'가 서로 분리되어 다른 말풍선에 들어있습니다.

보통 쓸 공간이 넉넉한데 말풍선을 다시 만들어 글을 쓰는 경우는

그 두 나눠진 글이 서로 같은 내용이 아니거나 같은 감정상태가 아닌 경우입니다.

우왕ㅋ와 굳ㅋ의 분리

우왕ㅋ는 좁은 말풍선에 꽉찬 글자가 들어 있습니다.

이는 잠잠하던 사이 갑자기 이 사건을 발견하였으며 여유없이 놀란 상태임을 의미하고

굳ㅋ의 말풍선은  매우 공간이 넉넉하며 말풍선다운 형태를 갖지 못하고 있습니다.

이것은 현재 자신이 매우 여유로우며 흥미있고 이득이 되는 상황에 이르렀음을 깨달았다는

의미를 가지고 있습니다.



  이 두 말풍선이 지닌 우리에게 줄 메세지

그것은 이미 여러분의 가슴속에 새겨지지 않았을련지요….



-2007 11월 10일 바이트드림-


2007/11/18 18:31 2007/11/18 18:31
이 글에는 트랙백을 보낼 수 없습니다
그동안 Prototype 자바스크립트 프레임웍의 사용법을 익히느라 필요이상으로 무리하게 사용해 왔습니다. 간단하게 크로스 브라우징이 보장되며 코드를 아름답게(?) 만들 수 있다는 장점 때문이였는데요. 개발하기 편하여 생산성 높은 환경을 제공하는 반면 원시코드에 비해 9배이상의 시스템 성능저하가 나타나기도 합니다. 아래와 같은 테스트용 루프 함수를 만들고 결과는 동일하면서 모든 브라우저에서 통용되며 상습적으로 사용되는 Prototype함수와 자바스크립트 메서드를 벤치마킹해 보았습니다. 테스트에 사용된 Prototype 프레임웍 라이브러리의 버전은 Scriptaculous1.6.1에 포함된 1.5.0_rc0이며, 브라우저는 파이어폭스2.0b, 시스템사양은 Pentium4, 3.00GHz입니다.

// 타임체크 함수
function timeChecker() {
    var befor, loops, after, tpo, tet, result
    before = new Date()
    loops = 500 //루프 수
    for (var i=0; i < loops; i++) {
        Element.update('element', '내용')// 태스트함수
    }
    after = new Date()
    tpo = Math.round(1000*(after-before)/loops)
    tet = (after-before)/1000
    result = 'Time per operation: '+tpo+', Total excuted time: '+tet; //결과값
    throw(result)
    //return result
}
timeChecker();


1. DOM 엘리먼트 스타일 설정:
프로토 타입에서는 여러가지 형태로 DOM을 주무를수 있게 해 주는데요. 1만회에 걸쳐 객체를  width:'300px', height:'100px'으로 변경하는 테스트입니다. 아래의 결과를 살펴봅시다.

1. document.getElementById('엘리먼트').style.스타일 = '값' // (원시코드, 2라인) -->  1.14초
2. $('엘리먼트').style.스타일 = '값' // (2라인) -->  1.656초
3. Element.setStyle('엘리먼트', {'스타일:값의 배열x2'})  // --> 1.985초 
4. $('엘리먼트').setStyle({'스타일:값의 배열x2'}) // --> 3.047초

동일한 결과값을 가지는 위 4가지 코드는 처리에 필요한 시간이 서로 다릅니다. 4번은 원시코드와 2배이상 차이가 벌어지는 재미있는 결과를 보여줍니다. 반복성이 짙은 경우, 즉 엘리먼트를 실시간으로 업데이트하거나 모션에 사용할 코드는 4번의 형태는 가급적 피하는 것이 좋습니다. 당연히 1.번이나 2번의 형태가 좋겠죠?

2. DOM 엘리먼트 스타일 값 구하기:
DOM의 위치또는 모양을 확인하기 위해 prototype은 getStyle, getDimensions 메서드를 제공하고 있으며, 엘리먼트의 높이 값만을 구하기 위한 getHeight와 같은 메서드도 있습니다.(그나저나 getWidht는 왜 없는걸까요?) 엘리먼트의 높이값(height)을 알기위한 가장 빠른 방법은 무엇인지 살펴 봅시다.

1. document.getElementById('엘리먼트').offsetHeight  // (원시코드) -->  Number 0.391초
2. $('엘리먼트').offsetHeight //Number --> 0.625초
3. Element.getHeight('엘리먼트') //Number --> 0.641초
4. document.getElementById('엘리먼트').getHeight() //Number --> 1.328초
5. $('엘리먼트').getHeight() //Number --> 1.563초
6. Element.getStyle('엘리먼트', '스타일')  //String  --> 2.812초 
7. $('엘리먼트').getStyle('스타일') //String  --> 3.843초

높이 값이 '123px'와 같은 스트링으로 반환된 6, 7번은 'px' 문자열을 없애기 위해 parseInt()하거나 '스트링.substring(0,스트링.length-2)'을 사용해야 합니다. 6, 7번은 쓸일이 없을 것 같은데 왜 집어넣었냐구요? 하지만 아래와 같은 경우(다중 이미지 비율계산)에는 벤치마크 결과와는 정 반대로 6, 7번이 훨씬 빠른 성능을 내기도 합니다.(슬라이드를 움직여 프레임을 비교해보세요.)

// Realtime image Ratio
function setRatio(v) {
    var x,y,h
    var w=Math.round(v)
    $$('array').each(function(e){
        x=Element.getStyle(e, 'width'); y=Element.getStyle(e, 'height')
        h=Math.round(v*y.substring(0,y.length-2)/x.substring(0,x.length-2));
        e.style.width=w+'px'
        e.style.height=h+'px'
        e.style.margin=(w-h)/10+'px'
    })
}


Element.getHeight('엘리먼트')
Width: 75px

Element.getStylet('엘리먼트', 'height')
Width: 75px

3. DOM 엘리먼트.Show/Hide:
1. document.getElementById('엘리먼트').style.display = 'block' // -->  0.547초
2. $('엘리먼트').style.display = 'block' // -->  0.812초
3. Element.show('엘리먼트')  // --> 1.312초
4. $('엘리먼트').show()  // --> 2.609초

또한 가장 많이 사용하는 것 중의 하나인 엘리먼트 Show/Hide를 봅시다. 1만회를 루프로 돌린 결과입니다. 스타일의 결과와 비슷하지요? 역시 원시코드가 가장빠릅니다. 하지만 이것은 결과값이 브라우저에 따라 서로 다르게 나타나기도 합니다. IE에서는 $('element').style.display = 'block', 파이어폭스에서는 Element.show('element')가 잘 돌아간다는 느낌입니다.

4. DOM 엘리먼트.InnerHTML:
1. document.getElementById('엘리먼트').innerHTML // -->  0.672초
2. $('엘리먼트').innerHTML = '내용' // -->  0.766초
3. Element.update('엘리먼트', '내용') // -->  2.39초

이것은 3천회 루프의 결과입니다. 이 또한  원시코드를 사용하는 것이 3배 빠르군요. 눈치 채셨겠지만 "오브젝트.메서드"의 결합과 "메서드(오브젝트)"에도 결과는 같지만 소요시간이 다릅니다. DOM 스타일링에는 메서드(오브젝트)와 같은 형태가 좋습니다. 뭐 결과적으로는 원시코드를 적절히 혼용하면 시스템 퍼포먼스를 크게 끌어올릴 수 있다는 것이지만요.

5. 정수 구하기:
1. Math.round(넘버) // -->  1.453초
2. (넘버).toFixed(0) // -->  3.859초

소수점 이하의 수를 반올림하는 경우 100만회 루프 결과 toFixed(0) 보다 Math.round()가 2배이상 빠릅니다.

6. 브라우저별 색상처리:
좀 다른 얘기지만, getStyle로 색상 값에 따른 이벤트를 처리하는 경우 브라우저 마다 가져오는 색상 값이 틀립니다. 스타일스트(CSS)에 '#f80'으로 설정되었을 경우 브라우저별로 가지고 온 결과는 아래와 같습니다.
오페라 = '#ff8800'
파이어폭스 = 'rgb(255, 136, 0)'
익스플로러 = '#f80'


7. 홀수 짝수 구하기:
홀수나 짝수를 루프에서 처리할 때 'n%2'를 쓰거나 'n&1'을 쓰는 두가지 방법이 있습니다. 이럴땐 'n&1'이 미약하게 나마 약간 더 빠릅니다.
for (var n=0; n < 100;n++) if(n%2==1)$('element').innerHTML = 'test';
for (var n=0; n < 100;n++) if(n&1==1)$('element').innerHTML = 'test';


추가. 문자열비교 : match 보다 test가 빠르고 indexOf가 두배 빠름(50만회)
var testing = 'test test test';
testing.match(/test/); // -->  0.702초
/test/.test(testing); // -->  0.643초
testing .indexOf('test ') != -1; // -->  0.395초


추가. if문과 단순 조건문 속도차이 없음.(500만회)
var boo = true; 
boo = boo? 'true' : 'false'; // -->  0.736초
if(boo){ boo = 'true'; } else { boo = 'false'; } // -->  0.726초


추가. DOM Selecter(500회)
$$('#taglist .row');// -->  1.496초
$$('.row');// -->  tset failed
document.getElementsByClassName('row', 'taglist');// -->  0.193초
document.getElementsByClassName('row');// -->  3.321초

2007/11/09 12:29 2007/11/09 12:29
이 글에는 트랙백을 보낼 수 없습니다
윈도우의 파일을 ftp나 기타 등등으로 utf-8 시스템의 리눅스에 올리면 한글이 다 깨진다.
이것을 해결 할 수 있는 방법은 여러가지이다.

시스템을 euc-kr로 쓰거나 기타등등 방법은 여러가지이다.

이것 때문에 구글링을 해 보았고, kldp 포럼에서 몇가지 방법을 찾아 낼 수 있었다.

1. ftp upload 시 적절한 케릭터 셋을 이용하는 법.
  lftp, filezilla, gftp 패치등이다.
  이중에서 lftp는
  set encoding=utf-8
  set fileencodings=utf-8,euc-kr
  이런식으로 세팅해서 이용 할 수 있다.

2. file system 마운트시 codepage 와 iocharset 을 명시
  ex) -o codepage=949,iocharset=utf8
  fstab에서 지정 해주면 편하게 이용이 가능 하다.

3. 자동 변경 쉘 스크립트.
   쉘스크립트로 그냥 변경 해버린다.
   만들까 했으나... KLDP에서 다른 분들의 글을 읽던 중 이미 만들어 놓은것을 발견 했다.
   recypace + musiphil 두분이 만들었다. 한분이 만들고 다른분이 버그 수정.
   내가 만든것이 아니므로, 원 저작자를 밝혔다.(또한 원자작자가 삭제를 원하면 지울것이다.)

  ----------------------------------------------------------------------
  $ cat char_convert.sh
  #!/bin/sh

  # mv2utf gets at least one filename

  if [ $# -eq 0 ]

   then
       echo "Usage : $0 filename1 [filename2] .....";
       exit 0
   fi
   # Rename euc-kr filename to utf-8 filename

   for filename

   do

       utfname=`echo "$filename" | iconv -f euc-kr -t utf-8`;

       err=$?;

       if [ $err -eq 0 ]

       then

               if [ "$filename" = "$utfname" ]

               then

   # if the filename is english file name

                       echo "$filename is not euc-kr (may be in english)"
               else
   # if the filename is euc-kr

                       echo "rename $filename : $utfname";
                       mv "$filename" "$utfname"
               fi
       else

   # if the filename is not euc-kr
               echo "$filename is not euc-kr"
       fi
   done
  ----------------------------------------------------------------------

4. iconv와 convmv
convmv --notest -f euckr -t utf-8 -r DIRECTORY

5. vi  에서
  set encoding=utf-8
  set fileencodings=utf-8,euc-kr
  
상황에 따라 적절한 방법을 선택해서 이용 하면 된다.
최고의 방법이란 없다. 언제나 사황에 따른 최선의 방법이 있을 뿐이다.
2007/10/25 13:14 2007/10/25 13:14
이 글에는 트랙백을 보낼 수 없습니다
Linux
=====
 
 .bash_profile 또는 .bashrc에서
export PATH=$JAVA_HOME/bin을 지정
 
#설치 위치 : /usr/local/j2sdk1.4.2_02/ (/usr/local/jdk 로 심볼릭 링크)
#export JAVA_HOME=/usr/local/jdk
#export CLASSPATH=$JAVA_HOME/classes12.zip:/root/j2sdk1.4.2_02/lib
#export PATH=$PATH:$JAVA_HOME/bin:$JAVA_HOME/jre/lib
 
 
# .bash_profile
# Get the aliases and functions
if [ -f ~/.bashrc ]; then
 . ~/.bashrc
fi
# User specific environment and startup programs
PATH=$PATH:$HOME/bin
BASH_ENV=$HOME/.bashrc
USERNAME="root"
export USERNAME BASH_ENV PATH
export PATH=$PATH:$usr/java/j2re1.4.2_06/bin/
 
 
 

 

 
FreeBSD
========
 
/etc/profile 또는 $HOME/.bash_profile $HOME/.bashrc, .cshrc
(env 명령으로 쉘을 확인한 후 어떤 쉘을 쓰는지 확인)
 
# tcsh 쉘의 설정(.cshrc)
----------------------
# alias
alias l  ls -alF
alias ns netstat -nr
alias pp ps -aux
 
# path
set path = (/sbin /bin /user/sbin /usr/bin /usr/local/sbin /usr/local/bin /usr/X11R6/bin)
set path = ($path $HOME/bin)
 
# 환경변수
setenv LANG ko_KR.EUC
 
# 쉘 프롬프트
set prompt = "$user %c2> "
set prompt = "$user $cwd> "
set prompt = "`hostname -s`> "
 
# jdk
setenv JDK /usr/local/jdk1.1.8
set path = ($path $JDK/bin)
setenv CLASSPATH $JDK/lib/classes.zip:.
setenv LD_LIBRARY_PATH $JDK/lib/i386
 
# stty
stty erase ^H

 
# Bourne Shell의 설정(.profile)
-----------------------------
# alias
alias l='ls -alF'
 
# 로케일
LANG="ko_KR.EUC"; export LANG
 
#path
PATH=/sbin:/bin:/usr/sbin:/usr/bin:$HOME/bin; export PATH
 

 
Windows
========

도스창을 열고 set path=...... 명령을 넣으면 그 도스창을 띄운 프로세스에만 적용
 
autoexec.bat에 아래 내용 추가후 Rebooting
예) set path=%path%;c:JDK설치위치bin
설명 ) path라는 변수를 셋팅하는데 "기존의 path 변수에 있는 설정값"을 가져오고(%path%) , 추가로(;) "c:j2sebin"라는 폴더를
패스를 잡아서 어디서든지 그 안에 있는 명령어들을 쓸수있게 한다
 
환경변수를 아래와 같이 수정
예) '시작' > '설정' > '제어판' > '시스템' 으로 가서... '고급'tab > '환경변수' 버튼 클릭
     > '시스템 변수'에 "path" 더블 클릭 >  ;c:JDK설치위치bin 내용 추가
2007/10/25 00:37 2007/10/25 00:37
이 글에는 트랙백을 보낼 수 없습니다

1. 클래스의 호출 순서

제목 : 자바에 대한 개념을 흔들어 놓는 소스다...

db책은 시간이 없어서 그냥 와우북에서 보고 영진껄루 사서 보았다....전체적인 개념잡기는 쉬운데...책 두께에 비해 깊은 내용이 없더군....하여간 엽기적인 소스를 만난 덕에 또 이렇게 글을 띠운다....

class A {
  static
  {
  System.out.println("A클래스 스태틱 로딩");
 
  }
  A() {
     System.out.println("A()");
  }
}

class ClassInitTest {
  static int monthNum=12;
  static int monthDays[];
  static
  {
     monthDays = new int[monthNum];
     monthDays[ 0] = 31; monthDays[ 1] = 28;
     monthDays[ 2] = 31; monthDays[ 3] = 30;
     monthDays[ 4] = 31; monthDays[ 5] = 30;
     monthDays[ 6] = 31; monthDays[ 7] = 31;
     monthDays[ 8] = 30; monthDays[ 9] = 31;
     monthDays[10] = 30; monthDays[11] = 31;
     
     System.out.println("static int monthDays[];...");
  }
   
  A a;
  {
     a = new A();
     System.out.println("A a;");
  }
  ClassInitTest() {
     this(0);
     System.out.println("ClassInitTest()");
  }
  ClassInitTest(int x) {
     System.out.println("ClassInitTest(int x)");
  }

  public static void main(String args[]) {
     new ClassInitTest();    //내생각엔 여기서 생성자를 호출해서 저기 아래로 이동해야 될 것 같은데 a객체를 생성하면서  a클래스로 가버리네...이해할 수 없다...5분만 투자하거라...늘그막이 고생하는 친구를 위해...(엉아라는 표현을 이젠 자제한다)

     System.out.println("new ClassInitTest();");
  }  
}

실행한 결과다....
static int monthDays[];...
A클래스 스태틱 로딩
A()
A a;
ClassInitTest(int x)
ClassInitTest()
new ClassInitTest();


답변: 자바 버츄얼머신 스펙을 참조...
 
덕분에 썬 사이트에 가서 자바 버츄얼머신 스펙을 꼼꼼히 읽어보았다.
클래스를 초기화 할때 즉 생성하면 생성자가 제일 먼저
불리우는 것이 아니었다.
간단하게 말을 하겠다.

그래야 잊지 않고 나도 기억하기 쉬우니까.
비록 정확한 대답은 아니라고 해도 전혀 틀린 것도 아니니까.

이건 만은 기억하자.

1. static을 초기화한다.
2. field를 초기화한다.
3. 생성자를 호출한다.

그러니까 소스에서는 ClassInitTest를 생성하면
1. 이 클래스내의 static을 초기화한다.
출력: static int monthDays[];...

2. 다음은 필드를 초기화한다.
필드 A a; 가 있으므로 클래스 A를 생성할 것이다.
이 클래스에도 static이 있기 때문에 이를 초기화한다.
출력: A클래스 스태틱 로딩

3. 그리고, A() 생성자를 호출한다.
출력:A()

4. 다음행 실행
출력:A a

5. 다음은 ClassInitTest의 생성자를 호출.
생성자에서 this(0)이라는 인자 있는 생성자를 호출하므로
출력:ClassInitTest(int x)

6. 다음행 샐행
출력:ClassInitTest()

7. 다시 다음행 실행
출력:new ClassInitTest()

아휴~ 힘들다. 1000자 넘기지 않으려고 말을 짧게 썻다.
이해해라.


 

2. 텍스트 필드에 저장할 수 있는 문자열의 최대 길이

먼저 질문부터 하겠습니다.

Form의 method방식을 get으로 지정했을 경우에 전달할 수 있는 최대 문자열의 길이는 ?

HTTP 데이터 전송 방식에는 get과 post 방식이 있습니다. get방식은 URL에 "이름=값"의 형식으로 데이터를 전송합니다. 쉽게 설명하자면 우리가 많이 사용하고 있는 인터넷 익스플로러를 잘 관찰하시면 다음과 같은 주소를 보시게 될 겁니다.

folderselect=639096&rand=9935556036482

이름과 값을 위와 같은 형식으로 서버에 전달하는 것입니다. 이렇게 get 방식으로 전달할 수 있는 스트링의 길이는 도대체 얼마나 될까 알아보았습니다.

알파벳과 한글 모두 2047문자까지 들어가더군요. 하지만 엄밀히 따지면 앞에 주소도 있고 기다 경로를 제외하면 조금 작아지겠지요. 그런데 실제 데이터를 get 방식으로 전달해 보면 그 보다 훨씬 미치지 못하는 길이만이 넘아갔습니다. 한글로 대략 340문자까지만 전송이 되었습니다[필자가 해보니깐 정말 변수명을 어떻게 하느냐에 따라 틀리고 사파리는 한글이 안된다;;;; 머 어쩌라구]. (+- 오차는 10문자 정도) 이를 post 방식으로 다시 전송하였더니 2000자 이상이 거뜬이 전송되었습니다.

이로써 get 방식과 post 방식의 또 다른 차이점을 알았습니다. 님들도 많은 양의 데이터를 전송할 때는 post 방식을 사용하세요. 무심코 get방식을 사용한다면, 왜 데이터가 제대로 전송이 되지 않는지 그 이유를 찾는데 한참을 보내야 할 겁니다.


 

3. 각종 데이터 타입의 최대값

  • MS Access의 MEMO 데이터 형 : Up to 65,535 characters
  • 유닉스의 IPC중 메시지큐 : 메시지의 최개크기 (4096), 큐의 최대 크기(16384)
  • 자바 자료형의 최소/최대값

유형

크기

최소값

최대값

byte

8bit

-128

127

short

16bit

-32768

32767

int

32bit

-2147483648(20억)

2147483647(20억)

long

64bit

-9223372036854775808

9223372036854775807


4. 네트워크 바이트 순서

바이트 오더(byte order)에는 두 가지가 있다. little endian과 big endian이다.

  • little endian : 작은 수를 끝에 놓는다.
  • big endian : 큰 수를 끝에 놓는다.

little endian은 56이란 수를 56으로 표현하고, big endian은 56을 65로 나타낼 수 있다는 말이다.

네트워크 상의 byte order은 big endian이다. 가령 little endian 머신에서 little endian머신으로 데이터를 전송한다면 문제가 없다 거꾸로 big endian에서 big endian으로도 마찬가지다. 문제는 다른 방식의 두 머신이 통신을 할 때이다.

일단 네트워크로 데이터를 전송할 때는 네트워크 바이트 순서로 바꿔준 다음 데이터를 수신한 쪽에서 자신의 머신에 맞게 순서를 다시 조정하면 된다. 소켓에선 이런 일을 도와주는 함수가 있으며 htonl, htons, ntohl, ntohs와 같은 것들이 바로 이런 일들을 한다.

참고로 SUN, IBM들은 big endian형식이고 Intel은 little 엔디안 형식이라고 한다. 왜 이렇게 두 가지 방법이 있어서 번거롭게 하는지 모르겠다. 한가지밖에 없다면 아무 신경 쓰지 않고 통신을 하면 될텐데...


5. JAVA 기초 문법 중에서

  • 같은 파일 안에 public class는 단 하나만 존재할 수 있으며 클래스는 파일이름과 같아야 한다.
  • float 형의 유효 데이터는 소수점이 있는 숫자로 끝에 F 또는 f가 붙은 숫자이어야 한다.
    float f = 1.3 -> ( X )
    float f = 1.3f -> ( O )
    소수점만 있을 경우에는 double형으로 인식된다.
  • 같은 클래스내에서 메소드를 overloading할 때
    리턴타입은 상관없이 메소드의 인자만 다르면 된다.

class Test
{
     boolean method( int arg ) { return true; }

     boolean method( long arg ) { return true; }

     boolean method( int arg1, int arg2 ) { return true; }

     void method( ) { }     //신기하지만 자바에서는 이것도 가능하다.

     // 컴파일 에러 발생( 메소드 이름과 인자가 같으면 안된다. )
     void method( int arg ) { return true; }    

}

  • 헷갈리는 overloading과 overriding 구분하기

"overloading : 짐을 너무 많이 싣다. "
( 같은 클래스에 이름이 같은 메소드를 여러번 정의할 수 있다. )

class Test
{
     void method( int arg ) {}     // load

     void method( int arg1, int arg2 ) {}     // load ( overload )
}

 

"overriding : 짓밟다. 무시하다. "
( 부모로부터 상속받은 메소드를 재정의할 수 있다. )

class SuperClass
{
     void method( int arg ) { System.out.println( "SuperClass : " + arg ) }
}

class SubClass extends SuperClass
{
     void method( int arg ) { System.out.println( "SubClass : " + arg ) }
}


2007/10/24 12:06 2007/10/24 12:06
이 글에는 트랙백을 보낼 수 없습니다

I. 자바 웹스타트 소개
-------------------------------

  자바 웹스타트는 2001년 초에 소개된 기술로 한번 클릭으로 자바 애플리케이션을 쉽게
수행 시킬 수 있는 환경으로 자바가 추가하던 사상 즉 "언제 어디서나 같은 코드로서
쉽게 접근하고 강력한 기능을 발휘하는 언어" 라는 강점을 유감 없이 발휘할 수 있는 
기술이다.

  자바 웹스타트는 기존의 웹 환경의 한계점을 뛰어넘어 네트웍으로 자유롭게 풍부하고
강력한 응용프로그램을 실행 시킬 수 있으며, 버젼 및 배포 관리를 자동으로 수행하는 
JNLP(Java Network Lanunching Protocol)을 기반으로 하여 관리함으로서 기존의 클라이언트
윈도우즈 프로그램의 문제점을 극복하고 새로운 파라다임을 제시한다.

자바 웹스타트의 장점을 요약하면 다음과 같다.

1. 바탕화면이나 웹화면의 아이큰을 단 한번 클릭함으로서 자동 다운로드 및 캐쉬를 사용한
   응용 프로그램이 구동되어 빠르고 쉽게 자바 응용 프로그램을 실행할 수 있다.

2. Linux, Mac, Windows 등 다양한 플랫폼에서 동일한 User Interface로 실행됨으로 
   OS로 부터 독립적인 환경을 제공한다.

3. 웹 환경의 단순한 HTML 및 JavaScript로 구현되던 User Interface를 보다 강력한 자바
  UI 기술인 AWT/Swing을 이용하여 구현할 수 있다.

4. 응용프로그램이 자동적으로 캐쉬되고 업데이트 됨으로 첫번째 구동 이후에는 매우 
  빠른 속도록 응용프로그램이 시작된다.

5. Java 2의 보안 모델 Signed Application 및 "sand box" 모델이 적용됨으로
  사용자들은 보다 안전한 환경에서 강력한 UI를 사용할 수 있다.


자바 웹스타트를 좀더 잘 이해 하기 위하여는 Client/Server 아키텍쳐 맥락에서 Client를
크게 Thin Client (가벼운 클라이언트) 와 Rich Client(강력한 기능의 클라이언트)로 나누어 보고
이 둘의 장단점을 비교해보면 서로의 성격을 좀더 확실히 이해할 수 있다.

Thin Client란 Web Browser와 같이 이미 사용자 환경에 Client가 설치되어 있고, 서버로부터
가벼운 HTML과 같은 화면 정보를 받아 화면을 매번 그려주는 체계를 말한다.

- 가벼운 클라이언트 환경 (web browser) 장점

    . Browser 만 있으면 언제 어디서나 가볍게 화면을 접속하여 볼 수 있다.
    . 화면 자체를 간단히 HTML 로 구성할 수 있다.
    . Hyper link 를 이용하여 화면 Navigation을 할 수 있다.
    . SessionLess 처리를 이용하여 짧은 트렌젝션을 많이 처리하는데 적합하다.
    . 문서 등을 보여주는 (Browsing) 하는데 효과적이다.

- 가벼운 클라이언트 환경 (web browser) 단점

    . HTML의 한계 때문에 복잡한  UI 또는 기능성 있는 입출력 구조를 처리하기 어렵다.
    . JavaScript 나 Dynamic HTML은 Language 자체가 유동성이 많고
      환경 오류의 가능성이 높다.
    . JavaScript 또는 Dynamic HTML은 완전한 Language 또는 객체 지향적 Language가
      아니라 유지 보수성 및 생산성이 떨어진다.
    . 전송된 자료가 쉽게 Browser를 통하여 노출되기 때문에 보안성이 떨어진다.
    . HTML의 한계 때문에 다량의 자료 처리가 쉽지 않다.
    . 옵션 및 환경이 Browser에 의존적이기 때문에 사용자 환경마다 처리될 때도 있고
      처리가 않 될 수도 있다.
    . 트렌젝션 마다 Image + HTML + Data 가 함께 내려 옮으로써 처리 속도가 저하된다.


Rich Client(강력한 기능의 클라이언트) 란 기존의 C/S 시스템 또는 전용 Browser 나 
Windows 프로그램과 같이 특정 업무에 맞도록 화면을 개발하고, 메뉴, 아이콘, 테이블, 트리,
드래그드랍등 다양하고 강력한 사용자 화면과 인터페이스를 구성하는 것이다. 

Browser와 같이 이미지 디자인 같은 작업이 별도로 필요 없는 대신에 Excel 처럼 필드 위주의
강력한 기능을 구현할 수 있는 것이 특징이다.


- Rich Client의 장점

    . 강력하고 기능성 있는 GUI 를 제공한다. (Spread Sheet, DeskTop, Tree, Drag and Drop 등등)
    . Browser에 의존하지 않고 개인 PC의 화면에서 바로 실행 가능하다.
    . Remote 환경 뿐만 아니라 Local 환경, DB, Network 처리, File 처리 등이 자유롭다.
    . 서버와 데이타 만 주고 받기 때문에 속도가 빠르다.
    . 다량의 데이타를 local 과 서버 작업으로 분산 처리가 가능하기 때문에 다량 처리에 적합하다.
    . Browser 의존적이 아니기 때문에 PC 자체의 환경 문제가 적다.
    . 객체 지향적 모델로 GUI를 만들기 때문에 유지 보수성이 뛰어 나다.
    . 전송되는 자료가 쉽게 노출되지 않으므로 보안성이 우수하다.

- Rich 클라이언트의 단점
   
    . 프로그램 설치가 필요하여 초심자가 설치할 시에 오류가능성이 있다.
    . 설치된 프로그램이 다른 요인으로 손상될 수 있다.
    . 반드시 모듈이 설치된 PC에서만 작동한다.
    . 프로그램 변경 또는 Version Up이 발생하면 다시 배포해야 한다. (Version 문제 가능성)
    . 대부분 MicroSoft Windows 환경이기 때문에 Windows 버젼 및 환경 때문에 
      오류 발생 가능성이 있다.


Java Web Start는 Thin Client와 Rich Client의 장점을 동시에 수용하고 각각의 단점을 서로
보안할 수 있도록 통합시킨 기술이다.

  Java Web Start 기술을 채용하면 Thin Client 처럼 어디서나 쉽게 접근하여 가볍게 사용할 수
있을 뿐만 아니라 Rich Client와 같은 강력한 기능을 구현할 수 있어 웹 기술과 C/S 기술을
동시에 채용하여 비용 대비 성능이 가장 우수하다.

 - Java Web Start 는 Thin Client 의 장점과 Rich Client의 장점을 통합한 기술이다.

      . web 환경과 같이 언제 어디에서든 손쉽게 사용할 수 있다. (아이콘 클릭으로 시작)
      . plug-in 이 자동으로 설치되며 일단 설치되면 다시 설치할 필요 없다. (웹으로 시작 가능)
      . Application 모듈이 local 에 cache 되어 자동으로 버젼을 체크하여,
        download 시간이 대폭 축소된다.
      . C/S 환경이 갖고있는  강력하고 다양한 GUI 기술이 Swing을 통하여 구현되며, 
        독립 데스크 탑 환경으로 갈 수 있다.
      . Web Version 이나 Client Server 버전을 따로 개발할 필요가 없다. Web Start 가
        두개의 환경을 단일 코드로 지원한다.
      . JavaScript, DHTML 등에 의존하지 않고, 독립환경의 단일 기술로 이루어짐으로서
        오류 가능성이 적다.
      . 결국 손쉽게 접근할 수 있는 Thin Client 의 장점과 강력한 GUI 및 작업 기능의 
        Rich Client 의 장점을 동시에 구현하는 것이다.


Java Web Start 환경적 특징을 살펴보면 다음과 같다.

- Java Web Start 환경은 새로운 기술이라기 보다는 Java 기술의 최종 완성된 형태이다.

- Web Version 이나 Client Server 버전을 따로 개발할 필요가 없다.

- JVM Version 1.4는 자동으로 Install 된다.

- XML, Security, Messaging 등 최신 기술 Component 가 같이 포함되어 있다. (미래가 있다.)

- Web Start 는 자동으로 Caching 을 하여 빠르다.

- Web Browser 나 MS Windows 환경에 독립적이다.

- 따로 Desktop 환경을 구성할 수 있다.


II. 웹스타트 실행하기
-----------------------

  자바 웹스타트가 어떤것인가를 가장 직관적으로 알 수 있는 방법은 실제로
웹스타트로된 시스템을 실행해보는 것이다.  웹스타트는 앞에서도 설명했듯이
언제 어느 곳에서나 쉽게 실행해 볼 수 있다는 장점을 가지고 있다.  따라서
여러분들이 지금 일반적인 네트웍 환경과 PC 환경을 가지고 있다면 직접 
웹스타트 시스템을 실행해 보는 것이 자바 웹스타트를 가장 빨리 이해하는
지름길 이다.

  본 강좌에서는 자바 웹스타트로 된 시스템을 실행해보기 위하여 JComtech에서
개발한 "JDMS"를 예제로 하겠다.  JDMS는 통신 판매(Direct Marketing) 회사들을
위한 판매관리 ASP 시스템으로, 현재 몇개 회사가 JDMS를 공동으로 사용하고 있다.
상용 시스템임으로 테스트 사용시 무리한 트렌젝션을 발생시키지 않기를 바란다.

  JDMS에 접근하려면 우선 웹브라우져에서 http://jdms.jcomtech.co.kr로 가서
홈페이지를 방문한다.  홈페이지에는 JDMS에 관련된 소개 및 사용법, 사용자 게시판
으로 구성되어 있다. 이러한 내용들은 업무관련 내용임으로 간단히 읽고 참고하기
바란다.

  자바 웹스타트로 JDMS를 실행하려면 홈페이지 좌측 상단에서 "Application"이라는
메뉴를 찾아 클릭한다.  이 메뉴를 클릭하면 우선 사용자 브라우져에 JRE 1.4 버젼이
설치되어 있는가를 검사한다.  만일 설치가 되있지 않다면 자동으로 Plug-in을 설치
하도록 되어 있다.  만일 JRE 1.4를 설치하겠냐고 묻는 화면이 나오면 "예"를 눌러
설치하도록 한다. 설치하는데는 다소 시간이 걸린다. 하지면 처음 실행시 한번만
실행하면 된다.

  자바 웹스타트는 자바 1.2 버젼부터 소개되었지만  1.4 버젼에서는 설치 및 운용이 
자동으로 되기 때문에 더욱편리하다.  따라서 본 강좌는 자바 1.4 버젼을 전제로
진행 하겠다.

  JRE 1.4가 모두 설치되었다면 브라우져 화면에 자바 로고와 "JDMS Web start [시작]"
라는 메뉴가 나온다.  이를 클릭하면 자바 웹스타트 로고가 나오면서 프로그램이
설치되어 있는 웹서버로부터 자바 클레스 화일들을 다운로드 받는 화면들이 진행된다.
처음 실행할 때는 웹서버로부터 JDMS를 실행하기위하여 필요한 모든 화일들을 다운로드
받겠지만 다음번 부터는 버젼이 변경된 부분만 다운로드 받기 때문에 속도가 매우
빨라진다.

  자바 프로그램 다운로드가 성공적으로 이루어지면 즉시 프로그램이 실행되면서 JDMS
로그인 화면이 나오게 된다.  로그인 아이디와 암호는 jcom/jcom으로 한다.  아이디와
암호가 맞으면 스윙으로 구성되 단독 윈도우가 실행되며, 드디어 JDMS 시스템 화면이
나오게 된다.  화면에 있는 메뉴들을 한번씩 실행해보며 시스템이 어떻게 실행되는지
경험해보기 바란다. 구체적인 JDMS은 업무 구성이나 사용법은 본 강좌에서 생략하도록 
한다.  

  JDMS를 한번 둘러보았다면 화면을 종료하고 브라우져도 닫기 바란다.  그러한 후에
여러분 PC에서 윈도우 바탕화면에서 Java Web Start 아이콘을 찾아보기 바란다.
정상적인 경우라면 JRE 1.4를 설치하면서 바탕화면에 "Java Web Start" 아이콘을 만든다.
이 아이콘은 웹스타트 설치를 관리할 수 있는 "Java Web Start 응용프로그램 관리자"를
실행하는 아이콘이다.

  Java Web Start 응용프로그램 관리자에는 PC에서 실행된 적이 있는 응용프로그램과
SUN에서 제공하는 몇가지 예제 프로그램을 실행할 수 있는 아이콘이 있어 웹브라우져를
통하지 않고도 직접 자바 프로그램을 실행할 수 있다.  웹브라우져에서 실행한 적이 
있는 JDMS로 아이콘으로 직접 실행할 수 있을 것이다.  Java Web Start 응용프로그램
관리자를 이용하지 않고 직접 JDMS를 바탕화면에 등록하여 사용할 수도 있는데 이는
프로그램 실행시 등록 여부를 물어서 아이콘 등록을 하게된다.

  이렇게 JRE 1.4 Plug-in을 설치하고 JDMS를 실행하였다면, 여러분은 자바 응용프로그램
시스템인 JDMS를 웹페이지에서도 실행할 수 있고, 직접 바탕화면에서도 실행할 수 있게
된다.  그러고 처음 실행할 때문 프로그램 다운로드 시간이 걸리겠지만 두번째 부터는
프로그램이 빠르게 실행된다는 것을 느낄 수 있을 것이다.  프로그램 Caching 및 버젼
관리는 웝스타트 환경이 자동으로 해주기 때문이다.

  JDMS를 다시 웹에서 실행해보고 또 다시 바탕화면에서 번갈아 실행해봄으로서 사용자들이
어떠한 환경에서 사용하게되고 어떠한 장접이 있는지 직관적으로 알게될것이다.

  개발자 입장에서 이러한 환경을 잘 분석해보면 응용 프로그램은 자바가 제공하는 AWT 및
Swing으로 만든 윈도우 환경이라는 것을 알게될 것이다. 결국 웹스타트 환경에서 응용
프로그램을 잘 개발하려면 자바 순수 프로그램 및 자바 UI 프로그램(AWT/Swing)기술이 
기본적으로 있어야 하면 웹스타트의 서버 환경은 웹서버 설치후 자바 클레스를 묶은 jar
화일및 jnlp 프로토콜 mine 타입을 지정하면 된다. 이러한 서버 설정 방법은 이 후에 
다루도록 한다.


III. 간단한 프로그램 만들기 
------------------------------
자바 웹스타트 환경을 만들려면 우선 그 환경에서 실행될 응용 프로그램이 필요하다. 
비교적 간단한 프로그램으로 자바 웹스타트의 다양한 측면을 테스트 하기 위하여 자바 Swing으로 
간단한 편집기를 만들어 보겠다. 간단한 편집기의 클래스 명은 SimpleEditor로서 다음과 같은 
화면으로 구성되어 있다.
  
[소스보기 - 파일첨부 참조] 소스를 살펴보면 우선 buildGUI() 메소드에서 
메뉴와 화면을 만들며 그리고 이벤트 처리를 위한 리슨너 등을 추가하고 있다. 
주요 기능 및 메소드를 살펴보면 다음과 같다. 
- buildGUI() 화면의 필드와 메뉴를 만들며, 이벤트 처리가 가능하도록 각각의 컴퍼넌트에 
ActionListener 및 DocumentListener를 추가하고 있다. 
- checkUpdate() 테스트 필드에 변경이 있으면 이를 화일에 저장할지를 묻고, 
사용자가 "예"를 선택하면 이를 화일에 저장한다. 
그리고 "취소" 버튼을 누르면 취소를 true 값으로 리턴한다. 
- newFile() 화면을 clear하고 상태를 초기 상태로 바꾸어 새로운 화일을 편집할 수 있도록 한다. 
- open() FileChooser를 이용하여 기존의 테스트 화일을 열고 이를 테스트 필드에 보여주는 
기능을 한다.
- close() 편집하던 화일을 닫는 기능을 수행한다. 
- save() 편집하던 화일을 저장하는 기능을 수행한다. 만일 화일명이 없으면 FileChooser를 
이용하여 화일명을 지정하도록 한다.
- exit() 편집하던 화일이 있으면 저장하고 편집기 프로그램을 종료한다. 
 위의 메소드들은 각각의 메뉴 선택시에 actionPerformed 메소드에서 실행되면 
 
현재 화일 문서가 변경되었는가의 여부는 DocumentListener를 사용하여 이벤트 처리한다. 
이 프로그램을 실행하려면 SimpleEditor.class 화일을 JRE가 설치된 PC에 복사하고 
단지 "Java -classpath . SimpleEditor" 라는 명령어를 치면 실행되어 아무런 문제 없이
 Local Computing 환경에서 사용할 수 있다. 하지만 다음 문제는 이 프로그램을 여러사람이
 자바 웹스타트를 이용하여 네트웍 환경으로 여러사람이 사용할 수 있게하는 것이다. 
이러한 환경은 응용 프로그램에 추가적인 고려 사항이 필요하며 또 자동 배포를 수행할 수 있는
 웹 서버가 필요한데 자세한 내용은 본 SimpleEditor 프로그램을 예제로 다음 장에 계속하여
 설명하겠다. 

 

 

IV. 웹스타트 응용프로그램
-------------------------


기본적으로 웹스타트 응용프로그램은 SimpleEditor에서 보았듯이 Java2 개발 방법을 사용하며 public static void mina(String[] arg)의 메인 함수에 의하여 실행된다.  하지만 이러한 응용프로그램을 웹스타트와 같이 자동 다운로드 환경에서 사용하려면 보안 및 공유 환경때문에 다음과 같이 몇가지 고려해야 할 사항이 있다.

- 자바 class화일들은 다운로드가 용이할 수 있도록 모두 JAR 화일로 묶어야 한다.

- 그림 화일이나 추가적인 화일 및 자원을 사용한다면 이 모든 것을 JAR로 묶어야 한다.

- SandBox 보안 모델을 사용함으로 다음과 같이 Local 시스템 자원을 사용하지 못한다.

  . local disk 및 native libarary를 사용하지 못한다.
  . Network 자원은 자신이 download된 서버로부터 뿐이 사용하지 못한다.
  . Security Manager을 설치할 수 없으며, 시스테 프로퍼티를 제한적으로 사용한다.

- 이러한 보안상 제약점을 넘으려면 signed JAR 화일을 사용해야 한다.



그럼 SimpleEditor.class를 이러한 사항을 고려하여 배포 준비를 해보도록한다.

우선 SimpleEditor.class를 다음과 같은 JAR 커멘드를 이용하여 .jar 화일로 묶는다.

-----------------------------------
prompt> jar cvf editor.jar *.class
-----------------------------------

이렇게 하면 배포할 수 있는 editor.jar 화일이 생성된다. 만일 클레스 화일 또는 이미지,
리소스 등 화일이 여러개라면 jar 커멘드를 이용하여 같은 화일에 묶도록한다.

SimpleEditor 프로그램은 화일을 읽거나 저장하기 위하여 local 디스크를 접근한다.
하지만 일반 jar 화일로는 SandBox 보안 모델 때문에 local 디스크를 접근할 수 없다.
따라서 local disk에 접근할 수 있는 권한을 부여하기 위하여 다음과 같이 jar 화일에 keytool 및 jarsigner를 이용하여 인증서 sign을 할 필요가 있다. (keytool 및 jarsigner는 JDK1.2 이상 버젼에 포함되어있는 툴이다.)


1. 다음과 같이 keytool 커멘드를 이용하여 teststore 화일에 test 라는 별명으로 새로운 키를 만든다.

----------------------------------------------------------
prompt> keytool -genkey -keystore teststore -alias test

keystore 암호를 입력하십시오:  test12345
이름과 성을 입력하십시오.
  [Unknown]:  editor
조직 단위 이름을 입력하십시오.
  [Unknown]:  editor
조직 이름을 입력하십시오.
  [Unknown]:  editor
구/군/시 이름을 입력하십시오?
  [Unknown]:  editor
시/도 이름을 입력하십시오.
  [Unknown]:  editor
이 조직의 두 자리 국가 코드를 입력하십시오.
  [Unknown]:  editor
CN=editor, OU=editor, O=editor, L=editor, ST=editor, C=editor이(가) 맞습니까?
  [아니오]:  y

<test>에 대한 키 암호를 입력하십시오
        (keystore 암호와 같은 경우 RETURN을 누르십시오):  test
키 암호가 너무 짧습니다. 여섯 글자 이상이어야 합니다.
<test>에 대한 키 암호를 입력하십시오
        (keystore 암호와 같은 경우 RETURN을 누르십시오):  test12345
--------------------------------------------------------------------


2. 생성된 keystore 화일과 그 화일에 생성된 키를 이용하여 자체 signed 인증서를 생성한다

----------------------------------------------------------
prompt> keytool -selfcert -alias test -keystore teststore

keystore 암호를 입력하십시오: test12345
----------------------------------------------------------


3. keystore 화일 teststore에 저장된 키 정보를 보려면 다음과 같이 명령을 주고 암호를 입력한다.

------------------------------------------
prompt> keytool -list -keystore teststore
------------------------------------------


4. 최종적으로 만들어진 인증서를 이용하여 다음과 같이 jarsigner를 이용하여 jar 화일에 sign한다.
   암호를 물으면 teststore 생성시 사용된 암호를 입력한다.

------------------------------------------------------
prompt> jarsigner -keystore teststore editor.jar test
------------------------------------------------------


이렇게서 만들어진 signed-jar 화일인 editor.jar는 최종적으로 자바웹스타트를 통해서 배포할 수 있는 화일이다.  웹스타트를 통하여 배포되는 화일이 local 자원이나 network 자원을 이용하려면 모두 이렇게 signed-jar를 만들어야 한다.

이렇게 JAR화일을 준비하면 응용프로그램은 배포 준비가 완료된 것이다.  하지만 이렇게 응용 프로그램만 준비한다고 해서 모든 과정이 끝난 것은 아니다.  

우선 이 프로그램이 어떤 것이고 어떻게 배포될지를 기술하는 XML 문법으로 이루어진 jnlp 화일이 필요하고, jar와 jnlp을 적절이 설정된 웹서버에 올리는 과정이 필요한다.

이러한 내용은 다음 장에서 다루도록 한다.




V. JNLP 화일 및 웹서버 설정 
-----------------------------

JNLP(Java Network Launching Protocol) 화일은 자바 웹스타트에서 자동 배포될 모듈에대한
정보 및 어떻게 배포될 것인 가에 대한 정보를 기술해주는 화일로 웹서버에서는 우선 이
화일부터 호출 되고 이 화일에 기술된 대로 JAR 화일을 다운로드하여 프로그램이 실행되도록
되어 있다.

JNLP 화일은 기본적으로 XML 문법 체계를 가지고 있으며, 모든 테그에 대한 정보를 자세히 
보려면 SUN site에서 JNLP Spec v1.0을 참고하도록 한다.  여기서는 JNLP에대한 기본적인 사항을
예제 위주로 살펴보겠다.

다음은 SimpleEditor.jar를 배포할 수 있도록 만들어진 simple.jnlp 화일의 내용이다.


================================= 
file : simple.jnlp
================================= 

<?xml version="1.0" encoding="utf-8" ?>
<jnlp spec="1.0+" codebase="http://www.javanuri.com/jaws/apps" href="simple.jnlp">

  <information>
    <title>Simple Editor Demo</title>
    <vendor>JavaNuri</vendor>
    <description>Simple Editor Demo Program</description>
    <description kind="short"> Simple Editor</description>
    <icon href="images/editor.jpg" />
    <offline-allowed />
  </information>
  
  <security>
    <all-permissions />
  </security>

  <resources>
    <j2se version="1.4" />
    <jar href="editor.jar" />
  </resources>

  <application-desc main-class="SimpleEditor" />

</jnlp>


본 simple.jnlp 예제에서 첫라인은 본 문서가 XML이고 encoding이 utf8로 되어 있음을 
기술하는 것이고 다음부터 나오는 JNLP 주요 태그 내용은 다음과 같다. 

- jnlp tag : <jnlp spec="1.0+" codebase="http://www.javanuri.com/jaws/apps" href="simple.jnlp">
  본 내용이 jnlp에 관련된 내용임을 기술하며 문서의 끝에 </jnlp>로 닫힌다.

  . spec 속성은 jnlp 1.0 버젼 이상임을 정의한다.
  . codebase 속성은 본 문서의 모든 화일의 기본 URL을 정의한다.
  . href 는 본문서 화일 이름을 정의한다.

- information tag : <information> ... </information>
  각종 하위 태그로 본 프로그램에 관한 제목, 제작사, 설명, 아이콘 등을 기술하고 있으며, 
  이는 배포 및 실행될 때 사용자가 화면을 통해 볼 수 있는 아이콘 및 설명으로 사용된다.

- security tag : <security> ... </security>
  이 태그는 프로그램에 실행될때 주는 local 자원에 대한 권한을 기술한다.

- resource tag : <resource>... </resource>
  이 태그는 하위 태그를 이용하여 j2se 버젼 및 사용되는 jar 화일들을 기술하여
  실행환경에 대한 정보를 담고 있다.

- application-desc tag :   <application-desc main-class="SimpleEditor" />
  이 태그는 플로그램 실행시 필요한 사항을 기술하며 특히 main-class 는 반드시
  지정되어야할 속성으로 자바 프로그램의 main 메소드가 있는 클래스명을 써준다.



여러분이 여기까지해서 JAR 및 JNLP 화일을 만들었다면 이를 적절하게 배포해줄
웹서버 환경이 필요하다.  자바 웹스타트는 기본적으로 HTTP 프로토콜을 사용하며 클래스가
실려있는 JAR화일 및 이 내용을 기술하고 있는 JNLP화일은 결국 웹서버의 서비스 디렉토리에
복사되어야 한다.


웹서버는 다양한 종류가 있으며, 기본적인 환경설정은 해당 웹서버 제품의 메뉴얼에 
따라야할 것이다.  

본 강좌에서는 가장 많이 쓰이고 있는 Apache 웹서버를 예제로하여 환경을 설정할 것이다. 
다른 웹서버도 크게 차이는 없으리라 생각된다. 

따라서 www.apache.org 사이트를 방문하여 windows, linux, solaris 등 여러분의 환경에 맞는 
apache웹서버를 다운로드하여 메뉴얼이 지시하는데로 설치하도록 한다. 
본 강좌에서는 웹서버 설치에 관해서 다루지는 않겠다.


웹서버를 설치한 후에 가장 먼저 해주어야 할일은 jnlp 확장자 화일에 관한 mime type의
설정이다.  

mime type의 설정은 웹서버로 하여금 jnlp 확정자를 갖은 화일은 어떠한 컨테츠로 취급할 
것인지를 알려준다. 
아파치 웹서버의 경우는 설정 디렉토리에서 mime.types 화일에 다음과 같이 한 라인만 
추가해 주면 된다.

   application/x-java-jnlp-file JNLP


추가한 후에는 반드시 웹서버를 다시 시작해야 한다.

이렇게 웹서버 설정이 끝났다면 지금까지 만들었던 jar 화일과 jnlp 화일을 웹서버의 
document root 에 적절히 복사해놓는 것이다. 이것은 마치 웹서버에 html 화일을 복사해놓는
것과 마찬가지이다. jar 및 jnlp 화일과 함께 복사하여야 할 것은 simple.jnlp에서 
지정한 아이콘 화일인 images/editor.gif이다. (첨부 File 이용)

아래에 첨부된 file을 다운로드하여 images 디렉토리에 복사하기 바란다.

simple.jnlp 화일은 경우는 반드시 jnlp 테스의 codebase 속성에 기술되어 있는 주소를
반드시 여러분의 웹서버 설치 주소로 변경해야한다. 그렇지 않으면 다운로드시 오류가 발생할 것이다.

여기까지 하면 SimpleEditor를 자바 웹스타트를 이용하여 실행할 준비가 모두 끝난 것이다.
실제 실행 및 실행시 유의 사항등은 다음 장에서 다루도록 한다.




VI. 예제프로그램 웹스타트로 실행하기 
---------------------------------------

 
웹스타트 응용프로그램 개발 과정을 다시한번 정리해보면 다음과 같다.

1. 실행될 응용프로그램을 AWT/Swing으로 개발한다.
2. 개발된 응용프로그램을 배포할 수 있도록 jar로 묶는다.
3. Local 자원을 사용할 수 있도록 jar에 sign을 하여 signed-jar를 만든다.
4. 배포 실행될 응용프로그램에 대한 내용을 jnlp 화일을 만들어 xml로 기술한다.
5. web 서버에 JNLP mime type을 설정한다.
6. web 서버의 적절한 디렉토리에 singed-jar, jnlp 화일 및 이미지 화일을 복사한다.

여기까지의 사항이 완료되었다면 일단 웹브라우져에서 웹스타트 응용프로그램을
실행할 수 있는 웹서버 환경이 갖추어진 것이다.

 실제 예제로 만들어진 editor.jar 와 simple.jnlp를 브라우져에서 실행하려면
웹서버를 실행하고 단순히 다음과 같이 simple.jnlp를 호출하는 URL을 치면 된다.

 http://www.javanuri.com/jaws/apps/simple.jnlp

  브라우져에 jre1.4가 정상적으로 설치되었으면 먼저 "Web Start Logo"화면이 뜨고
보안을 위해 jar에 sign된 내용이 나타나면서 승인 여부를 묻는 화면이 나온다.
여기에서 "예"를 선택하면 즉시 editor 프로그램이 실행될 것이다.
그리고 두번째로 웹에서 실행할 때는 바탕화면에 아이콘 생성 여부를 물을 것이다.
여기에서 "예"를 선택하면 바탕화면에 아이콘이 생성되면서 다음 부터는 웹브라우져를
통하지 않고 바탕에 있는 아이콘을 단순히 클릭함으로써 응용프로그램이 시작될 것이다.

  이제까지 만들어왔던 SimpleEditor 예제에 대한 관련 jar, jnlp, jpg 화일들은
example.zip에 묶어 첨부되어 있다. 이를 다운로드 받아 압축을 풀고 웹서버에 설치하고
실행시켜보기 바란다. 단 실행시 jnlp화일에 있는 codeBase 속성은 여러분의 웹서버 주소에
맞도록 수정해야 한다.

  서버 설정은 이제까지 설정한데로 하면 보통은 이상없이 환경이 설정된다. 하지만
클라이언트 즉 브라우져 환경은 사용자에 따라 웹스타트가 설정되어 있을 수도 있고, 아닐수
도 있다. 따라서 이러한 브라우져 환경을 통제하려면 html 및 javascript를 이용할 필요가
있다.
    
  사용자 브라우져를 체크해 웹스타트를 실행할 수 있는 방법은 여러가지가 있을 수 있지만
대체로 다음 세가지 방법이 있다.
    
1. 자바스크립트를 이용해 웹스타트 설치여부를 체크한 다음 설치돼지 않았을 경우
   설치하라는 메시지 링크를 주는 방법
   
2. 웹스타트 버젼1.2의 자동 설치 기능을 이용하는 방법
    
3. 간단한 JRE1.4를 이용한 Applet을 이용하는 방법
    

  첫번째 방법인 자바웹스타트 설치여부를 체크하는 방법은 우선 웹브라우져 종류를 파악해야 하는데,
이방법은 "Java Web Start Developers's Guide"에서 사용한 자바스크립트인 xbDetectBrowser()를 사용하여
할 수 있다. xbDetectBroser.js는 본 글에 첨부 하였다.
    
  다음 예제는 "Java Web Start Developers's Guide"에 나오는 예제를 응용하여 이해하기 쉽게 변경한
것으로서 스크립트를 실행시키면 Java Web Start 가 설치되었을 경우는 해당 프로그램을 실행할 수 있는
URL 링크가 나오고, 설치되어 있지 않을 경우는 Java Web Start를 설치하라는 메시지가 나오는 예제이다.

Simple1.html
=====================================================================================================
<HTML>
<HEAD><TITLE>Java Web Start Page</title>
<!----------------------------------------------------------------
  xbDetectBrowser() 함수를 이용하여 브라우져 환경에 관한 정보 생성
------------------------------------------------------------------>
<SCRIPT LANGUAGE="JavaScript" SRC="xbDetectBrowser.js"></SCRIPT>

<!----------------------------------------------------------------
  Web Start 설치 여부, 브라우져가 IE 인지 여부 설정 
------------------------------------------------------------------>
<SCRIPT LANGUAGE="JavaScript"> 

    var javawsInstalled = 0; 
    isIE = "false"; 

    if (navigator.mimeTypes && navigator.mimeTypes.length) { 
       x = navigator.mimeTypes['application/x-java-jnlp-file']; 
       if (x) javawsInstalled = 1; 
    } else { 
       isIE = "true"; 
    } 

</SCRIPT>

<!----------------------------------------------------------------
  브라우져가 IE일 경우 Web Start 설치 여부 결정하는 VBScript
------------------------------------------------------------------>

<SCRIPT LANGUAGE="VBScript"> 
    on error resume next 
    If isIE = "true" Then 
      If Not(IsObject(CreateObject("JavaWebStart.IsInstalled"))) Then 
            javawsInstalled = 0 
      Else 
            javawsInstalled = 1 
      End If 
    End If 
</SCRIPT>

<!----------------------------------------------------------------
 insertLink 함수 정의 :
   웹스타트가 설치되었을 경우는 응용 프로그램 실행 링크를 보여주고
   그렇지 않을 경우는 설치 필요 메시지 보여줌
  (설치 필요 메시지를 수정하여 download URL 링크로 만들 수 있음)
------------------------------------------------------------------>

<SCRIPT LANGUAGE="JavaScript"> 
    function insertLink(url, name) { 
    if (javawsInstalled || navigator.family == 'gecko') { 
          document.write("<a href=" + url + ">"  + name + "</a>"); 
       } else { 
          document.write("Java Web Start 설치가 필요합니다."); 
       } 
    } 
</SCRIPT>

<!----------------------------------------------------------------
  웹스타트 응용프로그램 실행 링크를 insertLink 함수를 통하여 만듬
------------------------------------------------------------------>

<SCRIPT LANGUAGE="Javascript"> 
    insertLink("http://www.javanuri.com/jaws/apps/simple.jnlp", "웹스타트 응용프로그램 시작"); 
</SCRIPT>

<BODY>    
</BODY>
</HTML>
    
=====================================================================================================
    
 두번째 방법인 자동 설치 기능을 이용하는 방법은 썬의 자바 사이트에서 제공하는 Web Start자동 설치
기능을 수행하는 ActiveX를 호출하는 것이다. Java Web Start만을 자동 설치하는 OBJECT 테그 및 URL은

<OBJECT CODEBASE="http://java.sun.com/products/javawebstart/autodl/jinstall_javaws-1_2-windows-i586.cab" 
CLASSID="clsid:5852F5ED-8BF4-11D4-A245-0080C6F74284" HEIGHT=0 WIDTH=0>
이며

자바 1.4 버전을 자동 설치할 수 있는 OBJECT 테그 및 URL은

<OBJECT CODEBASE="http://java.sun.com/products/plugin/autodl/jinstall-1_4_1-windows-i586.cab" 
CLASSID="clsid:5852F5ED-8BF4-11D4-A245-0080C6F74284" HEIGHT=0 WIDTH=0>

이다.

  이왕 자동 설치 기능을 사용하려면 web start만 설치하는 것 보다는 응용프로그램 환경이 더 좋은
자바 1.4버전을 설치하는 것이 좋다.

  다음 예제는 위의 예제와 같이 기존에 web start설치 여부를 체크하고 없으면 자동설치 되도록 자바 
스크립트 및 HTML을 작성 예를 보여주고 있다. ( Developer's Guide 참조 )

Simple2.html
=====================================================================================================

<!----------------------------------------------------------------
  브라우져가 Netscape일때 web start 설치 여부를 설정한다.
------------------------------------------------------------------>

<SCRIPT LANGUAGE="JavaScript">
var javawsInstalled = 0;
var javaws12Installed = 0;

isIE = "false";

if (navigator.mimeTypes && navigator.mimeTypes.length) {
  x = navigator.mimeTypes['application/x-java-jnlp-file'];
  if (x) {
     javawsInstalled = 1;
     javaws12Installed=1;
  }
} else { 
  isIE = "true";
}
</SCRIPT>

<!----------------------------------------------------------------
  브라우져가 IE일때 web start 설치 여부를 설정한다.
------------------------------------------------------------------>

<SCRIPT LANGUAGE="VBScript">
on error resume next
If isIE = "true" Then
  If Not(IsObject(CreateObject("JavaWebStart.isInstalled"))) Then
     javawsInstalled = 0
  Else
     javawsInstalled = 1
  End If
  If Not(IsObject(CreateObject("JavaWebStart.isInstalled.2"))) Then
     javaws12Installed = 0
  Else
     javaws12Installed = 1
  End If
End If
</SCRIPT>

<!----------------------------------------------------------------
  xbDetectBrowser.js를 이용하여 navigator.family를 설정한다.
------------------------------------------------------------------>
<SCRIPT LANGUAGE="JavaScript"
SRC="xbDetectBrowser.js">
</SCRIPT>

<!----------------------------------------------------------------
  gecko 방식이거나 이미 web start가 설치된 경우는 
  http://www.yyy.zzz/app.jnlp를 실행할 수있는 link를 보여준다.

  그렇지 않다면
  썬에서 제공하는 PluginBrowserCheck를 실행시킬수 있는 link를 제공한다.
  PluginBrowserCheck는 브라우져가 IE일 경우 "pass"파라미터에 있는
  자동 설치 URL인 http://www.yyy.zzz/download.html로 가고 아닐경우는
  플랫폼에 따라 설치 안내문이 나오도록 "fail"파라미터에 있는
  http://java.sun.com/cgi-bin/javawebstart-platform.sh로 간다.

------------------------------------------------------------------>

<SCRIPT LANGUAGE="JavaScript">
if (javawsInstalled ||  navigator.family == 'gecko') {
    document.write("<a href=http://www.yyy.zzz/app.jnlp>프로그램 실행</a>");
} else {

  document.write("<a href=http://dlres.java.sun.com/PluginBrowserCheck?");
  document.write("pass=http://www.yyy.zzz/download.html&");
  document.write("fail=http://java.sun.com/cgi-bin/javawebstart-platform.sh>");
  document.write("설치 및 응용프로그램 실행</a>");

}
</SCRIPT>

=====================================================================================================

download.html
=====================================================================================================
<!----------------------------------------------------------------
웹스타트 자동 설치 ActiveX프로그램을 호출하고 설치후에는 
http://www.yyy.zzz/app.jnlp로 링크되도록 한다.
만일 이미 설치돼었다면 곧바로 웹스타트가 실행된다.
------------------------------------------------------------------>

<HTML>
<BODY>
<OBJECT CODEBASE="http://java.sun.com/products/javawebstart/autodl/jinstall_javaws-1_2-windows-i586.cab" 
CLASSID="clsid:5852F5ED-8BF4-11D4-A245-0080C6F74284" HEIGHT=0 WIDTH=0>
<PARAM NAME="app" VALUE="http://www.yyy.zzz/app.jnlp">
<PARAM NAME="back" VALUE="true">
<!-- Alternate HTML for browsers which cannot instantiate the object -->
<A HREF="http://java.sun.com/cgi-bin/javawebstart-platform.sh?">
Download Java Web Start</A>
</OBJECT>
</BODY>
</HTML>
=====================================================================================================

이 방법은 결국 썬에서 제공하는 설치 ActiveX를 이용하는 것이고 그렇기 때문에 IE에서만 작동한다.
그리고 설치가 않될 경우는 플랫폼에 따라 설치 메뉴얼이 있는 페이지로 이동하게 된다. 이 방법을
잘 파악하려면 다음과 같이 썬에서 제공하는 각종 URL을 잘 이해하고 있어야 한다.

- http://dlres.java.sun.com/PluginBrowserCheck 
  pass와 fail파라미터가 있어 브라우져가 IE일 경우 pass에서 준 URL로 링크되고 아닐 경우는
  fail에서 준 URL로 링크된다.  파라미터가 없을 경우 플랫폼에 따라 JRE를 설치할 수 있는
  메뉴얼 페이지로 간다.

- http://java.sun.com/cgi-bin/javawebstart-platform.sh
  플렛폼에 따라 자바 웹스타트를 설치할 수 있는 메뉴얼 페이지로 이동한다.

- <OBJECT CODEBASE="http://java.sun.com/products/javawebstart/autodl/jinstall_javaws-1_2-windows-i586.cab" 
  CLASSID="clsid:5852F5ED-8BF4-11D4-A245-0080C6F74284" HEIGHT=0 WIDTH=0>
  자바 웹스타트를 자동 다운로드 및 설치하는 ActiveX를 구동한다. "app"파라미터는 설치후 자동 링크되는
  URL 값이다.


- <OBJECT CODEBASE="http://java.sun.com/products/plugin/autodl/jinstall-1_4_1-windows-i586.cab" 
  CLASSID="clsid:5852F5ED-8BF4-11D4-A245-0080C6F74284" HEIGHT=0 WIDTH=0>
  JRE1.4를 자동 다운로드 및 설치하는 ActiveX를 구동한다. 웹스타트는 자바 1.4버젼에 포함되어 있다.


  자동 설치 및 실행의 세번째 방법은 간단하게 시작 버튼을 가진 Applet을 만들고 Applet실행을 
JRE 1.4로 할 수 있도록 요구하는 것이다.  이렇게 하면 Applet이 실행되면서 JRE 설치 버젼을
체크해 자동으로 JRE1.4가 설치되며 자바웹스타트도 자동 설치된다.
 
  Applet을 조금더 응용하면 Applet에 실행버튼을 만들어 웹스타트 응용 프로그램을 바로 실행할 수 있도록
하는 것이다. 다음은 그러한 버튼을을 갖은 예제 Applet 및 실행 HTML이다.

StartApplet.java
=====================================================================================
import java.awt.*;
import java.awt.event.*;
import java.net.*;

/**
 * StartApplet
 *
 */
 
public class StartApplet extends java.applet.Applet implements ActionListener {

    // start button
    Button btStart        = new Button("응용프로그램 시작");
    
    // url
    String url;
    
    //--------------------------------
    // init
    //--------------------------------
    public void init() {
        
        btStart.addActionListener(this);
        add(btStart);
        
        url = getParameter("url");
        
    }
    
    //----------------------------------------
    // action performed
    //----------------------------------------
    public void actionPerformed(ActionEvent e) {
    
        try {
            getAppletContext().showDocument(new URL(url));
        }catch(Exception ex) {
            ex.printStackTrace();
        }
        
    }
    
}

==============================================================================

StartApplet.html
==============================================================================

<html>
<body>

<OBJECT    name="StartApplet"  classid="clsid:8AD9C840-044E-11D1-B3E9-00805F499D93"  WIDTH=150 HEIGHT=30
  codebase="http://java.sun.com/products/plugin/autodl/jinstall-1_4_0-win.cab#Version=1,4,0,0">
  <PARAM NAME=CODE VALUE="StartApplet.class">
  <PARAM NAME="type" VALUE="application/x-java-applet;version=1.4">
  <PARAM NAME="url" VALUE="<a href=http://www.yyy.zzz/app.jnlp>">

  <COMMENT>
   <EMBED invokeURLs=false type="application/x-java-applet;jpi-version=1.4" CODE="StartApplet.class" WIDTH=150 HEIGHT=30
    pluginspage="http://java.sun.com/products/plugin/index.html#download">
    scriptable=false
     <NOEMBED invokeURLs=false></NOEMBED invokeURLs=false>
   </EMBED invokeURLs=false>
  </COMMENT>
</OBJECT>    

</body>
</html>

================================================================================

  StartApplet은 url 파라미터를 받아들이고 시작 버튼을 누르면 브라우져의 링크를 url 파라미터가
지정한데로 가도록 되어 있다. 따라서 실행하고 싶은 JNLP 파일을 url 파라미터에 주면 웹스타트가
자동 실행된다.

  StartApplet.html은 StartApplet 클레스를 자바 플러그인 형태로 실행 시키며, 자바 플러그인이
설치되어있지 않으면 자동 설치되도록 되어 있다. 위의 예제에서 <a href=http://www.yyy.zzz/app.jnlp> 부분만
원하는 URL로 바꾸어주면 자동 설치 및 웹스타트 시작페이지로 사용할 수 있을 것이다.

  이 방법은 간단한 Applet이 필요하기는 하지만 가장 간단하고 쉬운 방법이 아닌가 생각한다.



VII. Jar 자원 활용 및 JNLP API 이용
-----------------------------------


  자바웹스타트 응용 시스템을 만들다 보면 때로는 클레스와 함께 jar 화일에 묶여있는
이미지 화일이나 프로퍼티 화일, 자료 화일 등이 필요할 경우가 있다.  이러한 경우에는
java.lang.ClassLoader로 부터 해당 자원을 얻을 수 있다. 

다음은 jar화일에 묶여있는 이미지 화일로부터 Icon 객체를 생성해내는 예제이다.

ClassLoader loader = this.getClass().getClassLoader();
Icon myIcon = new ImageIcon(loader.getResource("imgs/myImg.gif"));

이 밖에도 java.lang.ClassLoader의 getResourceAsStream(String name) 메소드를 이용하면
다양한 자원을 InputStream을 통하여 얻을 수 있다.

  
  자바웹스타트 환경에서는 Java 2 SE API에서 제공하지 않는 추가적인 기능을 하는 API를
제공한다. 이것은 JNLP API라고 한다. JNLP API를 이용하여 개발할 경우는 jnlp.jar가
필요한데 이러한 파일은 JNLP Developer's Pack에 포함 되어 있다. 다음은 Developer's Pack을
다운로드 할 수 있는 URL이다.

http://java.sun.com/products/javawebstart/download-jnlp.html

  JNLP API가 추가적으로 제공하는 클레스는 javax.jnlp package로 묶여 있으며
BasicService, ClipboardService, DownloadService, FileOpenService, FileSaveService, 
PrintService, PersistenceService 등이 있는데 이들은 ServiceManager 클레스를 통하여
사용할 수 있다. 각각의 기능은 다음과 같다.

- javax.jnlp.BasicService

BasicService는 웹스타트의 환경적인 면이나 브라우져를 통제하기 위한 API를 제공하는데
자바 애플릿의 경우 AppletContext와 비슷한 역할을 한다. 다음 예제는 웹스타트 환경에서
웹브라우져로하여금 특정 URL로 가도록 하는 것이다.

import javax.jnlp.*;
.....

BasicService bs = (BasicService)ServiceManager.lookup("javax.jnlp.BasicService");
bs.showDocument(new URL("http://www.javanuri.com"));



- javax.jnlp.ClipboardService

ClipboardService는 시스템에서 사용하는 클립보드에서 복사 객체를 가져오거나 클립보드로
복사하는 서비스를 제공한다. 자바웹스타트는 이 기능을 사용할 때 보안을 위하여 경고창을
보여준다. 다음은 간단한 스트링을 클립보드에 복사하는 예제이다.

import javax.jnlp.*;
.............

ClipboardService cs = (ClipboardService)ServiceManager.lookup("javax.jnlp.ClipboardService");
StringSelection ss = new StringSelection("Hello Web Start");
cs.setContents(ss);


- javax.jnlp.DownloadService

DownloadService는 자신의 자원을 Cache에 저장, 삭제등 Cache를 통제할 수 있는 서비스 API를
제공하는 클레스이다. 다음은 myapp.jar를 Cache에서 확인하고 있으면 삭제한후 다시 Cache에
저장하는 예제이다.

import javax.jnlp.*;
...........

DownloadServicd ds = (DownloadService)ServiceManager.lookup("javax.jnlp.DownloadService");
URL url = new URL("http://www.javanuri.com/jws/myapp.jar");
boolean isCached = ds.isResourceCached(url, "1.0");
if(isCached) {
  ds.removeResource(url, "1.0");
}

DownloadServiceListener dsl = ds.getDefaultProgressWindow();
ds.loadResource(url, "1.0", dsl);


- javax.jnlp.FileOpenService

FileOpenService는 권한이 제약된 환경에서도 이를 사용자에게 알리고 화일을 열 수 있는
다이얼로그 윈도우를 열어주는 서비스이다.  다음 예제는 FileOpenService를 이용하여 화일을
여는 예제이다.

import javax.jnlp.*;
..............

FileOpenService fo = (FileOpenService)ServiceManager.lookup("javax.jnlp.FileOpenService");
FileContents fc = fo.openFileDialog(null, null);

- javax.jnlp.FileSaveService


FileSaveService는 권한이 제약된 환경에서도 local disk에 화일을 저장할 수 있는
기능을 제공하는 서비스 클레스이다. 이는 FileOpenService의 경우와 반대인 기능을 제공하는
클레스이다.  다음은 FileOpenService를 이용하여 화일을 연 후에 FileSaveService를
이용하여 화일을 저장하는 예제이다.

import javax.jnlp.*;
.....................

FileOpenService fo = (FileOpenService)ServiceManager.lookup("javax.jnlp.FileOpenService");
FileContents fc = fo.openFileDialog(null, null);
FileContents newfc = fss.saveFileDialog(null, null, fc.getInputStream(), "newfile.txt");

- javax.jnlp.PrintService

PrintService는 권한이 제약된 웹스타트 환경에서도 프린트를 가능하게 해주는 API 를 갖고 있는
서비스 클레스이다.  이 API를 이용하여 프린트를 요청하면 사용자에게 허가할 것인가를 묻는
다이얼로그가 나타난다. 다음은 PrintService를 이용한 프린트 요청 예제이다.

import javax.jnlp.*;
.....................

PrintService ps = (PrintService)ServiceManager.lookup("javax.jnlp.PrintService");

// default page format
PageFormat pf = ps.getDefaultPage();

// customizing page format
PageFormat npf = ps.showPageFormatDialog(pf);

// print
ps.print(new Doc());


// printable class
class Doc implements Printable {

 ....
 public int print(Graphics g, PageFormat fm, int idx) {
  ....
 }

}

}


- javax.jnlp.PersistenceService

PersistenceService는 브라우져의 쿠키와 마찬가지고 사용자의 클라이언트에 간단한 자료를 
저장할 때 사용된다. 저장되는 형태는 url형태로 자장된다.
다음은 간단한 url을 저장하고 내용을 읽어들이는 예제이다.


import javax.jnlp.*;
.....................

PersistenceService ps = (PersistenceService)ServiceManager.lookup("javax.jnlp.PersistenceService");

String addr = "www.javanuri.com/some.txt";
java.net.URL = new URL(addr);

// create
ps.create(url, 1024);
FileContents fc = ps.get(url);
OutputStream os = fc.getOutputStream(false);
os.write(...);

// read
fc = ps.get(url);
InputStream in = fc.getInputStream();

in.read(...);

.......


- javax.jnlp.FileContents

FileContents 는 FileOpenService, FileSaveService, PersistenceService와 같은 서비스에서 
input과 output을 처리할 수 있도록 만들어진 클레스이다. 일반적인 File 클레스와 비슷하게
생각하면 된다.  보안과 자료 저장 형태 등이 일반 File 클레스와는 다르다.



출처 : http://www.javanuri.co.kr, 유용근

2007/10/24 11:22 2007/10/24 11:22
이 글에는 트랙백을 보낼 수 없습니다
자바스크립트(JavaScript)에서는 다음의 함수들로, HTML 페이지 주소를 인코딩/디코딩합니다.

encodeURI() / decodeURI()
최소한의 문자만 인코딩합니다.
; / ? : @ & = + $ , - _ . ! ~ * ' ( ) #
이런 문자는 인코딩하지 않습니다.
http:// ... 등은 그대로 나옵니다.


encodeURIComponent() / decodeURIComponent()
알파벳과 숫자 Alphanumeric Characters 외의, 대부분의 문자를 모두 인코딩합니다.
http:// ... 가 http%3A%2F%2F 로 됩니다.



escape() / unescape()
예전부터 있던 오래된 함수입니다. encodeURI() 와 encodeURIComponent() 의 중간 정도의 범위로 문자를 인코딩합니다.


encodeURI, encodeURIComponent, escape 함수 사용 예제


<html>

<body>

<script type="text/javascript">
  var s;

  s = encodeURI('http://www.google.co.kr/소 설.html');
  document.write('<p>' + s + '<p>');
  // 출력 결과: http://www.google.co.kr/%EC%86%8C%20%EC%84%A4.html


  s = encodeURIComponent('http://www.google.co.kr/소 설.html');
  document.write('<p>' + s + '<p>');
  // 출력 결과: http%3A%2F%2Fwww.google.co.kr%2F%EC%86%8C%20%EC%84%A4.html


  s = escape('http://www.google.co.kr/소 설.html');
  document.write('<p>' + s + '<p>');
  // 출력 결과: http%3A//www.google.co.kr/%uC18C%20%uC124.html
</script>

</body>
</html>




어떤 함수든 "공백 문자" 즉 스페이스는 %20 으로 치환합니다. 그러나 주소의 공백은 없어야 합니다.


2007/10/23 15:27 2007/10/23 15:27
이 글에는 트랙백을 보낼 수 없습니다
웅쓰:웅자의 상상플러스
웅자의 상상플러스
전체 (379)
게임 (5)
영화 (2)
기타 (23)
맛집 (5)
영어 (2)
대수학 (3)
형태소 (5)
Hacking (9)
Linux (112)
HTML (48)
Application_developing (48)
Web_developing (102)
Window (11)
«   2024/11   »
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
  1. 2016/01 (1)
  2. 2015/12 (3)
  3. 2015/10 (3)
  4. 2015/03 (2)
  5. 2015/01 (4)