Try the Node-webkit

갑자기 만들어 보고 싶은게 생겼는데 node-webkit으로 만들면 괜찮을꺼 같아서 사용해 볼까 한다.

일단 난 mac를 사용하니까 mac 에서 실행하는 방법을 알아보자.

Downloads

위 링크에서 다운로드를 받아서 압축을 풀고 Applications 디렉토리로 이동시킨다.

alias 등록

사용하기 편하게 .bash_profile 에 alias를 등록한다.

alias 등록
1
2
#alias to nw
alias nw="/Applications/node-webkit.app/Contents/MacOS/node-webkit"

앱 실행

nw 명령어를 치면 다음과 같은 창을 볼수 있다

앱실행
1
$ nw

Quick Start

https://github.com/rogerwang/node-webkit 에 있는 Quick Start 소스를 따라서 앱을 하나 만들어보자.

디렉토리를 하나 만들어서 index.html, package.json 파일을 생성하고 아래와 같은 내용을 넣어준다.

index.html
1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html>
  <head>
    <title>Hello World!</title>
  </head>
  <body>
    <h1>Hello World!</h1>
    We are using node.js <script>document.write(process.version)</script>.
  </body>
</html>
package.json
1
2
3
4
{
  "name": "nw-demo",
  "main": "index.html"
}

앱을 실행 하기 위해서 app.nw 파일로 압축을 한다.

package.json
1
2
3
$ zip app.nw index.html package.json
adding: index.html (deflated 30%)
adding: package.json (deflated 8%)

위와 같이 하면 app.nw 파일이 생성된걸 확인 할 수 있다. 이제 실행해 보자.

앱실행
1
$ nw app.nw

앱을 실행 하면 다음과 같은 창을 볼수 있다.

근데 파일을 수정할때 마다 압축을 시켜서 앱을 실행을 하려니 너무 귀찮은 것이다.

그래서 Haroopad 를 만든 Rhiokim 에게 문의를 해서 앱을 실행하는 다른 방법을 알아 냈다.

압축할 필요없이 다음과 같이 앱 디렉토리를 압축한 파일 대신 써주면 디렉토리에서 파일을 찾아서 앱을 실행해준다.

압축없이 실행
1
$ nw node_webkit_test_app/

Django Create Super User

Django 를 사용할때 syncdb 를 했을때 super user 가 생성이 안되는 경우나, 혹은 계정을 생성했지만 아이디 패스워드가 생각이 나지 않을때 user 를 확인해서 user 가 없으면 신규로 생성하거나 user 가 있는 경우에 password 를 변경 하는 방법에 대해서 적어 볼까 한다.

User 확인

user 확인
1
2
3
4
5
6
$ ./manage.py shell
>>> from django.contrib.auth.models import User
>>> users = User.objects.all()
>>> users
[<User: j2p>] // user 가 있는 경우
[] // user 가 없는 경우

User 가 있는 경우 password 변경

password 변경
1
2
3
4
5
$ ./manage.py changepassword userid
Changing password for user 'userid'
Password: // 입력
Password (again): // 다시 입력
Password changed successfully for user 'userid'

User 가 없는 경우 신규 유저 생성

신규 유저 생성
1
2
3
4
5
6
$ ./manage.py shell
>>> from django.contrib.auth.models import User
>>> u = User.objects.create_user('userid', 'useremail', 'userpassword')
>>> u.is_staff = True
>>> u.is_superuser = True
>>> u.save()

Blender

"Blender"

Blender란 무료로 사용할 수 있는 3D 그래픽 프로그램이다.

오픈소스 진영의 대표적인 그래픽 프로그램이라고 한다.

윈도우, 맥, 리눅스등 여러 운영체제를 동시에 지원 하기 때문에 운영 체제가 달라도 작업들을 쉽게 공유 가능하다고 한다.

3D 모델링툴에 비해 매우 배우기 쉬우면서 높은 수준의 렌터링 퀄리티를 보여준다고 한다.

