[Info]Tags categorized posts and contents patterns..

[AJAX] Ajax Code E xamples.. [Book] About the book.. [CSS] CSS Code E xamples.. [DB] Sql Code E xamples.. [DEV] All development stor...

2016년 2월 24일 수요일

[Book] 프리젠테이션 젠 - 생각을 바꾸는 프리젠테이션 디자인..

출처 : Outsider's Dev Story https://blog.outsider.ne.kr/

프리젠테이션 젠 - 8점
가르 레이놀즈 지음, 정순욱 옮김/에이콘출판

내가 어디가서 발표를 하고 그럴 직급은 아니긴 하지만 뭐 또 전혀 그럴일이 없다고 할 수도 없다. 어떤 일을 하던 직장인에게 프리젠테이션 능력이란 것은 꽤 큰 관심사이고 프리젠테이션 생각하면 떠오르는 스티브 잡스를 생각하며 그런 프리젠테이션을 하고 싶은 꿈을 꿀 것이다. 이 책은 그런 뛰어난 프리젠테이션을 위한 가이드라인을 잡아주는 책이다.

이 책은 열이아빠님으로 부터 작년에 선물로 받은 책인데 이제야 시간을 내서 읽어봤다. 나는 발표능력은 미비한 편이고 디자인 감각은 더 떨어지는 편이다. ㅋㅋㅋㅋ 회사다니다 보면 PPT를 많이 보게 되는데 난 우리회사의 PPT는 별로 안좋아하는 편이다. 내가 만든것도 아니고 내가 발표할 것도 아니긴 하지만.... 아주 심플한 PPT를 좋아하는 편이다. 예를 들면 한 페이지에 "왜 웹표준인가?", "크로스 브라우징" 머 이런것 처럼 명쾌하게 한 글귀만 있는 것들... ㅋ

PPT를 찬찬히 읽어봐도 다 읽기 힘들만큼 텍스트가 빡빡하게 있는 건 별로 좋아하지 않고 내가 청중으로 그런 PPT를 봐도 도대체 뭔 말을 하는건지... 하는 생각도 들고 솔직히 귀찮아서 다 읽어보지도 않는다. 머 이런 생각을 평소에도 좀 가지고 있었는데 이런 막연한 생각에 프리젠테이션젠은 이런 방식의 고급스러운 PPT에 대한 확실한 해답을 주고 있다. 여기서 제시하는 PPT를 볼때마다 그래 이래야지! 하는 생각 뿐이었다.

가르 레이놀즈가 얘기하는 프리젠테이션은 발표자의 발표를 도와주는 역할이라는 것이다. 사람은 읽으면서 듣을수 있는 사람은 극히 적기 때문에 오히려 전달성이 떨어지고 발표에 대한 핵심을 전달해주고 도와줄수 있는 발표자료여야 한다는 것이다. 발표자료는 발표와 함께 했을때 의미가 있어야 한다며 발표자료를 그대로 프린트해서 주는 것에 반대하고 있다. 발표자료만 봐도 다 이해가 되면 발표자는 왜 필요하냐고 하는데 확 공감이 갔다. 발표자료는 말로 설명하는 것을 보완하는 자료로 표현해야 하는 내용만 정확히 보여주고 더 자세한 자료는 유인물로 나누어 주는 것이 확실히 낫다는 것이다.

이런 발표를 하기 위해서 발표준비는 어떤 식으로 해야하는지... PPT는 어떻게 만들어야 하는지 발표할때는 어떻게 해야 하는지를 구체적으로 설명해준다. 물론 이런 부분은 많은 노력을 통해서 경험을 쌓아야 이룰 수 있는 부분이다.

가르 레이놀즈도 말하듯이 대부분의 사람은 더 많은 내용을 써야 뭔가 한게 있어 보이고 발표자든 청중이든 그게 정상적으로 보일정도로 너무 익숙해져 있다. 물론 내가 여기서 말하는 대로의 PPT를 만들어서 가져갔더니 상사가 "머 PPT가 이따구야?"라며 다시 만들어 오라고 할지도 모를 일이다. 사실 모를일이라기 보다는 그럴 가능성이 훨씬 높아보이긴 하다. 그런 건 각자 헤쳐나아가야 할 문제이고 이 책을 보면 PPT가 명쾌하다는 것에는 모두 공감할 것이다.

다른 프리젠테이션 관련 책들은 본적이 없지만 프리젠테이션에 대한 스킬업을 생각하고 있다면 꼭 한번 읽어봐야 할 책이라고 생각한다.

My Comment..
이 포스팅을 보니.. 옛날에 공공 사업을 할 때.. PPT 발표를 한 기억이 떠오른다..
그 때가 아마 2011년인가 2012년이었을 텐데.. 서울시 사업이었다..
내 위에 차장이라고 하는 직급이 있었음에도.. 본인이 바쁘다는 이유로..
더미제안서라는 이유로.. 나보고 가서 하라능.. 머 말인즉슨.. "이것도 경험이야.."
라고는 하지만 글쎄.. 그 때나 지금이나 썩 내키는 부분은 아니었고..

이 포스팅의 내용처럼 우리나라 PPT 가 조금은 심플화 됬으면 좋겠다..
너무 디자인의 화려함과.. 하고 싶은 모든 말들을.. 다 때려넣으려는 경향이 있다..
물론 지금은 조금씩 변화하겠지만, 그래도 여전한 것은 맞는 듯..
가끔.. 와이프가 공공사업 웹디자이너를 하기 때문에.. 집에서 작업하는 것을..
보노라면.. 이건 머.. 발표의 목적이 아닌.. 그냥 애니메이션을 보는 것 같다..
급.. 갑갑해지는구만..


[Book] Refactoring - 기존 코드의 디자인을 개선하는 방법..

출처 : Outsider's Dev Story https://blog.outsider.ne.kr/

Refactoring - 10점
Martin Fowler 지음, 윤성준.조재박 옮김/대청(대청미디어)

마치 TDD처럼 언젠가는 해야지 하면서 맘속에만 있고 현실로는 적용해보지 못하던 리팩토링을 좀 알기 위해서 집어든 책이다. 나는 자바를 사용하고는 있고 최근에는 MVC Model 2니 해서 클래스의 사용을 좀 높이고 있기는 하지만 자바가 객체지향언어임에도 불구하고 나는 여전히 절차식 프로그래밍을 하고 있다.

단순히 재사용성을 위해서 클래스를 사용하고 있을뿐 전혀 객체지향이라고 할 수 없는 개발을 하고 있고 이런 거는 대부분 마찬가지일 것이다. 항상 코드를 더 좋게 만들고자 하는 마음은 있지만 현실적으로는 적용하기가 쉽지 않고 그에 대한 지식을 쌓는 것도 만만치 않다.

이 책의 저자인 마틴 파울러는 리팩토링에 흠뻑 빠져 있는 것을 느낄 수 있으며 초기에 아주 간단한 리팩토링의 예제를 보여주고는 리팩토링이 무엇이며 어떻게 해야 하는 가를 설명하면서 개발자들이 리팩토링을 하려고 할때의 고민들은 하나하나 설명해 주고 있다.

이 책에서 마틴 파울러가 설명하고 있는 리팩토링에 대한 개념중 가장 맘에 드는 것은 리팩토링을 하면 언제든 더 좋은 디자인으로 바꿀 수 있다는 것이다. 항상 초기 설계할때 좋은 설계, 좋은 디자인을 하는 것은 매우 중요하지만 우리 모두 현실적으로 이것이 쉽지 않은 것임을 알고 항상 설계와 디자인은 개발과정에서 수없이 바뀌게 마련이다. 하지만 리팩토링을 잘 할수 있는 사람은 이에 대한 걱정이 없다는 것이다. 일단 코드를 짜고 잘못된 부분이 있거나 의도하지 않은 방향으로 갔으면 언제든지 리펙토링으로 더 좋은 디자인으로 코드를 수정할 수 있다는 것이다.

간단한 개념 설명뒤에는 대부분의 책 내용을 실제 리팩토링을 할 수 있는 예재로 보여주고 있다. 자바를 기준으로 예제를 보여주고 있기 때문에 자바개발자라면 어느정도 이해할 수 있을 것이고 개념은 좀 어렵더라도 해당 리팩토링 예제에만 집중할 수 있도록 간단하게 예제를 작성해 놓았기 때문에 그렇게 다다가기가 어렵지는 않을꺼라고 생각한다. 각 리팩토링을 단위별로 뽑아내어서 설명하고 있다. Extract Method, Move Method등 하나하나 단위화 하여서 처음 배우는 사람이 최대한 접근하는 것이 쉽도록 제공하고 있다.

물론 다 읽고 난 후에도 이런것은 적용하는 것이 쉽지 않다고 생각하고 있다. 리팩토링을 하려면 어떤 부분을 리팩토링해야 되는지 알아야 하고 그 부분을 찾으려면 어떻게 리팩토링해야 하는지 확실히 알고 있어야 하는데 현실에서의 코드는 여기에 나와있는 예제처럼 명확치 않을 것이 확실하기 때문에 쉽지 않다. 여기 나오는 수십개의 예제를 다 외우고 있을 수도 없고 외우고 있다고 하더라도 이걸 현실에서 적용하려면 단순히 외운것 만으로는 할 수 없고 실제 경험적으로 알고 있어야 한다. 책을 옆에 두고 있다고 하더라도 적용하기는 보통의 노력으로는 쉽지 않을 것이다.

