Google Docs에는 많이 알려지지 않았지만 매우 편리한 기능이 숨겨져있다.
그 중 유용한 것이 이메일을 통한 설문조사 기능이다.


Google Docs의 좌측 상단에 있는 'Create New'를 눌러 그 중 'Form'을 선택한다.


위와 같이 질문을 입력하는 화면이 나오게 된다. 여기서 각 질문을 만들어주면 된다.


각 항목에 대한 설명은 위와 같다.


답변 타입은 위와 같이 7개가 있다.


질문을 추가하려면 좌측 상단의 Add item 버튼을 눌러주면 된다.


설문지가 어떻게 보이는지 미리보기를 하고 싶으면 화면 아래쪽의 검은색 부분에 있는 주소를 눌러주면 된다.


그러면 브라우져의 새 창이나 탭에 위와같이 설문지를 미리보기 할 수 있다.


설문지 디자인이 심심(?)하다고 생각되면 Theme 버튼을 눌러 테마를 선택할 수 있다. 모든 확인이 끝났으면 Email this form 버튼을 눌러 설문지를 보내면 된다.


현재 약 70개 정도의 테마가 있다.


Email this form 버튼을 누르면 위와 같은 화면이 열려 누구에게 메일로 보낼지 지정해 줄 수 있다. 여러 사람의 주소를 쓰는 경우 각 주소는 ','로 구분해주면 된다.


설문지를 받은 사람의 브라우져 화면이다.


설문에 답한 다음 Submit버튼을 눌러주면 된다.


Submit 버튼을 누르면 데이터를 보내기 때문에 진짜로 보낼건지 확인한다. 'OK'를 눌러주면 된다.


데이터가 전달되고 나면 위와 같이 Thank you 화면이 나온다.


이렇게 메일로 수집된 데이터는 자동으로 설문을 보낸 사람의 스프레드쉬트 파일에 입력된다. Google Docs에 가 보면 자신이 만든 설문에 대한 파일이 생겨져 있다.


파일을 열어보면 위와 같이 설문 응답 내용이 자동으로 스프레드 쉬트에 입력되어 있다.


또한 설문을 보내 사람은 메일함을 열어보면 설문결과를 확인할 수 있는 링크와 설문지를 추가로 발송할 수 있는 링크가 들어있는 메일이 google docs로부터 와 있다.

이 기능을 사용하면 설문지를 돌리고 수집한 다음 결과를 정리하기 위해 입력하는 수고 없이 매우 쉽게 설문조사를 할 수 있다.
Posted by nautes



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

iProcessing이 라는 아이폰용 Processing이 나왔다.

Processing은 Ben Fry와 Casey Reas가 만든 오픈소스 프로그래밍 언어로 배우기 쉬워 미디어 아트나 예술계 쪽 학생들이 취미나 프로토타이핑, 미디어 아트 작품 제작에 많이 사용되고 있다.


맥, 윈도우, 리눅스등 플랫폼을 가리지 않고 동일하게 사용할 수 있다.


이전 포스트에서도 한번 소개했던 것처럼 Mobile Processing 이라고 java를 지원하는 휴대폰에서 실행되는 Processing도 있고 Wiring 보드에서 실행되는 코드를 만들어주는 Wiring이라는 Processing 도 있다.

모바일 프로세싱 - 휴대폰에서 프로세싱을 사용하기 (Mobile Processing runnng on a cellphone)

모바일 프로세싱을 사용해 보기 (Using Mobile Processing)

그런데 아이폰은 아쉽게도 java를 지원하지 않기 때문에 Mobile Processing을 사용할 수 없었는데 이번에 아이폰에서 동작하는 코드를 만들어주는 Processing인 iProcessing 나온것이다.

")//]]>


다만 다른 Processing과 차이점은 위의 화면과 같이 프로그램상에서 직접 코드를 집어넣어 실행파일을 만들어 주는게 아니고 아이폰용 앱을 만들 수 있는 프레임웍 형태로 제공된다.


iProcessing을 다운받아 압축을 풀으면 위와 같은 파일이 생긴다. Processing이나 Mobile Processing과는 달리 별도의 실행파일은 없다.


examples 폴더로 들어오면 이런식으로 각각의 예제 프로젝트가 들어있다.


Animator 폴더의 내용이다. Xcode 프로젝트 파일이 들어있는게 보인다. 저 파일을 더블클릭하면 Xcode가 실행되고 프로젝트가 열린다.