애니메이션, 파티클, 텍스쳐 페인팅, 옷감 시뮬레이션, 스컬핑 등 값비싼 사용 프로그램에서 쓸 수 있는 기능을 거의 다 쓸 수 있으며 자체 게임 엔진을 내장하고 있다고 한다.

이외 간단한 음향/영상 편집이나 복잡한 노드 합성 기능까지 내장하고 있다고 한다.

스크립트로 3D 게임을 만들 수 있지만 게임 전용툴이 아니다 보니 복잡한 게임일수록 스크립트 노가다를 해야 하고 퍼포먼스가 좋지 않다고 한다.

Unity3D 엔진이 Blender 파일을 지원하기 때문에 게임을 개발하기 위해서는 Unity3D 엔진을 사용하는게 좋다고 한다.

출처 : 엔하위키 미러

특색

  • Interface

참고 할만한 사이트 모음

국내

해외

Change Octopress Theme

블로깅을 다시 시작하기 위해서 정리좀하다가 몬가 변화를 주고 싶다는 생각에 이전에 사용하던 Octopress로 갈아타기로 마음 먹었다.

그러던중 혹시나 Octopress theme가 생겼을까 싶었는데 검색을 해보니…

오~ 있다!!!

Octopress Themes

찾아보니 github 에 3rd Party Octopress Themes 가 따로 있었다.

여기는 원래 있었던걸로 기억하는데… 이전에는 지금 처럼 많은 테마가 없었다.

조기에 가면 Octopress Theme 들이 꽤 많이 있는걸 확인 할 수 있다.

미리보기와 github 링크가 걸려 있다.

그중 제일 맘에 드는 녀석을 고르고 Install~

Install 방법은 아래와 같이 하면 된다.

Install

Theme Install
1
2
3
4
$ cd octopress
$ git clone [github theme url] .themes/[theme name]
$ rake install['theme name']
$ rake generate

Start Django Framework

새로운 회사에 입사를 하게 되어 Django 프레임웍 를 이용해서 개발을 하게 되었다.

아직 Python을 써본적도 없고 Django 프레임웍은 더더욱이나 써본일이 없다.

사실 프레임웍 자체가 아직은 낮설다. 그래도 모든 새로운걸 배우는건 재밌는 일이기에 매일 매일 즐겁게 배우고 있다.

그래서 “쉽고빠른 웹 개발 Django(인사이트)” EBook 을 구매해서 하나하나 따라해 보고 있다.

기본적으로 Django 프레임웍 은 Python 으로 만들어진 프레임웍 이기 때문에 Python 이 설치 되어 있어야 한다.

가상환경생성

Python 개발 환경은 @lqez 님이 블로깅해주신 virtualenv / virtualenvwrapper을 보고 따라서 설정했다.

virtualenvwrapper을 설치 했으면 하나의 가상환경을 만들어 보자.

가상환경생성
1
$mkvirtualenv mysite

Django 설치

다음은 Django 를 설치한다. 설치는 pip 를 사용해서 설치를 한다. 현재 버전은 1.4 이다. 이전 버전을 사용하고 싶으면 두번째 줄과 같이 해서 설치하면 된다.

Django 설치
1
2
$pip install django
$pip install django==1.3.1

Django 가 설치가 다 되었으면 설치가 잘되었는지 확인을 해보자. 다음과 같이 django-admin.py 명령어를 통해서 version 을 확인해보자.

Django 확인
1
2
$django-admin.py --version
1.4

프로젝트 생성

이제 Django 설치가 제대로 되었으니 새로운 프로젝트를 생성해보자. 적당한 디렉토리에 다음과 같은 명령어로 새로운 Django 프로젝트를 생성해보자.

새로운 프로젝트 생성
1
$django-admin.py startproject project-name

프로젝트를 생성하면 project-name라는 디렉토리에 다음과 같이 파일이 구성된다.

디렉토리 구조
1
2
3
4
5
6
manage.py(file)
project-name(directory)
  __init__.py(file)
  settings.py(file)
  urls.py(file)
  wsgi.py(file)