하지만 나에겐 이게 크게 가치있어 보인다. 리팩토링은 그만큼 매력적으로 보이고 아직 OOP를 제대로 알지 못하는 나에게도 이 책을 보면서 객체지향이 어떤것인지 약간은 개념을 잡을 수 있었다. (이걸로 객체지향을 알게 되었다면 웃긴 일이겠지만... ㅋ) 마틴 파울러가 얘기하는 것 중에서 메소드를 추출할 때 주석단위로 추출해서 메소드명을 명확히 하여서 주석자체가 필요없게 한다는 것은 꽤나 멋진 아이디어라고 생각했고 내가 추구하는 개발방향하고도 맞아떨어진다고 생각한다.

결국 이런 개념을 현실로 적용하려면 아주 간단한 거부터 적용하기 시작하는게 최고라고 생각한다. 그리고 물론 여기서 리팩토링을 한다는것 더 괜찮은 객체지향코드로 바꾼다는 것이기 때문에 리팩토링을 하기 전에 객체지향부터 확실히 이해해야 될듯하다. 그걸 제대로 이해못하면 객체지향 자체가 어렵기 때문에.... 하나하나 붙잡고 지속적으로 노력해서 경험을 통해서 감을 잡는 수밖에 없을듯 하다. 그래도 마틴파울러가 어떤걸 어떻게 바꾸어야 하는지는 다 적어놨으니....

[JS]script.aculo.us의 Sortable로 드래그앤드롭(DragNDrop) 사용하기..

출처 : Outsider's Dev Story https://blog.outsider.ne.kr/

Script.aculo.Us에서는 드래그앤드롭(Drag N Drop)을 쉽게 구현할 수 있는 클래스가 있는데 Draggable(Droppables)과 Sortable 2개의 클래스이다. 둘다 드래그앤드롭을 위한 클래스인데 Draggable는 객체를 드래그 가능하게 만드는 클래스이고 Sortable은 드래그앤드롭이 되면서 정렬이 가능하도록 하는 클래스이다. 정렬을 하고자 한다면 Sortable을 사용하는 것이 편하고 여기서 정리하려는 것은 Sortable이다.

Sortable.create('id_of_container',[options]);

위 코드이 Sortable을 사용하는 기본 문법이다. id_of_container에 드래그앤드롭을 가능하게 할 엘리먼트의 id를 주고 options에 드래그앤드롭에 대한 옵션을 JSON형태로 던져주면 바로 정렬가능한 드래그앤드롭을 사용가능하다.  id_of_container이라고 쓴 것처럼 드래그할 객체가 아니라 드래그할 객체들을 담고 있는 container의 id를 주면 되고 options는 안주면 디폴트옵션으로 동작하게 된다.

예를 들어

1
2
3
4
5
6
7
<ul id="contailner">
    <li id="dnd_1">Test 1</li>
    <li id="dnd_2">Test 2</li>
<ul>
<script type="text/javascript">
    Sortable.create("contailner");
</script>

위와같이 작성하면 dnd_1, dnd_2객체가 바로 드래그앤드롭이 가능하다. (이 얼마나 간단하단 말인가. ㅎㅎㅎ)

이제 옵션을 보자. 자유롭게 사용하려면 옵션을 주어서 원하는 형태로 사용하여야 한다. 옵션은 Draggable과 공통적인 것도 있고 아닌것들도 있다.(아직 Script.aculo.us의 API리스트는 솔직히 좀 아쉽다.) API와 내가 테스트해본걸 바탕으로 적었지만 빠진 부분이 있을수도 있다.

tag : 여기에 HTML태그명을 주면 해당 태그에 대해서 Sortable이 동작하게 된다. 디폴트는 li태그이고 container아래 한단계 밑까지만 동작한다. 쉽게 말해 li안에 li가 또 들어있다고 하더라도 안에 있는 li에는 드래그앤드롭이 동작하지 않는다. Block엘리먼트에만 가능하고 table같은걸 블가능하다.

only : 이 옵션을 주고 여기서 class명을 지정하면 tag에 설정한 태그명이라고 할지라도 only에서 준 class명과 맞지 않으면 드래그앤드롭이 동작하지 않는다.

overlap : 가로 리스트는 horizontal, 세로 리스트는 vertical로 설정하라는데 정확한 동작은 잘 모르겠다.

constraint : horizontal, vertical, false 3가지가 있고 드래그앤드롭의 방향을 설정한다. horizontal/vertical로 설정할 경우에는 가로/세로로만 이동하며 false나 옵션을 주지 않았을 경우에는 어떤 방향이든지 이동이 가능하다.

containment : 이곳에 container 리스트를 지정하고 이곳에 지정한 container간에는 드래그앤드롭이 가능하다.

handle : 드래그앤드롭을 할 엘리먼트에서 전체가 아닌 특정부분만을 이용해서 드래그앤드롭이 가능하도록 하고 싶을때 handle이용한다. 이곳에 id 혹은 class명을 지정하면 핸들러로 사용할 수 있다.

delay : 드래그앤드롭으로 반응할 시간을 미리세컨단위로 설정할 수 있고 기본은 0이다. 너무 민감하게 반응하지 않기를 바랄때는 delay의 숫자를 크게 지정할 수 있다.

dropOnEmpty : true로 설정하면 container가 비어있을때도 drop이 가능하다. false일 경우에는 container에 다른 드래그앤드롭객체가 있을때만 드롭이 가능하다. 기본은 false

scroll : 기본으로는 지정되어 있지 않으며 window로 설정할 경우에는 드롭할 container가 화면밖에 있을경우에 드래그객체가 화면밖으로 나가면 자동으로 스크롤한다.

scrollSensitivity : 기본은 20이고 스크롤이 되게 하기 위해서 넘어가야하는 크기를 지정한다.

scrollSpeed : 스크롤 속도이고 픽셀로 지정하면 기본은 15이다.

onChange : 콜백함수로 드래그앤드롭을 시작하는 등 객체의 위치가 변경되면 계속 발생한다.

onUpdate : 콜백함수로 드래그앤드롭을 하여 실제 순서의 변화가 생겼을때만 발생한다.



아주 간단한 예제를 만들었다. 동작과 소스코드를 보면 쉽게 이해할 수 있을꺼라고 생각한다. 최근 작업을 하면서 몇가지 주의점(?)을 발견한 거라면...

  • container의 id에 언더바(_)가 들어가면 동작하지 않는다.
  • Sortable.serialize를 사용하려면 드래그앤드롭 엘리먼트의 아이디가 언더바(_)로 이어져야 한다. 언더바 뒤쪽에 있는 문자만 표시된다.
  • IE에서는 왼쪽 콘테이너에서 오른쪽 콘테이너로 갈때 드래그엘리먼트가 컨테이너 뒤쪽으로 가는 문제가 생긴다. 드롭하기 위해서 반이상 넘어갈 경우에는 위로 올라오는데 이건 IE의 z-index버그때문에 생기는 문제로 예상되는데 정확한 해결책은 아직 찾지 못했다.
Sortable.serialize는 해당 컨테이너에 있는 드래그앤드롭객체를 순서대로 보여준다. 이것은 위치정렬을 한 후에 Ajax등을 통해서 새로 정렬한 위치를 서버에 저장할 수 있도록 하는 역할을 한다.

그리고 예제의 소스를 보면 알겠지만 기본적으로는 드래그앤드롭을 할 때 해당위치의 공간은 표시하지만 따로 표시되지는 않는다. 이 문제를 해결하기 위해 더미 엘리먼트를 만들었다. OnChage가 발생할 때 해당 엘리먼트의 바로 앞쪽에 더미엘리먼트를 만들어서 insert한다. 이렇게 함으로써 드롭했을때 떨어질 위치가 명시적으로 보일수 있도록 하고 OnChange는 계속 발생하기 때문에 처음에 더미엘리먼트를 지우고 다시 넣어주기 때문에 다른 컨테이너로 넘어가는 순간 더미엘리먼트도 적절하게 이동된다. 깔끔한 해결책은 아닌듯 하지만 일단 동작은 한다.

My Comment..
햄의 블로그에서 그을 가져오다보면, 댓글도 최대한 다 읽어보려고 한다..
물론 읽는다고 해서 머리에 다 들어오는것도 아니고.. 기억이 다 나지도 않는다..
하지만, 읽다보면 가끔.. 내가 아무 생각없이 가져다 쓴것들을 발견하는 경우가 있다..

이 글도 보니.. 댓글 중에.. 시대가 지나서 라이브러리를 안쓴다고 한.. 햄의 글이 있다..
그래.. 블로그에서 가져오다보면 그런것들이 꽤 된다..
그래도 나에게는 도움이 된다.. 생각을 다시 상기 시키고, 쓰기만 하고..
내부 구성을 모르던 것들에 대해서 알게 되니까..

[Book] Ajax 디자인 패턴 for 웹 2.0..

출처 : Outsider's Dev Story https://blog.outsider.ne.kr/

Ajax 디자인 패턴 for 웹 2.0 - 8점
마이클 마헤모프 지음, 정석모.김지원 옮김, 김태영 감수/한빛미디어

Ajax를 신기술이라고 하기는 뭐하긴 하지만 아직은 일반적으로는 고급기술로 받아들여진다고 생각하기 때문에 초기에는 새기술을 접할려면 이게 먼지 어떻게 하는 것인지가 궁금하기는 하지만 어느정도 개념을 파악하면 얼마나 더 고급스럽게 사용할 수 있을까가 주된 관심사가 아닐까 생각한다.

