원문(http://inocrazy.com/docs/6)에서 좀 더 깔끔하게 보실 수 있습니다.
RESTful Rails
Rails의 REST 기반의 개발 방식은 개발의 일관성과 편의성을 제공해준다. REST 기반의 개발에서는 웹을 페이지의 관점이 아닌 resource 관점에서 바라본다. 게시판에 글을 작성하고, 읽는 것이 아니라, 글이라는 resource를 생성하고, 가져오는 것이 된다. 또한 웹사이트의 요소를 resource로서 정확하게 정의하기만 하면, 해당 resource에 대한 CRUD 관점에서의 접근을 통해서 메소드명이나 경로명 등이 바로 결정되게 된다. 따라서 개발자는 그런 것들에 대해서 고민할 필요없이 핵심 코드에만 집중할 수 있게 된다. 예를 들어 블로그 어플리케이션을 개발하는 경우를 생각해보자. 만약 Rails가 아닌 다른 웹프레임워크(PHP, ASP 등)로 개발을 하거나, Rails에서 RESTful하지 않게 개발하는 경우에는, Post를 등록하고, 보여주고, 수정, 삭제하는 기능을 위해서 각각의 메소드명을 고민해야하고, 그에 대응하는 경로명을 고민해야한다. 하지만 Rails의 REST 기반의 개발 방식에서는 Post라는 resource가 결정된다면, 그에 대한 CRUD 메소드 및 경로가 정해진다. 다음은 Post resource를 처리하는 PostsController의 전형적인 소스 코드이다.
- class PostsController < ApplicationController
# GET /posts
# GET /posts.xml
def index
@posts = Post.find(:all)
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => @posts }
end
end
# GET /posts/1
# GET /posts/1.xml
def show
@post = Post.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => @post }
end
end
# GET /posts/new
# GET /posts/new.xml
def new
@post = Post.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => @post }
end
end
# GET /posts/1/edit
def edit
@post = Post.find(params[:id])
end
# POST /posts
# POST /posts.xml
def create
@post = Post.new(params[:post])
respond_to do |format|
if @post.save
flash[:notice] = 'Post was successfully created.'
format.html { redirect_to(@post) }
format.xml { render :xml => @post, :status => :created, :location => @post }
else
format.html { render :action => "new" }
format.xml { render :xml => @post.errors, :status => :unprocessable_entity }
end
end
end
# PUT /posts/1
# PUT /posts/1.xml
def update
@post = Post.find(params[:id])
respond_to do |format|
if @post.update_attributes(params[:post])
flash[:notice] = 'Post was successfully updated.'
format.html { redirect_to(@post) }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => @post.errors, :status => :unprocessable_entity }
end
end
end
# DELETE /posts/1
# DELETE /posts/1.xml
def destroy
@post = Post.find(params[:id])
@post.destroy
respond_to do |format|
format.html { redirect_to(posts_url) }
format.xml { head :ok }
end
end
end
각각의 메소드의 역할 및 접근 경로는 다음과 같다.
| 메소드명 | 타입 | 역할 | 접근경로 |
|---|---|---|---|
| index | collection | 리소스 collection에 대한 처리 | GET /posts |
| show | member | 특정 리소스에 대한 처리 | GET /posts/1 |
| new | new | 신규 리소스를 추가하기 위한 폼 | GET /posts/new |
| edit | member | 특정 리소스를 수정하기 위한 폼 | GET /posts/1/edit |
| create | collection | 리소스의 생성 | POST /posts |
| update | member | 특정 리소스의 수정 | PUT /posts/1 |
| destroy | member | 특정 리소스의 삭제 | DELETE /posts/1 |
더 자세한 RESTful Rails와 REST Routing에 대해서는 다른 문서에서 상세히 살펴보기로 하고, 여기서는 오늘의 주제인 make_resourceful 플러그인에 집중해보자.
DRY를 위한 make_resourceful
REST 기반의 Rails Application을 개발하다보면 위와 같은 Controller 코드를 반복적으로 사용하게 된다. 이는 Rails의 DRY(Don't Repeat Yourself) 정신에 맞지 않는다. 이러한 중복을 제거해주는 플러그인이 바로 make_resourceful 플러그인이다. 다음은 make_resourceful 플러그인을 이용해서 위의 소스코드와 동일한 기능을 하는 PostsController를 구현한 것이다.
- class PostsController < ApplicationController
make_resourceful do
actions :all
end
end
위의 코드가 끝이다! make_resourceful이란 클래스 메소드에 설정을 위한 블럭을 전달해주기만 하면, 기본적인 7개의 메소드들이 생성되게 된다. 그러면 make_resourceful 플러그인의 구체적인 사용법을 살펴보자.
설치하기
플러그인은 다음 명령어로 설치할 수 있다.
- $ ./script/plugin install http://svn.hamptoncatlin.com/make_resourceful/tags/make_resourceful
resourceful_scaffold
make_resourceful 플러그인에서는 resourceful_scaffold 라는 생성자도 함께 제공한다. 다음 명령어로 모델, 뷰, 테스트 및 make_resourceful 컨트롤러를 생성할 후 있다.
- $ ./script/generate resourceful_scaffold ResourceName attribute1:type1 attribute2:type2 ...
기본 action 커스터마이징하기 - Resourceful::Builder
make_resourceful 블럭은 Resourceful::Builder 클래스의 인스턴스의 문맥(context)에서 평가되게 된다. Resourceful::Builder 클래스는 make_resourceful에 의해서 생성되는 기본적인 action을 customizing하기 위해서 몇 개의 메소드를 제공한다. 이제 각 메소드의 사용법을 살펴보자.
actions(*available_actions)
actions 메소드는 위의 예제에서 이미 보았던 메소드이다. 이 actions 메소드에 전달된 action들만이 정의되게 된다. 인수로 :all 이 전달되는 경우, 기본 action 7개 (index, show, new, edit, create, update, destroy)가 모두 정의된다. 기본 action들은 Resourceful::Default::Actions에 정의되어 있다.
- make_resourceful do
- actions :all #=> 모든 메소드
- actions :show, :new, :create #=> show, new, create action만 정의.
- end
다음은 Resourceful::Default::Actions에 정의되어 있는 기본 action들이다. 내부적으로는 actions 메소드에 의해서 설정된 메소드를 제외한 나머지 메소드들이 remove_method에 의해서 제거된 후에, controller에 mix-in 되게 된다.
- module Actions
# GET /foos
def index
load_objects
before :index
response_for :index
end
# GET /foos/12
def show
load_object
before :show
response_for :show
rescue
response_for :show_fails
end
# POST /foos
def create
build_object
load_object
before :create
if current_object.save
save_succeeded!
after :create
response_for :create
else
save_failed!
after :create_fails
response_for :create_fails
end
end
# PUT /foos/12
def update
load_object
before :update
if current_object.update_attributes object_parameters
save_succeeded!
after :update
response_for :update
else
save_failed!
after :update_fails
response_for :update_fails
end
end
# GET /foos/new
def new
build_object
load_object
before :new
response_for :new
end
# GET /foos/12/edit
def edit
load_object
before :edit
response_for :edit
end
# DELETE /foos/12
def destroy
load_object
before :destroy
if current_object.destroy
after :destroy
response_for :destroy
else
after :destroy_fails
response_for :destroy_fails
end
end
end
before(*events, &block)
before 메소드는 특정 action의 앞에서 실행될 코드 블럭을 정의한다. 기본 메소드 7개 모두 인수로 전달될 수 있다. 위의 기본 action들의 정의에서도 볼 수 있듯이, before 메소드에 의해서 전달되는 블럭은 객체가 로드(load_object or load_objects)와 데이터베이스 처리 혹은 response 사이에서 실행되게 된다. (action 이전에 실행되는 before_filter와는 다른 것이므로 혼동하지 말자.)
- before :show, :edit do
- @page_title = current_object.title
- end
위의 코드는 show, edit 액션에서 response 직전에 @page_title이라는 인스턴스 변수를 설정하라는 것을 의미한다.
after(*events, &block)
after 메소드는 특정 action의 뒤에서 실행될 코드 블럭을 정의한다. 위의 기본 action들의 정의를 살펴보면, after 메소드는 create, update, destroy에서만 정의된 것을 볼 수 있다. after 메소드에는 다음 두 종류의 인수가 전달될 수 있다.
- :create, :update, :destory : 각각의 데이터베이스 처리가 성공한 후에 실행되게 되는 블럭을 정의한다.
- :create_fails, :update_fails, :destroy_fails : 각각의 데이터베이스 처리가 실패했을 경우, 실행할 블럭을 정의한다.
- after :destroy_fails do
- flash[:error] = '삭제하는데 실패하였습니다.'
- end
위의 코드는 destory action에서 데이터베이스 처리가 실패한 경우, flash 메시지를 설정하는 소스 코드이다.
response_for(*actions, &block)
response_for 메소드는 기본 response 대신에 사용될 코드 블럭을 정의한다. 블럭은 format 인수를 가질 수도 있고, 갖지 않을 수도 있다. 블럭이 format 인수를 가질 경우에는 Rails의 respond_to 메소드와 동일한 문법이 적용된다. 블럭이 인수를 갖지 않는 경우에는 HTML에 대한 응답만을 의미한다.
- response_for :index do |format|
- format.html
- format.atom do
- headers['Content-Type'] = 'application/atom+xml; charset=utf-8'
- render :action => 'atom', :layout => false
- end
- end
위의 코드는 index action의 기본 response 대신에 atom 확장자를 지원하는 형태로 기능을 변경하였다.
- response_for :new do
- render :action => 'edit'
- end
- response_for :new do |format|
- format.html { render :action => 'edit' }
- end
위의 두 코드는 동일한 기능을 한다.
지금까지 make_resourceful 블럭에서 기본 action을 customizing하기 위해 사용되는 몇 개의 Builder 메소드를 살펴보았다. 2부에서는 리소스의 인스턴스 변수에 대한 간단한 접근방식을 제공하는 Accessors와 쉬운 경로설정을 돕는 URL helper들에 대해서 살펴볼 것이다. 또한 hidden 파라미터를 통해서 response의 동작 방식을 변경하는 법을 알아볼 것이다.
이 글은 스프링노트에서 작성되었습니다.



::: 사람과 사람의 교감! 人터넷의 첫 시작! 댓글을 달아주세요! :::