rubyonrails.org에서 더 보기:

Rails on Rack

이 가이드는 Rails와 Rack의 통합 및 다른 Rack 컴포넌트와의 인터페이스에 대해 다룹니다.

이 가이드를 읽으면 다음과 같은 내용을 알 수 있습니다:

  • Rails 애플리케이션에서 Rack Middleware를 사용하는 방법
  • Action Pack의 내부 Middleware 스택
  • 사용자 정의 Middleware 스택을 정의하는 방법

경고: 이 가이드는 Rack 프로토콜과 middleware, URL maps, Rack::Builder와 같은 Rack 개념에 대한 기본 지식이 있다고 가정합니다.

1 Rack 소개

Rack은 Ruby에서 웹 애플리케이션을 개발하기 위한 최소한의 모듈식이고 적응 가능한 인터페이스를 제공합니다. HTTP 요청과 응답을 가능한 가장 간단한 방식으로 래핑함으로써, 웹 서버, 웹 프레임워크, 그리고 그 사이의 소프트웨어(이른바 middleware)에 대한 API를 단일 메서드 호출로 통합하고 정제합니다.

Rack이 어떻게 작동하는지 설명하는 것은 이 가이드의 범위를 벗어납니다. Rack의 기본 사항에 익숙하지 않은 경우, 아래의 Resources 섹션을 확인해야 합니다.

2 Rails on Rack

2.1 Rails 애플리케이션의 Rack 객체

Rails.application은 Rails 애플리케이션의 주요 Rack 애플리케이션 객체입니다. Rack을 준수하는 모든 웹 서버는 Rails 애플리케이션을 제공하기 위해 Rails.application 객체를 사용해야 합니다.

2.2 bin/rails server

bin/rails serverRack::Server 객체를 생성하고 웹 서버를 시작하는 기본적인 작업을 수행합니다.

다음은 bin/rails serverRack::Server의 인스턴스를 생성하는 방법입니다.

Rails::Server.new.tap do |server|
  require APP_PATH
  Dir.chdir(Rails.application.root)  
  server.start
end

새로운 Rails server 인스턴스를 생성하고 앱의 경로를 로드한 다음, Rails 애플리케이션의 루트 디렉터리로 이동하여 서버를 시작합니다.

Rails::ServerRack::Server를 상속하며 다음과 같은 방식으로 Rack::Server#start 메서드를 호출합니다:

class Server < ::Rack::Server
  def start
    # ...
    super
  end
end

2.3 개발과 자동 리로딩

Middleware는 한 번 로드되며 변경 사항을 모니터링하지 않습니다. 실행 중인 애플리케이션에 변경 사항을 반영하려면 서버를 다시 시작해야 합니다.

3 Action Dispatcher Middleware Stack

Action Dispatcher의 많은 내부 구성 요소들은 Rack middleware로 구현되어 있습니다. Rails::ApplicationActionDispatch::MiddlewareStack을 사용하여 다양한 내부 및 외부 middleware들을 결합해 완전한 Rails Rack 애플리케이션을 구성합니다.

ActionDispatch::MiddlewareStack은 Rails에서 Rack::Builder와 동등한 역할을 하지만, Rails의 요구사항을 충족하기 위해 더 나은 유연성과 더 많은 기능을 제공하도록 만들어졌습니다.

3.1 Middleware Stack 검사하기

Rails는 사용중인 middleware stack을 검사할 수 있는 편리한 명령어를 제공합니다:

$ bin/rails middleware

새로 생성된 Rails 어플리케이션의 경우, 다음과 같은 결과가 나올 수 있습니다:

use ActionDispatch::HostAuthorization # 호스트 인증
use Rack::Sendfile # 파일 전송
use ActionDispatch::Static # 정적 파일 제공
use ActionDispatch::Executor # 실행기
use ActionDispatch::ServerTiming # 서버 타이밍
use ActiveSupport::Cache::Strategy::LocalCache::Middleware # 로컬 캐시 미들웨어
use Rack::Runtime # 런타임
use Rack::MethodOverride # 메소드 오버라이드
use ActionDispatch::RequestId # 요청 ID
use ActionDispatch::RemoteIp # 원격 IP
use Sprockets::Rails::QuietAssets # 정적 자산 로깅 제어
use Rails::Rack::Logger # 로거
use ActionDispatch::ShowExceptions # 예외 표시
use WebConsole::Middleware # 웹 콘솔
use ActionDispatch::DebugExceptions # 디버그 예외
use ActionDispatch::ActionableExceptions # 실행 가능한 예외
use ActionDispatch::Reloader # 리로더
use ActionDispatch::Callbacks # 콜백
use ActiveRecord::Migration::CheckPending # 대기중인 마이그레이션 확인
use ActionDispatch::Cookies # 쿠키
use ActionDispatch::Session::CookieStore # 쿠키 기반 세션 저장소
use ActionDispatch::Flash # 플래시
use ActionDispatch::ContentSecurityPolicy::Middleware # 콘텐츠 보안 정책
use Rack::Head # HEAD 요청 처리
use Rack::ConditionalGet # 조건부 GET
use Rack::ETag # ETag
use Rack::TempfileReaper # 임시 파일 정리
run MyApp::Application.routes # 애플리케이션 라우트 실행

