2007년 5월 10일 목요일

Ruby 프로그래밍 시작(기초)

from http://www.ibm.com/developerworks/kr/library/l-ruby1.html
------------------------------------------------------------------------------------------------------


루비(Ruby) 프로그래밍

루비 변수, 쿼트, 어레이, 객체, 메소드

developerWorks
문서 옵션
이 페이지를 이메일로 보내기

이 페이지를 이메일로 보내기

JavaScript가 필요한 문서 옵션은 디스플레이되지 않습니다.


난이도 : 초급

Joshua D. Drake, 프로젝트 매니저, Command Prompt, Inc

2001 년 8 월 01 일

Linux Networking HOWTO, Linux PPP HOWTO, Linux Consultants HOWTO의 저자이자 Command Prompt, Inc 의 공동 창업자인 Joshua Drake가 Ruby 프로그래밍을 설명한다. 다른 언어 사용자라도 이 글을 통해 유용한 정보를 얻을 수 있을 것이다.

루비(Ruby)는 일본의 Yukihiro Matsumoto가 개발한 순수한 객체 지향 스크립팅 언어이다. 텍스트 프로세싱과 시스템 관리 태스크를 핸들하도록 디자인되었다.

아래 예제에서 보듯이, 펄 또는 PHP 같은 스크립팅 언어를 작성해 본 경험이 있는 사람이라면 루비 신택스도 익숙할 것이다. 그러나, 펄 또는 PHP와는 다르게, 라인 종결자로 세미 콜론(semi-colon)을 사용해야 한다. 어떤 개발자들은 이런 사실 때문에 혼란스러워 할 수도 있지만 실제로 이것은 개발 속도를 높일 수 있다.


Listing 1. Hello world

#!/usr/bin/ruby
#
# My first ruby program
#
print "Hello World\n"


Listing 2. 아웃풋 변경하기

#!/usr/bin/ruby
#
# My first ruby program
#

# Declare our salutation

$salut = "Good Bye World\n"

# Prepare statements

 print "Hello World\n"
 print $salut

Using the $salut 변수를 사용하여 언제라도 두 번째 print 문장을 쉽게 변경할 수 있다. 프로그램의 어떤 지점에서도 변수를 선언할 수 있다.


Listing 3. 변수 선언하기

#!/usr/bin/ruby
#
# My first ruby program
#

$salut = "Good Bye World\n"

 print "Hello World\n"
 print $salut

#
# Declare a new value for $salut.
#

$salut = "Oh, I am sorry. I didn't mean to warn
you...\n"

 print "What do you mean Good Bye World?\n"
 print $salut

위 예제를 실행하면 아웃풋은 다음과 같다:


Hello World
Good Bye World
What do you mean Good Bye World?
Oh, I am sorry. I didn't mean to warn you...

변수 재지정(re-assignment)이 작동한다.




위로


싱글 쿼트(single quote) & 더블 쿼트(double quote)

대부분의 언어가 그렇듯, 루비도 싱글 쿼트와 더블 쿼트가 구별된다. 루비에서 더블 쿼트는 "루비는 쿼트 안에 있는 모든 값을 인터프리팅한다"는 것을 의미한다:


print "Hello World\n"

루비 인터프리터는 \n 을 이스케이핑 하지 않고 대신 새로운 라인을 STDOUT (Standard Out)에 프린트 할 것이다. 하지만 싱글 쿼트로 같은 문장을 실행시키면 다음과 같다:


print 'Hello World\n'

루비는 다음과 같이 아웃풋을 만들것이다:


Hello World\n

새로운 라인이 프린트되지 않았다는 것을 주목하라. 대신 루비는 전체 문장이 리터럴(literal)이라고 생각한다. 이것은 펄이 사용하는 것과 같은 기능이다.




위로


단어 수학

루비에서 모든 스트링은 연산자를 사용할 수 있다. 다시 말해서 단어에 대해 수학을 사용할 수 있다는 의미이다. 예를 들어:


$salut = $salut * 3

결과는 다음과 같다:


