RSS myRuby.net

http://myruby.net

모든 꿈은 멋지고 소중하다

Last checked about 17 hours ago.

2 people have subscribed to this feed.

Feed frequency

post frequency (last month)

PostRank™ filter

latest 15 posts

« older items




Monday December 8th, 2008

스프링노트 개편과 여러가지 이야기

From myRuby.net, 1 month ago, 0 comments Comment

그림_12.png꽤 오랜기간 하나의 서비스 개발에 참여하고 있다. 현재 이 글을 쓰고 있는 바로 이 스프링노트다. 아직도 이 서비스에 대해 어떤 평가를 내리기는 이르다라고 생각하지만, 오픈 후부터 꾸준히 개선되어온(또는 개선하기 위해 노력해온) 모습은 좋게 평가할 수도 있지 않을까? 약간 촌스럽지만, '어제보다 나은 오늘, 오늘보다 나은 내일'을 위해 노력하는 서비스니까. 그런 스프링노트가 최근 한발 더 앞으로 나가기 위해 약간의 변신을 꾀했다.

 

이번 개편에 대한 자세한 내용은 아래 링크에 잘 설명되어 있다. 스프링노트 사용자라면 우상단에 새로운 기능이라는 귀여운 링크가 보일 것이다. 

 

9/18 릴리즈 노트 - 반갑다! New 스프링노트~

그림_11.png

 

사실 스프링노트에는 우리보다 우리를 더 잘아는 사용자분들이 있다. 꾸준히 스프링노트에 애정을 보여주시는 엘프화가님도 그런 분 중 한명인데, 어쩜 저리도 정확한 리뷰를 해주시는지 내가 뜨끔하기까지 하다 :) 

 

그런데 정말로 스프링노트 개발팀에서는 무슨 생각으로 이번 개편을 한걸까? 살짝 그들의 속은 아니고 개발자중 한명인 deepblue의 속을 들여다보자. 물론 이건 비공식적인 개인적인 생각들이다.

 

가독성을 높혀라

솔직히 이야기하면 스프링노트는 좋은 글을 잘 쓸 수 있는 환경만 제공하면 된다고 생각했다(그리고 그 문제만 해결하기도 우리의 일과는 짧기만 했다). 그리고 그렇게 작성된 글을 잘 활용하는 부분은 다른 플랫폼(ex. 블로그)이나 Open API를 통한 매시업이 충분히 커버할 수 있다고 믿었다. 그런 의도하에 모든 UI 결정은 글을 작성하는 사람에 맞춰졌다. 그 흔한 테마 기능이 없는 것도, 댓글 기능이 없는 것도 그런 이유였다.

 

그러던 어느 날, 이게 직무유기(?)는 아닐까라는 생각이 들었다. 글을 잘 쓰게 해주는 것도 중요하지만, 공들여서 만들어진 글들이 더 가치있게 보이게 하는 것도 우리가 당연히 해야할 일들이라는 것이다. 다른 플랫폼과의 연동, OpenAPI도 중요하지만 우리도 기본(!)은 하고 있어야하는 거다. 이번 개편의 읽기 모드는 그 기본을 갖추는 일이었다. 읽기 모드에는 가독성을 높히기 위해 글의 스타일(폰트, 스타일, 줄간격, 폭)등에 대한 고민도 다시 했다.

 

 

스프링노트 읽기 모드를 보니 문득 글이 쓰고 싶어졌다는 nainu님의 말이 생각난다(그 전에는 글 쓰고 싶은 생각이 안 들었다는 말인가? ㅠ). 다른 사용자분들도 그렇게 느껴준다면 우리는 어느 정도 성공한걸꺼다.

 

물론, 아직도 다른 플랫폼과의 연동, Open API는 대단히 중요하게 생각하고 있으며 계속 발전시켜갈 부분이다. 아, 그리고 하나 더 미투데이에 미남 개발자 kkung님이 있으면 스노에는 미녀 디자이너 kkong님이 있다 :) 이번에 입은 스프링노트의 예쁜 옷은 kkong님 작품이다.

 

그동한 사용자에게 미안했던 그룹노트

지난 가을에 오픈한 그룹노트는 내내 우리 팀의 마음을 무겁게 했다. 여기저기서 사용하기 어렵다는 평을 많이 받았다. 당연히 있어야 할 탈퇴 기능도 없다니 깜짝 놀랄 일이다. 정말 부족한 서비스였음을 인정한다. 이런 모습이 된 것은 커뮤니케이션의 부재가 가장 큰 원인이었다. 프로젝트를 하면서 프로젝트팀내의 소통 뿐 아니라, 외부와의 소통이 얼마나 중요한 일인지를 다시 한 번 배운다. 그룹 커뮤니케이션을 잘하기 위한 그룹노트를 만들면서, 커뮤니케이션의 부족함을 느끼다니 어찌보면 참 아이러니하다. 하하.

 

또 하나, 다소 부족한 서비스기는 하지만 현재의 모습으로도 그룹노트가 오픈마루에서 업무를 하는데 없어서는 안될 도구로 자리매김했다는 사실이다. 우리가 매일 직접 사용하면서, 그룹노트의 유용함을 느꼈기에 사용자들도 함께 느꼈으면 싶었다.

 

그래서 그룹노트를 조금 더 쉽게 사용할 수 있게 바꿨다. 혁신적인 뭔가를 만든 것은 절대 아니고 딱 사용자의 상식에 맞춘 그런 개편이다.

 

 

아직도 부족한 부분이 많다. 협업은 쉬운 일이 아니지만, 조금이라도 쉽고 편하게 잘 할 수 있도록 계속 변모하도록 노력할 생각이다.

 

노트의 벽을 허물어라

공간은 사고의 틀을 만든다. 내 개인노트에서 쓸 수 있는 글의 종류와 스프링노트팀의 그룹노트에서 쓸 수 있는 글의 종류는 전혀 다른 것이다. 그래서 스프링노트에서는 개인노트와 그룹노트를 명확히 구분하고 있기도 하다. 이건 기능적인 구분이 아니라, 사고의 전환이다.그리고 개인의 정체성은 다양하다. 개발자 deepblue도 있지만, 만화를 좋아하는 deepblue도 있고, 한 가정의 일원인 deepblue도 있다. '그래서 여러 권의 노트를 만들 수 있게 했다'는 설명을 하려는데 참 말이 길다. 하하.

 

어쨌든 그리하여 여러권의 노트를 만들 수 있고, 대시보드라는 공간에서 이 노트들의 글을 한눈에 보면서 관리할 수 있게 되었다. 지금까지 편법(?)으로 여러권의 노트를 가지고 있던 사용자라면, 한곳으로 모으기 바란다. 훨씬 관리하기 편할 것이다.

 

 

아직은 눈에 보이지 않지만, 재미있는 일들이 일어날 수 있다. 먼저 노트를 돌려가며 사용할 수 있다. 노트 소유 변경 기능을 이용해, (스프링노트 사용이 아직 익숙치 않은) 친구를 위해 노트를 어느정도 꾸며서 선물할 수도 있다(물론 적극 권장하는 바다. ^^).

 

그리고 노트가 꼭 물리적인 경계일 필요는 없을 것이다. 스프링노트에 있는 많은 글들 중 얼랭에 대해 이야기하는 페이지를 모아 '얼랭 메타 노트'를 만들 수 있으면 어떨까? 개인노트의 몇권이 특정 태그가 달린 글들이 모여 하나의 느슨한 그룹노트를 만들면 어떨까? 마치 공동의 공간이지만 글 하나하나는 개개인의 아이덴티티를 가진 팀블로그처럼... 뭐 이건 일단 내가 망상중인 모습들이니 언제 현실이 될지는 장담하기 힘들다.

 

원 모어 띵?

사실 원 모어 띵이 있기는 했다. 아니다. 우리는 항상 원 모어 띵 한개쯤은 가지고 있다. 그치만 한번에 다 보여주면 우리 장사 밑천이 떨어질테니.. 이건 잠시 미뤄두기로 결정했다. 그래야 회사에서 계속 월급도 받을 수 있고, 사용자들도 다음 릴리즈를 기대해줄테니 말이다 하하.

 

그럼 이제 스프링노트호는 어디로 가나?

일단 살짝 숨을 좀 돌려야겠다. 그래서 스프링노트호는 안면도로 가기로 했다는 풍문이다. ^^

 

그림_13.png

 

농담이고, 위에서 기본에 대한 이야기를 했었다. 어쨌든 스프링노트의 기본은 글을 잘 쓸 수 있게 하는 것이다. 이런 부분에 좀 더 노력을 하게 되지 않을까 싶다. 그리고 '협업을 잘하는 법'도 계속 가지고 있는 키워드다. 이 부분은 내가 결정할 수 있는 한계가 있기에 그만 적어야겠다.

 

레거시 코드를 대하는 우리의 자세

잠깐 개발 이야기를 해보자.

 

스프링노트를 루비 온 레일스를 활용해 2006년 말부터 개발하고 있다. 처음 시작할 당시 레일스 버전은 1.1.x였다. 2.2 버전(2배?)을 앞두고 있는 지금의 레일스와는 꽤 다른 모습이었다. 다중 뷰, REST 개념도 없었고, 코딩 스타일도 꽤 달랐다. 2년여 시간동안 정말 많이 변했다. 동적인 루비 언어만큼이나 빠르게 변하는 곳이다. 

 

