Recommanded Free YOUTUBE Lecture: <% selectedImage[1] %>

Contents

Tutorial

Sinatra 설치에서, Hello world를 출력하는 간단한 서비스 개발. erb 템블릿을 붙이고, Database 연결하는 것까지를 따라하기(Tutorial) 느낌으로 정리해 보려고 한다.

환경

  • VirtualBox : VM으로 운영체제 하나 띄워서 개발 및 테스트
  • Ubuntu Linux 13.04 server
  • Ruby 1.9.1

Sinatra 설치

Ruby 버전을 확인한다. Ruby가 없다면 설치
# ruby --version
ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-linux]

gem으로 sinatra를 설치한다.
gem install sinatra
Successfully installed sinatra-1.4.3
1 gem installed
Installing ri documentation for sinatra-1.4.3...
Installing RDoc documentation for sinatra-1.4.3...

Hello World

Hello World를 출력하는 프로그램으로 이름은 hi.py다.
#!/usr/bin/ruby
require 'sinatra'

get '/hi' do
    "Hello World!"
end

# ruby hi.rb 
[2013-09-15 21:18:40] INFO  WEBrick 1.3.1
[2013-09-15 21:18:40] INFO  ruby 1.9.3 (2012-04-20) [x86_64-linux]
== Sinatra/1.4.3 has taken the stage on 4567 for development with backup from WEBrick
[2013-09-15 21:18:40] INFO  WEBrick::HTTPServer#start: pid=1451 port=4567
Sinatra는 HTTP 웹 서버 라이브러리인 WEBrick를 이용한 자체 웹 서버를 내장하고 있다. 성능과 기능은 믿음직 하지 않지만 테스트 용도로 간단하게 사용할 수 있다.

Sinatra의 기본 바인드 주소는 127.0.0.1:4567이다. 따라서 외부에서는 접근할 수 없다. 다음과 같이 바인드 주소를 설정할 수 있다.
require 'sinatra'

set :bind, '0.0.0.0'
set :port, 80
get '/hi' do
    "Hello World!"
end

URL 파라메터 읽기

get '/user/:name' do
    "Hello #{params[:name]}"
end
혹은
get '/user/:name' do |name|
    "Hello #{name}"
end

두 개이상의 파라메터도 처리할 수 있다.
get '/user/:first/:second' do |name1, name2|
    "Hello #{name1}-#{name2}"
end

HTTP Request 정보 읽기

유저의 요청 정보를 처리하기 위해서는 HTTP headerBody 정보를 읽어야 한다. Header에서는 content-type, cookie 정보, 기타 애플리케이션 헤더 정보를 가져올 수 있다. Body에서 유저요청의 상세 내용을 읽어온다.
#!/usr/bin/ruby
require 'sinatra'
require 'json'
require 'pp'

post '/user' do
    user_data = JSON.parse(request.body.read)
    pp user_data
    "Content_type : #{request.env['CONTENT_TYPE']}"
end
Sinatra 서버를 실행한 다음, curl로 테스트를 해보자. curl이 익숙치 않다면, postman 으로 테스트를 해도 된다.
# cat data.json 
{'name':'yundream', 'mail':'yundream@gmail.com', 'address':'seoul' }

# curl -X POST http://192.168.56.101/user -d @data.json -H "Content-Type: application/json"

Rack의 Request로 가져올 수 있는 정보들을 정리했다.
get '/foo' do
  request.body              # 클라이언트의 요청 데이터 
  request.scheme            # "http"
  request.script_name       # "/example"
  request.path_info         # "/foo"
  request.port              # 80
  request.request_method    # "GET"
  request.query_string      # ""
  request.content_length    # request.body의 크기
  request.media_type        # request.body의 media 타입
  request.host              # request host 
  request.get?              # true (similar methods for other verbs)
  request.form_data?        # false
  request["SOME_HEADER"]    # value of SOME_HEADER header
  request.referer           # the referer of the client or '/'
  request.user_agent        # user agent (used by :agent condition)
  request.cookies           # 브라우저 cookie의 해쉬 값 
  request.xhr?              # is this an ajax request?
  request.url               # "http://example.com/example/foo"
  request.path              # "/example/foo"
  request.ip                # 클라이언트 IP 주소 
  request.secure?           # false
  request.env               # raw env hash handed in by Rack