그런 면에서 이 책은 Ajax와 관련된 디자인패턴을 대부분 다뤄주고 있다. Ajax가 웹2.0에서 상당히 주류기술이 된 뒤로 웹어플리케이션에서 다양하게 사용되고 있는데 여기서는 현재 사용되고 있는 거의 모든걸 다뤄주고 있다는 느낌이다. 그만큼 분량도 엄청나다. 800페이지 정도... ㄷㄷㄷㄷㄷ

Ajax를 사용하는 다양한 패턴에서 이책은 총 4가지 패턴으로 분류하고 있다. 제품에 대한 패턴인 기본기술패턴, 프로그래밍 패턴, 기능성과 가용성 패턴과 처리과정에 대한 패턴인 개발패턴으로 분류하여 각 디자인 패턴을 설명하고 있다. 위 패턴의 순서대로 설명을 해주고 있는데 내가 느끼기엔 뒤로 갈수록 좀 쉬운 느낌인데 아무래도 기능성과 가용성 패턴쪽이 내가 더 많이 다루어 보았기 때문인 것 같다. REST, 웹리모팅등 Ajax와 관련되 고급기술들이 많기 때문에 포괄적 지식이 없다면 상당히 어렵게 느껴지기도 한다.(아무래도 이 모든걸 일일이 다 설명해 주지는 않기 때문에...)

난 이 책을 처음 집었을대는 보안이나 성능 또는 Ajax개발을 하면서 고민하던 이슈들에 대해서 좀 해결해보고 싶었는데 이 책이 접근하는 건 좀 다르다. 세부코드의 구현방식에 집중한다기 보다는 약간은 더 포괄적으로 어떤 경우에 Ajax를 사용해야 하는가(꼭 들어맞는 말은 아닌것 같지만...)에 더 접근하고 있다. 그리고 워낙 많은 내용을 담으려고 하다보니까 책의 대부분을 개념설명으로 채우고 있다. 상황설명하고 방법적인 개념 설명하고  예상되는 문제점등에 대해서 얘기하고 비슷한 경우의 실제 서비스들에 대해서 소개한 후에 간단한 소스 설명을 한다.

이 소스설명이 상당히 간단하게 되어 있기 때문에 해당 디자인 패턴을 다 파악하는데는 솔직히 무리가 좀 있다고 생각하고 있다. 이런 부분은 좀 아쉽기는 한데 책의 예제 사이트에서 설명하고 있는 모든 예제의 소스와 데모를 제공하고 있다. 그리고 예제는 어떤 특정 서비스를 이용해서 패턴을 다양화 해서 보여주고 있기 때문에 소스파악을 하기에는 좀더 편하지 않을까 싶다. 책에 이 소스에 대한 좀 자세한 설명이 있었으면 좋겠지만 아쉽게도 디자인 패턴을 제대로 파악하려면 해당 데모사이트에서 소스를 직접 분석해서 파악해야 할 듯 싶다.

솔직히 난 이책의 내용을 제대로 다 파악하지는 못했다. 책을 볼 때는 내가 잘 몰라서 파악하기 어려운 경우가 있고 어렵게 설명을 해서 파악하기 어려운 경우가 있는데 내가 파악하지 못한 이유는 아무래도 내 기술적 지식이 적어서 그런게 아닌가 싶다. 처음에 볼때는 너무 어렵고 난해하게 설명이 되어 있는 것 같아서 좀 그랬지만 읽으면 읽을수록 너무나 다양한 패턴을 설명해 주고 있었기 때문에 Ajax를 개발한다면 필요할때마다 참고해서 보면 좋을 만한 책이다.

[JAVA]Java의 Foreach 루프 사용하기..

출처 : Outsider's Dev Story https://blog.outsider.ne.kr/

자바 1.5버전부터는 자바에도 Foreach루프가 추가되었다. 실제적으로 자바에서는 명령어가 foreach가 아니라 기존과 동일한 for를 사용하고 있기는 하지만 대부분의 다른 언어에서는 foreach라는 용어를 사용하고 있기때문에 여기서도 foreach라는 말을 사용한다. foreach는 실제적으로 타이핑의 양도 적으면서 가독성도 좋다고 생각하기 때문에 훨씬 편리하다고 생각한다.

1
2
3
4
String[] temp = { "aa", "bb", "cc" };
for (int i = 0; i < temp .length; i++) { 
    System.out.println(temp[i]);
}

일반적으로 사용하는 for문이다. 솔직히 워낙 익숙한 형태이기 때문에 코딩하기 어렵다거나 그런것도 없을 정도이기는 하다.(가끔 타입에 따라서 length인지 size()인지 헷갈리는것 말고는 ㅡ..ㅡ)

이걸 foreach형태로 사용하면 다음과 같이 작성할 수 있다.

1
2
3
4
String[] temp = { "aa", "bb", "cc" };
for (String el : temp) {
    System.out.println(el);
}

어느쪽이 좋은지는 각자 나름이겠지만 나는 foreach가 훨씬 편하다. 직관적이고 굳이 갯수를 셀 필요도 없고... foreach의 형태는 다음과 같다.

for (type var : iterate) {
    body-of-loop
}

루프를 돌릴 객체를 iterate부분에 넣어주고 각 루프에서 나오는 타입과 {} 안에서 사용할 변수명을 var에 지정해 주면 된다. iterate부분에 들어가는 타입은 당연히 루프를 돌릴수 있는 형태인 Array나 Collections가 가능하고  Iterable<E>를 상속받은 객체또한 가능하다.

따로 반복회수를 임의로 주는 형태가 아니라면 foreach를 이용해서 훨씬 간단하게 작성할 수 있다.  당연히 루프를 핸들링할 수는 없기 때문에 1스탭씩 순차적으로 반복할때만 사용할 수 있다.

[JAVA]JDBC Driver 리스트..

출처 : Outsider's Dev Story https://blog.outsider.ne.kr/

새로운 디비 연결할때마다 혹은 사용하던것도 굳이 외우고 있지는 않아서 이전소스를 찾아봐야하는 JDBC Drivers. 날잡아서 정리... ㅎ 실제 적용할때 한번더 찾아봐야겠지만 그래도 일일이 찾아다니는거 힘들어서.. ㅎ



Informix
Driver Class : com.informix.jdbc.IfxDriver
JDBC URL : jdbc:informix-sqli://<host>:<port>/<database>:informixserver=<dbservername>
Required File : ifxjdbc.jar  Download


JavaDB/Derby
Driver Class : org.apache.derby.jdbc.ClientDriver
JDBC URL : jdbc:derby:net://<host>:<port1527>/<databaseName>
Required File : derbyclient.jar  Download


Microsoft SQL Server 2000
Driver Class : com.microsoft.jdbc.sqlserver.SQLServerDriver
JDBC URL : jdbc:microsoft:sqlserver://<host>:<port1433>;DatabaseName=<database>
Required File : msjdbc.jar  Download


Microsoft SQL Server 2005
Driver Class : com.microsoft.sqlserver.jdbc.SQLServerDriver
JDBC URL : jdbc:sqlserver://<host>[:<port1433>];databaseName=<database>
Required File : sqljdbc.jar  Download


MySQL (Connector/J)
Driver Class : com.mysql.jdbc.Driver
JDBC URL : jdbc:mysql://<host>:<port3306>/<database>
Required File : mysql-connector-java-nn-bin.jar  Download


Oracle (Thin JDBC Driver)
Driver Class : oracle.jdbc.driver.OracleDriver
JDBC URL : jdbc:oracle:thin:@<host>:<port>:<SID>
                       jdbc:oracle:thin:@<host>:<port>/<service>
                       jdbc:oracle:thin:@<TNSName>
Required File : ojdbcxx.jar  Download
Oracle JDBC FAQ


Oracle (OCI JDBC Driver)
Driver Class : oracle.jdbc.driver.OracleDriver
JDBC URL : jdbc:oracle:thin:@<host>:<port>:<SID>
                      jdbc:oracle:thin:@<host>:<port>/<service>
                      jdbc:oracle:thin:<TNSName>
Required File : ojdbcxx.jar  Download   Oracle Database Instant Client
Oracle JDBC FAQ


PostgreSQL
Driver Class : org.postgresql.Driver
JDBC URL : jdbc:postgresql://<host>:<port5432>/<database>
Required File : postgresql-nn.jdbc3.jar   Download



Sun에서 제공하는 JDBC Driver 리스트

[JAVA]PreparedStatement 객체 재사용하기..

출처 : Outsider's Dev Story https://blog.outsider.ne.kr/

웹개발하면서 DB의 사용은 필연적이기 때문에 java.sql.PreparedStatement의 사용은 필연적이다. Statement도 있기는 하지만 PreparedStatement는 한번 사용한 SQL문이저장되기 때문에 반복해서 사용할 경우 성능이 좋기 때문에 일반적으로는 PreparedStatement를 사용한다.

1
2
3
4
5
6
StringBuffer sql = new StringBuffer(" INSERT INTO poll (col1, col2 ) VALUES (?, ?) ");

psmt = conn.prepareStatement(sql.toString());
psmt.setString(1, "test");
psmt.setString(2, "test");
psmt.executeUpdate();

일반적으로 위와 같이 사용하는데 저렇게 사용해서 결과값 받아서 결과값 리턴해주고 끝인데 상황에 따라서는 쿼리를 반복적으로 사용해야할 때가 있다. 보통은 저게 DAO에 들어있기 때문에 메서드를 여러번 실행해도 되겠지만 그렇게 하면 Connection도 끝었다 다시하기 때문에 별로 효율적이지 못하다.