main 폴더의 내용이다. 다른 부분은 프레임웍의 일부분으로 손 댈 필요가 없고 main.pde를 수정해서 자신이 원하는 동작을 하게 해 줄 수 있다.


이 파일을 수정해 원하는 프로그램을 만들어 준 다음 Xcode에서 빌드하면 아이폰 시뮬레이터에서 직접 실행시켜 동작을 확인할 수 있다.

기본적인 processing의 기능 이외에 추가로 멀티터치, 가속도센서, 방향(가로/세로), 위치, 사운드 플레이/녹음 등등 아이폰의 다양한 기능도 사용할 수 있다. 또한 따로 Objective-C를 배우지 않아도 간단하게 아이폰 앱을 만들어 줄 수 있다.

Posted by nautes

이전 포스트에서 소개했던 모바일 프로세싱을 실제 사용해 보도록 하겠다.

노키아 6210S를 구입한 사람들도 많아졌고 최근 노키아 5800 Music edition이 판매되기 시작하여 Symbian S60을 사용하는 사람들이 많이 늘어났기 때문에 모바일 프로세싱을 실제 폰에서 사용해 보기가 매우 편해졌다.

모바일 프로세싱을 설치해 주려면 Java, Java Wireless Toolkit for CLDC (WTK, 현재 최신버젼은 2.5.2_01이다), mobile processing까지 총 3개의 파일이 필요하다.
JavaWTK는 sun의 웹 사이트에서 다운받을 수 있고 mobile processing은 모바일 프로세싱 홈페이지에서 다운받으면 된다.

파일을 다 다운받았으면 먼저 java를 설치해준다. 이미 설치되어 있는 경우 이 단계는 건너뛰면 된다. 그 다음 WTK를 설치해 준다. 그리고 WTK가 설치된 위치를 기록해 놓는다. (디폴트는 c:\wtk2.5.2_01 이다.)
마지막으로 다운받은 mobile processing 파일의 압축을 풀어준다. 모바일 프로세싱은 별도의 설치 과정이 필요없이 그냥 압축을 풀어주면 된다.


압축을 풀면 위와 같은 파일이 보인다. 여기서 'mobile'을 더블클릭해서 실행해 주면 된다. (별도의 설치과정이 없기때문에 시작메뉴에도 자동으로 등록되지 않는다.)


모바일 프로세싱을 실행시킨 화면이다. 프로세싱 화면과 거의 동일하다. 다만 모바일 프로세싱을 사용하려면 먼저 환경설정을 해 줘야 한다.


File->Preferences 를 선택한다.


Preferences 창이 열리면 Mobile 탭을 선택한다.


이 화면에서 아까 설치한 WTK의 위치를 지정해 줘야 한다. 그리고 자신이 사용할 폰의 CLDC와 MIDP 버젼을 지정해준다. 최근 1~2년 사이에 나온 폰들이면 거의 다 CLDC 1.1과 MIDP 2.0을 지원한다. 자세한 내용은 각 폰의 technical specification을 참조하면 된다.

여기까지 하면 모든 설정이 끝나고 모바일 프로세싱을 사용할 수 있다.


테스트를 위해 File->Example에서 예제를 하나 선택해 보겠다. 여기서는 keypad를 선택했다.


화면 위쪽의 아이콘들 중에 가장 왼쪽의 삼각형 버튼을 누르면 프로그램을 컴파일하고 폰 에뮬레이터에서 실행을 해 준다.


실행을 시키면 이런 폰 에뮬레이터 창이 뜨고 프로그램이 로드된 걸 볼 수 있다. 로드된 프로그램을 실행하려면 오른쪽 소프트키(화면 오른쪽 아래 Launch 표시 바로 아래쪽의 '.' 버튼)를 눌러주면 된다.


'keypad' 프로그램이 실행된 화면이다. 프로그램을 종료하려면 왼쪽 소프트키를 눌러주면 된다.


'keypad' 프로그램은 숫자키패드를 누르면 그에 해당하는 수직막대기를 화면에 그려주는 프로그램이다. 몇개의 숫자키를 눌렀을 때 화면이다.

이 런 식으로 작성한 프로그램을 실제 폰이 없는 경우에도 컴퓨터 상에서 테스트가 가능하다. 디버깅이 다 끝났으면 만들어 진 프로그램을 직접 폰에서 실행시킬수도 있다.


