Tornado는 python으로 만들어 진 오픈소스 웹 프레임웍과 관련 툴들로 구글의 webapp과 유사하다.
하지만 가장 큰 차이점은 non-blocking이고 매우 빠르다는 것이다. epoll을 사용해서 non-blocking 서버를 구현하기 때문에 동시에 몇천개의 접속을 처리해 줄 수 있기 때문에 리얼타임 웹 서비스에 적합하다.

현재 tornado는 python 2.5, 2.6에서 테스트 되었다. 설치하려면 먼저 PycURL과 simplejson이 설치되어 있어야 한다.

우분투에서는 다음과 같이 하면 prerequisite가 해결된다.

% sudo apt-get install python-dev python-pycurl python-simplejson

Mac OS X에서는 다음과 같이 하면 prerequisite가 해결된다.

% sudo easy_install setuptools pycurl==7.16.2.1 simplejson

현 재 tornado의 버젼은 0.2이다. 여기를 눌러 다운받으면 된다.
다운받은 다음 같은 디렉토리에서 아래의 명령어를 입력해주면 설치가 끝난다.

% tar xvzf tornado-0.2.tar.gz
% cd tornado-0.2
% python setup.py build
% sudo python setup.py install

설치가 끝나고 나면 제대로 설치되었는가 확인도 할 겸 프로그래밍을 배울 때 가장 처음에 해 보게되는 hello world 예제를 실행해 보겠다.

import tornado.httpserver
import tornado.ioloop
import tornado.web

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello, world")

application = tornado.web.Application([
    (r"/", MainHandler),
])

if __name__ == "__main__":
    http_server = tornado.httpserver.HTTPServer(application)
    http_server.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

위의 코드를 저장하고(여기서는 helloworld.py란 이름으로 저장하였다고 하겠다.) 터미널의 쉘 프롬프트에서 python helloworld.py 를 입력해서 실행해 준 다음 웹 브라우져의 주소창에 http://localhost:8888 을 입력해주면 된다.

그러면 위와 같은 화면이 나오게 된다.


Request handler and request argument

토네이도 웹 어플리케이션은 URL 또는 URL패턴을 tornado.web.RequestHandler의 서브클래스로 매핑한다. 이 클래스들은 해당 URL로의 HTTP GET 또는 POST request를 처리하기 위한 get() 또는 post() 메소드를 정의한다.

아래 코드는 루트 URL인 '/'는 MainHandler에, URL패턴인 '/story/([0-9]+)' 는 StoryHandler로 매핑하고 있다. 정규식(regular expression) 그룹은 RequestHandler 메소드에게 argument로 전달된다.

import tornado.httpserver
import tornado.ioloop
import tornado.webclass

MainHandler(tornado.web.RequestHandler):    
     def get(self):        
          self.write("You requested the main page")

class StoryHandler(tornado.web.RequestHandler):    
     def get(self, story_id):        
          self.write("You requested the story " + story_id)

application = tornado.web.Application([    
     (r"/", MainHandler),    
     (r"/story/([0-9]+)",
     StoryHandler),
])

if __name__ == "__main__":    
     http_server = tornado.httpserver.HTTPServer(application)          
     http_server.listen(8888)   
     tornado.ioloop.IOLoop.instance().start()



또한 get_argument() 메소드를 사용하면 query 스트링을 가져와서 POST 바디의 내용을 파싱할 수 있다.

import tornado.httpserver
import tornado.ioloop
import tornado.web

class MainHandler(tornado.web.RequestHandler):    
     def get(self):        
          self.write('<html><body><form action="/" method="post">'                   
                    '<input type="text" name="message">'                   
                    '<input type="submit" value="Submit">'
                    '</form></body></html>')   

     def post(self):        
          self.set_header("Content-Type", "text/plain")        
          self.write("You wrote " + self.get_argument("message"))

application = tornado.web.Application([    
     (r"/", MainHandler),  
])

if __name__ == "__main__":    
     http_server = tornado.httpserver.HTTPServer(application)
     http_server.listen(8888)    
     tornado.ioloop.IOLoop.instance().start()





클라이언트에 에러 response(예를들면 403 Unauthorized 같은)를 보내려면 tornado.web.HTTPError exception을 raise해 주면 된다.

if not self.user_is_logged_in():    
     raise tornado.web.HTTPError(403)

request handler에서 현재 request 오브젝트를 self.request 로 억세스 할 수 있다. HTTPRequest 오브젝트는 여러가지 유용한 attribute를 포함하고 있다.

    * arguments - GET 과 POST의 전체 argument
    * files - (multipart/form-data POST request를 통한) 모든 업로드 된 파일들
    * path - request 패스 (URL에서 '?' 앞쪽 전체)
    * headers - request 헤더

모든 attribute 목록은 httpserver의 HTTPRequest 클래스 정의를 보면 된다.


템플릿

Tornado에서는 python에서 사용할 수 있는 어떤 템플릿 언어도 사용할 수 있지만 tornado는 다른 템플릿 언어들에 비해 매우 빠르고 유연한 템플릿 언어를 기본적으로 포함하고 있다. 상세한 내용은 template 모듈 문서를 참조하면 된다.

Tornado 템플릿은 마크업 내에 python 제어문(control statement)와 표현식(expression)을 포함하고 있는 HTML이다. 아래 예제를 보면 쉽게 이해가 될 것이다.