1
2
3
4
5
6
7
8
StringBuffer sql = new StringBuffer(" INSERT INTO poll (col1, col2 ) VALUES (?, ?) ");

for(int i = 0; i<5; i++) {
    psmt = conn.prepareStatement(sql.toString());
    psmt.setString(1, "test");
    psmt.setString(2, "test");
    psmt.executeUpdate();
}

하지만 여러번 반복해서 쿼리를 실행하기 위해서 위처럼 사용하면 안된다. 보기에는 크게 문제가 없고 일반적인 객체의 경우는 저렇게 재할당해서 사용해도 되기는 하지만 PreparedStatement는 다르다. 저렇게한 다음에 psmt.close();를 실행하면 아래쪽 맨 마지막psmt만 닫히고 앞의 psmt들은 닫히지 않는다. 정확히 말하면  앞에서 할당한 psmt는 결코 닫을수 없는 형태가 되어버린다.

1
2
3
4
5
6
7
8
9
StringBuffer sql = new StringBuffer(" INSERT INTO poll (col1, col2 ) VALUES (?, ?) ");
psmt = conn.prepareStatement(sql.toString());

for(int i = 0; i<5; i++) {
    psmt.setString(1, "test");
    psmt.setString(2, "test");
    psmt.executeUpdate();
    psmt.clearParameters();
}

이걸 위처럼 사용하고 있다. PreparedStatement의 객체를 새로할당하는게 아니라 파라미터를 할당하고 실행한 다음에 psmt.clearParameters();로 파라미터를 클리어해버린다. 루프돌면서 다시 할당하고 이렇게 돌리면 PreparedStatement객체를 여러번 사용할 수 있다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
StringBuffer sql = new StringBuffer(" INSERT INTO poll (col1, col2 ) VALUES (?, ?) ");
psmt = conn.prepareStatement(sql.toString());

for(int i = 0; i<5; i++) {
    psmt.setString(1, "test");
    psmt.setString(2, "test");
    psmt.addBatch();
    psmt.clearParameters();
}
psmt.executeBatch();

이걸 좀 더 세련(?)되게 하면 위와같이 사용할 수 있다. JDBC의 배치기능을 사용한 것이다. PreparedStatement의 배치기능은 다수의 쿼리를 한꺼번에 실행하기 위한 기능이다. 여러번에 나누어서 실행하지않고 배치기능을 이용해서 한번에 송신해서 실행함으로써 퍼포먼스를 높일 수 있다.

addBatch()는 쿼리와 파라미터 셋을 배치에 추가하고 이렇게 추가된 배치를 executeBatch()를 통해서 한꺼번에 실행한다. 정확한 테스트까지는 못해봤지만 이렇게 사용하는 배치 메모리의 크기 제한이 있기 때문에 너무 많은 배치를 추가할 경우에는 out of memory가 발생할 수 있기 때문에 많은 addBatch()를 실행해야 할 경우에는 중간중간 executeBatch()를 실행해 주어야 한다.

[JAVA]StringBuffer 객체 초기화하기..

출처 : Outsider's Dev Story https://blog.outsider.ne.kr/

문자열을 만들때는 보통 StringBuffer를 사용한다. StringBuilder인가도 있지만 사용해 본적도 없고 StringBuffer대신 쓸 이유는 아직 잘 모르겠다. 하지만 문자열을 만드는데는 단연코 String으로 +로 이어붙히는것보다는 훨씬 빠르다.

1
2
3
StringBuffer sql = new StringBuffer(" SELECT col1, col2, col3, col4 ");
sql.append(" FROM table1  ");
sql.append(" WHERE col1 = 1  ");

머 위의같은 코드들... append()를 이용해서 계속 이어붙힐 수 있고 속도도 빠르지만 이 StringBuffer객체를 재사용해야할 때가 있다. sql2를 만들기는 좀 그러니까...

가장 쉽게 생각할 수 있는 건 null이다.

1
2
sql = null;
sql = new StringBuffer(" SELECT col1, col2, col3, col4 ");

null로 객체 없애버리고 다시 새로 생성하는 것이다.  일반적으로 초기에 가장 많이 쓰는 방식이지 싶다.

또 하나는 setLength(0)이다.

1
2
sql.setLength(0);
sql.append(" SELECT col1, col2, col3, col4  ");

StringBuffer객체는 그대로 놔두고 setLength함수를 이용해서 길이를 0으로 설정해서 기존에 가지고 있던 문자열을 날려보리고 append로 새로 이어붙히는 것이다.

성능테스트는 안해봤지만 크게 차이는 나지 않을것 같기는 하지만 그래도 객체를 새로 생성하는 것보다는 길이를 0으로 만드는 것이 좀더 빠르지 않을까 생각한다.

간단한 것들은 잘 안적었더니 이런게 더 잘 찾기 힘들단 말야 ㅎ

[DB]여러 행 SELECT해서 INSERT 하기..

출처 : Outsider's Dev Story https://blog.outsider.ne.kr/

여러 행을 가지고 한번에 인서트를 해야할 경우가 최근에 꽤 있었다. 한쪽 테이블에서 어떤 조건으로 셀렉트해서 다른쪽 테이블에 한꺼번에 넣어주기... 그렇게 흔한 상황은 아니지만 충분히 있을법한 상황이다.

처음에는 이걸 할줄 몰라서 SELECT해와서 루프돌면서 인서트를 날려줬다. 아~ 느려.. ㅡ..ㅡ 갯수가 그렇게 많지 않았으니까 다행이지.......

1
2
3
4
5
INSERT INTO table_a
( title, name, regdate, register, memberid, categoryid )
SELECT title, name, regdate, register, memberid, categoryid
FROM table_b
WHERE categoryid=10

위에처럼 해주면 인서트문 한방으로 수십, 수백줄의 행을 추가해 줄 수 있다. 솔직히 이거 하기 전에는 INSERT는 한번에 하나만 되는 줄 알았다. ㅋ WHERE절은 SELECT문에 딸린 조건문이다. INSERT문이 아니라.... 이렇게 SELECT로 할 때는 원래의 INSERT문에 들어가는 VALUES는 들어가지 않고 당연한 얘기로 INSERT할 컬럼수와 SELECT하는 컬럼수 및 타입이 동일해야 된다.

SQL책하나 보고 좀 감오나 했더니 복잡한 쿼리 오니까 다시 헷갈리는 구만...

[Tool]괜찮은 클라이언트사이드 IDE : aptana Studio..

출처 : Outsider's Dev Story https://blog.outsider.ne.kr/

오랜만에 툴에 대한 소개를 하려고 한다. 내가 클라인트사이드 개발에 주로 사용하고 있는 aptana Studio다. 전에부터 한번 하려고 했는데 IDE를 소개한다는 것은 기능이 워낙 많기 때문에 만만치 않은 일인데 머 간단히 소개정도만 하고 그 뒤로는 새로운 기능파악할 때마다 포스팅해야할 듯 하다.(전체소개 없이 각 기능 소개하기도 좀 머해서....)

aptana Studio Logo

제목에는 클라이언트 사이드 IDE라고 소개하기는 했지만 이곳에 중점이 되어 있을 뿐 PHP, Python도 개발이 가능하다. RadRails라고 해서 플러그인을 깔면 Ruby on Rails개발도 할 수 있다. 툴을 이것 저것 써보았지만 클라이언트 사이드(자바스크립트, CSS, HTML)에서는 aptana Studio만큼 맘에 드는 것을 아직 발견하지 못해서 클라언트사이드 개발에는 메인 IDE로 사용하고 있다. (흔히 괜찮은 자바스크립트 에디터가 없다고 하는데 aptana Studio는 그 이상이다.)

aptana사이트에서 다운로드를 받을수 있고 무료이기 때문에 맘편히 사용할 수 있다. 초기에 나올때는 Communication버전과 Pro버전이 나뉘어져 있었는데 이번에 보니까 커뮤니케이션 버전이라는 말은 아예 사라진듯하다. 기본적으로 무료로 이용할 수 있도록 제공하고 있고 추가 기능이 필요한 사람들을 위해서 aptana Studio Pro(Pro의 추가 기능)를 위한 구매메뉴를 제공하고 있다. (Jaxer와 Cloud도 있는데 둘다 사용해 보지는 않은데 내가 대충 개념잡기로는 Jaxer는 서버이고 Cloud는 호스팅서비스 같은거다.)

08년 12월 31일 현재 1.2.1버전까지 나와있고 Standalone과 Eclipse 플러그인 2가지 타입으로 다운을 받을 수 있다. 난 Standalone타입을 선호하는 편인데 취향에 따라 쓰면 되겠다. OS는 Windows, Linux, Mac을 모두 제공하고 있다. Linux나 Mac에서는 안서봤지만 다양한 플랫폼을 지원한다는 것은 무조건 좋은거다.. ㅎㅎㅎ 현재는 Standalone로 풀인스톨러로 받으면 130MB정도 한다. 다운받아서 그냥 설치해주면 된다.

aptana Studio Interface

자바개발자라면 너무나도 익숙한 인터페이스이다. 위에서 플러그인 형태로 제공하는 걸 보고 눈치챘겠지만 Aptana Studio는 오픈소스 IDE인 Eclipse를 가지고 만든것이기 때문에 이클립스와 거의 흡사하다. 이클립스기반이기 때문에 이클립스가 요구하는 Requirements는 그대로 요구하고 있다. 1.5이상의 JRE가 필요하다는 얘기이다. 자바런타임환경만 있으면 달리 구성할 환경은 별로 없다.

