IT_Programming/PHP

프레임과 Javascript를 사용해서 서버의 부하 최소화

JJun ™ 2007. 9. 30. 11:53
LONG
 

 

 

 

완성된 HTML 태그 생성 라이브러리 입니다.
앞으로 강좌에서 이 함수들에 대한 설명은 생략하고 사용하겠습니다.
tags.js로 저장해서 top.html에서 불러 씁니다.

////////////////////////////////////////////////////////////////////////////////
// General Tags
////////////////////////////////////////////////////////////////////////////////

function _anchor(body, link, css, id) {
        var s= '<a href="' + link + '"';
        if (css) s += ' class="' + css + '"';
        if (id) s += ' id="' + id + '"';
        return s + '>' + body + '</a>';
}

function _div(body, css, id, p, l, t, w, h, v, z, bg, bgimg) {
        var s = '<div style="';
        s += (p) ? 'position:absolute;' : 'position:relative;';
        if (l) s += ' left:' + l + 'px;';
        if (t) s += ' top:' + t + 'px;';
        if (w) s += ' width:' + w + 'px;';
        if (h) s += ' height:' + h + 'px;';
        if (v) s += ' visibility:' + v + ';';
        if (z) s += ' z-index:' + z + ';';
        if (bg) s += ' background-color:' + bg + ';';
        if (bgimg) s += ' background-image: url(' + bgimg + '); background-repeat: repeat;';
        s += '"';
        if (css) s += ' class="'+css+'"';        
        return s + '>' + body + '</div>';
}

function _nobr(body) { return '<nobr>' + body + '</nobr>'; }

function _pipe() { return '  |  '; }

function _br() { return '<br>'; }

////////////////////////////////////////////////////////////////////////////////
// Image Tags
////////////////////////////////////////////////////////////////////////////////

function _img(src, css, w, h, alt, align, id, border) {
        var s = '<img src="' + src + '"';
        if (css) s += ' class="' + css + '"';
        if (w) s += ' width="' + w + '"';
        if (h) s += ' height="' + h + '"';
        if (alt) s += ' alt="' + alt + '"';
        if (align) s += ' align="' + align + '"';
        if (id) s += ' id="' + id + '"';
        if (!border) border = '0';
        return s + ' border="' + border + '">';
}

function _icon(name, alt, id) {
        var s = '<img src="i/i_' + name + '.gif" alt="' + alt + '"';

        if (id) s += ' id="' + id + '"';
        return s + ' width="18" height="18" border="0" hspace="2">';
}

function _space(w, h) {
        return '<img src="/i/spacer.gif" width="' + w + '" height="' + h + '">';
}

////////////////////////////////////////////////////////////////////////////////
// FORM Tag Generator
////////////////////////////////////////////////////////////////////////////////

// Creating form object through DOM must avoided at all cost.
// Current implementation of form is limited and does not work with table

function _form(body, name, method, action, target, validate, enctype) {
        var s = '<form';
        if (name) s += ' name="' + name + '"';
        if (method) s += ' method="' + method + '"';
        if (action) s += ' action="' + action + '"';
        if (target) s += ' target="' + target + '"';
        if (validate) s += ' onsubmit="' + validate + '"';
        if (enctype) s += ' enctype="' + enctype + '"';
        return s + '>' + body + '</form>';
}

function _select(option, name, onchange, css) {
        var s = '<select name="' + name + '"';
        if (onchange) s += ' onchange="' + onchange + '"';
        if (css) s += ' class="' + css + '"';
        return s + '>' + option + '</select>';
}

// CSS on Option tags are only working on IE. Hence, better be avoided

function _option(value, name, selected) {
        var s = '<option value="' + value + '"';
        if (value == selected) s += ' selected';
        return s + '>' + name + '</option>';
}

////////////////////////////////////////////////////////////////////////////////
// FORM-INPUT Tag Generator
////////////////////////////////////////////////////////////////////////////////

function _text(name, value, size, maxlength, width, css) {
        var s = '<input type="text"';
        if (name) s += ' name="' + name + '"';
        if (size) s += ' size="' + size + '"';
        if (maxlength) s += ' maxlength="' + maxlength + '"';
        if (width) s += ' style="width:' + width + 'px;"';
        if (css) s += ' class="' + css + '"';
        return s + ' value="' + value + '">';
}

function _password(name, size, maxlength, width, css) {
        var s = '<input type="password"';
        if (name) s += ' name="' + name + '"';
        if (size) s += ' size="' + size + '"';
        if (maxlength) s += ' maxlength="' + maxlength + '"';
        if (width) s += ' style="width:' + width + 'px;"';
        if (css) s += ' class="' + css + '"';
        return s + ' value="oldpass">';
}

function _file(name) {
        var s = '<input type="file"';
        if (name) s += ' name="' + name + '"';
        return s + '>';
}

function _hidden(name, value) {
        var s = '<input type="hidden" name="' + name + '"';
        return s + ' value="' + value + '">';
}

function _checkbox(name, value, checked, onclick) {
        var s = '<input type="checkbox" name="' + name + '"';
        if (checked) s += ' checked';
        if (onclick) s += ' onclick="' + onclick + '"';
        return s + ' value="' + value + '">';
}

function _image(name, src, w, h, alt) {
        var s = '<input type="image" src="' + src + '"';
        if (name) s += ' name="' + name + '"';
        if (w) s += ' width="' + w + '"';
        if (h) s += ' height="' + h + '"';
        if (alt) s += ' alt="' + alt + '"';
        return s + '>';
}

function _button(name, value, onclick) {
        var s = '<input type="button" style="color: #000000;"';
        if (name) s += ' name="' + name + '"';
        if (value) s += ' value="' + value + '"';
        if (onclick) s += ' onclick="' + onclick + '"';
        return s + '>';
}

 

 

 

5. PHP와 Javascript에서 클라스를 사용해서 4번 강좌 내용 획기적으로 개선하기

5.1 PHP클라스의 기본모델과 개선모델.
우선 보기 쉬운 기본 모델부터 만들어 볼까요?