File->Export MIDlet 을 선택하면 이 소스코드를 폰에서 실행할 수 있는 JAVA MIDlet으로 만들어준다.


위에서 Export MIDlet을 선택하면 keypad.jar 파일을 만들고 탐색기에서 그 파일이 들어있는 폴더를 자동으로 열어준다.


이제 만들어 진 파일을 폰으로 넣어주기만 하면 된다. MicroSD카드에 복사해서 옮겨도 되지만 여기서는 블루투스를 이용해 옮겨보겠다. (단 폰은 미리 컴퓨터와 페어링 되어 있어야 한다.) 'keypad.jar'파일을 선택한 다음 마우스 오른쪽 클릭해서 Send to->Bluetooth device를 선택한다.


Bluetooth File Transfer 창이 열리고 블루투스 디바이스들을 보여준다. 원하는 폰을 선택해주면 된다.



이렇게 전송이 끝났으면 폰에서 받은 프로그램을 설치할거냐고 물어본다. 설치해 준 다음 프로그램을 실행시키면 위의 시뮬레이터에서 본 것과 동일하게 움직이는걸 볼 수 있을 것이다.

모바일 프로세싱은 이전에 프로세싱이나 아뒤노를 사용해 본 사람이라면 거의 동일한 구조와 문법을 사용하기 때문에 배우기 쉽고 폰 프로그래밍을 할 때 단말기별로 신경써야 하는 것들을 알아서 처리해주기 때문에 매우 편리하다.

또한 단순히 키패드 입력, 화면 제어뿐 아니고 폰의 블루투스 통신도 프로그래밍이 가능하기 때문에 임베디드쪽으로도 다양하게 응용이 가능하다. 아뒤노에 블루투스 모듈을 붙이고 모바일 프로세싱으로 폰과 통신을 하게 해 주면 폰을 아뒤노의 입출력 디바이스 또는 리모컨으로 이용할 수도 있기 때문에 다양한 응용이 가능하다.
Posted by nautes

최근들어 피지컬 컴퓨팅이라는 분야에 대한 관심이 높아지면서 arduino 뿐 아니고 processing도 많이들 사용하고 있다. Processing은 주로 컴퓨터에서 독립적으로 사용하거나 외부 하드웨어와 연동하는 경우는 processing+arduino의 조합으로 사용하는 경우가 많다.



그에 비해 휴대폰에서 사용할 수 있는 mobile processing은 국내 휴대폰에서 사용할수가 없어서인지 아직까지 사용하는 사람들을 별로 본 적이 없는거 같다. 해외용으로 출시된 LG, 삼성 휴대폰에는 잘 돌아가지만 국내 출시 휴대폰들은 통신사와 제조사의 여러가지 제한때문에 mobile processing을 사용할 수 있는 모델이 거의 없었다. 하지만 최근 출시된 노키아 6210의 경우 심비안 S60 플랫폼을 사용하기 때문에 mobile processing을 사용할 수 있게 되었다. Mobile processing은 현재 Mac OS X와 윈도우용이 있다.

아래가 삼성, LG모델중에 mobile processing을 사용할 수 있는 모델들인데 대부분 수출모델로 알고 있다. 혹시 자신의 휴대폰에서 실행해보고 되는 경우 알려주면 국내 휴대폰 모델들 리스트에 업데이트 해서 목록을 만들어 봤으면 한다.

LG와 삼성의 지원되는 모델들


기본적인 문법은 processing과 거의 동일하고 mobile processing에서 스케치 파일을 만들어 컴퓨터상의 에뮬레이터에서 실행해 볼 수 있고 컴파일해서 만들어 진 MIDlet을 microSD 카드에 복사해서 휴대폰에서 어플리케이션으로 실행할 수 있다. MIDlet은 J2ME플랫폼의 CLDC 1.0, MIDP 1.0에서 실행된다.

기본적으로 휴대폰 전화기능, 이미지, 사운드, 비디오, 네트웍, XML, Messaging, 블루투스등을 제어하기 위한 라이브러리가 제공된다. 또한 3rd party가 추가한 3D 그래픽, QR code, 웹, UI, Location 서비스 등등 다양한 라이브러리가 존재한다.

