1. regedit를 실행해서 아래 키 값을 '0' 으로 바꾼다.
  2. HKEY_LOCAL_MACHINE/Software/Microsoft/Windows/CurrentVersion/setup/OOBE/mediabootinstall

  3. 명령프롬프트에 'slmgr /rearm' 을 타이핑하고 리부팅한다.
  4. 정품인증 끝!


Windows 7 이상 버전에서 응용프로그램 실행시, 파일 열기 대화상자 등에서 네트워크 드라이브가 표시되지 않는 경우가 있다. 탐색기 등에서는 정상적으로 표시되고 접근이 되지만, 응용프로그램에서만 표시되지 않는 경우인데, 레지스트리를 수정해서 해결이 가능하다.

  1. regedit를 실행하여 아래 위치를 찾는다.
    HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System
  2. 다음 값을 추가한다.
    EnableLinkedConnections / DWORD / 1

출처 : http://support.microsoft.com/kb/937624/ko

Mac OS X의 Spotlight 기능은 유용하지만, 외장 디스크를 연결했을 때는 대부분의 경우 필요없는데도 자동으로 인덱싱을 하느라 디스크를 사용한다. 따라서, 인덱싱을 하지 않도록 지정하여 이런 일을 막을 수 있는데, "터미널"에서 다음 명령어로 인덱싱을 하지 않도록 설정할 수 있다.

sudo mdutil -i off /Volumes/[장치이름]

리눅스에서 zip 파일 압축을 풀었는데, 한글로 된 파일이름이 깨져서 알아볼수가 없게 되는 경우가 있다. 대부분 Windows에서 압축한 파일들이 그러한데, 이럴 때에는 다음과 같이 풀어주면 된다.

$ unzip -O cp949 [파일이름]

Ruby on Rails 데모 동영상 등에 등장하며, 인기를 끌었던 Mac OS X용 에디터인 TextMate가 GNU GPLv3로 소스 공개되었다. GitHub에서 소스를 다운로드 받을 수 있고, 이것을 사용할 수 있게 빌드하는 방법도 친절하게 올라와 있어서 어렵지 않게 설치할 수 있다. 설치 과정을 한번 정리해봤다. (자세한 설치 방법은 여기를 참고) 아래의 설치 과정은 Mac OS X 10.7, HomeBrew 설치된 환경에서 진행되었다.

1. 소스코드 받기

git이 설치되어있다면 다음과 같이 받을 수 있다.

$ git clone https://github.com/textmate/textmate.git
$ cd textmate

만약에 git이 없다면, GitHub 프로젝트 페이지에서 ZIP으로 다운로드 받아서 압축을 풀어도 된다.

2. 필요한 프로그램 설치

문서를 보면 빌드에 필요한 프로그램이 있다. 앞서 밝힌대로 나는 HomeBrew를 사용하고 있기때문에 다음과 같이 설치했다.

$ brew install --HEAD https://raw.github.com/adamv/homebrew-alt/master/head-only/ninja.rb
$ brew install ragel boost multimarkdown hg
$ brew install pgrep

3. 빌드

그리고 다음과 같이 빌드를 시작하면 설치가 끝난다.

$ ./configure
$ ninja

하지만, 다음과 같은 메시지만 나오고 진행되지 않았다.

$ ./configure
clang is too old to build this project.
Please see README.md for build instructions.
$

이것은 삽질의 징조..

문서를 봐도 HomeBrew를 사용할 경우에는 저 메시지에 나오는 clang을 어쩌라는 말은 없다. MacPorts를 사용할 경우의 설치 방법만 소개되어있다.

별도로 설치하는 방법을 알아보다가 혹시나해서 Xcode 4.4.1 을 설치하니, 저 메시지가 나오지 않고 빌드가 진행된다. 빌드가 끝나면 자동으로 TextMate가 실행된다.