class CNEWS {
        function CNEWS($r) {
                $this->id = $r->ID;
                $this->registered = $r->REGISTERED;
                $this->title = $r->TITLE;
                $this->text = $r->TEXT;
        }

        function clientize() {
                return $s = "new CNEWS($this->id, '$this->registered', '" . rawurlencode($this->title) . "', '" . rawurlencode($this->text) . "');
        }
}

이대로도 훌륭히 작업을 수행할 수 있습니다. NEWS테이블에서 가져온 레코드 하나를 가지고 클라스 내부에 저장한다음 clientize()함수를 통해서 Javascript 코드로 만들어 주는 거지요.

하지만 저는 기능상으로는 똑같지만 다른 형식의 접근을 하려고 하는데요.
지금 방법으로는 테이블 하나만 쓰는 경우에는 상관이 없지만 여러개의 테이블을 쓰는 경우 코드가 점점더 길어지고 관리가 복잡해 지기 때문입니다.
제가 하려는 방법은 우선 베이스 클라스를 하나 만들어 놓고 똑같은 형식의 클라스를 필요한대로 복제해서 쓰려는 거지요.
아래의 두 클라스가 합쳐저서 어떻게 위의 한 클라스와 똑같은 일을 하는지 잘 연구해 보세요.

class CBASE {
        function constructor($r) {
                foreach ($r as $m => $v) $this->{strtolower($m)} = $v;         
        }

        function clientize() {
                $s = array();
                $members = $this->__sleep();
                $types = $this->__pillow();
                for($i=0; $i<count($members); $i++) {
                        switch ($types[$i]) {
                                case 0: $s[i] = $this->{$members[$i]}; //Numeric
                                case 1: $s[i] = "'" . $this->{$members[$i]} . "'"; //Simple String
                                case 2: $s[i] = "'" . rawurlencode($this->{$members[$i]}) . "'";
                        }
                }
                return "new " . strtoupper(get_class($this)) . "(" . implode(",", $s) . "')";
        }
}

class CNEWS extends CBASE {
        function CNEWS($r) { $this->constructor($r); }
        function __sleep() { return array('id', 'date', 'title', 'text'); }
        function __pillow() { return array(0, 1, 2, 2); }
}

지금 당장은 별로 나아진게 않보이지만요, 사용되는 클라스가 10개를 넘어서고

또 클라스 멤버변수가 10개 이상씩 될 경우 소스코드 길이가 몇분의 일로 줄어듭니다.

ARTICLE

출처 : http://www.phpschool.com/gnuboard4/bbs/board.php?bo_table=tipntech&wr_id=16616&sca=&sfl=wr_subject%7C%7Cwr_content&stx=%C4%B3%BD%AC&sop=and&page=3

 

[알고리즘] 프레임과 Javascript를 사용해서 서버의 부하 최소화하기. -- 0

 

 

이전에 저도 올린적이 있고 다른 분들도 몇번 올려주신적이 있지만 제 경험을 바탕으로 좀더 체계적으로 기본 개념과 여러가지 사용 예들을 하나하나 다뤄서 Javascript에 익숙하지 않으신 분들을 대상으로 소개 해볼까 합니다.
반응이 좋으면 앞으로 몇번에 걸쳐서 다뤄볼 내용을 미리 소개 해볼께요.

1. 장, 단점과 서버 부하 최소화를 위한 기본개념, 코드규칙 정하기
2. 페이지마다 반복해서 사용되는 Header(네비게이션 메뉴 포함) 와 Footer(주로 Copyright 링크 등) 캐쉬해서 사용하기.
3. 페이지에 많이 사용되는 이미지(마우스오버 메뉴이미지, 버튼 이미지 등) 캐쉬해서 사용하기
4. Javascript로 페이지를 완전히 파싱하기와 이에 따른 멀티브라우저, 속도개선, 소스 스크램블등의 방법 설명과 간단한 게시판의 경우로 예제 다루기.
5. PHP와 Javascript에서 클라스를 사용해서 4번 강좌 내용 획기적으로 개선하기 (소스코드 길이와 관리성 면에서)
6. Database Table - PHP Class - Javascript Class 연동해서 디비 테이블을여러 개 이용하는 경우 오브젝트 오리엔티드 프로그램을 효율적으로 이용하기와 이에따른 브라우저 호환성과 속도 등의 노하우
7. 메인 프레임의 Refresh없이 DB서버와의 통신을 통한 메인 프레임 내용 업데이트하기, 효율성 극대화와 자주 접하는 실수, 브라우저 버그 해결하는 노하우
8. 7번과 연계해서 클라이언트 입력에 따라서 서버 DB업데이트 하기. 이때 데이터 베이스 부하 최소화를 위해서 Object Orient모델을 이용, 클라이언트 사이드에서 Database Integrity 체크하기.

 

 

1-1 장점
서버와의 통신을 최소화해서 서버의 부하를 줄일 수 있다.
페이지의 리프레쉬를 최소화해서 방문자의 기다리는 시간을 줄일 수 있다.
웹페이지의 체계적인 관리가 용이해진다.

1-2 단점
Javascript를 사용하지 않는 방문자를 지원하지 않는다.
절대적으로 느린컴퓨터에서 Javascript로 인해 방문자가 페이지 로딩 속도의 저하를 경험할 수 있다.
프로그램시 약간의 버그가 페이지 전체를 않보이게 할 수 있다.

1.3 기본개념 및 준비.
웹페이지가 리프레쉬 되더라도 계속 유지되는 기억장소로는 서버의 세션과 클라이언트의 쿠키가 주로 쓰인다.
하지만 서버의 세션의 경우 서버자원고갈 문제로, 쿠키의 경우 메모리 용량 제한의 문제로 해서 실질적인 효용이 제한되어 있다.
지금부터 우리가 사용하는 방법은 페이지를 두 프레임으로 나누어서 top프레임은 변수와 함수들의 기억공간으로,
main프레임은 유저와의 인터페이스로, sub프레임은 서버와의 통신을 사용한다.

