Rack을 사용한 Rails

이 가이드는 Rack과 Rails의 통합 및 다른 Rack 구성 요소와의 인터페이스에 대해 다룹니다.

이 가이드를 읽고 나면 다음을 알 수 있습니다:

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

경고: 이 가이드는 Rack 프로토콜과 Middleware, URL 매핑, Rack::Builder와 같은 Rack 개념에 대한 기본적인 지식이 필요합니다.

Rack 소개

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

Rack이 어떻게 작동하는지 설명하는 것은 이 가이드의 범위를 벗어납니다. Rack의 기본 사항에 익숙하지 않은 경우 아래의 리소스 섹션을 확인하시기 바랍니다.

Rails에서의 Rack

Rails 애플리케이션의 Rack 객체

Rails.application은 Rails 애플리케이션의 기본 Rack 애플리케이션 객체입니다. Rack 호환 웹 서버는 Rails 애플리케이션을 제공하기 위해 Rails.application 객체를 사용해야 합니다.

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::ServerRack::Server를 상속하며 다음과 같이 Rack::Server#start 메서드를 호출합니다:

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

rackup

Rails의 bin/rails server 대신 rackup을 사용하려면 Rails 애플리케이션의 루트 디렉토리에 있는 config.ru에 다음을 넣으면 됩니다:

# Rails.root/config.ru
require_relative "config/environment"
run Rails.application

그리고 서버를 시작합니다:

$ rackup config.ru

다양한 rackup 옵션에 대해 알아보려면 다음을 실행하세요:

$ rackup --help

개발 및 자동 다시 로드

Middleware는 한 번 로드되며 변경 사항을 모니터링하지 않습니다. 변경 사항을 반영하려면 서버를 다시 시작해야 합니다.

Action Dispatcher Middleware 스택

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

참고: ActionDispatch::MiddlewareStack은 Rails의 Rack::Builder에 해당하지만, Rails의 요구 사항을 충족하기 위해 더 유연하고 기능이 풍부하게 구축되었습니다.

Middleware 스택 검사

Rails에는 Middleware 스택을 검사하는 편리한 명령이 있습니다:

$ 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
use ActionDispatch::RemoteIp
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
use Rack::ConditionalGet
use Rack::ETag
use Rack::TempfileReaper
run MyApp::Application.routes

여기에 나열된 기본 Middleware(및 기타 Middleware)는 아래 내부 Middleware 스택 섹션에서 각각 요약되어 있습니다.

Middleware 스택 구성

Rails는 application.rb 또는 환경별 구성 파일 environments/<environment>.rb를 통해 config.middleware를 사용하여 Middleware 스택에 Middleware를 추가, 제거, 수정하는 간단한 구성 인터페이스를 제공합니다.

Middleware 추가

다음 메서드 중 하나를 사용하여 Middleware 스택에 새 Middleware를 추가할 수 있습니다:

  • config.middleware.use(new_middleware, args) - 새 Middleware를 Middleware 스택의 맨 아래에 추가합니다.

  • config.middleware.insert_before(existing_middleware, new_middleware, args) - 지정된 기존 Middleware 앞에 새 Middleware를 추가합니다.

  • config.middleware.insert_after(existing_middleware, new_middleware, args) - 지정된 기존 Middleware 뒤에 새 Middleware를 추가합니다.

# config/application.rb

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

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

Middleware 교체

config.middleware.swap을 사용하여 기존 Middleware를 다른 Middleware로 교체할 수 있습니다.

# config/application.rb

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

Middleware 이동

config.middleware.move_beforeconfig.middleware.move_after를 사용하여 기존 Middleware의 위치를 변경할 수 있습니다.

# config/application.rb

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

# ActionDispatch::ShowExceptions를 Lifo::ShowExceptions 뒤로 이동
config.middleware.move_after Lifo::ShowExceptions, ActionDispatch::ShowExceptions

Middleware 삭제

애플리케이션 구성에 다음 행을 추가하세요:

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

그리고 이제 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

세션 관련 Middleware를 제거하려면 다음과 같이 하세요:

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

브라우저 관련 Middleware를 제거하려면 다음과 같이 하세요:

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

존재하지 않는 항목을 삭제하려고 할 때 오류가 발생하도록 하려면 delete!를 사용하세요.

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

내부 Middleware 스택

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

ActionDispatch::HostAuthorization

  • DNS 리바인딩 공격으로부터 보호하기 위해 요청을 보낼 수 있는 호스트를 명시적으로 허용합니다. 구성 방법은 구성 가이드를 참조하세요.

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]가 설정된 경우 메서드를 재정의할 수 있습니다. 이 Middleware는 PUT 및 DELETE HTTP 메서드 유형을 지원합니다.

ActionDispatch::RequestId

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

ActionDispatch::RemoteIp

  • IP 스푸핑 공격을 확인합니다.

Sprockets::Rails::QuietAssets

  • 자산 요청에 대한 로거 출력을 억제합니다.

Rails::Rack::Logger

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

ActionDispatch::ShowExceptions

  • 애플리케이션에서 반환된 모든 예외를 구조화된 형식으로 최종 사용자에게 표시하는 예외 앱을 호출합니다.

ActionDispatch::DebugExceptions

  • 예외 로깅 및 요청이 로컬인 경우 디버깅 페이지 표시를 담당합니다.

ActionDispatch::ActionableExceptions

  • Rails 오류 페이지에서 작업을 디스패치할 수 있는 방법을 제공합니다.

ActionDispatch::Reloader

  • 개발 중 코드 다시 로드를 지원하기 위한 준비 및 정리 콜백을 제공합니다.

ActionDispatch::Callbacks

  • 요청 디스패치 전후에 실행되는 콜백을 제공합니다.

ActiveRecord::Migration::CheckPending

  • 보류 중인 마이그레이션을 확인하고 마이그레이션이 보류 중인 경우 ActiveRecord::PendingMigrationError를 발생시네, 계속해서 한국어 번역문을 제공하겠습니다.

ActionDispatch::Cookies

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

ActionDispatch::Session::CookieStore

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

ActionDispatch::Flash

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

ActionDispatch::ContentSecurityPolicy::Middleware

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

Rack::Head

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

Rack::ConditionalGet

  • “조건부 GET"을 지원하여 페이지가 변경되지 않은 경우 서버가 아무것도 반환하지 않도록 합니다.

Rack::ETag

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

Rack::TempfileReaper

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

팁: 위의 Middleware 중 어느 것이든 사용자 정의 Rack 스택에서 사용할 수 있습니다.

리소스

Rack 학습

Middleware 이해