경고: 이 가이드는 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 server
는 Rack::Server
객체를 생성하고 웹 서버를 시작하는 기본적인 작업을 수행합니다.
다음은 bin/rails server
가 Rack::Server
의 인스턴스를 생성하는 방법입니다.
Rails::Server.new.tap do |server|
require APP_PATH
Dir.chdir(Rails.application.root)
server.start
end
새로운 Rails server 인스턴스를 생성하고 앱의 경로를 로드한 다음, Rails 애플리케이션의 루트 디렉터리로 이동하여 서버를 시작합니다.
Rails::Server
는 Rack::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::Application
은 ActionDispatch::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_before
와 config.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
- 서버별 X-Sendfile 헤더를 설정합니다.
config.action_dispatch.x_sendfile_header
옵션을 통해 설정할 수 있습니다.
ActionDispatch::Static
- public 디렉토리에서 정적 파일을 제공하는 데 사용됩니다.
config.public_file_server.enabled
가false
이면 비활성화됩니다.
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 스택에서 사용하는 것이 가능합니다.