07-08 시즌, 루비팀 전망 보고서마이크로소프트웨어 2008년 1월호 특집 <웹 개발 기대주 BEST 5>에 기고한 글을 마소의 허락을 받고 블로그에 올리는 것입니다. 

 

 

루비도 2.0 시대?

레일스만 2.0이 되는 것은 아니다. 루비 언어 자체도 2.0이 되기 위한 노력을 몇 년째 지속하고 있다. 역대 최고의 베이퍼웨어(vaporware)라는 말을 듣는 Perl6(Parrot)보다 더 오랜 기간 개발 중인 프로젝트가 루비 2라는 사실을 아는 사람이 얼마나 될지 모르겠다. 그래도 올해는 이름뿐이던 루비 2의 실체가 조금은 드러날 전망이다.

 

루비 2에서 일어나는 가장 큰 변화는 VM 기반이 된다는 것이다. YARV(Yet Another Ruby VM)라는 이름으로 개발되는 것이 루비 개발 버전과 합쳐졌고, 이번 크리스마스에 첫 번째 버전인 1.9.1이 발표될 예정이다. 기존에 워낙 느리다는 평을 받고 있던 루비인데, 1.9 부터는 꽤나 빨라진다고 하니 기대해도 좋을 것 같다. <그림 5>의 벤치마크 결과만 봐도 1.8 버전의 루비(흔히들 MRI, Matz Ruby Implementation 이라고 부른다)에 비해 3배가 넘게 빨라진 수행 속도를 확인해볼 수 있다. 기존 버전이 워낙 느린 것도 있었지만, 다양한 최적화 방법을 도입한 덕분이다.

 

루비 1.9 버전은 2.0으로 가기 위한 개발 버전이다. 1.9에서 버전 숫자가 얼마나 높게 올라갈는지 잘 모르겠지만, 지금까지로 봐서는 2008년, 늦어도 2009년 초에서는 루비도 2.0 버전이 되지 않을까 추측해본다. 지금 당장 서비스나 프로젝트에 루비 1.9를 도입하기는 힘들지만, 향상을 기대하며 조금만 더 참아줄 수는 있겠다. 그럼 지금부터는 루비 1.9에서 일어나는 변화를 간략하게 살펴보자.

 

루비 1.9.1의 크고 작은 변화들

먼저, 다국어 지원(Multilingualization, M17N)이 강화되었다. 파일에는 # -*- coding: utf-8-*-과 같은 식의 특별한 주석 줄을 둬서 인코딩을 지정하고, open 메서드 등에서도 'r:utf08'등의 옵션을 줘서 인코딩을 정할 수 있다. 그리고 스트링이 더 이상 바이트 배열이 아니게 되었다. 

 

  1. "Hello, Worlds!"[4]  # == "o"

 

1.8에서는 ?o가 결과겠지만, 이제는 문자열이다. each_lines, each_char each_byte 둥의 반복자는 여전히 사용할 수 있지만, 예전에는 반복의 대상이 바이트였다면 이제는 한 글자 문자열이라는 차이점이 있다. 루비의 다국어 지원이 아직도 다소 부족해보이지만 루비 1.9.1에서 가해진 이런 변화가 좋은 출발점이 될 것 같다.

 

문법 면에서 가장 흥미로운 부분은 람다를 표현하는 새로운 방법이다. 아래 3가지는 모두 같은 표현이다.

  1. strategies = { :simple => lambda { |item| item.capitalize } }
    strategies = { :simple => ->(item) { item.capitalize } }
    strategies = { :simple => -> item { item.capitalize } }

 

화살표가 반복되는 모습이 좀 괴상하기는 하지만, ->가 람다를 나타내는 λ를 형상화한 것이라는 사실을 듣고 보니 그럴듯해 보이기도 한다. 해시를 1.9에서 추가된 표기법(JSON 처럼)으로 적으면 좀 낫다.

 

  1. strategies = { simple: lambda { |item| item.capitalize } }
    strategies = { simple: ->(item) { item.capitalize } }
    strategies = { simple: -> item { item.capitalize } }

 

이렇게 바뀐 문법으로 브루스 윌리엄스씨는 이런 해괴한 코드를 만들기도 했다.

 

  1. 3.times.inject(-> { -> { -> { "Hello, #{RUBY_VERSION} World!" } } }){ |_| _.() }

 

이 문장을 이해할 수 있으면, 루비 1.9의 큰 변화에 잘 적응하는 것인지도 모르겠다. 퀴즈로 남길테니 여러분들이 문장의 의미를 한번 추측해보기 바란다.

 

그리고 다행스러운 변화 하나는 블럭 파라메터가 드디어 블럭 안 에서만 쓰이는 지역변수가 되었다는 사실이다. 찾아내기 힘든 버그를 유발하기도 했는데, 고쳐졌다니 다행이다. 블럭이 블럭을 파라메터로 받는 것도 가능해졌다. 그래서 이렇게 쓸 수 있다.

 

  1. define_method(:foo){|&b| b.call(bar)}

 

메서드를 선언할 때 필수 인자와 선택 인자를 섞어서 사용해도 된다. 루비가 똑똑하게 인자를 맞춰줄 것이다. 꽤 흥미롭다.

 

  1.    def m(a, b=nil, *c, d)
         [a,b,c,d]
       end

  2.    m(1,2)                                         # => [1, nil, [], 2]

 