<html>  
 <head>      
  <title>{{ title }}</title>   
 </head>  
 <body>    
  <ul>       
   {% for item in items %}         
    <li>{{ escape(item) }}</li>       
   {% end %}     
  </ul>   
 </body>
</html>

이 템플릿을 template.html 이라는 이름으로 저장하고 python 파일과 같은 디렉토리에 넣어주었다면 아래의 코드를 사용해서 이 템플릿을 렌더링 할 수 있다.

import tornado.httpserver
import tornado.ioloop
import tornado.web

class MainHandler(tornado.web.RequestHandler):    
     def get(self):        
          items = ["Item 1", "Item 2", "Item 3"]                 
          self.render("template.html", title="My title", items=items)

application = tornado.web.Application([    
     (r"/", MainHandler),  
])

if __name__ == "__main__":    
     http_server = tornado.httpserver.HTTPServer(application)    
     http_server.listen(8888)    
     tornado.ioloop.IOLoop.instance().start()

렌더링 된 결과이다.

<html>  
 <head>      
  <title>My title</title>   
 </head>  
 <body>    
  <ul>         
   <li>Item 1</li>        
   <li>Item 2</li>        
   <li>Item 3</li>
  </ul>   
 </body>
</html>

Tornado 템플릿은 제어문과 표현식을 지원한다. 제어문은 {%%}로 둘러쌓아주면 된다. 예제 {% if len(items) > 2 %}
표현식은 {{}}로 둘러쌓아준다. 예제 {{ items[0] }}

제어문은 python의 제어문과 동일하다. Tornado에서는 if, for, while, try를 지원하고 모두 {% end %}로 끝나야 한다. 또한 extends와 block 문을 사용해서 템플릿 상속(template inheritance)을 지원한다. 자세한 내용은 template module 문서를 참조하면 된다.

표현식은 함수 호출을 포함한 어떤 python 표현식이 될 수 있다. escape, url_escape, json_encode 함수는 디폴트로 제공한다. 또한 다른 함수를 템플릿에 넘겨주는것도 템플릿 렌더 함수에게 함수 이름을 keyword argument로 넘겨주기만 하면 된다.

class MainHandler(tornado.web.RequestHandler):    
     def get(self):        
          self.render("template.html", add=self.add)    

     def add(self, x, y):        
          return x + y

실제 어플리케이션을 만들 때 tornado 템플릿의 모든 기능, 특히 템플릿 상속 기능을 사용하길 원하게 될 것이다. templae module 섹션에서 이 기능에 대한 모든 내용을 읽어두는게 좋다.내부에서 tornado 템플릿은 python으로 직접 번역된다. 템플릿에 포함된 표현식은 템플릿을 나타내는 파이선 함수로 글자 그대로 복사된다. tornado 템플릿은 유연성을 최대한 보장하기 위해 다른 엄격한 템플릿 시스템에서는 금지하고 있는것들도 다 허용하기 위해 템플릿 언어에서 어떤것을 특별히 금지시키려고 하지 않았다. 결과적으로 템플릿 표현식에서 이상한 내용을 써 놓았으면 그 템플릿을 실행할 때 이상한 python에러를 접하게 될 것이다.

Cookies and secure cookies

사용자의 브라우져에서 set_cookie 메소드를 사용해서 쿠키를 설정할 수 있다.

import tornado.httpserver
import tornado.ioloop
import tornado.web

class MainHandler(tornado.web.RequestHandler):    
     def get(self):        
          if not self.get_cookie("mycookie"):
              self.set_cookie("mycookie", "myvalue")
              self.write("Your cookie was not set yet!")
          else:
              self.write("Your cookie was set!")

application = tornado.web.Application([    
     (r"/", MainHandler),    
])

if __name__ == "__main__":    
     http_server = tornado.httpserver.HTTPServer(application)    
     http_server.listen(8888)    
     tornado.ioloop.IOLoop.instance().start()



쿠키는 악의적인 해커가 매우 쉽게 위조할 수 있다. 현재 로그인 된 사용자의 ID를 저장하기 위해 쿠키를 사용해야 하면 쿠키 위조를 방지하기 위해 사인을 해 줄 필요가 있다. Tornado는 기본적으로 set_secure_cookie와 get_secure_cookie 메소드를 제공해준다. 이 메소드를 사용하려면 어플리케이션을 만들 때 cookie_secret 메소드를 사용해서 secret key를 지정해 줘야 한다. 어플리케이션 설정을 키워드 argument로 어플리케이션에 넘겨줄 수 있다.

application = tornado.web.Application([    
     (r"/", MainHandler),    
], cookie_secret="6fjslkdfjef24=d2t4jkldsjlv/sdfajl")

사인된 쿠키는 쿠키의 인코딩 된 값에 추가로 타임스탬프와 HMAC사인이 추가되어 있다. 만일 쿠키가 오래된 것이거나 사인이 일치하지 않으면 get_secure_cookie는 쿠키가 설정되지 않았을때와 마찮가지로 None을 리턴한다. 위의 예제의 secure 버젼은 다음과 같다.

import tornado.httpserver
import tornado.ioloop
import tornado.web

