Django_vs_Rails


Youngrok Pak at 12 years, 3 months ago.

6개월간 RubyOnRails 프로젝트를 하나 하고 이후 4개월간 Django로 프로젝트를 네 개 했다. 그리고 그 차이를 비교해보고자 한다.


이 글을 처음 쓰기 시작한지 거의 10개월이 지났다. 좀 시간 나면 완성해야지 하면서 미뤄오다가 이렇게 되버려서 그 동안 Django와 Rails의 무수한 업데이트를 반영하지 못한 글이 되버렸다. 그래서, 뒤늦게나마, 낮은 완성도로나마 마무리를 짓고 공개하기로 했다. 이 글은 Rails 1.2 시절 기준, Django 0.96.1 릴리스 버전 기준이기 때문에 지금과는 많은 차이가 있다는 점을 감안하기 바란다.

python vs ruby

비교에 앞서 공정함을 위해 Django 프로젝트를 시작할 당시의 두 언어에 대한 나의 배경 지식을 언급할 필요가 있을 것 같다. 일단 파이썬은 Learning Python을 꼼꼼히 읽었고 컴파일러 프로젝트를 하나 파이썬으로 수행했고 몇 차례 작은 스파이크를 해본 적이 있었다. 자바만 하다가 Learning Python을 읽으면서 많은 감동(?)을 받았기 때문에 파이썬에 대한 호감은 상당히 높았다. 루비는 Programming Ruby를 설렁설렁 읽었고 대안언어축제에서 조금 더 맛본 후 스프링노트 프로젝트를 했고 그 때의 감상을 스프링노트_기술보고서에 밝혀놓은 바 있다.

개인적으로 좋아하고, 자바 다음으로 익숙한 언어였음에도 파이썬을 쓰는 내내 루비랑 비교가 되었다. 루비라면 이렇게 할 수 있는데, 루비라면 이렇게 할 수 있는데... 물론 RubyOnRails 프로젝트 할 때도 파이썬이라면... 하는 경우가 있었고 또 이번에 아, 파이썬이라서 이런 것도 가능하구나 하는 경우도 많았다. 비슷한 언어로 알고 있었지만 둘다 어느 정도 깊이 써보고 나니 이 둘의 차이는 생각보다 컸다. Django와 Rails의 가장 큰 차이는 바로 루비와 파이썬의 차이이므로 이 둘을 이야기하지 않을 수 없다.

List Comprehension

파이썬에서 가장 아쉬운 부분이 이 부분이다. 간단하게 몇 가지 예를 비교해보자. 간단하게 필터링하는 경우 루비는 다음과 같이 할 수 있다.

  • user_list.select {|user| user.is_manager }

그런데 파이썬에서는 list comprehension으로 다음과 같은 코드가 된다.

  • [user for user in user_list if user.is_manager()]

이 방법은 앞 부분, user for user in은 거의 필요 없는 코드다. list comprehension의 문법적 한계다. 다른 방법으로 filter를 이용할 수도 있긴 하다.

  • filter(lambda user: user.is_manager(), user_list)

좀더 간단해졌지만 너무 수학적이라는 느낌이 든다. 사실 더 중요한 문제는 사고의 흐름을 좀 끊는다는 것이다. 일반적으로 리스트를 다룰 때는 그 리스트에 대해서 먼저 생각을 하고 그 다음에 어떻게 처리할지를 생각한다. 내가 사용자 목록을 다루고 싶다면 user_list를 먼저 떠올리는 것이 자연스럽다. 그러고 나서 사용자 중에 is_manager를 만족하는 애들을 뽑아내야지 생각하게 된다. 그러면 코드에도 당연히 user_list를 먼저 쓰게 마련인데 루비는 그 다음부터 자연스럽게 코드를 이어나가지만 파이썬에서는 다시 커서를 앞으로 옮겨서 list comprehension으로 바꾸든지, filter를 쓰든지 하게 된다. list comprehension이 얽히는 문제를 다룰 경우 이 문제는 더 심각해진다. 조금 복잡한 예를 보자.

  • posts.reject {|post| post.comment_count == 0 }.collect {|post| :title=>post.title, :selected=>post.id == id }