기본적으로 시작부터 필요한 파일은 다음과 같다.
index.html -> top 프레임, 기억장소로 사용됨
home.html -> main 프레임에 기본으로 로드되는 페이지, 유저 인터페이스로 사용됨
sub.html -> sub 프레임에 기본으로 로드되는 히든 페이지, 서버와의 통신을 위해 사용됨
base.js -> top프레임에서 로드할 글로벌 변수, 함수 & 클라스들을 정의한 Javascript페이지
base.css -> main프레임에서 사용할 스타일 시트 파일

index의 내용은 다음과 같다. 이 내용은 한번 만들어 놓으면 다시는 수정할 필요가 없다.
top프레임에 저장되는 함수들은 <script src...>로 참조되는 base.js에서 정의되며,
저장되는 변수들은 main과 sub프레임에 의해서 관리된다.
<html>
<head>
<title>사이트 타이틀</title>
<script src="base.js"></script>
</head>
<frameset rows="100%, 0" frameborder="NO" border="0" framespacing="0">
<frame name="main" src="home.html">
<frame name="sub" noresize src="sub.html">
</frameset>
</html>

sub프레임은 히든 프레임이기 때문에 높이가 0으로 지정되어서 방문자에게 보이지 않는 다는 것을 주목하자.
호기심 많은 방문자가 소스보기할 경우를 대비해서 index.html파일 자체는 최소한 단순하게 한 것을 주목하자.

프레임간의 엑세스는 top프레임을 통해서만 하며 sub프레임과 main 프레임 사이의 직접적인 함수 호출이나 변수 참조는 금지하기로 합니다.

1-4 코드규칙
강좌가 진행되는 동안 사용할 코드 규칙으로, 빠진 것은 진행도중에 한가지씩 더 추가한다.
1-4-1 html 태그는 반드시 그 태그를 다루는 글로벌 함수를 이용해서만 쓴다.
1-4-2 본문의 출력은 100% 자바스크립트를 통해서 한다.
1-4-3 DOM의 사용은 Dynamic 업데이트가 반드시 필요한 곳으로 제한하며 호환성을 위해서 DOM 1.0의 기본 함수만을 사용한다.
1-4-4 글로벌 함수의 이름은 _ 으로 시작한다.
1-4-5 5.0브라우저 이상을 지원하며 넷스케입 4.0은 지원은 고려하지 않는다.
1-4-5 문자열 정의를 위해서 PHP에서는 "을, Javascript에서는 '을 사용하는 것을 우선으로 한다.
1-4-6 화면의 배치를 위해서 화면 전체를 하나의 테이블로 사용하는 대신, 스타일을 사용한다.


자 그럼 이제 본격적인 강좌에 들어갈 내용이 슬슬 준비가 된 것 같네요.
정말 써먹을 것이 나오기 시작하는 2편을 기대해 주시길.. -_-;;

 

 

2. 페이지마다 반복해서 사용되는 Header(네비게이션 메뉴 포함) 와 Footer(주로 Copyright 링크 등) 캐쉬해서 사용하기.

2-1. 필요한 함수 정의와 사용법
다음의 함수들을 base.js에 정의해 봅시다. 앞으로 강의가 끝날때 까지 두고두고 쓰일 함수들입니다.
우선 한가지 집고 넘어갈 것이 있는데요, 이렇게 다들 함수로 만들어서 함수호출을 너무많이 하면 페이지 로딩 속도가 느려지지 않는가? 하고 걱정하실 필요가 없다는 것입니다. 물론 함수 호출에 전혀 시간이 않걸리는 것은 아니지만 웹프로그램의 특성상 빈번하게 처리되는 이미지 처리에 비해서는 아무것도 아니기 때문입니다.

데이터 파싱 함수들입니다. bool 타입의 경우 원시적으로 1:0으로 하는 이유는 PHP와 SQL과 데이터 호환을 쉽게 하기 위해서 입니다.

function _int(v) {return isNaN(parseInt(v, 10)) ? 0 : parseInt(v);}
function _hex(v) {return isNaN(parseInt(v, 16)) ? 0 : parseInt(v, 16);}
function _float(v) {return isNaN(parseFloat(v)) ? 0 : parseFloat(v);}
function _date(v) {return isNaN(new Date(v)) ? "" : new Date(v);}
function _bool(v) {return parseInt(v) ? 1 : 0;}

<A HREF> 대신 사용할 함수 입니다. 보시다 시피 꼭 필요하지 않은 입력값은 입력하지 않아도 되도록 처리했습니다.
아주 많이 쓰이는 함수라 속도문제도 있고, 프로그래머의 지능을 믿어서? 에러처리가 없습니다.

function _a(body, link, css, id) {
       var s= '<a href="' + link + '"';
       if (css) s += ' class="' + css + '"';
       if (id) s += ' id="' + id + '"';
       s += '>' + body + '</a>';
       return s;
}

<DIV> 대신 사용할 함수 입니다. 좀 복잡한 듯 해 보이지만 내용을 읽어보면 별거 아니라는 걸 아시게 될겁니다.
position의 경우 반드시 absolute와 relative중 하나를 정하게 한 것은 이렇게 하지 않으면 브라우저에 따라서 결과가 다르기 때문입니다.

function _div(body, css, p, l, t, w, h, v, z, bg, bgimg, id) {
       var s = '<div style="';
       s += (p == "abs") ? 'position:absolute;' : 'position:relative;';
       if (l) s += ' left:' + l + 'px;';
       if (t) s += ' top:' + t + 'px;';
       if (w) s += ' width:' + w + 'px;';
       if (h) s += ' height:' + h + 'px;';
       if (v) s += ' visibility:' + v + ';';
       if (z) s += ' z-index:' + z + ';';
       if (bg) s += ' background-color:' + bg + ';';
       if (bgimg) s += ' background-image: url(' + bgimg + '); background-repeat: repeat;';
       s += '"';
       if (css) s += ' class="'+css+'"';       
       s += '>' + body + '</div>';
       return s;
}

<IMG>대신 사용할 함수입니다. 대부분의 경우 border=0으로 사용하기 때문에 따로 지정을 하지 않아도 border=0으로 사용하도록 하였습니다.