~/build/TextMate/Applications/TextMate/ 에 TextMate.app 가 있으니, 원하는 곳에 옮겨놓고 사용하면된다.

4. 결론

  • 빌드는 문서가 꽤 잘 되어있으니 보고 따라하면 된다.
  • clang 문제가 있다면, 먼저 Xcode 4.4.1를 설치해보자. (MacPorts를 사용한다면 MacPorts로 설치해도 상관없겠지만..)
  • 공짜는 역시 좋다?

웹서버를 운영하면서 https 보안 서버를 사용하기 위해서는 인증기관에서 SSL 인증서를 발급받는다. 발급받은 개인키에 비밀번호가 걸려있을 경우에는 아파치 웹서버를 실행할 때마다 다음과 같은 메시지를 받게된다.

... waiting .Apache/2.2.14 mod_ssl/2.2.14 (Pass Phrase Dialog)
Some of your private key files are encrypted for security reasons.
In order to read them you have to provide the pass phrases.

Server www.test.local:443 (RSA)
Enter pass phrase: [비밀번호 입력]

OK: Pass Phrase Dialog successful.
[ OK ]
root@www:/#

메시지만 나오는 것이라면 별 상관없겠지만, 개인키에 걸려있는 비밀번호를 입력해야만 서버 시작을 진행하는 것이 문제이다. 서버를 운영하다보면 관리 목적에서 자동으로 서버를 재시작해야할 경우가 종종 있는데, 그때마다 수동으로 비밀번호를 입력해주고 있을 수는 없는 노릇이다. 따라서, 이런 경우에는 개인키에 걸린 비밀번호를 제거해줘야할 필요가 생긴다.

개인키에 걸린 비밀번호는 다음과 같이 제거할 수 있다.

root@www:/etc/apache2/ssl/ssl.key# openssl rsa -in key.pem.orig -out key.pem
Enter pass phrase for key.pem.orig: [비밀번호 입력]
writing RSA key
root@www:/etc/apache2/ssl/ssl.key# [완료]

위의 예에서는 "key.pem.orig"라는 파일이 비밀번호가 걸려있는 원래 개인키 파일이다. 이렇게 실행한 후에, "key.pem"이라는 새로 만들어진 개인키 파일을 사용하면 비밀번호를 물어보는 귀찮은 일이 발생하지 않는다. 다만, 보안키의 비밀번호가 사라진만큼 보안키 저장에 신경써야하지만, 보안키 저장 장소는 비밀번호가 있건 없건 접근 권한등 보안에 신경쓰자.


PHP에서 PDF 문서를 만들기 위한 방법에는 다음과 같은 것이 있다.

1. PDFlib

PHP 공식 사이트의 문서에 나와있는 PDF 생성방법이다. PHP 공식 사이트에 소개되어있는 만큼 가장 문서화가 잘 되어있다고 봐야할 것 같고, 제공하는 기능도 괜찮아 보인다. 하지만, PDFlib 자체는 유료 소프트웨어이므로 정식으로 사용하기 위해서는 라이센스를 구매해야한다.(구매하지 않으면, 생성하는 모든 페이지 위에 데모 문구가 찍혀서 나온다고 한다.) 비 상업적 용도로 사용한다면, PDFlib Lite 7을 사용하는 방법도 있다.(현재 유지보수는 하지 않고 있으며, 무료답게 기술 지원도 없다고 한다. 참고로 현재 PDFlib의 버전은 8이다.)

참고

2. FPDF

FPDF는 무료로 용도에 상관없이 사용할 수 있다. PDFlib에서 라이브러리를 설치해야하는 것과는 달리, PHP 라이브러리만 설치하면 되기때문에 설치도 쉽다. 게다가 ubuntu를 사용한다면, 저장소에 이미 있기때문에 더욱 간편하게 끝난다. 문서는 FPDF 공식 사이트에서 함수 설명과 예제를 제공한다.

참고

3. TCPDF

