API 서버 개발 중의 고민


Youngrok Pak at 3 years, 2 months ago.

외주로 위치정보를 많이 쓰는 앱의 API 서버를 제작 중이다. 이와 관련해서 요즘 하고 있는 설계 고민들을 적어본다.

데이터베이스 선택#

위치정보를 많이 쓴다면 2차원 인덱스를 통해 KNN Query 등의 위치정보 관련 쿼리를 쉽게 할 수 있는 MongoDB가 편하다. 그러니까 MongoDB 하나만 쓰고 RDBMS는 쓰지 말까? 아니면 그냥 Django 모델은 그대로 쓰고, MongoDB는 위치정보 인덱스로만 쓸까? Django 모델의 편리함을 그냥 버리긴 아깝다. 아니면 OpenGIS를 지원하는 PostgreSQL을 쓰면 다 해결되려나? 

고민 끝에 일단 MongoDB를 전면적으로 쓰기로 결정했다. 이미 채팅촌에서 써본 경험이 있었는데 괜찮았고, PostGIS는 사용하기 그리 편하지 않았다. MongoDB를 위치정보 인덱스로만 쓰고 Django 모델을 주로 쓰는 접근법도 해봤는데, 나름 괜찮긴 하지만 조금 더 MongoDB를 깊이 써보고 싶었다.

 

모델 레이어#

MongoDB를 쓰기로 했으니 파이썬에서 어떻게 사용할지를 결정해야 했다. 크게 방법은 세 가지. pymongo를 그대로 쓰는 것, MongoKit, MongoEngine. 셋 다 나쁜 방법은 아니지만, 아무래도 MVC에서 모델 중심으로 가려면 pymongo를 그대로 쓰긴 좀 그렇고 뭔가 wrapping을 하긴 해야 한다. MongoEngine은 저번에 써봤는데, Django model을 닮은 게 그다지 맘에 들진 않았다. MongoDB로 계속 개발을 하려면 아무래도 MongoDB의 쿼리 문법에 익숙해져야 하므로 그걸 그대로 쓰는 게 유익하다. MongoEngine이 Django랑 억지로나마 연동이 되긴 하지만, Admin 연동이 제대로 안되는 이상 별로 큰 이득도 아니다. 그래서 MongoEngine은 시도도 안해보고 MongoKit으로 넘어왔다. 근데 MongoKit도 생각보다 불편하다. 가장 불편한 건 collection을 wrapping해서 관리해주지 않고 들고 다녀야 한다는 것이다. MongoKit에서의 document 생성은 다음처럼 database와 collection을 가져온 다음 거기서 모델 객체를 생성해야 한다.

connection.database.collection.MyDocument()

왜 MyDocument()를 바로 쓰는 걸 지원하지 않는지 이해할 수 없다. database와 collection을 미리 지정해두면 이 둘은 생략할 수 있지만, 그래도 connection에 붙여서 써야 한다. 쿼리도 마찬가지다. 별 거 아닐지 모르지만 그냥 이게 맘에 안 들어서 버리기로 했다. 그래서 그냥 예전에 내가 간단하게 만들어 둔 MongoDB model wrapper를 계속 쓰기로 했다. 이름도 붙였다. MongoDB Model을 줄여서 momo.

 

스키마 문서화#

외주다보니 스키마 문서화가 필요하다. Django 모델로 작성했으면 그냥 django-extensions의 graph_models 써서 다이어그램 넘겨버리면 그만이겠지만, momo는 그런 거 없다. 그리고 MongoDB는 schemaless가 기본이다보니 스키마를 정의하는 일이 부가 작업이 된다. 여기서 고민에 빠졌다. 따로 코드로 스키마를 정의할 필요가 없으니 그냥 수동으로 다이어그램만 그려서 관리할까, 아니면 그래도 스키마가 코드에 정의되는 게 명시성도 좋고 나중에 validation 붙이기도 좋으니 코드에 정의하고 다이어그램은 자동으로 뽑아낼까. 

