웹2.0과 루비 온 레일즈 by thinkr

이 글은 제가 월간 마이크로 소프트웨어 2006년 10월호 Special Report에 "웹2.0을 위한 루비 프로그래밍"이라는 제목으로 기고한 글의 원문입니다. 잡지사의 편집 과정에서 일부 잡지의 내용과 다를 수 있습니다.

[스페셜 리포트 | 2부] 웹 2.0을 위한 루비 프로그래밍

웹 2.0루비레일즈(Ruby on Rails)

루비가 관심의 대상으로 부각된 데는 루비에 기반한 웹 애플리케이션 프레임워크인 루비 온 레일즈(Ruby on Rails)가 기여한 바 크다고 해야 할 것이다. 도대체 탄생한지 불과 2년 여 밖에 되지 않은 오픈소스 프레임워크가 자바나 닷넷 등 기존의 다른 주류 프레임워크들의 틈 속에서 이렇게 인기를 누릴 수 있는 이유가 무엇일까? 루비 온 레일즈에 대하여는 이미 본지에서도 몇 차례 소개된 적이 있으므로, 이 글에서는 최근 관심의 대상이 되고 있는 “웹 2.0”과 관련된 기능들을 중심으로 하여 루비 온 레일즈의 기찻길을 걸어보려 한다. 기차가 지나갈 수도 있으므로 조심하면서 따라 걷다 보면 독자 여러분도 루비 온 레일즈의 은은한 향기를 느낄 수 있을 것이다.

흔 히들 “웹 2.0”이라는 것을 말할 때 “집단지능”이나 “소셜 네트워크(social network)”와 같은 단어를 이야기한다. 또는 복잡계 과학에 관심이 있는 사람이라면 좀 더 거창하게 “창발성(emergence)”이나 “작은 세상(Small World)” 또는 “자기 조직화”와 같은 개념들로 이야기를 풀어나갈 지도 모르겠다. 물론, 필자와 같이 소프트웨어를 만드는 일에 종사하는 사람들이라면 보다 실제적이고도 구체적인 “블로그”나 “태깅” 또는 “에이젝스(Ajax)”와 같은 말들을 떠올리면서 지긋지긋해 하고 있을 수도 있을 것이다.