블로그의 post 중에서 답글이 없는 포스트를 골라서 hash로 바꾸는데 그 과정에서 선택된 것에는 표시를 하는 코드다. 코드 조각의 순서는 거의 사고의 순서와 일치한다. 그런데 같은 코드를 파이썬으로 한다면 어떨까? 내 경우는 일반적으로 다음과 같은 진화 과정(?)을 거친다.

  • posts.
    [for post in posts]
    [post for post in posts]
    [{'title':post.title, 'selected':post.id == id} for post in posts]
    [{'title':post.title, 'selected':post.id == id} for post in posts if post.comment_count > 0]

커서가 몇 번이나 왕복한다. 익숙해지면 바로 [부터 써 나가게 되지 않을까 싶지만 의외로 그게 그렇게 잘 안된다. 리스트를 다루는 작업이라면 아무래도 어떤 리스트(posts)를 다룰 것인지를 떠올리는 게 자연스럽다. 그리고 그 다음에 그 리스트를 필터링할 조건을 생각하거나 변형할 작업을 생각하게 된다. 근데 파이썬의 list comprehension은 거듭해서 사고의 흐름을 끊는다. 웹 프로그래밍은 아무래도 리스트 조작이 많은 만큼 이런 부분이 편리한 루비가 좀더 강점을 갖는 것 같다.

귀도는 리스트를 복잡하게 다루어야 한다면 그냥 for 루프를 돌리라고 한다. 파이썬은 함수형 언어가 아니라면서. 파이썬의 for 루프가 C 계열의 for 루프를 아주 많이 개선한 것은 사실이고 나도 처음 파이썬의 for 루프를 접했을 때 참 좋아했었다. 근데 10년도 넘게 써왔던 for 루프의 사고 흐름보다 단 몇 개월 써본 루비의 each가 더 자연스럽게 느껴진다는 것은 루비의 방식이 좀더 자연스러운 사고에 가깝다는 뜻으로 해석해도 되지 않을까.

import vs require

또 하나 사고의 흐름을 끊는 것이 import다. 예를 들어 django에서는 프로젝트명의 최상위 패키지를 두고 그 아래에 애플리케이션 이름으로 패키지를 둔 다음 그 아래에 파일들을 만들게 하고 있다. 이를테면 ecus.issue 패키지 아래에 models.py 모듈이 있고 이 안에 User 클래스가 있는 식이다. 이런 경우 User 클래스를 사용하고 싶으면 대개 다음처럼 쓰게 된다.

from ecus.issue.models import User
User.objects.get(name='xxx')

근데 models 안에 여러 클래스를 사용하고 싶다면 다음과 같은 방법들을 사용한다.

from ecus.issue.models import User, Tag, Project
User.objects.get(name='xxx')

from ecus.issue.models import *
User.objects.get(name='xxx')

from ecus.issue import models
models.User.objects.get(name='xxx')

이런 다양한 방법이 가능한 것은 어떻게 보면 파이썬 문법의 일관성 때문이다. 패키지, 모듈, 클래스가 모두 namespace이고 또 first-class object이기 때문에 가능한 것이다. 그래서 입맛에 맞는 방법으로 쓰기는 좋지만 아무래도 가능한 조합이 많다보니 컨벤션이 제각각인 경우가 많다. 그리고 바로 사고의 흐름을 끊는 부분이 바로 from이다. 이것은 from이 나오기 전의 일반적인 import 문에서 기인하는 것인지도 모른다. 파이썬 코어 모듈들은 보통 다음처럼 쓴다.

import sys
print sys.path

게다가 from import건 import건 둘다 프로그래머의 머리 속에는 import라는 개념으로 들어가 있다. 그러다보니 뭔가를 import해야 할 때 다음과 같은 사고의 과정을 거치게 된다.

import ecus.issue.models.User

from ecus.issue.models.User

from ecus.issue.models import User

이 문제 역시 list comprehension과 마찬가지로 단순히 익숙함의 문제가 아니라 일반적인 사고의 흐름에서 벗어나 있기 때문에 발생하는 문제다. 파이썬 3000에서 코어 모듈들이 자바처럼 다단계 패키지로 구조화되고 절대 경로 import만 가능해지므로 from import와 import의 이런 혼동 문제는 줄어들겠지만 그렇다면 굳이 import하는데 두 개의 키워드를 사용해야 하는 이유는 무엇인가.

소제목을 import vs require로 붙였지만 사실 python import vs java import를 하고 싶었다. 루비의 require도 마음에 들지 않기 때문이다. 이런 개념만큼은 자바가 제일 나은 것 같다.

루비 require의 문제는 계속 지적되어 왔던 require_gem의 문제다. 같은 라이브러리라도 gem으로 설치한 것은 require 'rubygem'을 한 후 써야 하고 파일로 설치한 것은 그냥 require로 써야 한다. 사실 난 아직도 require_gem에 대해 잘 모른다. rails 안에서는 그냥 require로 쓸 수 있는데 그냥 ruby 애플리케이션으로 돌릴 때는 require_gem을 써야 한다. 아마도 rails에서 이 문제를 해결한 코드를 넣어둔 것 같다.

또 다른 문제는 require가 파일 경로를 사용할 수 있다는 것이다. 자바나 파이썬은 패키지의 경로를 PYTHON_PATH나 CLASSPATH에서만 찾고 그 경로에서 시작해서 상대 경로로 위치를 추적한다. 하지만 루비의 require는 파일 실제 경로가 가능하기 때문에 PYTHON_PATH에 해당하는 루비의 라이브러리 경로가 적극적으로 활용되지 않는다. 그래서 Rails 안에 보면 다음과 같은 코드가 종종 보인다.

require File.dirname(__FILE__) + "/../spec_helper"

만약 디렉토리를 한 단계 더 깊이 만들면 다음과 같은 코드로 써야 한다.

require File.dirname(__FILE__) + "/../../spec_helper"

이 무슨 지저분한 짓인가. 게다가 루비의 module 개념은 일을 더 어렵게 만든다. 자바나 파이썬에서는 패키지 구조가 그대로 네임스페이스로 쓰이지만 루비에서는 루비 파일명이 네임스페이스와 상관이 없다. 그래서 내부적으로 module을 사용해서 별도의 네임스페이스를 만들어야 한다. 그래서 다음과 같은 코드가 등장하곤 한다.

module Spec
  module DSL
    class Runner
      ...
      ...
    end
  end
end

Spec::DSL::Runner.new

이런 식으로 네임스페이스를 사용한다. C++의 namespace와 거의 같다. 물론 루비와 자바가 다른 것 뿐이라고 주장할 수도 있겠지만 어쨋거나 자바에서 클래스, 패키지 두 가지 개념으로 할 수 있는 일을 루비에서는 클래스, 모듈, 파일 세 가지 개념으로 해야 하는 것이니 더 어려운 건 사실이다. 어쨋건 파이썬이나 루비나 C의 include보다 낫다고 말하기 힘든 것 같다.

괄호

내가 루비의 가장 큰 장점으로 꼽는 것이 바로 괄호다. ambiguous하지 않은 상황에서 괄호를 생략할 수 있기 때문에 다른 언어에 비해 아주 매끄러운 DSL이 가능하다. 예를 들어 Selenium RC에서 사용하는 예를 보면 루비는 이런 코드가 가능하다.

open "/fixtures/load"
open "/users/5"
click "change_property"
type "property_name" "xxx"
assert "property_value" "type in..."

파이썬에서는 다음처럼 된다.

self.open('/fixtures/load')
self.open("/users/5")
self.click("change_property")
self.type("property_name" "xxx")
self.assert("property_value" "type in...")

큰 차이가 아니라고 할 수도 있겠지만 코드 자체를 Specification으로 사용해도 별 무리가 없는 루비 코드에 비해 파이썬 코드는 프로그래밍 언어의 냄새가 너무 많이 난다. 그래서 연산자 오버로딩을 이용해서 다음처럼 해보기는 했다.

open | '/fixtures/load'

하지만 이렇게 하기까지의 과정은 꽤 고통스러웠다. 작은 차이 같지만 가독성에 꽤나 큰 차이를 불러오는 것 같다. BDD의 경우도 루비에는 RSpec이 메이저가 되어가고 있지만 파이썬에는 별다른 BDD 프레임웍이 등장하지 않는 이유가 이런 차이 때문이 아닌가 싶기도 하다.

데코레이터

파이썬이 루비보다 나은 점 중 하나가 바로 데코레이터다. 바로 비교 코드를 보자. 파이썬에서 컨트롤러에서 로그인 체크해야 하는 메소드에 표시를 하고 싶다면 다음과 같이 하면 된다.

def read:
 ...

@login_required
def edit:
 ...

@login_reuiqred
def update:
 ...

자바의 annotation과도 비슷하다. 루비에서는 이걸 이렇게 처리하고 있다.

before_filter :logrin_required => :edit, :update

def read
 ...
end

def edit
 ...
end

def update
 ...
end

해당 메소드에서 가까운 곳에 선언할 수 있다는 점이 장점이다. 인자도 받을 수 있기 때문에 다음과 같은 코드도 가능하다.

@permision_required('owner', models.Issue)
def update:
 ...

루비도 사실 흉내는 낼 수 있다.

permision_required :update => 'owner', Issue
def update
 ...
end

하지만 역시 파이썬의 데코레이터 문법이 좀더 세련되어 보이고 중복이 적다.

Model

모델은 예전에 스프링노트_기술보고서에서 ActiveRecord가 Rails에서 주장하는 DRY(Don't Repeat Yourself)를 제대로 해내지 못했다는 이야기를 하면서도 이게 의외로 실용적인 대안일 수 있다는 이야기를 했었다. 그 이유는 마이그레이션이다. Rails에서는 모델을 바꾸는 공식적인 방법이 migrate를 이용하는 것이기 때문에 자연스럽게 마이그레이션을 할 수 있다...고 주장한다. 하지만 현실은 그렇지 않다. migrate는 단지 마이그레이션 SQL을 어디 기록해두는 것에 비해 더 낫지 않았다. 자동으로 되는 것도 아니고 어차피 수동으로 마이그레이션 스크립트를 짜야 하는 건 똑같다. 유일한 장점이 up/down이 가능하다는 것인데 그나마도 마이그레이션 스크립트를 짤 때 model 객체를 사용하면 up/down을 못하는 경우가 생긴다. 그래서 루비교 한국 교구장이신 deepblue군은 SQL로 마이그레이션 짤 것을 권고한다. 그렇지만 SQL로 마이그레이션을 짠다면 Rails 마이그레이션의 장점은 무엇인가? 그냥 데이터베이스 변경할 때마다 SQL을 기록해두는 것보다 별로 나은 점이 없다. 실용적이 아니라고 할 수는 없지만 예전에 쓰던 방법에 비해 더 실용적이진 않다. 결국 DRY 원칙은 깨뜨렸지만 그 대가는 얻지 못한 것이다.

마이그레이션 스크립트 자체에도 문제가 있다. 테이블간 관계를 형성할 수가 없다. Rails의 모델이 전부 id를 외래키로 사용해서 관계를 만드는데 그조차도 공식 명령이 지원되지 않는다. 지금은 어떤지 모르겠으나 2.0 발표 시점에는 없었던 걸로 기억한다. 그래서 결국 SQL을 써야 했다. migrate는 루비 스크립트이고 ORM을 위한 것임에도 불구하고 SQL이 없으면 안되는 것이다.

이에 비하면 Django는 좀 더 DRY하다. 모델 객체만 바라보면 되고 SQL을 써야 하는 경우는 거의 없다. 모델 객체를 수정하고 syncdb를 수행하면 모델대로 테이블이 자동으로 만들어지고 관계도 자동으로 형성된다. 다만, 몇 가지 문제는 있다. 하나는 syncdb가 새로 만드는 테이블에만 적용되고 테이블 변경을 반영해주지는 않는다는 것이다. 그래서 Django가 DRY는 지켰지만 그 과정이 자동은 아니다. 그래도 개발 단계에선 별로 문제가 안된다. 테스트용 데이터는 fixtures에 다 들어 있기 때문에 테이블 바뀌면 그냥 테이블 덮어 쓰면 되기 때문이다. 어차피 TDD로 개발하다보면 테스트 한 번 돌릴 때마다 fixture를 로드해야 하기 때문에 테이블 내용을 갈아 엎는다. 그래서 개발 단계에선 syncdb 명령보다 reset 명령으로 DB를 통째로 다시 만들게 된다. 100개가 넘는 테이블이 있어도 테이블 재생성하고 fixture 집어넣기까지 1초를 거의 안 넘는다.

문제가 되는 건 리얼 환경이다. 리얼 환경에서 데이터를 갈아엎을 수는 없다. 그래서 이 경우는 전통적인 방법대로 SQL을 이용해서 마이그레이션을 하게 된다. 결국 리얼 환경에선 Rails보다 매끄럽지 못한 부분이 있다.

그래도 Rails보다 나은 점이 하나 더 있다. Rails에서는 모델 변경을 할 때마다 마이그레이션을 해줘야 하지만 Django에서는 리얼에 반영할 때만 마이그레이션을 하면 된다. 그 외의 경우는 그냥 reset 명령으로 갈아 엎으면 된다. 무슨 얘기냐 하면, Rails에서는 리얼 환경에 반영하지 않는 테이블 변경 사항도 일일이 마이그레이션으로 만들어야 하는데 Django에서는 개발 단계에서 여러 번의 테이블 변경이 있더라도 최종적인 리얼 환경의 마이그레이션만 준비하면 된다는 것이다. 그래서 개발 단계에선 Django가 훨씬 애자일하다. 모델의 속성 하나 추가하고 싶으면 부담 없이 모델 객체에 한 줄 추가하고 reset하면 된다. Rails에서는 속성 하나 추가하려면 마이그레이션 스크립트 하나를 짜야 한다.

실제로 뭔가 간단한 애플리케이션을 만들어 보고 싶을 땐 이 차이가 결정적으로 작용한다. 그냥 간단한 게시판 정도의 테이블에 몇 가지 실험만 해보면 되는 건데 Rails로 작업하려면 마이그레이션을 짜야 하니 고통스럽다. 반면 Django는 그냥 생각을 그대로 코드에 기술하고 돌리면 된다. 그래서 나는 지금도 뭔가 빨리 만들어야 하는 경우가 생기면, 그리고 컨트롤러가 많지 않다면 Django를 이용한다.

Controller

컨트롤러에서 Rails와 Django가 차이가 나는 부분은 명시성이다. Rails에선 컨트롤러의 메소드에서 사용할 수 있는 것들이 죄다 숨어 있다. request, params, cookie에 각종 메소드 등이 모두 상속과 injection으로 들어가 있어서 그냥 컨트롤러 코드만 보면 request를 사용할 수 있다는 단서가 없다. 알고 나면 코드가 한 줄이라도 줄어드니까 좋은 점도 있고 중복이 줄어든다고도 할 수 있지만 리팩토링의 중요한 두 가지 원칙, 중복을 제거하라의도를 드러내라에서 전자만 만족되는 셈이다. Rails의 코드를 보면 이런 것들이 많이 발견된다. 중복을 정말 극한까지 제거하려는 노력들이 많아서 정말 필요한 코드만 쓰면 되는 경우가 많고 syntactic noise가 아주 적은 코드를 만들 수 있다. 하지만, Rails에 익숙한 사람이 작성한 코드를 Rails의 세세한 기능을 모르는 입문자가 볼 경우 거의 손댈 수 없는 경우가 많다.

helper는 이런 특성을 더 강화한다. helper는 개별 view에서 자주 쓰는 메소드를 모아 놓은 것인데 view에서 못 보던 메소드가 쓰이면 이게 Rails에 원래 있는 건지, 어느 helper 클래스에서 온 건지 찾기가 어렵다. 그래서 메소드의 정의를 찾기 위해 파일 서너 개를 뒤져야 하는 일도 흔하게 생긴다. 전반적으로 Rails가 API를 죄다 외우고 있으면 정말 막강하고 편리하지만 그렇지 않을 경우에는 문서도 부실하고, 코드 내에서 단서를 찾을 수 없어 그 편리함을 활용하기가 어렵다.

이에 비해 Django에선 컨트롤러에서 사용하는 것은 모두 import를 하거나 메소드의 인자로 받아야 하기 때문에 명시성은 잘 충족된다. 하지만 그 대가로 컨트롤러의 코드 자체는 Rails보다 번잡하다. 그리고 또 하나의 단점은 Django에서는 컨트롤러에 해당하는 것을 view라고 부른다는 것이다. MVC에서 view에 해당하는 부분은 template이라고 부른다. 그래서 Rails는 Model-View-Controller인데 Django는 Model-View-Template이다. 근데 이게 개념적으로는 다르지 않으면서 이름만 바꿔놔서 별로 좋지 않다. 나중에 뷰 이야기할 때 자세히 이야기하겠지만 Django에서 이렇게 한 데는 의도가 있지만 그 의도가 그리 좋지 않은 것 같다.

URL dispatch

view에서 또 하나 중요한 것은 URL 매핑이다. Django는 정규식 기반의 URL 매핑을 제공하고 Rails는 소위 ConventionOverConfiguration 개념의 URL 매핑을 제공한다. 그냥 쓰기는 Rails 쪽이 훨씬 편하다. REST로 할 경우도 Rails 쪽 지원이 월등히 좋다. 하지만, URL을 마음대로 튜닝하고 싶으면 아무래도 정규식 기반을 사용하는 게 좀 더 낫다. Rails에서도 비슷한 작업들이 가능하지만 디폴트를 벗어나는 행동을 할 때는 역시 암기력에 의존해야 하는 문제가 있다.

View

Django Template은 최대한 기능을 줄이려는 생각이 들어가 있다. 웬만한 작업은 view에서 다 하고 template에서는 그냥 뿌려주기만 하라는 것이다. 그래서 부가적인 작업을 좀 해주지 않으면 메소드 실행조차 못한다. template에서 좀 복잡한 작업을 하려면 tag나 filter를 만들어야 한다. 거의 자바 태그 라이브러리 수준으로 내려가는 것이다. 이 불편함만으로도 Django Template에는 좋은 점수를 줄 수 없다.

파이썬에는 파이썬 고유 문법을 그대로 활용할 수 있는 좋은 템플릿 엔진이 많다. cheetah도 있고 mako도 있다. 이런 템플릿 엔진을 사용할 때는 JSP의 <% %>, <%= %>에 해당하는 정도만 배우면 나머지는 파이썬으로 다 해결할 수 있다. 하지만 Django Template은 Django Template이란 언어를 새로 배워야 하는 거나 마찬가지다. 필터의 문법도 불만이다. 파이썬 메소드 연쇄로 충분히 쉽게 할 수 있는 일을 필터까지 만들어가면서 해야 하는 이유가 무엇인가. if도 심각하다. 조건문에 파이썬 표현식을 못 쓰고 변수만 쓸 수 있다. 그래서, 두 값이 같다는 것을 비교해야할 때는 if가 아니라 ifequal을 써야 한다. 아무리 생각해도 Django Template은 내가 경험한 모든 종류의 템플릿 엔진 중에 최악이다.

다행스러운 것은 view와의 결합도가 높지 않아서 쉽게 다른 템플릿 엔진으로 교체할 수 있다는 것이다. Mako 정도면 좋은 선택이 아닐까 싶다. cheetah가 더 맘에 들지만 $ 기호가 prototype의 $와 충돌해서 골치 아프다.

Rails는 별로 나무랄 데가 없다. 스프링노트_기술보고서에서 언급했던 생각들은 여전히 유효하지만 RHTML보다 나은 템플릿 엔진은 찾기 힘든 것 같다. .

테스트

그냥 개발 중의 테스트 환경은 django가 좋다. 다른 것보다 리로드 속도가 압도적으로 빠르고 모든 자원이 리로드된다. Rails는 lib에 있는 것들이 리로드 안되서 이 부분에 뭔가 넣고 개발할 때는 불편함이 많았고 리로드 속도가 너무 느렸다. 물론, 그래도 자바보다는 빠르다. 요즘은 본의 아니게 자바로 웹 개발을 다시 하고 있는데 Django나 Rails 하다가 자바로 개발하면 짜증 많이 날 것이다. 스프링을 안 쓰는 것만 해도 다행이라는 생각이 든다.

TDD 측면에서 본다면 Django 쪽에 점수를 주고 싶다. 자동화라든가, TDD 관련 기능은 Rails가 압도적으로 많지만 Rails에서는 테스트할 부분을 isolation 시키기가 너무 힘들다. 엔진이 뜨면서 MonkeyPatching을 너무 많이 하기 때문에 그냥 rb 파일만 require한다고 테스트할 수 있는 게 아니라 거의 엔진을 다 띄우다시피 해야 테스트를 할 수 있다. 아니면 MockObject를 산더미처럼 만들거나. 도요타_방식에서 복잡한 것을 자동화하지 말고 복잡한 것을 단순하게 만든 다음 자동화하라라는 말이 있는데 Rails는 그냥 복잡한 것을 그대로 자동화시켜버려서 이것 이외에도 많은 문제점이 있다.

이에 비해 Django는 기능적으로는 부족함이 많지만 그래도 pythonic함을 잘 지키고 있기 때문에 개별 모듈 테스트하는 게 일반 파이썬 개발과 다를 바가 없다. 나름대로 복잡한 것을 단순화시키는 과정을 꽤 많이 거친 흔적이 보인다.

AcceptanceTest 측면에서 본다면 Rails가 좀더 낫다. Selenium도 쉽게 통합할 수 있고 StoryTest라는 멋진 물건도 있다. 문법 자체의 syntactic noise가 적은 것도 중요한 장점이다. 이렇게 보면 UnitTest는 Django 승, AcceptanceTest는 Rails 승이라고 해도 될 것 같다.

Deployment

Django는 apache + mod_python이 권장 환경이고 설정 파일 하나만 만들면 deploy할 수 있다. 하지만 쉽다고는 말 못한다. 디렉토리만 복사하면 되는 PHP나 war 파일을 떨구거나 서블릿 엔진의 webapps 디렉토리에 복사만 하면 되는 자바에 비하면 설정 때문에 삽질할 일이 좀 있을 것이다.

Rails는 deploy 관련 툴이 많지만 별로 좋은 게 없다. 역시 복잡한 것을 단순화시키지 않고 자동화한 것이 문제다. 서버 성능도 여전히 문제다. http://myruby.net/ 의 소식에 따르면, 이젠 Passenger라는 게 나와서 mod_rails처럼 쓸 수 있다고 한다. 왜 진작 이렇게 안했을까 싶긴 하지만 지금이라도 이렇게 하니 다행인 것 같다. 이게 제대로만 되면 Django보다 deploy에서 우위를 점할 수 있을 것이다.

총평

비슷해 보이는 두 프레임워크지만 사상적 차이(?)로 인해 두 프레임워크의 차이는 작지 않다. 프로그래머의 성향에 따라서도 많이 갈릴 것이다. 난 사실 둘다 만족스럽지 않다. 그래도 선택하라면 Django 쪽을 선택할 것 같다. Django 쪽의 불편함들은 몇 줄 내지 몇십 줄로 해결할 수 있는 게 대부분인 반면, Rails의 불편함은 해소가 거의 불가능하다. 리팩토링 책에 보면 리팩토링은 코드에 대한 통제권을 회복하기 위한 것이라는 말이 있다. 그런데, Rails를 쓰다보면 코드 전반에 대한 통제권을 쥐기가 힘들다. 그러려면 Rails의 모든 기능을 외워야 한다. Rails의 자동화도 유연하지 않아서 뭔가 약간 바꾸려고 하면 수많은 코드를 헤매면서 고쳐야 한다.

그래도 Rails를 버리기 아까운 이유는 루비라는 언어다. 파이썬의 list comprehension에 비해 루비 블럭의 리스트 처리가 월등히 편리하다. 웹 애플리케이션의 특성상 리스트를 처리할 일이 많기 때문에 이 점은 Rails의 큰 강점이다. 그리고 보다 DSL에 가까운 문법을 만들기 쉽다는 것도 버릴 수 없는 장점이다. 그래서, python4ply를 이용해서 파이썬 문법을 좀 바꿔가면서 해볼까 하는 생각도 하는 중이다.

어쨋든, 둘 중 뭘로 해도 생산성에서 자바를 압도할 수 있는 것은 분명하다. 오늘만 해도 자바에서 char[] array를 LinkedList<Character>로 바꾸는 방법을 찾기 위해 30분을 날려먹었다. 파이썬이나 루비에서는 할 필요도 없는 일인데 말이다.


Comments




Wiki at WikiNamu