그리고 루비/레일스에 대한 우리팀의 이해도도 빠르게 변했다. 처음에는 여러 부분에서 고민이 많이 되었지만, 지금은 springnote style이라는게 조금은 보일 정도로 잘 이해하가면서 사용하고 있다. REST도 몰랐다. 실전 배포 환경(Rails Deployment)도 무던히 고민해왔다.

 

자바스크립트에 대한 인식과 이해도도 큰 차이가 생겼다. 2006년 말에는 상상도 하기 힘든 여러 코드들이 지금은 당연한 듯 웹브라우저에서 동작하는 시대니까 말이다.

 

물론, 스프링노트 자체도 정말 크게 크게 변해왔다.

 

2년 숙성 스프링노트의 코드는 이런 변화를 계속 흡수해왔고, 많은 레거시 코드를 생산했다. 효율적이지는 않지만, 역사적인 이유(이 이유가 많은 부분을 설명하기도 한다)로 존재하는 코드도 있다. 이 레거시 코드라는 것은 개발자들이 가장 꺼려하는 것이기도 하다. 어떤 개발자든 시간이 지나면 자신이 짠 코드가 예쁘게 보이지 않고, 처음부터 다시 만들고 싶은 충동에 휩싸인다. 오늘 내가 만든 코드에는 정말 만족하고, 막 자랑하고 싶겠지만, 얼마간 시간이 지난 후에는 그 코드를 보면서 홍당무 얼굴이 되는게 개발자다. 물론 나도 그렇다.

 

1212052326.516195_PP08052900071.JPG

<사진 설명> 장맛을 지키는 오숙수의 마음으로~

 

하지만 최근 DHH의 키노트 내용을 보면서 마음을 바꿔먹었다. 내가 과거의 코드를 보면서 부끄러워할 수 있는 것은 내 지식이 그만큼 늘었고, 성숙해졌기 때문이다. 과거에는 보지 못하는 것을 지금은 보고 있다는 의미다. 그러니 부끄러워할게 아니라, 당당하게 기뻐해야 할 일이다. 그리고 어찌보면 당연한 일이기도 하다.

 

대신 한가지 필요한 일은 왜 부끄러운지, 과거와 지금의 생각의 차이는 무엇인지에 대해 조금만 더 고민해보는 시간을 갖는 것이다. 그렇다면 막연히 부끄럽기만 한 느낌이 하나의 교훈으로 남을 것이다.

 

뭔가 나를 부끄럽게 하는 레거시 코드를 보며 우리가 해야할 생각은 이렇다. '와, 나 오늘도 성장했나봐.. 예전에는 ~~~해서 이런 코드를 만들었는데 지금 보니 ~~~해서 뭔가 이상하네... 조금씩 고쳐볼까?' 그리고 작은 스텝으로 하나씩 하나씩 고쳐간다. 당연히 절대로(!) 한번에 많은 것을 고치려하면 안될 일이다. 더군다다 바닥부터 다시 개발? 이건 절대 피해야한다. 이건 개발자가 하는 가장 큰 실수 중 하나라고 생각한다.

 

어쨌든, 스프링노트 소스도 보고 있으면 혼자서 홍당무가 되기도 하지만, 속으로 뿌듯해하기도 한다. 2년동안 스프링노트 프로젝트를 하면서 개발에 대한 이해가 조금은 높아졌구나...라면서 말이다.

 

오랜기간 하나의 서비스를 개발 운영하는 경험은 힘들지만, 좋은 면도 있다. 같은 고민을 여러차례 반복하면서 자신만의 생각을 만들어갈 수 있다는 점이다. 개발자 테스팅에 대한 나의 생각도 그렇다. 매번 나를 가장 당혹스럽게 하는 것이 스프링노트의 테스트 코드들이다. 만들때는 꽤 자랑스럽게 만들었는데, 지금 다시 보니 엉성한 부분 투성이다. 그리고 아직도 고민되는 부분이 너무나 많다. 다행인 점은 어제보다 오늘 더 나은 테스트 코드를 만들 수 있을 것 같다는 막연한 느낌이다. 이건 같은 테스트를 여러 차례 다시 작성해오면서 얻게된 느낌일 거다.

 

'Truly succesful software takes 10 years to write' - DHH

 

개발자들의 장난감이 되는 서비스

그림_15.png일반 사용자가 잘 사용할 수 있는 서비스가 되는게 중요하지만, 또 한 축으로 개발자들의 재미있게 가지고 놀 수 있는 서비스를 만드는 것도 중요하다고 생각하고, 또 그런 서비스를 만들고 싶다. 나 자신이 스프링노트를 좋은 장난감으로 여기는 개발자이기 때문이기도 하다(내 은 스프링노트를 자주 활용한다). 개발자들이 잘 가지고 놀 수 있으면, 결국 일반 사용자도 언젠가는 그 덕을 볼 수 있지 않을까?

 

이번에도 개발자들만(^^) 좋아할 실험을 몇가지 해봤다. 중요한 것은 언제 없어질지 모르는 실험기능이라는 사실 :)

 

  1. 이 링크이 링크의 차이가 뭘까?
  2. 이 링크는 어떤 의미가 있을까?
  3. 이 링크는 어떻게 활용할 수 있을까?

 

실험은 계속된다~ 쭈욱!!!

 

끝으로 - 팀이란 뭘까?

팀웍은 공짜로 생기는 것이 아닌 것 같다. 발령장만 준다고 팀이 되는것도 더더욱 아니다. 상처와 감동을 번갈아 한번씩 주고 받아봐야 진짜 팀이 되는게 아닐까 싶다. 뜬금없이 이런 이야기를 하는 것은, 우여곡절끝에 새로운 스프링노트와 함께 또 다른 새로운 스프링노트팀이 생겼기 때문이다.

 

팀으로 함께 일한다는 것은 정말 어렵고 피곤한 일이지만, 그래도 서로 성장하면서 많이 배울 수 있는 보람찬 일이 아닐까? 그런 의미에서 좋은 팀원들에게 다시한번 감사를~!

 

마지막으로 사진은 좋은 팀이 되기 위해 만들어 본 장난감을 최초 공개~ 물론 스프링노트 매시업!

팀원들끼리 대화를 나누고, 한눈에 다른 멤버들의 상태(현재 업무)를 볼 수 있다.

 

그림_16.png

 

 

유쾌한 점퍼 (Jolly Jumper)

From myRuby.net, 1 month ago, 0 comments Comment

이전 연습: 여행 비용를 공평하게 나눠라  다음 연습: 안녕, UI! 작전명: 창을 띄워라

 

계속 쉬운 문제만 푼다. 짧게 금방 풀 수 있어서 좋다.

 

문제

http://www.programming-challenges.com/pg.php?page=downloadproblem&probid=110201&format=html

리스트에서 인근한 값들의 차이를 구했을 때 1~N-1의 숫자를 모두 포함할 때 이 리스트를 jolly jumper라 부른다고 한다. 주어진 리스트가 jolly인지 아닌지 판단하는 문제.

 

풀이

그림_4.png

  1. class Array
      def jolly?
        differences.sort == (1...length).to_a
      end
     
      def differences
        self[0..-2].zip(self[1..-1]).map{|x| (x[0] - x[1]).abs }
      end
    end

 

 

logo.png

  1. : differences ( seq -- new-seq )
      [ but-last ] keep rest v- [ abs ] map ;

    : jolly? ( seq -- ? )
      differences natural-sort
      [ length >array [ 1+ ] map ] keep
      = ;

 

 

erlang.gif

  1. % from trip.erl
    subtract(A, B) -> subtract(A, B, []).
    subtract([A|H], [B|T], L) -> subtract(H, T, L ++ [abs(A-B)]);
    subtract([], [], L) -> L.

    differences(A) ->
      List1 = lists:sublist(A, length(A)-1),
      List2 = lists:sublist(A, 2, length(A)),
      subtract(List1, List2).
     
    is_jolly(L) ->
      lists:sort(differences(L)) == lists:seq(1, length(L)-1).

 

 

후기

  • 약간 허전한데, 맞게 푼건가? -_-;
  • 이번에 만든 factor 코드가 살짝 마음에 든다 :)
  • erlang은 여행 비용를 공평하게 나눠라에서 만든 함수를 재활용해서 쉽게 해결되었다.

 

 

안녕, UI! 작전명: 창을 띄워라

From myRuby.net, 1 month ago, 0 comments Comment

이전 연습: 유쾌한 점퍼 (Jolly Jumper)

 

문제

고작 세 문제뿐이 안 풀었지만, 이쯤에서 살짝 쉬어가는 단계를 만들기로 했다.

 

갑자기 정말 간단한 GUI 프로그램이 만들고 싶어졌다. Factor나 얼랭으로 GUI 프로그래밍을 처음하므로 화면에 버튼 한개를 그리고 그 반응으로 메시지를 출력하는 매우 초보적인 Hello world 수준의 예제를 만들어보기로 했다. 이른바, 수단 방법 가리지 말고 창을 띄워보자~!

 

