루비는 일본의 '마츠'라는 사람이 개발한 스크립트 언어입니다. 루비를 이용해 만든 웹 개발 프레임워크인 루비 온 레일스는 엄청나게 빠른 웹 개발 프로세스로 국내에서도 명성이 높죠. 이 사이트에 가보면 어떤 사람이 루비 온 레일스를 이용해 코멘트 기능과 관리자 기능이 포함된 그럴싸한 블로그를 15분만에 뚝딱 만들어내는 충격적인 동영상을 보실 수 있습니다.

루비의 첫인상은 뭐랄까... 굉장히 장난스럽습니다. 언어에 포함된 기본적인 기능과 함수들을 가지고 여러가지 장난을 쳐볼 수 있죠. 예를 들면

5.times { print "A" }
#=> AAAAA

[1, 2, 3, 4].each { |i| print i * 2 }
#=> 2468

이런 코드라던지

string = "hello, world!"
string['hello'] = 'goodbye'
print string
#=> goodbye, world!

이런 코드를 보면 "재밌다!" 는 생각이 한편 들면서도, 한편으로는 "재미있지만 좀 쓸데가 없어보인다" 싶기도 합니다. 그렇지만 루비는 이 정도가 전부가 아닙니다. 조금만 파보면 이 언어의 심상치 않음을 차차 깨닫게 됩니다.

먼저, 루비는 철저한 객체지향 언어입니다. 파이썬과 비슷한 특징이죠. 때문에 위에서처럼 5.times { print "A" }같은 함수가 가능해집니다. 5라는 "상수"가 마치 객체인 것처럼 멤버 함수 times 를 불러서 { print "A" } 라는 "코드 조각"을 마치 객체처럼 함수 인자로 넘겨줄 수 있는 거죠. 파이썬과 비슷한 특징이지만, 파이썬보다 훨씬 쉽고 강력하게 해치웁니다.

함수를 객체처럼 사용할 수 있는 특징은 함수형 프로그래밍의 여러가지 장점을 명령형 언어에서 간편하게 표현하도록 도와줍니다.

[1, 2, 3, 4, 5].map { |x| x * 2 }
#=> [2, 4, 6, 8, 10]

[1, 2, 5, 6, 7, 8, 11].select { |x| x % 2 == 0 }
#=> [2, 6, 8]

이런 코드를 짤 수 있는 거죠. 물론 코드 조각을 받아서 실행하는 프로그램을 직접 만들 수도 있습니다. yield 명령어로 인자로 들어온 코드 블록을 실행하면 됩니다.

# sum_for_10 함수는 코드를 받아 코드에 1..10까지의 숫자를 던져넣은 결과를 합해줍니다.
def sum_for_10

  sum = 0
  for num in 1..10
    sum += yield num
  end
end

sum_for_10 { |x| x }
#=> 55
sum_for_10 { |x| x*x }
#=> 385

이렇게요.

루비의 또 한가지 재미있는 점은, 모든 클래스 정의가 오픈되어 있다는 점입니다. 이미 정의된 클래스를 나중에 오버로딩하고 덧붙이는 것이 가능합니다. 그런데 아까도 말했듯 루비의 모든 것(변수, 상수, 코드, 함수 등등등)은 객체, 즉 클래스입니다. 그러므로 언어 자체의 동작을 코드 내에서 쉽게 바꿀 수 있다는 뜻이 됩니다. 예를 들어, 리스트(루비에서는 Array)에서 합을 구해주는 함수 sum을 추가해 볼까요

# Array를 열어 새 함수를 추가합니다.
class Array
  def sum
    result = 0
    each { |x| result += x }
    result
  end
end

[1, 2, 3, 4, 5].sum
#=>15

간단하군요. +ㅅ+

덕분에 심지어는 이런 코드도 가능해집니다.

# Numeric은 정수와 실수를 포함한 루비의 모든 수를 나타내는 클래스입니다.
class Numeric
  def minutes
    # 자기 자신에 60을 곱해 분으로 표현합니다.
    self * 60
  end
  def hours
    self * 60.minutes
  end
  def days
    self * 24.hours
  end
  def years
    self * 365.days
  end
end

3.years + 13.days < (3.1).years
#=> true

다른 언어에서라면 Date 클래스를 따로 만들고 연산자를 오버로딩하고 어쩌구 저쩌구 했어도 이렇게 짧고 우아하게 해결하지는 못했을 겁니다.

그러면 지금까지 나온 두가지, 함수형 프로그래밍과 열린 클래스 구조를 이용해서 어떤 일이 가능해지는지 볼까요

# HTML이라는 클래스를 정의합시다.
class HTML
  # method_missing은 메소드가 호출되었지만 그런 이름을 가진 메소드가 없을 때 불려집니다.
  def method_missing (method_id)
    # 메소드 이름을 받아 문자열로 변환해줍니다.
    tag_name = method_id.to_s
    # 코드 블록이 함께 주어졌다면 안쪽의 내용을 태그로 감싸서 출력합니다.
    # 아니면 그냥 태그 한줄만 출력합니다.
    if block_given? then
      puts "<" + tag_name + ">"
      yield
      puts "</" + tag_name + ">"
    else
      puts "<" + tag_name + "/>"
    end   
  end
end

tag = HTML.new

tag.html {
  tag.head {
    tag.title { puts "이름" }
  }
  tag.body {
    tag.h1 { puts "제목" }
    tag.p { puts "문단" }
  }
}

결과는 간단한 html 코드가 됩니다.

<html>
<head>
<title>
이름
</title>
</head>
<body>
<h1>
제목
</h1>
<p>
문단
</p>
</body>
</html>

루비는 이런 식으로 지금까지 제가 "프로그래밍"에 대해 갖고 있던 생각을 완전히 뒤집어 놓았습니다. 지금까지 제가 알고 있던 프로그래밍이 어떤 과제를 수행하는데 적합한 언어를 고르고, 그 언어의 문법으로 그 과제를 기술하는 일이었다면, 루비로 코딩하는 것은 과제를 언어의 문법에 맞추는 동시에 언어의 문법을 과제에 맞춰나가는, 양방향 과정입니다. 요즘 루비 커뮤니티에서 유행하는 말로 하면, domain-specific language를 만드는 거죠.

한줄요약 : 루비는 정말 재미있습니다.

'자랑 > 삽질' 카테고리의 다른 글

루비에 심취해 있습니다.  (4) 2007/03/27
아이고하느님 - 데스크탑 3호기에 바치는 부고  (4) 2004/04/18
tag