별도 문서화보다는 아무래도 코드에 정의를 하고 싶다. 코드에 정의를 하면 나도 개발을 하면서 계속 참조할 수 있어 이득을 얻기 때문이다. 사실 클라이언트에서 꼭 다이어그램까지 원하는 것은 아니기 때문에 스키마 정의한 문법을 그대로 보내줘도 될 것 같긴 하다. 다만 조금 세련된 문법이면서, 나중에 programmable하게 처리할 수 있는 구조이기만 하면 될 듯.

스키마를 코드에서 정의한다면 MongoKit을 베낄까, MongoEngine을 베낄까도 고민이다. MongoKit이 간결하고 시각적이지만, MongoEngine은 여러 가지 속성을 줄 수 있어서 확장성이 좋다. 문법 정의할 때 파이썬이 인터프리터 언어라는 점도 은근히 귀찮다. 클래스 정의할 때 자신을 참조하게 만들 수가 없기 때문이다. method 안에서는 참조할 수 있지만 클래스 레벨로 정의할 때는 참조할 수가 없어서 Django 모델도 문자열이나 'Self' 같은 문법을 쓴다. 아직 이 부분은 고민 중.

 

REST API 프레임워크#

일단 django를 쓰긴 할 텐데, REST API는 뭘로 만들까? 이전까지는 그냥 내가 만든 djangox-route를 써서 라우팅만 처리하고, JSONResponse 같은 걸 또 붙여서 대충 썼다. PUT, DELETE 따윈 지원하지 않지만 내 프로젝트라면 아마 또 그렇게 할 듯. 하지만 외주다보니 좀더 제대로(?) 만들어야 하고, 또 좀더 널리 알려진 프레임워크를 쓰는 게 좋을 듯 하다. 그래서 django-rest-framework와 tastypie를 비교하기 시작했다.

둘다 예제가 너무 Django model 연동 중심으로 되어 있다. 하지만 난 Django 모델을 안 쓰기로 한지라, 별로 도움이 안된다. 모델을 바꿔 끼우려고 해보니 tastypie는 상속해야 하는 메서드들이 좀 이상하다. get_object_list와 obj_get_list라는 어이 없는 네이밍을 해놨다. 확장하려고 문서를 찾아보니 문서도 좀 부실하다. 그래서 잠깐 django-rest-framework 문서를 더 보는데, 일단 문서가 좀더 풍부하고, browsable api가 있어서 이게 더 편할 것 같았다. View와 Serializer를 API마다 세트로 작성해야하는 것처럼 보여서 조금 꺼려지긴 했지만, 아마도 Serializer는 어떻게든 하나로 할 수 있는 방법이 있겠지. 그래서 일단 django-rest-framework로 왔다. 생각보다 쉽게 연동이 되서 Browserable API에서 JSON으로 POST하고 조회하고 하는 건 잘 된다. 일단 API 작성까지는 이게 괜찮은 것 같다.

그런데 또 하나 중요한 건 API 문서화다. 요즘 REST API는 executable documentation이 대세가 되어가고 있고, django-rest-framework의 Browsable API도 그걸 지원한다. 그런데, 이게 좀 제한적이다. 문서화 기능도 적고, list에 GET 요청을 할 때 파라미터를 줄 수 있는 UI는 없는 것 같아보인다. POST야 그냥 JSON으로만 해도 되서 별 상관은 없지만 필터링 파라미터는 필요한데 아직 어떻게 하는지 모르겠다.

그래서 documenting-your-api라는 문서를 봤더니 자체 Browsable API의 문서화보다 오히려 django-rest-swagger를 추천한다. 이건 swagger integration인데, 이것도 생각보다 쉽게 붙었다. 그런데, 클래스 레벨의 docstring만 파라미터 인식을 하고, 메서드 레벨로는 안되는 것 같다. 한글도 문제가 있는지 에러를 낸다. 하지만, 일단 UI 자체에는 필요한 기능이 다 담겨 있는 것 같다. 일단은 이걸로 좀더 파볼 듯. 처음 원했던 것처럼 손 안대고 코 푸는 건 아직 안되는 것 같다.

 

블로그 / 소프트웨어 개발


Comments




Wiki at WikiNamu