제공: 한빛 네트워크
출처: Ruby on Rails : 초고속 웹 개발의 시작 Chapter 4.
스캐폴딩(비계)은 수세기 동안 건축 공사에서 편리하게 가건물을 올리고 내릴 수 있도록 많은 도움을 주고 있다. 프로그래머 역시 개발 중인 코드가 제 모습을 갖기 전까지 임시 스캐폴딩 코드를 코드 구조를 잡는데 사용한다. 레일즈는 코딩 초기 단계에서 보다 생산적인 코딩이 가능하도록 스캐폴딩을 자동화하였다.
대략 5분 정도에 불과한 루비 온 레일즈 동영상들에도 스캐폴딩에 관한 내용은 빠지지 않고 등장하는 것을 알 수 있다. 어떤 이는 레일즈에서 스캐폴딩은 당장 없어져야 한다고 말하기도 한다. 상대적으로 스캐폴딩으로부터 얻는 이득이 부자연스러운 면이 있기 때문이다. 이런 주장이 틀린 것은 아니다. 스캐폴딩 사용자 인터페이스는 정말 볼품 없고, 불완전하다. 그럼에도 불구하고 스캐폴딩은 단지 소개 정도로만 그치기에는 훨씬 많은 장점이 있다.
- 코딩 속도를 높임으로써 사용자 피드백을 빠르게 받을 수 있다.
- 보다 빠르게 개발을 완료할 수 있게 동기를 준다.
- 생성된 코드를 보며 레일즈의 동작 원리를 배우게 된다.
- 스캐폴딩을 생성하는 것에서부터 프로젝트 개발을 시작할 수 있다.
- 데이터베이스 구조가 바뀌면 자동으로 수정 내역이 반영되는 메타프로그래밍을 사용할 수 있다.
이번 장에서는 사진 공유 애플리케이션에서 기본 사용자 인터페이스를 스캐폴딩을 사용하여 만드는 방법을 알아볼 것이다. 그리고 다음 장에서는 이를 기반으로 풍부한 기능을 덧붙여 나갈 것이다.
01_ 스캐폴드 메소드 사용하기
사진 공유 애플리케이션에 사용할 사진, 분류, 슬라이드쇼, 슬라이드 등에 대한 모델 정의는 이미 앞장에서 알아보았고, 레일즈 콘솔로 액티브 레코드 객체를 사용하여 스키마를 어떻게 관리할 수 있는지도 알아보았다. 이제 스캐폴딩으로 이들 클래스에 대한 사용자 인터페이스를 만들어 볼 차례이다. 스캐폴딩이 완전한 애플리케이션을 생성해주지는 않으나 충분한 기반을 마련해준다. 우리는 완벽한 실제 서비스 수준의 애플리케이션을 만드는 것이 목적이 아니라 좀더 유리한 환경에서 빠른 개발을 하는 것이 주 목적임을 기억하자.
사진 리스트
웹에서 사용자가 사진 리스트를 관리할 수 있는 기능을 만들어보자. 앞서 만들었던 슬라이드, 슬라이드쇼, 분류, 사진에 대한 객체 모델과 대응되는 테이블 및 데이터베이스가 제대로 동작하는지 확인해본다. 서버를 시작하지 않았다면 ruby script/server 명령으로 서버를 시작한다. 웹 브라우저 주소창에 http://localhost:3000/을 입력해서 제대로 동작하는지 확인한다. 레일즈 환영 메시지가 나오면 제대로 동작하는 것이다. 이제 스캐폴딩을 만들어보자.
스캐폴딩을 만들려면, scaffold 메소드를 컨트롤러에 추가해야 한다. 그러므로 가장 먼저 Photos 컨트롤러를 만든다.
ruby script/generate controller Photos
photo_controller.rb 파일을 열어 다음과 같이 scaffold :photo를 추가한다.
class PhotosController < ApplicationController
scaffold :photo
end
이것으로 해야 할 작업은 다 한 것이다. 나머지는 레일즈가 알아서 한다. 웹 브라우저로http://localhost:3000/photos를 열어 스캐폴딩이 제대로 동작하는지 확인해보자. 사진 리스트와 새 사진 추가하기, 편집, 사진 보기 등의 링크를 확인할 수 있다. scaffold :photo,이 간단한 문장으로 [그림 4-1]에 나온 모든 페이지가 구현되었다. 스캐폴딩은 첫눈에 봐도 필요한 것은 이미 모두 갖춘 채로 컨트롤러와 뷰를 생성하였다. 다시 한번 언급하지만, 스캐폴딩은 완전한 코드를 만들어주는 것이 아니라, 코딩을 시작할 수 있는 튼튼한 발판을 제공해 주는 것이다. 곧 스캐폴딩을 어떻게 동작하는지 알아보게 될 것이다.
메타프로그래밍
scaffold :photo는 마술이라 할 수 있을 정도로 많은 일을 해준다. scaffold는 Action Controller의 메소드이다.
:photo는 레일즈가 이 스캐폴드에서 사용할 액티브 레코드 모델에 대한 심벌이다. 이 메소드를 추가하면, 레일즈는 [표 4-1]에 나와있는 9개의 메소드들을 컨트롤러에 추가한다. 이 중 4개는 뷰를 출력한다. 레일즈가 추가해준 이 메소드들은 모델 객체에 기반한 액티브 레코드 모델에 대하여 간단한CRUD 인터페이스를 모두 구현한다. 모델의 @@content_columns 속성에는 각각의 데이터베이스 테이블 컬럼에 대한 정보를 담고 있다.
[표 4-1] scaffold :target 메소드는 컨트롤러에 다음과 같은 메소드를 추가한다
메소드 |
목적 |
뷰 |
index |
환영 페이지를 표시(렌더링)한다. 기본적으로 index는 컨트롤러에서 list 액션으로 리다이렉트한다. 또한 사용자가 컨트롤러만 지정하고, 액션을 지정하지 않은 상태로 요청을 보내오면 레일즈는 index 액션을 기본 액션으로 취한다. |
아니오 |
list |
스캐폴드에 대한 모델 객체들을 리스트 형태로 렌더링한다. |
예 |
create(target) |
target 객체로 새 액티브 레코드 객체를 생성하고 저장한다. |
아니오 |
new |
컨트롤러 객체를 새로 생성하는 뷰를 렌더링한다. |
예 |
edit(id) |
id로 구분되는 객체를 편집하는 뷰를 렌더링한다. |
예 |
update(id) |
id로 구분되는 액티브 레코드 객체를 갱신한다. |
아니오 |
show(id) |
객체에 대한 뷰를 렌더링한다. |
예 |
destroy(id) |
id로 구분되는 객체를 삭제한다. |
아니오 |
render_scaffold |
.rhtml 뷰가 존재하지 않을 경우, 기본 뷰를 렌더링한다. |
N/A |
[표 4-1]에 나열된 대부분의 메소드들은 render_scaffold 메소드를 호출하여 필요한 뷰가 추가되었는지 검사한다. (레일즈 뷰는 컨트롤러 메소드와 같은 이름을 같는다) 따라서 따로 정의한 뷰가 있으면 레일즈는 이를 사용하고, 없으면 기본 뷰를 제공한다.
02_ 스캐폴딩 바꾸기
코드 생성 중심의 여타 프레임워크에서는 생성된 코드 일부분을 수정하기 시작하면, 수정된 부분을 포함해 나머지 부분에 관한 모든 책임을 프로그래머가 지게 된다. 그러나 레일즈에서는 다르다. 레일즈는 특정 뷰나 컨트롤러 메소드를 수정하더라도 나머지 스캐폴딩에는 전혀 영향을 미치지 않는다. PhotoController 클래스에 index 메소드를 이용하여 제목 페이지를 추가해보자.
class PhotosController < ApplicationController
scaffold :photo
def index
render_text("Welcome to Photo Share"s Title Page")
end
end
이제 http://localhost:3000/photos/index를 웹 브라우저에서 호출해보자. [그림 4-2]처럼“Welcome to Photo Share’s Title Page”메시지가 브라우저에 나타날 것이다. 이 결과는 스캐폴딩이 기본으로 제공해주는 index 메소드를 재정의했다는 의미가 된다.
http://localhost:3000/photos/list를 호출해보면 나머지 스캐폴딩은 전혀 영향이 없음을 확인할 수 있다. 레일즈에서는 컨트롤러 스캐폴딩에 전혀 영향을 주지 않고, 뷰를 대체할 수 있다. 이제 show 액션에 대한 뷰를 바꿔보자. app/views/photos/show.rhtml 파일을 만들고 다음 코드를 입력해보자.
Show Photos
filename: <%= @photo.filename %>
<%= link_to "list of photos", :action => "list", :id => @photo %>
[그림 4-3]에서 새로 만든 뷰를 확인할 수 있다. 이전과 마찬가지로 다른 부분에 전혀 영향을 주지 않고, 뷰만 바꿔볼 수 있다. 스캐폴딩은 따로 재정의하지 않는 이상 그 형태를 그대로 유지한다. 따라서 하나씩 재정의하면서 스캐폴딩은 자연스럽게 자취를 감추게 된다.
스캐폴딩은 동적이다
레일즈 스캐폴딩은 데이터베이스 스키마와 관련된 작업을 하면서 손쉽게 사용자 인터페이스를 만들어낼 수 있다. 이를 통해 사용자는 이 애플리케이션에서 필요한 자신의 데이터를 바로 확인할 수 있게 된다. 먼저 데이터베이스 테이블에 사진에 대한 타임스탬프, 썸네일, 설명 컬럼을 추가해보자. 이 컬럼들을 추가하는 것은 마이그레이션으로 할 수 있다. 다음과 같이 add_photo_columns 마이그레이션을 만든다.
ruby script/generate migration add_photo_columns
db/migrate 디렉토리 밑에 생성된 마이그레이션을 다음과 같이 수정해보자.
class AddPhotoColumns < ActiveRecord::Migration
def self.up
add_column "photos", "created_at", :datetime
add_column "photos", "thumbnail", :string
add_column "photos", "description", :string
Photo.find(:all).each do |photo|
photo.update_attribute :created_at, Time.now
photo.update_attribute :thumbnail, photo.filename.gsub(".", "_m.")
end
end
def self.down
remove_column "photos", "created_at"
remove_column "photos", "thumbnail"
remove_column "photos", "description"
end
end
이 마이그레이션 스크립트는 사진 테이블과 그 데이터를 갱신한다. 이제 rake migrate 명령으로 마이그레이션을 실행하고 웹 브라우저 주소창에 http://localhost:3000/photos/list를 호출해보자. [그림 4-4]처럼 새로 추가한 컬럼들이 나타난다. 이처럼 스캐폴딩은 뷰를 자동으로 처리한다. 따라서, 스캐폴딩을 이용하면, 사용자 인터페이스를 신경 쓰지 않고 데이터베이스 스키마와 모델에 집중해서 작업할 수 있다.
장단점
scaffold 명령으로 어떻게 스캐폴딩을 사용할 수 있는지 알아보았다. 코드 생성을 주로 하는 기타 프레임워크에 비해 스캐폴딩은 장점이 많다.
- scaffold 명령은 동적이다. 데이터베이스 스키마를 변경하면 사용자 인터페이스는 자동으로 이를
감지하고 반영한다.
- 전체 스캐폴딩을 다 고려하지 않고도, 필요한 컨트롤러 메소드나 뷰만 재정의할 수 있다.
- scaffold 명령은 간결하다. 단 한 줄의 코드로 많은 것을 할 수 있다.
일반적으로 레일즈의 메타프로그래밍은 기존 코드를 생성하는 방식에 비해 획기적인 장점을 제공한다. 특히, 동적인 스캐폴딩은 주변 변화에 즉각적으로 반응한다. 그러나 이러한 메타 프로그래밍 방법도 단점이 없는 것은 아니다.
- 어떤 일이 일어나는지 정확히 알 수 없다. 레일즈나 스캐폴딩을 배울 때 실제 돌아가는 코드가 숨
겨져 있어 가장 두드러지는 단점이 된다.
- 레일즈의 이후 버전에서 스캐폴딩의 동작 방식이 바뀔 수 있다. 즉, 스캐폴딩의 동작 방식을 예상
할 수 없다.
- 추가적인 개발에 있어 스캐폴딩은 더 이상 시작지점으로 사용할 수 없다.
이러한 이유로 레일즈는 스캐폴딩 코드를 생성해주는 기능도 갖고 있다. 이제 스캐폴딩 코드 제너레이터에 대해 알아보자.