class MainHandler(tornado.web.RequestHandler):    
     def get(self):        
          if not self.get_secure_cookie("mycookie"):
              self.set_secure_cookie("mycookie", "myvalue")
              self.write("Your cookie was not set yet!")
          else:
              self.write("Your cookie was set!")

application = tornado.web.Application([    
     (r"/", MainHandler),    
], cookie_secret="6fjslkdfjef24=d2t4jkldsjlv/sdfajl")

if __name__ == "__main__":    
     http_server = tornado.httpserver.HTTPServer(application)    
     http_server.listen(8888)    
     tornado.ioloop.IOLoop.instance().start()


User authentication

현재 인증된 사용자는 모든 request handler에서는 self.current_user로, 템플릿에서는 current_user에 들어있다. 기본적으로 current_user에는 None이 들어있다.

어플리케이션에서 사용자 인증 기능을 넣어주려면 쿠키 값에 따라 현재 사용자를 판단하기 위해 request handler의 get_current_user 메소드를 override해 줘야 한다. 아래 코드는 사용자가 단순히 닉네임을 지정하면 그게 쿠키에 저장되는 간단한 예제이다.

import tornado.httpserver
import tornado.ioloop
import tornado.web

class BaseHandler(tornado.web.RequestHandler):
     def get_current_user(self):
          return self.get_secure_cookie("user")

class MainHandler(BaseHandler):
     def get(self):
          if not self.current_user:
              self.redirect("/login")
              return
          name = tornado.escape.xhtml_escape(self.current_user)
          self.write("Hello, " + name)

class LoginHandler(BaseHandler):
     def get(self):
          self.write('<html><body><form action="/login" method="post">'
                    'Name: <input type="text" name="name">'
                    '<input type="submit" value="Sign in">'
                    '</form></body></html>')

     def post(self):
          self.set_secure_cookie("user", self.get_argument("name"))
          self.redirect("/")

application = tornado.web.Application([
     (r"/", MainHandler),
     (r"/login", LoginHandler),
], cookie_secret="&k4jtlwjflkdjsdlkj24t243t==afgfg3lgjelrgds")

if __name__ == "__main__":    
     http_server = tornado.httpserver.HTTPServer(application)    
     http_server.listen(8888)    
     tornado.ioloop.IOLoop.instance().start()




이제는 로그인이 되고 쿠키가 설정되었기 때문에 http://localhost:8888 을 입력하면 http://localhost:8888/login 으로 리다이렉트 되지 않고 바로 위와 같은 화면이 나온다.

사용자가 파이선 데코레이터인 tornado.web.authenticated를 사용해서 로그인하도록 요구할수도 있다. Request가 이 데코레이터와 함께 메소드로 가고 사용자가 로그인되어 있지 않으면 login_url로 리다이렉트 된다. 위의 예제는 아래와 같이 바뀌게 된다.

import tornado.httpserver
import tornado.ioloop
import tornado.web


class MainHandler(BaseHandler):
     @tornado.web.authenticated
     def get(self):
          name = tornado.escape.xhtml_escape(self.current_user)
          self.write("Hello, " + name)

settings = {
     "cookie_secret": "&k4jtlwjflkdjsdlkj24t243t==afgfg3lgjelrgds",
     "login_url": "/login",
}

application = tornado.web.Application([
     (r"/", MainHandler),
     (r"/login", LoginHandler),
], **settings)

if __name__ == "__main__":    
     http_server = tornado.httpserver.HTTPServer(application)    
     http_server.listen(8888)    
     tornado.ioloop.IOLoop.instance().start()


Post메소드를 authenticated 데코레이터로 장식하고 사용자가 로그인 되어있지 않으면 서버는 403 응답을 보낸다.

Tornado는 구글의 OAuth같은 3rd party 인증 기법을 기본적으로 지원한다. 상세한 내용은 auth module 문서를 참조하면 된다. 이 방식의 사용자 인증과 사용자 정보를 MySQL DB에 저장하는 완전한 예제는 Tornado 블로그를 보면 된다.


Cross-site request forgery protection

XSRF는 개인화 된 웹 어플리케이션에서 매우 흔한 문제이다. XSRF가 어떻게 동작하는지에 대한 상세한 설명은 위키피디아를 보면 된다.

XSRF를 방지하기 위해 가장 일반적으로 사용되는 방법은 모든 사용자에게 쿠키로 예측할 수 없는 값을 주고 모든 form submit에 대해 그 값을 추가 argument로 포함하는 것이다. 쿠키와 form submission의 값이 일치하지 않으면 request는 위조되었을 가능성이 크다.

Tornado는 기본적으로 XSRF 방지 기능을 지원한다. 이 기능을 추가하고 싶으면 어플리케이션 설정에 xsrf_cookies를 포함시켜 주면 된다.

settings = {
     "cookie_secret": "&k4jtlwjflkdjsdlkj24t243t==afgfg3lgjelrgds",
     "login_url": "/login",
     "xsrf_cookies": True,
}

application = tornado.web.Application([
     (r"/", MainHandler),
     (r"/login", LoginHandler),
], **settings)

xsrf_cookies가 설정되어 있으면 tornado 웹 어플리케이션은 모든 사용자에 대해 _xsrf 쿠키를 설정하고 정확한 _xsrf 값을 포함하고 있지 않은 POST request는 모두 거부한다. 이 설정을 활성화 시켜 놨으면 POST로 값을 submit하는 모든 폼들에 대해 이 필드를 포함하도록 해 줘야만 한다. 모든 템플릿에서 사용할 수 있는 xsrf_form_html() 이라는 특수 함수를 지정해주면 된다.