Arduino 에 블루투스 모듈을 붙여 휴대폰과 블투로 통신하게 할 수도 있고 아니면 휴대폰의 시리얼 포트와 arduino의 시리얼 포트로 통신을 하게 해 줄 수도 있다. 이 경우 arduino에 별도의 LCD나 키패드를 붙이지 않아도 훌륭한 입출력 인터페이스를 만들어 줄 수 있다. 또한 휴대폰의 다양한 장치들을  arduino의 센서(디지털 나침판, GPS, 조도센서, 가속도센서 등등)로 사용하거나 휴대폰을 arduino의 네트웍 인터페이스(무선랜, 블루투스, 휴대폰 망)로 사용할수도 있게 된다. 또한 전화를 걸고 받기, 문자메세지 보내기 등등도 제어할 수 있기 때문에 센서 입력에 따라 문자를 보내거나 전화를 걸도록 할 수도 있다.


인터넷에서 발견한 mobile processing과 arduino를 시리얼로 연결한 예제 프로젝트이다. 그림에서처럼 arduino와 휴대폰은 시리얼로 연결하고 휴대폰은 휴대폰망의 무선데이터 전송을 통해 컴퓨터와 http로 연결해서 컴퓨터에서 arduino에 연결되어 있는 LED의 색깔을 제어하는 예제이다.


")//]]>

최 근 노키아 6210이 매우 싼 가격(약 6개월 약정인 경우 1000원정도)에 풀렸다고 알고 있다. Arduino를 사용하는 사람들이라면 이번 기회에 mobile processing에도 관심을 가져 보는것도 나쁘지 않을 것이다. Arduino만으로 하기 힘든 다양한 아이디어의 응용 프로젝트가 가능해진다.
Posted by nautes

인터넷 시대 이전에 모뎀을 사용해서 PC통신을 하던 사람들에게는 눈에 익은 AT 명령어(Hayes AT command)를 사용해서 노키아 휴대폰을 컴퓨터에서 직접 제어해서 전화를 걸고 받거나 문자 메시지를 보낼 수 있다.
컴퓨터와 노키아 휴대폰은 블루투스를 사용해서 연결해 준다.

여기서는 맥에서 노키아 E71x를 연결해서 제어하는 방법을 설명하겠다.

먼 저 맥과 E71x는 페어링 되어 있어야 한다.



'Preferences' 에서 'Bluetooth'를 선택한다.


디바이스(여기서는 E71x)를 선택하고 아래쪽의 휠 버튼을 눌러준다.



버튼을 누르면 나오는 풀다운 메뉴 중에 'Edit Serial Ports..'를 선택한다.



디바이스 설정 창이 열리면 '+' 버튼을 누른다.



Name, Protocol, Service를 차례대로 설정한다. Name은 디바이스 이름으로 사용되기 때문에 간단한 이름을 넣어주는게 좋다. 여기서는 e71x를 넣어줬고 저 가상 시리얼 포트의 패스는 /dev/tty.e71x가 된다.



앞의 화면에서 'Apply'를 눌러 설정이 끝난 상태이다. 여기까지 했으면 맥과 휴대폰 설정은 끝났다.



맥에서 시리얼 터미널 프로그램을 실행시킨다. 여기서는 GoSerial 이라는 프로그램을 사용했지만 아무 시리얼 터미널 프로그램(CoolTerm, jterm 등등)들을 사용해도 관계 없다.

위의 그림은 GoSerial의 포트 설정 화면이다. 시리얼 포트 파라미터는 9600-N-8-1로 맞춰주면 된다.



설정이 끝났으면 'Connect' 버튼을 눌러 휴대폰과 연결시켜주면 된다.



정상적으로 연결되면 이제부터 AT 명령어를 사용해 휴대폰을 제어할 수 있다.



휴대폰으로 전화를 걸어 본 예이다. 모뎀과 동일하게 ATDT 명령어를 사용해서 전화를 걸 수 있다.

문자메시지를 보내는 방법이다.

AT+CMGF=1
AT+CMGS=”##########”      ;#####은 수신자 번호이다.
[메시지]를 입력하고 끝나면 CTRL-Z를 누른다.


노 키아 휴대폰에서 사용할 수 있는 AT 명령어 셋은 노키아 사이트에서 확인할 수 있다.
Posted by nautes

아이폰에서 사진을 찍은 다음 그 사진을 메일로 보내면 사진 크기가 자동을 800*600(원래 크기의 1/4)으로 리사이즈 되어 보내지게 된다.
웹사이트에 첨부하거나 싸이에 올리는데는 충분한 크기지만 인쇄를 하거나 후보정을 원하는 경우는 풀사이즈(1600*1200)의 사진이 필요하다.