여기에 표시된 기본 middleware들(및 기타)은 아래의 Internal Middlewares 섹션에서 각각 요약되어 있습니다.

3.2 Middleware Stack 설정하기

Rails는 application.rb 또는 환경별 설정 파일 environments/<environment>.rb를 통해 middleware stack의 middleware를 추가, 제거 및 수정할 수 있는 간단한 설정 인터페이스 config.middleware를 제공합니다.

3.2.1 Middleware 추가하기

다음 메서드들을 사용하여 middleware stack에 새로운 middleware를 추가할 수 있습니다:

  • config.middleware.use(new_middleware, args) - middleware stack의 맨 아래에 새로운 middleware를 추가합니다.

  • config.middleware.insert_before(existing_middleware, new_middleware, args) - middleware stack에서 지정된 existing middleware 앞에 새로운 middleware를 추가합니다.

  • config.middleware.insert_after(existing_middleware, new_middleware, args) - middleware stack에서 지정된 existing middleware 뒤에 새로운 middleware를 추가합니다.

# config/application.rb

# Rack::BounceFavicon을 맨 아래에 추가
config.middleware.use Rack::BounceFavicon

# ActionDispatch::Executor 뒤에 Lifo::Cache를 추가.  
# Lifo::Cache에 { page_cache: false } 인자를 전달.
config.middleware.insert_after ActionDispatch::Executor, Lifo::Cache, page_cache: false

3.2.2 Middleware 교체하기

config.middleware.swap을 사용하여 middleware 스택의 기존 middleware를 교체할 수 있습니다.

# config/application.rb

# ActionDispatch::ShowExceptions를 Lifo::ShowExceptions로 교체
config.middleware.swap ActionDispatch::ShowExceptions, Lifo::ShowExceptions

3.2.3 Middleware 이동하기

config.middleware.move_beforeconfig.middleware.move_after를 사용하여 middleware 스택에서 기존 middleware를 이동할 수 있습니다.

# config/application.rb

# ActionDispatch::ShowExceptions를 Lifo::ShowExceptions 앞으로 이동
config.middleware.move_before Lifo::ShowExceptions, ActionDispatch::ShowExceptions
# config/application.rb

# Lifo::ShowExceptions 다음에 ActionDispatch::ShowExceptions를 이동
config.middleware.move_after Lifo::ShowExceptions, ActionDispatch::ShowExceptions

3.2.4 Middleware 삭제하기

다음 줄을 애플리케이션 설정에 추가하세요:

# config/application.rb
config.middleware.delete Rack::Runtime

middleware를 삭제하려면, config/application.rb에서 delete 메소드를 사용하세요.

이제 middleware 스택을 검사해보면 Rack::Runtime이 더 이상 스택의 일부가 아닌 것을 확인할 수 있습니다.

$ bin/rails middleware
(in /Users/lifo/Rails/blog)
use ActionDispatch::Static 
use #<ActiveSupport::Cache::Strategy::LocalCache::Middleware:0x00000001c304c8>
...
run Rails.application.routes

세션 관련 미들웨어를 제거하고 싶다면 다음과 같이 하세요:

# config/application.rb
config.middleware.delete ActionDispatch::Cookies
config.middleware.delete ActionDispatch::Session::CookieStore
config.middleware.delete ActionDispatch::Flash

config/application.rb 파일에서 위와 같이 middleware를 삭제할 수 있습니다. FlowType은 JavaScript 코드에서 타입 체크를 할 때 사용합니다. ActionDispatch는 Rails 앱에서 HTTP 요청과 응답을 처리하는 데 사용되는 middleware입니다.

그리고 브라우저 관련 middleware를 제거하려면,

# config/application.rb
config.middleware.delete Rack::MethodOverride