<form action="/login" method="post">
     {{ xsrf_form_html() }}
     <div>Username: <input type="text" name="username"/></div>
     <div>Password: <input type="password" name="password"/></div>
     <div><input type="submi" value="Sign in"/></div>
</form>

AJAX POST request를 submit하는 경우 각 request에 대해 _xsrf 값을 포함하도록 JavaScript에게 알려줘야 한다. FriendFeed에서는 AJAX POST request가 자동으로 request에 _xsrf 값을 추가하도록 하기 위해 jQuery의 함수를 사용했다.

function getCookie(name) {
     var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
     return r ? r[1] : undefined;
}

jQuery.postJSON = function(url, args, callback) {
     args._xsrf = getCookie("_xsrf");
     $.ajax({url: url, data: $.param(args), dataType: "text", type: "POST",
          success: function(response) {
          callback(eval("(" + response + ")"));
     }});
};


Static files and aggressive file caching

어플리케이션에서 static_path 설정을 지정해주면 Tornado에서 static file을 사용할 수 있다.

settings = {
    "static_path": os.path.join(os.path.dirname(__file__), "static"),
    "cookie_secret": "61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=",
    "login_url": "/login",
    "xsrf_cookies": True,
}

application = tornado.web.Application([
    (r"/", MainHandler),
    (r"/login", LoginHandler),
], **settings)

이 설정을 해 주면 /static/으 로 시작하는 모든 request는 자동으로 지정된 static directory를 검색하게 해 준다. 즉 http://localhost:8888/static/foo.png 를 입력하면 지정된 static directory에 있는 foo.png파일을 보여준다. 또한 /robots.txt와 /favicon.ico 의 경우는 설사 /static으로 시작하지 않더라도 자동으로 static directory에 있는 파일을 사용한다.

브라우져가 static resource를 미리 캐슁해 놓으면 페이지 렌더링을 지연시킬 수 있는 불필요한 If-Modified-Since 나 Etag request를 보내는걸 막을 수 있기 때문에 성능 향상에 도움이 된다. Tornado는 이를 위해 기본적으로 static content versioning 이라는 기능을 지원한다.

이 기능을 사용하려면 템플릿에서 HTML파일내에서 static file의 URL을 직접 지정해주는 대신 static_url() 메소드를 사용해야 한다. 

<html>
   <head>
      <title>FriendFeed - {{ _("Home") }}</title>
   </head>
   <body>
     <div><img src="{{ static_url("images/logo.png") }}"/></div>
   </body>
 </html>

static_url() 함수는 상대패스를 /static/images/logo.png?v=aae54 같은 URI로 번역된다. v argument는 logo.png 파일 내용에 대한 해쉬값으로 이게 있으면 Tornado 서버가 사용자의 브라우져로 캐쉬 헤더를 보내 브라우져 캐쉬 내용을 영구적으로 사용할 수 있게 해 준다.

v argument는 파일 내용에 의해 계산되기 때문에 파일을 업데이트 하고 서버를 재시동시키면 새로운 v 값을 보내기 시작하기 때문에 사용자의 브라우져는 자동적으로 새 파일을 가져올 수 있게 된다. 파일의 내용이 바뀌지 않으면 브라우져는 서버에 있는 파일이 업데이트 되었는지 따로 확인하지 않고도 로컬에 캐쉬되어 있는 파일을 계속 사용할 수 있기 때문에 엄청난 렌더링 성능 향상을 가져온다.
Posted by nautes

2차원 바코드 중에 일본에서 매우 광범위하게 사용되고 있는 QR code가 있다.
QR code에 대한 설명은 이전 포스트를 참고하길 바란다.

구글이 오늘 새로운 계획을 발표하였다. 구글맵과 구글서치의 pagerank를 기초로 약 10만개의 업체를 선정하여 'Favorite Place on Google'이라는 이름이 들어간 decal을 무료로 만들어 주겠다고 한 것이다.


이 decal에 위의 그림과 같이 업체의 전화번호, 주소, 리뷰 및 쿠폰정보가 포함되어 있는 QR code를 집어넣는다고 한다.
이렇게 되면 QR code reader를 가진 휴대폰의 경우 가게 정보를 얻기 위해 따로 브라우져에서 가게 홈페이지 주소를 입력하거나 검색을 할 필요 없이 QR code만 읽으면 자동으로 가게의 홈페이지가 열려 손 쉽게 원하는 정보를 얻을 수 있게 된다.

일본 휴대폰은 거의 모든 휴대폰에 QR code reader가 기본으로 포함되어 있지만 미국에서 판매되는 휴대폰에 QR code reader가 기본으로 포함되어 있는 경우는 아직 없다. 하지만 아이폰, 블랙베리, WM, 심비안이나 java를 지원하는 대부분의 휴대폰용 무료 QR code reader가 있기 때문에 사용하는데 불편은 없다.

NeoReader - http://www.neoreader.com/
QuickMark - http://www.quickmark.com.tw/En/basic
UpCode - http://www.upc.fi/en/upcode/
Kaywa - http://reader.kaywa.com/
zxing - http://code.google.com/p/zxing
i-nigma - http://www.i-nigma.com/i-nigmahp.html