풀이

그림_4.png

루비는 GUI 바인딩이 썩 좋은 편은 아니었다. 언젠가 마소에 GUI를 루비의 단점으로 꼽은적도 있었을 정도다. 하지만, 이것도 시간이 많은 부분 해결해주었다. 최근 주목받고 있는 shoes를 이용하면 정말 간단하고, 루비스러운 코드로 대부분의 플랫폼에서 동작하는 GUI를 만들 수 있다. 맥에서는 깔끔하게 코코아로 동작하는 모습을 볼 수 있다.

 

그림_5.png

 

  1. Shoes.app(:width => 100, :height => 70) do
      background "#EFC"
      border "#BE8", :strokewidth => 6

      stack(:margin => 10) {
        button("Hello!") { alert("I'm Shoes") }
      }
    end

 

자신감 충만한(?) 루비에는 좋은 무기가 하나 더 있다. 전에도 소개한 적 있는 맥루비의 신무기, HotCocoa다. 오래 걸리지 않을테니 HotCocoa로도 한번 만들어보았다.

 

그림_6.png

 

  1. alert = Proc.new {
      NSAlert.alertWithMessageText("I'm MacRuby",
        defaultButton: "OK",
        alternateButton: "Cancel",
        otherButton: nil,
        informativeTextWithFormat: "HotCocoa").
      runModal
    }

    application do |app|
      window :frame => [100, 100, 100, 60], :title => "MacRuby!"  do |win|
        win << button(:title => "Hello!", :on_action => alert)
      end
    end

 

 

 

 

logo.png

Factor IDE를 보면 (OpenGL을 잘 활용할 수 있는) UI 프레임워크가 잘 갖춰져있는 모습이다. cocoa 바인딩도 있지만, 이번에는 Gadget 기반의 UI 프레임워크를 사용해봤다. 은근히 없는게 없는 Factor다.

 

그림_10.png

 

  1. : open-label-window ( -- )
      <pile> 1 >>fill
      "Factor Widget" <label> 20 <border> add-gadget
      "OK" [ close-window ] <bevel-button> add-gadget
      "Alert" open-window ;

    : open-hello-window ( -- )
      "Hello!" [ open-label-window ] <bevel-button>
      "Factor" open-window ;

    : hello ( -- )
      [ open-hello-window ] with-ui ;

 

 

erlang.gif

얼랭에는 Graphics System이 있다. 역시나 플랫폼 독립적인 라이브러리다. 맥에서는 Tk를 기반 시스템으로 사용한다. 얼랭은 서버 역할에 충실하고, GUI 클라이언트를 외부 시스템으로 만드는 느낌으로 접근하면 좋을 것 같다.

 

그림_9.png

 

  1. start() ->
      S = gs:start(),
     
      Win = gs:window(S,[{title,"Erlang"},{width,150},{height,100}]), 
      gs:create(button,hello,Win,[{y,30},{x,50},{width,50},
                   {label,{text,"Hello!"}}]),
                      
      gs:config(Win,{map,true}),
      loop().


    hello() ->
      S = gs:start(),

      Message = gs:window(S,[{title,"Hello"},{width,250},{height,70}]),
      gs:create(label,text,Message,[{y,0},{x,0},{width,250},
                   {label,{text,"Erlang Graphics System"}}]),
      gs:create(button,hellook,Message,[{y,30},{x,100},{width,50},
                   {label,{text,"OK"}}]),              

      gs:config(Message,{map,true}).


    loop() ->
      receive
        {gs,hello,_,_,_} -> hello();
        {gs,hellook,_,_,_} -> exit(normal)
      end,
      loop().

 

 

후기

  • 약간씩 어설픈 결과물이지만, 다들 자주 사용하지 않는 시스템들이어서 찾아서 뭔가 만드는 재미가 꽤 좋았다.
  • 개발을 하면서 느끼는 주관적인 감정들이라 딱히 설명하기는 힘들지만 각 언어마다 만들어주는 분위기가 있어서, 같은걸 만들겠다고 시작해도 어떤 언어를 이용하냐에 따라 결과물은 조금씩 달라질 수 있겠다.
  • 클라이언트 사이드의 UI는 점점 더 풍부하고 미려함을 추구하고 있는데, 위 언어들은 어느 정도에 와 있는걸까?
  • 다음 쉬어가는 페이지는 웹 애플리케이션에 도전할 생각이다.
  • 이제 자야지.

 

 

1.0

레일스에서 특정 작업 비동기로 처리하기- Delayed Job 플러그인

From myRuby.net, 1 month ago, 0 comments Comment

GTD(Getting Things Done) 플로우를 보면 이런 부분이 있다.

 

그림_13.png

그림 출처: 3 more GTD wallpapers!

 

일을 할 때는 다음 작업을 찾아서, 2분안에 할 수 있으면 당장 해버리고 그렇지 않으면 미루거나 위임하라는 것이다. 이런 원칙은 빠른 응답이 생명인 요즘 웹 개발에도 똑같이 적용된다. 오래 걸릴만한 일은 미루고 일단 사용자에게 응답을 보내라는 것이다.

 

약간 억지스러웠지만, 어쨌든 웹 애플리케이션을 개발할 때 비동기 작업이 필요한 경우는 꽤 많다. 예를 들어 스프링노트처럼 내 데이터 전체를 내려받게 해주는 기능이 있고 해보자. 이 기능은 오래 걸리기도 하지만, DB 쿼리도 많고, 압축이라도 한다면 CPU, Disk를 많이 소모하는 무거운 기능이다. 이런 기능은 비동기로 처리하는게 좋다.

 

비동기로 작업이 떠오르는 경우는 이럴때다.

 

  • '짧은 시간'안에 끝날 것이라는 보장할 수 없는 작업
  • 너무 많은 시스템 리소스를 소비하는 무거운 작업

 

특히나 레일스 애플리케이션은 비동기 작업이 더 절실한데.. 그 이유는 몽그렐 클러스터가 필요한 이유에서 찾아보기 바란다.

 

당신의 선택은?

비동기 처리를 위해 사용할 수 있는 솔루션은 너무나도 많고 다양하다. 많이 언급되는 것들 중 몇가지를 선택해서 테스트한 후 고르는 것이 좋겠다. 아니면 일반화해서 나중에 플러그인만 바꿔 끼울 수 있도록 하는 것도 좋은 접근이다!

 

그림_14.png

<그림> 선택이 너무 많다. 출처는 Handling Long-Running Tasks in Rails

 

내가 솔루션을 선택하는 방법은 일단 내가 가진 요구사항을 충족하는 '가장 가벼운' 것을 고르는 것이다. 혹시 나중에 필요할지 모르는 기능은 머릿속에서 지운다. 그리고 나중에 진짜 그 기능이 필요해지면, 그 때 고민해도 좋다. 그런 근거로 내가 선택한 것은 Shopify의 일부로 개발되었다는 Delayed Job(DJ)이다. github팀의 이 글에도 큰 영향을 받았다. 이 팀에서 DJ를 선택한 이유를 아래처럼 설명하고 있다.

 

It is simple, required no research beyond the short README, works wonderfully with Rails, is fast, is hackable, solves both the queue and the worker problems, and has no external dependecies. Also, it’s hosted on GitHub!

 

Delayed Job 설정 및 사용법

DJ는 DB를 Job Queue로 사용하므로, 아래처럼 마이그레이션을 수행해줘야 한다.

 

  1. create_table :delayed_jobs, :force => true do |table|
      table.integer :priority, :default => 0
      table.integer :attempts, :default => 0
      table.text :handler
      table.string :last_error
      table.datetime :run_at
      table.datetime :locked_at
      table.datetime :failed_at
      table.string :locked_by
      table.timestamps
    end

 

그리고 Job 객체를 만들어 위 테이블에 직렬화해서 넣어준다.

 

  1. class NewsletterJob < Struct.new(:text, :emails)
      def perform
       emails.each { |e| NewsletterMailer.deliver_text_to_email(text, e) }
      end
    end

 

큐에 작업을 넣는 코드는 아래와 같다.

 

  1. Delayed::Job.enqueue NewsletterJob.new('lorem ipsum...', Customers.find(:all).collect(&:email))

 

이게 DJ 사용법의 거의 전부다. 별도의 S/W를 설치할 필요가 없고, 사용법이 간결한 것이 가장 큰 장점이다.

 

Job Runner

이제, Job Queue에 담긴 내용을 꺼내서 실행하는 job_runner를 만들어준다. 아래와 같은 내용으로 script/job_runner를 작성한다.

 

  1. #!/usr/bin/env ruby

    unless ARGV.size == 2
      $stderr.puts "USAGE: #{$0} environment pid_file_path"
      exit 1
    end

    RAILS_ENV = ARGV[0]
    File.open(ARGV[1], 'w+') {|f| f.write("#{$$}") }

    require File.dirname(__FILE__) + '/../config/environment'
    Delayed::Worker.new.start

 

monit을 이용한 모니터링과 배포