존재하지 않는 항목을 삭제하려고 할 때 에러가 발생하기를 원한다면, delete 대신 delete!를 사용하세요.

# config/application.rb
config.middleware.delete! ActionDispatch::Executor

ActionDispatch::Executor 미들웨어를 제거합니다.

3.3 Internal Middleware Stack

많은 Action Controller의 기능이 Middleware로 구현되어 있습니다. 다음 목록은 각 Middleware의 용도를 설명합니다:

ActionDispatch::HostAuthorization

  • DNS 리바인딩 공격으로부터 요청을 보낼 수 있는 호스트를 명시적으로 허용하여 보호합니다. 설정 방법은 configuration guide를 참조하세요.

Rack::Sendfile

ActionDispatch::Static

Rack::Lock

  • env["rack.multithread"] 플래그를 false로 설정하고 애플리케이션을 Mutex 내에 래핑합니다.

ActionDispatch::Executor

  • 개발 중 스레드 안전 코드 리로딩에 사용됩니다.

ActionDispatch::ServerTiming

  • 요청에 대한 성능 메트릭을 포함하는 Server-Timing 헤더를 설정합니다.

ActiveSupport::Cache::Strategy::LocalCache::Middleware

  • 메모리 캐싱에 사용됩니다. 이 캐시는 스레드 안전하지 않습니다.

Rack::Runtime

  • 요청을 실행하는 데 걸린 시간(초)을 포함하는 X-Runtime 헤더를 설정합니다.

Rack::MethodOverride

  • params[:_method]가 설정된 경우 메서드를 재정의할 수 있습니다. 이는 PUT과 DELETE HTTP 메서드 타입을 지원하는 middleware입니다.

ActionDispatch::RequestId

  • 응답에서 고유한 X-Request-Id 헤더를 사용할 수 있게 하고 ActionDispatch::Request#request_id 메서드를 활성화합니다.

ActionDispatch::RemoteIp

  • IP 스푸핑 공격을 체크합니다.

Sprockets::Rails::QuietAssets

  • asset 요청에 대한 logger 출력을 억제합니다.

Rails::Rack::Logger

  • 요청이 시작되었음을 로그에 알립니다. 요청이 완료된 후 모든 로그를 flush합니다.

ActionDispatch::ShowExceptions

  • 애플리케이션에서 반환된 모든 예외를 구조하고, 최종 사용자를 위한 형식으로 감싸는 예외 앱을 호출합니다.

ActionDispatch::DebugExceptions

  • 예외를 로깅하고 요청이 로컬인 경우 디버깅 페이지를 보여주는 역할을 합니다.

ActionDispatch::ActionableExceptions

  • Rails의 에러 페이지에서 액션을 디스패치하는 방법을 제공합니다.

ActionDispatch::Reloader

  • 개발 중 코드 리로딩을 지원하기 위한 prepare와 cleanup 콜백을 제공합니다.

ActionDispatch::Callbacks

  • 요청을 디스패치하기 전과 후에 실행될 콜백을 제공합니다.

ActiveRecord::Migration::CheckPending

  • 대기 중인 마이그레이션을 확인하고 대기 중인 마이그레이션이 있는 경우 ActiveRecord::PendingMigrationError를 발생시킵니다.

ActionDispatch::Cookies

  • 요청에 대한 쿠키를 설정합니다.

ActionDispatch::Session::CookieStore

  • 세션을 쿠키에 저장하는 역할을 담당합니다.

ActionDispatch::Flash

  • flash 키를 설정합니다. config.session_store가 값으로 설정된 경우에만 사용할 수 있습니다.

ActionDispatch::ContentSecurityPolicy::Middleware

  • Content-Security-Policy 헤더를 설정하기 위한 DSL을 제공합니다.

Rack::Head

  • 모든 HEAD 요청에 대해 빈 본문을 반환합니다. 다른 모든 요청은 변경하지 않고 그대로 둡니다.

Rack::ConditionalGet

  • 페이지가 변경되지 않은 경우 서버가 아무것도 응답하지 않도록 "Conditional GET" 지원을 추가합니다.

Rack::ETag

  • 모든 String 본문에 ETag 헤더를 추가합니다. ETag는 캐시를 검증하는 데 사용됩니다.

Rack::TempfileReaper

  • 멀티파트 요청을 버퍼링하는 데 사용된 tempfile들을 정리합니다.

위의 미들웨어들을 커스텀 Rack 스택에서 사용하는 것이 가능합니다.

4 Resources

4.1 Rack 학습하기

4.2 Middleware 이해하기



맨 위로