웹 2.0에 대하여는 이미 본지에서도 특집기사 등을 통해 여러 차례 소개 된 적이 있고, 또 마소 독자라면 저마다 나름대로 한 가락씩 웹 2.0에 대한 뚜렷한 “철학”들을 가지고 있을 터이니, 여기서는 그냥 그런 것이다 정도로만 하고 넘어가기로 하자. 다음의 [표1]은 웹 2.0이라는 화두를 만들어 낸 팀 오라일리(Tim O'Reilly)의 유명한 글 “웹 2.0이란 무엇인가 - 다음 세대 소프트웨어를 위한 디자인 패턴 및 비즈니스 모델”에서 언급하고 있는 웹 2.0의 특징과 그 특징들이 구체적으로 웹 상에서 어떠한 기술들로 구현되는지를 필자 나름대로 한번 정리해 본 것이다. 잠시 후에 실제로 루비 온 레일즈를 사용하여 이들 기술을 구현해 볼 것이다.


[표1] 웹 2.0의 특징과 구현 기술

웹 2.0의 특징

구현 기술

1. 플랫폼으로서의 웹

웹 서비스, 매쉬업(mashup)

2. 집단 지능을 이용한다

블로그, 위키(wiki), 태그(tag), 트랙백(trackback)

3. 다음 인텔 인사이드는 데이터

오픈소스, 매쉬업(mashup)

4. 소프트웨어 릴리즈 주기의 종말

베타(beta) 버전, 동적 언어(dynamic language)

5. 가벼운 프로그래밍 모델

REST(Representation state Transfer)

6. 단일 디바이스를 넘어선 소프트웨어

백엔드 웹(back-end web)

7. 풍부한 사용자 경험

Ajax, RIA(Rich Internet Application)

이제 루비 온 레일즈로 넘어가 보자. 루비 온 레일즈(Ruby on Rails, 이하 “레일즈”)는 한마디로 말해 웹 애플리케이션 프레임워크(Framework)다. 좀 더 풀어 말하자면 루비 언어로 작성된 MVC 기반의 오픈소스 웹 애플리케이션 프레임워크라 할 것이다.

독 자 여러분도 알다시피 이미 세상에는 웹 개발 및 유지보수를 신속하고 효과적으로 할 수 있게 도와주는 많은 웹 애플리케이션 프레임워크들이 나와 있다. 자바 진영에 있는 것들만 보더라도 Struts, Tapestry, Hibernate, Spring, JSF 등 헤아릴 수조차 없을 만큼 여러 가지 프레임워크들이 저마다의 용도와 장점으로 무장하고 사용자들을 기다리고 있다. 마이크로소프트의 .NET 프레임워크는 그 자체가 하나의 거대한 웹 애플리케이션 프레임워크이기도 하다. 이들 프레임워크들 중에는 상용인 것들도 있지만 소스가 공개된 것들도 많다. 물론 MVC 패턴을 따르는 프레임워크들도 넘쳐난다. 그렇다면, 뭐가 다른가? 도대체 레일즈라고 해서 다른 프레임워크들과 다른 것은 뭐가 있을까?

고 민은 잠시 접어두고, 우선 레일즈 프레임워크의 구조를 간단히 살펴보도록 하자. 앞서도 말했지만 레일즈는 MVC 기반의 프레임워크다. MVC는 모델과 뷰, 그리고 컨트롤러로 각각 역할이 구분된 구성요소들이 결합하여 어떤 일을 처리하는 아키텍처 패턴이다. 프레임워크에 따라서는 이들 세 구성요소 중 하나 또는 둘만을 담당하도록 만들어진 것들도 있지만, 레일즈는 이 세 가지 모두를 다룬다.

레 일즈에서 MVC를 담당하는 구성요소들은 각각 별도의 모듈(라이브러리)로 분리되어 있는데, 모델 부분을 담당하는 모듈은 액티브 레코드(Active Record)라고 불리고, 컨트롤러는 액션 컨트롤러(Action Controller), 그리고 뷰는 액션 뷰(Action View)라고 하는 모듈이 각각 담당하고 있다. 레일즈에서 어떤 웹 요청이 웹서버로 들어 왔을 때 그것을 처리하는 과정은 개략적으로 [그림1]과 같다. 그림에서 보듯, 예컨대 http://localhost:3000/main/list와 같이 URL 호출을 하게 되면 main이라는 컨트롤러 내에 있는 list라는 액션(action) 메서드가 실행되게 되고, 이 액션 메서드는 다시 Post라고 하는 모델 객체를 통해 데이터베이스를 액세스하여, 그 결과를 list.rhtml이라는 뷰를 통해 반환하는 것이다.

[그림1] 레일즈 프레임워크에서의 웹 요청 처리 과정

왜 레일즈인가?

그 렇다면 이제 왜 레일즈일까로 다시 돌아와 보자. 어떤 프레임워크든 나름대로 특징과 장단점을 가지고 있을 것이다. 필자가 생각하기에 레일즈의 가장 큰 장점은 생산성이다. 이 때 생산성이라고 하면 개발 시의 생산성은 물론 이미 만들어진 애플리케이션의 유지보수에 있어서의 생산성도 포함하는 개념이다. 레일즈는 애자일(agile) 개발을 지원하며, 프레임워크 자체에서 개발환경과 테스트환경, 그리고 운영환경을 구분하고 있다. 잘 갖춰진 테스트 프레임워크는 테스트 주도적인 개발을 쉽고도 재미있게 해 주며, 무엇보다도 동적인 언어(dynamic language)인 루비의 특성을 백분 활용함으로써 변화가 심한 개발환경에서도 신속하게 적응할 수 있게 해 준다.

또 한가지 레일즈의 특징은 관례(convention)에 따르는 개발방식을 사용한다는 점이다. 구성설정 보다는 관례(Convention over Configuration)라는 모토를 가지고 출발한 레일즈 프레임워크는 개발자들이 레일즈가 정해 놓은 관례(코딩규칙이나 명명규칙 등)를 따르기만 한다면 별도의 구성설정을 거의 하지 않아도 되게 해 준다. 따라서 다른 프레임워크들에서 흔히 볼 수 있는 XML파일 등을 사용한 복잡한 구성설정의 부담이 많이 줄어들게 된다. 예를 들어, Post라는 모델을 만들 때 이 모델 객체와 맵핑된 실제 데이터베이스 테이블의 이름을 posts라고 주고 프라이머리 키를 id라고만 주게 되면 자동적으로 OR맵핑이 이루어지는 것과 같은 식이다.

이 밖에도 DRY(Don't Repeat Yourself)원칙을 고수하여 코드의 중복을 최대한 줄이는 점, 동적인 언어인 루비의 리플렉션(reflection)과 메타프로그래밍(metaprogramming) 기능을 이용하여 유연하면서도 직관적인 코드를 만들어 내는 점 등 얘기하자면 끝도 없겠지만 이 글의 주제를 벗어나는 내용이 될 듯하여 생략하기로 한다. 관심있는 독자는 참고자료를 참조하기 바란다.

레일즈와 웹 2.0

앞 서 간단히 소개한 레일즈의 이런 측면들은 웹 2.0 개발에 적용될 때 더 빛을 발하게 된다. 1년, 2년이 넘도록 계속해서 베타(beta) 서비스가 이루어지고, 그 속에서 사용자들의 요구를 즉시즉시 애플리케이션에 반영해 나가야 하는 상황에서, 레일즈는 그런 니즈(needs)를 가장 잘 수용할 수 있는 프레임워크인 것이다. 이는 레일즈 프레임워크 자체가 하나의 웹 2.0 애플리케이션을 만드는 과정에서 탄생한 것이라는 점에서도 짐작할 수 있다.

특 히, 잘 갖춰진 테스트 프레임워크라든가, Ajax에 대한 부분을 프레임워크 속에 담고 있는 점, 그리고 무엇보다도 액션 팩(Action Pack)과 액티브 레코드(Active Record), 액션 메일러(Action Mailer)나 액티브 웹 서비스(Active Web Service)와 같은 웹 개발의 필수 구성요소들을 모두 갖추고 있으면서 이들 구성요소에 대하여 새로운 기능들을 필요에 따라 바로바로 붙였다 땠다 할 수 있는 유연한 아키텍처 구조는 웹 2.0 기반의 애플리케이션을 만드는 데 있어서 큰 도움을 준다. 레일즈를 사용하여 구축된 대부분의 애플리케이션이 웹 2.0 기반 애플리케이션들이란 사실은 이를 뒷받침해 준다 할 것이다..

이 제 실제로 레일즈를 사용하여 앞서 설명한 웹 2.0의 기능들을 구현해 보도록 하자. 간단한 블로그를 하나 만들고, 그 위에 RSS와 트랙백(trackback), 태깅과 같은 기능들을 구현해 볼 것이다. 물론 이런 블로그나 RSS, 또는 태깅이 있다고 하여 그 자체로 무조건 웹 2.0이라고 말할 수는 없겠지만, 적어도 웹 2.0을 구현하는데 효과적인 도구라는 것은 분명하다 할 것이므로, 여기서는 이들 기능을 구현해 보는데 초점을 맞추기로 하겠다.

간단한 블로그 만들기

웹 2.0 애플리케이션 하면 빠지지 않고 등장하는 것이 바로 블로그(blog)일 것이다. 1인 미디어 시대를 창조한 주역으로서, 블로그는 이제 사람들의 생활에 없어서는 안될 필수적인 도구가 되었다. 여기서도 블로그를 하나 만들어 볼 것이다. 다만, 이 글의 주제가 레일즈에서 웹 2.0적인 부분들을 설명하는 것이므로, 튜토리얼(tutorial) 식의 접근은 하지 않을 것이다. 지면 관계상 해당 기술에 있어서 핵심적이면서도 레일즈에 특징적인 부분만 언급하면서 넘어가도록 하겠다.

사 용자의 컴퓨터에 루비와 레일즈, 그리고 MySQL과 같은 데이터베이스가 이미 설치되어 있다는 가정 하에, 명령행 프롬프트에서 다음과 같은 명령을 실행하여 레일즈 애플리케이션을 하나 생성하자. 애플리케이션의 이름은 간단한(simple) 블로그라는 의미에서 simblog라 주었다.

>rails simblog

이 명령을 실행하면 [그림2]에서 보는 것과 같은 디렉터리 구조가 새로 만들어 지게 된다. 이 디렉터리 구조가 바로 레일즈 웹 애플리케이션의 골격을 이루는 디렉터리 구조이며 또한 레일즈 애플리케이션의 배포단위가 된다. app 디렉터리 아래에 보면 controllers, models, views라는 디렉터리도 보이는데, 이들 디렉터리가 바로 MVC 각각의 코드들이 들어갈 디렉터리들인 것이다.

[그림2] 레일즈 웹 애플리케이션 디렉터리 구조

이 상태에서 여기 디렉터리 속에 모델 객체와 컨트롤러 객체, 뷰를 각각 만들어 넣으면 되는 것이다. 우선 모델 객체를 만들어 보자. 블로그를 만들고 있으므로 Post라는 모델을 하나 만들도록 하자. 그리고 Main이라는 컨트롤러도 하나 만들자.

simblog>ruby script/generate model Product
simblog>ruby script/generate controller Main

이 제 데이터베이스(MySQL을 사용하는 것으로 가정한다)에 simblog_development라는 이름으로 데이터베이스를 하나 추가하고, 앞서 모델을 만들 때 함께 만들어진 액티브 레코드 마이그레이션(migration) 파일을 열어 [리스트1]와 같이 데이터베이스 스키마를 작성한 다음, 명령행에서 rake migrate 명령을 실행시키게 되면, 모든 작업이 완료된다(여기서 rake는 유닉스의 make나 자바의 ant와 같은 루비의 자동화 관리 툴이다).

class CreatePosts < ActiveRecord::Migration
  def self.up
    create_table :posts do |t|
      t.column :title, :string
      t.column :pub_date, :datetime
      t.column :author, :string
      t.column :contents, :text
    end
  end

  def self.down
    drop_table :posts

  end

end

[리스트1] db/migrate/001_create_posts.rb

앞 서 script/generate 명령을 통해 생성된 모델 클래스와 컨트롤러 클래스의 내용은 각각 [리스트2], [리스트3]과 같을 것이다. Post라는 모델 클래스와 MainController라고 하는 컨트롤러 클래스가 각각 만들어 졌다. 뭔가 복잡하고 특별한 것을 기대한 독자라면 실망스러울 지도 모르겠다. 보면 알겠지만 단지 클래스를 열고 닫는 두 줄이 전부다.

class Post < ActiveRecord::Base
end

[리스트2] app/models/post.rb

class MainController < ApplicationController
end

[리스트3] app/controllers/main_controller.rb

이 제 마지막으로 할 일은 컨트롤러에 액션(메서드)을 추가하는 것이다. 컨트롤러의 class문과 end문 사이에 [리스트4]와 같이 한 줄 코드를 입력한 다음, 웹 서버(WEBrick)를 실행시키고, 브라우저를 열어 URL 창에 http://localhost:3000/main/ 이라고 입력하면 [그림3]과 같이 웹 애플리케이션이 작동되는 것을 볼 수 있을 것이다.

class MainController < ApplicationController
 
scaffold :post
end

[리스트4] scaffold 추가(app/controllers/main_controller.rb)

[그 림3]은 비록 외양이 허술하기는 하지만, 그래도 posts 테이블에 대한 CRUD 기능을 갖춘 완전한 애플리케이션이다. 대부분의 웹 애플리케이션이 그 본질에 있어서는 결국 데이터베이스에 대한 CRUD 작업을 수행하는 것이라는 점을 감안한다면, 이렇게 두 세번의 명령과 몇 줄의 코딩만으로 프로그램이 작동한다는 것은 봐줄 만 하다. 더 신기한 것은, 만약 데이터베이스 테이블의 스키마가 변경된다고 하더라도, 별도의 수정이 필요없다는 사실이다. 웹서버를 재시작하거나 할 필요도 물론 없다. 바로바로 반영되어 화면에 표시된다.

[그림3] 다이내믹 scaffold를 사용한 최초의 블로그

이 제 앞의 [그림1]과 맞춰보면 레일즈에서의 웹 요청 처리 과정이 한결 분명해 질 것이다. 다만, 한 가지 의문은 뷰가 어디 있느냐 하는 것이다. 그리고 또 한 가지, 컨트롤러에 대응되는 액션 메서드가 전혀 보이질 않는다. 이 부분이 바로 레일즈의 다이내믹한 측면을 잘 보여주는 부분인데, 웹 요청을 받아 컨트롤러가 실행될 때 우리가 작성한 scaffold라는 메서드가 필요한 액션들을 동적으로 만들어 내는 것이다. 이 한 줄의 코드가 데이터베이스 테이블의 스키마 정보를 읽어서 입력 폼을 만들어 내기까지의 모든 과정에 필요한 코드들 - 예컨대, 액션 메서드와 뷰 코드 - 을 런타임에서 동적으로 생성시킨다. 코드를 생성하는 코드를 런타임에서 만들어 내고 바로 실행시키는 것. 바로 메타프로그래밍인 것이다.

RSS와 트랙백 구현하기

RSS 기능이 없는 블로그는 블로그가 아니라고 할 정도로, RSS는 블로그를 기존의 개인 홈페이지와 구별시켜 주는 중요한 구성요소다. RSS가 무엇이며 어떠어떠한 종류가 있고 또 각각 어떠한 형식을 가지는지 하는 등은 이미 많이 알려져 있으므로, 여기서는 레일즈에서 RSS를 구현하는 방법에 초점을 맞추기로 하자. 어떤 일을 하는 데는 한 가지 이상의 방법이 있다(There’s more than one way to do it)는 Perl의 격언처럼, 많은 부분 Perl의 전통을 계승하고 있는 루비 언어에 기반한 레일즈 역시 어떤 일을 처리하는데는 반드시 한 가지 이상의 방법이 존재하며, RSS의 경우도 마찬가지다.

여 기서는 앞에서 만든 Post 모델을 사용하여, 블로그에 RSS 배포(syndication) 기능을 추가시켜 보기로 하자. 알다시피 RSS는 일정한 형식을 가진 XML 파일이며, 따라서 RSS 피드를 배포한다는 것은 방금 전 만든 Post 모델에서 내용을 추출하여 이것을 RSS 포맷에 맞는 XML 형태로 클라이언트 브라우저에 전송하면 되는 것이다. 루비로 작성된 여러 가지 RSS 관련 라이브러리들 중의 하나를 사용할 수도 있지만, 여기서는 가장 간단하게, 레일즈에서 제공하는 표준 뷰 템플릿을 이용할 것이다. 레일즈에서는 XML 형태의 출력을 위해 흔히 빌더(Builder) 템플릿이라고 부르는 별도의 뷰 템플릿(view template)을 제공하며, 따라서 이 템플릿을 이용하면 쉽게 RSS 처리를 할 수가 있다. 모델 객체는 이미 만들어 졌으므로, 컨트롤러에 RSS 배포를 위한 액션을 하나 만들고, 액션명과 동일한 이름으로 빌더 템플릿(rxml 템플릿)을 만들어 주면 그만이다. 이제 브라우저의 URL 창에 http://localhost:3000/main/rss 라고 입력하면, 예상했던 대로 XML 형식의 RSS 파일이 출력될 것이다.

def rss
  @posts = Post.find(:all, :limit => 10)  # 10개의 포스트만 추출
end

[리스트5] rss 액션 추가(app/controllers/main_controller.rb)

xml.rss('version' => '2.0') do
  xml.channel do

     xml.title "SimBlog RSS Feed Sample"

     xml.link @request.protocol + @request.host_with_port + url_for(:rss => nil)   

     xml.description "this is a rss demo"   

     @posts.each { |post|     

        xml.item do        

            xml.title post.title       

            xml.link @request.protocol + @request.host_with_port
                +url_for(:controller=>"main", :action => "show", :id => post.id)
            xml.description post.contents       

            xml.pubDate post.pub_date

        end   

      } 

   end

end

[리스트6] app/views/main/rss.rxml

RSS 와 함께 블로그를 좀 더 블로그 답게 만들어 주는 기능이 바로 트랙백(trackback)이다. 어떤 블로그와 관련된 글을 자신의 블로그에 올리거나 혹은 다른 블로그에 있는 자신의 블로그와 관련된 글들을 서로 볼 수 있게 함으로써, 블로그 간의 쌍방향 연결을 통해 집단지능을 키워나갈 수 있다는 것이 트랙백이 가진 기능이다.

그 런데 이 트랙백은 기술적으로는 트랙백 핑(ping)이라고 하는 간단한 메시지를 HTTP POST 방식을 통해 상대방 블로그로 보내면, 상대방 블로그에서는 이 POST 핑 요청을 받아서 처리하고 그 처리 결과의 성공 유무를 간단한 XML 양식으로 반환하는 구조로 구성된다. 따라서 트랙백의 구현은 결국 트랙백 핑을 보내는 트랙백 클라이언트 부분과 받은 트랙백 핑을 처리하는 트랙백 서버 부분으로 나뉘게 된다.

레 일즈에서 트랙백을 구현하는 것은 아주 쉽다. 아니, 기본적으로 트랙백을 구현하는 것 자체가 그렇게 어려울 것이 없다. 여기서는 지면 관계상 트랙백 서버를 구현하는 부분만 살펴보기로 하자. 앞서 우리가 만든 Post 모델 각각의 포스트에 대하여 트랙백을 달 수 있도록 하려면, Trackback이라는 모델을 하나 추가로 만들고, 이 Trackback 모델과 Post 모델 간에 연관관계(relationship)를 설정해 주어야 한다. Trackback 모델 역시 앞서 Post 모델을 만드는 것과 같은 방식으로 만들면 되며, 기존의 Post 모델에 다음과 같이 한 줄을 추가하기만 하면 두 모델 간에 일-대-다 연관관계가 자동으로 만들어 지게 된다.

class Post < ActiveRecord::Base
  has_many :trackbacks
end

[리스트7] 일-대-다 연관관계 추가(app/models/post.rb)

이 제 받은 트랙백 핑을 처리하는 일만 남았다. 트랙백 핑을 처리한다는 것은 HTTP POST를 통해 받은 POST 매개변수의 값들을 저장하고, 이어서 그 처리결과를 간단한 XML로 반환하면 되는 것이다. Main 컨트롤러에 [리스트8]과 같은 액션을 추가하면, 이제 이 블로그에 트랙백을 보내고자 하는 다른 블로거들은 http://yoursite.com/main/trackback/1 과 같은 식의 트랙백 주소를 사용하여 이 블로그로 트랙백을 걸 수 있게 되는 것이다. 여기서 트랙백 처리 결과는 XML로 반환되는데, 처리가 성공하면 0, 실패할 경우 1을 반환하도록 되어있다. 간단한 XML이므로 별도의 뷰를 사용하지 않고 액션에서 직접 텍스트 문장 형태로 반환하는 방식을 사용하였다.

  def trackback
    if request.post?

      tb = Trackback.new

      tb.title = params[:title]

      # ...
      tb.save!
      post = Post.find(params[:id])

      post.trackbacks << tb

      result = post.save ? 0 : 1 # 성공이면 0, 실패면 1
      render :text => "<?xml version='1.0' encoding='utf-8'?>
                <response><error><#{result}></error></response>",
    end

  end

[리스트8] trackback 액션 추가(app/controllers/main_controller.rb)

그 럼 이제 이 트랙백이 제대로 작동하는지 한번 확인해 보자. 앞에서도 언급하였듯이 레일즈는 자체적으로 강력한 테스트 프레임워크를 제공하며, 그 중 하나로 특정 컨트롤러의 동작을 테스트하는 기능 테스트(functional test)가 있다. 앞서 컨트롤러를 생성할 때 이미 함께 생성되었을 기능 테스트 파일을 열어서 다음 [리스트9]와 같은 테스트 케이스를 하나 추가하고, 이 테스트를 실행해 보면 테스트가 성공했다는 메시지가 출력될 것이다. 참고로,이 밖에도 레일즈에는 여러 컨트롤러와 모델들 간에 걸친 유스케이스나 사용자 스토리에 대한 통합 테스트(integration test)를 할 수 있는 기능도 있다.

  def test_post_trackback
    post :trackback, { :id => 1,

                                 :title => "sample trackback test",

                                 :url => "http://myblog.net",

                                 :excerpt => "Excerpt bla bla bla",

                                 :blog_name => "My blog" }

    assert_response :success

    post = Post.find(1)

    assert_equal 1, post.trackbacks.count

  end

[리스트9] trackback 테스트 케이스(test/functional/main_controller_test.rb)

태깅 구현하기

태 깅(tagging)이란 어떤 대상에 대하여 태그(tag)를 붙임으로써 기존의 카테고리에 의한 분류 방식이나 자동 검색이 가지는 한계를 극복하고, 태그의 공유를 통해 집단 지능을 높일 수 있도록 해 주는 새로운 콘텐츠 분류 및 검색 방법이다. 마찬가지로, 여기서도 태깅에 대한 상세한 설명은 생략하기로 하고, 바로 레일즈로 태깅을 구현하는 부분으로 들어가 보자.

태 깅을 위해 맨 먼저 할 일은 데이터 모델을 태깅이 가능한 구조로 만드는 것이다. 이를 위해서 물론 태깅과 관련된 모든 데이터 모델들을 직접 설계할 수도 있지만, 바퀴를 새로 만들 필요는 없다. 레일즈에는 이미 애플리케이션에 태깅 기능을 추가해 주는 여러 방법들이 존재하며, 여기서는 그 중의 하나인 acts_as_taggable 이라고 하는 플러그인(plugin)을 사용할 것이다. 참고로, 플러그인이란 레일즈에서 새로운 개념을 확장하려 할 때 사용하는 하나의 작은 배포단위다(IDE인 이클립스의 플러그인과 같은 개념이다). 우선 다음과 같이 하여 플러그인을 설치하자.

simblog>ruby script/plugin install acts_as_taggable

이 렇게 하면 vendor라는 디렉터리 아래에 있는 plugins 디렉터리에 acts_as_taggable 플러그인이 설치된다. 다음으로는 acts_as_taggable 에서 필요로 하는 데이터베이스 테이블의 스키마를 정의해 주어야 한다. 액티브 레코드 마이그레이션을 사용하여 [리스트10]과 같은 마이그레이션 파일을 작성한 다음, rake migrate를 실행하게 되면 모든 준비가 끝나게 된다.

class AddTaggable < ActiveRecord::Migration
  def self.up

    create_table :taggings do |t|

      t.column :taggable_id, :integer

      t.column :tag_id, :integer

      t.column :taggable_type, :string

    end

    create_table :tags do |t|

      t.column :name, :string

    end

  end

  def self.down

    drop_table :taggings

    drop_table :tags

  end

end

[리스트10] acts_as_taggable을 위한 마이그레이션 파일(db/migrate/003_add_taggable.rb)

이 제 태깅 기능을 추가할 대상을 선택할 차례다. acts_as_taggable은 내부적으로 액티브 레코드에서 제공하는 다형적 연관(polymorphic association)이라는 기능을 사용하고 있기 때문에, 일단 acts_as_taggable이 설치된 이상, [리스트10]의 마이그레이션을 통해 생성된 tags와 taggings라는 두 개의 테이블만으로, 태그를 걸고 싶은 어떠한 모델 객체에 대하여도 태깅 기능을 추가할 수가 있다. 여기서는 Post 모델에 태깅 기능을 추가해 보자. 어떻게 하면 될까? 다음 [리스트11]과 같이 Product 모델 클래스에 acts_as_taggable이란 코드만 추가시키면 된다.

class Post < ActiveRecord::Base
  has_many :trackbacks
 
acts_as_taggable
end

[리스트11] acts_as_taggable 추가(app/models/post.rb)

이 제 우리는 태깅 기능이 추가된 Product 모델을 사용할 수 있게 되었다. 물론, Product이 아닌 다른 어떤 모델 객체에 대하여도 클래스 정의에 이 acts_as_taggable 만 추가시키게 되면, 그 모델 객체는 그 순간부터 즉시 태깅 기능을 가지게 된다. 컨트롤러나 뷰에서는 이제 다음과 같이 하여 Product 모델의 태그에 접근할 수가 있을 것이다(여기서는 레일즈에서 제공하는 console이라는 명령행 유틸리티를 사용하여 태깅을 테스트하고 있다).

simblog>ruby script/console
Loading development environment.

>> p = Post.find(1)

=> #<Post:0x3cb9188 @attributes={"title"=>"My First Post", ...>
>>
p.tag_with "ruby rails test" <-- (1) 특정 모델 객체에 태그 추가하기
=> ["ruby", "rails", "test"]

>>
p.tag_list <--- (2) 특정 모델 객체에 붙은 태그 목록 보기
=> "ruby rails test"

>>
Post.find_tagged_with "rails" <--- (3) 특정 태그를 가진 모든 레코드 추출
=> [#<Post:0x3c3f6e8 @attributes={"title"=>"My First Post", ...>
>>
tag_items = {}
=> {}

>>
Tag.find(:all).each { |t| tag_items[t.name] = t.taggings_count }
=> [#<Tag:0x3e9de80 @attributes={"name"=>"ruby", "id"=>"1"}, ...>
>>
tag_items  <--- (4) 태그와 태그 횟수를 해시(Hash)로 반환
=> {"rails"=>2, "beta"=>1, "rss"=>1, "test"=>1, "ruby"=>1}

여기서 맨 마지막의 tag_items는 루비 언어에서 지원하는 클로저(closures)를 이용하여 만든 것인데, 태그 구름(tag cloud)을 만드는 경우 등에 유용하게 사용할 수 있을 것이다.

Ajax과 RJS의 활용

Ajax 은 웹 2.0의 간판 스타다. 구글 맵 등을 통해 세상에 소개된 Ajax은 RIA(Rich Internet Application)를 위한 훌륭한 도구이다. 앞에서도 잠깐 언급하였듯이, 레일즈는 프레임워크 차원에서 Ajax을 지원한다. Ajax 기술들을 사용하는데 필요한 거의 모든 것들이 이미 프레임워크 내부에 갖춰져 있어서, 개발자들은 몇 가지 간단한 메서드 호출만으로도 쉽게 Ajax이 제공하는 화려한 사용자 인터페이스나 비동기 HTTP 호출을 사용할 수 있다.

<div>
    <%= link_to_remote "Ajax 호출하기", :update => "result",

                                    :complete => "Element.toggle($('result'))",

                                    :url => {:action => "go_for_it"} %>
    <div id="result"></div>

</div>

[리스트12] 레일즈에서의 Ajax 호출 예(app/views/main/ajax_test.rhtml)

우 선 간단하게, [리스트12]를 보자. 이 리스트는 화면 출력을 위한 뷰의 일부분인데, 브라우저에서 보면, “Ajax 호출하기”라는 링크가 하나 출력되며, 이 링크를 클릭하면 go_for_it이라는 액션에 대하여 비동기 XMLHttpReqeust 호출이 일어나게 되고, 그 액션의 처리 결과 반환받은 값으로 result라는 id를 가진 div의 innerHTML을 채우게 된다. 그리고 마지막으로 호출이 완료되고 나면, result div를 토글(toggle)하게 된다. 만약 이 뷰를 브라우저에 출력된 상태로 소스보기를 통해 열어 보게 되면, 다음의 [리스트13]과 같은 자바스크립트 코드로 변환된 것을 알 수 있을 것이다. 이렇듯 레일즈의 Ajax 지원 기능은 여러 가지 복잡한 Ajax 호출을 간단하게 만들어 준다. 참고로, 레일즈는 내부적으로 Prototype과 script.aculo.us라고 하는 Ajax 지원 자바스크립트 라이브러리를 래핑(wrapping)하여 프레임워크와 통합시켜 놓았다.

<div>
    <a href="#" onclick="new Ajax.Updater('result', '/main/go_for_it',
        { asynchronous:true,
         evalScripts:true,
         onComplete:function(request){Element.toggle($('result'))}}
        ); return false;">Ajax 호출하기</a>
    <div id="result"></div>

</div>

[리스트13] 소스보기를 통해 본 변환된 Ajax 호출

이 외에도 레일즈를 사용하면 각종 비주얼 효과나 자동완성(autocompletion) 기능, 즉석 폼 편집(in-place form editing), 드래그 앤 드롭(drap-and-drop) 등과 같은 여러 가지 Ajax 기능들을 쉽게 구현할 수 있지만, 지면 관계상 생략하니, 필요한 독자는 참고자료를 참조하기 바란다.

Ajax 과 관련하여 마지막으로 한 가지 소개하고 넘어갈 부분은 RJS(Ruby JavaScript)에 관한 것이다. 레일즈는 기본적으로 세 가지 형태의 뷰 템플릿을 제공하는데, HTML을 생성하는 rhtml 템플릿, XML 파일을 만들어 주는 rxml 템플릿(앞서 살펴 보았음), 그리고 JavaScript 파일을 만들어 주는 rjs 템플릿이 그것이다. HTML 파일을 만들고 XML 파일을 만드는 것이야 일반적으로 모든 프레임워크에서 제공하는 것이지만, 자바스크립트를 만든다는 것은 조금 생소할 수도 있을 것이다. 다음의 [리스트14]와 같은 템플릿을 하나 만들어 just_do_it.rjs 라는 이름으로 views/main 디렉터리 아래에 둔다고 해 보자.

page.insert_html :top, "result", "Hello RJS!!"
page.visual_effect :highlight, "result", :duration => 2

page.delay(2) do

  page.visual_effect :shake, "result"

end

[리스트14] app/views/main/just_do_it.rjs

그 리고 이제 위의 [리스트12]의 Ajax 호출을 실행하게 되면, 어떤 일이 일어날까? XMLHttpRequest를 통해 just_do_it 액션, 그리고 그 액션의 뷰인 just_do_it.rjs가 호출되게 되며, 이 파일은 레일즈에 의해 서버에서 적절한 자바스크립트 파일로 변환된 다음 브라우저로 전달되게 된다. 따라서 브라우저에서는 “Hello RJS!”라는 문장이 result div에 출력되게 되고, 이어서 그 문장이 2초 동안 노랗게 강조(highlight) 되다가, 다시 2초가 지나고 나서는 한번 심하게 몸을 흔들게(shake) 되는 효과가 생기게 되는 것이다. 이게 바로 RJS다. 자바스크립트 마저도 루비 코드로 대체하고 있는 것이며, 이것이 Ajax의 비동기 XMLHttp 호출을 통해 이루어 지게 되면, 한번의 호출로 동시에 여러 가지의 Ajax 효과가 가능하게 되는 것이다. 기발하지 않은가?

REST 방식을 이용한 매쉬업(mashup) 구현

웹 2.0과 관련하여 마지막으로 살펴 볼 기능은 웹 서비스에 관련된 것이다. 일반적으로 웹 서비스를 구현하는 방식에는 SOAP, XML-RPC, 그리고 REST의 세 가지 방식이 있다. 이 중 SOAP과 XML-RPC는 XML로 정의되어 있는 메서드(또는 함수)를 호출하는 방식이라는 점에서 서로 비슷하며, 특히 WSDL을 사용하는 SOAP의 경우 W3C에서 권장하는 방식이기도 하다. 하지만 보다 느슨한 연결(loosely-coupled)을 지향하는 웹 2.0의 세계에서는 REST 방식이 인기를 얻는 것 같다.

레 일즈에서는 이 세 방식의 웹 서비스를 모두 지원하며, 특히 SOAP에 대하여는 복잡한 WSDL 작성 작업을 쉽게 해주는 액티브 웹서비스(Active WebService)라는 라이브러리도 제공하고 있다. 그렇지만 주제가 주제인 만큼 여기서는 REST 방식을 통한 웹 서비스에 대하여만 소개하도록 하겠다. 다음 [리스트15]는 REST 방식을 통해 네이버의 블로그 검색 오픈 API에 접근하는 웹 서비스 클라이언트 예제다. 이런 식으로 REST를 사용하면 야후 맵이나 구글 검색과 같은 기존 서비스를 활용한 매쉬업을 쉽게 만들 수 있을 것이다.

  def open_api
    product = Product.find(params[:id])
    query = CGI.escape(product.name)
    access_key = "test"
    url = "http://openapi.naver.com/search?key=#{access_key}&query=#{query}...
    result = Net::HTTP.get(URI(url))
    @doc = REXML::Document.new result
  end

[리스트15] REST 방식을 통한 네이버 오픈API 접근(app/controllers/main_controller.rb)

여 기서는 웹 서비스 서버에 접속하기 위해 루비의 Net 라이브러리와 REXML 라이브러리를 사용하고 있지만, 조만간 액티브 리소스(Active Resource)라고 하는 REST 전용 라이브러리가 레일즈에 포함될 것이라고 하니, 이 새로운 레일즈 전용의 REST 모델을 사용한다면 레일즈에서의 REST 개발이 한층 더 쉬워지지 않을까 예상해 본다.

여 기까지가 전부다. 물론 여기서 다루지 못한 기능들 중에서도 웹 2.0과 관련된, 아니 굳이 웹 2.0과 관련된 것이 아니라 하더라도, 미처 다루지 못한 레일즈의 훌륭한 기능들이 아직도 많이 남아 있지만, 지면이 부족하다는 핑계로 필자의 능력 부족을 감추어야 할 듯하다.  관심있는 독자들을 참고자료를 참조하기 바란다.

웹 2.0, 루비, 그리고 개발자

이 글을 시작하면서 필자는 루비가 인기를 얻게 된 데는 레일즈의 공이 컸다고 말하였다. 그런데 실은 그 반대도 성립한다. 즉, 레일즈가 인기를 얻게 된 데는 루비 언어의 공이 컸다는 것이다. 누군가, 프로그래밍 언어는 이미 머릿속으로 생각한 프로그램을 표현하는 도구가 아니라, 아직 존재하지 않는 프로그램을 생각해 내기 위한 도구이며, 좋은 프로그래밍 언어는 마치 유화 물감처럼 생각을 번복하는 것을 용이하게 만들어 주어야 한다고 하던 말이 떠오른다. 필자는 루비와 레일즈가 바로 그런 화가의 유화 물감 같은 것이 아닐까 생각해 본다. 적어도 웹 2.0이라는 것을 개발하는 한에 있어서는 말이다.

세 상 참 빠르다. 지난해를 거쳐 올 해 초까지 온 장안을 떠들썩하게 달구었던 웹 2.0의 인기도 제법 시들해져 버린 듯 하다. 아주 혁신적인 개념처럼 등장했다가도 어느 순간 뭐 별거 아니네 하며 끝나버리는 많은 현란한 마케팅 수식어들처럼, “웹 2.0” 역시 그렇고 그런 것들 중의 하나였는지도 모를 일이다. 너무 빨리 왔다 사라지는 바람에 도대체 그 실체를 종잡을 수조차 없는 것들이 너무 많은 세상이다. 마치 태풍이 지나가듯 웹 2.0이 한 차례 휩쓸고 지나간 자리에 남은 것은 무엇일까?

블 로그로 시작했으니 블로그로 끝을 맺자. 다나 보이드(danah boyd)라는 사람의 블로그에 나와 있는 글인데, 독자 여러분도 한번 나름대로 머릿 속에 남아 있을 웹 2.0이란 것의 기억과 함께 음미해 보길 바란다. 많이 부족한 글을 끝까지 읽어 준 것에 대하여 감사드린다.

" 그 다음에 할 일은 사용자의 관점에서 시스템을 설계해서 구현하는 것입니다. 사용자의 입장을 용인하는 것만으로는 부족합니다. 새로운 관점을 만들어 내는 이들은 바로 사용자입니다. 그렇기 때문에 우리의 기술에는 그들의 관점이 반영돼야 하는 것입니다. 우리가 빌어먹을 사용자에 대해 불평만 해댄다면 정작 중요한 것을 잃게 될 겁니다. 사용자는 뭔가를 증명하기 위해 기술을 사용하는 게 아닙니다. 자신의 틀에 들어맞기 때문에 사용할 뿐입니다.“

참고자료

웹 2.0에 관한 O'reilly 사이트 http://network.hanbitbook.co.kr/view.php?bi_id=1141
루비온레일즈 공식 사이트 http://www.rubyonrails.org/
월간 마이크로소프트웨어 2006년 5월호 웹 2.0 특집기사
월간 마이크로소프트웨어 2004년 12월호 루비온레일즈 소개
Chad Fowler, <Rails Recipes>
조엘 스폴스키, <조엘이 엄선한 소프트웨어 블로그 베스트 29선>
폴 그레이엄, <해커와 화가>
스티븐 존슨, <이머전스>


핑백

  • 낭망글루 : RoR links 2007-10-16 23:32:06 #

    ... tp://www.ibm.com/developerworks/kr/library/os-ecl-radrails/http://kldp.org/node/69474http://thinkr.egloos.com/796564 ... more

  • links for 2010-03-05 &laquo; 한번 날렸다;;; 2010-03-06 09:33:37 #

    ... g online programming tutorial tech swing sun software resources reference tutorials web) Serendipitous : 웹2.0과 루비 온 레일즈 (tags: rails reading ruby rubyonrails tutorial web2.0) 루비 : 프로그래머의 단짝 친구 (tags: book ... more

덧글

  • 이노메이커 2007/04/21 02:04 # 삭제

    최근에 저도 루비라는 언어를 접하게 되었고 지금 한참 루비의 매력에 빠져들고 있습니다.

    몇줄 안되는 코드로 금방 무언가를 만들어 낼 수 있다는데 있어서 만족감이라고 해야할까요..그게 바로 루비의 매력인 아닌가 싶습니다.

    요즘 미니블로그가 유행하고 있는데.. 사용자들이 금방 글을 올리면 다른이가 어느새 또 답을 해주는 그런게 루비하고 비슷하네요.

    짧은 글에 다시 돌아오는 큰 관심이라고 해야하나? 하여튼..주목해야할 하나의 언어임에는 틀림이 없다는 생각이 듭니다.

    그리고 올려주신 글 잘 읽었습니다.
※ 이 포스트는 더 이상 덧글을 남길 수 없습니다.



Follow me on Twitter

Follow sjoonk on Twitter