나는 툴 사용의 효율성에 관심을 많이 가지고 있는데 개발의 속도를 높이려면 툴의 익숙함이 상당히 중요하다고 생각한다. 그런면에서 이클립스를 사용하는 개발자라면 서버사이드와 클라이언트 사이드의 개발툴이 통일화되어서 따로 익숙해지는 과정이 필요없다는 것은 매우 큰 장점이다. 물론 이클립스를 쓴다면 특별히 클라이언트사이드의 코딩이 엄청나지 않는 이상은 이클립스에서 코딩하기도 하지만 지원하는 면에서 비교가 안된다고 생각한다. 나도 일일이 벌갈아가면서 하지는 않지만 aptana Studio의 기능이 이클립스에서 안되서 답답한 적이 많다.(물론 플러그인형태로도 제공하니까 플러그인을 쓰면 된다.)

그리고 또하나의 장점은 이클립스에서 사용하는 플러그인을 그대로 사용할 수 있다는 것이다. 오픈소스 이클립스를 지원하는 방대한 양의 플러그인의 규모는 엄청난데 SVN, MyLyn등등 그대로 다 쓸수 있는 것이다.

그럼 aptana Studio의 몇가지 기능을 살펴보자. 머 거창한건 아니고 간단한 기본기능들 위주로....이클립스를 안써본 사람도 있겠지만 기본적인 프로젝트 생성같은거는 이클립스랑 동일하기 때문에 굳이 언급하지 않겠다.

IDE의 기본 기능이면서도 개발자에게 가장 크게 와닿는 것중 하나가 코드 어시스트이다. 클라이언트 사이드에서 aptana Studio가 제공하는 코드어시스트는 강력하다. 많은 툴을 다뤄본 것은 아니지만 내가 만져본 것들 중에는 aptana Studio가 제공하는 코드 어시스트가 가장 강력한것 같다.

HTML 코드 인텔리전스CSS 코드 인텔리전스Javascript 코드 인텔리전스

html, CSS, javascript할 꺼 없이 거의 완벽하게 코드어시스트를 제공해주고 있다. CSS경우는 각 속성에 대한 이름뿐만 아니라 사용할 수 있느 값들까지도 제공하고 있으면 자바스크립트의 경우는 키워드, 함수명, DOM을 다 제공해 줄뿐만 아니라 위의 이미지처럼 사용자가 만든 function까지도 파라미터값까지도 보여준다. 사용할수 있는 키워드를 타이핑할 때마다 직관적으로 보여주고 그 오른쪽에는 지원되는 브라우저가 아이콘으로 표시되기 때문에 크로스브라우징에 맞추어서 개발하는데 정말 편하다. 오른쪽에는 해당 키워드에 대한 간단한 설명까지 나오기 때문에 부족함이 없을 정도이다.

html, css, javascript에서 자주 사용하는 엘리먼트를 바로 추가할 수 있도록 상단에는 아이콘들이 있는데 마우스를 써야 되니까 잘 쓰게 되지는 않는것 같다. ㅎ

Browsers/User Agents 코드어시스트

코드 어시스트에서 보여주는 브라우저 호환에 대한 부분은 [window] - [Preferences]에 들어가서 [Aptana] - [Browsers/User Agents]에서 설정해 줄 수 있다. 여기서 필요한 브라우저를 체크할 수 있다. (아직 크롬은 없다. ㅎ) 이렇게 설정하면 아래처럼 선택한 브라우저의 호환성을 코드어시스트에서 제공 받을 수 있다.

코드어시스트 브라우저 호환성

위에서 본 에디터 부분에서 기본적으로 소스창을 보지만 따로 브라우저를 띄우지 않고 aptana Studio안에서 IE와 FF에서 미리보기를 해 볼 수 있다.

에디터 하단

아래쪽 탭을 선택하면 바로 볼 수 있으면 추가를 누르면 미리보기 셋팅을 추가해 볼 수 있다.(현재는 브라우저는 IE, FF만 제공하고 있다.) 여기서 볼때도 스크립트 에러가 나는 경우는 바로 표시가 된다.


CSS파일을 만들때도 preview를 제공하고 있는데(HTML에 들어가는 스타일부분 말고 확장자가 .css로 된 파일들....)

CSS 미리보기

CSS파일을 편집할때는 하단쪽 탭이 Preview로 나오게 되는데 실제 html에 적용된걸 보려면 html을 실행시켜서 봐야하기는 하지만 내가 작성한 css를 바탕으로 기본으로 제공되는 텍스트를 통해서 각 엘리먼트가 어떤식으로 보여지는 지를 간단하게 미리볼 수 있다. 오른쪽 톱니바퀴 아이콘을 눌러서 Edit Default preview template를 누르면 CSS preview에서 제공하는 텍스트를 수정해서 사용할 수 있다. file preview setting을 클릭해서 현재 프로젝트의 html파일을 선택해주면 프리뷰를 원하는 형태로 볼 수도 있다.

Run As로 서버 구동

그리고 이게 코드어시스트말고  내가 aptana Studio를 좋아하는 큰 이유중 하나인데 html파일에서(jsp, asp, php같은 서버측코드는 해석할 수 없으므로...) Run AS - JavaScript Web Application을 시작하면 aptana Studio 내부의 Jaxer서버가 시작되면서 해당 페이지를 구동시켜준다.(정확히는 프로젝트째..)

http://127.0.0.1:8000/Test/new_file.html 와 같은 형태의 주소로 구동되기 때문에 주소를 이용해서 다양한 브라우저에서 쉽게 테스트해볼 수 있다. 수정하면 바로바로 확인해 볼 수 있고 프로젝트 이름이 붙기는 하지만 적대주소도 사용할 수 있기 때문에 아주 유용하다. ㅎㅎ

서버

Run As에서 Run을 눌러서 설정창을 띄우면 서버실행할 때 자동으로 구동시킬 웹브라우저 및 여러가지 설정사항을 지정해 줄 수 있다.

이렇게 개발하는데 도움이 될만한 대략적인 기능들을 살펴봤다. IDE이기 때문에 그 기능은 엄청나고 많이 알면 알수록 개발의 효율은 높아질 것이다.

이외에도 클라이언트사이드개발을 위해서 많은 기능들을 가지고 있다.

프로젝트를 생성할 때 다양한 자바스크립트 프레임워크들을 선택해서 기본적으로 프로젝트에 포함 시킬수 있고 FTP연결연결하거나 Outline뷰에서 DOM트리를 보거나 js파일에서 함수구조를 본다던가 하는 등 많은 기능을 가지고 있다. aptana Studio 짱좋아.. ㅎㅎㅎ

[Book] Head First SQL : 효율적인 DB 관리를 위한 SQL 학습법..

출처 : Outsider's Dev Story https://blog.outsider.ne.kr/

Head First SQL : 효율적인 DB 관리를 위한 SQL 학습법 - 8점
린 베일리 지음, 박종걸 옮김/한빛미디어


역시 헤드퍼스트 시리즈는 정말이지 어려운 걸 아주 쉽게 설명해주는 재주가 있다.
제목대로 SQL쿼리에 대한 책이다. 물론 SQL을 설명하자니 정규화라든지 디비모델링에 대해서도 설명하고 있기는 하지만 대부분은 오로지 SQL쿼리에만 집중하고 있다.

헤드퍼스트 책을 처음 보면 초반에는시시하게 느껴질정도로 아주 기초적인 설명을 하고 있다. 이책도 마찬가지고 SELECT 조회 한번 날리는데 페이지를 몇페이지를 소비하는지 깜짝 놀랄 뿐이다. 여기서 너무 쉽다고 책을 접어버리면 나중에 후회한다. SQL에서 핵심적인 내용을 아주 기가막히게 설명해준다. 특히 설명하기 쉽지 않은 정규화나, 서브쿼리, 조인 같은거는 개념 정리하기에 정말 좋다.

실제 웹개발을 하면 쿼리는 사용하는 쿼리만 쓰게 되고 솔직히 create같은 경우는 잘 안쓰게 된다. 물론 이것저것 복잡해 지면 다 써야되기는 하지만 GUI툴을 많이 쓰고(물론 개발할때는 쿼리짜서 넣어야 하지만.. ㅎ) 처음 테이블 만들때도 ERWIN같은 툴을 이용해서 ERD만들어서 바로 DBMS로 쏴버리기 때문에 처음부터 create해서 제약조건 걸고 타입선언해주면서 하는 경우는 극히 드물다고 생각한다.

하지만 이해는 당연히 하고 있어야 한다. 쿼리를 좀 만져본 사람이라면 어렵다 할만한 내용은 그리 많지 않다고 생각하기에 수준이 중급까지는 안된다고 생각한다.  초급에서 초중급정도까지? 아니면 나처럼 쿼리를 좀 막 배운 사람이 쿼리의 기초를 복습(?) 또는 정리하는 면에서는 딱 좋은것 같다. 그리고 아주 쉽게 설명되어 있어서 쿼리를 처음 배울때도 적극 추천이다.

헤드퍼스트책이 그렇듯이 구성도 아주 잘 되어 있다. 일반적으로 SELECT설명하고 다른쿼리 설명하고 정규화 쭉~ 설명하고, 함수들 설명하고 그런 것이 아니라 어떤 문제를 해결하기 위한 점점 살붙혀나가기 식의 설명은 배우는 입장으로서는 이해하는데 큰 도움이 된다. 하나 배우고 거기서의 문제점 제기하고 또 설명하고 계속 꼬리에 꼬리를 무는... 이렇게 하면 기억도 많이 남게 되는것 같다. 정규화 같은 경우도 실무에서는 교과서처럼 하진 않으니까 그냥 감으로 하곤 했는데 이 책을 통해서 좀 확실히 이해를 할 수 있었다.