생성된 파일들이 어떤 역할을 하는지 알아보자. 내용은 책에서 참고 했다.

  • manage.py : django-admin.py 와 같이 프로젝트를 관리하는 스크립트 이다.
  • __init__.py : 이 파일은 디렉터리를 파이썬 패키지로 만들어 준다.
  • settings.py : Django 프로젝트를 설정한다. 데이터베이스, 언어 등을 설정한다.
  • url.py : url 주소와 Django 의 기능을 연결시켜주는 역할을 한다.
  • wsgi.py : wsgi 을 설정하는 파일이다. 1.4 버전에 추가되었다.

애플리케이션 생성

이제 프로젝트를 생성했으니 새로운 애플리케이션을 하나 만들어 보자. 다음과 같은 명령어로 새로운 애플리케이션을 생성한다. 애플리케이션 이름은 상황에 맞게 이름을 정하면 된다.

새로운 애플리케이션 생성
1
$django-admin.py startapp app-name

서버실행 확인

이제 서버를 실행해보자. 서버는 다음과 같은 명령어를 사용해서 실행할수 있다. 기본적으로 포트는 8000번으로 실행된다. 포트번호를 바꾸고 싶으면 두번째 줄과 같이 실행하면 된다.

서버 시작
1
2
$python manage.py runserver
$python manage.py runserver 3000

이제 브라우저에서 http://localhost:8000 으로 확인을 해보자.

FRENDS Hackathon 2012 늦은 후기

지난달 3월30일 저녁 10시부터 3월 31일 저녁10시까지 FRENDS Hackathon 2012 을 했다.

예전 한글과 컴퓨터에 포함되었던 산성비 게임을 웹으로 만들어 보면 재미있지 않을까해서 주제를 산성비 게임으로 정하고 만들어 보았다.

사실 3월에는 둘째 출산으로 인해서 힘든 상태에 여러 귀차니즘이 발동을 해서 Hackathon 참여를 포기하고 있던 상태에 (@Rhiokim)님 의 도움을 받아서 마지막에 겨우 겨우 다시 진행하게 되어 살짝이나마 결과를 낼수 있었다.

사용했던 기술은 Node.js 서버, express 프레임웍을 사용했고, 로그인 구현을 위해서 passport (passport-twitter, passport-facebook) 모듈을 사용했다. 또 디자인이 약한 개발자인 지라 Bootstrap으로 기본 화면을 꾸며 봤다.

소스는 github repository 에 올려서 관리를 했다. (@Rhiokim)님 덕분에 github를 사용하는 방법을 많이 배울 수 있었다.

Issues에 Issue를 등록해서 Milestone 계획을 잡아 기능을 하나씩 구현해 나가는 방법은 머리로만 생각해서 작업을 진행하지도 못하고 해매고 있던 나에게는 더 없이 좋은 방법이 였고, github에서 프로젝트를 관리 할 수 있는 좋은 방법이였다. 이번 Hackathon을 통해서 가장 크게 배운점이 프로젝트를 진행해 나가는 방법이 아닐까 싶다.

아직은 v0.1.0 이라서 기본적인 기능만을 구현했지만 Hackathon 이 끝났다고 해서 버려두지 말고 기능을 계속추가해 나가면서 발전시켜볼까 한다.

"Typing" "Typing"

Web Editor Beforeunload Event 적용하기

현재 개발 하고 있는 사이트에서는 Tinymce Editor 를 사용하고 있는데 plugin 으로 Editor 을 사용중에 back space를 누르거나 다른 페이지로 이동으로 하려고 할때 다음과 같은 modal 창이 뜬다.

"modal"

실수로 인해서 작성했던 내용을 잊어버리지 않기 위한 방한이다. 물론 자동저장 같은 기능을 지원하면 문제가 되지는 않겠지만 현재는 자동저장을 지원하고 있지 않기 때문에 문제가 됐던것이다. facebook 등 다른 웹사이트에서도 저런 창을 띄워서 확인 하는걸 알수 있다.

이번 문제는 Tinymce Editor 을 사용하지 않는 곳에서 발생했다. 사용자는 실수로 인해서 back space를 눌렀고 작성했던 장문의 글을 날렸던것이다.

