More at rubyonrails.org: More Ruby on Rails

API 전용 레일스 애플리케이션 만들기

이 가이드의 내용:

1 API 애플리케이션에 대해

지금까지 레일스로 API를 사용한다고 하면 프로그램이 사용할 수 있는 API를 웹 애플리케이션에 추가하는 방식을 의미했습니다. 예를 들어 GitHub이 제공하는 API를 직접 만든 클라이언트에서 사용할 수 있습니다.

클라이언트 프레임워크의 등장에 따라, 다른 웹 애플리케이션과 네이티브 애플리케이션에서 레일스로 만든 백엔드 서버를 사용하는 경우가 늘었습니다.

Twitter는 자사 웹 애플리케이션에서 공개 API를 사용하고 있습니다. 이 웹 애플리케이션은 JSON 리소스만을 사용하는 정적인 사이트입니다.

많은 개발자가 레일스를 폼이나 링크를 통하는 서버 간의 통신을 위해 HTML을 생성하는 대신, 웹 애플리케이션을 단순한 API 클라이언트로 정의하고, JSON API를 사용하는 HTML과 자바스크립트를 제공하는 방식으로 다루게 되었습니다.

여기에서는 클라이언트 프레임워크의 설명을 포함해, JSON 리소스를 API 클라이언트에 제공하는 레일스 애플리케이션을 구축하는 방법에 관해서 설명합니다.

2 JSON API에 레일스를 사용하는 이유

레일스로 JSON API를 만드는 것에 대해서 많은 개발자가 가장 먼저 떠올리는 질문은 이렇습니다. "레일스로 JSON을 제공하는건 너무 거창하지 않나요? 그냥 Sinatra로 만들면 어떤가요?"

단순한 API 서버라면 아마도 그럴 겁니다. 하지만 HTML의 비중이 매우 큰 애플리케이션이라도, 로직 대부분은 뷰의 바깥에 존재합니다.

많은 개발자가 레일스를 채용하는 이유는 세세한 설정을 고민하지 않고, 빠르게 애플리케이션을 제공할 수 있기 때문입니다.

API 애플리케이션 개발에 도움이 되는 레일스의 기능을 몇 가지 소개합니다.

미들웨어에서 제공하는 기능 목록

  • 리로딩: 레일스 애플리케이션은 '투명한 리로딩'을 지원합니다. 예를 들어 애플리케이션이 커져 요청마다 서버를 재기동하는 방법을 사용할 수 없더라도 투명한 리로딩이 가능합니다.
  • 개발 모드: 레일스 애플리케이션의 개발 모드에는 기본값이 이미 설정되어 있으므로 실제 환경의 성능에 대한 걱정 없이 즐겁게 작업을 진행할 수 있습니다.
  • 테스트 모드: 개발 모드와 같습니다.
  • 로그 출력: 레일스 애플리케이션은 요청마다 로그를 출력합니다. 또한 현재 모드에 따라서 로그의 레벨이 조정됩니다. 개발 모드의 로그에는 요청 환경, 데이터베이스 질의, 간단한 성능 정보 등이 출력됩니다.
  • 보안: IP 스푸핑 공격을 검출, 방어합니다. 또한 타이밍 공격에 대응할 수 있는 암호화 서명을 다룹니다.
  • 매개변수 분석: URL 인코딩이나 문자열 대신 JSON으로 매개변수를 지정할 수 있습니다. JSON은 레일스에서 해석되어 params를 통해 접근할 수 있습니다. 물론 중첩된 URL 인코딩 매개변수도 다룰 수 있습니다.
  • 조건부 GET: 레일스에서는 ETagLast-Modified를 사용한 조건부 GET을 사용합니다. 이는 요청 헤더를 처리하고, 올바른 응답 헤더와 상태 코드를 돌려줍니다. 컨트롤러에 stale?을 추가하면 HTTP의 구체적인 동작은 레일스가 처리합니다.
  • HEAD 요청: 레일스에서는 HEAD 요청을 투명하게 GET 요청으로 변환하고, 헤더만을 반환합니다. 이를 통해서 모든 레일스 API에서 HEAD 요청을 사용할 수 있습니다.

Rack 미들웨어의 이런 기능들을 직접 구현할 수도 있습니다만, 레일스의 기본 미들웨어를 "JSON 생성용"으로 쓰더라도 많은 이점을 얻을 수 있습니다.

