Ruby On Rails & 생각

짧은 시간 이었지만, 태어나서 처음으로 웹 프로젝트를 끝냈다. 나에게 ‘끝냈다’는 남들이 사용할 수 있을만한 결과물을 내 놓았다는 의미이다. 이를 기념 삼아 간단히 소회를 풀어보고자 한다.

내가 웹 프로그래밍을 해본 것이라고는 한 십 년쯤 전에 PHP로 뭔가 간단히 끄적여 본 것이 전부였다. 그러다가 Ruby를 접하고 내친김에 Ruby On Rails(이하 RoR)도 시작했지만, 태생이 태생인지라 웹서버와 브라우저간의 통신이라는 다소 미묘한 세계를 이해하는데 상당히 애를 먹었다. 서버프로그래머인 나에게 있어서, 웹 프로그래밍 – 나의 경우에는 좀 더 구체적으로 RoR이 되겠다 – 에서 사용하는 서버와 클라이언트간의 정보전달방법은, 그 추상화의 정도가 너무 심했던 것 같다. 이런 이유로 RoR에 관한 책을…, 그러니깐 다섯 권을 샀다. 물론, Ruby에 관한 책은 별도고. 세 번째 책쯤에서 느낌이 왔고, 네 번째 책에서 감을 잡았다. 그래서 다섯 번째 책은 아직 그냥 있다. 언젠간 읽겠지. 꽤 빨리 읽을 수 있을 것이다.

회사에서 게임데이터를 엑셀로 관리하고 있었다. 같은 실수가 몇 번이고 반복되고 있었고, 기획자는 툴의 필요성을 강조했다. 그래서 내가 RoR을 꺼냈다.

어느 게임회사나, 시간이 넉넉한 곳은 없다. 따라서 게임개발에 사용되는 툴은 그 중요도에 따라 완성도가 결정되는 경우가 많다. 프로그래밍지식이 없는 기획자나 그래픽담당자는 자신이 하는 일이 막노동인줄도 모르고 그냥 열심히 하며, 설사 이를 프로그래머가 알게 되었다 하더라고 전담자가 배치되지 않는 이상 제대로 된 툴이 나오지 않는다. 어지간한 규모가 아니라면, 툴을 전담하는 개발팀을 두는 회사는 거의 없다. 결국, 툴 개발은 게임프로그래머에게 추가작업을 요구하게 된다.

게임데이터를 다루는 툴은 단순해 보이지만, 이것이 DB와 연결되고 데이터 검증과정이 들어가고, 또 사용자 인증 및 히스토리기능까지 넣으려 한다면, 이를 독립된 어플리케이션으로 만드는데 꽤 많은 시간이 소모될 것은 뻔한 일이다. 더구나 원활한 편집을 위한 UI는 게임프로그래머가 구현하기엔 결코 간단한 기능이 아니다.

나는 조인을 사용하는 쿼리문을 제대로 작성할 줄 못하고, 서로 다른 DBMS의 특성도 잘 모른다. Ajax는 거의 들어만 본 수준이다. HTML은 기본적인 것만 알고, 당연히 웹 표준은 딴 세상 이야기다. 프로젝트 시작전이나 지금이나 이 사실은 변함이 없다. RoR이 멋진 것은, 이런 상태에서도 프로젝트를 끝낼 수 있었다는 것이다. 물론, 아는 것이 많을수록 더 괜찮은 프로그래밍을 할 수 있는 것이 당연하겠지만, 그렇지 않아도 in-house툴로는 충분한 결과물을 만들어 낼 수 있다.

이후에, 테스트 삼아 만들어본 또 다른 프로젝트는, 반나절 만에 동작하는 프로토타입을 만들어 낼 수 있었다. 서버의 덤프파일이 생기면 이를 모아서 관리하고 그 통계를 보여주는 웹 프로그램이었다. 이를 웹으로 구현하지 않으면, 파일전송을 위해서 FTP를 사용하든가 아예 이를 구현해야 한다. 그리고 또 이를 정보화하기 위한 작업도 필요하다. 개발자가 덤프분석을 위해 이를 다운받는 건 또 어떻게 할 것인가.

대부분의 게임프로그래머는 웹 프로그래밍에 대해 알지 못한다. 게다가 웹 프로그래밍에 대한 기술적 하대는 게임프로그래머로 하여금 웹 프로그래밍에 대한 관심을 점점 더 멀게 한다. 안타까운 점은, 동일한 목적에 대해서, 어플리케이션개발보다 웹 개발이 훨씬 더 빠르고 효율적인 경우가 많다는 것이다. 더구나, RoR처럼 생산성이 높은 웹 프레임워크를 사용한다면, 그 효과는 배가되기 마련이다. 이 일을 사내의 웹팀에게 넘기지 않고 직접 해야 하는 이유는, 그들도 바쁘거니와, 프로그래밍을 할 줄 아는 사람 중에서는 게임프로그래머가 그 게임과 만들 툴을 가장 잘 이해하고 있기 때문이다. 웹팀이 각 팀에 특화된 툴을 만들어 줄 수 있을 리도 요원하다.