이 이외에도 검색을 해 보면 더 많은 무료 QR code reader를 쉽게 찾을 수 있다.




일본에서는 이미 상당히 오래전부터 널리 쓰여왔지만 그 이외 나라에서는 별로 쓰이지 않았는데 이번에 구글 덕택에 미국에서도 널리 쓰일 수 있는 기반이 만들어질거 같다.
Posted by nautes

구글은 다양한 서비스를 만드는것에 그치지 않고 일반인들이 자신의 서비스를 직접 이용할 수 있도록 거의 대부분의 경우 서비스에 대한 API를 제공해준다. 그 중 상당히 유용하게 사용할 수 있는것중에 하나가 Chart API이다.

이를 사용하면 동적으로 다양한 종류의 그래프를 매우 손 쉽게 그려서 웹 페이지에 보여줄 수 있게 된다.

'백문이 불여일run'이라고 직접 그래프를 만들어 보는것이 장황하게 설명하는것보다 빠를것 같다. 웹 브라우져의 주소창에 'http://chart.apis.google.com/chart?cht=p3&chd=t:60,40&chs=250x100&chl=Hello|World' 을 입력해보기 바란다.

위의 URL을 입력하고 엔터를 누르면 브라우져에 아래와 같은 그래프가 출력된다.


구글 챠트 API는 선 그래프, 막대 그래프, 파이챠트, 스캐터 그래프 등등 다양한 종류의 그래프를 그려줄 수 있으며 물론 각 그래프에 대해서 크기, 칼라, 라벨 등등 다양한 속성을 지정해 줄 수도 있다.

Chart API에서는 모든 데이터와 속성을 URL을 통해 전달해 준다.

URL의 포맷은 다음과 같다.

http://chart.apis.google.com/chart?<parameter 1>&<parameter 2>&...&<parameter n>

여러개의 파라미터는 순서는 상관없고 각 파라미터는 '&'로 연결해준다. 각 파라미터는 속성=값 쌍으로 되어 있다.

위의 예제에서 사용한 URL은 다음과 같이 구성되어 있다.

http://chart.apis.google.com/chart?       Chart API의 위치

cht=p3                                                            그래프 타입

&

chs=250x100                                                   그래프의 크기(픽셀)

&

chd=t:60,40                                                      그래프의 데이터

&

chl=Hello|World                                               그래프 라벨

또한 이 URL을 html문서 내의 <img> 태그에 임베딩해서 해당 웹 페이지가 브라우져에 보여질 때 그래프도 같이 렌더링해서 보여지게 할 수도 있다.

이 경우 <img src="<URL>" /> 의 "<URL>" 부분에 넣어주면 되는데 주의할 점은 '&' 대신 '&amp'를 사용해 줘야 하는것에 주의해야 한다.

구글 Chart API에서 사용할 수 있는 속성은 여러가지가 있는데 그래프를 그리는 경우 항상 다음의 3개 속성은 필수로 지정되어야 한다.

- 타입 (cht)

- 크기 (chs)

- 데이터 (chd)

나머지 속성들은 그래프 타입에 따라 필수이거나 또는 옵션으로 지정해 줄 수 있다.


위의 표를 보면 각 그래프 타입별로 지정할 수 있는 속성의 종류를 알 수 있다. 단 QR chart인 경우 필수 3개 이외에 추가로 지정할 수 있는 속성이 없기 때문에 위의 도표에는 생략되어 있다. 이전 포스트에 보면 구글 챠트 API를 이용해서 QR Code를 만드는 예제를 볼 수 있다.

그래프의 크기는 'chs=<폭>x<높이>'로 지정해 준다. 단위는 픽셀이다. 지도를 제외한 나머지 그래프 타입의 경우 가능한 최대 면적은 30만 픽셀이다. 폭이나 높이의 최대값은 1000 픽셀이므로 그릴 수 있는 최대 크기의 그래프로는 1000x300, 300x1000, 600x500, 500x600, 800x375, 375x800 등등이 있다. (모두 폭에 높이를 곱하면 30만이다.)

단, 지도 타입은 최대 크기가 폭 440 픽셀에 높이 220 픽셀이다.


* 다음번 글에서는 데이터 속성의 포맷과 인코딩 방식에 대해 설명하겠다.

Posted by nautes

웹 브라우징을 하다 보면 주소창의 주소 왼쪽에 표시되는 작은 아이콘을 favicon이라고 한다.


83097f2a0590c8bd375003852b4200ab.jpg

16x16 사이즈로 주소창이나 탭을 여러개 열 때 탭의 왼쪽에도 표시된다. 최근 대부분의 웹브라우져는 GIF나 PNG로 된 파비콘도 지원하지만 (유감스럽게도) 아직까지 가장 큰 점유율을 가지고 있는 IE는 Microsoft Icon Format인 ICO 파일만을 지원한다.

에디터로 직접 그릴수도 있지만 GIF, PNG, JPG로 된 이미지를 ICO로 변환하는게 훨씬 쉽게 파비콘을 만들어 줄 수 있다.

Favicon Generator 에서 파비콘을 직접 그리거나 다른 포맷의 이미지를 ICO포맷으로 변환을 할 수 있다.4a140d06a643b6cdf78bb3c389d9d9da.jpg