function _img(src, css, w, h, alt, align, id, border) {
       var s = '<img src="' + src + '"';
       if (css) s += ' class="' + css + '"';
       if (w) s += ' width="' + w + '"';
       if (h) s += ' height="' + h + '"';
       if (alt) s += ' alt="' + alt + '"';
       if (align) s += ' align="' + align + '"';
       if (id) s += ' id="' + id + '"';
       if (!border) border = '0';
       return s + ' border="' + border + '">';
}

다시 짚고 넘어갈 것은 이 함수들은 모두 <script src=base.js></script> 를 통해서 top프레임에만 정의가 됩니다.
main프레임에서는 이 base.js를 사용하지 않기 때문에 직접적으로 이런 함수들을 사용할 수 가 없습니다.
꼭 top._img(); 의 형태로 top프레임을 통해서 사용합니다.

그럼 이 함수들을 이용해서 화면에 홈페이지 로고를 출력하도록 해 볼까요?
home.gif라는 100 x 20의 이미지파일이 있다고 해 봅시다.
참 모든 이미지 파일들은 i 디렉토리 밑에 있다고 가정합니다.

document.write(top._img("/i/home.gif", false, 100, 20));

src, css, w, h값만 주고 그 뒤의 값은 필요없으므로 주지 않았습니다. 간단하지요?

이제 그냥 이미지만 출력하는 것이 아니라 이 이미지를 클릭하면 홈페이지로 이동하도록 해 봅시다.

document.write(top._a(top._img("/i/home.gif", false, 100, 20), "/home.html"));

쉽지요?
이렇게 HTML태그를 직접 사용하는 대신 함수를 사용하면 실수했을 경우 바로 브라우저의 디버깅 기능을 통해서 몇째 줄이 틀렸는지 알수 있기 때문에 결과적으로 더 깔끔하고 실수없는 페이지를 만들 수 있습니다.

마지막으로 이 이미지를 아무데나 표시하는 게 아니라 화면 구조상 위에서 100픽셀, 왼쪽에서 100픽셀 떨어진 곳에 출력되도록 해 봅시다.

document.write(top._div(top._a(top._img("/i/home.gif", false, 100, 20), "/home.html"), false, "abs", 100, 100, 100, 20));

아주 간단하지요?

이쯤에서 하나 짚고 넘어갈게 있는데요.
어떤분들은 코드를 뭐 이따위로 지저분하게 하느냐? 여러줄로 나누어 써서 한줄에는 함수 하나만 사용하도록 해서 보기 쉽게 해야 하지 않느냐? 하고 생각할 겁니다.
또 그렇게 학교에서들 배우셨을 거구요. 저도 학생 시절에는 그렇게 했습니다.

그러나 날이갈수록 이렇게 하는게 코드가 짧아서 오히려 제 눈에는 더 잘들어오고 더 관리하기가 좋더군요.
좀 눈에 거슬리더라도 이해 해 주세요.

바로 이런 제 취향 때문에 앞으로 boolean 변수값 false 대신에 0을 사용하고 또 가장 많이 사용하는 변수명 top 대신에 t를, document대신 d를, document.write()대신 p() (Print의 약자) 를 사용하겠습니다.
위의 예제를 다시 쓰면 이렇게 되겠네요.
p(t._div(t._a(t._img("/i/home.gif", 0, 100, 20), "/home.html"), 0, "abs", 100, 100, 100, 20));


또한가지 다른 프레임에서 엑세스 할 함수들과 변수들 이름을 _로 시작하는 것에는 한가지 중요한 이유가 있습니다. 지적재산권 및 사이트 보안을 위해서 나중에 이 base.js파일 내용을 사정없이 encrypt, scramble할 텐데요, 이때 외부와의 인터페이스를 가지는 바로 _로 시작되는 변수와 함수명은 스크램블 하지 않기 위해서 구분하는 의미입니다.

그럼 다음회에 이 함수들을 이용해서 Header와 Footer를 만들어서 그걸 top프레임에 캐쉬 해보지요.

 

 

2. 페이지마다 반복해서 사용되는 Header(네비게이션 메뉴 포함) 와 Footer(주로 Copyright 링크 등) 캐쉬해서 사용하기.

2-2 Header 내용을 HTML코드로 출력하는 함수 만들기

지난 번에 정의한 함수들을 이용해서 base.js에 새로운 함수를 하나 만들어 봅시다.
페이지마다 반복해서 보여지는 헤더의 로고와 메뉴 이미지들을 출력하는 HTML코드를 만드는 함수입니다.

메뉴이미지를 만들기 위해서 우리가 개발할 사이트의 구조와 파일 이름들을 정해야 겠지요?

메인 프레임에 들어갈 페이지는 이미 소개한 home.html 이외에
news.html : 오늘의 뉴스 리스트 (관리자 업데이트 방문자 열람)
list.html : 게시물 리스트 (방문자 업데이트 / 열람)
register.html : 가입하기
login.html : 로그인하기
입니다.

이 페이지들을 링크할 이미지 이름들은 페이지 이름대로, 그러나 마우스가 올라갔을 때 효과를 위해서 두개식 입니다.
home_on.gif, home_off.gif, news_on.gif, news_off.gif, list_on.gif, list_off.gif,
register_on.gif, register_off.gif, login_on.gif, login_off.gif
이렇게 되겠지요. 각 이미지 파일의 크기는 가로 70, 세로 20 으로 합시다.
여기다 지난번에 정한 로고파일 home.gif(100x20), 1x1의 투명 이미지 space.gif, 배경 이미지 bg.png 도 정해줍시다.

** 여기서 배경 이미지가 gif가 아닌 png인 점에 주목합시다. 배경 이미지에 웹 표준 칼라가 아닌 다른 색상이 들어갈 경우(대부분 그렇겠죠?) 웹 브라우저와 시스템에 따라서 색상을 마음대로 변환하거나 디터링 해서 보여주는 경우가 많습니다. 익스 넷스 모두 해당됩니다. 하지만 png로 하면 안전합니다.