나의 경우는 Ruby가 마음에 들어서 RoR을 사용한 것이지만, 어떤 언어든, 어떤 프레임워크든, 어떤 방식이든, 빨리, 그리고 사용자가 편하게 사용할 수 있는 물건을 내놓는 것이 최고 아니겠나.

마지막으로, 그 툴이 지금 어떻게 지내고 있는지 살짝 덧붙인다. 툴만 완성되면 실수가 없을 것처럼 이야기하던 기획자는 아직 그 툴을 제대로 사용하고 있지 않다. 요청한지 몇 주가 지나도록 툴 설정에 관련된 자료는 오지 않고 있으며, 그럴 의지가 있는지도 잘 모르겠다. 스크립트에 관련된 각종 자료를 정리해주는 것보다, 실수를 하더라도 지금까지의 방식을 고수하는 것이, 차라리 더 편하다고 생각하는 것 같다.

툴이 사용되지 못해도, 이를 통해서 내가 배운 것들로 충분히 행복하다. 애당초, 거기에 더 큰 비중이 있었다. 다만, 계속 부지런 떠는 그들이 마음에 걸릴 뿐이다.

Authlogic 사용하기

Authlogic이 쉽고 간단하단다. 그래서 썼다. Railscasts #160 Authlogic를 따라 하는 데, 이거 영 안 된다. 그렇다. 난 레일즈 초보다. ㅠㅠ

Authlogic은 사용자세션을 ActiveRecord클래스와 동일한 방법으로 다룰 수 있도록 Authlogic::Session::Base클래스를 제공한다. 이 클래스를 자동으로 생성하기 위해서는 generate스크립트를 사용할 때 generator로 session을 지정하면 된다.

$ ruby script/generate session user_session

이는 사용자의 로그인/로그아웃을 ActiveRecord클래스가 데이터를 추가하거나 삭제하는 것처럼 처리할 수 있게 해주며, 실제 사용자 정보를 가질 (진짜 ActiveRecord를 상속한)모델클래스도 필요하다. 이 모델클래스를 Authlogic가 사용하게 하기 위해서는 클래스에 acts_as_authentic지시자를 추가해 주어야 한다.

class User < ActiveRecord::Base
  acts_as_authentic
end

데이터모델클래스의 칼럼에는 string타입의 persistence_token이 존재해야 하며, 이 외에 다음과 같은 값을 사용할 수 있다.

t.string    :login,               :null => false                # optional, you can use email instead, or both
t.string    :email,               :null => false                # optional, you can use login instead, or both
t.string    :crypted_password,    :null => false                # optional, see below
t.string    :password_salt,       :null => false                # optional, but highly recommended
t.string    :persistence_token,   :null => false                # required
t.string    :single_access_token, :null => false                # optional, see Authlogic::Session::Params
t.string    :perishable_token,    :null => false                # optional, see Authlogic::Session::Perishability

# Magic columns, just like ActiveRecord's created_at and updated_at. These are automatically maintained by Authlogic if they are present.
t.integer   :login_count,         :null => false, :default => 0 # optional, see Authlogic::Session::MagicColumns
t.integer   :failed_login_count,  :null => false, :default => 0 # optional, see Authlogic::Session::MagicColumns
t.datetime  :last_request_at                                    # optional, see Authlogic::Session::MagicColumns
t.datetime  :current_login_at                                   # optional, see Authlogic::Session::MagicColumns
t.datetime  :last_login_at                                      # optional, see Authlogic::Session::MagicColumns
t.string    :current_login_ip                                   # optional, see Authlogic::Session::MagicColumns
t.string    :last_login_ip                                      # optional, see Authlogic::Session::MagicColumns

또한, 이 모델클래스가 Authlogic의 세션모델과 연동되기 위해서는 세션모델의 이름이 데이터모델의 이름에 Session이 덧붙은 형태여야 한다. 즉, 데이터모델이 User라면, 세션모델의 이름은 UserSession이어야 하는 식이다.

이렇게 Authlogic의 사용준비를 마쳐서 User라는 데이터모델클래스와 UserSession라는 세션모델클래스가 마련되었다면, 사용자의 계정정보는 User클래스를 사용하여 저장하고, 세션정보는 UserSession클래스를 사용하여 다룰 수 있게 된다.

음, 이렇게 쓰고 보니 정말 쉬운 것 같기도 하다. – -;

RoR: remote_form_for와 테이블

루비온레일즈에서 테이블의 별도의 셀에 입력 폼을 나눠 배치했을 때, form_for를 사용하면 문제가 없었지만, remote_form_for를 사용하면 파라미터 값이 넘어오지 않는 문제가 있어서 엄청나게 삽질했다. 인터넷에 검색해보니 다음과 같은 내용이 있다.

Tables and forms can be nested either way. But if you put forms into tables, each form must be completely included into a single table cell (one TD element in practice). Thereby the forms are each completely independent.

결국, 테이블 밖으로 remote_form_for 블록을 빼니 파라미터 전달이 잘 된다. form_for는 왜 문제가 없는지 잘 모르겠지만, 테이블과 폼을 중첩시키는 것이 옳은 방법은 아닌 듯하다.

웹이라는 또 다른 패러다임에 적응하느라 삽질 중. 낯선 것이 너무 많다.