먼저 'Photos' 앱을 실행한다.

Photos 앱에서 왼쪽 아래의 버튼을 누른다.


사진을 선택할 수 있는 상태가 되었다.


메일로 보낼 사진들을 터치해서 선택해 준다. 선택된 사진은 체크 표시가 나타난다. 선택이 끝나면 아래쪽의 Copy 버튼을 눌러준다. 그리고 Photos 앱은 종료하고 Mail 앱을 실행한다.


메일 앱에서 메일 작성을 선택하고 메일 내용에 가서 한 지점을 약간 오래 터치하고 있으면 메뉴가 나타나게 되고 그 메뉴중에 Paste를 선택해서 사진을 첨부해주면 된다.

이렇게 해서 메일을 보내면 사진이 자동으로 리사이즈 되지 않고 원래의 풀사이즈로 보내진다.
Posted by nautes

한국식(?) mp3플레이어에 익숙한 사람들은 그냥 mp3파일을 mp3플레이어 내의 폴더에 바로 복사하는걸 더 편하게 생각하고 아이튠의 음악 관리 방식을 불편하다고 생각하는 경우가 많다.
하지만 drag-n-drop방식은 아주 원시적인 방식으로 플레이어에 기껏해야 자주 듣는 음악 몇십곡 집어넣고 검색같은거 없이 곡들을 그냥 순차적으로 듣는 경우에나 유용하지 곡이 많아지거나 조금만 복잡한(?) 방식의 음악듣기를 원하는 경우에는 속수무책이 되어 버리기 쉽다.

예를 들어 '우울할 때 듣는 곡', '내 best 30', '야간 드라이브 할 때 듣는 곡' 식으로 폴더를 나눠 놓고 음악을 집어 넣는데 만일 A라는 곡이 3가지에 모두 포함되는 경우 A의 파일을 폴더 3곳에 각각 복사를 해 줘야 하기 때문에 플래쉬 메모리가 낭비되게 된다.

그 에 비해 아이튠의 경우 A의 파일은 한 곳에만 저장되고 (저장 위치는 사용자가 신경 쓸 필요가 없다. iTunes가 자동으로 알아서 관리해 준다.) 3개의 플레이 리스트를 만들어 그 플레이 리스트에 A라는 곡을 넣어만 주면 된다.

그리고 iTunes는 거기서 한발 더 나아가서 Smart playlist를 지원한다.
이건 플레이리스트에 사용자가 따로 곡을 집어넣어 줄 필요 없이 원하는 조건만 지정해 주면 그 조건에 맞는 곡들을 자동으로 목록에 추가해 주는 기능이다.


아이튠에서 곡 정보 화면이다. 여기에 그 곡의 다양한 정보(Tag)가 들어간다. 아이튠의 경우 대부분의 동작이 이 tag를 기반으로 움직이기 때문에 곡의 tag를 정확하게 관리해 주는게 필수적이다. 이 역시 drag-n-drop방식에 익숙한 사람들에게는 쓸데없는 일이라고 생각되기 쉬운데 tag는 일단 한번만 정리 해 놓으면 더 이상 편하고 효율적일수가 없다.


iTunes를 실행한다.


Files->New Smart Playlist... 를 선택해 준다.


스마트 플레이리스트를 설정해 줄 수 있는 창이 열리게 된다. 조건 항목이 1개인데 항목을 늘리고 싶으면 오른쪽의 '+' 버튼을 눌러주면 된다.


위쪽의 화면에서 '+' 버튼을 눌러 조건 항목을 2개로 만든 화면이다. 다시 조건항목을 줄이려면 '-' 버튼을 눌러주면 된다.

여기서 조건이란 태그에 들어있는 값들 중에 자신이 원하는 조건을 말한다. 즉 '[가 수 이름](a)[ABCDE](b)[시작](c)하는 노래' 같은 식으로 원하는 조건을 설정할 수 있다. 위의 조건 항목에서 첫번째 칸에 (a)의 값을, 두번째 칸에 (c)의 값을, 세번째 칸에 (b)의 값을 넣어주면 된다.


(a) 항목에 들어갈 수 있는 값들이다. 가수 이름 뿐 아니라 앨범제목, 작곡가, 쟝르, 발매년도, rating, 샘플링 레이트, 플레이 횟수 등등 매우 다양한 조건을 선택할 수 있다.