FPDF처럼 TCPDF도 무료로 용도에 상관없이 사용가능한 자유소프트웨어이다. 설치도 다운로드해서 파일을 include해서 사용하는 간편한 방식이다. 다만, FPDF가 ubuntu 패키지로 존재하는 것에 비해서 TCPDF는 별도로 다운로드 받아야한다. 그러나, FPDF가 기본적으로는 유니코드를 지원하지 않는 것에 비해서, TCPDF는 기본 지원한다.(FPDF의 경우, 한글은 CP949 코드셋을 사용한다.) 문서는 공식 사이트에서 PHPDoc과 예제를 제공한다.

참고

Redmine으로 소프트웨어 개발 프로젝트를 관리하고 있으면서 Subversion을 사용중이라면, Redmine 계정과 Subversion 계정이 별도로 관리하는 것이 꽤 번거로울 수 있다. 이런 번거로움을 덜수 있도록 Redmine 계정에 대해서 Subversion 접근권한을 부여하는 방법을 소개한다.

아래의 설정은 아파치 설정파일 내용으로 이와같이 설정하면 Redmine 계정으로 Subversion 접근이 가능하다. 7줄은 Subversion 저장소가 위치할 절대 경로인데, 이 설정으로는 이 디렉토리 하위 디렉토리에 Redmine에서 관리할 저장소를 만들면 된다. 11~14줄은 Redmine DB 접속 정보이다.

<VirtualHost *>
    ServerAdmin webmaster@test.net
    ServerName test.net

    <Location /svn>
        DAV svn
        SVNParentPath /svn

        Auth_MySQL On
        Auth_MySQL_Authoritative on
        Auth_MySQL_DB "redmine"
        Auth_MySQL_Host "localhost"
        Auth_MySQL_User "redmine"
        Auth_MySQL_Password "redmine"

        Auth_MySQL_Password_Table users
        Auth_MySQL_Username_Field login
        Auth_MySQL_Password_Field hashed_password
        AuthMySQL_Empty_Passwords off
        AuthMySQL_Encryption_Types SHA1Sum

        AuthType Basic
        AuthName "Authorization Realm"
        Require valid-user
        AuthUserFile /dev/null
        AuthBasicAuthoritative Off
    </Location>

    ErrorLog /var/log/apache2/error.log

    # Possible values include: debug, info, notice, warn, error, crit,
    # alert, emerg.
    LogLevel warn

    CustomLog /var/log/apache2/access.log combined
</VirtualHost>

여러 프로젝트가 있고, 프로젝트별로 참여자에게만 접근 권한을 줘야하는 상황에는 위의 설정만으로는 부족하다. 그런 경우에는 아래의 설정을 추가하면 해당 프로젝트에 참가하는 계정만 저장소에 접근할 수 있게 할 수 있다. 아래 설정 중의 project_id는 Redmine의 해당 프로젝트 번호로 Redmine DB의 project 테이블을 참고하면 알 수 있다.

Auth_MySQL_Password_Clause " AND status = 1 AND id IN (select user_id from members where project_id = 1)"

또한, Subversion 경로도 아래와 같이 각 프로젝트별로 설정해줘야한다.

SVNPath /svn/project1

위와 같이 한다면, 반드시 프로젝트에 포함되어있는 계정을 Redmine에서 Subversion 저장소 설정 시에 넣어줘야한다는 문제가 남는다. 특정 계정을 사용하기가 번거롭다면 조회용 계정을 하나 만들어서 프로젝트에 포함시킨뒤에 잠궈두고, 다음과 같이 설정을 추가해도 된다. 아래에서 id는 조회용 계정의 번호로 Redmine DB의 members 테이블에서 확인할 수 있다.

Auth_MySQL_Password_Clause " AND (status = 1 OR id = 1) AND id IN (select user_id from members where project_id = 1)"

'Operating' 카테고리의 다른 글