자 그럼 저번 강의 때 만든 로고 출력 루틴을 이용해서 로고와 그 밑에 메뉴로 구성된 페이지 헤더를 출력하는 함수를 만들어 봅시다.

우선 골격은 이렇습니다.

var header //만들어진 header의 캐쉬

function _header() {
        if (!header) {
                // header가 이미 존재할 때는 그냥 쓰고 헤더가 없을때만 여기서 헤더를 생성한다.
        }
        return header
}

간단하지요?

그럼 여기다 헤더 내용까지 만들어 봅시다.
이렇게 자세하게 하는건 사실 독자의 지적 능력에 대한 모독이자 제 손가락에 대한 혹사지만 처음이니까 한번 해 봅시다. -_-;;

var header //만들어진 header의 캐쉬

function _header() {
        if (!header) {
                var s = "", menu = Array('home', 'news', 'list', 'register', 'login');
                for (var i=0; i<menu.length; i++) s += _a(_img("/i/" + menu[i] + ".gif", 0, 70, 20), "/" + menu[i] + ".html");
                header = _div(_a(_img("/i/home.gif", 0, 100, 20), "/" + menu[i] + ".html"), 0, "abs", 10, 10, 100, 20)
                        + _div(s, 0, "abs", 20, 35, 350, 20);
        }
        return header;
}

이제 footer도 만들어 볼까요? footer는 따로 이미지는 없고 그저 contact.html, copyright.html, about.html 에 대한 링크라고 합시다.

footer하고 본문을 구분하는 줄도 한줄 좍 그어야 하니까 새로운 함수 _hr()을 우선 추가합시다.

function _hr() {
        return "<hr><br>";
}

이렇게 간단한 태그까지 함수로 만드는 이유는 나중에 맘이 바뀌어서 <hr>태그 대신에 이미지를 사용하더라도 간단하게 해결하기 위해서 입니다.

var footer

function _footer() {
        if (!footer) {
                var s = "", menu = Array('contact', 'copyright', 'about');
                for (var i=0; i<menu.length; i++) s += _a(" [ " + menu[i].toUpperCase() + " ] ", "/" + menu[i] + ".html");
                footer = _hr() + s;
        }
        return footer;
}

너무너무 간단합니다.
이제 home.html에서 활용 해 볼까요?

<html>
<body background = "/i/bg.png">
<script>
        var t = top;
        document.write(t.header() + t._div("안녕하세요 홈페이지 입니다." + t._footer(), 0, "abs", 10, 70));
</script>
</body>
</html>

한가지 조심할 것은 호기심많은 방문자가 (방문자는 항상 호기심이 많습니다) 주소란에 바로 home.html을 쳐 넣어서 에러가 날 경우입니다.
그러니 예방책을 추가합시다.

<html>
<body background = "/i/bg.png">
<script>
        var t = top;
        var server = "http://www.somecompany.com/"; //자기 주소로 바꿔주세요.
        if (t.location != server) t.location.replace(server);

        document.write(t.header() + t._div("안녕하세요 홈페이지 입니다." + t._footer(), 0, "abs", 10, 70));
</script>
</body>
</html>


다 좋은데 글자체가 밋밋하죠? 양념을 치는 기분으로 다음 내용을 base.css에 추가해 봅시다.