처음에는 unload event 로 하면 될꺼라 생각했지만 아니 였다. 검색을 해봤더니 beforeunload 라는 event가 있는것을 알게 되었다.

아래 예제소스를 보면서 사용방법을 알아보자.

beforeunload event
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<html>
  <head>
      <script>
          if (document.addEventListener) {
              window.addEventListener('beforeunload', function() {
                  return "다른 페이지로 이동하면 편집한 내용이 취소됩니다.";
              }, false);
          // IE
          } else {
              window.attachEvent('onbeforeunload', function(event) {
                  if (event) {
                      event.returnValue = "다른 페이지로 이동하면 편집한 내용이 취소됩니다.";
                  }
              });
          }
      </script>
  </head>
  <body>
      <textarea id="editor"></textarea>
  </body>
</html>

위와 같이 onbeforeunload 를 써서 return 값에 moal 창에 보여주고자 하는 문구를 입력하면 된다.

  • IE9 이하 버전에서는 event 객체를 받아서 returnValue 에 문구를 넣어주면 된다.

경우에 따라서 editor 에 값이 있을경우에만 보여주고 싶다면 다음과 같이 수정하면 된다. 값을 확인해서 value가 있을경우에만 return 을 해주면 된다. 그리고 한가지 문제가 더 있었던건 저장을 할려고 할때 submit 을 하게 되면 저 이벤트가 발생을 하게 된다. 그래서 나는 submit 을 하기전에 event 를 삭제해서 해결했다.

참고

Event Capturing Bubbling

스터디를 진행하면서 다시 한번 알게 된것이 있어서 정리를 해볼까 한다.

보통 javascript 에서 click, over 등등… 이벤트가 발생하게 되면 target(예를들어 click한 element) 에서만 이벤트가 실행 된다고 생각하고 있을것이다.

하지만 실질적으로 브라우저에서는 이벤트가 발생하게 되면 capturing 과 bubbling 이 모두 발생하게 된다.

Capturing

capturing은 이벤트가 발생한 target 이 있으면 최상위 부모에서 부터 이벤트가 실행되면서 target 까지 내려오는 것을 말한다. 예를 들어 부모에도 click 이벤트가 걸려 있었다면 부모에서 부터 click 이벤트가 모두 실행이 되면서 target 까지 내려오게 된다.

Bubbling

bubbling은 capturing이 발생해서 target 까지 내려온 후에 다시 target에서 부터 최상위 부모까지 올라가면서 이벤트가 실행이 된다. capturing 에서 이벤트가 실행이 되었다면 bubbing 이벤트는 실행 되지 않는다.

아래 예제 소스를 통해서 눈으로 확인해 보자.

event.html
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
<!DOCTYPE html>
<html>
  <head>
      <style>
          div, p{
              border: 2px solid #ccc;
              padding: 20px;
          }
          #div_content{
              width: 400px;
              height: 200px;
          }

          #p_content{
              width: 200px;
              height: 100px;
          }
      </style>
  </head>
  <body>
      <div id="div_content">
          <p id="p_content">
              <a href="http://blog.j2p.kr" target="_blank" id="a_link">j2p</a>
          </p>
      </div>
      <script src="event.js"></script>
  </body>
</html>
event.js
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
(function() {
    var div = document.getElementById('div_content');
    var p = document.getElementById('p_content');
    var a = document.getElementById('a_link');

    function divEvent() {
        console.log('DIV EVENT');
    }

    function pEvent() {
        console.log('P EVENT');
    }

    function aEvent(event) {
        //bubbling 을 막을때 사용
        if (event.stopPropagation) {
            event.stopPropagation();
        } else {
        //IE9 이하 버전
            event.cancelBubble = true;
        }

        //기본 a Tag 링크 이동 막을때 사용
        if (event.preventDefault) {
            event.preventDefault();
        } else {
        //IE
            event.returnValue = false;
        }

        console.log('A EVENT');
    }

    //capturing 을 설정해준다. (true, false)
    if (document.addEventListener) {
        div.addEventListener('click', divEvent, true);
        p.addEventListener('click', pEvent, true);
        a.addEventListener('click', aEvent, true);
    } else {
        div.attachEvent('onclick', divEvent);
        p.attachEvent('onclick', pEvent);
        a.attachEvent('onclick', aEvent);
    }
})();

