Sablog Models/인터넷·웹

장고는 반쪽 MVC이다

어­리 2013. 12. 1. 02:13

요 며칠 간 프로젝트를 진행하면서 장고(Django)를 쓰기 시작했다. 사실 장고 자체를 접해 본 건 다소 오래 된 일이었던 것으로 기억한다. 아마 서버를 겸해 공부한답시고 설치한 리눅스가 너무 어려워서 날리면서 한동안은 리눅스와 거리를 두고 살았고, 파이썬도 잊었을 것이다. 그 이후 호스팅 업체를 알아보면서 윈도우 기반으로 PHP+MySQL이 돌아가는 APM을 쓴 적도 있다. 지금은? 만약 지금의 내가 과거의 내게 충고할 수 있다면, PHP를 가능한 대안으로 생각할 여지를 만들도록 놔 두지 않을 것이다. 요즘은 멀티부팅을 밥 먹듯 한다.

장고는 MVC 웹 프레임워크이다. 구체적으로는 DBMS에 대한 파이썬 클래스 ORM 래핑이 M이고, HTTP Request-Response를 수행하는 파이썬 객체 래핑이 V이며, URL 패턴에 따라 V를 적용(dispatch)하는 파이썬 코드와 그 밖의 데코레이터 기능이 C에 들어간다. 잘 보면 C의 의미가 MVC에 비해 다소 다른데, 이 떄문에 장고는 MVC의 변종인 MTV라고도 한다. 그러나 장고처럼 백엔드가 M과 V를 구성하기 위한 유틸리티가 되는 상황에서, 다 만들어진 M과 V에 대해 C가 또 한 번의 추상화를 하는 건 MVC 모두의 코드를 늘리는 퍽 불필요한 짓이다. 이런 건 대강 MVC가 맞다고 해도 되지 않나 싶다.

아무튼 올해에야 장고를 잡은 건 PHP를 버린 이후로 내가 서버 프로그래밍을 게을리 했다는 걸 방증하긴 하는데... 그 외에도 내가 장고를 잡기 꺼리고 레일즈나 스프링 MVC를 건드린 이유가 몇 가지 있었을 것이다. 파이썬 패키지 관리가 단연 첫째 문제였는데 (그래서 이번 서버는 우분투로 하고 pip만을 썼다) 다른 프레임워크에 비하면 파이썬은 몇백 배 나은 배포사용(deployment) 환경을 가지고 있었다... 그 다음 문제가 뭐였는지 기억도 잘 안 나고, 그냥 장고를 쓰기로 했다. 적절한 결정 덕분에 지금까지 별 난관 없이 장고로 그럭저럭 서버 하나를 완성해 나가고 있다. 유일한 문제라면 장고가 슬슬 마음에 안 든다는 것이다.

장고의 문제는 MVC 모두에 존재한다. 우선 M에서 가장 큰 문제는 장고 ORM이 정책적으로 DBMS에 구현된 기능에 대한 래퍼라는 것이다. 아무리 Enum의 구현이 RDBMS마다 다르기로서니 장고 규모의 프레임워크에서 models.EnumField가 없는 게 말이 되는가. 무조건 serial로 PK를 만드는 문제는 또 어떤가! 이런 정책은 ORM과 RM 모두에 해악이다. 한편 V에서 가장 큰 문제를 꼽자면, M과는 역설적으로 장고의 규모가 너무 크다는 점일 것이다. 나는 왜 @csrf_exempt가 데코레이터(decorator)여야 하는지 이해할 수 없다. 분명 장고의 액션 기반 HTTP 뷰 모델은 성공한 모델이고, 여타의 웹 프레임워크와 함께 놓고 보아도 꽤 성숙한 축에 속한다. 그러나 장고의 V를 이용해 좀 더 복잡한 일을 하려고 하면 반드시 문제가 생긴다. 거기 당신, 장고를 이용해 코드 중복 없이 XML과 JSON으로 된 API를 모두 제공하는 서버를 객체지향적으로 짤 수 있는가?