44570084_5be779d27d_m.jpg이 작업을 하기전에 가장 궁금했던 것이, 어떻게 job_runner를 관리하는가 였다. 여기서 관리라 함은,

 

  • 배포 후 최신 버전을 띄운다
  • job_runner가 잘 돌아가고 있는지 모니터링 한다.

 

먼저 job_runner를 구동하는 컨트롤 프로그램을 작성한다. /bin/job_runner_ctl이다.

 

  1. #!/bin/bash
    cd /path/to/app

    if [ "$1" == "start" ]; then
      nohup /usr/bin/ruby script/job_runner production /path/to/log/job_runner.pid &
    else
      ps ux | awk '/script\/job_runner/ && !/awk/ {print $2}' |xargs -i kill {}
      rm /path/to/log/job_runner.pid
    fi

 

그리고 job_runner_ctl을 monit에 등록한다.

 

  1. check process job_runner with pidfile /path/to/log/job_runner.pid
      start program = "/bin/job_runner_ctl start"
      stop program = "/bin/job_runner_ctl stop"
     
      if totalmem is greater than 1024.0 MB for 5 cycles then restart # eating up memory?
      if cpu is greater than 50% for 2 cycles then alert # s an email to admin
      if cpu is greater than 80% for 3 cycles then restart # hung process?
      if loadavg(5min) greater than 10 for 8 cycles then restart # bad, bad, bad
      if 3 restarts within 5 cycles then timeout # something is wrong, call the sys-admin

 

마지막으로 할 일은 Capistrano의 restart 작업에 job_runner를 재시작하는 명령을 추가하는 일이다.

 

  1. desc "restart web application servers"
    task :restart, :roles => :app do
      # (..omitted..)
      run "monit restart job_runner"
    end

 

마치며

꽤 걱정을 했는데, 그에 비해 별 문제없이 Delayed Job을 서비스에 적용할 수 있었다. 조금 더 대용량을 다루는 사이트가 되면 그때는 Beanstalkd, starling, ActiveMQ 등을 고려해야할까? 일단은 defer it!

 

 

OAuth와 파일 업로드 - 서명된 요청의 진실

From myRuby.net, 1 month ago, 0 comments Comment

모든 OAuth 요청은 컨슈머가 만든 서명을 포함해야하고, 서비스 프로바이더는 정해진 약속에 따라 서명을 검증한다. 

 

OAuth 요청에는 자신이 사용한 oauth_signature_method와 그 결과인 oauth_signature를 파라메터가 포함되어야 한다. 그러면 서버에서는 요청이 적절한 signature를 담고 있는지 확인하고, 그렇지 않다면 적절히 무시한다. 

 

그림_17.png

 

여기서 가장 중요한 점은 Signature를 생성하는 일관된 규칙이다. 이 규칙이 잘 설명되지 않았던 초기에는 파일 업로드, XML 요청 등이 OAuth로 처리하기에 까다로운 부분으로 여겨졌다. 1.0 최종 스펙을 보며 이 규칙을 조금 정리해보자.

 

Signature

signature는 signature base string에 signature method(ex. HMAC-SHA1)를 적용한 값이다.

 

  1. oauth_signature = oauth_signature_method(signature_base_string)

 

signature method는 어느 플랫폼이나 동일하므로, 중요한 점은 Signature Base String을 생성하는 방법이다.

 

Signature Base String (SBS)

SBS를 만들때는 아래 3가지 요소를 각각 퍼센트 인코딩한 다음 &로 묶어준다.

그림_18.png

  • Method

    • 대문자로 GET, POST 등
  • URL

    • 정규화하고 query는 제외한다. 
    • 포트를 생략할 수 있으면 생략한다
  • Parameter

    • 아래 파라메터를 모아 정렬한 다음 하나의 문자열로 연결한다

      • HTTP Authorization Header - realm 제외
      • URL의 query 부분
      • POST 요청의 본문. 단, 요청 형식이 application/x-www-form-urlencoded인 경우만

 

그렇다면 파일 업로드는?

Signature Base String을 만들때 multipart/form-data로 이뤄진 POST 본문은 포함하지 않는다. 따라서 문제없이 처리할 수 있다.

 

XML을 담은 요청은?

예를 들어 액티브리소스에서 POST를 하면 요청 형식은 application/xml이고, POST 본문에 리소스를 표현하는 XML이 들어간다. 이 경우도 SBS를 만들때 POST 본문이 포함되지 않아야 한다는 사실에만 주의하면 큰 문제는 없다.

 

_method 트릭

특정 환경(ex. 브라우저)은 PUT, DELETE 요청을 활용하지 못하지만, REST 스타일에서는 이런 메서드은를 의미있게 사용하고 있다. 이 때 메서드의 한계를 피해갈 수 있는 방법으로 _method를 사용하고 있다. POST 요청을 하면서 _method 파라메터에 put을 담으면 서버에서는 부디 PUT 요청으로 봐달라는 것이다. 레일스에서 특히 잘 쓰고 있는 트릭이다.

 

그런데 이 경우 SBS를 이루는 Method를 뭘로 해야할까?

  • 컨슈머는 POST 요청인가? _method에 담긴 PUT 요청인가
  • 프로바이더는 이걸 POST 요청으로 봐야하나 PUT 요청으로 봐야하나.

 

스프링노트는 REST API를 존중한다는 의미에서 컨슈머, 프로바이더 모두 PUT 요청(서버에서 인식하는 요청)으로 보도록 하고 있다. 예를 들어 DELETE 메서드를 사용할 수 없는 환경에서 특정 페이지를 지우고 싶을 때는 POST와 _method=delete 파라메터를 사용할텐데 SBS를 만들때는 method를 POST가 아닌 DELETE로 넣어주기를 바란다는 것이다. 논란의 여지는 있는 것 같다.

 

이 글을 쓰는 이유는?

글을 마치는 시점에 글의 동기가 나온다니 좀 이상하지만.. 그래도 이 글의 계기가 있으니 소개한다.

 

 

ruby-oauth 라이브러리의 최근 버전에서야 SBS를 만드는 방법을 잘 지키게 되었기 때문이다. 그 전에는 파일 업로드를 하는 것, XML 요청을 보내는 것 모두 제약이 있었다. 한참을 뜯어보며 드디어 patch를 만들었는데, 리포트하려고 gem update를 해 최신버전을 내려받으니 비슷한 코드가 이미 들어있어 좀 황당했다. ^^

 

최근 humbroll님이 iPhone 환경에서 OAuth 인증을 하느라 꽤 많은 고생을 했다. 곧 고생담을 들을 수 있기를 기대해본다. (updated: 고생담은 여기)

 

끝으로

OAuth 인증을 지원하는 오픈 API를 사용하는 일이 쉽지않다. 특히나 요청 사이닝때문에 더 어려울 수 있다. 되도록 OAuth 요청을 직접 만들지 말고, 미리 만들어진 라이브러리의 최신 버전을 사용하는 편이 장수할 수 있는 비결이다. 그리고 프로바이더에서는 구글처럼 적절한 디버깅 도구를 제공해야하면 좋겠다.

 

참고자료

 

DropBox - 스프링노트 Drag&Drop Uploader

From myRuby.net, 1 month ago, 0 comments Comment

DropBox는 OS X에서 Drag&Drop으로 스프링노트에 파일을 첨부할 수 있는 업로더 프로그램으로 오픈소스 프로젝트로 개발하고 있다. 혼자만 사용하다가, 문득 다른 누군가에게도 유용할지도 모른다(?)는 생각이 들어 공개한다.

 

 

DEMO

 

설치 방법

 

변경 이력

 

주의

Rucola Application을 패키징 및 릴리즈해본 경험이 없어서 다른 맥에서 잘 돌아간다는 보장이 없다. 이 부분은 노하우가 생기는대로 차츰 업데이트할 예정이다. 어쨌든 현재는 아래와 같은 인증을 받은 상태다.

 

works-on-my-machine-starburst.png

 

Source Code

 

 

 

5.8

스프링노트의 달인을 공개 수배합니다

From myRuby.net, 1 month ago, 0 comments Comment

요즘 스프링노트 소식을 자주 전하는 것 같네요. 좋은 일 맞죠?

 

여러분의 스프링노트에서는 어떤 향기가 나나요?

스프링노트팀에서 매일 아침 하는 일이 뭔지 아세요?

사용자분들이 너무나 잘 만든 노트를 함께 보면서 놀라고 감탄하고 기뻐하는 일입니다.

 

이런 발견의 즐거움을 함께 나누고 싶습니다. 잘 사용해주시는 사용자분들께 작지만 감사의 마음도 전하고 싶습니다.

지금 사용하고 계신 노트를 보여주세요. 자랑해주세요. 거기서 많이 배우겠습니다. 

 

img03.jpg

 

스프링노트 구경하고, 카라멜 마끼아또 마셔요~

달인 이벤트 페이지에 방문하면, 스프링노트의 달인들이 만든 노트를 감상할 수 있습니다. 구경오셔서, 마음에 드는 노트는 추천 버튼을 꾸욱~ 눌러주세요. 칭찬(격려)의 따뜻한 메시지도 남겨주시고요. 받는 분에게는 즐거움을, 추천하시는 분께는 따뜻한 커피를 선물합니다.

 