SSL 인증서 개인키 비밀번호 없애기  (2) 2012.07.29
VMWare에서 리부팅없이 새 하드디스크 추가하기  (0) 2010.11.11
LVM 설정  (0) 2010.04.01

"패러디 웨이브가 어떤 것인지 빨리 한번 돌려보고 싶은 사람들을 위해서 간단한 사용방법을 적어보려고 한다. 먼저 프로그램및 발표 자료를 첨부한다.

ParodyWave-0.1a.zip 패러디 웨이브 0.1a
websocket.pdf 발표자료

먼저 Node.js 설치가 필요하다. Linux 등을 사용한다면 설치에 큰 어려움이 없겠지만, Windows를 사용한다면, cygwin을 먼저 설치하는 등 손이 좀 갈 것이다. 설치방법은 Node.js 웹사이트를 참고하여 다음과 같이 하면 된다.(Windows에서 설치해보고 싶은 사람은 여기를 참고하자.)

./configure
make
make install

설치가 끝나면 "패러디 웨이브" 파일을 다운로드 받아서 적당한 곳에 풀어놓고 다음과 같이 실행한다.

node WebSocketServer.js

그 뒤에 웹브라우저를 열고, 주소표시줄에 'http://localhost:8888'을 입력해서 다음과 같은 화면이 나오면 성공이다.

혹시 8888포트를 다른 곳에서 사용중이거나 다른 서버를 다른 곳에서 실행해서 접속 정보를 바꾸고 싶다면 다음 코드를 수정하면된다.

WebSocketServer.js 175줄
server.listen(8888);
ParodyWave.html 66줄
ParodyWaveClient.conn = new WebSocket("ws://localhost:8888");


이번에는 마지막으로 클라이언트 부분이다. 이 부분은 HTML5에서 제공하는 WebSocket API대로만 작성하면 되기때문에 크게 어려운 부분은 없다.

