Ruby on Rails는 웹 애플리케이션 개발 세계로 최근에 진입했다. 하지만 아직까지는 베타 버전이다. Rails는 대부분의 웹 애플리케이션의 구현을 자동화한다. 더욱이 웹 애플리케이션의 개별 양상들을 수행하는 Free Software 라이브러리들과 비교해 볼 때, Rails는 모든 양상에 맞는 통합된 툴 세트들을 포함하고 있다.
Ruby on Rails는 매력적인 웹 개발 도구이다. 우선 기초부터 공부해보자.
Rails는 Ruby의 풀-스택, 오픈 소스 웹 프레임웍으로서 애플리케이션 구현이 쉽고, 코드는 적게 든다.
풀-스택(full-stack) 프레임웍이란 Rails의 모든 레이어들이 함께 작동하도록 구현되었기 때문에 처음부터 끝까지 한 언어를 사용할 수 있다는 것을 의미한다. Rails 안에서 모든 것(템플릿, 제어 흐름, 비즈니스 로직)은 Ruby로 작성된다. Rails는 리플렉션과, 설정 파일과 주석을 통한 런타임 확장을 선호한다.
이 글에서 Rails의 컴포넌트와 작동방법을 설명하겠다.
Rails
Rails를 이해하기 위해서는 모델/뷰/컨트롤러(MVC) 아키텍쳐를 알아야 한다. 이 방식이 Rails에만 적용되는 것은 아니지만, Rails는 매우 명확하고 집중적으로 MVC 방식을 사용한다. MVC 패러다임을 사용하지 않는다면 Rails는 별로 유용하지 않다.
모델
Rails 애플리케이션의 모델은 이 애플리케이션이 사용하는 기반 데이터베이스이다. 사실, 많은 경우, Rails 애플리케이션은 관계형 데이터베이스 관리 시스템(RDBMS)에서 지정한 방식으로 데이터 조작을 수행하는 수단이다.
Rails의 중앙 컴포넌트는 ActiveRecord 클래스이다. 이것은 관계형 테이블을 Ruby 객체들로 매핑한다. 거기에서 데이터가 컨트롤러에 의해 조작되고 뷰에서 나타난다. Rails 애플리케이션은 유비쿼터스 MySQL 데이터베이스를 사용하지만, 바인딩은 수 많은 기타 RDBMS (IBM?? DB2?羚颱?)에도 존재한다.
원한다면, Ruby코드를 추가하여 애플리케이션 모델 내에서 추가 밸리데이션을 수행하고, 데이터 관계를 적용하거나 기타 액션들을 실행한다. 애플리케이션의 app/models/ 디렉토리에 있는 Ruby 파일들은 ActiveRecord의 다양한 밸리데이션 메소드를 호출할 수 있다. 하지만 이 모델 코드를 스텁으로서 남겨두고 데이터를 저장하고 있는 RDBMS에 의존한다. 예를 들어, 이 예제에서 내가 구현하는 애플리케이션에는(적어도 초기에는) 뼈대 모델 코드만 포함된다.
Listing 1. app/models/contact.rb 뼈대 모델
class Contact < ActiveRecord::Base
end
컨트롤러
컨트롤러는 추상 형식으로 애플리케이션 로직을 수행한다. 애플리케이션의 app/controllers/ 디렉토리에 있는 Ruby 스크립트는 모델 데이터를 변수에 로딩하고 이를 저장하여 이를 조작한다. 하지만 컨트롤러는 데이터가 구체적으로 표현되고 사용자에 의해 입력되는 방식과는 관계가 없다. 일반적인 MVC 다이어그램에서 같은 컨트롤러와 다중 인터랙션이 허용된다. 원시 GUI, 웹 인터페이스, 시각 장애자를 위한 스피치 인터페이스는 모두 같은 컨트롤러와 인터랙팅한다.
하지만 Rails가 그렇게 일반적인 것은 아니다. 대신 웹 페이지 내에 데이터를 제공하고 모으는데 집중한다. 웹 페이지의 레이아웃(색상, 폰트, 테이블, 스타일시트)을 수정할 수 있다.
뷰
Rails 뷰는 Ruby 코드를 저장한 곳에 있다. Rails에는 순수 HTML을 임베디드 Ruby 코드와 결합하는 .rhtml 파일용 템플릿 언어를 포함하고 있다. Rails 애플리케이션 스크린 외형은 일반적으로 CSS 스타일시트로 제어된다. .rhtml 포맷은 HTML에서 발전한 것이다. 실제로 HTML도 유효한 RHTML 템플릿이 될 수 있지만 스크립팅 제어 외에는 이점이 별로 없다.
RHTML은 진정한 템플릿 포맷이다. HTML에 코드를 삽입하는 단순한 방식만이 아니다. 보다 강력한 접근 방식이다. PHP에 익숙하다면 PHP와 Smarty 템플릿을 비교해보라. 임베디드 스크립팅은 코드와 인터프리팅 되지 않은 HTML을 그저 섞기만 한다. 클라이언트에게 무엇인가를 보여줄 때 print 문장을 만들어내는 것은 여전히 코드에서 수행하고 있다.
반대로 템플릿 엔진은 커스텀 세트의 태그를 HTML에 추가한다. 조건, 루프, 기타 로직을 강화된 HTML 마크업의 일부로서 나타낼 수 있다.
코드 만들기
Rails가 제공하는 툴은 기본적으로 코드 생성기 세트이다. 나는 워크스페이스와 IDE를 사용해야 하는 개발 환경 보다 이 방식을 훨씬 좋아한다. 직접적인 프로그래밍 작업을 줄여준다. 스카폴딩(scaffolding)을 무료로 제공하여 직접 코딩하는 부분이 줄어든다.
스카폴딩(scaffolding)은 Rails의 기본 개념이다. 매우 간단한 애플리케이션들의 경우, Rails가 동적으로 클라이언트 HTML 페이지를 만들기 때문에 직접 코딩하지 않아도 된다. 코드 생성의 첫 번째 단계는 미가공 스카폴딩을 만드는 것이다. 그런 다음 커스터마이징 할 수 있는 보다 구체적인 컨트롤러, 뷰, 모델을 만들 수 있다. 하지만 시작부터 그렇게 많은 것을 만들 필요는 없다.
Rails는 고정되고 일반적인 파일 구성에 의존하지만 이 구성은 비교적 융통성이 없다. 다른 파일 및 코드 구성을 실행하려고 한다면 Rails 환경과의 사투를 벌여야 한다. Rails가 제공하는 구성을 따르지 않는 이유가 없다. 프레임웍을 처음부터 디자인한다면 디렉토리 이름과 구조가 여러분이 선택했던 것과 완전히 다르다.
애플리케이션 구현하기
Ruby on Rails 웹 사이트에서 여러 튜토리얼이 제공된다.(참고자료) 이 글에서 제공하는 샘플 애플리케이션으로도 Rails 애플리케이션 구현을 바로 시작할 수 있다. 하지만 충분히 이해가 되지 않았다면 튜토리얼을 공부하기 바란다.
샘플 애플리케이션은 기본적인 주소록이다. 다음은 애플리케이션을 구현하는 일반적인 단계이다.
- (MySQL 데이터베이스와 테이블을 구현한 곳에) 모델을 구현한다.
- (기본 코드와 디렉토리와 함께) 애플리케이션을 구현한다.
- Rails를 실행한다. (데이터베이스 액세스를 설정한다.)
- 콘텐트를 만든다. (스카폴드 모델과 컨트롤러를 만들고 컨트롤러에게 스카폴트를 사용할 것을 명령한다.)
이제 각 단계별로 자세하게 설명하겠다.
AddressBook 모델 구현하기
첫 번째로 해야 할 일은 데이터와 그 데이터가 상주할 데이터베이스를 만드는 것이다. 기술적으로 볼 때 이 단계는 초기에 배치되어서는 안되지만 일찍 만들어야 한다. 애플리케이션 코드, 심지어 생성된 코드 전에 데이터베이스를 만들어야 하기 때문이다. MySQL로 데이터베이스를 구현하고 이 데이터베이스 안에서 첫 번째 테이블을 만든다. (MySQL과 기타 RDBMS를 설치하여 실행하는 방법은 다른 문서들을 참조하라.)
MySQL을 설치하여 사용해 보자.
Listing 2. MySQL 데이터베이스와 테이블 구현하기
[~/Sites]$ cat AddressBook.sql
CREATE DATABASE IF NOT EXISTS AddressBook;
USE AddressBook;
CREATE TABLE IF NOT EXISTS contacts (
id smallint(5) unsigned NOT NULL auto_increment,
name varchar(30) NOT NULL default '',
created_on timestamp(14) NOT NULL,
updated_on timestamp(14) NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY name_key (name)
) TYPE=MyISAM COMMENT='List of Contacts';
[~/Sites]$ cat AddressBook.sql | mysql
첫 번째 테이블에서 두 가지를 주목하자. 모든 테이블은 정확히 그 이름을 가진 id 칼럼을 가져야 한다. Rails는 이 프라이머리 키 칼럼 id 를 태스크의 기록 저장과 참조에 사용한다. created_on 필드와 updated_on 필드는 필요 없다. 하지만 이것을 포함시킨다면 Rails는 이들을 자동으로 관리한다. 대부분의 경우 타임스탬프를 사용해도 무방하다. 따라서 여러분이 추가했던 유일한 " 진짜 " 데이터는 주소록의 contact의 이름이다.
또 하나 이상한 것은 Rails가 다양한 것에 단수와 복수 이름들을 사용한다는 점이다. 다양한 아이템들은 사용과 정황에 따라 단수와 복수 버전 사이에서 재명명 된다. 테이블 이름은 복수형을 사용한다. 불규칙 복수형을 가진 단어로는 시험해보지 않았다. datum 과 data같은 단어들은 Rails의 골칫거리이다.
AddressBook 애플리케이션 구현하기
인터랙팅 할 데이터베이스가 생겼으니 AddressBook 애플리케이션을 구현한다. 첫 번째 단계는 rails를 실행하여 기본 디렉토리와 스카폴드 코드를 만드는 것이다.
Listing 3. 기본 코드와 디렉토리 생성하기
[~/Sites]$ rails AddressBook
create
create app/apis
create app/controllers
create app/helpers
create app/models
create app/views/layouts
create config/environments
create components
[...]
create public/images
create public/javascripts
create public/stylesheets
create script
[...]
create README
create script/generate
create script/server
[...]
rails를 실행하여 아웃풋을 압축했다. 생략된 라인은 이전에 구현했던 다양한 파일과 코드를 생각하면 된다. 시스템에서 시험해 보고 모든 생성된 파일들을 검사하라. 몇 가지 가장 중요한 파일과 디렉토리를 코드에 디스플레이 했다.
Rails 실행하기
AddressBook/ 디렉토리를 구현하고 자식이 필요하면 초기 설정을 수행하면 된다. 우선 YAML 설정 파일을 다음과 같이 수정하여 데이터베이스를 설정한다.
Listing 4. 데이터베이스 액세스 설정
[~/Sites]$ cd AddressBook
[~/Sites/AddressBook]$ head -6 config/database.yml # after editing
development:
adapter: mysql
database: AddressBook
host: localhost
username: some_user
password: password_if_needed
마지막으로 데이터를 제공해야 한다. Rails에는 웹 서버 기능을 하는 WEBrick이 있다. 이것은 이 실험에 아주 완벽하게 쓰인다. 또한 Ruby on Rails 웹 사이트에서 지시를 따라 Apache 또는 다른 서버를 설정하여 FCGI(또는 CGI도 가능하지만 느리다.)를 통해Rails 애플리케이션을 제공한다.
Listing 5. WEBrick 서버 실행하기
[~/Sites/AddressBook]$ ruby script/server -d
=> Rails application started on http://0.0.0.0:3000
[2005-03-21 17:57:38] INFO WEBrick 1.3.1
[2005-03-21 17:57:38] INFO ruby 1.8.2 (2004-12-25) [powerpc-darwin7.8.0]
콘텐트 구현하기
이전 단계들에서도 충분히 WEBrick 포트에서 웰컴 페이지를 볼 수 있다. 예를 들어, 내 로컬 시스템에서 http://gnosis-powerbook.local:3000/을 볼 수 있다. 하지만 커스텀 데이터베이스를 조작하려면 더 많은 코드를 만들어야 한다. generate 스크립트로 이를 수행한다. 이것은 AddressBook/ 애플리케이션 디렉토리에서 구현된 것이다.
Listing 6. 스카폴드 모델과 컨트롤러의 코드 생성
[~/Sites/AddressBook]$ ruby script/generate model contact
exists app/models/
exists test/unit/
exists test/fixtures/
create app/models/contact.rb
create test/unit/contact_test.rb
create test/fixtures/contacts.yml
[~/Sites/AddressBook]$ ruby script/generate controller contact
exists app/controllers/
exists app/helpers/
create app/views/contact
exists test/functional/
create app/controllers/contact_controller.rb
create test/functional/contact_controller_test.rb
create app/helpers/contact_helper.rb
이제 상응하는 테이블 이름에서 복수 contacts 보다는 단수 contact를 사용해야 한다. 생성된 파일을 편집하여 컨트롤러가 스카폴드를 사용할 수 있도록 한다.
Listing 7. 컨트롤러에게 스카폴드를 사용하도록 명령하기
[~/Sites/AddressBook]$ cat app/controllers/contact_controller.rb
class ContactController < ApplicationController
model :contact
scaffold :contact
end
이제 http://rails.server/contact/(나의 테스트 케이스에서는, http://gnosis-powerbook.local:3000/contact/이다.)에서 데이터베이스의 콘텐츠를 보고 수정할 수 있다. 데이터를 입력하면 그림 1과 그림 2처럼 보여진다.
Figure 1. 연락처 리스트
Figure 2. 연락처 편집
커스터마이징 가능한 콘텐트 구현하기
이전 코드는 완전히 작동하는 인터페이스를 구현하여 데이터베이스를 보고 수정하지만 모든 포맷팅, 표현, 비즈니스 로직은 Rails에서 수행된다. 더 많은 커스터마이징을 원한다면 더 많은 코드가 필요하다. 이제 필요한 것은 Rails에서 모든 스카폴딩을 명확하게 작성하는 것이다.
Listing 8. 명확한 컨트롤러와 뷰의 코드 생성
[~/Sites/AddressBook]$ ruby script/generate scaffold Contact
dependency model
[...]
create app/views/contacts
exists test/functional/
create app/controllers/contacts_controller.rb
create test/functional/contacts_controller_test.rb
create app/helpers/contacts_helper.rb
create app/views/layouts/contacts.rhtml
create public/stylesheets/scaffold.css
create app/views/contacts/list.rhtml
create app/views/contacts/show.rhtml
create app/views/contacts/new.rhtml
create app/views/contacts/edit.rhtml
할 일이 약간 더 남아 있다. 몇 가지를 수정해 보자. (이 코드는 복수 형태의 contacts로 갔다. 이유는 잘 모르겠다. 지금으로선 이를 받아들이는 수 밖에 없다.) CSS에서 색상과 폰트를 변경한다.
Listing 9. 캐스케이딩 스타일시트 설정
[~/Sites/AddressBook]$ head -8 public/stylesheets/scaffold.css
body { background-color: #ffe; color: #338; }
body, p, ol, ul, td {
font-family: verdana, arial, helvetica, sans-serif;
font-size: 13px;
}
td { border: 1px solid; }
a { color: #eef; background-color: #446; }
a:hover { color: #fff; background-color:#000; }
이제 코드가 있으니 contacts_controller.rb가 무엇을 할까? 이전 코드에서 본 contact_controller.rb 보다 분명하고 설정 가능하다. 컨트롤러는 다음과 같다.
Listing 10. 컨트롤러: app/controllers/contacts_controller.rb
class ContactsController < ApplicationController
def list
@contacts = Contact.find_all
end
def show
@contact = Contact.find(@params['id'])
end
def create
@contact = Contact.new(@params['contact'])
if @contact.save
flash['notice'] = 'Contact was successfully created.'
redirect_to :action => 'list'
else
render_action 'new'
end
end
컨트롤러의 주요 역할은 데이터를 변수에 로딩하는 것이다. Contact 객체는 이 모델이 제공하는 ActiveRecord 객체 관계형 매핑이다. 변수 @contacts 또는 @contact에는 해당 메소드에 데이터가 주어진다. 이 메소드들은 http://rails.server/contacts/show/2 같은 URL에 의해 참조된다. (이것은 "2"의 id를 가진 연락처를 보여준다.)
이 예제의 컨트롤러는 뷰에 연결된다. 데이터 값들을 사용하는 RHTML 파일들은 컨트롤러에 의해 변수로 로딩되었다. 다음은 list 뷰의 일부이다.
Listing 11. 리스트 뷰: app/views/contacts/list.rhtml
[...]
<% for contact in @contacts %>
<tr>
<% for column in Contact.content_columns %>
<td><%=h contact.send(column.name) %></td>
<% end %>
<td><%= link_to 'Show', :action => 'show', :id => contact.id %></td>
<td><%= link_to 'Edit', :action => 'edit', :id => contact.id %></td>
<td><%= link_to 'Destroy', :action => 'destroy', :id => contact.id %></td>
</tr>
<% end %>
[...]
ContactsController.list 메소드는 @contacts 변수를 로딩하고, RHTML의 플로우 컨트롤 태그는 이 어레이에서 개별 기록들을 추출한다.
모델 변경하기
초기 모델은 오직 이름만 포함했다. 이 모델을 확장하여 전화번호, 주소, 이메일 같은 데이터를 추가할 추가하지는 않겠다. 일반적으로 이 데이터는 contacts 테이블에 관련된 왜래 키를 가진 자식 테이블에 상주한다. Rails 모델은 커스텀 코드와의 관계를 다음과 같이 나타낸다.
Listing 12. 커스텀 코드: app\models\phone.rb
class Phone < ActiveRecord::Base
belongs_to :contact
end
끝내기 전에 데이터 모델을 약간 수정하여 이것이 애플리케이션에 어떤 영향을 미치는지를 보자. 우선 칼럼을 추가해본다.
Listing 13. first_met date 를 모델에 추가하기
$ cat add-contact-date.sql
USE AddressBook;
ALTER TABLE contacts ADD first_met date;
$ cat add-contact-date.sql | mysql
기반 모델 http://rails.server/contact/을 변경했으므로 별다른 노력 없이 간단히 적용된다. 컨트롤러와 뷰는 이 모델에 기반하여 완전히 자동화된다. 하지만 http://rails.server/contacts/의 애플리케이션 버전들은 다르다.
list뷰는 자동으로 모든 칼럼들을 찾는다. Contact.content_columns를 템플릿 루프의 일부로서 추가한다. 하지만 edit뷰는 이미 만들어졌고, 새로운 데이터 필드를 추가해야 한다.
Listing 14. Edit 뷰: app/views/contacts/edit.rhtml
<h1>Editing contact</h1>
<%= error_messages_for 'contact' %>
<%= start_form_tag :action => 'update' %>
<%= hidden_field 'contact', 'id' %>
<p><label for="contact_name">Name</label><br/>
<%= text_field 'contact', 'name' %></p>
<p><label for="first_met">Known Since</label><br/>
<%= date_select "contact", "first_met", :use_month_numbers => false %></p>
<input type="submit" value="Update" />
<%= end_form_tag %>
<%= link_to 'Show', :action => 'show', :id => @contact.id %> |
<%= link_to 'Back', :action => 'list' %>
여러분이 수정한 애플리케이션이 어떻게 보이는가? 디폴트와 큰 차이가 없다. 하지만 그림 3과 4에서 변경이 적용되었다.
그림 3. 연락처 리스팅
Figure 2. 연락처 편집
결론
Rails는 빠른 웹 애플리케이션 개발을 보장한다. 완벽한 프레임웍에는 웹 기반 애플리케이션에 가장 많이 사용되는 액션들을 수행하는 매우 유용한 클래스들과 메소드가 포함되어 있다.
무엇보다도 Rails는 우리가 필요로 하는 모든 지원 코드를 갖추었기 때문에 완전한 "Rails 방식"이 가능하다. 이는 다른 툴킷과 프레임웍에 월등히 앞서는 부분이기도 하다.
참고자료
- "루비(Ruby) 프로그래밍" (developerWorks, July 2001)(한글)
- "Deploy an application with Cerise Web server" (developerWorks, February 2005)
- Ruby on Rails
- IBM DB2, Database Drivers page.
- ten-minute video
- MVC architectural paradigm.
- developerWorks Linux zone.
- developerWorks blogs.
- Browse for books
- Order the no-charge SEK for Linux
- IBM trial software,