(c)항목에 들어갈 수 있는 값이다. 가수 이름인 경우 정확한 가수 이름(is) 뿐 아니고 특정 가수 제외(is not), 이름 일부분(contains), 이름 일부분에 제외(does not contain), 특정 글자로 시작하는 이름(starts with), 특정 글자로 끝나는 이름(ends with)등의 조건을 줄 수 있다.

또한 여기는 안 나왔지만 (a)가 발매년도나 Ratings인 경우 어디부터 어디까지(is in the range)등의 조건도 줄 수 있다.


임의로 만들어 본 조건이다. 저 플레이 리스트에는 내 ratings가 별 4개 이상이고 Jazz쟝르이면서 2000에서 2009년에 나온 곡들이 자동으로 들어가게 된다.

위에서 본 것 같이 Smart Playlists를 잘 활용하면 그냥 순차적으로 또는 랜덤하게 듣는 방법 이외에 진짜 매우 smart하게 자신이 원하는 곡들을 찾아 들을 수 있는 다양한 방법을 사용할 수 있게 된다.
Posted by nautes

아이폰에서 필드테스트 모드로 들어가는 방법은 다음과 같다.


아이폰 전화 모드에서 '*3001#12345#*'를 넣고 통화(Call) 버튼을 누르면 바로 Field Test 모드로 진입한다.

Field Test 모드 메뉴 화면이다.




각 메뉴로 들어가면 위와 같은 무선망에 대한 상세 정보를 직접 확인이 가능하다.

* 필드테스트 모드에서 확인할 수 있는 내용은 상당히 전문적인 값들이라 휴대폰 기술 관련되어 각 용어와 의미를 아는 사람이 아니면 별로 필요 없는 내용이다.
Posted by nautes

일반적으로 많이 쓰이지는 않아도 라우터등의 장비 펌웨어 업그레이드라던지 임베디드 보드 개발할 때 유용한 도구중에 하나가 tftp(trivial ftp)이다.
Mac OS X에도 역시 기본으로 포함되어 있지만 다른 daemon들과는 다르게 자동으로 실행하게 되어있지 않다. 그리고 실행하려면 직접 하면 안되고 꼭 launchd와 /System/Library/LaunchDaemon/tftp.plist를 사용해서 실행해야 한다.



실행하는 방법은 다음과 같다.

$ sudo launchctl
Password:
launchd% load -F /System/Library/LaunchDaemons/tftp.plist
launchd%

이제 아무 문제가 없으면 launchd가 tftp 포트를 listen하고 있다가 connection이 들어오면 tftp를 실행해주게 된다.

그 리고 linux/unix의 경우 일반적으로 tftp 디렉토리는 /tftpboot 를 사용하지만 맥에서는 tftp.plist 파일에서 지정하게 되어 있고 디폴트로는 /private/tftpboot 디렉토리이다.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Disabled</key>
<true/>
<key>Label</key>
<string>com.apple.tftpd</string>
<key>ProgramArguments</key>
<array>
<string>/usr/libexec/tftpd</string>
<string>-i</string>
<string>-s</string>
<string>/private/tftpboot</string>
</array>
<key>Sockets</key>
<dict>
<key>Listeners</key>
<dict>
<key>SockServiceName</key>
<string>tftp</string>
<key>SockType</key>
<string>dgram</string>
</dict>
</dict>
<key>inetdCompatibility</key>
<dict>
<key>Wait</key>
<true/>
</dict>
</dict>
</plist>

 
텍 스트 에디터로 tftp.plist파일을 열어보면 내용은 위와 같다. 그 중 bold체로 된 부분을 변경해 주면 tftp 디렉토리를 원하는대로 바꿔줄 수 있다.

사용이 끝나 실행을 중단시키고 싶으면 실행할 때와 마찬가지로 launchctl에서 명령을 내려주면 된다.

launchd% unload /System/Library/LaunchDaemons/tftp.plist

만일 tftp를 부팅할 때 부터 계속 실행하도록 해 주고 싶으면 launchctl에서 다음의 명령을 사용한다.

launchd% load -w  /System/Library/LaunchDaemons/tftp.plist

또한 반대로 부팅할 때 부터 계속 실행되는걸 멈추고 싶으면 아래 명령을 사용하면 된다.

launchd% unload -w /System/Library/LaunchDaemons/tftp.plist
Posted by nautes