클래스를 살펴보면, 최상위 객체로 꼭 필요한 매서드만을 지는 BasicObject 클래스가 도입되었다. 이제 Object는 BasicObject의 자식에 Kernel 모듈이 믹스인 된 것이다. 그리고 BasicObject를 상속해 객체를 만들면 좀 더 가벼운 객체 시스템을 가질 수 있다.

 

필자는 이미 중독되어버린 유용한 메서드 Object#tap도 루비 1.9에 포함되었다. 이 메서드는 액티브 서포트의 returning과 유사하다. tap을 이용하면 메서드 연쇄를 계속 유지하면서, 특정 작업을 잠시 끼워 넣을 수 있다. 그리고 루비 관례에 맞지 않게 self를 반환하지 않아 연쇄를 깨뜨리는 메서드가 있을 때도 연쇄를 계속 이어갈 수 있게 된다. 특히나, 개발 과정에서 디버깅 등의 목적으로 중간 결과값을 알고 싶을 때 유용하다.

 

  1. [3,1,7,0].sort.tap{|ary| p ary}.reverse

 

외부 반복자(external iterator)도 새로 추가된 문법의 하나다. 그래서 예전에는 불가능했던 이런 코드를 쓸 수 있게 되었다.

 

  1. i1 = [2, 12, 13, 22].each
    i2 = [5, 10, 15].each
    loop {
      puts i1.next + i2.next
    }


결과는 7, 22, 28이 나올 것이다. 그리고 iteration completed 예외가 발생하고, 이 예외를 loop 메서드에서 잡아서 자연스럽게 루프를 종료한다. 물론 이 예외를 따로 잡아서 처리해도 무방하다.

 

마지막으로 그린 스레드(루비 프로세스에서 자체적으로 구현하고 스케줄링 하는 스레드)를 버리고 OS의 네이티브 스레드를 적용하게 되었다. 하지만 아직 Continuation에 대해 정해진 바는 없다.

 

루비 1.9.1에 포함된 변경 사항 중 일부는 실험적인 것이고, 일부 2.0 스펙으로 확정된 것이다. 어떤 것이 확정된 것이고, 어떤 것이 실험으로 끝날 것인지는 명확해지려면 조금 더 시간이 필요할 것 같다. String의 내부 표현이 바뀐다는 점을 제외하고는 크게 하위 호환성을 해칠 부분이 없어보이므로 큰 걱정을 할 필요는 없을 것 같다. 레일스 2 특히나 액티브 서포트에는 루비 1.9를 접대하기 위한 코드가 여기저기 많이 들어있다. 1.9가 나오면 여러분의 레일스 애플리케이션을 그 위에서 한번 돌려봐도 좋겠다. 아마도 큰 문제없이 동작할 것이다.

 

이 글을 쓰는 시점에는 루비 1.9.1이 발표될 것으로 알려져 있었으나, 실제 발표된 버전은 1.9.0이었다(참조 루비 1.9.0 - 크리스마스 선물!).

 

mean-linux.png

그림 4 다양한 루비 VM의 벤치마크 테스트 결과(출처: http://antoniocangiano.com/2007/12/03/the-great-ruby-shootout/)

 

graph-mean.jpg

그림 5 다양한 루비 VM의 벤치마크 테스트 결과(출처: http://antoniocangiano.com/2007/12/03/the-great-ruby-shootout/) 루비 1.9의 성능이 상당히 좋아졌음을 확인할 수 있다.

 

루비 개발팀은 4개다

다 음 버전 루비(VM 시대의 루비)를 위해 고생하고 있는 팀은 MRI와 1.9 버전을 준비하는 루비 코어팀만이 아니다. 다른 곳에서는 쉽게 찾아볼 수 없는 재미있는 현상인데, 총 4개의 프로젝트에서 팀을 이뤄서 상업적 지원을 받으며 각기 다른 루비를 구현하고 있다. 그중 단연 돋보이는 곳은 썬의 지원을 받은 JRuby 팀이다. 이미 1.8 버전과 완변하게 호환되는 루비를 JVM 위에서 구현을 마쳤고, 현재는 1.9 버전과 개선 작업을 진행하고 있다. JRuby가 가진 가능성에 대해서는 이창신님의 목소리를 빌어 따로 박스 기사를 마련했으니, 참고 바란다. 그 외에도 마이크로소프트의 지원을 받아 DLR(Dynamic Language Runtime) 기반 위에서 돌아가는 IronRuby(http://www.ironruby.net/)와 작은 코어 VM을 C 언어로 개발하고 그 위에 루비로 코어 라이브러리와 모든 것을 구현하겠다는 멋진 아이디어를 가진 Rubinus(http://rubini.us/)도 2008년이 기대되는 프로젝트이다. 특히나 Rubinius의 경우 아직 부족한 부분이 있지만 가장 발전 가능성이 크지 않을까 싶다. JVM 위에서 Rubinus VM을 돌리겠다는 논의도 진행 중이다.

 

이 렇게 다양한 루비 구현이 있는 것은 바람직한 걸까? 적어도 지금까지는 긍정적이다. 덕분에 루비 스펙이 투명해졌고(루비 소스의 yacc 파일만이 유일한 스펙이던 시절도 있었다), 서로 경쟁해서 성능도 나아졌다. 2008년에는 다양한 VM들이 어느 정도 선에서 수렴할 것이라고 생각한다. 그 모습이 JVM일지 YARV일지 Rubinius일지는 아직 잘 모르겠다. 사용자의 입장에서는 혼돈이나 과도한 경쟁보다는 서로 윈윈하기를 바랄 뿐이다.

 

이어서 - (4) 2008년 전망

 

 

Leave a Reply

Website

Email