end
문서를 보면 request!["SOME_HEADER"!]로 헤더값을 읽을 수 있다고 했는데, 실행해보면 읽어오지 않는다.

템플릿

템플릿은 애플리케이션이 처리한 값을 문서에 집어넣기 위해서 사용한다. 수십종의 템플릿엔진이 있는데(Sinatra readme 참고, 주로 (기본 내장 템플릿 엔진인)erb와 haml(HTML abstraction markup language)를 사용한다. HTML 문서 처리의 경우 성능 때문에 haml을 많이 사용하는 추세인것 같다. Haml의 단점은 HTML 전용이라는 점이다. 나는 주로 REST + JSON 기반의 API 서버 개발 목적으로 sinatra를 사용하기 때문에, 범용적으로 사용할 수 있는 erb를 사용한다.
ERBHaml
<section class=”container”>
  <h1><%= post.title %></h1>
  <h2><%= post.subtitle %></h2>
  <div class=”content”>
    <%= post.content %>
  </div>
</section>
%section.container
  %h1= post.title
  %h2= post.subtitle
  .content
    = post.content

ERB 사용

Sinatra는 view 파일들을 views 디렉토리에서 읽는다. views 디렉토리를 만든다음 index.erb 파일을 배치했다.
# cat views/index.erb
{
    "first":<%=name1%>,
    "second":<%=name2%>
}

get '/user/:first/:second' do |name1, name2|
    erb :index
end

ERB관련 내용은 여기를 참고

haml 사용

haml은 내장 템플릿엔진이 아니다. 먼저 설치해야 한다.
# gem install haml

index.haml 파일을 만들었다.
# cat views/index.haml
!!!
%html
    %head
        %title Test !!!
    %body
        %h2 #{@first}.#{@second}

#!/usr/bin/ruby
require 'sinatra'
require 'haml'

get '/user/:first/:second' do |name1, name2|
        @first = name1
        @second = name2
        haml :index

응답 헤더 편집

Sinatra의 ResponseHelpers 모듈을 이용해서 응답헤더를 편집할 수 있다.

content_type 변경
get '/test' do
    content_type 'text/html', :charset => 'utf-8'
    "<h1>안녕하세요<h1>"
end

headers를 이용한 변경
headers 'Content-Type'=> 'application/json', 
       'X-APP-NAME'=> 'test-app'

URL redirect
get '/redirect' do
    redirect '/somewhere/else', 301
end

HTTP authentication

HTTP 인증을 위한 두 개의 옵션이 있다.

첫번째 인증은 Rack::Auth::Basic를 이용한 인증이다.
require 'sinatra'

use Rack::Auth::Basic, "Restricted Area" do |username, password|
  username == 'admin' and password == 'admin'
end

get '/' do
  "You're welcome"
end

get '/foo' do
  "You're also welcome"
end

특정 URL만 보호하고 싶다거나 복잡한 인증 방식을 구현하길 원할 경우.
require 'sinatra'

helpers do
  def protected!
    return if authorized?
    headers['WWW-Authenticate'] = 'Basic realm="Restricted Area"'
    halt 401, "Not authorized\n"
  end

  def authorized?
    @auth ||=  Rack::Auth::Basic::Request.new(request.env)
    @auth.provided? and @auth.basic? and @auth.credentials and @auth.credentials == ['admin', 'admin']
  end
end

get '/' do
  "Everybody can see this page"
end

get '/protected' do
  protected!
  "Welcome, authenticated client"
end

세션 이용하기

세션은 disabled 상태로 시작한다. 세션을 사용하기 위해서는 enable 상태로 하면 된다. 세션은 hash 형태로 사용할 수 있다.
enable :sessions

get '/foo' do
  session[:message] = 'Hello World!'
  redirect to('/bar')
end

get '/bar' do
  session[:message]   # => 'Hello World!'
end

세션의 다른 파라메터에 대한 수정이 필요하다면 Rack::Session::Cookie를 이용하면 된다.
use Rack::Session::Cookie, :key => 'rack.session',
                           :domain => 'foo.com',
                           :path => '/',
                           :expire_after => 2592000,
                           :secret => 'change_me'

Rack 미들웨어 개발

Ruby Rack문서 참고

참고