Rev. 2.73

지난 12일 약 6개월간 열과 성을 다해 작업한 프로젝트가 드디어 세상에 얼굴을 디밀었습니다. 관심있는 분이라면 한번씩 둘러봐 주시고 사정없는 채찍질 부탁드립니다. 부분 Ajax가 남용에 가깝게 사용되고 있으며 Ajax에 최적화된 인터페이스를 제공하고 있지는 않습니다만 자바스크립트의 여러 기법들이 사용되고 있습니다.

olalog.png

제가 담당한 파트는 웹 프론트-엔드입니다. 미흡하고 실험적인 부분이 많습니다만, 웹파트에서 주목해 보실 부분은 RadRails의 강력한 Ajax 구현능력을 엿보실 수 있고 부가적으로 드래그/토글을 이용한 다중선택 아이템, DIV엘리먼트의 코너를 시각적으로 둥글게 표현하는 자바 스크립트의 실용화와 인라인 Ajax 에러 메시지 처리, 얼럿과 컨펌을 오버라이드하는 윈도 위젯, 그리고 프로파일 이미지 크로퍼 등입니다. 내키면(?) 각개 심층적인 기술 분석을 연재하도록 하겠습니다.

덧1. 앗! 올라로그의 주소는 http://www.olalog.com 입니다.
덧2. 올라로그에 초대를 원하시면 이메일 주소를 덧글로 남겨주세요.

Comments

최근에 날라드는 스팸 트랙백들 살펴보면, 패턴을 이용한 차단을 피하기 위해 서로 다른 내용의 동일 스팸 트랙백을 생성하며, 하나의 URL에서 다량으로 수집한 트랙백 주소에 단 시간내에 무작위로 발사하도록 되어있다. 이 것은 단 10분만에 100여건을 성공시키기도 한다. OR2 패치를 적용하면 영문으로 구성된 트랙백이야 차단된다 하더라도 영문에 2바이트 특수문자를 사용하거나 영문이 아닌 외국어가 들어간 스팸 트랙백에 대한 차단은 아직 무방비 상태다. 그래서 아래와 같은 꼼수를 만들어 보았다.

스팸의 인식 조건은 지정된 시간 안에 지정된 량의 수신 트랙백에서 내용(사이트명, URL, 타이틀, IP)이 동일한 트랙백을 발견하면 스팸으로 간주하고 해당 수신을 거부한다. 이것으로 모두 차단할 수는 없겠지만 대량으로 걸리는 스팸은 어느정도 차단할 수 있었다. 참고로, 정헌님의 트랙백스팸 추적과 함께 사용하면 더욱 든든하다.

rserver.php 파일의 43열 부근을 아래와 같이 수정한다.

$result = is_spam("2", "1", $title);
if (!$result) $result = is_spam("2", "2", $url);
if (!$result) $result = is_spam("2", "3", $excerpt);

/* TrackbackBlocker */
if (!$result) {
$r_limit = 5; // 검사할 최근 트랙백 설정, 기본 5개
$r_time = 1; // 시간 설정, 기본 1시간
$r_sql = "select site, url, title, regdate, ip from t3_".$dbid."_trackback order by regdate desc limit $r_limit";
$r_result = @mysql_query($r_sql);
while(list($r_site, $r_url, $r_title, $r_regdate, $r_ip) = @mysql_fetch_array($r_result)) {
if (time() - $r_regdate < 60*60*$r_time && ($REMOTE_ADDR == $r_ip && ($url == $r_url && $title == $r_title && $blog_name == $r_site && $r_ip))) {
$result = true;
break;
}
}
}
/* end of TrackbackBlocker */

if (!$no || !$url || !$title || !$blog_name || !$excerpt || $result || (is_eng_only($title) && is_eng_only($excerpt))) {
echo "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?".">\n<response>\n<error>1</error>\n<message>Incomplete Information</message>\n</response>";
exit;
}

※ 오류, 버그, 개선 등과 관련된 피드백은 이곳에 댓글 및 트랙백으로 남겨주십시오.
※ 검증된 태스트과정을 거치지 않아 오작동을 일으킬 수도 있습니다.
※ 라이센스는 지나가던 개에게 주었습니다.

Comments

최근 블로그에 Ajax를 반영하면서 맞닥드린 난관은 브라우저의 뒤로가기, 앞으로가기 그리고 새로고침 버튼입니다. Ajax로 불러들인 문서는 페이지를 로드하지 않고 필요한 부분만 가지고 오기 때문에 브라우저의 히스토리에 흔적을 남기지 않습니다. 덕분에 유저들이 가장 많이 사용하는 브라우저의 기본기능 중 하나인 뒤로가기 버튼이 제대로 작동하지 않아 혼란을 겪게되고, 방문객은 본의 아니게 사이트를 떠나게 될수도 있다고 경고하는 출판물을 접한적이 있습니다.