<!doctype html> 
<html> 
<head> 
    <meta charset="UTF-8" />
    <title>패러디 웨이브 version 0.1a</title>
	<style type="text/css"> 
		* { margin: 0; padding: 0; font-family: Helvetica; font-size: 9pt; }
		body { width: 100%; height: 100%; color; #444; overflow: hidden; }
		ul { list-style: none; }
		li { padding-left: 5px; font-weight: normal; color:#aaa; border-bottom: 1px dotted #ddd; padding-top: 5px; padding-bottom: 5px; }
		li:first-child { font-weight: bold; color: #000; }
		#thread-list { margin-top: 20px; }
		#thread-list li { border-style: none; display: inline; margin-right: 10px; }
		#welcome { position: fixed; width: 100%; height: 60px; margin-top: 200px; text-align: center; background-color: #888; padding-top: 30px; background: -moz-linear-gradient(center top, #444 0%,#222 100%); background: -webkit-gradient(linear, left top, left bottom, color-stop(0, #444),color-stop(1, #222));}
		#home { position: fixed; width: 100%; height: 200px; margin-top: 200px; text-align: center; background-color: #888; padding-top: 30px; background: -moz-linear-gradient(center top, #444 0%,#222 100%); background: -webkit-gradient(linear, left top, left bottom, color-stop(0, #444),color-stop(1, #222));}
		#header { display: none; position: fixed; width: 100%; height: 30px; left: 0; top: 0; background: -webkit-gradient(linear, left top, left bottom, color-stop(0, #444),color-stop(1, #222));}
		#header > h1 { color: #fff; font-weight: bold; font-size; 16px; float: left; margin-left: 10px; margin-top: 8px;}
		#join-count { float: right; height: 12px; margin-right: 5px; margin-top: 4px; background-color: #666; color: #ddd; padding: 3px; padding-left: 10px; padding-right: 10px; -webkit-border-radius: 10px 10px; font-size: 10px; background: -webkit-gradient(linear, left top, left bottom, color-stop(0, #111),color-stop(1, #555)); }
		#input-container { position: fixed; width: 90%; height: 30px; bottom: 0; left: 10px; }
		#thread-container { width: 100%; height: 100%; overflow: auto; background-color: #fff; padding: 10px; padding-top: 30px;}
		input[type=text] { border: 1px solid #ccc; height: 25px; font-size: 14px; }
		input[type=button] { padding: 5px; padding-left: 15px; padding-right: 15px; font-size: 14px; border: 1px solid #e0a903; border-radius: 6px 6px; text-shadow: rgba(255,255,255,.5) 0 1px 0; background: -webkit-gradient(linear, left top, left bottom, color-stop(0, #ffc000),color-stop(1, #e0a903)); background: -moz-linear-gradient(center top, #ffc000 0%,#e0a903 100%);}
		#thread > div { display: block; float: left; width: 180px; height: 250px; margin-right: 10px; margin-top: 5px; border: 1px solid #ddd; -webkit-box-shadow: 5px 5px 5px #aaa; -webkit-border-radius: 10px 10px;}
		#thread > div .title { background-color: #eee; padding: 3px; border-bottom: 1px solid #bbb; -webkit-border-top-left-radius: 10px 10px; -webkit-border-top-right-radius: 10px 10px; background: -webkit-gradient(linear, left top, left bottom, color-stop(0, #fff),color-stop(1, #ccc));}
		#thread > div .title .msg-count { float: right; height: 12px; margin-right: 5px; margin-top: 3px; background-color: #fff; border: 1px solid #bbb; color: #444; padding-left: 3px; padding-right: 3px; -webkit-border-radius: 10px 10px; font-size: 10px; }
		#thread > div .title .msg-count:hover { background-color: #444; border: 1px solid #222; color: #ccc; cursor: pointer;  }
		#thread > div .title > h1 { display: inline; text-align: right; margin-right: 10px; text-overflow: ellipsis; font-size: 14px; }
		#thread > div div.article-container { clear: both; padding: 5px; height: 205px; overflow: hidden; }
		#thread > div .article { font-size: 12px; }
		#thread > div .article .before { color: #bbb; }
		#thread > div .article .current { color: #444; font-weight: bold; }
		#thread > div .article hr { stroke-width: 1px; margin-top: 5px; margin-bottom: 5px; }
		#line { width: 90%; }
	</style> 
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script> 
    <script> 
    var ParodyWaveClient = {
        conn: null,
        user: "",
        thread: false,
        line: null,
        writing: false,
        currentValue: "",
		threadStore: null
	}
 
    $(document).ready(function () {
		var docWidth = $(document).width();
		var docHeight = $(document).height() - 30;
		
		$("#thread-container").css("height", docHeight+"px");
		
        $("#new-thread").click(openThread);
        $("#connect").click(function () {
            if (! ParodyWaveClient.conn) login();
            else logout();
        });
    });
 
    window.onunload = function () {
        if (ParodyWaveClient.conn) ParodyWaveClient.conn.close();
    }
    function login() {
        if("WebSocket" in window) {
            debug("connecting...");
            ParodyWaveClient.conn = new WebSocket("ws://localhost:8888");
            ParodyWaveClient.conn.onopen = function () {
                debug("connection established");
                ParodyWaveClient.user = $("#userid").val();
                $("#welcome>input").attr("disabled",true);
                $("#connect").val("LogOut").attr("disabled",false);
                $("#home").show();
				$("#welcome").hide();
				
                sendMsg({cmd: "status"});
                sendMsg({cmd: "list"});
 
				ParodyWaveClient.threadStore = new Array();
				
                setInterval(function() {
                  ParodyWaveClient.conn.send('HB');
                }, 60000);
            }
            ParodyWaveClient.conn.onmessage = onWebSocketMessage;
            ParodyWaveClient.conn.onclose = onWebSocketClose;
        }
        else {
            alert("This web browser doesn't support WebSocket!");
        }
    }
    function logout() {
        ParodyWaveClient.conn.close();
    }
    function openThread() {
        sendMsg({cmd: "open", user: $("#userid").val(), title: $("#title").val()});
    }
    function joinThread(idx) {
        sendMsg({cmd: "join", user: $("#userid").val(), id: idx});
    }
    function debug(str) {
        if ($("#chk-debug").attr("checked"))
            console.log(str);
    }
    function sendMsg(msg) {
        if (ParodyWaveClient.conn) ParodyWaveClient.conn.send(JSON.stringify(msg));
    }
    function onWebSocketMessage(e) {
        console.log(e.data);
        var msg;
        try {
            msg = JSON.parse(e.data);
        }
        catch (e) {
            debug("Invalid message");
            msg = {type: "error"};
        }
        switch (msg.type) {
            case "status":
                $("#join-count").text("Connection: "+msg.connections);
                break;
            case "join":
                if (msg.data) {
                    for (var i = 0 ; i < msg.data.length ; i ++) {
						addThreadData("new", msg.data[i][0], i, msg.data[i][1], msg.data[i][2]);
					}
                }
                $("#input-container").show();
                $("#home").hide();
 
				$("#header > h1").html("패러디 웨이브 version 0.1a");
				$("#header").show();
				
                ParodyWaveClient.currentValue = "";
                
                $("#line").bind("keyup", function (event) {
                    cmd = {cmd: 'update'};
                    if (! ParodyWaveClient.writing) {
                        ParodyWaveClient.writing = true;
                        cmd.cmd = 'new';
                    } else if (event.keyCode == 13 && event.srcElement.value != "") {
                        cmd.cmd = 'submit';
                        event.srcElement.value = "";
                        ParodyWaveClient.writing = false;
                    } else if (ParodyWaveClient.currentValue == event.srcElement.value) return;
                    cmd.value = ParodyWaveClient.currentValue = event.srcElement.value;
                    sendMsg(cmd);
                });
                break;
            case "list":
                $("#thread-list").html("");
                for (var i = 0 ; i < msg.threads.length ; i ++) {
                    if (! msg.threads[i]) continue;
                    $("#thread-list").append("<li><input type='button' onclick='joinThread(" + msg.threads[i][0] + ")' value='"+msg.threads[i][1]+"방에 조인' /></li>");
                }
                break;
            case "update":
                if (typeof msg.value != "string") addThreadData("new", msg.id, msg.line, msg.user, msg.value);
                else addThreadData("update", msg.id, msg.line, msg.user, msg.value);
        }
    }
 
	function addThreadData(type, id, lineNo, user, msg) {
		if (msg == undefined) msg = "<img src='wait.png' align='absmiddle'>";
		if (!$.isArray(ParodyWaveClient.threadStore[id])) {
			ParodyWaveClient.threadStore[id] = new Array();
			ParodyWaveClient.threadStore[id].push(new Array(lineNo, user, msg));
		} else {
			var matchLineNo = false;
			
			for (i = 0; i < ParodyWaveClient.threadStore[id].length; i++) {
				if (ParodyWaveClient.threadStore[id][i][0] == lineNo) { 
					matchLineNo = true;
					ParodyWaveClient.threadStore[id][i][2] = msg;
					break;
				}
			}
			
			if (!matchLineNo) ParodyWaveClient.threadStore[id].push(new Array(lineNo, user, msg));
		}
		
		if (ParodyWaveClient.threadStore[id].length == 1 && type == "new") { // new 
	        $("#thread").append('<div id="fdata'+id+
				'"><div class="title"><img src="person.png" align="absmiddle"><h1>'+ParodyWaveClient.threadStore[id][0][1]+
				'</h1><div class="msg-count" boxid="'+id+'" onclick="toogleHistory(this);">1</div></div><div class="article-container"><ul class="article"><li class="line-'+lineNo+'">'+ParodyWaveClient.threadStore[id][0][2]+
				'</li></ul></div></div>');
		} else {
			for (i = 0; i < ParodyWaveClient.threadStore[id].length; i++) {
				if ($("#fdata"+id+" .article li.line-"+lineNo).length <= 0)
					$("#fdata"+id+" .article").prepend("<li class='line-"+lineNo+"'>"+ParodyWaveClient.threadStore[id][i][2] + "</li>");
				else 
					$("#fdata"+id+" .article li.line-"+lineNo).html(ParodyWaveClient.threadStore[id][i][2]);
			}
			
			$("#fdata"+id+" .title .msg-count").text(ParodyWaveClient.threadStore[id].length);
		}
	}
	
	function toogleHistory(f) {
		var idf = "#fdata"+$(f).attr("boxid")+" .article-container";
		if ($(idf).css("overflow-y") == "auto")
			$(idf).css("overflow-y", "hidden");
		else 
			$(idf).css("overflow-y", "auto");
	}
 
    function onWebSocketClose() {
        debug('Disconnected');
        $("#welcome>input").attr("disabled",false);
        
        // initiate text input
        $("#userid").val("");
        $("#title").val("");
        $("#line").val("");
        $("#connect").val("LogIn")
        
        // close others
        $("#home").hide();
        $("#input-container").hide();
        $("#thread").html("");
        $("#users").text("");
        $("#welcome").show();
 
        // initiate status
        ParodyWaveClient.conn = null;
        ParodyWaveClient.user = "";
        ParodyWaveClient.thread = false;
        ParodyWaveClient.line = null;
        ParodyWaveClient.writing = false;
        ParodyWaveClient.currentValue = false;
        
        // unbind event
        $("#line").unbind();
    }
    </script> 
</head> 
<body> 
    <div id="welcome"> 
 
        <input type="text" id="userid" placeholder="대화명을 입력해주십시오."/> 
        <input type="button" id="connect" value="로그인" /> 
        <input type="checkbox" id="chk-debug" title="Show Debug Message"/> 
    </div> 
 
    <div id="home" style="display:none;"> 
        <input type="text" id="title" placeholder="생성할 방제목을 입력해주십시오."/> 
        <input type="button" id="new-thread" value="Create!" /> 
        <div> 
 
            <ul id="thread-list"></ul> 
        </div> 
    </div> 
 
	<div id="header"> 
		<h1>패러디 웨이브</h1> 
		<div id="join-count"></div> 
	</div> 
 
	<div id="thread-container"> 
    	<div id="thread"></div> 
	</div> 
 
    <div id="input-container" style="display:none;"> 
        <img src="chat.png" align="absmiddle"> <input type="text" id="line"/> 
    </div> 
    <div id="status" style="display:none;"> 
        <p id="users"></p> 
 
    </div> 
</body> 
</html>

login 메소드가 WebSocket 접속을 시도하는 부분인데, 64줄의 조건문으로 웹브라우저가 WebSocket을 지원하는지 여부를 판단할 수 있다. 66줄에는 접속하는 호스트 주소가 들어가는데, 환경에 따라서 적당히 바꾸면 되겠다.

80줄에는 타이머 설정이 있는데, 접속 유지를 위한 HeartBeat 코드가 들어가 있다. 서버에서 임의로 적용해놓은 타임아웃 설정 이외에도 웹브라우저에서 기본으로 적용하는 타임아웃이 있기때문에 사용중에 접속이 끊겨버리는 일을 방지하기 위해서는 이러한 코드가 필요하다. 현재 WebSocket Protocol에는 타임아웃에 대한 처리는 없다.

67, 84, 85줄에서는 각각 open, message, close 이벤트에 대한 핸들러를 지정하고 있다. 역시 대부분의 동작은 message 이벤트 핸들러에서 수행하게 된다.

+ Recent posts