그러나 이와는 비교도 안 되는 문제가 바로 C의 문제이다. 운을 떼자면, 나는 SOAP/REST 양시론자이다. MVC냐 아니냐의 문제는 차치하더라도 여기서 문제가 발생한다. 장고의 URL dispatcher는 REST에만 대응한다. 그러나 그마저 반쪽 REST이다! 그 이유는 간단한데, URL만 신경쓸 뿐 Method에 따른 dispatching이 전혀 없기 때문이다. URL에 따라서만 뷰가 달라지는 프레임워크는 다시 말해 URL 패턴에 따라 뷰가 달라지는 환경에서만 사용 가능하다. 이 방식은 같은 자원에 대해 메서드와 헤더에 민감하게 반응하기 어렵고 따라서 OPTIONS나 Accept를 놓치기 쉽기 때문에 REST 의미론과 배치되는 경향이 있다. 그렇다고 장고에서 무리해서 메서드를 먼저 걸러내자니 뷰를 자원 객체처럼 만들게 되고 여러 URL 패턴을 하나의 뷰에 대응시키면서 지저분한 코드만 남는다.[각주:1] 아마 이것이 앞으로 장고에서 고쳐지는 일은 없을 것이다. 참고로 URL과 Method dispatching을 모두 해 주는 프레임워크도 있다. 스프링 MVC가 대표적이다.

그래서 어떻게 할 거냐고? 물론 다음에 웹 서버를 만든다고 해도 여전히 파이썬을 쓸 생각이다. 하지만 새로운 프레임워크를 배울 시간이 모자라지만 않다면 다른 라이브러리로 건너갈 생각이 강하다. Flask같은 마이크로프레임워크는 영 취향이 아니고, 데이터 모델로는 SQLAlchemy를, 웹 서비스 모델에는 Werkzeug를 고려할 것이다. 아무래도 장고의 한계를 느꼈기 때문이다. 물론 장고는 좋은 웹 프레임워크이다. 그러나 연장 탓을 많이 하는 프로그래머에게는 남이 만든 프레임워크가 맞지 않는지도 모르겠다.


추가(2013-12-1 16:07). Class-based view를 어떻게 생각하느냐는 의견을 전달받았다. 일단 "괜찮지 않나, 잘 모르겠다" 정도로 대답했는데, 아무래도 장고를 더 써 봐야 알 것 같다. 클래스 기반 뷰 자체는 REST와 잘 상통하는 면이 있지만, 사실 이상적인 웹 서비스에서 REST의 자원 객체는 데이터베이스 ORM 객체와 크게 다를 게 없기 때문에 개념적으로 생각하자면 같은 모델을 두 번 정의하는 게 된다. 굳이 중복을 없애자면 데이터 모델을 만들고 DB나 HTTP API를 위한 데코레이터를 작성하는 방식이 나은데 이건 장고 프레임워크의 장점을 완전히 깎아 먹자는 말이므로 패스. 그리고 사족이지만 WebDAV같은 프로토콜은 django.views.generic.View를 평범하게 써서는 구현할 수 없다...

  1. 구체적인 예를 들자면, 회원 가입에 쓰이는 POST /account와 회원 열람에 쓰이는 GET /account/(id)가 있다고 하자. 이들은 사실상 같은 자원에 대한 다른 접근이기 때문에 URL에 따라 뷰를 바꾸기보다는 메서드로 먼저 걸러내고 (id)를 뜯어내는 쪽이 훨씬 적절하다. GET /account에서 어떤 오류를 돌려줘야 할지를 생각해 보면 명확하다. 그렇다고 r'^account$'와 r'^account/(?P<_id>\d+)$'로 나눌 수 있는 뷰를 r'^account(?:/(?P<_id>\d+))?$'로 하나의 뷰에 넣어야 하는가. [본문으로]