웹 브라우져에서 Favicon generator로 가면 다음과 같은 화면을 볼 수 있다. 여기서 1의 버튼을 눌러 변환하려는 이미지 파일을 선택하고 2번의 Create Favicon 버튼을 눌러주면 된다. 그러면 아래와 같은 화면이 나오게 된다. 여기서 동그라미로 표시된 체크박스는 원본이미지의 가로,세로 비율을 유지할것인가 여부이다.


d6c1c252335b03f33efcf824dae52718.jpg

여기서 Download the generated favicon 링크를 눌러 파일을 다운로드 받는다. 이 다운받은 파일을 자신의 홈페이지의 root directory (제일 초기화면의 index.html이 들어있는 디렉토리)에 넣어준다.

그리고 index.html 파일의 헤더부분(즉 <head> ....... </head> 사이)에 위의 두 줄을 복사해서 넣어주면 된다.

이제 자신의 홈페이지를 접속해 보면 주소창 왼쪽에 방금 만든 파비콘을 볼 수 있을 것이다.

블로그코리아에 블UP하기

Posted by nautes

구글에서 제공하는 picasa web album을 자신의 티스토리 블로그나 홈페이지에 집어넣는 방법을 설명한다.
피카사에서 바로 서비스하는건 아니고 Picasa Web Album Widget Creator for Blogger in Beta 라는 홈페이지를 이용하면 자신의 홈페이지에 추가할 수 있는 스크립트를 자동으로 만들어 준다.
아래 화면이 이 방법으로  웹 앨범을 tistory에 추가한 것이다. 아래쪽 사진이 슬라이드쇼로 보여지고 사진을 클릭하면 자동으로 웹 앨범의 그 사진으로 넘어간다. 실제 동작하는걸 보고 싶으면 이 링크로 가 보면 된다.

* 캡춰된 화면이 블로그 해상도로 자동으로 줄어들어서 보여지다 보니 글자들이 잘 안보이는데 각 사진을 클릭하면 제 크기의 선명한 화면이 나타납니다.
사용자 삽입 이미지

1. Picasa Web Album Widget Creator for Blogger in Beta 에 접속하면 아래의 화면이 나온다. 중간 부분의 빈 칸에 자신이 원하는 웹 앨범의 RSS link를 넣어주면 된다. 그 이외의 옵션은 이름을 보면 대강 의미를 짐작할 수 있을테니 생략하겠다.
사용자 삽입 이미지

2. 1번 화면에서 넣어줄 RSS링크는 먼저 picasa web album으로 가서 원하는 앨범을 선택하면 화면 오른쪽 맨 아래 부분에 파란색으로 RSS 라고 되어 있는 부분에서 얻을 수 있다. 저 부분에 마우스를 가져가서 오른쪽 버튼을 클릭해서 메뉴가 나오면 'Copy Link Location'을 선택하면 된다.
사용자 삽입 이미지

3. 2번 화면에서 RSS링크를 클립보드로 복사했으면 아래 화면의 빈 칸에 paste(붙여넣기)를 하고 'Generate Widget and Code'를 클릭하면 된다. 그러면 아래 칸에 선택한 웹 앨범을 슬라이드쇼로 보여주는 스크립트가 자동으로 생성된다.
아래쪽 'Add Picasa Web Album Widget'은 Blogger를 사용하는 경우에 자동으로 삽입해주는 것인데 여기서는 필요 없다.
사용자 삽입 이미지

4. 3번 화면에서 만들어 진 스크립트를 티스토리의 스킨에 추가해 주면 된다. 다만 html과 스크립트에 익숙하지 않은 사람은 '스킨에 추가'하는 방법이 막막할 수 있기 때문에 자세히 설명하겠다.

5. 만들어 진 스크립트는 크게 두 부분으로 나눠서 넣어줘야 한다.

more.. (생성된 코드 예제)