액션 팩에서 제공하는 기능

  • 리소스 기반 라우팅: RESTful JSON API를 개발한다면 레일스의 라우터도 사용하고 싶을 것입니다. HTTP로부터 컨트롤러로 명확하게 연결할 수 있으므로 HTTP에 대해서 API를 어떻게 구성할지 고민할 필요가 없습니다.
  • URL 생성: 라우팅은 URL을 생성할 때에도 편리합니다. 잘 구성된 HTTP 기반의 API에는 URL도 포함됩니다(GitHub Gist API가 좋은 예시입니다).
  • 헤더 응답이나 리다이렉션 응답: head :no_contentredirect_to user_url(current_user) 등을 사용할 수 있습니다. 헤더 응답을 직접 생성하지 않아도 됩니다.
  • 캐시: 레일스는 페이지 캐싱, 액션 캐싱, 조각 캐싱을 사용할 수 있습니다. 특히 조각 캐싱은 중첩된 JSON 객체를 만들 때 유용합니다.
  • 기본 인증, 다이제스트 인증, 토큰 인증: 3종류의 HTTP 인증을 간단하게 도입할 수 있습니다.
  • 계측(Instrumentation): 레일스의 계측 API는 등록한 다양한 이벤트 핸들러를 실행합니다. 액션 처리, 파일이나 데이터 전송, 리다이렉트, 데이터베이스 질의 등을 다룹니다. 각 이벤트의 페이로드에 다양한 정보가 포함되어 있습니다. 예를 들어 이벤트를 처리하는 액션의 경우, 페이로드에는 컨트롤러, 액션, 매개변수, 요청 형식, 요청 경로등이 포함됩니다.
  • 제너레이터: 명령 하나로 리소스를 간단하게 생성하고, API에 맞는 모델, 컨트롤러, 테스트 스텁, 라우팅을 바로 사용할 수 있습니다. 마이그레이션 등의 작업도 명령으로 실행할 수 있습니다.
  • 플러그인: 수많은 서드파티 라이브러리를 사용할 수 있습니다. 라이브러리 설정이나 웹 프레임워크 등의 연동도 간단하므로, 비용을 줄일 수 있습니다. 플러그인을 통해 기존 제너레이터를 덮어쓰거나 Rake 태스크를 추가, 또는 레일스의 동작을 변경할 수도 있습니다(로거나 캐시 백엔드 등).

물론 레일스의 실행 프로세스에서는 등록된 컴포넌트를 모두 읽어서 연동합니다. 예를 들어 실행할 때에 config/database.yml 파일을 통하여 액티브 레코드를 설정합니다.

한줄 요약: 레일스에서 뷰와 관련된 동작을 제외하면 어떤 기능을 사용할 수 있을까요? 기능 대부분을 사용할 수 있습니다.

3 기본 설정

레일스 애플리케이션을 API 서버로 구축하고 싶다면, 기능을 제한한 레일스 하위 셋을 사용하여 필요한 기능을 추가하는 것이 좋을 겁니다.

3.1 애플리케이션을 새로 생성하기

API 레일스 애플리케이션을 생성하려면 다음의 명령을 사용합니다.

$ rails new my_api --api

이 명령은 다음 3개의 동작을 실행합니다.

  • 사용하는 미들웨어를 기존보다 적게끔 설정합니다. 특히 브라우저용 애플리케이션에서 유용한 미들웨어(쿠키 등의 지원)를 완전히 사용할 수 없게 됩니다.
  • ApplicationControllerActionController::Base가 아닌 ActionController::API에서 상속받습니다. 미들웨어와 마찬가지로 액션 컨트롤러 모듈에서 브라우저용 애플리케이션에서만 사용되는 부분을 모두 제외합니다.
  • 제너레이터가 뷰, 헬퍼, 어셋을 생성하지 않도록 합니다.

3.2 기존의 애플리케이션을 변경하기

기존의 애플리케이션을 API 전용으로 만들려면 다음 순서를 따라주세요.

config/application.rbApplication 클래스에 다음을 추가합니다.

config.api_only = true

config/environments/development.rb에서 config.debug_exception_response_format을 통해 개발 모드에서 에러가 발생할 경우에 응답에서 사용할 형식을 지정하세요.

:default는 HTML 페이지로 디버깅 정보를 제공합니다.

config.debug_exception_response_format = :default

:api는 응답 형식을 유지한 채로 디버깅 정보를 제공합니다.

config.debug_exception_response_format = :api

config.api_only를 true로 설정하면 config.debug_exception_response_format의 기본값이 :api로 설정됩니다.

마지막으로 app/controllers/application_controller.rb를,

class ApplicationController < ActionController::Base
end 

다음과 같이 변경합니다.

class ApplicationController < ActionController::API
end 

4 미들웨어 선택하기