Oh, I am sorry. I didn't mean to warn you...
Oh, I am sorry. I didn't mean to warn you...
Oh, I am sorry. I didn't mean to warn you...

그리고


$salut = $salut + " 10, 9, 8..."

 print $salut

다음과 같이 결과가 나온다:


Oh, I am sorry. I didn't mean to warn you...
Oh, I am sorry. I didn't mean to warn you...
Oh, I am sorry. I didn't mean to warn you...
 10, 9, 8...

변수가 세번 프린트되었지만, "10, 9, 8..."은 프로그램의 끝에 한 번만 프린트되었다. 이유는 무엇일까?

이유는 "10, 9, 8..." 을 변수의 끝에 추가했기 때문이다. 변수에게 "10, 9, 8..."을 각 라인에 추가하라고 명령하지 않았다.

"What do you mean Good Bye World? \n" 에서 "Hello World\n" 로 바꾸었을 때 설명했지만, 일단 변수가 지정되면 그 변수가 재 지정(reassigned)되지 않는 한 정적(static)이다. 하지만 이것은 멀티플라이어(multiplier) 또는 연결(concatenation)이 변수와 함께 사용되는 경우를 의미하지는 않는다. 변수와 함께 연결(+ 표시)을 사용하면 이전에 지정된 변수와 지정된 변수에 추가된 스트링을 변수가 상속받을 수 있다. 우리의 경우, " 10, 9, 8..." 이다.




위로


어레이

$salut 변수를 여러번 재지정해야 한다면? 대부분 그런일은 발생하기 마련이다. 따라서 변수를 재지정하는 대신, 모든 변수 값을 어레이에 놓을 수 있다.

어레이는 각 변수의 값이 개별적으로 어드레스(address) 할 수 있도록 한다. 다음 예제를 보자:


