Permalink
Browse files

add korean readme, fixes #59

  • Loading branch information...
1 parent 6dafae0 commit 2250ea6096c4faa68449d94d357427f894799011 @rkh rkh committed Apr 2, 2012
Showing with 2,562 additions and 1 deletion.
  1. +1 −1 Rakefile
  2. +2,531 −0 _includes/README.ko.html
  3. +1 −0 intro-de.html
  4. +1 −0 intro-es.html
  5. +1 −0 intro-fr.html
  6. +1 −0 intro-hu.html
  7. +1 −0 intro-jp.html
  8. +20 −0 intro-ko.html
  9. +1 −0 intro-pt-br.html
  10. +1 −0 intro-pt-pt.html
  11. +1 −0 intro-ru.html
  12. +1 −0 intro-zh.html
  13. +1 −0 intro.html
View
2 Rakefile
@@ -5,7 +5,7 @@ require 'uri'
def readme(pattern = "%s", &block)
return readme(pattern).each(&block) if block_given?
- %w[en de es fr hu jp zh ru pt-br pt-pt].map do |lang|
+ %w[en de es fr hu jp zh ru ko pt-br pt-pt].map do |lang|
pattern % "README#{lang == "en" ? "" : ".#{lang}"}"
end
end
View
2,531 _includes/README.ko.html
@@ -0,0 +1,2531 @@
+<div class='toc'>
+ <ol class='level-1'>
+ <li><a href='#%EB%9D%BC%EC%9A%B0%ED%84%B0(Routes)'>라우터(Routes)</a></li>
+ <ol class='level-2'>
+ <li><a href='#%EC%A1%B0%EA%B1%B4(Conditions)'>조건(Conditions)</a></li>
+ <li><a href='#%EB%B0%98%ED%99%98%EA%B0%92(Return%20Values)'>반환값(Return Values)</a></li>
+ <li><a href='#%EC%BB%A4%EC%8A%A4%ED%85%80%20%EB%9D%BC%EC%9A%B0%ED%84%B0%20%EB%A7%A4%EC%B2%98(Custom%20Route%20Matchers)'>커스텀 라우터 매처(Custom Route Matchers)</a></li>
+ </ol>
+ <li><a href='#%EC%A0%95%EC%A0%81%20%ED%8C%8C%EC%9D%BC(Static%20Files)'>정적 파일(Static Files)</a></li>
+ <li><a href='#%EB%B7%B0%20/%20%ED%85%9C%ED%94%8C%EB%A6%BF(Views%20/%20Templates)'>뷰 / 템플릿(Views / Templates)</a></li>
+ <ol class='level-2'>
+ <li><a href='#%EA%B0%80%EB%8A%A5%ED%95%9C%20%ED%85%9C%ED%94%8C%EB%A6%BF%20%EC%96%B8%EC%96%B4%EB%93%A4(Available%20Template%20Languages)'>가능한 템플릿 언어들(Available Template Languages)</a></li>
+ <li><a href='#Haml%20%ED%85%9C%ED%94%8C%EB%A6%BF'>Haml 템플릿</a></li>
+ <li><a href='#Erb%20%ED%85%9C%ED%94%8C%EB%A6%BF'>Erb 템플릿</a></li>
+ <li><a href='#Builder%20%ED%85%9C%ED%94%8C%EB%A6%BF'>Builder 템플릿</a></li>
+ <li><a href='#Nokogiri%20%ED%85%9C%ED%94%8C%EB%A6%BF'>Nokogiri 템플릿</a></li>
+ <li><a href='#Sass%20%ED%85%9C%ED%94%8C%EB%A6%BF'>Sass 템플릿</a></li>
+ <li><a href='#SCSS%20%ED%85%9C%ED%94%8C%EB%A6%BF'>SCSS 템플릿</a></li>
+ <li><a href='#Less%20%ED%85%9C%ED%94%8C%EB%A6%BF'>Less 템플릿</a></li>
+ <li><a href='#Liquid%20%ED%85%9C%ED%94%8C%EB%A6%BF'>Liquid 템플릿</a></li>
+ <li><a href='#Markdown%20%ED%85%9C%ED%94%8C%EB%A6%BF'>Markdown 템플릿</a></li>
+ <li><a href='#Textile%20%ED%85%9C%ED%94%8C%EB%A6%BF'>Textile 템플릿</a></li>
+ <li><a href='#RDoc%20%ED%85%9C%ED%94%8C%EB%A6%BF'>RDoc 템플릿</a></li>
+ <li><a href='#Radius%20%ED%85%9C%ED%94%8C%EB%A6%BF'>Radius 템플릿</a></li>
+ <li><a href='#Markaby%20%ED%85%9C%ED%94%8C%EB%A6%BF'>Markaby 템플릿</a></li>
+ <li><a href='#Slim%20%ED%85%9C%ED%94%8C%EB%A6%BF'>Slim 템플릿</a></li>
+ <li><a href='#Creole%20%ED%85%9C%ED%94%8C%EB%A6%BF'>Creole 템플릿</a></li>
+ <li><a href='#CoffeeScript%20%ED%85%9C%ED%94%8C%EB%A6%BF'>CoffeeScript 템플릿</a></li>
+ <li><a href='#Yajl%20%ED%85%9C%ED%94%8C%EB%A6%BF'>Yajl 템플릿</a></li>
+ <li><a href='#%EB%82%B4%EC%9E%A5%EB%90%9C(Embedded)%20%ED%85%9C%ED%94%8C%EB%A6%BF'>내장된(Embedded) 템플릿</a></li>
+ <li><a href='#%ED%85%9C%ED%94%8C%EB%A6%BF%EC%97%90%EC%84%9C%20%EB%B3%80%EC%88%98%EC%97%90%20%EC%A0%91%EA%B7%BC%ED%95%98%EA%B8%B0'>템플릿에서 변수에 접근하기</a></li>
+ <li><a href='#%EC%9D%B8%EB%9D%BC%EC%9D%B8%20%ED%85%9C%ED%94%8C%EB%A6%BF'>인라인 템플릿</a></li>
+ <li><a href='#%EC%9D%B4%EB%A6%84%EC%9D%84%20%EA%B0%80%EC%A7%80%EB%8A%94%20%ED%85%9C%ED%94%8C%EB%A6%BF(Named%20Templates)'>이름을 가지는 템플릿(Named Templates)</a></li>
+ <li><a href='#%ED%8C%8C%EC%9D%BC%20%ED%99%95%EC%9E%A5%EC%9E%90%20%EC%97%B0%EA%B2%B0%ED%95%98%EA%B8%B0'>파일 확장자 연결하기</a></li>
+ <li><a href='#%EB%82%98%EB%A7%8C%EC%9D%98%20%EA%B3%A0%EC%9C%A0%ED%95%9C%20%ED%85%9C%ED%94%8C%EB%A6%BF%20%EC%97%94%EC%A7%84%20%EC%B6%94%EA%B0%80%ED%95%98%EA%B8%B0'>나만의 고유한 템플릿 엔진 추가하기</a></li>
+ </ol>
+ <li><a href='#%ED%95%84%ED%84%B0(Filters)'>필터(Filters)</a></li>
+ <li><a href='#%ED%97%AC%ED%8D%BC(Helpers)'>헬퍼(Helpers)</a></li>
+ <ol class='level-2'>
+ <li><a href='#%EC%84%B8%EC%85%98(Sessions)%20%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0'>세션(Sessions) 사용하기</a></li>
+ <li><a href='#%EC%A4%91%EB%8B%A8%ED%95%98%EA%B8%B0(Halting)'>중단하기(Halting)</a></li>
+ <li><a href='#%EB%84%98%EA%B8%B0%EA%B8%B0(Passing)'>넘기기(Passing)</a></li>
+ <li><a href='#%EB%8B%A4%EB%A5%B8%20%EB%9D%BC%EC%9A%B0%ED%84%B0%20%EB%B6%80%EB%A5%B4%EA%B8%B0(Triggering%20Another%20Route)'>다른 라우터 부르기(Triggering Another Route)</a></li>
+ <li><a href='#%EB%B3%B8%EB%AC%B8,%20%EC%83%81%ED%83%9C%20%EC%BD%94%EB%93%9C%20%EB%B0%8F%20%ED%97%A4%EB%8D%94%20%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0'>본문, 상태 코드 및 헤더 설정하기</a></li>
+ <li><a href='#%EC%9D%91%EB%8B%B5%20%EC%8A%A4%ED%8A%B8%EB%A6%AC%EB%B0%8D(Streaming%20Responses)'>응답 스트리밍(Streaming Responses)</a></li>
+ <li><a href='#%EB%A1%9C%EA%B9%85(Logging)'>로깅(Logging)</a></li>
+ <li><a href='#%EB%A7%88%EC%9E%84%20%ED%83%80%EC%9E%85(Mime%20Types)'>마임 타입(Mime Types)</a></li>
+ <li><a href='#URL%20%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0'>URL 생성하기</a></li>
+ <li><a href='#%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80%20%EC%9E%AC%EC%A7%80%EC%A0%95(Browser%20Redirect)'>브라우저 재지정(Browser Redirect)</a></li>
+ <li><a href='#%EC%BA%90%EC%8B%9C%20%EC%BB%A8%ED%8A%B8%EB%A1%A4(Cache%20Control)'>캐시 컨트롤(Cache Control)</a></li>
+ <li><a href='#%ED%8C%8C%EC%9D%BC%20%EC%A0%84%EC%86%A1%ED%95%98%EA%B8%B0(Sending%20Files)'>파일 전송하기(Sending Files)</a></li>
+ <li><a href='#%EC%9A%94%EC%B2%AD%20%EA%B0%9D%EC%B2%B4%EC%97%90%20%EC%A0%91%EA%B7%BC%ED%95%98%EA%B8%B0(Accessing%20the%20Request%20Object)'>요청 객체에 접근하기(Accessing the Request Object)</a></li>
+ <li><a href='#%EC%B2%A8%EB%B6%80(Attachments)'>첨부(Attachments)</a></li>
+ <li><a href='#%EB%82%A0%EC%A7%9C%EC%99%80%20%EC%8B%9C%EA%B0%84%20%EB%8B%A4%EB%A3%A8%EA%B8%B0'>날짜와 시간 다루기</a></li>
+ <li><a href='#%ED%85%9C%ED%94%8C%EB%A6%BF%20%ED%8C%8C%EC%9D%BC%20%EC%B0%B8%EC%A1%B0%ED%95%98%EA%B8%B0'>템플릿 파일 참조하기</a></li>
+ </ol>
+ <li><a href='#%EC%84%A4%EC%A0%95(Configuration)'>설정(Configuration)</a></li>
+ <ol class='level-2'>
+ <li><a href='#%EA%B3%B5%EA%B2%A9%20%EB%B0%A9%EC%96%B4%20%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0(Configuring%20attack%20protection)'>공격 방어 설정하기(Configuring attack protection)</a></li>
+ <li><a href='#%EA%B0%80%EB%8A%A5%ED%95%9C%20%EC%84%A4%EC%A0%95%EB%93%A4(Available%20Settings)'>가능한 설정들(Available Settings)</a></li>
+ </ol>
+ <li><a href='#%ED%99%98%EA%B2%BD(Environments)'>환경(Environments)</a></li>
+ <li><a href='#%EC%98%88%EC%99%B8%20%EC%B2%98%EB%A6%AC(Error%20Handling)'>예외 처리(Error Handling)</a></li>
+ <ol class='level-2'>
+ <li><a href='#%EC%B0%BE%EC%9D%84%20%EC%88%98%20%EC%97%86%EC%9D%8C(Not%20Found)'>찾을 수 없음(Not Found)</a></li>
+ <li><a href='#%EC%98%A4%EB%A5%98(Error)'>오류(Error)</a></li>
+ </ol>
+ <li><a href='#Rack%20%EB%AF%B8%EB%93%A4%EC%9B%A8%EC%96%B4(Rack%20Middleware)'>Rack 미들웨어(Rack Middleware)</a></li>
+ <li><a href='#%ED%85%8C%EC%8A%A4%ED%8C%85(Testing)'>테스팅(Testing)</a></li>
+ <li><a href='#Sinatra::Base%20-%20%EB%AF%B8%EB%93%A4%EC%9B%A8%EC%96%B4(Middleware),%20%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC(Libraries),%20%EA%B7%B8%EB%A6%AC%EA%B3%A0%20%EB%AA%A8%EB%93%88%20%EC%95%B1(Modular%20Apps)'>Sinatra::Base - 미들웨어(Middleware), 라이브러리(Libraries), 그리고 모듈 앱(Modular Apps)</a></li>
+ <ol class='level-2'>
+ <li><a href='#%EB%AA%A8%EB%93%88(Modular)%20vs.%20%EC%A0%84%ED%86%B5%EC%A0%81%20%EB%B0%A9%EC%8B%9D(Classic%20Style)'>모듈(Modular) vs. 전통적 방식(Classic Style)</a></li>
+ <li><a href='#%EB%AA%A8%EB%93%88%20%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98(Modular%20Application)%20%EC%A0%9C%EA%B3%B5%ED%95%98%EA%B8%B0'>모듈 애플리케이션(Modular Application) 제공하기</a></li>
+ <li><a href='#config.ru%EB%A1%9C%20%EC%A0%84%ED%86%B5%EC%A0%81%20%EB%B0%A9%EC%8B%9D%EC%9D%98%20%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98%20%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0'>config.ru로 전통적 방식의 애플리케이션 사용하기</a></li>
+ <li><a href='#%EC%96%B8%EC%A0%9C%20config.ru%EB%A5%BC%20%EC%82%AC%EC%9A%A9%ED%95%A0%EA%B9%8C?'>언제 config.ru를 사용할까?</a></li>
+ <li><a href='#Sinatra%EB%A5%BC%20%EB%AF%B8%EB%93%A4%EC%9B%A8%EC%96%B4%EB%A1%9C%20%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0'>Sinatra를 미들웨어로 사용하기</a></li>
+ <li><a href='#%EB%8F%99%EC%A0%81%EC%9D%B8%20%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98%20%EC%83%9D%EC%84%B1(Dynamic%20Application%20Creation)'>동적인 애플리케이션 생성(Dynamic Application Creation)</a></li>
+ </ol>
+ <li><a href='#%EB%B2%94%EC%9C%84(Scopes)%EC%99%80%20%EB%B0%94%EC%9D%B8%EB%94%A9(Binding)'>범위(Scopes)와 바인딩(Binding)</a></li>
+ <ol class='level-2'>
+ <li><a href='#%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98/%ED%81%B4%EB%9E%98%EC%8A%A4%20%EB%B2%94%EC%9C%84'>애플리케이션/클래스 범위</a></li>
+ <li><a href='#%EC%9A%94%EC%B2%AD/%EC%9D%B8%EC%8A%A4%ED%84%B4%EC%8A%A4%20%EB%B2%94%EC%9C%84'>요청/인스턴스 범위</a></li>
+ <li><a href='#%EC%9C%84%EC%9E%84%20%EB%B2%94%EC%9C%84(Delegation%20Scope)'>위임 범위(Delegation Scope)</a></li>
+ </ol>
+ <li><a href='#%EB%AA%85%EB%A0%B9%ED%96%89(Command%20Line)'>명령행(Command Line)</a></li>
+ <li><a href='#%EC%9A%94%EA%B5%AC%EC%82%AC%ED%95%AD(Requirement)'>요구사항(Requirement)</a></li>
+ <li><a href='#%EC%B5%9C%EC%8B%A0(The%20Bleeding%20Edge)'>최신(The Bleeding Edge)</a></li>
+ <ol class='level-2'>
+ <li><a href='#Bundler%EB%A5%BC%20%EC%82%AC%EC%9A%A9%ED%95%98%EC%97%AC'>Bundler를 사용하여</a></li>
+ <li><a href='#%EC%A7%81%EC%A0%91%20%ED%95%98%EA%B8%B0(Roll%20Your%20Own)'>직접 하기(Roll Your Own)</a></li>
+ <li><a href='#%EC%A0%84%EC%97%AD%EC%9C%BC%EB%A1%9C%20%EC%84%A4%EC%B9%98(Install%20Globally)'>전역으로 설치(Install Globally)</a></li>
+ </ol>
+ <li><a href='#%EB%B2%84%EC%A0%80%EB%8B%9D(Versioning)'>버저닝(Versioning)</a></li>
+ <li><a href='#%EB%8D%94%20%EC%9D%BD%EC%9D%84%20%EA%B1%B0%EB%A6%AC(Further%20Reading)'>더 읽을 거리(Further Reading)</a></li>
+ </ol>
+</div>
+
+
+
+<p>Sinatra는 최소한의 노력으로 루비 기반 웹
+애플리케이션을 신속하게 만들 수 있게 해 주는 DSL이다:</p>
+
+<pre># myapp.rb
+require 'sinatra'
+
+get '/' do
+ 'Hello world!'
+end</pre>
+
+<p>다음과 같이 젬을 설치하고 실행한다:</p>
+
+<pre>gem install sinatra
+ruby -rubygems myapp.rb</pre>
+
+<p>확인: <a href="http://localhost:4567">localhost:4567</a></p>
+
+<p><tt>gem install thin</tt>도 함께 실행하기를 권장하며, 그럴
+경우 Sinatra는 thin을 부른다.</p>
+
+<a name='%EB%9D%BC%EC%9A%B0%ED%84%B0(Routes)'></a>
+<h2>라우터(Routes)</h2>
+
+<p>Sinatra에서, 라우터(route)는 URL-매칭 패턴과 쌍을 이루는
+HTTP 메서드다. 각각의 라우터는 블록과 연결된다:</p>
+
+<pre>get '/' do
+ .. 무언가 보여주기(show) ..
+end
+
+post '/' do
+ .. 무언가 만들기(create) ..
+end
+
+put '/' do
+ .. 무언가 대체하기(replace) ..
+end
+
+patch '/' do
+ .. 무언가 수정하기(modify) ..
+end
+
+delete '/' do
+ .. 무언가 없애기(annihilate) ..
+end
+
+options '/' do
+ .. 무언가 주기(appease) ..
+end</pre>
+
+<p>라우터는 정의된 순서에 따라 매치되며 매칭된 첫 번째
+라우터가 호출된다.</p>
+
+<p>라우터 패턴에는 이름을 가진 매개변수가 포함될
+수있으며, <tt>params</tt> 해시로 접근할 수 있다:</p>
+
+<pre>get '/hello/:name' do
+ # &quot;GET /hello/foo&quot;&quot;GET /hello/bar&quot;와 매치
+ # params[:name]은 'foo' 또는 'bar'
+ &quot;Hello #{params[:name]}!&quot;
+end</pre>
+
+<p>또한 블록 매개변수를 통하여도 이름을 가진 매개변수에
+접근할 수 있다:</p>
+
+<pre>get '/hello/:name' do |n|
+ &quot;Hello #{n}!&quot;
+end</pre>
+
+<p>라우터 패턴에는 스플랫(splat, 또는 와일드카드)도
+포함될 수 있으며, 이럴 경우 <tt>params[:splat]</tt> 배열로
+접근할 수 있다:</p>
+
+<pre>get '/say/*/to/*' do
+ # /say/hello/to/world와 매치
+ params[:splat] # =&gt; [&quot;hello&quot;, &quot;world&quot;]
+end
+
+get '/download/*.*' do
+ # /download/path/to/file.xml과 매치
+ params[:splat] # =&gt; [&quot;path/to/file&quot;, &quot;xml&quot;]
+end</pre>
+
+<p>또는 블록 매개변수도 가능하다:</p>
+
+<pre>get '/download/*.*' do |path, ext|
+ [path, ext] # =&gt; [&quot;path/to/file&quot;, &quot;xml&quot;]
+end</pre>
+
+<p>정규표현식을 이용한 라우터 매칭:</p>
+
+<pre>get %r{/hello/([\w]+)} do
+ &quot;Hello, #{params[:captures].first}!&quot;
+end</pre>
+
+<p>또는 블록 매개변수로도 가능:</p>
+
+<pre>get %r{/hello/([\w]+)} do |c|
+ &quot;Hello, #{c}!&quot;
+end</pre>
+
+<p>라우터 패턴에는 선택적인(optional) 매개변수도 올 수
+있다:</p>
+
+<pre>get '/posts.?:format?' do
+ # &quot;GET /posts&quot;&quot;GET /posts.json&quot;, &quot;GET /posts.xml&quot; 와 같은 어떤 확장자와도 매칭
+end</pre>
+
+<p>한편, 경로 탐색 공격 방지(path traversal attack protection,
+아래 참조)를 비활성화시키지 않았다면, 요청 경로는
+라우터와 매칭되기 이전에 수정될 수 있다.</p>
+
+<a name='%EC%A1%B0%EA%B1%B4(Conditions)'></a>
+<h3>조건(Conditions)</h3>
+
+<p>라우터는 예를 들면 사용자 에이전트(user agent)와 같은
+다양한 매칭 조건을 포함할 수 있다:</p>
+
+<pre>get '/foo', :agent =&gt; /Songbird (\d\.\d)[\d\/]*?/ do
+ &quot;Songbird 버전 #{params[:agent][0]}을 사용하는군요!&quot;
+end
+
+get '/foo' do
+ # songbird 브라우저가 아닌 경우 매치
+end</pre>
+
+<p>그 밖에 다른 조건으로는 <tt>host_name</tt>과
+<tt>provides</tt>가 있다:</p>
+
+<pre>get '/', :host_name =&gt; /^admin\./ do
+ &quot;Admin Area, Access denied!&quot;
+end
+
+get '/', :provides =&gt; 'html' do
+ haml :index
+end
+
+get '/', :provides =&gt; ['rss', 'atom', 'xml'] do
+ builder :feed
+end</pre>
+
+<p>여러분만의 조건도 쉽게 정의할 수 있다:</p>
+
+<pre>set(:probability) { |value| condition { rand &lt;= value } }
+
+get '/win_a_car', :probability =&gt; 0.1 do
+ &quot;내가 졌소!&quot;
+end
+
+get '/win_a_car' do
+ &quot;미안해서 어쩌나.&quot;
+end</pre>
+
+<p>여러 값을 받는 조건에는 스플랫(splat)을 사용하자:</p>
+
+<pre>set(:auth) do |*roles| # &lt;- 이게 스플랫
+ condition do
+ unless logged_in? &amp;&amp; roles.any? {|role| current_user.in_role? role }
+ redirect &quot;/login/&quot;, 303
+ end
+ end
+end
+
+get &quot;/my/account/&quot;, :auth =&gt; [:user, :admin] do
+ &quot;내 계정 정보&quot;
+end
+
+get &quot;/only/admin/&quot;, :auth =&gt; :admin do
+ &quot;관리자 외 접근불가!&quot;
+end</pre>
+
+<a name='%EB%B0%98%ED%99%98%EA%B0%92(Return%20Values)'></a>
+<h3>반환값(Return Values)</h3>
+
+<p>라우터 블록의 반환값은 HTTP 클라이언트로 전달되는
+응답 본문을 결정하거나, 또는 Rack 스택에서 다음 번
+미들웨어를 결정한다. 대부분의 경우, 이 반환값은 위의
+예제에서 보듯 문자열이지만, 다른 값도 가능하다.</p>
+
+<p>유효한 Rack 응답, Rack 본문 객체 또는 HTTP 상태 코드가
+되는 어떠한 객체라도 반환할 수 있다:</p>
+<ul><li>
+<p>세 요소를 가진 배열: <tt>[상태 (Fixnum), 헤더 (Hash), 응답
+본문 (#each에 반응)]</tt></p>
+</li><li>
+<p>두 요소를 가진 배열: <tt>[상태 (Fixnum), 응답 본문 (#each에
+반응)]</tt></p>
+</li><li>
+<p><tt>#each</tt>에 반응하고 주어진 블록으로 문자열만을
+전달하는 객체</p>
+</li><li>
+<p>상태 코드를 의미하는 Fixnum</p>
+</li></ul>
+
+<p>이에 따라 우리는, 예를 들면, 스트리밍(streaming) 예제를
+쉽게 구현할 수 있다:</p>
+
+<pre>class Stream
+ def each
+ 100.times { |i| yield &quot;#{i}\n&quot; }
+ end
+end
+
+get('/') { Stream.new }</pre>
+
+<p>이런 번거로움을 줄이기 위해 <tt>stream</tt> 헬퍼
+메서드(아래 참조)를 사용하여 스트리밍 로직을 라우터
+속에 둘 수도 있다.</p>
+
+<a name='%EC%BB%A4%EC%8A%A4%ED%85%80%20%EB%9D%BC%EC%9A%B0%ED%84%B0%20%EB%A7%A4%EC%B2%98(Custom%20Route%20Matchers)'></a>
+<h3>커스텀 라우터 매처(Custom Route Matchers)</h3>
+
+<p>위에서 보듯, Sinatra에는 문자열 패턴 및 정규표현식을
+이용한 라우터 매칭 지원이 내장되어 있다. 그렇지만,
+그게 끝이 아니다. 여러분 만의 매처(matcher)도 쉽게
+정의할 수 있다:</p>
+
+<pre>class AllButPattern
+ Match = Struct.new(:captures)
+
+ def initialize(except)
+ @except = except
+ @captures = Match.new([])
+ end
+
+ def match(str)
+ @captures unless @except === str
+ end
+end
+
+def all_but(pattern)
+ AllButPattern.new(pattern)
+end
+
+get all_but(&quot;/index&quot;) do
+ # ...
+end</pre>
+
+<p>사실 위의 예제는 조금 과하게 작성된 면이 있다. 다음과
+같이 표현할 수도 있다:</p>
+
+<pre>get // do
+ pass if request.path_info == &quot;/index&quot;
+ # ...
+end</pre>
+
+<p>또는 네거티브 룩어헤드(negative look ahead)를 사용할 수도
+있다:</p>
+
+<pre>get %r{^(?!/index$)} do
+ # ...
+end</pre>
+
+<a name='%EC%A0%95%EC%A0%81%20%ED%8C%8C%EC%9D%BC(Static%20Files)'></a>
+<h2>정적 파일(Static Files)</h2>
+
+<p>정적 파일들은 <tt>./public</tt>에서 제공된다. 위치를 다른
+곳으로 변경하려면 <tt>:public_folder</tt> 옵션을 사용하면
+된다:</p>
+
+<pre>set :public_folder, File.dirname(__FILE__) + '/static'</pre>
+
+<p>이 때 public 디렉터리명은 URL에 포함되지 않는다는 점에
+유의. <tt>./public/css/style.css</tt> 파일은
+<tt>http://example.com/css/style.css</tt>로 접근할 수 있다.</p>
+
+<p><tt>Cache-Control</tt> 헤더 정보를 추가하려면
+<tt>:static_cache_control</tt> 설정(아래 참조)을 사용하면
+된다.</p>
+
+<a name='%EB%B7%B0%20/%20%ED%85%9C%ED%94%8C%EB%A6%BF(Views%20/%20Templates)'></a>
+<h2>뷰 / 템플릿(Views / Templates)</h2>
+
+<p>각 템플릿 언어는 그들만의 고유한 렌더링 메서드를
+통해 표출된다. 이들 메서드는 단순히 문자열을
+반환한다.</p>
+
+<pre>get '/' do
+ erb :index
+end</pre>
+
+<p>이 메서드는 <tt>views/index.erb</tt>를 렌더한다.</p>
+
+<p>템플릿 이름 대신 템플릿의 내용을 직접 전달할 수도
+있다:</p>
+
+<pre>get '/' do
+ code = &quot;&lt;%= Time.now %&gt;&quot;
+ erb code
+end</pre>
+
+<p>템플릿은 두 번째 인자로 옵션값의 해시를 받는다:</p>
+
+<pre>get '/' do
+ erb :index, :layout =&gt; :post
+end</pre>
+
+<p>이렇게 하면 <tt>views/post.erb</tt> 속에 내장된
+<tt>views/index.erb</tt>를 렌더한다. (기본값은
+<tt>views/layout.erb</tt>이며, 이 파일이 존재할 경우에만
+먹는다).</p>
+
+<p>Sinatra가 이해하지 못하는 모든 옵션값들은 템플릿
+엔진으로 전달될 것이다:</p>
+
+<pre>get '/' do
+ haml :index, :format =&gt; :html5
+end</pre>
+
+<p>옵션값은 템플릿 언어별로 일반적으로 설정할 수도
+있다:</p>
+
+<pre>set :haml, :format =&gt; :html5
+
+get '/' do
+ haml :index
+end</pre>
+
+<p>render 메서드에서 전달된 옵션값들은 <tt>set</tt>을 통해
+설정한 옵션값을 덮어 쓴다.</p>
+
+<p>가능한 옵션값들:</p>
+<dl class="rdoc-list"><dt>locals</dt>
+<dd>
+<p>문서로 전달되는 local 목록. 파셜과 함께 사용하기 좋음.
+예제: <tt>erb &quot;&lt;%= foo %&gt;&quot;, :locals =&gt; {:foo =&gt;
+&quot;bar&quot;}</tt></p>
+</dd><dt>default_encoding</dt>
+<dd>
+<p>불확실한 경우에 사용할 문자열 인코딩. 기본값은
+<tt>settings.default_encoding</tt>.</p>
+</dd><dt>views</dt>
+<dd>
+<p>템플릿을 로드할 뷰 폴더. 기본값은 <tt>settings.views</tt>.</p>
+</dd><dt>layout</dt>
+<dd>
+<p>레이아웃을 사용할지 여부 (<tt>true</tt> 또는 <tt>false</tt>),
+만약 이 값이 심볼일 경우, 사용할 템플릿을 지정. 예제:
+<tt>erb :index, :layout =&gt; !request.xhr?</tt></p>
+</dd><dt>content_type</dt>
+<dd>
+<p>템플릿이 생성하는 Content-Type, 기본값은 템플릿 언어에
+의존.</p>
+</dd><dt>scope</dt>
+<dd>
+<p>템플릿을 렌더링하는 범위. 기본값은 어플리케이션
+인스턴스. 만약 이 값을 변경하면, 인스턴스 변수와 헬퍼
+메서드들을 사용할 수 없게 됨.</p>
+</dd><dt>layout_engine</dt>
+<dd>
+<p>레이아웃 렌더링에 사용할 템플릿 엔진. 레이아웃을
+지원하지 않는 언어인 경우에 유용. 기본값은
+템플릿에서 사용하는 엔진. 예제: <tt>set :rdoc, :layout_engine
+=&gt; :erb</tt></p>
+</dd></dl>
+
+<p>템플릿은 <tt>./views</tt> 아래에 놓이는 것으로 가정됨.
+만약 뷰 디렉터리를 다른 곳으로 두려면:</p>
+
+<pre>set :views, settings.root + '/templates'</pre>
+
+<p>꼭 알아야 할 중요한 점 한 가지는 템플릿은 언제나
+심볼로 참조된다는 것이며, 템플릿이 하위 디렉터리에
+위치한 경우라도 마찬가지임(그럴 경우에는
+<tt>:'subdir/template'</tt>을 사용). 반드시 심볼이어야 하는
+이유는, 만약 그렇게 하지 않으면 렌더링 메서드가
+전달된 문자열을 직접 렌더하려 할 것이기 때문임.</p>
+
+<a name='%EA%B0%80%EB%8A%A5%ED%95%9C%20%ED%85%9C%ED%94%8C%EB%A6%BF%20%EC%96%B8%EC%96%B4%EB%93%A4(Available%20Template%20Languages)'></a>
+<h3>가능한 템플릿 언어들(Available Template Languages)</h3>
+
+<p>일부 언어는 여러 개의 구현이 있음. 어느 구현을
+사용할지 저정하려면(그리고 스레드-안전thread-safe
+모드로 하려면), 먼저 require 시키기만 하면 됨:</p>
+
+<pre>require 'rdiscount' # or require 'bluecloth'
+get('/') { markdown :index }</pre>
+
+<a name='Haml%20%ED%85%9C%ED%94%8C%EB%A6%BF'></a>
+<h3>Haml 템플릿</h3>
+<table class="rdoc-list"><tr><td class="rdoc-term"><p>의존</p></td>
+<td>
+<p><a href="http://haml-lang.com/">haml</a></p>
+</td></tr><tr><td class="rdoc-term"><p>파일 확장자</p></td>
+<td>
+<p><tt>.haml</tt></p>
+</td></tr><tr><td class="rdoc-term"><p>예</p></td>
+<td>
+<p><tt>haml :index, :format =&gt; :html5</tt></p>
+</td></tr></table>
+
+<a name='Erb%20%ED%85%9C%ED%94%8C%EB%A6%BF'></a>
+<h3>Erb 템플릿</h3>
+<table class="rdoc-list"><tr><td class="rdoc-term"><p>의존</p></td>
+<td>
+<p><a href="http://www.kuwata-lab.com/erubis/">erubis</a> 또는 erb (루비
+속에 포함)</p>
+</td></tr><tr><td class="rdoc-term"><p>파일 확장자</p></td>
+<td>
+<p><tt>.erb</tt>, <tt>.rhtml</tt> 또는 <tt>.erubis</tt> (Erubis만 해당)</p>
+</td></tr><tr><td class="rdoc-term"><p>예제</p></td>
+<td>
+<p><tt>erb :index</tt></p>
+</td></tr></table>
+
+<a name='Builder%20%ED%85%9C%ED%94%8C%EB%A6%BF'></a>
+<h3>Builder 템플릿</h3>
+<table class="rdoc-list"><tr><td class="rdoc-term"><p>의존</p></td>
+<td>
+<p><a href="http://builder.rubyforge.org/">builder</a></p>
+</td></tr><tr><td class="rdoc-term"><p>파일 확장자</p></td>
+<td>
+<p><tt>.builder</tt></p>
+</td></tr><tr><td class="rdoc-term"><p>Example</p></td>
+<td>
+<p><tt>builder { |xml| xml.em &quot;hi&quot; }</tt></p>
+</td></tr></table>
+
+<p>인라인 템플릿으로 블록을 받음(예제 참조).</p>
+
+<a name='Nokogiri%20%ED%85%9C%ED%94%8C%EB%A6%BF'></a>
+<h3>Nokogiri 템플릿</h3>
+<table class="rdoc-list"><tr><td class="rdoc-term"><p>의존</p></td>
+<td>
+<p><a href="http://nokogiri.org/">nokogiri</a></p>
+</td></tr><tr><td class="rdoc-term"><p>파일 확장자</p></td>
+<td>
+<p><tt>.nokogiri</tt></p>
+</td></tr><tr><td class="rdoc-term"><p>예제</p></td>
+<td>
+<p><tt>nokogiri { |xml| xml.em &quot;hi&quot; }</tt></p>
+</td></tr></table>
+
+<p>인라인 템플릿으로 블록을 받음(예제 참조).</p>
+
+<a name='Sass%20%ED%85%9C%ED%94%8C%EB%A6%BF'></a>
+<h3>Sass 템플릿</h3>
+<table class="rdoc-list"><tr><td class="rdoc-term"><p>의존</p></td>
+<td>
+<p><a href="http://sass-lang.com/">sass</a></p>
+</td></tr><tr><td class="rdoc-term"><p>파일 확장자</p></td>
+<td>
+<p><tt>.sass</tt></p>
+</td></tr><tr><td class="rdoc-term"><p>예제</p></td>
+<td>
+<p><tt>sass :stylesheet, :style =&gt; :expanded</tt></p>
+</td></tr></table>
+
+<a name='SCSS%20%ED%85%9C%ED%94%8C%EB%A6%BF'></a>
+<h3>SCSS 템플릿</h3>
+<table class="rdoc-list"><tr><td class="rdoc-term"><p>의존</p></td>
+<td>
+<p><a href="http://sass-lang.com/">sass</a></p>
+</td></tr><tr><td class="rdoc-term"><p>파일 확장자</p></td>
+<td>
+<p><tt>.scss</tt></p>
+</td></tr><tr><td class="rdoc-term"><p>예제</p></td>
+<td>
+<p><tt>scss :stylesheet, :style =&gt; :expanded</tt></p>
+</td></tr></table>
+
+<a name='Less%20%ED%85%9C%ED%94%8C%EB%A6%BF'></a>
+<h3>Less 템플릿</h3>
+<table class="rdoc-list"><tr><td class="rdoc-term"><p>의존</p></td>
+<td>
+<p><a href="http://www.lesscss.org/">less</a></p>
+</td></tr><tr><td class="rdoc-term"><p>파일 확장자</p></td>
+<td>
+<p><tt>.less</tt></p>
+</td></tr><tr><td class="rdoc-term"><p>예제</p></td>
+<td>
+<p><tt>less :stylesheet</tt></p>
+</td></tr></table>
+
+<a name='Liquid%20%ED%85%9C%ED%94%8C%EB%A6%BF'></a>
+<h3>Liquid 템플릿</h3>
+<table class="rdoc-list"><tr><td class="rdoc-term"><p>의존</p></td>
+<td>
+<p><a href="http://www.liquidmarkup.org/">liquid</a></p>
+</td></tr><tr><td class="rdoc-term"><p>파일 확장자</p></td>
+<td>
+<p><tt>.liquid</tt></p>
+</td></tr><tr><td class="rdoc-term"><p>예제</p></td>
+<td>
+<p><tt>liquid :index, :locals =&gt; { :key =&gt; 'value' }</tt></p>
+</td></tr></table>
+
+<p>Liquid 템플릿에서는 루비 메서드(<tt>yield</tt> 제외)를
+호출할 수 없기 때문에, 거의 대부분의 경우 locals를
+전달해야 함.</p>
+
+<a name='Markdown%20%ED%85%9C%ED%94%8C%EB%A6%BF'></a>
+<h3>Markdown 템플릿</h3>
+<table class="rdoc-list"><tr><td class="rdoc-term"><p>의존</p></td>
+<td>
+<p><a href="https://github.com/rtomayko/rdiscount">rdiscount</a>,</p>
+</td></tr></table>
+
+<pre>{redcarpet}[https://github.com/tanoku/redcarpet],
+{bluecloth}[http://deveiate.org/projects/BlueCloth],
+{kramdown}[http://kramdown.rubyforge.org/] *또는*
+{maruku}[http://maruku.rubyforge.org/]</pre>
+<table class="rdoc-list"><tr><td class="rdoc-term"><p>파일 확장</p></td>
+<td>
+<p><tt>.markdown</tt>, <tt>.mkd</tt>, <tt>.md</tt></p>
+</td></tr><tr><td class="rdoc-term"><p>예제</p></td>
+<td>
+<p><tt>markdown :index, :layout_engine =&gt; :erb</tt></p>
+</td></tr></table>
+
+<p>마크다운에서는 메서드 호출 뿐 아니라 locals 전달도
+안됨. 따라서 일반적으로는 다른 렌더링 엔진과 함께
+사용하게 될 것임:</p>
+
+<pre>erb :overview, :locals =&gt; { :text =&gt; markdown(:introduction) }</pre>
+
+<p>또한 다른 템플릿 속에서 <tt>markdown</tt> 메서드를 호출할
+수도 있음:</p>
+
+<pre>%h1 안녕 Haml!
+%p= markdown(:greetings)</pre>
+
+<p>Markdown에서 루비를 호출할 수 없기 때문에, Markdown으로
+작성된 레이아웃은 사용할 수 없음. 단,
+<tt>:layout_engine</tt> 옵션으로 템플릿의 레이아웃은 다른
+렌더링 엔진을 사용하는 것은 가능.</p>
+
+<a name='Textile%20%ED%85%9C%ED%94%8C%EB%A6%BF'></a>
+<h3>Textile 템플릿</h3>
+<table class="rdoc-list"><tr><td class="rdoc-term"><p>의존</p></td>
+<td>
+<p><a href="http://redcloth.org/">RedCloth</a></p>
+</td></tr><tr><td class="rdoc-term"><p>파일 확장자</p></td>
+<td>
+<p><tt>.textile</tt></p>
+</td></tr><tr><td class="rdoc-term"><p>예제</p></td>
+<td>
+<p><tt>textile :index, :layout_engine =&gt; :erb</tt></p>
+</td></tr></table>
+
+<p>Textile에서 메서드를 호출하거나 locals를 전달하는 것은
+불가능함. 따라서 일반적으로 다른 렌더링 엔진과 함께
+사용하게 될 것임:</p>
+
+<pre>erb :overview, :locals =&gt; { :text =&gt; textile(:introduction) }</pre>
+
+<p>또한 다른 템플릿 속에서 <tt>textile</tt> 메서드를 호출할
+수도 있음:</p>
+
+<pre>%h1 안녕 Haml!
+%p= textile(:greetings)</pre>
+
+<p>Textile에서 루비를 호출할 수 없기 때문에, Textile로
+작성된 레이아웃은 사용할 수 없음. 단,
+<tt>:layout_engine</tt> 옵션으로 템플릿의 레이아웃은 다른
+렌더링 엔진을 사용하는 것은 가능.</p>
+
+<a name='RDoc%20%ED%85%9C%ED%94%8C%EB%A6%BF'></a>
+<h3>RDoc 템플릿</h3>
+<table class="rdoc-list"><tr><td class="rdoc-term"><p>의존</p></td>
+<td>
+<p><a href="http://rdoc.rubyforge.org/">rdoc</a></p>
+</td></tr><tr><td class="rdoc-term"><p>파일 확장자</p></td>
+<td>
+<p><tt>.rdoc</tt></p>
+</td></tr><tr><td class="rdoc-term"><p>예제</p></td>
+<td>
+<p><tt>rdoc :README, :layout_engine =&gt; :erb</tt></p>
+</td></tr></table>
+
+<p>rdoc에서 메서드를 호출하거나 locals를 전달하는 것은
+불가능함. 따라서 일반적으로 다른 렌더링 엔진과 함께
+사용하게 될 것임:</p>
+
+<pre>erb :overview, :locals =&gt; { :text =&gt; rdoc(:introduction) }</pre>
+
+<p>또한 다른 템플릿 속에서 <tt>rdoc</tt> 메서드를 호출할
+수도 있음:</p>
+
+<pre>%h1 Hello From Haml!
+%p= rdoc(:greetings)</pre>
+
+<p>RDoc에서 루비를 호출할 수 없기 때문에, RDoc로 작성된
+레이아웃은 사용할 수 없음. 단, <tt>:layout_engine</tt>
+옵션으로 템플릿의 레이아웃은 다른 렌더링 엔진을
+사용하는 것은 가능.</p>
+
+<a name='Radius%20%ED%85%9C%ED%94%8C%EB%A6%BF'></a>
+<h3>Radius 템플릿</h3>
+<table class="rdoc-list"><tr><td class="rdoc-term"><p>의존</p></td>
+<td>
+<p><a href="http://radius.rubyforge.org/">radius</a></p>
+</td></tr><tr><td class="rdoc-term"><p>파일 확장자</p></td>
+<td>
+<p><tt>.radius</tt></p>
+</td></tr><tr><td class="rdoc-term"><p>예제</p></td>
+<td>
+<p><tt>radius :index, :locals =&gt; { :key =&gt; 'value' }</tt></p>
+</td></tr></table>
+
+<p>Radius 템플릿에서는 루비 메서드를 호출할 수 없기
+때문에, 거의 대부분의 경우 locals로 전달하게 될 것임.</p>
+
+<a name='Markaby%20%ED%85%9C%ED%94%8C%EB%A6%BF'></a>
+<h3>Markaby 템플릿</h3>
+<table class="rdoc-list"><tr><td class="rdoc-term"><p>의존</p></td>
+<td>
+<p><a href="http://markaby.github.com/">markaby</a></p>
+</td></tr><tr><td class="rdoc-term"><p>파일확장</p></td>
+<td>
+<p><tt>.mab</tt></p>
+</td></tr><tr><td class="rdoc-term"><p>예제</p></td>
+<td>
+<p><tt>markaby { h1 &quot;Welcome!&quot; }</tt></p>
+</td></tr></table>
+
+<p>인라인 템플릿으로 블록을 받을 수도 있음(예제 참조).</p>
+
+<a name='Slim%20%ED%85%9C%ED%94%8C%EB%A6%BF'></a>
+<h3>Slim 템플릿</h3>
+<table class="rdoc-list"><tr><td class="rdoc-term"><p>의존</p></td>
+<td>
+<p><a href="http://slim-lang.com/">slim</a></p>
+</td></tr><tr><td class="rdoc-term"><p>파일 확장자</p></td>
+<td>
+<p><tt>.slim</tt></p>
+</td></tr><tr><td class="rdoc-term"><p>예제</p></td>
+<td>
+<p><tt>slim :index</tt></p>
+</td></tr></table>
+
+<a name='Creole%20%ED%85%9C%ED%94%8C%EB%A6%BF'></a>
+<h3>Creole 템플릿</h3>
+<table class="rdoc-list"><tr><td class="rdoc-term"><p>의존</p></td>
+<td>
+<p><a href="https://github.com/minad/creole">creole</a></p>
+</td></tr><tr><td class="rdoc-term"><p>파일 확장자</p></td>
+<td>
+<p><tt>.creole</tt></p>
+</td></tr><tr><td class="rdoc-term"><p>예제</p></td>
+<td>
+<p><tt>creole :wiki, :layout_engine =&gt; :erb</tt></p>
+</td></tr></table>
+
+<p>creole에서는 루비 메서드를 호출할 수 없고 locals도
+전달할 수 없음. 따라서 일반적으로는 다른 렌더링
+엔진과 함께 사용하게 될 것임.</p>
+
+<pre>erb :overview, :locals =&gt; { :text =&gt; creole(:introduction) }</pre>
+
+<p>또한 다른 템플릿 속에서 <tt>creole</tt> 메서드를 호출할
+수도 있음:</p>
+
+<pre>%h1 Hello From Haml!
+%p= creole(:greetings)</pre>
+
+<p>Creole에서 루비를 호출할 수 없기 때문에, Creole로 작성된
+레이아웃은 사용할 수 없음. 단, <tt>:layout_engine</tt>
+옵션으로 템플릿의 레이아웃은 다른 렌더링 엔진을
+사용하는 것은 가능.</p>
+
+<a name='CoffeeScript%20%ED%85%9C%ED%94%8C%EB%A6%BF'></a>
+<h3>CoffeeScript 템플릿</h3>
+<table class="rdoc-list"><tr><td class="rdoc-term"><p>의존성</p></td>
+<td>
+<p><a href="https://github.com/josh/ruby-coffee-script">coffee-script</a></p>
+
+<pre>와 {자바스크립트 실행법}[https://github.com/sstephenson/execjs/blob/master/README.md#readme]</pre>
+</td></tr><tr><td class="rdoc-term"><p>파일 확장자</p></td>
+<td>
+<p><tt>.coffee</tt></p>
+</td></tr><tr><td class="rdoc-term"><p>예제</p></td>
+<td>
+<p><tt>coffee :index</tt></p>
+</td></tr></table>
+
+<a name='Yajl%20%ED%85%9C%ED%94%8C%EB%A6%BF'></a>
+<h3>Yajl 템플릿</h3>
+<table class="rdoc-list"><tr><td class="rdoc-term"><p>의존</p></td>
+<td>
+<p><a href="https://github.com/brianmario/yajl-ruby">yajl-ruby</a></p>
+</td></tr><tr><td class="rdoc-term"><p>파일 확장자</p></td>
+<td>
+<p><tt>.yajl</tt></p>
+</td></tr><tr><td class="rdoc-term"><p>예제</p></td>
+<td>
+<p><tt>yajl :index, :locals =&gt; { :key =&gt; 'qux' }, :callback =&gt;
+'present', :variable =&gt; 'resource' </tt></p>
+</td></tr></table>
+
+<p>The template source is evaluated as a Ruby string, and the resulting json
+variable is converted #to_json. 템플릿 소스는 루비 문자열로
+평가(evaluate)되고, 결과인 json 변수는 #to_json으로 변환됨.</p>
+
+<pre>json = { :foo =&gt; 'bar' }
+json[:baz] = key</pre>
+
+<p><tt>:callback</tt>과 <tt>:variable</tt> 옵션은 렌더된 객체를
+꾸미는데(decorate) 사용할 수 있음.</p>
+
+<pre>var resource = {&quot;foo&quot;:&quot;bar&quot;,&quot;baz&quot;:&quot;qux&quot;}; present(resource);</pre>
+
+<a name='%EB%82%B4%EC%9E%A5%EB%90%9C(Embedded)%20%ED%85%9C%ED%94%8C%EB%A6%BF'></a>
+<h3>내장된(Embedded) 템플릿</h3>
+
+<pre>get '/' do
+ haml '%div.title Hello World'
+end</pre>
+
+<p>내장된 템플릿 문자열을 렌더함.</p>
+
+<a name='%ED%85%9C%ED%94%8C%EB%A6%BF%EC%97%90%EC%84%9C%20%EB%B3%80%EC%88%98%EC%97%90%20%EC%A0%91%EA%B7%BC%ED%95%98%EA%B8%B0'></a>
+<h3>템플릿에서 변수에 접근하기</h3>
+
+<p>Templates are evaluated within the same context as route handlers. Instance
+variables set in route handlers are directly accessible by templates:
+템플릿은 라우터 핸들러와 같은 맥락(context)에서
+평가된다. 라우터 핸들러에서 설정한 인스턴스 변수들은
+템플릿에서 접근 가능하다:</p>
+
+<pre>get '/:id' do
+ @foo = Foo.find(params[:id])
+ haml '%h1= @foo.name'
+end</pre>
+
+<p>또는, 명시적으로 로컬 변수의 해시를 지정:</p>
+
+<pre>get '/:id' do
+ foo = Foo.find(params[:id])
+ haml '%h1= bar.name', :locals =&gt; { :bar =&gt; foo }
+end</pre>
+
+<p>This is typically used when rendering templates as partials from within
+other templates. 이 방법은 통상적으로 템플릿을 다른
+템플릿 속에서 파셜(partial)로 렌더링할 때 사용된다.</p>
+
+<a name='%EC%9D%B8%EB%9D%BC%EC%9D%B8%20%ED%85%9C%ED%94%8C%EB%A6%BF'></a>
+<h3>인라인 템플릿</h3>
+
+<p>템플릿은 소스 파일의 마지막에서 정의할 수도 있다:</p>
+
+<pre>require 'sinatra'
+
+get '/' do
+ haml :index
+end
+
+__END__
+
+@@ layout
+%html
+ = yield
+
+@@ index
+%div.title Hello world.</pre>
+
+<p>참고: require sinatra 시킨 소스 파일에 정의된 인라인
+템플릿은 자동으로 로드된다. 다른 소스 파일에서
+인라인 템플릿을 사용하려면 명시적으로 <tt>enable
+:inline_templates</tt>을 호출하면 됨.</p>
+
+<a name='%EC%9D%B4%EB%A6%84%EC%9D%84%20%EA%B0%80%EC%A7%80%EB%8A%94%20%ED%85%9C%ED%94%8C%EB%A6%BF(Named%20Templates)'></a>
+<h3>이름을 가지는 템플릿(Named Templates)</h3>
+
+<p>템플릿은 톱 레벨(top-level)에서 <tt>template</tt>메서드를
+사용하여 정의할 수 있다:</p>
+
+<pre>template :layout do
+ &quot;%html\n =yield\n&quot;
+end
+
+template :index do
+ '%div.title Hello World!'
+end
+
+get '/' do
+ haml :index
+end</pre>
+
+<p>"layout"이라는 이름의 템플릿이 존재하면, 매번 템플릿이
+렌더될 때마다 사용될 것이다. 이 때 <tt>:layout =&gt;
+false</tt>를 전달하여 개별적으로 레이아웃을
+비활성시키거나 또는 <tt>set :haml, :layout =&gt; false</tt>으로
+기본값을 비활성으로 둘 수 있다:</p>
+
+<pre>get '/' do
+ haml :index, :layout =&gt; !request.xhr?
+end</pre>
+
+<a name='%ED%8C%8C%EC%9D%BC%20%ED%99%95%EC%9E%A5%EC%9E%90%20%EC%97%B0%EA%B2%B0%ED%95%98%EA%B8%B0'></a>
+<h3>파일 확장자 연결하기</h3>
+
+<p>어떤 파일 확장자를 특정 템플릿 엔진과 연결하려면,
+<tt>Tilt.register</tt>를 사용하면 된다. 예를 들어,
+<tt>tt</tt>라는 파일 확장자를 Textile 템플릿과 연결하고
+싶다면, 다음과 같이 하면 된다:</p>
+
+<pre>Tilt.register :tt, Tilt[:textile]</pre>
+
+<a name='%EB%82%98%EB%A7%8C%EC%9D%98%20%EA%B3%A0%EC%9C%A0%ED%95%9C%20%ED%85%9C%ED%94%8C%EB%A6%BF%20%EC%97%94%EC%A7%84%20%EC%B6%94%EA%B0%80%ED%95%98%EA%B8%B0'></a>
+<h3>나만의 고유한 템플릿 엔진 추가하기</h3>
+
+<p>우선, Tilt로 여러분 엔진을 등록하고, 그런 다음 렌더링
+메서드를 생성하자:</p>
+
+<pre>Tilt.register :myat, MyAwesomeTemplateEngine
+
+helpers do
+ def myat(*args) render(:myat, *args) end
+end
+
+get '/' do
+ myat :index
+end</pre>
+
+<p><tt>./views/index.myat</tt>를 렌더함. Tilt에 대한 더 자세한
+내용은 <a
+href="https://github.com/rtomayko/tilt">github.com/rtomayko/tilt</a>
+참조.</p>
+
+<a name='%ED%95%84%ED%84%B0(Filters)'></a>
+<h2>필터(Filters)</h2>
+
+<p>사전 필터(before filter)는 라우터와 동일한 맥락에서 매
+요청 전에 평가되며 요청과 응답을 변형할 수 있다.
+필터에서 설정된 인스턴스 변수들은 라우터와 템플릿
+속에서 접근 가능하다:</p>
+
+<pre>before do
+ @note = 'Hi!'
+ request.path_info = '/foo/bar/baz'
+end
+
+get '/foo/*' do
+ @note #=&gt; 'Hi!'
+ params[:splat] #=&gt; 'bar/baz'
+end</pre>
+
+<p>사후 필터(after filter)는 라우터와 동일한 맥락에서 매
+요청 이후에 평가되며 마찬가지로 요청과 응답을 변형할
+수 있다. 사전 필터와 라우터에서 설정된 인스턴스
+변수들은 사후 필터에서 접근 가능하다:</p>
+
+<pre>after do
+ puts response.status
+end</pre>
+
+<p>참고: 만약 라우터에서 <tt>body</tt> 메서드를 사용하지
+않고 그냥 문자열만 반환한 경우라면, body는 나중에
+생성되는 탓에, 아직 사후 필터에서 사용할 수 없을
+것이다.</p>
+
+<p>필터는 선택적으로 패턴을 취할 수 있으며, 이 경우 요청
+경로가 그 패턴과 매치할 경우에만 필터가 평가될
+것이다.</p>
+
+<pre>before '/protected/*' do
+ authenticate!
+end
+
+after '/create/:slug' do |slug|
+ session[:last_slug] = slug
+end</pre>
+
+<p>라우터와 마찬가지로, 필터 역시 조건을 갖는다:</p>
+
+<pre>before :agent =&gt; /Songbird/ do
+ # ...
+end
+
+after '/blog/*', :host_name =&gt; 'example.com' do
+ # ...
+end</pre>
+
+<a name='%ED%97%AC%ED%8D%BC(Helpers)'></a>
+<h2>헬퍼(Helpers)</h2>
+
+<p>톱-레벨의 <tt>helpers</tt> 메서드를 사용하여 라우터
+핸들러와 템플릿에서 사용할 헬퍼 메서드들을 정의할 수
+있다:</p>
+
+<pre>helpers do
+ def bar(name)
+ &quot;#{name}bar&quot;
+ end
+end
+
+get '/:name' do
+ bar(params[:name])
+end</pre>
+
+<p>또는, 헬퍼 메서드는 별도의 모듈 속에 정의할 수도
+있다:</p>
+
+<pre>module FooUtils
+ def foo(name) &quot;#{name}foo&quot; end
+end
+
+module BarUtils
+ def bar(name) &quot;#{name}bar&quot; end
+end
+
+helpers FooUtils, BarUtils</pre>
+
+<p>이 경우 모듈을 애플리케이션 클래스에 포함(include)시킨
+것과 동일한 효과를 갖는다.</p>
+
+<a name='%EC%84%B8%EC%85%98(Sessions)%20%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0'></a>
+<h3>세션(Sessions) 사용하기</h3>
+
+<p>세션은 요청 동안에 상태를 유지하기 위해 사용한다.
+세션이 활성화되면, 사용자 세션 당 session 해시 하나씩을
+갖게 된다:</p>
+
+<pre>enable :sessions
+
+get '/' do
+ &quot;value = &quot; &lt;&lt; session[:value].inspect
+end
+
+get '/:value' do
+ session[:value] = params[:value]
+end</pre>
+
+<p><tt>enable :sessions</tt>은 실은 모든 데이터를 쿠키 속에
+저장함에 유의하자. 항상 이렇게 하고 싶지 않을 수도
+있을 것이다(예를 들어, 많은 양의 데이터를 저장하게
+되면 트래픽이 높아진다). 이 때는 여러 가지 랙 세션
+미들웨어(Rack session middleware)를 사용할 수 있을 것이다:
+이렇게 할 경우라면, <tt>enable :sessions</tt>을 호출하지
+*말고*, 대신 여러분이 선택한 미들웨어를 다른 모든
+미들웨어들처럼 포함시키면 된다:</p>
+
+<pre>use Rack::Session::Pool, :expire_after =&gt; 2592000
+
+get '/' do
+ &quot;value = &quot; &lt;&lt; session[:value].inspect
+end
+
+get '/:value' do
+ session[:value] = params[:value]
+end</pre>
+
+<p>보안을 위해서, 쿠키 속의 세션 데이터는 세션
+시크릿(secret)으로 사인(sign)된다. Sinatra는 여러분을 위해
+무작위 시크릿을 생성한다. 그렇지만, 이 시크릿은
+여러분 애플리케이션 시작 시마다 변경될 수 있기
+때문에, 여러분은 여러분 애플리케이션의 모든
+인스턴스들이 공유할 시크릿을 직접 만들고 싶을 수도
+있다:</p>
+
+<pre>set :session_secret, 'super secret'</pre>
+
+<p>조금 더 세부적인 설정이 필요하다면, <tt>sessions</tt>
+설정에서 옵션이 있는 해시를 저장할 수도 있을 것이다:</p>
+
+<pre>set :sessions, :domain =&gt; 'foo.com'</pre>
+
+<a name='%EC%A4%91%EB%8B%A8%ED%95%98%EA%B8%B0(Halting)'></a>
+<h3>중단하기(Halting)</h3>
+
+<p>필터나 라우터에서 요청을 즉각 중단하고 싶을 때
+사용하라:</p>
+
+<pre>halt</pre>
+
+<p>중단할 때 상태를 지정할 수도 있다:</p>
+
+<pre>halt 410</pre>
+
+<p>또는 본문을 넣을 수도 있다:</p>
+
+<pre>halt 'this will be the body'</pre>
+
+<p>또는 둘 다도 가능하다:</p>
+
+<pre>halt 401, 'go away!'</pre>
+
+<p>헤더를 추가할 경우에는 다음과 같이 하면 된다:</p>
+
+<pre>halt 402, {'Content-Type' =&gt; 'text/plain'}, 'revenge'</pre>
+
+<p>물론 <tt>halt</tt>를 템플릿과 결합하는 것도 가능하다:</p>
+
+<pre>halt erb(:error)</pre>
+
+<a name='%EB%84%98%EA%B8%B0%EA%B8%B0(Passing)'></a>
+<h3>넘기기(Passing)</h3>
+
+<p>라우터는 <tt>pass</tt>를 사용하여 다음 번 매칭되는
+라우터로 처리를 넘길 수 있다:</p>
+
+<pre>get '/guess/:who' do
+ pass unless params[:who] == 'Frank'
+ 'You got me!'
+end
+
+get '/guess/*' do
+ 'You missed!'
+end</pre>
+
+<p>이 떄 라우터 블록에서 즉각 빠져나오게 되고 제어는
+다음 번 매칭되는 라우터로 넘어간다. 만약 매칭되는
+라우터를 찾지 못하면, 404가 반환된다.</p>
+
+<a name='%EB%8B%A4%EB%A5%B8%20%EB%9D%BC%EC%9A%B0%ED%84%B0%20%EB%B6%80%EB%A5%B4%EA%B8%B0(Triggering%20Another%20Route)'></a>
+<h3>다른 라우터 부르기(Triggering Another Route)</h3>
+
+<p>경우에 따라서는 <tt>pass</tt>가 아니라, 다른 라우터를
+호출한 결과를 얻고 싶은 경우도 있을 것이다. 이 때는
+간단하게 +<tt>call</tt>+을 사용하면 된다:</p>
+
+<pre>get '/foo' do
+ status, headers, body = call env.merge(&quot;PATH_INFO&quot; =&gt; '/bar')
+ [status, headers, body.map(&amp;:upcase)]
+end
+
+get '/bar' do
+ &quot;bar&quot;
+end</pre>
+
+<p>위 예제의 경우, <tt>&quot;bar&quot;</tt>를 헬퍼로 옮겨
+<tt>/foo</tt>와 <tt>/bar</tt> 모두에서 사용하도록 함으로써
+테스팅을 쉽게 하고 성능을 높일 수 있을 것이다.</p>
+
+<p>만약 그 요청이 사본이 아닌 바로 그 동일 인스턴스로
+보내지도록 하고 싶다면, <tt>call</tt> 대신 <tt>call!</tt>을
+사용하면 된다.</p>
+
+<p><tt>call</tt>에 대한 더 자세한 내용은 Rack 명세를 참고하면
+된다.</p>
+
+<a name='%EB%B3%B8%EB%AC%B8,%20%EC%83%81%ED%83%9C%20%EC%BD%94%EB%93%9C%20%EB%B0%8F%20%ED%97%A4%EB%8D%94%20%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0'></a>
+<h3>본문, 상태 코드 및 헤더 설정하기</h3>
+
+<p>라우터 블록의 반환값과 함께 상태 코드(status code)와
+응답 본문(response body)을 설정하는 것은 가능하기도
+하거니와 권장되는 방법이다. 그렇지만, 경우에
+따라서는 본문을 실행 흐름 중의 임의 지점에서
+설정하고 싶을 수도 있다. 이 때는 <tt>body</tt> 헬퍼
+메서드를 사용하면 된다. 이렇게 하면, 그 순간부터
+본문에 접근할 때 그 메서드를 사용할 수가 있다:</p>
+
+<pre>get '/foo' do
+ body &quot;bar&quot;
+end
+
+after do
+ puts body
+end</pre>
+
+<p><tt>body</tt>로 블록을 전달하는 것도 가능하며, 이 블록은
+랙(Rack) 핸들러에 의해 실행될 것이다. (이 방법은
+스트리밍을 구현할 때 사용할 수 있는데, "값
+반환하기"를 참고).</p>
+
+<p>본문와 마찬가지로, 상태코드와 헤더도 설정할 수 있다:</p>
+
+<pre>get '/foo' do
+ status 418
+ headers \
+ &quot;Allow&quot; =&gt; &quot;BREW, POST, GET, PROPFIND, WHEN&quot;,
+ &quot;Refresh&quot; =&gt; &quot;Refresh: 20; http://www.ietf.org/rfc/rfc2324.txt&quot;
+ body &quot;I'm a tea pot!&quot;
+end</pre>
+
+<p><tt>body</tt>처럼, <tt>header</tt>와 <tt>status</tt>도 매개변수
+없이 사용하여 그것의 현재 값을 액세스하는 데 사용될
+수 있다.</p>
+
+<a name='%EC%9D%91%EB%8B%B5%20%EC%8A%A4%ED%8A%B8%EB%A6%AC%EB%B0%8D(Streaming%20Responses)'></a>
+<h3>응답 스트리밍(Streaming Responses)</h3>
+
+<p>응답 본문의 일정 부분을 계속 생성하는 가운데
+데이터를 내보내기 시작하고 싶을 경우도 있을 것이다.
+극단적인 예제로, 클라이언트가 접속을 끊기 전까지
+계속 데이터를 내보내고 싶을 수도 있다. 여러분만의
+래퍼(wrapper)를 만들기 싫다면 <tt>stream</tt> 헬퍼를
+사용하면 된다:</p>
+
+<pre>get '/' do
+ stream do |out|
+ out &lt;&lt; &quot;It's gonna be legen -\n&quot;
+ sleep 0.5
+ out &lt;&lt; &quot; (wait for it) \n&quot;
+ sleep 1
+ out &lt;&lt; &quot;- dary!\n&quot;
+ end
+end</pre>
+
+<p>이렇게 하면 스트리밍 API나 <a
+href="http://dev.w3.org/html5/eventsource/">서버 발송 이벤트Server
+Sent Events</a>를 구현할 수 있게 해 주며, <a
+href="http://en.wikipedia.org/wiki/WebSocket">WebSockets</a>을 위한
+기반으로 사용될 수 있다. 또한 이 방법은 일부 콘텐츠가
+느린 자원에 의존하는 경우에 스로풋(throughtput)을
+높이기 위해 사용될 수도 있다.</p>
+
+<p>스트리밍 동작, 특히 동시 요청의 수는 애플리케이션을
+서빙하는 웹서버에 크게 의존적이다. 어떤 서버, 예컨대
+WEBRick 같은 경우는 아예 스트리밍을 지원조차 하지 못할
+것이다. 만약 서버가 스트리밍을 지원하지 않는다면,
+본문은 <tt>stream</tt> 으로 전달된 블록이 수행을 마친
+후에 한꺼번에 반환될 것이다. 스트리밍은 Shotgun에서는
+작동하지 않는다.</p>
+
+<p>만약 선택적 매개변수 <tt>keep_open</tt>이 설정되어 있다면,
+스트림 객체에서 <tt>close</tt>를 호출하지 않을 것이고,
+따라서 여러분은 나중에 실행 흐름 상의 어느 시점에서
+스트림을 닫을 수 있다. 이 옵션은 Thin과 Rainbow 같은
+이벤트 기반 서버에서만 작동한다. 다른 서버들은
+여전히 스트림을 닫을 것이다:</p>
+
+<pre>set :server, :thin
+connections = []
+
+get '/' do
+ # 스트림을 열린 채 유지
+ stream(:keep_open) { |out| connections &lt;&lt; out }
+end
+
+post '/' do
+ # 모든 열린 스트림에 쓰기
+ connections.each { |out| out &lt;&lt; params[:message] &lt;&lt; &quot;\n&quot; }
+ &quot;message sent&quot;
+end</pre>
+
+<a name='%EB%A1%9C%EA%B9%85(Logging)'></a>
+<h3>로깅(Logging)</h3>
+
+<p>In the request scope, the <tt>logger</tt> helper exposes a <tt>Logger</tt>
+instance: 요청 스코프(request scope) 내에서, <tt>Logger</tt>의
+인스턴스인 <tt>logger</tt> 헬퍼를 사용할 수 있다:</p>
+
+<pre>get '/' do
+ logger.info &quot;loading data&quot;
+ # ...
+end</pre>
+
+<p>이 로거는 여러분이 Rack 핸들러에서 설정한 로그 셋팅을
+자동으로 참고한다. 만약 로깅이 비활성이라면, 이
+메서드는 더미(dummy) 객체를 반환할 것이며, 따라서
+여러분은 라우터나 필터에서 이 부분에 대해 걱정할
+필요는 없다.</p>
+
+<p>로깅은 <tt>Sinatra::Application</tt>에서만 기본으로
+활성화되어 있음에 유의하자. 만약
+<tt>Sinatra::Base</tt>로부터 상속받은 경우라면 직접
+활성화시켜 줘야 한다:</p>
+
+<pre>class MyApp &lt; Sinatra::Base
+ configure :production, :development do
+ enable :logging
+ end
+end</pre>
+
+<p>어떠한 로깅 미들웨어도 설정되지 않게 하려면,
+<tt>logging</tt> 설정을 <tt>nil</tt>로 두면 된다. 그렇지만,
+이럴 경우 <tt>logger</tt>는 <tt>nil</tt>을 반환할 것임에
+유의하자. 통상적인 유스케이스는 여러분만의 로거를
+사용하고자 할 경우일 것이다. Sinatra는
+<tt>env['rack.logger']</tt>에서 찾은 것을 사용할 것이다.</p>
+
+<a name='%EB%A7%88%EC%9E%84%20%ED%83%80%EC%9E%85(Mime%20Types)'></a>
+<h3>마임 타입(Mime Types)</h3>
+
+<p><tt>send_file</tt>이나 정적인 파일을 사용할 때에 Sinatra가
+인식하지 못하는 마임 타입이 있을 수 있다. 이 경우
+<tt>mime_type</tt>을 사용하여 파일 확장자를 등록하면 된다:</p>
+
+<pre>configure do
+ mime_type :foo, 'text/foo'
+end</pre>
+
+<p>또는 <tt>content_type</tt> 헬퍼와 함께 사용할 수도 있다:</p>
+
+<pre>get '/' do
+ content_type :foo
+ &quot;foo foo foo&quot;
+end</pre>
+
+<a name='URL%20%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0'></a>
+<h3>URL 생성하기</h3>
+
+<p>URL을 생성하려면 <tt>url</tt> 헬퍼 메서드를 사용해야
+한다. 예를 들어 Haml에서:</p>
+
+<pre>%a{:href =&gt; url('/foo')} foo</pre>
+
+<p>이것은 리버스 프록시(reverse proxies)와 Rack 라우터를, 만약
+존재한다면, 참고한다.</p>
+
+<p>This method is also aliased to <tt>to</tt> (see below for an example). 이
+메서드는 <tt>to</tt>라는 별칭으로도 사용할 수 있다 (아래
+예제 참조).</p>
+
+<a name='%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80%20%EC%9E%AC%EC%A7%80%EC%A0%95(Browser%20Redirect)'></a>
+<h3>브라우저 재지정(Browser Redirect)</h3>
+
+<p><tt>redirect</tt> 헬퍼 메서드를 사용하여 브라우저
+리다이렉트를 촉발시킬 수 있다:</p>
+
+<pre>get '/foo' do
+ redirect to('/bar')
+end</pre>
+
+<p>여타 부가적인 매개변수들은 <tt>halt</tt>에서 전달한
+인자들처럼 다루어 진다:</p>
+
+<pre>redirect to('/bar'), 303
+redirect 'http://google.com', 'wrong place, buddy'</pre>
+
+<p><tt>redirect back</tt>을 사용하면 사용자가 왔던 페이지로
+다시 돌아가는 리다이렉트도 쉽게 할 수 있다:</p>
+
+<pre>get '/foo' do
+ &quot;&lt;a href='/bar'&gt;do something&lt;/a&gt;&quot;
+end
+
+get '/bar' do
+ do_something
+ redirect back
+end</pre>
+
+<p>리다이렉트와 함께 인자를 전달하려면, 쿼리에
+붙이거나:</p>
+
+<pre>redirect to('/bar?sum=42')</pre>
+
+<p>또는 세션을 사용하면 된다:</p>
+
+<pre>enable :sessions
+
+get '/foo' do
+ session[:secret] = 'foo'
+ redirect to('/bar')
+end
+
+get '/bar' do
+ session[:secret]
+end</pre>
+
+<a name='%EC%BA%90%EC%8B%9C%20%EC%BB%A8%ED%8A%B8%EB%A1%A4(Cache%20Control)'></a>
+<h3>캐시 컨트롤(Cache Control)</h3>
+
+<p>헤더를 정확하게 설정하는 것은 적절한 HTTP 캐싱의
+기본이다.</p>
+
+<p>Cache-Control 헤더를 다음과 같이 간단하게 설정할 수 있다:</p>
+
+<pre>get '/' do
+ cache_control :public
+ &quot;cache it!&quot;
+end</pre>
+
+<p>프로 팁: 캐싱은 사전 필터에서 설정하라:</p>
+
+<pre>before do
+ cache_control :public, :must_revalidate, :max_age =&gt; 60
+end</pre>
+
+<p><tt>expires</tt> 헬퍼를 사용하여 그에 상응하는 헤더를
+설정한다면, <tt>Cache-Control</tt>이 자동으로 설정될
+것이다:</p>
+
+<pre>before do
+ expires 500, :public, :must_revalidate
+end</pre>
+
+<p>캐시를 잘 사용하려면, <tt>etag</tt> 또는
+<tt>last_modified</tt>의 사용을 고려해야 할 것이다. 무거운
+작업을 하기 *전*에 이들 헬퍼를 호출할 것을
+권장하는데, 이러면 만약 클라이언트 캐시에 현재
+버전이 이미 들어 있을 경우엔 즉각 응답을
+반환(flush)하게 될 것이다:</p>
+
+<pre>get '/article/:id' do
+ @article = Article.find params[:id]
+ last_modified @article.updated_at
+ etag @article.sha1
+ erb :article
+end</pre>
+
+<p><a
+href="http://en.wikipedia.org/wiki/HTTP_ETag#Strong_and_weak_validation">약한
+ETag</a>를 사용하는 것도 가능하다:</p>
+
+<pre>etag @article.sha1, :weak</pre>
+
+<p>이들 헬퍼는 어떠한 캐싱도 하지 않으며, 대신 필요한
+정보를 캐시에 제공한다. 여러분이 만약 손쉬운 리버스
+프록시(reverse-proxy) 캐싱 솔루션을 찾고 있다면, <a
+href="http://rtomayko.github.com/rack-cache/">rack-cache</a>를 써보라:</p>
+
+<pre>require &quot;rack/cache&quot;
+require &quot;sinatra&quot;
+
+use Rack::Cache
+
+get '/' do
+ cache_control :public, :max_age =&gt; 36000
+ sleep 5
+ &quot;hello&quot;
+end</pre>
+
+<p>정적 파일에 <tt>Cache-Control</tt> 헤더 정보를 추가하려면
+<tt>:static_cache_control</tt> 설정(아래 참조)을 사용하라:</p>
+
+<p>RFC 2616에 따르면 If-Match 또는 If-None-Match 헤더가 <tt>*</tt>로
+설정된 경우 요청한 리소스(resource)가 이미 존재하느냐
+여부에 따라 다르게 취급해야 한다고 되어 있다. Sinatra는
+(get 처럼) 안전하거나 (put 처럼) 멱등인 요청에 대한
+리소스는 이미 존재한다고 가정하며, 반면 다른
+리소스(예를 들면 post 요청 같은)의 경우는 새 리소스로
+취급한다. 이런 설정은 <tt>:new_resource</tt> 옵션으로
+전달하여 변경할 수 있다:</p>
+
+<pre>get '/create' do
+ etag '', :new_resource =&gt; true
+ Article.create
+ erb :new_article
+end</pre>
+
+<p>여전히 약한 ETag를 사용하고자 한다면, <tt>:kind</tt>으로
+전달하자:</p>
+
+<pre>etag '', :new_resource =&gt; true, :kind =&gt; :weak</pre>
+
+<a name='%ED%8C%8C%EC%9D%BC%20%EC%A0%84%EC%86%A1%ED%95%98%EA%B8%B0(Sending%20Files)'></a>
+<h3>파일 전송하기(Sending Files)</h3>
+
+<p>파일을 전송하려면, <tt>send_file</tt> 헬퍼 메서드를
+사용하면 된다:</p>
+
+<pre>get '/' do
+ send_file 'foo.png'
+end</pre>
+
+<p>이 메서드는 몇 가지 옵션을 받는다:</p>
+
+<pre>send_file 'foo.png', :type =&gt; :jpg</pre>
+
+<p>옵션들:</p>
+<dl class="rdoc-list"><dt>filename</dt>
+<dd>
+<p>응답에서의 파일명. 기본값은 실제 파일명이다.</p>
+</dd><dt>last_modified</dt>
+<dd>
+<p>Last-Modified 헤더값. 기본값은 파일의 mtime.</p>
+</dd><dt>type</dt>
+<dd>
+<p>사용할 컨텐츠 유형. 없으면 파일 확장자로부터
+유추된다.</p>
+</dd><dt>disposition</dt>
+<dd>
+<p>Content-Disposition에서 사용됨. 가능한 값들: <tt>nil</tt>
+(기본값), <tt>:attachment</tt> 및 <tt>:inline</tt></p>
+</dd><dt>length</dt>
+<dd>
+<p>Content-Length, 기본값은 파일 크기.</p>
+</dd><dt>status</dt>
+<dd>
+<p>전송할 상태 코드. 오류 페이지로 정적 파일을 전송할
+경우에 유용.</p>
+</dd></dl>
+
+<p>Rack 핸들러가 지원할 경우, Ruby 프로세스로부터의
+스트리밍이 아닌 다른 수단을 사용할 수 있다. 만약 이
+헬퍼 메서드를 사용하게 되면, Sinatra는 자동으로 범위
+요청(range request)을 처리할 것이다.</p>
+
+<a name='%EC%9A%94%EC%B2%AD%20%EA%B0%9D%EC%B2%B4%EC%97%90%20%EC%A0%91%EA%B7%BC%ED%95%98%EA%B8%B0(Accessing%20the%20Request%20Object)'></a>
+<h3>요청 객체에 접근하기(Accessing the Request Object)</h3>
+
+<p>인입되는 요청 객에는 요청 레벨(필터, 라우터, 오류
+핸들러)에서 <tt>request</tt> 메서드를 통해 접근 가능하다:</p>
+
+<p># <a href="http://example.com/example">example.com/example</a> 상에서
+실행 중인 앱</p>
+
+<pre>get '/foo' do
+ t = %w[text/css text/html application/javascript]
+ request.accept # ['text/html', '*/*']
+ request.accept? 'text/xml' # true
+ request.preferred_type(t) # 'text/html'
+ request.body # 클라이언트로부터 전송된 요청 본문 (아래 참조)
+ request.scheme # &quot;http&quot;
+ request.script_name # &quot;/example&quot;
+ request.path_info # &quot;/foo&quot;
+ request.port # 80
+ request.request_method # &quot;GET&quot;
+ request.query_string # &quot;&quot;
+ request.content_length # request.body의 길이
+ request.media_type # request.body의 미디어 유형
+ request.host # &quot;example.com&quot;
+ request.get? # true (다른 동사에 대해 유사한 메서드 있음)
+ request.form_data? # false
+ request[&quot;SOME_HEADER&quot;] # SOME_HEADER 헤더의 값
+ request.referrer # 클라이언트의 리퍼러 또는 '/'
+ request.user_agent # 사용자 에이전트 (:agent 조건에서 사용됨)
+ request.cookies # 브라우저 쿠키의 해시
+ request.xhr? # 이게 ajax 요청인가요?
+ request.url # &quot;http://example.com/example/foo&quot;
+ request.path # &quot;/example/foo&quot;
+ request.ip # 클라이언트 IP 주소
+ request.secure? # false (ssl 접속인 경우 true)
+ request.forwarded? # true (리버스 프록시 하에서 작동 중이라면)
+ request.env # Rack에 의해 처리되는 로우(raw) env 해시
+end</pre>
+
+<p>일부 옵션들, <tt>script_name</tt> 또는 <tt>path_info</tt>와 같은
+일부 옵션은 쓸 수도 있다:</p>
+
+<pre>before { request.path_info = &quot;/&quot; }
+
+get &quot;/&quot; do
+ &quot;all requests end up here&quot;
+end</pre>
+
+<p><tt>request.body</tt>는 IO 또는 StringIO 객체이다:</p>
+
+<pre>post &quot;/api&quot; do
+ request.body.rewind # 누군가 이미 읽은 경우
+ data = JSON.parse request.body.read
+ &quot;Hello #{data['name']}!&quot;
+end</pre>
+
+<a name='%EC%B2%A8%EB%B6%80(Attachments)'></a>
+<h3>첨부(Attachments)</h3>
+
+<p><tt>attachment</tt> 헬퍼를 사용하여 브라우저에게 응답이
+브라우저에 표시되는 게 아니라 디스크에 저장되어야
+함을 알릴 수 있다:</p>
+
+<pre>get '/' do
+ attachment
+ &quot;store it!&quot;
+end</pre>
+
+<p>이 때 파일명을 전달할 수도 있다:</p>
+
+<pre>get '/' do
+ attachment &quot;info.txt&quot;
+ &quot;store it!&quot;
+end</pre>
+
+<a name='%EB%82%A0%EC%A7%9C%EC%99%80%20%EC%8B%9C%EA%B0%84%20%EB%8B%A4%EB%A3%A8%EA%B8%B0'></a>
+<h3>날짜와 시간 다루기</h3>
+
+<p>Sinatra는 <tt>time_for_</tt> 헬퍼 메서드를 제공하는데, 이
+메서드는 주어진 값으로부터 Time 객체를 생성한다.
+<tt>DateTime</tt> 이나 <tt>Date</tt> 또는 유사한 클래스들도
+변환 가능하다:</p>
+
+<pre>get '/' do
+ pass if Time.now &gt; time_for('Dec 23, 2012')
+ &quot;still time&quot;
+end</pre>
+
+<p>이 메서드는 내부적으로 <tt>expires</tt> 나
+<tt>last_modified</tt> 같은 곳에서 사용된다. 따라서
+여러분은 애플리케이션에서 <tt>time_for</tt>를
+오버라이딩하여 이들 메서드의 동작을 쉽게 확장할 수
+있다:</p>
+
+<pre>helpers do
+ def time_for(value)
+ case value
+ when :yesterday then Time.now - 24*60*60
+ when :tomorrow then Time.now + 24*60*60
+ else super
+ end
+ end
+end
+
+get '/' do
+ last_modified :yesterday
+ expires :tomorrow
+ &quot;hello&quot;
+end</pre>
+
+<a name='%ED%85%9C%ED%94%8C%EB%A6%BF%20%ED%8C%8C%EC%9D%BC%20%EC%B0%B8%EC%A1%B0%ED%95%98%EA%B8%B0'></a>
+<h3>템플릿 파일 참조하기</h3>
+
+<p><tt>find_template</tt>는 렌더링할 템플릿 파일을 찾는데
+사용된다:</p>
+
+<pre>find_template settings.views, 'foo', Tilt[:haml] do |file|
+ puts &quot;could be #{file}&quot;
+end</pre>
+
+<p>This is not really useful. But it is useful that you can actually override
+this method to hook in your own lookup mechanism. For instance, if you want
+to be able to use more than one view directory: 이건 별로 유용하지
+않다. 그렇지만 이 메서드를 오버라이드하여 여러분만의
+참조 메커니즘에서 가로채는 것은 유용하다. 예를 들어,
+하나 이상의 뷰 디렉터리를 사용하고자 한다면:</p>
+
+<pre>set :views, ['views', 'templates']
+
+helpers do
+ def find_template(views, name, engine, &amp;block)
+ Array(views).each { |v| super(v, name, engine, &amp;block) }
+ end
+end</pre>
+
+<p>또다른 예제는 각각의 엔진마다 다른 디렉터리를 사용할
+경우다:</p>
+
+<pre>set :views, :sass =&gt; 'views/sass', :haml =&gt; 'templates', :default =&gt; 'views'
+
+helpers do
+ def find_template(views, name, engine, &amp;block)
+ _, folder = views.detect { |k,v| engine == Tilt[k] }
+ folder ||= views[:default]
+ super(folder, name, engine, &amp;block)
+ end
+end</pre>
+
+<p>여러분은 이것을 간단하게 확장(extension)으로 만들어
+다른 사람들과 공유할 수 있다!</p>
+
+<p><tt>find_template</tt>은 그 파일이 실제 존재하는지 검사하지
+않음에 유의하자. 대신 모든 가능한 경로에 대해 주어진
+블록을 호출할 뿐이다. 이것은 성능 문제는 아닌 것이,
+<tt>render</tt>는 파일이 발견되는 즉시 <tt>break</tt>를
+사용할 것이기 때문이다. 또한, 템플릿 위치(그리고
+콘텐츠)는 개발 모드에서 실행 중이 아니라면 캐시될
+것이다. 정말로 멋진 메세드를 작성하고 싶다면 이 점을
+명심하자.</p>
+
+<a name='%EC%84%A4%EC%A0%95(Configuration)'></a>
+<h2>설정(Configuration)</h2>
+
+<p>모든 환경에서, 시작될 때, 한번만 실행:</p>
+
+<pre>configure do
+ # 옵션 하나 설정
+ set :option, 'value'
+
+ # 여러 옵션 설정
+ set :a =&gt; 1, :b =&gt; 2
+
+ # `set :option, true`와 동일
+ enable :option
+
+ # `set :option, false`와 동일
+ disable :option
+
+ # 블록으로 동적인 설정을 할 수도 있음
+ set(:css_dir) { File.join(views, 'css') }
+end</pre>
+
+<p>환경(RACK_ENV 환경 변수)이 <tt>:production</tt>일 때만 실행:</p>
+
+<pre>configure :production do
+ ...
+end</pre>
+
+<p>환경이 <tt>:production</tt> 또는 <tt>:test</tt>일 때 실행:</p>
+
+<pre>configure :production, :test do
+ ...
+end</pre>
+
+<p>이들 옵션은 <tt>settings</tt>를 통해 접근 가능하다:</p>
+
+<pre>configure do
+ set :foo, 'bar'
+end
+
+get '/' do
+ settings.foo? # =&gt; true
+ settings.foo # =&gt; 'bar'
+ ...
+end</pre>
+
+<a name='%EA%B3%B5%EA%B2%A9%20%EB%B0%A9%EC%96%B4%20%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0(Configuring%20attack%20protection)'></a>
+<h3>공격 방어 설정하기(Configuring attack protection)</h3>
+
+<p>Sinatra는 <a
+href="https://github.com/rkh/rack-protection#readme">Rack::Protection</a>을
+사용하여 일반적인, 일어날 수 있는 공격에 대비한다.
+이 부분은 간단하게 비활성시킬 수 있다(성능 향상
+효과를 가져올 것이다):</p>
+
+<pre>disable :protection</pre>
+
+<p>하나의 방어층만 스킵하려면, 옵션 해시에
+<tt>protection</tt>을 설정하면 된다:</p>
+
+<pre>set :protection, :except =&gt; :path_traversal</pre>
+
+<p>방어막 여러 개를 비활성하려면, 배열로 주면 된다:</p>
+
+<pre>set :protection, :except =&gt; [:path_traversal, :session_hijacking]</pre>
+
+<a name='%EA%B0%80%EB%8A%A5%ED%95%9C%20%EC%84%A4%EC%A0%95%EB%93%A4(Available%20Settings)'></a>
+<h3>가능한 설정들(Available Settings)</h3>
+<dl class="rdoc-list"><dt>absolute_redirects</dt>
+<dd>
+<p>만약 비활성이면, Sinatra는 상대경로 리다이렉트를
+허용할 것이지만, 이렇게 되면 Sinatra는 더 이상 오직
+절대경로 리다이렉트만 허용하고 있는 RFC 2616(HTTP 1.1)에
+위배될 것이다.</p>
+
+<p>적정하게 설정되지 않은 리버스 프록시 하에서 앱을
+실행 중이라면 활성화시킬 것. <tt>rul</tt> 헬퍼는, 만약 두
+번째 매개변수로 <tt>false</tt>를 전달하지만 않는다면,
+여전히 절대경로 URL을 생성할 것임에 유의하자.</p>
+
+<p>기본값은 비활성.</p>
+</dd><dt>add_charsets</dt>
+<dd>
+<p><tt>content_type</tt>가 문자셋 정보에 자동으로 추가하게 될
+마임(mime) 타입.</p>
+
+<p>이 옵션은 오버라이딩하지 말고 추가해야 한다:</p>
+
+<pre>settings.add_charsets &lt;&lt; &quot;application/foobar&quot;</pre>
+</dd><dt>app_file</dt>
+<dd>
+<p>메인 애플리케이션 파일의 경로. 프로젝트 루트와 뷰,
+그리고 public 폴더, 인라인 템플릿을 파악할 때 사용됨.</p>
+</dd><dt>bind</dt>
+<dd>
+<p>바인드할 IP 주소(기본값: 0.0.0.0). 오직 빌트인(built-in)
+서버에서만 사용됨.</p>
+</dd><dt>default_encoding</dt>
+<dd>
+<p>모를 때 가정할 인코딩 (기본값은 <tt>&quot;utf-8&quot;</tt>).</p>
+</dd><dt>dump_errors</dt>
+<dd>
+<p>로그로 에러 출력.</p>
+</dd><dt>environment</dt>
+<dd>
+<p>현재 환경, 기본값은 <tt>ENV['RACK_ENV']</tt> 또는 알 수 없을
+경우 "development".</p>
+</dd><dt>logging</dt>
+<dd>
+<p>로거(logger) 사용.</p>
+</dd><dt>lock</dt>
+<dd>
+<p>매 요청에 걸쳐 잠금(lock)을 설정. Ruby 프로세스 당
+요청을 동시에 할 경우.</p>
+
+<p>앱이 스레드 안전(thread-safe)이 아니라면 활성화시킬 것.
+기본값은 비활성.</p>
+</dd><dt>method_override</dt>
+<dd>
+<p>put/delete를 지원하지 않는 브라우저에서 put/delete 폼을
+허용하는 <tt>_method</tt> 꼼수 사용.</p>
+</dd><dt>port</dt>
+<dd>
+<p>접속 포트. 빌트인 서버에서만 사용됨.</p>
+</dd><dt>prefixed_redirects</dt>
+<dd>
+<p>절대경로가 주어지지 않은 리다이렉트에
+<tt>request.script_name</tt>를 삽입할지 여부. 이렇게 하면
+<tt>redirect '/foo'</tt>는 <tt>redirect to('/foo')</tt> 처럼 동작.
+기본값은 비활성.</p>
+</dd><dt>protection</dt>
+<dd>
+<p>웹 공격 방어를 활성화시킬 건지 여부. 위의 보안 섹션
+참조.</p>
+</dd><dt>public_folder</dt>
+<dd>
+<p>public 파일이 제공될 폴더의 경로. static 파일 제공이
+활성화된 경우만 사용됨(아래 <tt>static</tt>참조). 만약
+설정이 없으면 <tt>app_file</tt>로부터 유추됨.</p>
+</dd><dt>reload_templates</dt>
+<dd>
+<p>요청 간에 템플릿을 리로드(reload)할 건지 여부. 개발
+모드에서는 활성됨.</p>
+</dd><dt>root</dt>
+<dd>
+<p>프로젝트 루트 디렉터리 경로. 설정이 없으면
+<tt>app_file</tt> 설정으로부터 유추됨.</p>
+</dd><dt>raise_errors</dt>
+<dd>
+<p>예외 발생(애플리케이션은 중단됨). 기본값은
+<tt>environment</tt>가 <tt>&quot;test&quot;</tt>인 경우는 활성,
+그렇지 않으면 비활성.</p>
+</dd><dt>run</dt>
+<dd>
+<p>활성화되면, Sinatra가 웹서버의 시작을 핸들링. rackup 또는
+다른 도구를 사용하는 경우라면 활성화시키지 말 것.</p>
+</dd><dt>running</dt>
+<dd>
+<p>빌트인 서버가 실행 중인지? 이 설정은 변경하지 말 것!</p>
+</dd><dt>server</dt>
+<dd>
+<p>빌트인 서버로 사용할 서버 또는 서버 목록. 기본값은
+['thin', 'mongrel', 'webrick']이며 순서는 우선순위를 의미.</p>
+</dd><dt>sessions</dt>
+<dd>
+<p><tt>Rack::Session::Cookie</tt>를 사용한 쿠키 기반 세션 활성화.
+보다 자세한 정보는 '세션 사용하기' 참조.</p>
+</dd><dt>show_exceptions</dt>
+<dd>
+<p>예외 발생 시에 브라우저에 스택 추적을 보임. 기본값은
+<tt>environment</tt>가 <tt>&quot;development&quot;</tt>인 경우는
+활성, 나머지는 비활성.</p>
+</dd><dt>static</dt>
+<dd>
+<p>Sinatra가 정적(static) 파일을 핸들링할 지 여부. 이 기능을
+수행하는 서버를 사용하는 경우라면 비활성시킬 것.
+비활성시키면 성능이 올라감. 기본값은 전통적
+방식에서는 활성, 모듈 앱에서는 비활성.</p>
+</dd><dt>static_cache_control</dt>
+<dd>
+<p>Sinatra가 정적 파일을 제공하는 경우, 응답에
+<tt>Cache-Control</tt> 헤더를 추가할 때 설정.
+<tt>cache_control</tt> 헬퍼를 사용. 기본값은 비활성. 여러
+값을 설정할 경우는 명시적으로 배열을 사용할 것: <tt>set
+:static_cache_control, [:public, :max_age =&gt; 300]</tt></p>
+</dd><dt>threaded</dt>
+<dd>
+<p><tt>true</tt>로 설정하면, Thin이 요청을 처리하는데 있어
+<tt>EventMachine.defer</tt>를 사용하도록 함.</p>
+</dd><dt>views</dt>
+<dd>
+<p>뷰 폴더 경로. 설정하지 않은 경우 <tt>app_file</tt>로부터
+유추됨.</p>
+</dd></dl>
+
+<a name='%ED%99%98%EA%B2%BD(Environments)'></a>
+<h2>환경(Environments)</h2>
+
+<p>환경은 <tt>RACK_ENV</tt> 환경 변수를 통해서도 설정할 수
+있다. 기본값은 "development"다. 이 모드에서, 모든
+템플릿들은 요청 간에 리로드된다. 특별한 <tt>not_found</tt>
+와 <tt>error</tt> 핸들러가 이 환경에 설치되기 때문에
+브라우저에서 스택 추적을 볼 수 있을 것이다.
+<tt>&quot;production&quot;</tt>과 <tt>&quot;test&quot;</tt>에서는
+템플릿은 캐시되는 게 기본값이다.</p>
+
+<p>다른 환경으로 실행시키려면 <tt>-e</tt>옵션을 사용하면
+된다:</p>
+
+<pre>ruby my_app.rb -e [ENVIRONMENT]</pre>
+
+<p>현재 설정된 환경이 무엇인지 검사하기 위해 사전
+정의된 <tt>development?</tt>, <tt>test?</tt> 및 <tt>production?</tt>
+메서드를 사용할 수 있다.</p>
+
+<a name='%EC%98%88%EC%99%B8%20%EC%B2%98%EB%A6%AC(Error%20Handling)'></a>
+<h2>예외 처리(Error Handling)</h2>
+
+<p>예외 핸들러는 라우터 및 사전 필터와 동일한 맥락에서
+실행된다. 이 말인즉, 이들이 제공하는 모든 것들을
+사용할 수 있다는 말이다. 예를 들면 <tt>haml</tt>,
+<tt>erb</tt>, <tt>halt</tt>, 등등.</p>
+
+<a name='%EC%B0%BE%EC%9D%84%20%EC%88%98%20%EC%97%86%EC%9D%8C(Not%20Found)'></a>
+<h3>찾을 수 없음(Not Found)</h3>
+
+<p><tt>Sinatra::NotFound</tt> 예외가 발생하거나 또는 응답의
+상태 코드가 404라면, <tt>not_found</tt> 핸들러가 호출된다:</p>
+
+<pre>not_found do
+ '아무 곳에도 찾을 수 없습니다.'
+end</pre>
+
+<a name='%EC%98%A4%EB%A5%98(Error)'></a>
+<h3>오류(Error)</h3>
+
+<p><tt>error</tt> 핸들러는 라우터 또는 필터에서 뭐든 오류가
+발생할 경우에 호출된다. 예외 객체는 Rack 변수
+<tt>sinatra.error</tt>로부터 얻을 수 있다:</p>
+
+<pre>error do
+ '고약한 오류가 발생했군요 - ' + env['sinatra.error'].name
+end</pre>
+
+<p>사용자 정의 오류:</p>
+
+<pre>error MyCustomError do
+ '무슨 일이 생겼나면요...' + env['sinatra.error'].message
+end</pre>
+
+<p>그런 다음, 이 오류가 발생하면:</p>
+
+<pre>get '/' do
+ raise MyCustomError, '안좋은 일'
+end</pre>
+
+<p>다음을 얻는다:</p>
+
+<pre>무슨 일이 생겼냐면요... 안좋은 일</pre>
+
+<p>또는, 상태 코드에 대해 오류 핸들러를 설치할 수 있다:</p>
+
+<pre>error 403 do
+ '액세스가 금지됨'
+end
+
+get '/secret' do
+ 403
+end</pre>
+
+<p>Or a range:</p>
+
+<pre>error 400..510 do
+ '어이쿠'
+end</pre>
+
+<p>Sinatra는 개발 환경에서 동작할 경우에 특별한
+<tt>not_found</tt> 와 <tt>error</tt> 핸들러를 설치한다.</p>
+
+<a name='Rack%20%EB%AF%B8%EB%93%A4%EC%9B%A8%EC%96%B4(Rack%20Middleware)'></a>
+<h2>Rack 미들웨어(Rack Middleware)</h2>
+
+<p>Sinatra는 <a href="http://rack.rubyforge.org/">Rack</a> 위에서
+동작하며, Rack은 루비 웹 프레임워크를 위한 최소한의
+표준 인터페이스이다. Rack이 애플리케이션 개발자들에게
+제공하는 가장 흥미로운 기능 중 하나가 바로
+"미들웨어(middleware)"에 대한 지원이며, 여기서
+미들웨어란 서버와 여러분의 애플리케이션 사이에
+위치하면서 HTTP 요청/응답을 모니터링하거나/또는
+조작함으로써 다양한 유형의 공통 기능을 제공하는
+컴포넌트(component)다.</p>
+
+<p>Sinatra는 톱레벨의 <tt>use</tt> 메서드를 사용하여 Rack
+미들웨어의 파이프라인을 만드는 일을 식은 죽 먹기로
+만든다:</p>
+
+<pre>require 'sinatra'
+require 'my_custom_middleware'
+
+use Rack::Lint
+use MyCustomMiddleware
+
+get '/hello' do
+ 'Hello World'
+end</pre>
+
+<p><tt>use</tt>의 의미는 <a
+href="http://rack.rubyforge.org/doc/classes/Rack/Builder.html">Rack::Builder</a>
+DSL (rackup 파일에서 가장 많이 사용된다)에서 정의한
+것들과 동일하다. 예를 들어, <tt>use</tt> 메서드는 블록 뿐
+아니라 여러 개의/가변적인 인자도 받는다:</p>
+
+<pre>use Rack::Auth::Basic do |username, password|
+ username == 'admin' &amp;&amp; password == 'secret'
+end</pre>
+
+<p>Rack은 로깅, 디버깅, URL 라우팅, 인증, 그리고 세센
+핸들링을 위한 다양한 표준 미들웨어로 분산되어 있다.
+Sinatra는 설정에 기반하여 이들 컴포넌트들 중 많은
+것들을 자동으로 사용하며, 따라서 여러분은
+일반적으로는 <tt>use</tt>를 명시적으로 사용할 필요가
+없을 것이다.</p>
+
+<p>유용한 미들웨어들은 <a
+href="https://github.com/rack/rack/tree/master/lib/rack">rack</a>, <a
+href="https://github.com/rack/rack-contrib#readme">rack-contrib</a>, <a
+href="http://coderack.org/">CodeRack</a> 또는 <a
+href="https://github.com/rack/rack/wiki/List-of-Middleware">Rack wiki</a>
+에서 찾을 수 있다.</p>
+
+<a name='%ED%85%8C%EC%8A%A4%ED%8C%85(Testing)'></a>
+<h2>테스팅(Testing)</h2>
+
+<p>Sinatra 테스트는 Rack 기반 어떠한 테스팅 라이브러리 또는
+프레임워크를 사용하여도 작성할 수 있다. <a
+href="http://rdoc.info/github/brynary/rack-test/master/frames">Rack::Test</a>를
+권장한다:</p>
+
+<pre>require 'my_sinatra_app'
+require 'test/unit'
+require 'rack/test'
+
+class MyAppTest &lt; Test::Unit::TestCase
+ include Rack::Test::Methods
+
+ def app
+ Sinatra::Application
+ end
+
+ def test_my_default
+ get '/'
+ assert_equal 'Hello World!', last_response.body
+ end
+
+ def test_with_params
+ get '/meet', :name =&gt; 'Frank'
+ assert_equal 'Hello Frank!', last_response.body
+ end
+
+ def test_with_rack_env
+ get '/', {}, 'HTTP_USER_AGENT' =&gt; 'Songbird'
+ assert_equal &quot;You're using Songbird!&quot;, last_response.body
+ end
+end</pre>
+
+<a name='Sinatra::Base%20-%20%EB%AF%B8%EB%93%A4%EC%9B%A8%EC%96%B4(Middleware),%20%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC(Libraries),%20%EA%B7%B8%EB%A6%AC%EA%B3%A0%20%EB%AA%A8%EB%93%88%20%EC%95%B1(Modular%20Apps)'></a>
+<h2>Sinatra::Base - 미들웨어(Middleware), 라이브러리(Libraries), 그리고 모듈 앱(Modular Apps)</h2>
+
+<p>톱레벨에서 앱을 정의하는 것은 마이크로 앱(micro-app)
+수준에서는 잘 동작하지만, Rack 미들웨어나, Rails
+메탈(metal) 또는 서버 컴포넌트를 갖는 간단한
+라이브러리, 또는 더 나아가 Sinatra 익스텐션(extension)
+같은 재사용 가능한 컴포넌트들을 구축할 경우에는
+심각한 약점을 가진다. 톱레벨은 마이크로 앱 스타일의
+설정을 가정한다(즉, 하나의 단일 애플리케이션 파일과
+<tt>./public</tt> 및 <tt>./views</tt> 디렉터리, 로깅, 예외 상세
+페이지 등등). 이게 바로 <tt>Sinatra::Base</tt>가 필요한
+부분이다:</p>
+
+<pre>require 'sinatra/base'
+
+class MyApp &lt; Sinatra::Base
+ set :sessions, true
+ set :foo, 'bar'
+
+ get '/' do
+ 'Hello world!'
+ end
+end</pre>
+
+<p><tt>Sinatra::Base</tt> 서브클래스에서 사용가능한
+메서드들은 톱레벨 DSL로 접근 가능한 것들과 동일하다.
+대부분의 톱레벨 앱들이 다음 두 가지만 수정하면
+<tt>Sinatra::Base</tt> 컴포넌트로 변환 가능하다:</p>
+<ul><li>
+<p>파일은 <tt>sinatra</tt>가 아닌 <tt>sinatra/base</tt>를 require해야
+하며, 그렇지 않으면 모든 Sinatra의 DSL 메서드들이 메인
+네임스페이스에 불러지게 된다.</p>
+</li><li>
+<p>앱의 라우터, 예외 핸들러, 필터, 그리고 옵션들을
+<tt>Sinatra::Base</tt>의 서브클래스에 둘 것.</p>
+</li></ul>
+
+<p><tt>Sinatra::Base</tt>는 빈서판(blank slate)이다. 빌트인 서버를
+비롯한 대부분의 옵션들이 기본값으로 꺼져 있다.
+가능한 옵션들과 그 작동에 대한 상세는 <a
+href="http://sinatra.github.com/configuration.html">Options and
+Configuration</a>을 참조할 것.</p>
+
+<a name='%EB%AA%A8%EB%93%88(Modular)%20vs.%20%EC%A0%84%ED%86%B5%EC%A0%81%20%EB%B0%A9%EC%8B%9D(Classic%20Style)'></a>
+<h3>모듈(Modular) vs. 전통적 방식(Classic Style)</h3>
+
+<p>일반적인 믿음과는 반대로, 전통적 방식에 잘못된
+부분은 없다. 여러분 애플리케이션에 맞다면, 모듈
+애플리케이션으로 전환할 필요는 없다.</p>
+
+<p>모듈 방식이 아닌 전통적 방식을 사용할 경우 생기는
+주된 단점은 루비 프로세스 당 오직 하나의 Sinatra
+애플리케이션만 사용할 수 있다는 점이다. 만약 하나
+이상을 사용할 계획이라면, 모듈 방식으로 전환하라.
+모듈 방식과 전통적 방식을 섞어쓰지 못할 이유는 없다.</p>
+
+<p>하나의 방식에서 다른 것으로 전환할 경우에는, 기본값
+설정의 미묘한 차이에 유의해야 한다:</p>
+
+<pre>설정 전통적 방식 모듈 방식
+
+app_file sinatra를 로딩하는 파일 Sinatra::Base를 서브클래싱한 파일
+run $0 == app_file false
+logging true false
+method_override true false
+inline_templates true false
+static true false</pre>
+
+<a name='%EB%AA%A8%EB%93%88%20%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98(Modular%20Application)%20%EC%A0%9C%EA%B3%B5%ED%95%98%EA%B8%B0'></a>
+<h3>모듈 애플리케이션(Modular Application) 제공하기</h3>
+
+<p>모듈 앱을 시작하는 두 가지 일반적인 옵션이 있는데,
+공격적으로 <tt>run!</tt>으로 시작하거나:</p>
+
+<pre># my_app.rb
+require 'sinatra/base'
+
+class MyApp &lt; Sinatra::Base
+ # ... 여기에 앱 코드가 온다 ...
+
+ # 루비 파일이 직접 실행될 경우에 서버를 시작
+ run! if app_file == $0
+end</pre>
+
+<p>다음과 같이 시작:</p>
+
+<pre>ruby my_app.rb</pre>
+
+<p>또는 <tt>config.ru</tt>와 함께 사용하며, 이 경우는 어떠한
+Rack 핸들러라도 사용할 수 있다:</p>
+
+<pre># config.ru
+require './my_app'
+run MyApp</pre>
+
+<p>실행:</p>
+
+<pre>rackup -p 4567</pre>
+
+<a name='config.ru%EB%A1%9C%20%EC%A0%84%ED%86%B5%EC%A0%81%20%EB%B0%A9%EC%8B%9D%EC%9D%98%20%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98%20%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0'></a>
+<h3>config.ru로 전통적 방식의 애플리케이션 사용하기</h3>
+
+<p>앱 파일을 다음과 같이 작성하고:</p>
+
+<pre># app.rb
+require 'sinatra'
+
+get '/' do
+ 'Hello world!'
+end</pre>
+
+<p>대응하는 <tt>config.ru</tt>는 다음과 같이 작성:</p>
+
+<pre>require './app'
+run Sinatra::Application</pre>
+
+<a name='%EC%96%B8%EC%A0%9C%20config.ru%EB%A5%BC%20%EC%82%AC%EC%9A%A9%ED%95%A0%EA%B9%8C?'></a>
+<h3>언제 config.ru를 사용할까?</h3>
+
+<p>Good signs you probably want to use a <tt>config.ru</tt>: 다음은
+<tt>config.ru</tt>를 사용하게 될 징후들이다:</p>
+<ul><li>
+<p>다른 Rack 핸들러(Passenger, Unicorn, Heroku, ...)로 배포하고자
+할 때.</p>
+</li><li>
+<p>하나 이상의 <tt>Sinatra::Base</tt> 서브클래스를 사용하고자
+할 때.</p>
+</li><li>
+<p>Sinatra를 최종점(endpoint)이 아니라, 오로지 미들웨어로만
+사용하고자 할 때.</p>
+</li></ul>
+
+<p><b>모듈 방식으로 전환했다는 이유만으로
+<tt>config.ru</tt>로 전환할 필요는 없으며, 또한
+<tt>config.ru</tt>를 사용한다고 해서 모듈 방식을 사용해야
+하는 것도 아니다.</b></p>
+
+<a name='Sinatra%EB%A5%BC%20%EB%AF%B8%EB%93%A4%EC%9B%A8%EC%96%B4%EB%A1%9C%20%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0'></a>
+<h3>Sinatra를 미들웨어로 사용하기</h3>
+
+<p>Sinatra에서 다른 Rack 미들웨어를 사용할 수 있을 뿐
+아니라, 모든 Sinatra 애플리케이션은 순차로 어떠한 Rack
+종착점 앞에 미들웨어로 추가될 수 있다. 이 종착점은
+다른 Sinatra 애플리케이션이 될 수도 있고, 또는 Rack
+기반의 어떠한 애플리케이션(Rails/Ramaze/Camping/...)이라도
+가능하다:</p>
+
+<pre>require 'sinatra/base'
+
+class LoginScreen &lt; Sinatra::Base
+ enable :sessions
+
+ get('/login') { haml :login }
+
+ post('/login') do
+ if params[:name] == 'admin' &amp;&amp; params[:password] == 'admin'
+ session['user_name'] = params[:name]
+ else
+ redirect '/login'
+ end
+ end
+end
+
+class MyApp &lt; Sinatra::Base
+ # 미들웨어는 사전 필터보다 앞서 실행됨
+ use LoginScreen
+
+ before do
+ unless session['user_name']
+ halt &quot;접근 거부됨, &lt;a href='/login'&gt;로그인&lt;/a&gt; 하세요.&quot;
+ end
+ end
+
+ get('/') { &quot;Hello #{session['user_name']}.&quot; }
+end</pre>
+
+<a name='%EB%8F%99%EC%A0%81%EC%9D%B8%20%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98%20%EC%83%9D%EC%84%B1(Dynamic%20Application%20Creation)'></a>
+<h3>동적인 애플리케이션 생성(Dynamic Application Creation)</h3>
+
+<p>경우에 따라선 어떤 상수에 할당하지 않고 런타임에서
+새 애플리케이션들을 생성하고 싶을 수도 있을 것인데,
+이 때는 <tt>Sinatra.new</tt>를 쓰면 된다:</p>
+
+<pre>require 'sinatra/base'
+my_app = Sinatra.new { get('/') { &quot;hi&quot; } }
+my_app.run!</pre>
+
+<p>이것은 선택적 인자로 상속할 애플리케이션을 받는다:</p>
+
+<pre># config.ru
+require 'sinatra/base'
+
+controller = Sinatra.new do
+ enable :logging
+ helpers MyHelpers
+end
+
+map('/a') do
+ run Sinatra.new(controller) { get('/') { 'a' } }
+end
+
+map('/b') do
+ run Sinatra.new(controller) { get('/') { 'b' } }
+end</pre>
+
+<p>이것은 Sintra 익스텐션을 테스팅하거나 또는 여러분의
+라이브러리에서 Sinatra를 사용할 경우에 특히 유용하다.</p>
+
+<p>또한 이 방법은 Sinatra를 미들웨어로 사용하는 것을 아주
+쉽게 만들어 준다:</p>
+
+<pre>require 'sinatra/base'
+
+use Sinatra do
+ get('/') { ... }
+end
+
+run RailsProject::Application</pre>
+
+<a name='%EB%B2%94%EC%9C%84(Scopes)%EC%99%80%20%EB%B0%94%EC%9D%B8%EB%94%A9(Binding)'></a>
+<h2>범위(Scopes)와 바인딩(Binding)</h2>
+
+<p>현재 어느 범위에 있느냐가 어떤 메서드와 변수를
+사용할 수 있는지를 결정한다.</p>
+
+<a name='%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98/%ED%81%B4%EB%9E%98%EC%8A%A4%20%EB%B2%94%EC%9C%84'></a>
+<h3>애플리케이션/클래스 범위</h3>
+
+<p>모든 Sinatra 애플리케이션은 <tt>Sinatra::Base</tt>의
+서브클래스에 대응된다. 만약 톱레벨 DSL (<tt>require
+'sinatra'</tt>)을 사용한다면, 이 클래스는
+<tt>Sinatra::Application</tt>이며, 그렇지 않을 경우라면
+여러분이 명시적으로 생성한 그 서브클래스가 된다.
+클래스 레벨에서는 <tt>get</tt> 이나 <tt>before</tt> 같은
+메서드들을 가지나, <tt>request</tt> 객체나 <tt>session</tt>
+에는 접근할 수 없다. 왜냐면 모든 요청에 대해
+애플리케이션 클래스는 오직 하나이기 때문이다.</p>
+
+<p><tt>set</tt>으로 생성한 옵션들은 클래스 레벨의
+메서드들이다:</p>
+
+<pre>class MyApp &lt; Sinatra::Base
+ # 이봐요, 저는 애플리케이션 범위에 있다구요!
+ set :foo, 42
+ foo # =&gt; 42
+
+ get '/foo' do
+ # 저기요, 전 이제 더 이상 애플리케이션 범위 속에 있지 않아요!
+ end
+end</pre>
+
+<p>다음 속에 있을 때 애플리케이션 범위가 된다:</p>
+<ul><li>
+<p>애플리케이션 클래스 본문</p>
+</li><li>
+<p>확장으로 정의된 메서드</p>
+</li><li>
+<p><tt>helpers</tt>로 전달된 블록</p>
+</li><li>
+<p><tt>set</tt>의 값으로 사용된 Procs/blocks</p>
+</li><li>
+<p><tt>Sinatra.new</tt>로 전달된 블록</p>
+</li></ul>
+
+<p>범위 객체 (클래스)는 다음과 같이 접근할 수 있다:</p>
+<ul><li>
+<p>configure 블록으로 전달된 객체를 통해(<tt>configure { |c| ...
+}</tt>)</p>
+</li><li>
+<p>요청 범위 내에서 <tt>settings</tt></p>
+</li></ul>
+
+<a name='%EC%9A%94%EC%B2%AD/%EC%9D%B8%EC%8A%A4%ED%84%B4%EC%8A%A4%20%EB%B2%94%EC%9C%84'></a>
+<h3>요청/인스턴스 범위</h3>
+
+<p>매 요청마다, 애플리케이션 클래스의 새 인스턴스가
+생성되고 모든 핸들러 블록은 그 범위 내에서 실행된다.
+이 범위 내에서 여러분은 <tt>request</tt> 와 <tt>session</tt>
+객체에 접근하거나 <tt>erb</tt> 나 <tt>haml</tt> 같은 렌더링
+메서드를 호출할 수 있다. 요청 범위 내에서
+애플리케이션 범위는 <tt>settings</tt> 헬퍼를 통해 접근
+가능하다:</p>
+
+<pre>class MyApp &lt; Sinatra::Base
+ # 이봐요, 전 애플리케이션 범위에 있다구요!
+ get '/define_route/:name' do
+ # '/define_route/:name'의 요청 범위
+ @value = 42
+
+ settings.get(&quot;/#{params[:name]}&quot;) do
+ # &quot;/#{params[:name]}&quot;의 요청 범위
+ @value # =&gt; nil (동일한 요청이 아님)
+ end
+
+ &quot;라우터가 정의됨!&quot;
+ end
+end</pre>
+
+<p>다음 속에 있을 때 요청 범위 바인딩이 된다:</p>
+<ul><li>
+<p>get/head/post/put/delete/options 블록</p>
+</li><li>
+<p>before/after 필터</p>
+</li><li>
+<p>헬퍼(helper) 메서드</p>
+</li><li>
+<p>템플릿/뷰</p>
+</li></ul>
+
+<a name='%EC%9C%84%EC%9E%84%20%EB%B2%94%EC%9C%84(Delegation%20Scope)'></a>
+<h3>위임 범위(Delegation Scope)</h3>
+
+<p>위임 범위(delegation scope)는 메서드를 단순히 클래스
+범위로 보낸다(forward). 그렇지만, 100% 클래스 범위처럼
+움직이진 않는데, 왜냐면 클래스 바인딩을 갖지 않기
+때문이다. 오직 명시적으로 위임(delegation) 표시된
+메서드들만 사용 가능하며 또한 클래스 범위와
+변수/상태를 공유하지 않는다 (유의: <tt>self</tt>가
+다르다). <tt>Sinatra::Delegator.delegate :method_name</tt>을
+호출하여 메서드 위임을 명시적으로 추가할 수 있다.</p>
+
+<p>다음 속에 있을 때 위임 범위 바인딩을 갖는다:</p>
+<ul><li>
+<p>톱레벨 바인딩, <tt>require &quot;sinatra&quot;</tt>를 한 경우</p>
+</li><li>
+<p><tt>Sinatra::Delegator</tt> 믹스인으로 확장된 객체</p>
+</li></ul>
+
+<p>직접 코드를 살펴보길 바란다: <a
+href="https://github.com/sinatra/sinatra/blob/ca06364/lib/sinatra/base.rb#L1609-1633">Sinatra::Delegator
+믹스인</a> 코드는 <a
+href="https://github.com/sinatra/sinatra/blob/ca06364/lib/sinatra/main.rb#L28-30">메인
+객체를 확장한 것</a>이다.</p>
+
+<a name='%EB%AA%85%EB%A0%B9%ED%96%89(Command%20Line)'></a>
+<h2>명령행(Command Line)</h2>
+
+<p>Sinatra 애플리케이션은 직접 실행할 수 있다:</p>
+
+<pre>ruby myapp.rb [-h] [-x] [-e ENVIRONMENT] [-p PORT] [-o HOST] [-s HANDLER]</pre>
+
+<p>옵션들:</p>
+
+<pre>-h # 도움말
+-p # 포트 설정 (기본값은 4567)
+-o # 호스트 설정 (기본값은 0.0.0.0)
+-e # 환경 설정 (기본값은 development)
+-s # rack 서버/핸들러 지정 (기본값은 thin)
+-x # mutex 잠금 켜기 (기본값은 off)</pre>
+
+<a name='%EC%9A%94%EA%B5%AC%EC%82%AC%ED%95%AD(Requirement)'></a>
+<h2>요구사항(Requirement)</h2>
+
+<p>다음의 루비 버전은 공식적으로 지원한다:</p>
+<dl class="rdoc-list"><dt> Ruby 1.8.7 </dt>
+<dd>
+<p>1.8.7은 완전하게 지원되지만, 꼭 그래야할 특별한 이유가
+없다면, 1.9.2로 업그레이드하거나 또는 JRuby나 Rubinius로
+전환할 것을 권장한다. 1.8.7에 대한 지원은 Sinatra 2.0과
+Ruby 2.0 이전에는 중단되지 않을 것이다. 또한 그때도,
+우리는 계속 지원할 것이다. <b>Ruby 1.8.6은 더이상
+지원되지 않는다.</b> 만약 1.8.6으로 실행하려 한다면,
+Sinatra 1.2로 다운그레이드하라. Sinatra 1.4.0이 릴리스될 때
+까지는 버그 픽스를 받을 수 있을 것이다.</p>
+</dd><dt> Ruby 1.9.2 </dt>
+<dd>
+<p>1.9.2는 완전하게 지원되면 권장된다. Radius와 Maraby는 현재
+1.9와 호환되지 않음에 유의하라. 1.9.2p0은, Sinatra를
+실행했을 때 세그먼트 오류가 발생한다고 알려져 있으니
+사용하지 말라. Ruby 1.9.4/2.0 릴리스까지는 적어도 지원을
+계속할 것이며, 최신 1.9 릴리스에 대한 지원은 Ruby
+코어팀이 지원하고 있는 한 계속 지원할 것이다.</p>
+</dd><dt> Ruby 1.9.3 </dt>
+<dd>
+<p>1.9.3은 완전하게 지원된다. 그렇지만 프로덕션에서의
+사용은 보다 상위의 패치 레벨이 릴리스될 때까지
+기다리길 권장한다(현재는 p0). 이전 버전에서 1.9.3으로
+전환할 경우 모든 세션이 무효화된다는 점을 유의하라.</p>
+</dd><dt> Rubinius </dt>
+<dd>
+<p>Rubinius는 공식적으로 지원되며 (Rubinius &gt;= 1.2.4), 모든
+템플릿 언어를 포함한 모든 것들이 작동한다. 조만간
+출시될 2.0 릴리스 역시 지원할 것이다.</p>
+</dd><dt> JRuby </dt>
+<dd>
+<p>JRuby는 공식적으로 지원된다 (JRuby &gt;= 1.6.5). 서드 파티
+템플릿 라이브러리와의 문제는 알려진 바 없지만, 만약