먼저 capturing 은 이벤트를 걸어줄때 설정할수 있다. addEventListener 에서 마지막 세번째 인자가 capturing 설정하는 값이다. true 값을 주게 되면 capturing 이 발생하고 false 값을 주게 되면 capturing 은 발생하지 않고 bubbling 만 발생하게 된다.

  • IE9 이하 버전에서는 capturing을 지원하지 않는다.

다음 bubbling은 capturing 이 발생해서 target 까지 도달한 후에 발생하게 된다. bubbling 을 발생하지 않게 하기 위해서는 이벤트에 걸려 있는 함수에서 event 객체를 받아서 event.stopPropagation() 을 해주면 bubbling 이 일어나지 않게 해줄수 있다.

  • IE9 이하 버전에서는 event.cancelBubble = true 값을 주면 bubbling을 발생하지 않게 할수 있다.

한가지 추가로 얘기 하면 a tag 같은경우 기본적 링크 이동 이벤트가 있다. 이것을 발생하지 않게 하기 위해서는 event 객체에서 event.preventDefault() 를 실행해주면 a tag의 기본 링크 이동을 막을수 있다.

  • IE 경우는 event.returnValue = false 값을 주면 기본 이벤트 동작을 막을수 있다. 혹은 return false를 해줘도 된다.

참고

MSDN Event Propagation

간단한 WYSIWYG Editor 만들기

회사에서 작업하다가 bold, italic, underline 기능만 필요한 WYSIWYG가 필요해서 어떻게 해야 할지 앞이 캄캄해서 그냥 오픈 소스를 써야 하나 했는데 역시 구글신이 간단한 방법을 알려줬다.

contenteditable 사용해서 엘리먼트에 바로 편집이 가능하게 된다. 요것은 HTML5 에 표준이 된거라고 하는데 브라우저 지원 여부를 확인해보니 거의 모든 브라우져에서 지원을 하고 있다. 이전에는 WYSIWYG Editor 을 만들기 위해서 iframe 과 designMode=“on” 으로 사용했다고 한다.

브라우저 지원 여부에 대해서 알아보면 다음과 같다.

"contenteditable"

사용방법은 아주아주 간단하다. 소스로 보면 다음과 같다.

WYSIWYG Editor
1
2
3
4
<div contenteditable="true"></div>
<input type="button" onclick="document.execCommand('bold', null, false);" value="B" />
<input type="button" onclick="document.execCommand('italic', null, false);" value="I" />
<input type="button" onclick="document.execCommand('underline', null, false);" value="U">

위 소스와 같이 div 엘리먼트에 contenteditable attribute 값만 주면 편집이 가능하게 되고 버튼 3개로 입력한 Text 에 바로 blod, italic, underline 효과를 줄수 있다.

그 외에 다른 execCommand 에 대한 정보는 아래 링크에서 확인 할 수 있다.

Node.js 이용해서 Chat을 만들어보자[3]

오늘은 닉네임 입력을 하고 로그인을 하고 페이지를 빠져나오게 되면 로그아웃이 되는걸 구현해 볼까 한다.

소스는 다음과 같이 수정을 하면 된다.

server.js
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
31
var app = require('http').createServer(handler).listen(5023)
  , io = require('socket.io').listen(app)
  , fs = require('fs')
  , nicklist = {};


function handler(req, res){
  fs.readFile(__dirname + '/index.html', function (err, data) {
    if (err) {
      res.writeHead(500);
      return res.end('Error loading index.html');
    }

    res.writeHead(200, { 'Content-Type': 'text/html'});
    res.end(data);
  });
}

io.sockets.on('connection', function (socket) {
  socket.on('join', function(nick){
      nicklist[nick] = socket.nickname = nick;

      socket.broadcast.emit('joinok', nick);
      io.sockets.emit('nicknames', nicklist);
  });

  socket.on('disconnect', function(){
      delete nicklist[socket.nickname];
      socket.broadcast.emit('nicknames',nicklist);
  });
});
index.html
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>JP Chat</title>
  <script src="http://code.jquery.com/jquery-1.7.min.js"></script>
  <script src="/socket.io/socket.io.js"></script>