이 문제를 해결하고자 이곳 저곳을 싸돌아 다니던 중 The Rails-spinoffs Archives에 'Ajax Bookmarks & Browser Navigation'라는 주제로 작성된 Ajax사용시 백버튼 이슈 중에서 Siegfried Puchbauer씨가 작성한 내용과 코드를 발견했습니다. Prototype의 Ajax.request 또는 Ajax.update를 사용할 때 해시 히스토리를 만들어 앞으로/뒤로 가기 문제를 해결하고 있습니다. 이것은 구글이 즐겨 사용하는 Ajax트릭과 흡사합니다.(예를 들면 피카사 웹앨범)

// Author: Siegfried Puchbauer <rails-spinoffs@lists.rubyonrails.org>
Ajax.History = {
    initialize: function(options) {
        this.options = Object.extend({
            interval: 200
        },options||{});
        this.callback = this.options.callback || Prototype.emtpyfunction;
        if(navigator.userAgent.toLowerCase().indexOf('msie') > 0)
            this.locator = new Ajax.History.Iframe('ajaxHistoryHandler', this.options.iframeSrc);
        else
            this.locator = new Ajax.History.Hash();
        this.currentHash = '';
        this.locked = false;
    },
    add: function(hash) {
        this.locked = true;
        clearTimeout(this.timer);
        this.currentHash = hash;
        this.locator.setHash(hash);
        this.timer = setTimeout(this.checkHash.bind(this), this.options.interval);
        this.locked = false;
    },
    checkHash: function(){
        if(!this.locked){
            var check = this.locator.getHash();

            if(check != this.currentHash){
                this.callback(check);
                this.currentHash = check;
            }
        }
        this.timer = setTimeout(this.checkHash.bind(this), this.options.interval);
    },
    getBookmark: function(){
        return this.locator.getBookmark();
    }
};

// Hash Handler for IE (Tested with IE6)
Ajax.History.Iframe = Class.create();
Ajax.History.Iframe.prototype = {
    initialize: function(id, src) {
        this.url = '';
        this.id = id || 'ajaxHistoryHandler';
        this.src = src || '';
        document.write('<iframe src="'+this.src+'" id="'+this.id+'"name="'+this.id+'" style="display: none;" ></iframe>');
    },
    setHash: function(hash){
        try {
            $(this.id).setAttribute('src', this.src + '?' + hash);
        }catch(e) {}
        window.location.href = this.url + '#' + hash;
    },
    getHash: function(){
        try {
            return (document.frames[this.id].location.href||'?').split('?')[1];
        }catch(e){ return ''; }
    },
    getBookmark: function(){
        try{
            return window.location.href.split('#')[1]||'';
        }catch(e){ return ''; }
    }
};

// Hash Handler for a modern browser (tested with firefox 1.5)
Ajax.History.Hash = Class.create();
Ajax.History.Hash.prototype = {
    initialize: function(){
    },
    setHash: function(hash){
        window.location.hash = hash;
    },
    getHash: function(){
        return window.location.hash.substring(1)||'';
    },
    getBookmark: function(){
        try{
            return window.location.hash.substring(1)||'';
        }catch(e){ return ''; }
    }
};

위와 같은 코드를 로드한 후 아래처럼 콜백 메서드를 히스토리 핸들러에 Ajax 요청을 보낼 수 있습니다.

var historyUpdate = function(hash) {
	new Ajax.Request(
	....
}

Prototype의 Ajax.Request 또는 Ajax.Updater를 사용할 때 onSuccess 및 onComplete 메서드에 히스토리를 아래와 같이 추가할 수 있습니다.

var handleSuccess = function(request){
    //... update DOM or smth like that
    Ajax.History.add(request.options.parameters)
    ...
}

그리고 페이지 푸터에 아래의 코드를 실행하도록 합니다.

var historyHandler = Ajax.History.initialize({
    callback: historyUpdate,
    iframeSrc: '_blank.html' // 비어있는 임시 html 페이지 경로
});

해시가 추가된 주소로 접근했을 때의 액션은 정의되지 않았지만, Scriptaculous 프레임웍 기반 구찌(GUCCI)는 해시를 가진 상품주소로 접근한 경우 해당 상품정보로 멋지게 연결하고 있습니다. 참고해 봅시다.

2008-10-2 여러분의 요청으로 간단한 예제파일을 만들어 첨부합니다.

Ajax.History.Example.zip Ajax.History.Example.zip (2.1 KB)

Comments