6. 예제 코드중에 맨 앞쪽 부분(빨간색)과 그 이후 부분(파란색)을 스킨에 분리해서 넣어 줘야 한다.
<div id="pwFeed" style="text-align:center;"></div><script type="text/javascript"> var imgNum = 0;  var pwaHeader = false;var albumDisplay = false; var pwaLogo = false; var pwaSlideShow = true; var pwaSlideSpeed = 5000; var pwaImageSize = 160; var pwaImageFeed = 20; var pwaBorder = true; var pwaBorderSize = 2; var pwaBorderColor = '#000'; function pwShow(root) { var feed = root.feed; var entries = feed.entry || []; var pwTitle = feed.title.$t; var album = feed.link[0].href; pwf=
......

7. 자신의 티스토리 관리화면으로 들어가서 스킨->스킨편집으로 간다.
사용자 삽입 이미지

8. 웹 앨범 위젯을 넣을 위치를 찾아준다. 여기서는 사이드바에서 블로그 이미지(일명 대문사진) 바로 아래 부분에 넣어 보겠다. skin.html파일에서 <div id="sidebar"> 부분으로 가 보면 모듈이 순서대로 나열되어 있다.
여기서는 블로그 로고 모듈과 공지사항 모듈 사이에 6번 화면의 빨간 부분을 넣어 준다.

</s_sidebar_element>
                                        <- 이 부분에 넣어 주면 된다.
<s_sidebar_element>
<!-- 공지사항 모듈 -->

사용자 삽입 이미지

9. 변경된 화면은 다음과 같다.
사용자 삽입 이미지


10. 그리고 6번 화면의 파란 부분을 skin.html의 </body> 바로 앞 부분에 추가해주면 된다.
아래 화면에서 </script>와 </body>의 사이에 넣어준다.
사용자 삽입 이미지

11. 변경된 화면은 다음과 같다.
사용자 삽입 이미지

12. 다 끝났으면 화면 맨 아래쪽으로 가서 '저장하기'를 눌러주면 된다. 다만 빨간 글씨로 경고하듯이 스킨을 변경하는건 잘못하면 블로그를 사용할 수 없는 상태로 만들 수 있기 때문에 주의해야 한다. 먼저 '결과보기'로 제대로 변경되었는가 확인하는게 안전하다.
사용자 삽입 이미지


Posted by nautes

자신의 도메인 네임을 가지고 있는 사람이 tistory와 google apps를 같이 사용할 수 있게 설정하는 방법을 설명한다.

일단 자신의 도메인 네임은 이미 가지고 있다고 가정하고 시작하겠다.


1. http://www.google.com/a 로 접속하여 좌측 상단에 있는 "Get Started" 버튼을 클릭한다.

2. 3가지 에디션이 있으나 Education edition의 경우 도메인 네임이 .edu인 경우만 신청이 가능하기 때문에 돈을 내고 사용할 생각이 아니면 Standard edition밖에 사용할 수가 없다. Standard Edition아래의 "Sign Up"을 클릭!

3. 도메인은 이미 가지고 있는것으로 가정하고 설명을 시작했으므로 좌측에 자신의 도메인 이름을 넣고 'Use my domain'을 클릭한다. 예) foobar.com

* 도메인을 가지고 있지 않은 경우에는 어느쪽에 이름을 넣던지 등록할 수 있는 화면으로 넘어간다.

4. 등록할 도메인에 관한 정보를 입력한다. 도메인의 사용자 수와 관리자 정보는 필수이다. (사용자 수는 적게 넣더라도 기본이 100명으로 되는것 같다.)

정보를 다 넣고 아래 박스를 체크하고 다음으로 넘어간다. (내용은 "dns 정보를 바꿀 수 없으면 등록한 도메인이 Google Apps를 사용할 수 없다는걸 이해하고 있다"이기 때문에 신경쓸 거 없다.)

5. 관리자 계정을 만들어준다.

6. 일단 설정 완료. 하지만 아래 화면에서 보듯이 모든 서비스가 'Not active'상태이기 때문에 activate시켜줘야 한다. 먼저 email을 설정하기로 하자. Email 아래의 'Activate email' 을 클릭한다.

7. Google hosted email을 사용하려면 DNS서버의 'MX' 필드값을 바꿔줘야 한다. 기본적으로 'Any hostring company'로 되어 있지만 아래방향 화살표를 눌러보면 유명한 도메인 호스팅 회사들 목록이 나온다. (아쉽게도 미국 회사들만 있다.)

다만 목록에 없는 회사라 하더라도 'Any hosting company'에 나와 있는대로 MX 필드값만 제대로 바꿔주면 아무 문제가 없다.

8. DNS의 MX 필드값을 아래와 같이 바꿔주면 된다. 이 화면은 'any hosting company'를 선택했을 경우이다.

9. 여기서는 godaddy에서 도메인을 등록하였기에 godaddy 기준으로 설명하겠다. 아래 화면은 Showing instructions for 'GoDaddy' 의 경우이다.

10. 브라우져의 다른 창에 GoDaddy로 가서 로그인 한 후 자신의 도메인을 선택하면 아래의 화면이 나온다. Total DNS 아래의 'Total DNS Control and MX Records'를 클릭한다.

11. 메일 포워딩을 위해서는 화면의 MX 레코드가 9번 화면에 나온것과 같이 설정되어야 한다.

12. 자신의 설정이 11번의 화면과 다르면 필요없는 항목은 'X'를 눌러 삭제하고 없는 항목은 'Add New MX Record' 를 클릭해서 추가한다. 'Add New MX Record'를 클릭하면 다음의 팝업이 열린다. 여기서 Host Name은 모두 '@'으로 넣어준다.

13. 위의 MX항목 설정이 다 끝났으면 9의 화면에서 'I've completed these steps'를 클릭한다. 그러면 6의 화면으로 돌아오는데 Email 옆에가 'Not active'에서 'Updating'으로 바뀐다. DNS설정은 변경한것이 효과를 나타내는데 시간이 좀 걸리기 때문에 기다려야 한다. Google에서 MX 값이 바뀐게 확인이 되면 'Updating'이 'Active'로 바뀐다.

14. 이메일 포워딩 설정이 끝났으면 나머지 서비스를 활성화 시킨다. 이메일 포워딩을 제외한 나머지 서비스 활성화는 동일한 방법을 사용하기 때문에 여기서는 Calendar를 예로 설명하겠다. 13의 화면에서 'Activate calendar'를 클릭한다. 그러면 일단 도메인의 소유주가 맞는지 확인해주는 절차를 거쳐야 한다. 확인하는 방법은 2가지가 있다. 첫번째는 HTML파일을 자신의 서버에 올려주는 것이고 두번째 방법은 DNS서버에 CNAME을 하나 추가해주는 것이다.