</head>
<body>
  //기존 소스에서
  <form id="set-nickname">
      <label>NickName : </label><input type="text" id="nick" />
      <button id="join">Join</button>
  </form>
  <div id="nicknames"><ul></ul></div>
  <script>
      (function($){
          var socket = io.connect('localhost:5023');

          socket.on('joinok', function(nick){
              $("ul", "#nicknames").append("<li>"+nick+"</li>");
          });

          socket.on('nicknames', function(data) {
              var nicklist = $("ul", "#nicknames").empty();

              for (var i in data) {
                  $("ul", "#nicknames").append("<li>"+data[i]+"</li>");
              }
          });

          $("#join").on({
              click: function() {
                  var nick = $("#nick");
                  if (nick.val() == "") {
                      alert('NickName 을 입력해주세요.');
                      nick.focus();
                      return false;
                  } else {
                      socket.emit('join', nick.val());
                      nick.val("");
                  }

                  return false;
              }
          });
      })(jQuery);
  </script>
</body>
</html>

server.js 소스에 보면 on Method 와 emit Method가 있는데 간단하게 생각하면 된다 on Method는 받는거고 emit Method는 보내는 거다 라고 생각하면 된다.

index.html 에서 join 이라는 이벤트를 server로 요청(emit)을 하게 되면 서버에서는 join 이벤트가 실행이(on) 된다.

인자값으로 넘어온 nick 을 미리 선언된 nicklist 객체에 넣는다. 배열로 하지 않고 객체로 하는 이유는 중복을 막기 위함이다. 배열로 해도 막을 수는 있지만 막으려면 또 과정을 거쳐야 하기 때문에 간단하게 key : value 로 넣어서 같은 닉네임이 있으면 덮어 씌워지는 것이다.

또 socket.nickname 에 추가로 nick 을 넣어줘서 로그아웃 처리를 할때 사용 할 수 있게 미리 담아둔다.

이제 각 client 에게 로그인을 했다는 정보를 보내주는 과정이다. 위에 소스를 보면 두가지 이벤트를 실행하게 된다. joinok 와 nicknames 이렇게 두개를 처리하는 이유는 여러 사람에게 나 로그인 했다~ 라고 보내주는것과 또 하나는 내 화면에 나 로그인 했다~ 라고 뿌려주는 역할을 따로 따로 하기 때문이다.

여기서 broadcast 에 대한 설명은 @firejune님의 블로그에서 자세한 설명을 볼 수 있다.

Socket.IO 학습 – 퍼블릭/브로드캐스트/프라이빗 구분

이제 index.html 에서 joinok 이벤트와 nicknames 이벤트를 보면 joinok 는 기본 닉네임 리스트에 내가 쓴 닉네임을 추가 시켜주고 전체 nicknames 를 가져와서 nicklist를 다시 그려주게 된다. 전체 닉네임을 갱신한다고 생각하면 될꺼 같다.

로그아웃은 따로 로그아웃 버튼이 있는건 아니고 단지 페이지를 빠져 나가게 되면 로그아웃이라고 생각하면 될꺼 같다. 그래서 server.js 를 보게되면 disconnect 이 일어 났을때 로그인을 했을때 socket.nickname 에 넣었던 닉네임을 nicklist 객체에서 delete 해준다.

그리고 다시 socket.broadcast.emit() 로 전체 client에게 nicknames 이벤트를 호출해서 로그인 리스트를 갱신하게 된다. 그러면 로그아웃한 회원을 제외한 나머지 사람들 리스트만 보이게 된다. 여기서 왜 자신꺼는 이벤트를 호출을 안하는지 혹시 의문이 들수도 있는데 이건 생각해보면 당연한거다 내껀 페이지를 빠져 나오거나 페이지 자체를 갱신했기때문이다. 이렇게 해서 로그인, 로그아웃 구현을 끝냈다.