그리고 대부분의 쿼리는 ANSI기준으로 하고 있다. 따로 ANSI라는 말은 전혀 사용하고 있지 않고 MySQL에서 최적화 되어 있다고 하지만 DBMA마다 좀 다르기 때문에 MySQL을 중점으로 설명했을뿐 쿼리는 ANSI SQL에 초점이 맞춰있고 DBMS마다 확인해 봐야하는 상황이라거나 최적화 한다는 MySQL에서는 지원하지 않는 부분등에는 꼭 체크해주고 있기 때문에 사용하는 DBMS에 신경쓰지 않고 볼 수 있다. (개인적으로 T-SQL이나 이런거 다 필요없고 그냥 ANSI에만 집중하면 된다고 생각하기에...)

My Comment..
이 책은 아마도.. ksic 에서 본듯한.. 기억이 어렴풋하게.. 그 때도 아마..
좀 보다가 관둔거로 기억한다.. 은근히 책을 길게 못본단 말이지 ㅡㅡ.. 원체..
책을 잘 안보는 스탈이기도 하고.. 나중에 책을 볼 때는.. 좀 찬찬히 다 봐야징..
이놈의 조급증 부터 좀 고쳐야 될텐데 말이야.. (__)..

[JS]Template사용시 페이지 별로 Javascript 초기화 코드 다르게 하기..

출처 : Outsider's Dev Story https://blog.outsider.ne.kr/

보통 웹사이트를 구축하면 헤더나 푸터등 공통된 부분이 있기 때문에 이런 부분은 별도로 만들어서 공통으로 사용하게 한다. 각페이지에 헤더,푸터파일을 인클루드해서 사용할 수도 있지만 그렇게 되도 반복코드가 많이 발생하니까 일반적으로는(그냥 내 생각에.. ㅡ..ㅡ) 템플릿 파일을 만들고 contents부분만 바꿔가면서 사용하지 않을까 싶다.(일단 난 그렇게 한다 ㅡ..ㅡ 전에 올렸던 포스팅처럼...)

이렇게 할때 가장 큰 문제가 자바스크립트 초기화 코드이다. 항상 바꿔치기할 contents부분은 <body>의 정중앙에 들어있는데 보통 스크립트는 <head>안에 있거나 <body>맨아래 있단 말이지. contents부분에 들어갈 페이지에 스크립트코드를 같이 써줘도 되기는 하지만 완성된 html에서 중간에 스크립트 코드가 들어가기 때문에 별로 좋은 방법은 아니라고 본다.

여기서 초기화코드라는 것은 머 여러가지가 될수 있는데 쉽게 페이지 로딩후(onload)에 자동으로 수행할 함수를 말한다. 가장 많이 쓰는데 이벤트 핸들러 등록이라던가 하는거고 onload후에 focus를 어디에 둔다던지 하는등의 코드를 말한다. 페이지가 다 다르게 생겼으므로 이런 코드도 다 다를 수 밖에 없는데 contents에 들어갈 페이지에서는 이걸 다룰수가 없다는게 고민거리였다. 물론 if-else문으로 해당 엘리먼트가 있는지 라든가 페이지 주소를 이용해서 억지로 할수야 있겠지만 쓸데없이 검사해야 하기 때문에 성능도 떨어지고 별로 아름답지 못한 방법같았다. 이벤트 핸들러의 경우 html 엘리먼트에 직접 써넣는게 일반적이지만(<input type="button" onclick="test();"> 이렇게...) 요즘 추세는 자바스크립트도 완전히 분리하는 것이기 때문에 이걸 분리하자면 초기화코드가 꽤 많아진다. (이게 Unobtrusive Javascript.... 이렇게 하면 디자인 바뀌어도 많이 손 안대도 된다. 물론 Unobtrusive Javascript가 말하고자 하는건 저게 중점은 아니지만 개인적으로는 큰범주에서 보면 같은 흐름이라고 생각한다.)


어쨌든 이번에 플젝하면서는 이걸 좀 해결해 보고 싶었기 때문에 OKJSP에 질문글을 올렸더니 임은천님이 답변을 해 주셨다.(거의 익명사이트긴 하지만 도움얻은걸 혼자한척 하진 않는다.. ㅡ..ㅡ) 딱 내가 기다리던 명쾌한 답변.... 너무 자세하게 답변을 주셔서 구현하는데 별로 어렵지 않았다.


1
2
3
4
5
6
7
document.observe('dom:loaded', function() {
    <% if (request.getAttribute("jsDispatcher") != null && !request.getAttribute("jsDispatcher").equals("")) { %>
    var dispatcher = new Dispatcher();
    var jdName = "<%= request.getAttribute("jsDispatcher") %>";
    eval(dispatcher.get(jdName));
    <% } %>
}); }

위의 코드를 Template의 부모(?)가 되는 페이지의 스크립트 부분에 넣어준다. 물론 저 코드는 Prototype Framework의 코드이다. 쉽게 가자면 window.onload에다가 이벤트를 건거다. 여기선 JSP코드니까 JSP로 jsDispatcher이 파라미터로 넘어왔는지를 검사하고 jsDispatcher값이 있으면 디스패쳐를 실행한다. 물론 템플릿에 어떤 페이지를 contents부분에 띄워줄지 파라미터로 넘겨줄때 jsDispatcher의 값도 같이 넘겨주어야 한다. 어차피 템플릿으로 구성되는게 구현되어 있을테니까 파라미터 하나 더 넘겨주는건 그리 어렵지 않은 일이다.

디스패쳐가 각 jdName별로 실행할 함수목록을 가지고 있고 파라미터로 넘겨받은 jsDispatcher의 이름으로 디스패쳐에다가 조회해서(get) 해당 실행할 함수들을 가져오고 그자리에서 eval()해서 실행해 버리는거다. 이것 자체가 window.onload때 실행되니까(Prototype.js의 dom:loaded는 약간은 다른의미지만 여기선 별로 중요하지 않다.) 디스패쳐로 가져온 값도 페이지 초기화코드로 실행된다. 물론 여기서 Dispatcher은 이어서 만들 클래스이다. 원래 제공되는게 아니라...

그럼 Dispatcher클래스를 보자.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var Dispatcher = Class.create();
Dispatcher.prototype = {
    initialize : function() {},
    get : function(name) {
        return this.map[name];
    },
    map : { listform: 'ListenerCollection.registEventListFrom()',
                  writeform: 'ListenerCollection.registEventWriteFrom()',
                  modifyform: 'ListenerCollection.registEventModifyForm()'
    }
}

var ListenerCollection = {
    registEventListFrom: function() {
        // 리스트 페이지의 초기화 코드들
    },
    registEventWriteFrom: function() {
        // 작성 페이지의 초기화 코드들
    },
    registEventModifyForm: function() {
        // 수정 페이지의 초기화 코드들
    }
}

여기에는 Dispatcher와 ListenerCollection 2가지 클래스가 있다.(Prototype.js의 관점에서는 Dispatcher만 클래스이지만 그냥 넘어가자.)

Dispatcher클래스는 3가지 매서드가 있다.  initialize는 보는바와같이 아무것도 없는데 prototype.js의 클래스에선 필수로 만들어주어야 하는거라서 신경안써두 되고 get하고 map이사용할 메서드이다. map은 넘겨받은 이름과 함수가 쌍으로 맵핑되어 있고 get에서 넘겨받은 name으로 map에서 찾아서 해당 함수를 돌려준다.

ListenerCollection은 등록할 이벤트리스너(꼭 이벤트리스너일 필욘 없지만)의 코드목록을 담는 곳이다. map내애서 funcion(){}으로 바로 적어줄 수도 있지만 코드량이 많아서 분리했다. 약간 꼬아놔서 그렇지 구조는 별로 어렵지 않다.

이렇게 하면 템플릿을 유지하면서도 페이지별로 초기화 자바스크립트 코드를 따로 둘 수 있다. 모든 페이지에서 이코드가 들어가긴 하지만 위쪽에서 onload로 이벤트를 건 곳을 제외하고는 외부 js파일로 빼면 어차피 캐시가 되기 때문에 성능면에서는 별로 신경안써도 될듯하다. prototype.js를 기반으로 작성되서 prototype.js를 안써본 사람은 약간 코드이해에 어려움이 있을까 좀 걱정되긴 한다...

[JS]JSON Text를 JSON Object로 변환하기..

출처 : Outsider's Dev Story https://blog.outsider.ne.kr/


JSON은 써보면 써볼수록 유용한 것 같다. 간결하고 편하고 직관적이다. 어쨌든 JSON Text를 Object로 변환해야 할 때가 있다. 여기서 JSON Text라는 것은 형태는 JSON의 형태이지만 자바스크립트에서 이걸 Object가 아닌 그냥 텍스트로만 인식하고 있다는 것이다. 이걸 Object로 바꾸어야만 그안의 값을 불러다가 사용할 수 있다.

가장 흔한 예가 Ajax를 사용할 경우이다. Ajax로 호출을 하고 결과값으로 받은 req.responseText로 JSON을 받았을 경우에는 그냥 Text이기 때문에 Object로 변환해 주어야 한다.

{ id:'Outsider', sex:'male' }

Ajax에서 리턴받을 JSON의 형태가 위와 같이 되어 있을 경우에는