15. HTML 파일을 올리는것을 선택하면 아래 화면이 나온다. 설명대로 자신의 웹 서버의 웹 루트 디렉토리에 googlehostedservice.html이라는 파일을 만들고 파일에 'googleffffffffeb955e9b1'을 넣어준다. 파일에 넣어주는 값은 구글이 자동 생성해주는 것이기 때문에 아래 화면에서 복사해서 집어 넣어주면 된다. 그리고 웹 브라우져에서 'http://<자신의 웹 서버주소>/googlehostedservice.html을 입력해서 파일이 보이는지 확인하고 확인이 되면 'Verify' 를 클릭한다.

16. 14에서 'Change your CNAME record'를 선택하면 아래의 화면이 나온다. 자신의 DNS서버에 인증을 위해서 새로운 CNAME(아래 화면에서 2번항목의 굵은 글씨)을 하나 추가하고 'Verify' 를 클릭한다. CNAME을 추가하는 방법은 부록을 참조하면 된다.

17. 입력이 끝나면 Calendar도 'Not active'에서 'Updating'으로 바뀐다. 이 역시 도메인 설정을 변경하는 것이기 때문에 확인되는데 시간이 좀 걸린다. 일단 domain ownership이 확인되면 다른 서비스들은 'Activate <서비스명>' 을 클릭하면 바로 'Active'로 바뀌고 서비스를 사용할 수 있다.

18. 자신의 tistory 주소를 자신의 도메인 이름으로 하고 싶으면 먼저 도스창을 열어 자신의 Tistory 서버의 주소를 확인한다. 'nslookup <자신의 tistory주소>'를 치면 ip주소를 알 수 있다. 아래의 예에서 techblog.tistory.com의 ip 주소는 맨 아랫줄의 '211.172.252.15' 이다.

19. 위에서 확인한 주소를 DNS서버에 'A' 필드에 넣어줘야 한다. 예를 들어 'http://techblog.tistory.com' 대신 'http://techblog.sior.org'를 사용하려고 하면 아래 화면같이 'A (Host)'에 @ 항목이 '211.172.252.15'를 가르키게 하고 'CNAME (Aliases)'에 'techblog'가 '@' 을 가르키게 하면 된다.

다른 방법으로 CNAME의 'techblog' 항목을 삭제하고 A에 Host는 'techblog', Points To는 '211.172.252.15' 값을 가지는 항목을 추가해도 된다.

20. Tistory의 환경설정 -> 기본설정 화면에서 2차 블로그 주소에 방금 설정한 새 주소를 넣어준다.

20. 여기까지 제대로 되었으면 기본 설정은 끝났다. 다만 다른 서비스(calendar, docs 등등)의 URLdefault주소가 길고 복잡하기 때문에 그것도 짧고 쉬운 이름으로 바꿔보겠다. 아래 화면은 이름을 이미 바꿔놓은 후인데 바꾸기 전이면 URL들이 꽤 길고 복잡하다. 먼저 Calendar의 주소를 바꾸고 싶으면 'Calendar' 를 클릭한다.

21. 캘린더 설정화면에서 'Change URL'을 클릭한다.

22. 아직 바꾸기 전이면 위쪽의 (default)가 선택되어 있을것이다. 아래쪽의 (custom)을 선택하고 원하는 주소를 넣어주고 'Continue'를 클릭한다.

23. 이 작업 역시 DNS서버에 새 CNAME 항목을 추가해줘야 한다. 부록을 참조해서 추가해주면 된다. 이 경우 Enter an Alias Name에 'cal' 을 넣고 Points To Host Name에 'ghs.google.com' 을 넣어주면 된다. 이것 역시 DNS를 변경하는 것이기 때문에 새 이름을 사용하려면 조금 기다려야 한다.

24. 다른 서비스의 URL을 바꾸는것도 동일한 방법으로 하면 된다. 다만 한번에 하나씩 하기 귀찮으면 22번 화면에서 'Change URLs for all domain services' 를 클릭하면 아래 화면으로 진행해서 한꺼번에 다 바꿔줄 수 있다. 이 경우도 DNS에 CNAME을 동시에 추가/변경 해 줘야 한다.

25. 모든것이 다 정상으로 끝나고 DNS 설정 변경도 적용이 되면 아래같은 화면이 표시되게 된다. 이제 설정은 다 끝나고 잘~ 사용하는 일만 남았다. :)




부록 (Appendix)


a. GoDaddy로 가서 로그인 한 후 자신의 도메인을 선택하면 아래의 화면이 나온다. Total DNS 아래의 'Total DNS Control and MX Records'를 클릭한다.

 

b. 새 CNAME을 추가하려면 'Add New CNAME Record' 를 클릭한다.

c. 아래의 팝업창에서 데이터를 입력한다. 위의 16번 화면의 경우 Enter an Alias Name에 'googleffffffffeb95e9b1' 을 넣고 Points To Host Name에 'google.com' 을 넣고 'OK'를 클릭한다.

d. 새로운 A항목을 추가하려면 b의 화면에서 'Add New A Record' 를 클릭하면 아래의 팝업화면이 나온다. Host Name에 새 호스트의 이름을, Points To Ip Address에 새 호스트의 IP주소를 넣어주고 'OK' 를 클릭하면 된다.

Posted by nautes

이전버튼 1 이전버튼