API 애플리케이션에서는 기본으로 다음의 미들웨어를 사용합니다.

  • Rack::Sendfile
  • ActionDispatch::Static
  • ActionDispatch::Executor
  • ActiveSupport::Cache::Strategy::LocalCache::Middleware
  • Rack::Runtime
  • ActionDispatch::RequestId
  • Rails::Rack::Logger
  • ActionDispatch::ShowExceptions
  • ActionDispatch::DebugExceptions
  • ActionDispatch::RemoteIp
  • ActionDispatch::Reloader
  • ActionDispatch::Callbacks
  • Rack::Head
  • Rack::ConditionalGet
  • Rack::ETag

자세한 설명은 Rack 가이드의 내부 미들웨어에서 확인하세요.

미들웨어는 액티브 레코드 등의 플러그인에 의해서 추가되는 경우도 있습니다. 일반적으로 구축할 애플리케이션의 종류와 미들웨어는 관련이 없습니다만, API 전용 레일스 애플리케이션에서는 의미가 있습니다.

애플리케이션의 모든 미들웨어를 확인하려면 다음 명령을 실행하세요.

$ rails middleware

4.1 캐시 미들웨어를 사용하기

레일스는 애플리케이션의 설정에 따라 캐시 저장소(기본값은 memcache)를 제공하는 미들웨어를 추가합니다. 다시 말해, 레일스에 포함된 HTTP 캐시는 이 캐시 저장소에 의존합니다.

예를 들자면, 다음과 같이 stale? 메소드를 호출한다고 가정합시다.

def show
  @post = Post.find(params[:id])

  if stale?(last_modified: @post.updated_at)
    render json: @post
  end
end

stale? 호출은 @post.updated_at과 요청에 있는 If-Modified-Since 헤더를 비교합니다. 헤더가 마지막 변경 시점보다 새로운 경우 "304 Not Modified"를 반환하거나 Last-Modified 헤더를 포함하여 응답을 랜더링합니다.

일반적으로 이 동작은 클라이언트마다 이루어집니다만, 캐시 미들웨어가 있다면 클라이언트 간에 이 캐시를 공유할 수도 있습니다. 클라이언트 캐시 공유는 stale? 호출 시점에 지정할 수 있습니다.

def show
  @post = Post.find(params[:id])

  if stale?(last_modified: @post.updated_at, public: true)
    render json: @post
  end 
end

캐시 미들웨어는 URL에 대응하는 Last-Modified 값을 레일스 캐시에 저장하고 이후 같은 URL 요청을 수신할 경우 If-Modified-Since 헤더를 추가합니다.

이는 HTTP를 사용하는 페이지 캐싱이라고 생각할 수도 있을 겁니다.

4.2 Rack::Sendfile 사용하기

레일스 컨트롤러에서 send_file 메소드가 실행되면 X-Sendfile 헤더가 추가됩니다. Rack::Sendfile은 실제 파일 전송을 책임집니다.

빠른 파일 전송(accelerated file sending)을 지원하는 프론트엔드 서버는 Rack::Sendfile 대신에 실제 파일을 전송합니다.

프론트엔드 서버에서 파일 전송에 사용하는 헤더의 이름은 해당하는 환경 설정 파일의 config.action_dispatch.x_sendfile_header에서 지정할 수 있습니다.

Rack::Sendfile 문서에서 인기있는 프론트엔드와 함께 Rack::Sendfile을 사용하는 방법을 확인하세요.

빠른 파일 전송을 사용하려면 헤더에 다음과 같은 값을 설정하세요.

# Apache, lighttpd
config.action_dispatch.x_sendfile_header = "X-Sendfile"

# Nginx
config.action_dispatch.x_sendfile_header = "X-Accel-Redirect"

이 옵션을 사용하려면 Rack::Sendfile 문서를 따라서 서버를 설정해주세요.

4.3 ActionDispatch::Request 사용하기

ActionDispatch::Request#params는 클라이언트로부터 매개변수를 JSON 형식으로 받아 컨트롤러의 params로 접근할 수 있게 해줍니다.

이 기능을 사용하려면 JSON으로 인코딩된 매개변수를 클라이언트에서 보내고, 이 때 Content-Typeapplication/json이어야 합니다.

jQuery 예제는 다음과 같습니다.

jQuery.ajax({
  type: 'POST',
  url: '/people',
  dataType: 'json',
  contentType: 'application/json',
  data: JSON.stringify({ person: { firstName: "Yehuda", lastName: "Katz" } }),
  success: function(json) { }
});

ActionDispatch::Request에서는 이 Content-Type으로 다음 인자를 받습니다.

{ :person => { :firstName => "Yehuda", :lastName => "Katz" } }

4.4 그 이외의 미들웨어