1
2
var obj = eval("("+returnValue.responseText+")");
alert(obj.id);  // Outsider

위의 코드처럼 eval을 해주면 JSON 오브젝트로 변환할 수 있다.

[ { id:'Outsider', sex:'male' },
  { id:'Zziuni', sex:'male' } ]

JSON이 위의 형태처럼 배열로 되어 있을 경우에는

1
2
var obj = eval(returnValue.responseText);
// -> { id:"Outsider", sex:"male" }

그냥 eval을 해주면 JSON 오브젝트로 변환할 수 있다.

다만 이렇게 변환할 경우 eval()은 빠르기는 하지만 단순히 그안의 스트링을 그대로 실행시켜 주는 것이기 때문에 리턴값으로 자바스크립트 명령문이 온다면 그대로 실행시켜버리기 때문에 보안이슈가 발생할 수 있다. 

이렇게 리턴받은 소스를 신뢰하기 어려울 때는 JSON.org에서 제공하는 JSON parser을 사용해야 한다. JSON parser는 오직 JSON text만 받아들이고 다른 스크립트는 거부하고 eval()만큼이나 빠르다.

1
2
var obj = JSON.parse(returnValue.responseText);
// -> { id:"Outsider", sex:"male" }

JSON.parse()의 목적대로 JSON 텍스트 외에는 거부해야하기 때문에 JSON문법을 정확히 지켜주지 않으면 SyntaxError 예외가 던져진다. 그렇기 때문에 키와 값을 모두 쌍따옴표(")로 묶어주는 문법을 정확하게 지켜주어야 한다.(물론 이 경우는 값들이 스트링이기 때문에 쌍따옴표로 묶어주는 것입니다.) 아래처럼....

{ "id":"outsider", "sex":"male" }

여기서 JSON.parse()를 사용하기 위해서는 Douglas Crockford가 만든 json2.js가 필요하다. json2.js는 더글라스 크록포드가 JSON의 편리한 사용을 위해 만든 JSON API의 새버전이다.(그래서 2가 붙어있다.) 이 API에는 2개의 메서드가 들어있는데 JSON.stringify()와 JSON.parse()이다. JSON.stringify()는 JSON.parse()와는 완전히 반대로 JSON 객체를 주면 JSON 텍스트로 만들어서 준다. 필요할때 갖다 쓰면 된다. (John Resig이 이전버전보다 새버전의 API가 훨씬 좋다고 꼭 이걸 쓰란다.)

[JS]FireUnit : FireBug 확장 자바스크립트 유닛 테스트..

출처 : Outsider's Dev Story https://blog.outsider.ne.kr/

Ajaxian을 보다가 눈에 띄는 기사가 보였다. "FireUnit: JavaScript Unit Testing Extension"라는 기사로 jQuery를 만든 John Resig과 Firebug 풀타임 Contributor인 Jan Odvarko이 만든 Firebug의 확장플러그인으로 자바스크립트 유닛테스트를 할 수 있게 하는 FireUnit이라는 걸 만들어서 공개했다는 기사이다. 현재 적용하고 있지는 않지만 TDD에는 관심을 꽤 가지고 있기 때문에 언젠가 꼭 적용해봐야지 하고 있었는데 이런것까지 등장했다.

Alankang님이 만드신 JSSpec이라는 BDD프레임웍도 있기는 하지만(이것도 써봐야되는데... ㅠ..ㅠ) 프레임웍은 소스에 관련 라이브러리를 포함시켜야 하지만(단점이란 얘긴 아니다.) 이건 그냥 유닛테스트 소스만 있으면 된다.

FireUnit은 Firebug의 확장기능이므로 당연히 파이어폭스에서만 가능하고 파이어버그가 깔려 있어야 한다. 설치는 다른 파이어폭스 익스텐션과 동일하게 설치하면 된다.(Firefox 3이상, Firebug 1.2이상일때만 사용가능하다.) 설치하고 나면 아래처럼 Firebug에 Test라는 탭이 생긴다. 끝이다. 이제 테스트유닛만 만들어 내면 된다.

사용자 삽입 이미지

FireUnit의 Wiki에 가면 관련정보를 얻을 수 있다.  위키문서의 Internals를 보면 사용할 수 있는 유닛테스트 함수를 알 수 있다.

  • ok : prints a message (test result) into the Test panel. If the specified condition is false, the message is marked as failing (displayed in red).
  • compare : prints a message into the Test panel. If expected and result parameters are not equal, the message is marked as failing.
  • reCompare : expects regular expression (within expected) to compare with result parameter.
  • testDone : Finishes a test.
  • forceHTTP : starts local HTTP server (httpd.js) for network related tests. Also makes sure that cache is cleared.
  • registerPathHandler : makes possible to register handler for dynamically generated responses.
  • runTests : run list of specified tests (in the same order as the are specified)
  • id : returns DOM element according to the specified ID.
  • test : inserts tests to execute into a current queue.
  • click : fires fake click event on specified DOM element.
  • focus : focuses specified DOM element.
  • value : sets value of the specified DOM element.
  • key : fires fake keypress event on specified DOM element.
  • panel : returns specified Firebug’s panel.


Internals부분은 파이어유닛의 개발자를 위한 설명 페이지라서 각 메서드의 정확한 사용법에 대해서는 잘 나와있지 않다. 다행히도 존레식잔 오드바코(이렇게 읽는거 맞나 ㅡ..ㅡ)가 FireUnit에 대한 포스팅을 하면서 간단한 예제를 올려두었고 사용방법이 그리 어렵지 않기 때문에 약간 보면 이해할 수 있다.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function okUnitTest(){
    return true;
}    
    
fireunit.ok(true, "ok 성공 테스트");
fireunit.ok(false, "ok 실패 테스트");
fireunit.ok(okUnitTest, "ok 성공 테스트 - okUnitTest함수");

fireunit.compare("문자열 비교", "문자열 비교", "성공 : compare 문자열 비교");
fireunit.compare("<div>파이어유닛</div>", "<div>파이어버그</div>", "실패 : compare 문자열 비교");

fireunit.reCompare(/01[016789]-[0-9]{3,4}-[0-9]{4}/, "016-123-4567", "성공 : reCompare 비교");

window.onload = function() {
    var target = document.getElementById("box");
    fireunit.mouseDown(target);
    fireunit.click(target);
    fireunit.focus(target);
    fireunit.key(target, "a");
    fireunit.value("username", "Outsider");
    
    fireunit.testDone();
}

내가 테스트해본 코드이다. 이 유닛테스트 코드를 실행하면 아래와 같이 출력되는 것을 볼 수 있다. Compare같은 경우는 하위탭으로 비교값을 바로 확인해 볼 수 있다.

사용자 삽입 이미지

잠시 만져본 걸로 간단히 함수를 설명하면....(깊게는 나도 몰라서.. ㅋ)

fireunit.ok( , "") : ok는 boolean값으로 판단한다. 첫번째 파라미터로 불린값이 오고(위의 코드처럼 함수를 따로 호출해도 된다.) 두번째 파라미터나 Test패널에 표시될 메시지이다.
fireunit.compare("", "", "") : 첫번째와 두번째 파라미터의 문자열을 비교하고 세번째 파라미터나 표시될 메시지다.
fireunit.reCompare( , "", "") : 정규식으로 문자열을 검사한다. 첫번째 파라미터에 정규식이 들어가고 두번째 파라미터에 검사할 문자열이 들어가고 세번째 파라미터가 표시될 메시지이다.

fireunit.mouseDown(target)
fireunit.click(target)
fireunit.focus(target)
fireunit.key(target, "a") : 위 4개의 메서드는 가짜로 이벤트(각 onmousedown, onclick, onfucus, onkeypress)를 발생시킨다. 파라미터로는 이벤트를 발생시킬 엘리먼트를 넘겨준다. key의 경우에는 두번째 파라미터로 keypress로 눌려질 키를 입력한다.

fireunit.value("", "") : value는 대상엘리먼트에 값을 입력한다. 첫번째 파라미터에 대상엘리먼트(input등)의 id값을 주고 두번째 파라미터에 넣을 값을 준다.

fireunit.testDone() : 테스트를 수행하는 메서드로 항상 테스트코드 맨 아래에 있어야 테스트코드가 실행된다.


위쪽에 메서드를 나열한대로 이밖에도 몇가지가 더 있는데 어떻게 사용하는 건지 잘 모르겠다. 특히 fireunit.runTests();는 꽤나 유용할 것 같은데 죽어도 동작을 안한다. 어떻게 사용하는건지.. (누가 이 우매한 중생을 좀... ㅡ..ㅡ 존레식이 예제소스까지 써놨는데도 따라를 못하네.. ㅠ..ㅠ)

이제 막 발표한 상황이기는 하지만 이거 잘 쓰면 꽤나 유용할 듯 싶다. JSSpec은 아직 안써봐서(script.aculo.us에도 Unit Test가 있던데) 어느쪽이 더 좋은지는 잘 모르겠지만.... ㅎ

[Book] 웹 2.0을 이끄는 방탄웹 - 크리에이티브한 웹 표준 기법과 제작 사례..

출처 : Outsider's Dev Story https://blog.outsider.ne.kr/

웹 2.0을 이끄는 방탄웹 - 8점
댄 씨더홈 지음, 박수만 옮김/에이콘출판


이 책의 제목만 처음 봤을때는 방탄웹이라는 익숙치 않은 단어때문에 보안에 관한 책인가 생각이 들었었는데 웹표준을 준수한 웹사이트 구축에 관한 책이다. 읽고 나서도 약간은 어색한 방탄웹이란 단어는(솔직히 이책말고는 이런 표현은 거의 안하기에...) 책의 초반에서도 나오듯이 마치 방단조끼처럼 어느 환경에서도 유연하게 대처할 수 있는 웹사이트를 의미하고 있다.

이 책보다 먼저 쓰여진 "실용예제로 배우는 웹표준"은 내가 보지않아서 정확히 모르겠지만 책의 부제에서도 알수 있듯이 웹표준 HTML과 CSS를 이용한 웹사이트 구축에 대한 실무위주로 되어 있다. 웹사이트를 구축할 때 가장 많이 사용하면서도 HTML+CSS를 사용할 때 가장 기본적으로 접해야 하는 폰트사이즈, float, 메뉴바, 테이블, 레이아웃등 차례차례 지금은 어떻게 쓰이고 있고 어떤 점이 문제이고 그럼 어떻게 구축해야 하는지를 설명하고 있다. 코드하나하나를 친절하게 설명하고 있기 때문에 초보자가 읽기에도 어렵지 않게 다가갈 수 있다.

물론 실무위주이기 때문에 그 기반이 되는 개념은 일단 전제로 하고 넘어간다. 웹표준을 해야하나 말아야 하나 하는 케케묵은 논쟁은 크게 언급하지 않는다. 물론 기존의 방식이 어떤 잘못된 점이 있는가 하는 점은 충분히 집고 넘어가지만 이 모든 건 대부분의 브라우저를 지원하고 웹접근성을 최대한 지원하겠다는 생각에는 충분히 공감을 하고 있어야 크게 거부감 없이 읽을 것이다. 그럴 필요가 없다고 생각한다면 다른 책에서 그런 부분을 더 고민하고 읽는게 나을것이다.

상당히 기본에 충실하고 처음 CSS를 본격적으로 사용하고자 한다면 도움되는 부분이 많다고 생각된다. 구성도 괜찮아서 구조적인 HTML 구성에 대한 개념을 잡고 CSS적용에 대한 접근을 어떻게 해야 할지에도 큰 도움이 되리라고 생각한다. 다만 난이도는 낮은 편이다. 어떤 것이든 기초가 가장 중요한 것이지만 실무에서는 훨씬 다양한 상황에 처하게 되는데 이책은 기초에 충실한 것이 약간은 아쉽다. 처음 접하려고 하는 사람한테는 큰 도움이 될 듯...

스펙이나 그런것들이 있긴 하지만 개인적으로는 웹표준 관련 책들중에서 구조적 HTML에서 각 태그의 기능과 용도등에 대해 정리하거나 CSS에서 각 사용법에 대해서 정리된 레퍼런스격인 책이 없는건 좀 아쉽다.

[JS]Select Box에서 선택한 Option의 Text값 가져오기..

출처 : Outsider's Dev Story https://blog.outsider.ne.kr/

보통 셀렉트박스를 사용할 때 Ajax전송등 사용할 때 선택한 Option에서 value값을 사용하기는 하지만 상황에 따라서는 표시한 텍스트값을 가져와야 할 경우가 있다. 물론 처음 넣을 때 value값 안에 중간연결 기호(, / 등등)를 넣어서 할수도 있지만 텍스트가 띄어쓰기가 있을수고 있고 길이가 길면 그렇게 하는 것도 만만치 않다. 이왕 Option에 텍스트가 들어 있는데 굳이 그렇게 하는것도 좀 그렇고...

<option value="0">선택하세요</option>

위처럼 되어 있으면 "선택하세요"라는 텍스트를 가져오고 싶다는 것이다. 어쨌든 가져올 수 있는 코드는 간단하다.


1
2
var target = document.getElementById("testSelect");
target.options[target.selectedIndex].text

보면 뻔한 소스이이다. 셀렉트박스의 Option배열에서 text를 가져오는데 선택한 Option의 위치를 알기 위해서 해달 셀렉트박스의 selectedIndex를 사용한 것이다.

My Comment..
오옷.. 첨으로 포스팅 가져오면서.. 소스를 hilite 통해서 해봤당..
신기하군.. ㅋㅋ.. 앞으로 잘 활용해야지.. 햄하고 똑같은 스탈은 아니지만..
이런 것을 알게해줘서.. 은근 감사.. 혼자였다면, 검색 안해보고 걍 지나쳤을 텐데.. ㅎㅎ..

[Talk]hilite.me 를 알게되다..

햄의 블로그에서 글들을 퍼오다 보면, 신기했던 것이 소스 코드를 어떻게 저런식으로..
붙여서 넣을까 였다..근데 머 내가 아는게 있나.. 블로그도 첨인데.. ㅡㅡ..
그래서 그냥 냅다 붙여다 넣었다.. 약간의 구분만 되도록 하고.. [무식하게..;;]

그런데 그 이유..??..방법을 어제 알았다.. 그것 또한 햄의 블로그를 보다가 알게 되었는데..
Syntax Highlight 였다.. 근데 해당 명칭을 검색해보니.. [많이는 안해봤당..귀차;; ㅠ]

대표적으로 SyntaxHighlighter 와 highlightjs 가 나오더란..
두 곳 다 가서 봤는데.. 기본적으로 무엇인가를 설치하고 어쩌고 해야되더라..

지금 내 근무환경이 무엇을 설치해서 유지하기가 힘들다..
보안이라는 명목하에.. 하루가 지나면 어지간한건 초기화되버려서..
그나마 즐겨찾기나.. 브라우저 옵션 등만 유지가 된다.. [암울.. ㅠ..]
집에서 하라고..?? 난 아직은 햄처럼.. 집에서도 열정적으로 하긴 그렇고..
회사에서 최대한 시간이 날 때마다.. 쏟아 부을 생각이라규.. +_+..

무튼 머 상황이 그러니.. 그건 별 수 없고.. 위 두곳을 위주로 기웃거리다보니..
일반 웹 에서 제공해주는.. 변환 기능이 있는 사이트도 있어서..
옳다쿠나!!!.. 테스트를 몇번 해보고 그것을 사용하기로 해찌롱.. 후훗..

우선 사이는 hilite.me 다.. 아래 화면이 첫 메인인데.. 대충 봐도.. UI가 똭!!!..



상당히 직관적인데.. 아래 화면 왼쪽 Source code 공간에 자신이 원하는 소스를 붙여넣고..
빨강색으로 표시한 Language 를 선택해준다.. 근데 솔직히 자신이 어떤 언어의..
소스코드를 가지고 왔다고 해서.. 꼭 그것을 선택할 필요는 없다.. [Java, Jsp, Js 등등..]

왜냐믄.. 각각 언어의 Style 에 따라서 변하는 형태가 틀려서.. 결국에는..
자신이 원하는 언어 + 스타일을 지정할 듯하기 때문.. [물론.. 나도 그렇공..]
결론은.. 픽스된 것은 없다 이거지..




위에서 잠시 언급한것처럼.. Language 를 선택하고 나면, Style 을 선택해준다..
난 개인적으로 fruity 가 맘에 들긴 하는데.. javascript native 도 나쁘지는 않다..
[선택은.. 이것저것 눌러보고서.. 본인이 원하는 것을 하면 좋을 듯..]



위처럼.. 코드도 붙여넣고.. 옵션도 다 선택하고나면.. 누가 알려주지 않아도..
해야 될 듯한.. Hightlight! 버튼을 클릭..!!..
그럼 아래처럼 상태가 보여지고, 오른쪽 HTML 공간에 해당 소스 코드가 나온다..



포스팅을 위한 소스코드를 다 변환했다면, 우리는 이제 무엇을 해야되나..
HTML 소스 코드를 복사해서.. 블로그에 글을 쓰면 된다..
단, 글쓰기 할 때.. 대부분 에디터가 그럴테지만, HTML 로 글쓰기를 해서..
원하는 곳에 붙여 넣어주면.. 그것으로 끝.. 참 쉽죠잉.. 후후..
[블로그에 올리는 것도 캡쳐를 할까 했지만, 대부분 블로그 하는 사람들이라면..
알것이라고 생각한다.. 아니 생각하고 싶다.. 100%는 아닐것이기에..]

참고로.. 언어와 스타일 선택하는 공간 옆쪽에.. Line numbers 라는게 있는데..
이것을 선택하면.. 우리가 붙여넣은 소스코드 앞에 라인 수가 표시 된다..

웅키키.. 이것으로 나의 포스팅 아닌 포스팅.. 첫 포스팅..
끝이다..

2016.02.24 추가..
포스팅을 가져오면서.. 계속 소스를 hilite 통해서 변경하다 보니 알게 되었는데..
Language 선택이 셀렉트[콤보] 박스로 되어있다고 해서.. 꼭 마우스로 할 필요 없다..
영문 자판을 걍 치면, 그쪽으로 간다능..

흠.. 생각보다 포스팅이라는게 참 힘들구나.. 싶다..
걍 머리로 이렇게 하면 되겠다 하는 생각을 하는 것과..
실제 글로 적다보니.. 이거 머.. 아침부터 출근해서.. 건 1시간 가까이..
적고 지우고를 반복 중이다.. ㅡㅡ.. 햄은 대체 어떻게 한겨.. 흐미..
처음에는 힘들긴 하지만 계속 무엇인가를..
포스팅 하다보면, 나만의 노하우도 생길것이고, 틀이란 것이 잡히것지..
별거 아닌거 올렸는데.. 겁니 뿌듯하네.. ㅋㅋㅋㅋ.. ^____________________,^v