$salut = ['Hello World','Good Bye World','What do you
mean Good Bye World?']

 print $salut

위에 대한 결과 아웃풋은 다음과 같다:


Hello WorldGood Bye WorldWhat do you mean Good Bye
World?

원하던 아웃풋이 아니다. 공간이나 새로운 라인이 없다. 따라서 우리는 어떤 부분의 어레이를 디스플레이 할지 정할 수 있고 아웃풋을 읽을 수 있도록 하기 위해서 연결(concatenation)을 사용할 수 있다.


$salut = ['Hello World','Good Bye World','What do you
mean Good Bye World?']

 print $salut[0] + "\n"
 print $salut[1] + "\n"
 print $salut[2] + "\n"

아웃풋은 다음과 같다:


Hello World
Good Bye World
What do you mean Good Bye World?

코드를 분석해보자:


$salut = ['Hello World','Good Bye World','What do you
mean Good Bye World?']

루비에서는 다음의 값을 갖고 있는 salut 변수를 지정할 수 있다:

$salut = 0 1 2
Hello World Good Bye World What do you mean Good Bye World?

0

으로 시작하고 이것에서 부터 늘어난다. 어레이의 두 번째 값을 프린트하기 위해 다음과 같이 타이핑한다:


 print $salut[1]




위로


IF, ELSE 조건

루비에서 데이터를 나타내는 기초를 살펴보았다. 이제 루비 프로그램의 로직에 대해 살펴보자. 말하자면 프로그래머에게 받은 결과에 근거한 기본 함수작동을 수행하도록 지시하는 것이다. 또한 루비 프로그램이 사용자에게 받은 결과에 대한 조건을 수행하는 방법도 살펴 볼 것이다. 새로운 코드가 이 예제에 사용될 것이다.


Listing 4. 결과에 근거한 기본 함수 수행하기

$salut = ['Hello World','Good Bye World','What do you
mean Good Bye World?']

 print "When you enter the world, what do you say? "
 while enterWorld = STDIN.gets
   enterWorld.chop!
   if enterWorld == $salut[0]
     print "\n" + "Yes. Hello World would be
polite.\n"
     break
   else
     print "You say '", enterWorld, "'?!\n" + "You
humans are so rude!\n"
   end
  end

루비 개발의 새로운 면이 위 코드에 많이 소개되었다.


예제

#!/usr/bin/ruby
#
# My first interactive ruby script
#

# Define our main variable with an array

$salut = ['Hello World','Good Bye World','What do you
mean Good Bye World?']

# Print my first question

 print "When you enter the world, what do you say? "

# Create a while loop with the object enterWorld and
await data from
# standard input. The while loop will make sure that
ruby continues
# to process the application until the program tells
it to stop.

 while enterWorld = STDIN.gets

# Make sure we use the chop method on the enterWorld
object. The use
# of the chop method will insure that we strip new
lines and carriage
# returns from our input.

# You will notice that using the chop method has an
extra
# character. The ! allows the existing object to be
modified
# by the method. If you did not use the !, you would
have to redeclare
# the enterWorld object for the if condition to
correctly occur.

   enterWorld.chop!

# Begin the condition sequence. Basically, if
enterworld equals
# Hello World, which is 0, within the array, print
# a new line. Then print, "Yes, Hello World would be
polite." to the screen.

   if enterWorld == $salut[0]
     print "\n" + "Yes. Hello World would be
polite.\n"

# The break statement tells ruby to stop executing if
the previous
# condition is met. If we did not include this in our
while loop,
# the program would run continuously.

    break

# The else statement is used as the secondary
condition. In other
# words, if the first condition is not met, please do
the following.

   else
     print "You say '", enterWorld, "'?!\n" + "You
humans are so rude!\n"
   break

# The end statement is used to close a condition or
loop. In our case,
# it is being used to close both. We are first closing
our if
# condition statements and then stopping our while
loop.

   end
  end




위로


객체와 메소드

이 코드에 사용된 몇 가지의 기술과 방법들은 여러분에게 생소할 수도 있다. 루비는 객체 지향 프로그래밍(OOP) 언어이다. OOP를 사용한다는 것은 일반적으로 객체와 매소드 같은 아이템을 호출하는 것이다. 객체(object)는 콘테이너와 같다. 이것은 변수와 함수를 포함하고 있다. 매소드(method) 는 객체를 어드레싱하는 함수와 같은 것이다. 이전 예제에서, 객체와 메소드가 모두 실행되는 것을 보여줄 수 있다.


1

while enterWorld = STDIN.gets
   enterWorld.chop!

두 개의 객체와 두 개의 메소드가 있다. 첫 번째 객체는 enterWorld 이고 두 번째 객체는 STDIN 이다. enterWorld 객체는 사용자 정의 객체이고 STDIN 객체는 루비에 구현되어 있다.

예제에는 두 개의 메소드도 있다. 첫 번째는 gets 이고 두 번째는 chop!이다. 앞서 언급했지만 메소드는 객체를 어드레싱 한다. 특히 메소드는 객체 안에서 액션을 수행한다. gets 메소드를 사용하여 루비에게 STDIN을 얻도록(get) 명령할 수 있다. 루비가 STDIN 과 관련된 gets를 보면, 키보드 인풋과 캐리지 리턴(carriage return)을 기다릴 것이다. STDIN.gets은 사용자가 무언가를 타이핑하고 엔터를 치기를 기다린다.

두 번째 메소드인 chop!은 사용자 정의 객체인 enterWorld를 어드레싱 한다. chop! 메소드는 enterWorld에게 enterWorld 객체와 관련된 데이터에서 새로운 라인과 캐리지리턴으로 자르도록 (chop) 명령한다. 이전의 코드에서 chop! (또는 chomp!)을 사용하지 않는다면 "true"가 결코 될 수 없다.


 if enterWorld == $salut[0]

chop!를 사용하지 않고 $salut[0]이 실제로 $salut[0]\n와 같기 때문에 "false"가 된다. 새로운 라인은 STDIN 객체가 gets 메소드에서 받은 인풋에 의해 생겨난다. 캐리지 리턴을 사용하면 새로운 라인 문자들을 값의 끝에 추가하게 된다.




위로


결론

루비는 매우 강력하지만 사용하기가 쉽지만은 않다. C++, Perl, Python 프로그래밍 경험이 있다면 그러한 언어들과 루비와의 상당한 유사성을 발견하게 될 것이다 (특히 Python).




위로


참고자료




위로


필자소개

Joshua Drake는 Command Prompt, Inc의 공동 창립자이다. PostgreSQL과 리눅스 개발을 하고 있다. Linux Networking HOWTO, Linux PPP HOWTO, Linux Consultants HOWTO의 저자이기도 하다.

Ruby On Rails로 웹 어플리케이션 구현하기

from http://www-128.ibm.com/developerworks/kr/library/l-rubyrails/


Ruby On Rails로 웹 어플리케이션 구현하기

모델 뷰 컨트롤러 패턴을 사용하는 Ruby 기반의 프레임웍

developerWorks
문서 옵션
이 페이지를 이메일로 보내기

이 페이지를 이메일로 보내기

JavaScript가 필요한 문서 옵션은 디스플레이되지 않습니다.


제안 및 의견
피드백

난이도 : 초급

David Mertz, Developer, Gnosis Software

2005 년 6 월 14 일

Ruby on Rails는 웹 애플리케이션 개발 세계로 최근에 진입했다. 하지만 아직까지는 베타 버전이다. Rails는 대부분의 웹 애플리케이션의 구현을 자동화한다. 더욱이 웹 애플리케이션의 개별 양상들을 수행하는 Free Software 라이브러리들과 비교해 볼 때, Rails는 모든 양상에 맞는 통합된 툴 세트들을 포함하고 있다.

Ruby on Rails는 매력적인 웹 개발 도구이다. 우선 기초부터 공부해보자.

  • Ruby는 무료의 간단하고 단순하며 확장성 있고 이식 가능한 인터프리티드 스크립팅 언어로서, 빠르고 쉬운 객체 지향 프로그래밍으로서 텍스트 파일을 처리하고 시스템 관리 태스크를 수행하는 많은 기능들을 갖추었다.
  • 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 애플리케이션 구현을 바로 시작할 수 있다. 하지만 충분히 이해가 되지 않았다면 튜토리얼을 공부하기 바란다.

샘플 애플리케이션은 기본적인 주소록이다. 다음은 애플리케이션을 구현하는 일반적인 단계이다.

  1. (MySQL 데이터베이스와 테이블을 구현한 곳에) 모델을 구현한다.
  2. (기본 코드와 디렉토리와 함께) 애플리케이션을 구현한다.
  3. Rails를 실행한다. (데이터베이스 액세스를 설정한다.)
  4. 콘텐트를 만든다. (스카폴드 모델과 컨트롤러를 만들고 컨트롤러에게 스카폴트를 사용할 것을 명령한다.)

이제 각 단계별로 자세하게 설명하겠다.

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가 다양한 것에 단수와 복수 이름들을 사용한다는 점이다. 다양한 아이템들은 사용과 정황에 따라 단수와 복수 버전 사이에서 재명명 된다. 테이블 이름은 복수형을 사용한다. 불규칙 복수형을 가진 단어로는 시험해보지 않았다. datumdata같은 단어들은 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. 연락처 리스트
Listing contacts


Figure 2. 연락처 편집
Editing contact



위로


커스터마이징 가능한 콘텐트 구현하기

이전 코드는 완전히 작동하는 인터페이스를 구현하여 데이터베이스를 보고 수정하지만 모든 포맷팅, 표현, 비즈니스 로직은 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. 연락처 리스팅
Listing contacts modified


그림 4. 연락처 편집
Editing contact modified



위로


결론

Rails는 빠른 웹 애플리케이션 개발을 보장한다. 완벽한 프레임웍에는 웹 기반 애플리케이션에 가장 많이 사용되는 액션들을 수행하는 매우 유용한 클래스들과 메소드가 포함되어 있다.

무엇보다도 Rails는 우리가 필요로 하는 모든 지원 코드를 갖추었기 때문에 완전한 "Rails 방식"이 가능하다. 이는 다른 툴킷과 프레임웍에 월등히 앞서는 부분이기도 하다.



참고자료



필자소개

David Mertz

David Mertz : 개발자, Gnosis Software.