레일스에서는 이외에도 API 애플리케이션을 위한 여러 미들웨어를 사용할 수 있습니다. 특히 브라우저가 API 클라이언트가 되는 경우에 다음의 미들웨어들이 유용합니다.

  • Rack::MethodOverride
  • ActionDispatch::Cookies
  • ActionDispatch::Flash
  • 세션 관리용
    • ActionDispatch::Session::CacheStore
    • ActionDispatch::Session::CookieStore
    • ActionDispatch::Session::MemCacheStore

이 미들웨어들은 다음과 같이 추가할 수 있습니다.

config.middleware.use Rack::MethodOverride

4.5 미들웨어 제거하기

API 전용 미들웨어에 포함하고 싶지 않은 미들웨어는 다음과 같이 삭제할 수 있습니다.

config.middleware.delete ::Rack::Sendfile

이 미들웨어를 삭제하면 액션 컨트롤러의 일부 기능을 사용할 수 없게 되므로 조심하세요.

5 컨트롤러에서 사용할 모듈 선택하기

API 애플리케이션(ActionController::API를 사용)에는 다음과 같은 컨트롤러 모듈이 포함됩니다.

  • ActionController::UrlFor: url_for 등의 헬퍼 제공
  • ActionController::Redirecting: redirect_to 제공
  • AbstractController::RenderingActionController::ApiRendering: 기본적인 랜더링을 제공
  • ActionController::Renderers::All: render :json 등을 제공
  • ActionController::ConditionalGet: stale?을 제공
  • ActionController::BasicImplicitRender: 명시적인 응답이 없으면 빈 응답을 반환
  • ActionController::StrongParameters: 매개변수를 위한 화이트리스트를 제공(액티브 모델의 대량 할당과 함께 동작)
  • ActionController::ForceSSL: force_ssl을 제공
  • ActionController::DataStreaming: send_file이나 send_data를 제공
  • AbstractController::Callbacks: before_action 등의 헬퍼를 제공
  • ActionController::Rescue: rescue_from을 제공
  • ActionController::Instrumentation: 액션 컨트롤러에서 정의하는 계측 훅을 제공(계측 가이드를 참조)
  • ActionController::ParamsWrapper: 매개변수 해시를 감싸서 중첩된 해시로 만듦. 이를 통해서 POST 요청을 전송하는 경우에도 최상위 요소를 지정하지 않도록 해줌

다른 플러그인을 통해 모듈이 추가되는 경우도 있습니다. ActionController::API의 모든 모듈 목록은 다음 명령으로 확인할 수 있습니다.

$ bin/rails c
>> ActionController::API.ancestors - ActionController::Metal.ancestors

5.1 그 외의 모듈 추가하기

액션 컨트롤러의 어떤 모듈도 자신이 의존하는 모듈을 파악하고 있으므로 자유롭게 컨트롤러에 모듈을 추가할 수 있습니다.

자주 사용되는 것은 다음과 같습니다.

  • AbstractController::Translation: 지역화용 l과 번역용 t 메소드를 제공
  • ActionController::HttpAuthentication::Basic(그리고 Digest, Token): HTTP의 기본 인증, 다이제스트 인증, 토큰 인증을 제공
  • ActionView::Layouts: 레이아웃을 제공
  • ActionController::MimeResponds: respond_to을 제공
  • ActionController::Cookies: 서명과 암호화를 포함한 cookies를 제공. 쿠키 미들웨어가 필요.

모듈은 ApplicationController에 추가하는 것이 가장 좋습니다만, 각각의 컨트롤러에 추가해도 괜찮습니다.

피드백

이 가이드의 질을 향상시키기 위한 도움을 기다리고 있습니다.

수정이 필요한 오타나 에러를 발견하면 기여해주세요. 이를 위해서 기여 가이드를 읽어주세요.

미완성된 내용이나 업데이트되지 않은 내용이 있을 수 있습니다. 누락된 문서를 추가해 주시기 바랍니다. 최신 가이드나 마스터 브랜치를 참고하여 이미 고쳐지지 않았는지 확인하세요. 문서작성시 참고해야할 스타일과 규칙은 Ruby on Rails Guides Guidelines를 확인하시기 바랍니다.

어떤 이유에서든지, 수정이 필요한 부분을 직접 변경할 수 없는 경우에는 open an issue로 접속하여 문제점을 제출해 주시기 바랍니다.

마지막으로, 루비온레일스 문서화에 관련된 논의가 필요한 사항은 rubyonrails-docs mailing list에서 해 주시기 바랍니다.

문서생성일

최종 생성일 : 2017-03-09 01:43:26 +0000