body, table, input, select, option, textarea { font-family: Arial, Helvetica, sans-serif; font-size: 10pt; color: #333333; margin: 0px;}

A {text-decoration:underline;}
A:hover {color:#000000; text-decoration:underline;}

.footer { font-size: 8pt; color: #FFFFFF; text-decoration: none; text-align: center }

아주 좋습니다. 이 css를 적용해서 footer()를 다시 쓰면.

function _footer() {
        if (!footer) {
                var s = "", menu = Array('contact', 'copyright', 'about');
                for (var i=0; i<menu.length; i++) {
                        s += _a(" [ " + menu[i].toUpperCase() + " ] ", "/" + menu[i] + ".html", "footer");
                }
                footer = _hr() + s;
        }
        return footer;
}

_a() 함수에서 css값 자리에 "footer"라고 추가해 준게 보이시죠?

home.html에서도 다음을 추가 해 주어야 겠지요

<head>
<link rel="stylesheet" href="base.css" type="text/css">
</head>

2번 강좌가 다 끝났습니다. 이걸 자기걸로 만들어야 겠다고 생각하시는 분은 직접 타이핑해서 index.html, base.js, base.css, home.html 등을 만들어 보세요.
단 제가 드린 예제를 그대로 쓰시는 게 아니라 직접 만들어 보신 홈페이지나 다른 곳의 홈페이지 중 하나를 가져다가 적용해 보세요. 귀찮더라도 그게 제일 좋은 방법일 겁니다.

완전히 이해하셔야 내일 3번 강좌에서 header() 에다가 마우스 롤오버 이미지 기능 적용하는 것도 쉽게 이해하실 겁니다.

그럼.

P.S. 만들어 놓은 걸 올리는게 아니라 써서 올리기 때문에 부족한부분, 바라는 점 등 의견을 남겨주시면 바로바로 적용해서 더 도움이 되도록 쓰겠습니다.
의견 남겨주세요.

 

3. 페이지에 많이 사용되는 이미지(마우스오버 메뉴이미지, 버튼 이미지 등) 캐쉬해서 사용하기

이번은 지난번 까지의 진전상황에서 메뉴 이미지 롤 오버 기능을 추가할 뿐 아니라 롤 오버 되는 이미지들을 top 프레임의 메모리에 로드해놓고 쓰는 방법을 해 볼 차례네요.

우선 지난회 마지막 버전의 home.html을 보세요.


<html>
<head>
<link rel="stylesheet" href="base.css" type="text/css">
</head>
<body background = "/i/bg.png">
<script>
var t = top;
var server = "http://www.somecompany.com/"; //자기 주소로 바꿔주세요.
if (t.location != server) t.location.replace(server);

document.write(t.header() + t._div("안녕하세요 홈페이지 입니다." + t._footer(), 0, "abs", 10, 70));
</script>
</body>
</html>

벌써 눈치 채셨겠지만 본문 스크립트 중 맨 위의 세줄은 모든 페이지에 반복해서 나오겠지요?
이런 것은 따로 스크립트 파일에 넣어 줍시다.

main.js라는 파일을 편집해서

var t = top;
var w = window;
var d = window.document;
var f = window.document.form;
var server = "http://www.somecompany.com/"; //자기 주소로 바꿔주세요.
if (t.location != server) t.location.replace(server);

그럼 새 home.html을 볼까요?


<html>
<head>
<link rel="stylesheet" href="base.css" type="text/css">
<script src="main.js"></script>
</head>
<body background = "/i/bg.png">
<script>
document.write(t.header() + t._div("안녕하세요 홈페이지 입니다." + t._footer(), 0, "abs", 10, 70));
</script>
</body>
</html>

뭐 간단하고 좋네요.
이번회에서는 더이상 home.html을 건드릴 필요가 없습니다.
이대로 그냥 두고 base.js 안에서만의 작업으로 롤오버 기능을 추가할 수 있습니다.

먼저 롤 오버 기능을 추가하기 위해서 base.js에 다음 세가지 함수를 추가해야 합니다.
하나하나 보지요.

function _nav(id, w, h, link, alt) {
return '<a href="' + link + '"'
+ ' onmouseover="t._roll(\'' + id + '\', \'_on\');"'
+ ' onmouseout="t._roll(\'' + id + '\', \'_off\');"'
+ ' onmousedown="t._roll(\'' + id + '\', \'_off\');"'
+ ' onmouseup="t._roll(\'' + id + '\', \'_on\');">'
+ _img("/i/m_" + id + "_" + "_off.gif", 0, w, h, alt, 0, id)
+ '</a>';
}

우선 이 함수는 _header() 에서 바로 사용할 함수로 이전에 사용하던 구절인
s += _a(_img("/i/" + menu[i] + ".gif", 0, 70, 20), "/" + menu[i] + ".html");
를 대체할 함수입니다.

보시다 시피 이미지에 id의 개념이 들어갔지요?
이건 사실 이미지의 이름이라고 할 수 있는데요. 나중에 이 이미지를 엑세스해서 그림을 바꿔놓기 위한 준비작업입니다.
이 함수에서 만들어진 HTML코드도 결국 main프레임에서 사용될 만큼 그 메인 프레임에서 마우스가 올라갔을 때 실행할 top프레임의 함수를 실행하기 위해서
' onmouseover="t._roll(\'' + id + '\', \'_on\');"'
라고 한 부분에 주목하세요.

이 _roll()함수를 정의하기 전에 준비작업이 하나 있는데요.
필요한 메뉴의 on, off이미지를 모두 top프레임의 메모리에 로드하는 겁니다.

function _load_flip_images() {
var id = '';
for (var i=0; i<arguments.length; i++) {
id = arguments[i];
document[id] = new Object();
(document[id].on = new Image()).src = "/i/" + id + "_on.gif";
(document[id].off = new Image()).src = "/i/" + id + "_off.gif";
}
}

몇가지 흔히 못보던 테크닉이 있으실까봐 설명드릴께요.
이 함수에서는 입력값을 전혀 받지 않으면서도 argements라는 변수를 이용해서 자유자재로 입력값을 쓰지요?
입력변수가 몇개이든 간에 다 처리하고 싶을때 이 방법을 많이 씁니다.
따로 함수 정의에서 입력값의 이름을 정해주지 않아도 모든 입력값이 자동으로 arguments 라는 배열변수에 저장이 되지요.
이 갯수대로 하나씩 처리해주면 되는 겁니다.

그다음 넷째 줄에 보면
document[id] = new Object();
라고 나오지요?
우선 document[id] 부분은 메뉴에 사용될 이미지와 똑같은 이름의 변수를 top프레임의 document속에 만들어 주는 거구요.
여기다 바로 이미지를 로드하는 대신 new Object()를 써준 이유는
여기다 로드해야할 이미지가 하나가 아니라 두개이기때문에 이 변수를 Object로 만들어서 그 밑에 이미지를 두개다 붙일려고 한거지요.

이걸 실제로 사용하는 코드는 메인프레임에 넣는게 좋겠죠?
다음 줄을 home.html에서 읽어올 스크립트 파일인 main.js에 추가합시다.
t._load_flip_images("home", "news", "list", "register", "login");


이제 마지막으로 실제 이미지가 바뀌는 걸 제어해 줄 _roll()함수를 정의해 줄 차례인데요.
여기서 우리가 그냥 간과할 수 업는 Win32 IE 5.5+ 만의 독특한 기능이 있습니다.
filters.blendTrans라는 이미지가 점차적으로 바뀌게 해주는 기능이지요.
그래서 브라우저가 이 기능을 지원하는지 하지 않는지에 따라서 두가지 중 한가지 형태의 _roll()함수가 정의되도록 합시다.

if (Image.filters && Image.filters.blendTrans) {
function _roll(id, mode) {
var img = main.document.getElementById(id);
img.filters.blendTrans.stop();
img.filters.blendTrans.Apply();
img.src = document[id][mode].src;
img.filters.blendTrans.Play();
return;
}
}
else {
function _roll(id, mode) {
var img = main.document.getElementById(id);
img.src = document[id][mode].src;
return;
}
}

첫번째 형태의 함수는 두번째 함수에 단지 blendTrans기능이 추가 된 것 뿐이니 두번째 함수만 설명을 할께요.
입력값중 id는 메뉴의 이름, mode 는 "_on"과 "_off"상태를 말하는 겁니다.
main프레임에 사용된 이 메뉴를 표시하는 이미지를 찾아서 소스를 바꾸어야 하는데요.

var img = main.document.getElementById(id);
이 부분이 바로 DOM 1.0의 getElementById() 함수를 이용해서 main프레임의 id를 가진 객체를 연결한 부분입니다.
이 객체에 원래 사용하던 이미지 대신 같은 이름의 on 이나 off이미지를 적용시켜 주면 끝이지요.

한가지 지나가는 점으루요.
이 시점에서 4.0버전 브라우저들을 지원하고 싶으면 아직 늦지는 않았습니다.
getElementById를 바로 쓰는 대신에 getElementByName라는 비슷한 함수를 정의한다음,
브라우저를 체크해서 getElementById함수가 지원되지 않는 경우 IE 4.0이나 Netscape 4.0의 DHTML모델을 사용하게 하면 간단하지요.
제가 그렇게 하지 않는 이유는 앞으로 계속 사용할 기능중 Netscape에서 지원되지 않는 것들도 있고,
또 굳이 4.0버전 브라우저들까지 지원해야 할 필요를 못느껴서 입니다.
만약 고객과의 계약사항에 의해 지원해야 한다면 굳이 할 수는 있는 일이지요.

그럼 여태까지 만든 걸 모두 _header()함수에 적용시켜 볼까요?

우선 저번 시간에 만들어진 코드를 보면 이렇지요.

var header //만들어진 header의 캐쉬
function _header() {
if (!header) {
var s = "", menu = Array('home', 'news', 'list', 'register', 'login');
for (var i=0; i<menu.length; i++) s += _a(_img("/i/" + menu[i] + ".gif", 0, 70, 20), "/" + menu[i] + ".html");
header = _div(_a(_img("/i/home.gif", 0, 100, 20), "/" + menu[i] + ".html"), 0, "abs", 10, 10, 100, 20)
+ _div(s, 0, "abs", 20, 35, 350, 20);
}
return header;
}

새로운 코드는

var header //만들어진 header의 캐쉬
function _header() {
if (!header) {
var s = "", menu = Array('home', 'news', 'list', 'register', 'login');

for (var i=0; i<menu.length; i++) {
s += _nav(menu[i], 70, 20, "/" + menu[i] + ".html", menu[i].toUpperCase())
}

header = _div(_a(_img("/i/home.gif", 0, 100, 20), "/" + menu[i] + ".html"), 0, "abs", 10, 10, 100, 20)
+ _div(s, 0, "abs", 20, 35, 350, 20);
}
return header;
}

어때요?
쓸만한게 나오니까 조금씩 어려워지지요?
확실하게 이해하고 넘어가지 않으면 다음 내용을 계속 이해할 수 없습니다.

질문이 있으면 코멘트로 남겨서
꼭 이해하고 넘어갑시다.

 

 

 

4. Javascript로 페이지를 완전히 파싱하기와 이에 따른 멀티브라우저, 속도개선, 소스 스크램블등의 방법 설명과 간단한 게시판의 경우로 예제 다루기.

4-1 Javascript로 페이지를 완전히 파싱하기

지난회까지 보신대로 이미 메인 프레임의 모든 내용을 Javascript를 통해서 파싱하고 있지요.
코멘트에 클라이언트 컴퓨터를 느리게 만드는 것이 아닌가 하는 의문을 제기하신 분도 있는데요.
이정도 수준의 처리로는 거의 느껴질정도의 부하증가는 없습니다.

이런식으로 처리할 때 주의할 점은 처음에 세웠던 계획과 규칙을 잘 지켜서 프로그램 해야만 나중에 소스가 지저분해지거나 관리가 복잡해 지는 것을 막고 계속 깔끔한 처리를 해 줄수가 있습니다.

지켜야할 몇가지 규칙이라면요

4-1-1 데이터는 모두 top프레임의 메모리에만 저장한다
4-1-2 함수의 경우 일반적인 것은 top프레임에 저장되는 base.js에, main프레임에서 따로 필요한 것은 main.js에 잘 구분해서 정의한다.
4-1-3 top프레임에 저장되는 변수와 함수중 외부 프레임에서 엑세스 해야 할 것들은 이름을 _ 로 시작한다.
4-1-4 main프레임과 서버와의 통신 (페이지 리프레쉬)를 최소한 줄이기 위해서 가능한 것들은 sub프레임에서의 통신을 통해 처리한다
4-1-5 main프레임에서는 직접적으로 HTML태그를 사용하지 않는다.

4-2 멀티 브라우저, 속도개선, 소스 스크램블

Javascript 는 1.1 에서 지원되는 함수만을 씁니다.
DOM 1.0에서는 getElementById, createElement, appendChild 만을 사용하고 다른 함수들은 없다고 생각합니다.
사용자 입력데이터에 대한 체크(제대로 된 이메일 주소인지, 필요한 입력필드를 다 체웠는지 등)에 대한 체크는 javascript에서 다 합니다.
Object.innerHTML 함수는 4.0브라우저에서 문제가 많지만 너무 지나치게 남용하지만 않으면 5.0이상 브라우저에서는 아주 잘 작동합니다. 동적으로 업데이트하기 위해서 최선의 방법입니다. 비슷한 역할을 하는 DOM 함수들은 아직도 버그가 너무 많기 때문에 대안이 없는 유일한 방법이기도 합니다.
<form> 태그에 관련되는 HTML코드들은 아직 createElement함수에서 잘 지원되지 않습니다.
동적인 아이템중에서 갯수가 일정한 것은 createElement로 그때그때 만들어 주는 것이 아니라 정적으로 처음부터 만들어 놓고 object.style.visibility 필드 값을 통해서 보여주고 감춥니다.
완성되고 난 js 파일은 우선 1단계로 php에서 Encode하고 Javascript에서 unencrypt해주는 방법으로 encode한 다음 다시 원래 js 파일 자체를 _로 시작하는 이름은 제외한체 스크램블 해 줍니다. (인터넷에 많이 있는 스크램블 소스를 약간 수정해서 사용)
단 완전하게 방문자가 보는 것을 막는 것이 아니라 보기 어렵게 하는 것 뿐이라는 것은 인정해야 합니다.

4-3. 간단한 게시판 예제.

저번회에 정의해준 페이지 중에서
news.html은 관리자가 업데이트해주고 방문자는 열람만 하는 기본적인 게시판입니다.
list.html은 일반 의미의 게시판이지요.

우선 news.html을 가지고 작업해봅시다.
사용하는 디비 테이블은 news입니다. 이 내용에 관리자가 매일 몇개씩의 news를 올려줍니다.

news 테이블의 구조는

id (int) 일련번호
registered (date) 올린 일시
title (varchar(40)) 게시물 제목
text (varchar(2048) 뉴스내용

로 간단하게 합니다.
참고로 DB를 옵티마이즈 하는 부분은 이번 강좌의 범위에 벗어나므로 다루지 않겠습니다.

PHP에서 각자 자기가 사용하는 데이터베이스 프로그램에 맞춰서 세 함수를 만들어 주세요.

sql($sql_text) 디비에다 $sql_text에 들어있는 select문을 보내서 리턴되는 레코드값이 하나만 있을 경우 그 레코드를 패치 하는 함수입니다.
query sql_q($sql_text) 디비에다 select문을 보내서 리턴되는 레코드 값이 만을경우 쿼리 자체를 리턴 하거나 아니면 update문이나 delete문을 실행하는 함수입니다.
record sql_r($query) sql_q()를 통해서 리턴된 쿼리를 입력하면 하나씩 레코드를 패치해주는 함수입니다.

그럼 news.html의 기본형을 만들어 보기 시작할까요?

참 이번 코드에서 뉴스를 리스트해주기 위해서 테이블을 사용해야 하므로 테이블 태그를 만들어주는 함수 세개를 우선 base.js에 추가합시다.

// <table></table>
function _table(body, css, border, spacing, padding, w, h) {
       var s = '<table';
       s += ' border="' + border + '"';
       s += ' cellspacing="' + spacing + '"';
       s += ' cellpadding="' + padding + '"';
       if (w) s += ' width="' + w + '"';
       if (h) s += ' height="' + h + '"';
       if (css) s += ' class="' + css + '"';
       s += '>' + body + '</table>';
       return s;
}

// <tr></tr>
function _tr(body, css, w, h, valign) {
       var s = '<tr';
       if (css) s += ' class="' + css + '"';
       if (valign) s += ' valign="' + valign + '"';
       s += '>' + body + '</tr>';
       return s;
}

// <td></td>
function _td(body, css, w, h, bg, cols, rows, align) {
       var s = '<td';
       if (css) s += ' class="' + css + '"';
       if (w) s += ' width="' + w + '"';
       if (h) s += ' height="' + h + '"';
       if (bg) s += ' bgcolor="' + bg + '"';
       if (cols) s += ' colspan="' + cols + '"';
       if (rows) s += ' rowspan="' + rows + '"';
       if (align) s += ' align="' + align + '"';
       s += '>' + body + '</td>';
       return s;
}

무슨 함순지 알겠지요?
뉴스 리스트 테이블을 보기좋게 출력하기 위해서 몇가지 스타일을 base.css에 추가할까요?

.newslayer { width: 600px; backgroundColor: #9999FF; }
.newstable { width: 100%; border: 0px; padding: 1px; }
.newstr { backgroundColor: #0000FF; color: #FFFFFF; }
.newstb { backgroundColor: #CCCCFF; color: #000000; }

음 미적인 고려는 전혀 하지 않았습니다. 알아서 마음에 들게 바꾸세요.

드디어 news.html의 초안입니다.

<html>
<head>
<link rel="stylesheet" href="base.css" type="text/css">
<script src="main.js"></script>
</head>
<body background = "/i/bg.png">
<script>
       var r = new Array(); //뉴스 레코드를 담을 변수
       var s = ""; //출력될 HTML코드를 담을 변수
       var rtemp;
<?
       $q = sql_q("select * from news;");
       while ($r = sql_r($q)) {
              echo "rtemp = new Object();\n";
              echo "rtemp.id = '$r->id';\n";
              echo "rtemp.registered = '$r->registered';\n";
              echo "rtemp.title = '$r->title';\n";
              echo "rtemp.text = unescape('" . rawurlencode($r->text) . "');\n";
              echo "r[r.length] = rtemp;
       }
?>

       function _news_header() {
              return t._tr(t._td("ID") + t._td("DATE") + t._td("TITLE"), "newstr");
       }

       function _news_body(r) {
              var s = "";
              for (var i=0; i<r.length; i++) {
                     s += t._tr(t._td(r[i].id) + t._td(r[i].registered) + t._td(r[i].title), "newstb");
              }
       }

       s = t._div(t._table(_news_header() + _news_body(), "newstable"), "newslayer");

document.write(t.header() + t._div(s + t._footer(), 0, "abs", 10, 70));
</script>
</body>
</html>

네 물론 아직 여기에는 손볼게 엄청나게 많습니다.
하나하나 이해하시기 쉽도록 쉬운 단계부터 보여드리는 겁니다.

우선 자바 스크립트 배열에 레코드를 하나씩 집어넣는 PHP코드 부분을 보세요. 궁금증을 하나하나 풀어드리죠.
sql_q() 와 sql_r() 함수에 대해서는 위에서 이미 설명을 했구요.
이걸 전체 뉴스가 아니라 오늘 뉴스만 보려면 select문을 고치시면 되구요.
Javascript에 값을 전달하는 부분이 아주 비효율적인 것은 다음 강좌에서 클라스를 사용하면서 해결 될거구요.
지금 당장 사용하지 않을 전체 뉴스 (text)값을 받는 것도 나중에 고쳐나갈 문제입니다.
id, register, title값은 그냥 받으면서 text값은 좀 특별하게 받지요?
이건 뉴스 본문중에 각종 특수문자와 개행문자 등을 마음대로 쓰게 하기 위해서 입니다. 직접 해보시면 무슨 말인지 알게 될겁니다. PHP에서 ulrencode대신에 rawulrencode를 쓰는 이유는 이 함수가 표준 규격에 맞는 함수로 Javascript의 unescape()함수와 서로 호환이 되기 때문입니다.

꽤 간단한 내용이지만 응용해서 쓸 수 있는 아이디어가 여러가지 인데요.
하나하나 직접 해보시고 자기걸로 만드세요.