물론 deepblue 노트를 추천하는 것도 잊지 말아주세요 하하 :)

 

img05.jpg

 

많은 참여 부탁드립니다!

 

커밍순~ 아이팟 터치용 스프링노트!

 

이번 이벤트 경품은 아무 고민없이 아이팟 터치로 결정했습니다. 그 이유는?

아이팟 터치용 스프링노트가 곧 나오는 이유에서죠.

첫 버전이라 부족한 부분도 있겠지만, 가려운 부분을 조금이나 긁어줄 수 있으면 좋겠습니다.

(약간의 걸림돌이 생겼지만 정말 곧 나옵니다 ㅠ)

 

 

 

3.0

루비 메타프로그래밍의 끝을 보여주는 RubyInline, ParseTree, Ruby2Ruby

From myRuby.net, 1 month ago, 0 comments Comment

RubyQuiz를 풀며 Code Beautifier를 간단하게 구현해보려다가, 약간 삼천포로 빠졌다. 그치만 그 과정에서 사용하면 즐거운 도구들을 만났다. 그 친구들을 소개하고, 어떻게 활용할 수 있을지 고민해보자.

 

RubyInline

RubyInline은 루비 코드 안에서 다른 언어로 만든 코드를 적을 수 있는 라이브러리다. 아래 예제는 루비 코드가 C 코드를 포함하고 있다.

 

  1. class Hello
      inline do |builder|
        builder.include "<stdio.h>"
        builder.c 'void hello() { puts("hello world"); }'
      end
    end

 

위 파일을 실행하면 ~/.ruby_inline 디렉토리에서 C 코드를 컴파일하여 수행한다. 느리기로 유명한 루비에서 속도가 중요한 일을 할 때, 또는 다른 언어로된 코드와의 연동을 위해 사용할 수 있다.

 

ParseTree

ParseTree는 RubyInline을 이용해 루비 코드의 Parse tree를 추출하여 s-expression으로 반환한다. 예를 들면 이런 식이다.

 

  1.   def conditional1(arg1)
        if arg1 == 0 then
          return 1
        end
        return 0
      end

 

이 코드를 변환하면 아래와 같다.

 

  1.   [:defn,
        :conditional1,
        [:scope,
         [:block,
          [:args, :arg1],
          [:if,
           [:call, [:lvar, :arg1], :==, [:array, [:lit, 0]]],
           [:return, [:lit, 1]],
           nil],
          [:return, [:lit, 0]]]]]

 

<그림> 1+1을 나타내는 ParseTree

 

Ruby2Ruby

Ruby2Ruby는 ParseTree가 반환하는 s-expression을 이용해 다시 루비 코드를 생성해낸다. ruby2ruby를 이용하면 동적으로 언어를 다루는 일을 쉽게 할 수 있다. 

 

좋은 예 중 하나는 내가 루비세미나에서 소개한 적이 있는 Heckle이다. Heckle은 코드의 특정 부분을 Ruby2Ruby를 이용해 변경하고, 그 때 테스트가 깨지는지 확인하는 도구다. 물론 깨져야 테스트를 촘촘하게 잘 만든 것이다.

 

또, Ruby2Ruby를 이용하면 Code Beautifier를 정말 간단하게 구현할 수 있다. 

 

  1. sexp = ParseTree.new.parse_tree_for_string($stdin.read).first
    puts Ruby2Ruby.new.process(sexp)

 

위 코드가 전부다.

 

  1. #!/usr/bin/ruby -rcgi
    H,B=%w'HomePage w7.cgi?n=%s';c=CGI.new'html4';n,d=c['n']!=''?c['n']:H,c['d'];t=`
    cat #{n}`;d!=''&&`echo #{t=CGI.escapeHTML(d)} >#{n}`;c.instance_eval{out{h1{n}+
    a(B%H){H}+pre{t.gsub(/([A-Z]\w+){2}/){a(B%$&){$&}}}+form("get"){textarea('d'){t
    }+hidden('n',n)+submit}}}

 

이런 요상한 코드가 있으면 아래처럼 읽을 수 있는 코드로 바꿔준다.

 

  1. H = , B =  = ["HomePage", "w7.cgi?n=%s"]
    c = CGI.new("html4")
    n, d = (not (c["n"] == "")) ? (c["n"]) : (H), c["d"]
    t = `\ncat #{n}`
    ((not (d == "")) and `echo #{t = CGI.escapeHTML(d)} >#{n}`)
    c.instance_eval do
      out do
        (((h1 { n } + a((B % H)) { H }) + pre { t.gsub(/([A-Z]\w+){2}/) { a((B % $&)) { $& } } }) + form("get") { ((textarea("d") { t } + hidden("n", n)) + submit) })
      end
    end

 

ParseTree와 Ruby2Ruby를 이용해 또 어떤 재미있는 일을 할 수 있을까? 

 

Forbidden Fruit: A Taste of Ruby's ParseTree

Goruko2008에 이와 관련된 재미있는 발표가 있으니 한번 확인해보자 . 아래는 루비 코드를 SQL로 바꿔주는 참신한 ORM인 Ambition을 공개한 Chris Wanstrath의 발표자료를 보자. 동영상도 공개되어 있다.

 

Chris는 ParseTree와 Ruby2Ruby를 이용해서 이런 일을 한다.

 

  • mapreducerb: RingyDingy를 이용한 간단한 분산 처리
  • sake: rake 코드를 만들어내기 위해 ruby2ruby를 사용한다.
  • Ambition: 루비 Enumerable을 이용한 코드를 SQL, ActiveRecord 코드, LDAP 쿼리 등으로 변환한다.

 

다시 한번 생각해보자. 또 어떤 재미있는 일을 할 수 있을까? 

 

프로덕션에서도 사용가능하다: merb의 예

성능을 떨어뜨리는 코드는 버그라는 철학을 가진 Merb에서도 ruby2ruby를 사용한다. merb-action-args가 그 예인데 요청의 쿼리 파라메터를 컨트롤러의 액션 메서드의 파라매터로 매핑해주는 역할을 한다. def bar(baz) 라는 액션이 있고, "/foo/bar?baz=bat"라는 요청이 들어오면 foo("bat")을 호출해주는 식이다. 조금 더 자세한 설명은 이 링크에서 확인할 수 있다.

 

이 사용예에서의 핵심은 특정 루비 메서드의 파라메터 목록을 런타임에 찾아내는 아래 코드다.

 

예를 들어, 아래같은 Example 클래스가 있다고 하자.

 

  1. class Example
      def hello(one,two="two",three)
      end

      def goodbye
      end
    end

 

get_args 메서드를 사용하면 메서드의 파라메터와 기본값을 알 수 있다.

 

  1. Example.instance_method(:hello).get_args
    #=> [[:one], [:two, "two"], [:three, "three"]]

    Example.instance_method(:goodbye).get_args 
    #=> nil

 

구현은 아래와 같다.

 

  1.   def get_args
        klass, meth = self.to_s.split(/ /).to_a[1][0..-2].split("#")
        # Remove stupidity for #<Method: Class(Object)#foo>
        klass = $` if klass =~ /\(/
        ParseTreeArray.translate(Object.const_get(klass), meth).get_args
      end

 

merb-action-args는 구동시에 Merb::AbstractController를 상속받는 모든 클래스의 메서드에 대해 파라매터 정보를 구축해놓고 있다가, 디스패치 작업에 이용한다.

 

메타프로그래밍의 재미

메타프로그래밍은 조금 더 일을 잘하기 위한 노력이다. 약간이라도 읽기 쉬운 코드, 반복을 최대한 줄이고 효율적인 작업이 가능하려면 어떻게 해야할까?를 고민하다보면 어느새 메타프로그래밍에 푹 빠지게 된다. 루비는 메타프로그래밍을 자연스럽게 할 수 있는 언어다. 그리고 다양한 예제와 프랙티스, 그리고 도구들이 있다. 남은 것은 코드를 조금 더 잘 만들려는 개발자들의 작은 열정과 틀을 깨는 상상력이 아닐까 싶다.

 

참조

 

 

5.0

파이썬으로 OAuth 컨슈머 만들기

From myRuby.net, 1 month ago, 0 comments Comment

Leah Culver씨가 공개한 파이썬 라이브러리를 이용하면 어렵지 않게 OAuth 컨슈머를 구현할 수 있다.

(하지만 라이브러리라기보다는 예제 코드에 가까워 사용하기 조금 불편한 API라는 느낌이다.)

 

이 코드에서 제공하는 예제를 살짝 수정해 스프링노트에 접속해서 페이지 하나를 가져오는 OAuth 컨슈머 예제 코드를 만들었으니 필요하다면 참고하시길.

 

  1. # setup
    client = SpringnoteClient(CONSUMER_TOKEN, CONSUMER_SECRET)

    # get request token
    token = client.fetch_request_token()
    print 'please visit %s in your browser and press any key.' % client.authorize_url(token)

    # get access token
    token = client.fetch_access_token(token)

    # access some protected resources
    print client.get_page(144, 'deepblue')

 

전체 소스 코드는 여기 있다.

조만간 파이썬용 스프링노트 클라이언트 라이브러리를 만들어 제공할 예정이다(이 글의 주인공에게 제작 의뢰했음 ^^).

 

1.0

몽그렐 사용자를 위한 몇가지 팁

From myRuby.net, 1 month ago, 0 comments Comment

지난 글 변하지 않는 것은 없다. 이제 대세는 Passenger(aka. mod_rails)에서 말한대로 Passenger가 현재 시점에서 가장 추천할만한 환경임에 변함이 없다. 게다가 최근 Global Queuing 기능이 들어갔다는 소식은 기존 애플리케이션도 Passenger로 옮겨타는 것을 고려하도록 유혹한다.

 

하지만, 이런 저런 이유로 아직도(?) 몽그렐을 사용하고 있는 곳을 위해 몇가지 팁을 공유한다.

 

이벤트 머신을 활용한 Swiftiply

루비 1.8(MRI)의 경우, 설계상의 이유(그린 쓰레드) 때문에 이벤트 기반 몽그렐의 성능이 더 낫다. 단순히 swiftiply 젬을 설치하고 환경변수만 설정하면 되는 문제이므로, 이벤트 기반 몽그렐을 사용하는게 더 낫다. 안정성 문제는 전혀 없는 것으로 알려져있다.

 

HAProxy를 이용한 글로벌 큐잉(Global Queuing)

보통 사용하는 HTTP 서버에서 제공하는 로더 밸런서(예를들어, 아파치라면 mod_proxy_balancer)를 사용하지만, HAProxy를 이용한 글로벌 큐잉을 도입히는게 낫다.

 

  1.     server myapp_3000 127.0.0.1:3000 check inter 60000 minconn 1 maxconn 1

 

위처럼 HAProxy의 설정에서 maxconn을 1로 제한하면, HAProxy가 요청을 큐잉하면, 몽그렐에는 한번에 하나의 요청만을 보낸다. 약간 더 자세한 설명은 스프링노트 배포 환경 Before & After: Capistrano, God, HAProxy, Seesaw!을 참고하면 좋겠다.

 

현 상태를 신고받는 mongrel_proctitle

mongrel_proctitle 젬을 설치하면 현재 처리하고 있는 요청에 대한 정보를 프로세스 타이틀로 표시해준다. 이런 식이다.

 

  1. mongrel_rails [10010/2/358]: handling 127.0.0.1: HEAD /feed/cal/global/91/6de4

 

여기 담긴 정보는 왼쪽부터 보자면 다음과 같다.

 

  • The port that Mongrel is serving
  • Requests currently queued/being processed concurrently
  • Requests processed during server lifetime
  • What it's doing
  • The client IP
  • The current req (method / path)

 

간단한 아이디어에서 시작했지만, 문제를 찾는데 걸리는 시간을 꽤 단축해주는 유용한 녀석이기도 하다.

 

참고로, Evented Mongrel의 경우는 mongrel_proctitle이 동작하지 않는다. mongrel_proctitle.rb 파일을 열어 아래 코드를 추가해주자.

 

  1.     def dispatch_to_handlers_with_proctitle(handlers,request,response)
          unless @handler
            @handler = ProctitleHandler.new(@titler)
            register("/", @handler, true)
          end
          @titler.request do
            return dispatch_to_handlers_without_proctitle(handlers,request,response)
          end
          end 
          alias_method :dispatch_to_handlers_without_proctitle, :dispatch_to_handlers
        alias_method :dispatch_to_handlers, :dispatch_to_handlers_with_proctitle
      end 

 

참고

 

Monday August 25th, 2008

SpringnoteClient - 스프링노트 루비 라이브러리(without Rails)

From myRuby.net, 4 months ago, 0 comments Comment

SpringnoteClient는 SpringnoteResources와 달리 ActiveResource에 의존하지 않는 스프링노트 클라이언트 라이브러리다. 지난 번에 만들었던 Preview를 발전시켜 젬으로 배포한다. 레일스를 사용하지 않을 때, 그리고 멀티쓰레드 환경일때는 SpringnoteResouces보다 SpringnoteClient가 나은 선택이다.

 

특징은 REST Client를 사용한다는 사실과, 정말 간단하게나마 ActiveResource의 API를 흉내냈다는 점이다. 액티브 리소스 초기 버전(DHH가 3일만에 만들었다)과 비슷한 상태랄까. 현재버전(0.0.2)는 기능이 부족하고(이유는 내가 뜨거운 루비 초기 버전을 만드는데 딱 필요한 부분까지만 구현해서 ^^), OAuth도 지원하지 않지만, 차츰 개선해갈 예정이다.

 

설치 방법

루비젬 만세!

 

  1. gem install springnote_client

 

사용예

따로 설명할 필요없이 코드를 보면 사용법을 바로 알 수 있을 것이다.

 

  1. # Instantiate a note
    note = Springnote('rubyseminar',
      :app_key => '__YOUR_APP_KEY__',
      :user_openid => 'http://openid.myid.net/',
      :user_key => '__YOUR_USER_KEY__')

 

  1. # Get a page
    page = note.pages.find(1325546)
    puts page.title

 

  1. # Update a page
    page.title += '_'
    page.source += '_'
    page.save

 

  1. # Create a page
    page = note.pages.build(:title => Time.now.to_s, :source => Time.now.to_s, :relation_is_part_of => 1329222)
    page.save

 

  1. # Get several pages
    pages = note.pages.find(1325546, 1325544)
    puts pages.map{|page| page.title}.join(',')

 

소스코드

github 만세!

 

  1. git clone git://github.com/deepblue/springnote_client.git

 

이 라이브러리를 사용하는 프로젝트

 

참고

 

 

뜨거운 루비 - 함께 전하는 루비 뉴스

From myRuby.net, 4 months ago, 0 comments Comment

개인적으로 루비 소식을 계속 전하려는 노력을 여기, 저기에서 해왔지만, 혼자의 힘으로는 꾸준한 정보를 제공하는 일이 쉽지 않다. 그래서 함께 모여서 뉴스 사이트를 만들어보면 어떨까 싶어 아래처럼 뜨거운 루비라는 다소 촌스러운(^^) 이름의 사이트를 하나 꾸렸다.

 

http://hot.rubykr.org/

 

회원 가입없이 누구나 글을 쓸 수 있도록 했으니, 여러분이 참여해주셔서 RubyFlow처럼 유용한 사이트가 될 수 있기를 바란다. 그리고 한국 루비 커뮤니티에도 뜨거운 열정과 재미있는 이야기꺼리를 계속 전달해줄 수 있으면 좋겠다.

 

여기까지가 새로운 사이트 오픈에 대한 짧은 변이었고, 지금부터는 조금 더 재미있는 이야기를 해보자. 뜨거운 루비는 웹 개발에 익숙한 사람이면 누구나 1~2일만에 만들 수 있는 규모의 작은 사이트다. 그렇지만, 조금만 더 들여다보면 이 글을 읽는 분들이 흥미를 보일만한 내용들이 몇가지 있다. 이 사이트가 슬러거의 사촌쯤되는 DB를 전혀 사용하지 않은 매시업 사이트라는 점, 레일스가 아니라 Sinatra라는 생소한 마이크로 프레임워크를 사용했다는 점, 오픈소스라는 점 등이다.

 

지금부터 개발하면서 떠올렸던 몇가지 키워드를 가지고 이야기를 풀어보자.

 

레일스 닮은 꼴, Merb

Merb는 레일스의 대안으로 루비계에서 새롭게 떠오르고 있는 스타다. 레일스가 했던 경험 위에서 후발 주자라는 이점을 살려 더 가볍고, 나은 그리고 성능도 좋은 레일스를 추구하고 있다. 레일스가 아닌 대안 프레임워크를 사용해보자라는 것이 이 프로젝트의 목표 중 하나였기에, 자연스럽게 Merb를 손에 잡고 문서를 따라하며 만들기 시작했다. 그런데 재밌는 사실은 Merb가 레일스와 비슷하다는 점이 단점으로 작용했다는 것이다. 거의 같지만 약간씩 다른 모습이 신기하기도 했지만, 어디인지 모르게 거슬리는 묘한 기분이었다. 그리고 결정적으로 Merb가 벌써 레일스 수준으로 복잡한 프레임워크가 되어 있다는 느낌이 들었다. Merb는 레일스와 함께 계속 발전해주기를 기대한다. 서로 계속 디자인과 내부 구현을 섞어가면서 말이다. 이미 나한테는 Merb와 레일스가 거의 같은 것으로 보인다. 이번에 만들려는 사이트는 굉장히 기능이 작은 사이트이기 때문에, 개발 환경도 레벨이 맞아야한다는 생각에 살포시 rm 명령을 내렸다.

 

작지만 강한 Sinatra

023 작은 웹 애플리케이션 개발에 최적화된 Sinatra라고 글을 썼던 기억이 떠올랐다. 그 때의 느낌이 꽤 좋았는지, 가장 먼저 떠오르다. 그리고 다시 꺼내들어보니 역시나 완성도 높은 좋은 프레임워크라는 느낌이 들었다. 루비로 만들어진 웹 프레임워크는 기본기가 탄탄하다. 레일스라는 스타로 부터 어떻게 만들어야 프로그래머를 기쁘게 할 수 있는지에 대한 암묵적인 교육을 받아왔고, Rack이라는 베이스 라이브러리가 있기 때문이다. 경험과 이를 받쳐주는 라이브러리가 있으니, 평균이 높은건 어찌보면 당연하다.

 

일단, 간결한 directory 구조가 마음에 든다.

 

 

이게 구조의 전부다. 작은 사이트를 만들때까지 app, controller, view, models, scripts, lib, vendors 이런 디렉터리를 줄줄이 달고 다닐 필요는 없을 것이다. 서버 구동도 간편한다 단순히 애플리케이션(이 사이트는 hotruby.rb)을 실행해주는 것으로 끝이다.

 

만들어지는 코드는 더 간결하다. 아래 코드가 뜨거운 루비 코드의 거의 전부다. 나머지는 뷰 템플릿과 모델 대신 스프링노트와 연동하는 부분들이다.

 

  1. %w(rubygems sinatra haml hpricot springnote).each{|lib| require lib}

    before do
      @springnote = SpringnoteStore.new
    end

    get '/' do
      @items = @springnote.items(params[:page] || 1)
      haml :index
    end

    get '/items.atom' do
      header 'Content-Type' => 'application/atom+xml; charset=utf-8'
      @items = @springnote.items(1)[1..-1]
      haml :atom, :layout => false
    end

    get '/write' do
      haml :write
    end

    post '/write' do
      @springnote.write params[:rref].to_s, params[:rres].to_s, params[:rree].to_s if params[:rref].to_s.length > 0 && params[:rree].to_s.length > 0
      redirect '/'
    end

    helpers do
      def sidebar
        @springnote.sidebar.source.to_s rescue ""
      end
    end

 

요즘 Fat Model, Thin Controller를 따르는 것이 대세이기에, 이 관점에서 보면 위 코드처럼 패턴우매칭을 이용한 라우팅과 하나의 코드 블럭으로 이뤄진 액션은 좋은 접근이다. 이 30여줄의 코드와 뷰 템플릿만으로 그럴싸한 구조를 가진 사이트가 만들어지니 꽤 기분 좋은 일이 아닐 수 없다.

 

정리하면, Sinatra는 꼭 필요한 기본을 갖춘, 작은 사이트를 만드는데 최적화된 추천할만한 프레임워크다.

 

데이터 저장소로서의 스프링노트

계획은 DataMapper ORM을 사용하는 것이다. 그런데, Sinatra의 간결한 모습을 보니 갑자기 데이터베이스를 만들고, 스키마를 설정하고, 또 서버에 배포하고 설정하고 마이그레이션을 하는 일련의 과정이 너무 큰 귀찮음으로 다가왔다. Sqlite도 있지만, 이것조차도 귀찮았다. 그래서 이번에도 (언제나 그렇듯) 스프링노트를 데이터 저장소로 사용하기로 했다. 이런걸 주최측의 농간이라고 하나? 암튼, 슬러거처럼 글 목록을 관리하는 메타 페이지가 하나 있고, 사이드바의 내용을 담은 페이지도 하나 있다. 그리고 일별로 하나씩 페이지를 만들어 여기에 뉴스를 차례로 담는다.

 

결과적으로 자연스럽게, 하루에 한 페이지씩 모인 다이제스트 페이지가 생겼고, 나는 데이터 관리 및 운영에 대한 걱정에서 벗어날 수 있게 되었다(이건 스프링노트 개발자역의 deepblue가 최선을 다해서;;;;). 그리고 첨부 파일 저장소, 검색 기능, HTML Validation 등, 따로 구현하려면 시간이 필요한 기능을 거의 공짜로 얻을 수 있게 되었다. 아직 삭제 기능이 없는데, 테스트로 만든 포스트들을 지우기 위해 택한 방법은 글의 리비전 히스토리에서 몇시간 전의 글로 복구해주는 것이었다 :) 물론, 익숙한 RDBMS가 주는 장점을 누리지 못한다는 단점도 있지만, 이는 조금만 생각을 바꾸면 얼마든지 다른 방법으로 해결할 수 있기도 하다.

 

  • springnote.rb - 뜨거운 루비의 모델 구현체로 스프링노트와의 연동을 담당한다.

 

좋은 오픈 API가 있고, 또 자신의 컴퓨팅 파워와 스토리지를 거의 공짜로 제공해주는 사이트가 많다. 이제는 DB도 없이, 서버 하나도 없이 단순히 HTML 파일 몇개만 업로드하는 것으로도 충분히 화려한 애플리케이션을 만들 수 있다. 맞는 방향인지는 따져봐야겠지만, 개발자가 좀 더 생산적인 일을 하게 돕는 방향인 것만은 확실하다.

 

덧) 이 기능을 구현하기 위해  먼저 SpringnoteClient 라이브러리를 만들었는데, 이 사이트를 만드는 것보다 더 오랜 시간이 걸린 것 같기도 하다. :)

 

간결함이 살아있는 Haml

루비 프로젝트에서 사용할 수 있는 다양한 템플릿 엔진에서 설명한 것 처럼, 참 다양한 템플릿 엔진이 있다. 그런데 요즘에는 유난히도 Haml의 간결한 결과물이 마음에 든다. 이건 템플릿계의 파이썬이다. 딱 필요한 코드만 쓰는 군더더기 없는 모습이 매력적이다. UI 개발직군과의 협업이 많지 않다면, 앞으로도 Haml을 애용할 것 같다.

 

나의 구세주 BlueprintCSS

나는 CSS가 참 어렵다. 내가 사용하는 브라우저에서 온전하게 렌더링되게 만드는데도 꽤 많은 시간이 필요한데, 여러 브라우저의 서로 다른 렌더러까지 고려해서 코딩하라는 현실이 매우 가혹하게 느껴지기만 한다. 그래도 BueprintCSS 프레임워크가 있다는 사실이 얼마나 다행스러운지 모른다. 요즘 새로운 프로젝트를 열면 가장 먼저 하는 일이 이 css 파일 3개를 복사하는 일이다 :)

 

새로운 생존 전략, 매시업

댓글 기능이 필요하다는 생각을 하고, 실제 구현을 완료하는데까지 걸리는 시간은 정확히 10분이었다. Disqus에서 제공하는 AJAX API 덕분이다. 언제가 CAPTCHA 기능이 필요해질텐데, 이를 위해 준비해둔 무기는 라이브러리도 젬도 아닌 reCAPTCHA API다. 다른 대안으로 Akismet도 있다. 얼마나 많은 OpenAPI를 알고, 또 사용할 수 있는지가 경쟁력이 되는 시대가 된지도 모르겠다. 어쨌거나 오늘도 나의 오픈API 예찬론은 계속된다.

 

배포의 신기원, Rack+Passenger

Sinatra를 이용해 금방 흡족한 애플리케이션을 만들었다. 이제 어떻게 배포할지가 잠깐 고민이었다. 하지만 Railsconf2008에서 Passenger가 2.0 버전부터 Rack을 지원한다는 발표를 했다는 생각에 미치자 모든 근심이 사라졌다. 그리고 실제로 RC1을 설치하고 문서를 따라 설정해보니 30분여를 투자해서 쉽게 설정을 마칠 수 있었다.

 

Passenger는 정말 루비 웹 프레임워크를 한단계 진보시키고 있다는 생각이 든다. 너무 프로모션을 열심히 해서 불필요한 buzz를 만든다는 안티성 발언도 있지만, 뭐 어떤가 많은 사람들이 Passenger를 통해 루비 애플리케이션 배포가 매우 쉬워졌음을 알게 되면 좋을 일이라고 본다.

 

Rack은 루비 개발자에게 커다란 선물이다. Rack 덕분에 수많은 웹프레임워크들이 시너지를 낼 수 있고, 자신만의 에지를 날카롭게 하는데만 집중할 수 있게 되었다. (참고: RACK속에 들어간 레일스) 좋은 세상이다.

 

소스 코드

뜨거운 루비는 오픈소스 프로젝트다. 누구든 개발에 동참하실 수 있다는 의미다. 그리고 협업을 간절하게 원하고 있다고 하다. 전체 소스코드는 언제나처럼 github에 있다.

 

  1. git clone git://github.com/deepblue/hotruby.git

 

결언

처음에는 뉴스 공유 사이트가 필요하다는 순수한 목적(?)에서 시작한 프로젝트였지만, 뒤로 갈수록 흥미로운 기술들에 빠져들어 정말 며칠동안 굉장히 즐거웠다. 다시 순수한 목적으로 돌아와서 이 사이트가 도움되는 사이트가 되기를 희망해본다.

 

참고

 

변하지 않는 것은 없다. 이제 대세는 Passenger(aka. mod_rails)

From myRuby.net, 4 months ago, 0 comments Comment

출근길에 문득 Rails Deployment의 글을 빨리 수정해야겠다고 생각했다. 6개월 정도 글을 업데이트하지 않았을 뿐인데, 그 사이에 바뀐 내용이 반영되지 않아, 어쩌면 틀린 글이 되어버렸는지도 모르겠다는 위기감이 들었기 때문이다.  관련해서 올초에 도 한권 출판되었는데, 내가 이 책의 저자라면 이런 속도가 무척이나 당혹스러울 것 같다(책을 쓰는 중간에도 대대적인 업데이트가 있었다고 한다).

 

세상은 빠르게 변하고, IT 업계는 더 빨리 변하고 있지만, 정말 내 주변에서 레일스 배포 환경만큼 빠르게 변하는 것도 없는 것 같다. 먼저 지금까지의 역사를 한번 돌아보면 좋겠다. 지난 5회 루비 세미나에서 ikspres님이 잘 정리해주신 내용이 있으니 이 내용을 한번 읽어보자. 참 파란만장했다.        

 

 

마지막으로 깔끔한 정리버전도 있다. 역시 출처는 ikspres님 발표 자료다.

 

레일스 오늘 처음이에요 Webrick
이제 조금 알 것 같네요. Mongrel ( evented Mongrel)
좀 더 빠른거요. 쉬우면서. Pen/Balance + Mongrel_cluster
좀 더 빠르거요. SSL 되면서요 Pound + Mongrel_cluster
아파치 아니면 안되요. 좀  복잡해도 참을께요 Apache + mod_proxy_balance + Mongrel_cluster
무지 빠르고. 간지나는 NginX + Mongrel_cluster

 

이제 그 이후 어떤 변화가 있었는지 돌아보자.

 

Thin의 등장

한동안 몽그렐 천하였던 루비 웹서버 판에 새로운 도전자가 나타났다. 바로 Thin이다. Thin은 현재 루비가 가진 것을 잘 활용해, 조금 더 나은 것을 만들었다. 몽그렐의 파서와 루비 프로젝트를 슈퍼카로 만들어즌 EventMachine, 그리고 Rack이 Thin을 있게한 요소들이다. 물론 Thin의 판매 전략은 성능이다.

 

그림: Evented Mongrel보다 나은 성능을 보여주는 Thin(출처 - Thin 홈페이지)

 

몽그렐이 훌륭하게 자기 몫을 하고 있기는 하지만, 약간의 잡음(그 유명한 Rant)으로 살짝 위기감이 들었는데, Thin이라는 좋은 경쟁자 또는 대체제가 생겨서 다행이라는 생각이다. 그치만, 현재 Mongrel에 큰 문제가 없다면 꼭 Thin으로 바꿔야할 이유는 없어보이기도 한다. 이 부분은 좀 더 지켜보자.

 

Merb, 누구냐 넌!

몽그렐 클러스터가 필요한 이유에서 설명했던 그 이유, 레일스가 쓰레드 안전성(Thread-Safety)를 갖추고 있지 않다는데 대부분의 사람들은 개의치 않지만, 어떤 사람들은 매우 민감하다. 가장 민감한 곳은 자원의 효율성이 돈과 직결되는 호스팅 업체들이다. 그래서 나타난 것이 쓰레드 안전성을 전면에 내세운 Merb다.

 

Merb는 EngineYard에서 일하며, 레일스 배포환경에 큰 공헌을 한 Ezra Zygmuntowicz이 주도적으로 개발하고 있다. 더 빠르고 가벼운 레일스를 추구한다. ORM을 선택해서 사용할 수 있는 것도 큰 차이점이다.

 

Merb가 레일스보다 느릴 이유가 없기에, 여러 벤치마크에서도 Merb의 성능이 더 나은 것으로 나온다(그렇지 않으면 Merb의 존재 이유 No.1이 사라진다).

 

출처: Performance Comparison For Rails and Merb

 

최근 Merb가 조금이나 새력을 확장해가고 있다. 곧 1.0이 나올텐데, 그 때는 레일스의 대안으로 자리매김할 수도 있을 것 같다.

 

2008년 상반기 히트작 Phusion Passenger(aka. mod_rails)

마지막으로 소개할 것은 최근 큰 인기를 얻고 있는 Phusion Passenger다. 왜 레일스 애플리케이션은 PHP 처럼 배포가 간단하지 않은 것인가?라는 문제에서 시작한 프로젝트로, 이 문제를 훌륭하게 풀어냈다. 이제 복잡한 클러스터 세팅 없이 mod_rails를 설치하고 Virtual Host를 설정해주는 것만으로 레일스 애플리케이션이 돌아간다. 정말 쉽고 편해졌다! 레일스 배포 환경이 너무 꼬졌다고 불편하던 Dreamhost도 최근 Passenger를 도입했다는 소식이 들린다. 클러스터의 관리를 요청에 따라 아파치가 관리하도록 위임하는 것이기때문에, 항상 N개의 인스턴스가 떠 있었던 기존 방식에 비해 더 많은 리소스를 확보할 수 있다는 큰 장점이 있는 것이다.

 

Passenger의 존재 이유는 쉬운 설정, 적은 관리 비용이지만, 성능도 괜찮은 편이라니 이보다 좋을 수 없다(이 부분은 약간의 이견이 있기도 하다).

 

passenger_mongrel_thin_benchmark.png

출처: Benchmark: Passenger (mod_rails) vs Mongrel vs Thin

 

설치 및 사용법은 먼저 잘 설명해주신 분들이 많으니 이 글을 참고하자.

 

 

국내에서도 이미 몇개의 서비스가 Passenger를 사용하고 있다.

 

 

Passenger와 함께 등장한 Ruby Enterprise Edition도 GC를 패치해서 메모리 사용량을 줄요준다고 하니 참고하자.

 

결론

또 6개월이 지나면 얼마나 바뀔지 모르겠지만, 현 시점에서 추천하는 배포 스택은 선호하는 웹서버에 따라 아래 두가지다.

 

  • Apache 2.2 + Phusion Passenger
  • Nginx + EventedMongrel + Monit

 

경쟁과 변화는 항상 좋은 일이다. 나도 뒤쳐지지 않으르면 오늘도 내 루비를 닦아야겠다. 항상 반짝거릴 수 있게..

 

참고

 

 

뜨거운 루비 주간 정리 #001

From myRuby.net, 4 months ago, 0 comments Comment

이 글은 지난 한 주간 뜨거운 루비에 올라온 소식을 중심으로, 지난 주 화제가 되었던 소식을 개인 취향으로 정리합니다. 뜨거운 루비 주간 정리 #001 :: 2008/6/16 ~ 2008/6/23

 

뜨거운_루비____함께_전하는_루비_뉴스-1.jpg

최고 인기는 Rails 2.1

레일스 2.1이 나온지 한달이 되어가지만, 아직 관심이 식지 않고 있네요. Ruby on Rails 2.1 - What's new라는 이북이 인기였고 2판까지 나왔습니다. 그리고 이 책의 번역 프로젝트사 시작되었고, 현재는 초벌이 완성되기 직전에 있습니다.

 

Nome_do_Jogo_»_Artigo_»_New_Free_Book__“Ruby_on_Rails_2.1_-_What’s_new”.jpg

 

보안도 좋고, 캐싱 지원도 좋은 레일스 2.1을 반기지 않을 이유는 없겠죠? 업그레이드시 주의점입니다.

아, 레일스는 2.1 이후에도 조금씩 개선되고 있습니다.

 

 

날 좀 봐주세요

 

금주의 말

미투데이가 세상에 나온 이후로, 한국에서도 갈 수록 인기를 얻고 있는 Ruby on Rails. 혹시 남친이 아직도 Java를 하는지 점검들 해보시길.  - 만박

 

후기

이번주에는 루비세미나가 있죠?  많은 분들 만날 수 있기를 기대합니다.

 

마지막으로 뜨거운 루비 구독하기!

 

놀러오세요~ 레일스 (6회 루비세미나 튜토리얼 발표자료)

From myRuby.net, 4 months ago, 0 comments Comment

6월 28일 여섯번째 루비세미나가 있었다. 이번에는 장소가 조금 넓은 곳이 섭외되어, 좀 더 많은 분들(백여명이 넘는)이 함께 할 수 있었다. 그리고 루비/레일스를 처음 접하는 분들을 위한 짧은 튜토리얼을 준비하기도 했다. 루비 언어에 대한 튜토리얼은 ikspres님이 맡아주셨고, 나는 레일스를 소개했다.

 

       

 

발표자료는 StotyQ에 올렸다(pdf는 여기). 진즉, 이 곳을 알았으면 slideshare는 안 쓰는건데 그랬다(storyQ는 레일스로 개발된 국내 서비스다. chang님께 다음 세미나에서 소개도 부탁드렸다).

 

이번에는 실제적인 코드보다는, 레일스가 어떤 생각으로 만들어졌고, 어떤 점이 다른지, 어느 부분에 열광할 수 있는지 전달하고 싶었다. 발표를 준비하며, 발표를 마치고 daesan님과 짧은 대화를 나누며 내 생각도 어느 정도 더 정리할 수 있어서 의미있는 시간이었다. 듣는 분들은 어떻게 느끼셨을지 궁금하기도 하다.

 

촬영은 했으니 곧 세미나 공식 블로그를 통해 동영상도 올릴 예정이다(부끄럽긴 하지만). 루비세미나 행사 후기는 다음 글에!

 

« older items