백엔드 프레임워크 순위 2022 - baeg-endeu peuleim-wokeu sun-wi 2022

파이썬 백엔드에 대한 상당히 주관적인 견해를 쓴 거라 반박 시, 님 말이 다 맞음

개발자 취업을 위한 파이썬 백엔드 프레임워크 코스(원티드 프리온보딩 2차 백엔드 코스)를 참여해서 5주 정도 진행했다. 프레임워크는 주로 DJango를 사용했는데, 이전 부터 DJango 사용법은 알고 있었지만, 그래봐야 야매로 배운 탓에 어떻게 코딩을 해야 하는 지 몰랐고, 이 프로그램을 통해 간신히 제대로 배울 수 있었다. 그리고 여러 기술 스택에 대한 내 개인적인 생각이 일부 바뀌게 되었는데, 이 중 가장 크게 바뀐 분야갸 파이썬 백엔드 프레임워크의 선택이다.


파이썬 백엔드 프레임워크의 선택

자금까지 나는 MicroService는 Flask/FastAPI가, 조금 규모가 큰 분야부터는 DJango를 쓰는게 디폴트(물론 특수한 경우는 다를 수 있겠다.)라고 생각했지만. 이거 이후로 내 생각은 완전히 뒤집어졌다.

DJango

위에서도 서술했듯이 나는 DJango를 제대로 배운 지 한 달도 안되서 깊이는 모르겠지만, 내가 이 프로그램을 통해 DJango에 대해 느낀건 아래와 같다.

DJango를 이용한 개발은 마치 Template Method Pattern과 유사하다.

Template Method Pattern은 Design Pattern의 일종으로 알고리즘이나 프로세스의 구조를 변경하지 않고 필요한 부분만 추가하는 기법이다. 이 기법은 OOP의 DIP(의존관계 역전 원칙)와 연계가 된다.

DJango는 startproject 명령어를 이용해 DJango 개발을 위한 파이썬 파일들(아키텍처)을 생성한다. 그리고 파일들 위에서만 규칙에 맞게 코딩만 하면 된다. 

굳이 DJango가 아니어도 Java의 Spring, 프론트엔드 Javascript의 ReactJS 뿐만 아니라 데스크탑 프레임워크인 .NET Framework나 QT도 똑같은 특징 아니냐 라고 의문을 가질 수 있겠지만, 내가 지금까지 겪어온 프레임워크들 중에 얘가 정도가 제일 심했다.

대표적인 예로 GenericView와 Mixin을 들 수 있는데 APIView의 하위 클래스로 View의 역할을 할 뿐만 아니라 CRUD 프로세스까지 지가 다해먹는다! 그러니까, CRUD를 구현하라고 하면 따로 로직을 구현할 필요 없이 GenericView상속받고 View만든 다음에 사용할 메소드만 대충 선언하면 끝난다. 예외처리는 Model과 Serializer에서만 잘 구현해 주면 된다. 이렇게

class RestaurantsView(generics.GenericAPIView,
                    mixins.RetrieveModelMixin,
                    mixins.UpdateModelMixin,
                    mixins.DestroyModelMixin):

    lookup_field = 'id'
    serializer_class = RestaurantSerializer
    queryset = Restaurant.objects.all()

    def get(self, request, id):
        return self.retrieve(request, id)
    
    def patch(self, request, id):
        return self.update(request, id, partial=True)

    def delete(self, request, id):
        return self.destroy(request, id)

lookup_field는 특정 DB의 Record 하나를 검색할 때 사용하는 컬럼이다. 주로 primary key이거나 unique key가 된다. 대충 이렇게만 해주면 CRUD는 알아서 작동한다.

이런거 말고도 사용자(Admin, Client)나, 보안 인증과 관련된 기능들도 제공한다. 근데 Admin 기능 까진 써봤는데 인증/로그인은 안써봐서 거기까지 쓰는 건 힘들듯 하다. 정확히는 내가 잘 몰라서 실패한거다.

RDB 상호작용에 최적화 되어 있다.

DB의 테이블을 정의할때 Model을 사용한다. 근데 이건 어느 프레임워크나 다 있는 거고, DJango는 좀더 나아가서 Serializer까지 제공한다. Flask에서는 꿈도 못 꿀 일이다. Model쓰려면  SQLAlchemy를 추가적으로 설치해야 하고 게다가 SQLAlchemy는 Serializer를 제공 안하기 때문에 pydantic을 또 설치해야 한다.

GenericView, Mixin은 이 Serializer를 활용해서 최대한 코드를 간결하게 해준다.

이러한 특징들 때문에 RDB와 상호작용을 하는 서버를 만든다면 DJango를 사용하는게 적합하다고 생각했다.

APP단위로 나눠서 개발한다

DJango 기능의 최소단위는 APP이다 모든 View들은 APP 안에서 작동한다. APP을 사용해서 기능에 따라 독립적으로 따로 모듈을 만드는 전략을 세울 수 있기 때문에, 다른 프로젝트에서도 재사용해서 활용이 가능하다.

여기까지가

DJango의 장점들을 나열했다. RDB에 최적화 되어 있는 Architecture, 모듈화, 그리고 각종 기능을 위한 각종 클래스 및 탬플릿 들은 개발자로부터 하여금 가능한 코드를 간결하고 명료하게 작성하는데 지대한 도움을 준다. 코드가 명확하고 간결할 수록 다른 동료 또는 제 3자가 이 코드를 볼 때 프로세스를 금방 파악할 수 있다. 개발자 채용에서 이를 중요하게 보는 이유도 여기에 있다. 그렇기에 DJango는 이러한 부분에 대해서 상당한 효과를 낼 수 있다. 괜히 DJango가 Flask보다 점유율이 높은 게 아니다. 그러나 항상 장점만 있는 건 아니다. 얘네들은 오히려 양날의 검이다.

확장성, 유연성의 한계?

원티드 프리온보딩 백엔드 코스 마지막 주차 (개인과제)에서 확장성 까지 고려해서 과제를 진행한 적이 있었다.

GitHub - Vector-7/wanted-2nd-wanted: 확장성고려+인증 체계+객체지향 설계가 추가된 원티드 프리온보딩 2

확장성고려+인증 체계+객체지향 설계가 추가된 원티드 프리온보딩 2차-5주차 개인과제. Contribute to Vector-7/wanted-2nd-wanted development by creating an account on GitHub.

github.com

백엔드 프레임워크 순위 2022 - baeg-endeu peuleim-wokeu sun-wi 2022

기존 과제에서는 단순 CRUD에 연관검색어 기능 구현만 있었다면, 거기에 JWT인증, 이메일 인증(패스워드 입력 또는 패스워드 재설정)을 추가했고, 인증 토큰을 활용해 API요청 시 API 요청 자격에 맞는지 검토하는 기능 까지 개발했다.

단순 CRUD, 혹은 오직 DB와 상호작용하는 기능이였다면 빠르게 GenericView를 사용해 해결했겠지만. 차후 기능 추가 때도 고려를 해야 하고 CRUD를 수행하기 전에 전처리 작업을 해야 했다. 내가 DJango를 깊게 알고 있었다면 좋았지만. 당장 이러한 복합적인 로직들은 GenericView같은 곳에서 바로 간결하게 해결할 수 없기 때문에

아키텍처를 따로 설계를 하고 DJango 기능은 Model, Serializer, Cache(Redis)만 사용했다.

내가 앱을 구현할 때 썼던 DJango 모듈은 Model, Serializer, Cache 뿐이었고, 나머지는 일절 사용하지 않았다. 인증 기능은 DJango에서 자동으로 제공해 주지만, Permisson Level(Client, Company, Admin)까지 고려를 해야 했기 때문에, User Model을 생성하는 AbstractUserModel 빼고는 인증 관련 모듈을 아예 사용하지 않았다. 결국 오히려 사용하지 않는 모듈이 더 많은 것이다. 사용하지 않는 모듈이 많아지면 앱의 용량이 커지는 것은 물론, 앱 부팅 또는 작동 과정에, 불필요한 모듈 로딩이 생기면 성능에 지장이 생길 수 있다.

그리고 앱 실행 과정도 커스텀 하기가 힘들었다. 어플리케이션을 실행할 때 어떤 특수 작업을 거쳐야 한다고 가정할 때, Flask는 맨 처음 실행 부분이 app.py이기 때문에 여기서 내 맘대로 커스텀이 가능하지만, DJango는 쉽지가 않다. 특히, 앱 부팅 전 DB 쿼리 검색이 필요한 경우가 있었는데, DJango에서 이걸 해결하는 방법은 아직도 찾지 못하는 중이다.

결론

결국 DJango는 규모가 큰 프로젝트에는 오히려 안맞을 수 있다는 것이 내가 이 프로그램을 통해 내린 결론이다. 물론 User Login, JWT 인증 등, 큰 프로젝트를 진행하는데 도움이 될 만한 모듈들도 제공은 하나, 규모가 큰 프로젝트를 진행할 경우, 상황에 따라 일부 기능들을 직접 하드코딩(또는 커스터마이징)을 해야 할 때가 있다. 그렇게 되면 DJango에서 기본적으로 제공하는 모듈들은 일절 사용하지 않게 된다. 이런거 하려면 아마 DJango를 거의 밑바닥 까지 아는 수준이어야 하는데 나는 아직 걸음마 수준이라 거기까지 못간다. 그러므로 DJango는 극소, 극대 프로젝트가 아닌 적당한 규모에 DB CRUD가 주를 이루는 어플리케이션에는 맞다는 것이 내 생각이다.

Flask

반면에 Flask는 뭘 따로 주는 게 없다. 아무것도 없다. 솔직히 이 포스트에서 DJango에 비해서 쓸 거리가 없다. Flask의 강점이자 약점은 딱 하나, 유연성이다. 얘도 DJango의 특성 똑같이 양날의 검인데 날은 Flask쪽이 더 서있다. 즉 아키텍쳐 설계에 대한 도박성은 Flask가 더 높다는 뜻이다.

유연성의 강점은 아시다시피 자유도다. Flask는 어플리케이션이 돌아갈 수 있을 정도의 필수적이고 최소한의 모듈만 제공한다. 그렇기 때문에 필요한 것들만 외부에서 받아서 사용하므로 사용하지 않는 모듈은 사실상 없다고 보면 된다.

대신 얘는 모든 책임은 프로그래머에게 돌린다.  마치 C언어와 파이썬의 관계와 비슷하다고 보면 된다. 파이썬에는 GC(Garbage Collector)가 있기 때문에 메모리 관리를 굳이 프로그래머가 직접 하드코딩 하지 않아도 알아서 해준다. 그렇기 때문에 프로그래머가 헛발질을 하지 않는 이상 내부에 메모리 문제가 생긴다면 이건 파이썬 내부의 버그다. 하지만 C언어는 그런거 없다. 포인터로 메모리를 직접 관리해야 하는 데 이건 프로그래머의 몫이 되고 메모리에 문제가 생기면 이건 C언어 자체의 버그가 아니라 프로그래머가 실수한 거다.

Flask와 DJango의 관계도 마찬가지다. DB에 임의의 쿼리를 날려 데이터를 검색한다고 가정해 보자. DJango의 경우 DJango에 내장되어 있는 DB 모듈은 DB Connection을 담당하는 Instance를 알아서 관리해준다. 하지만 Flask의 SQLAlchemy는 DB와 상호작용하려면 DB Instance를 직접 호출해야 하는데, 이때 많은 트래픽이 왔다갔다할 경우 DB를 연결하는 Instance가 수백 수천개로 늘어날 수 있다. 이렇게 되면 메모리 관리에 문제가 생기게 된다. DJango는 메모리 관리를 직접 해주지만, Flask에서는 이런 작업을 프로그래머가 직접해야 한다. 따라서 여기에 문제가 생기면 일차적으로 프로그래머의 책임이 가장 크다.

나같은 경우, DB Connection을 생성할 때, 똑같은 Instance를 두번 이상 생성하지 않게 Singletone Pattern을 적용하거나, DB Connection의 갯수를 제한하고, 해당 Instance를 요청하면 할당하거나 없으면 대기하는 방식인 Memory Pool 기법을 활용해서 메모리를 관리하는 편이다. 후자의 방법은 보통 사용자 접속을 제한할 때 사용하고 나머지 경우는 전부 전자의 경우를 활용한다.

FastAPI의 등판

최근에 나는 FastAPI를 공부하고 있다. 이제 Flask는 사용하지 않으려고 한다. wsgi가 아닌 asgi를 기본스탯으로 사용하기 때문에 속도가 상당히 빠르다고 한다. 이것도 아직 막 시작한 단계라 아것말고 자세한 정보는 모르고 나중에 지금 진행하는 프로젝트가 완성이 되면(8 ~9월 사이) 한꺼번에 정리해서 블로그에 올릴 예정이다.

더이상 개인프로젝트에 DJango를 쓰지 않는 이유

나는 작년에 진행했던 프로젝트인 Microcloudchip-NATURAL 이후로 더이상 파이썬+웹서비스 형태의 개인 프로젝트에서는 더이상 DJango를 쓰지 않는다. 이유는 위에 설명했듯이 확장성의 문제가 가장 크다. 나는 프로젝트들을 단순히 구현만 하는 것이 아니라 유지보수까지 하는 것을 목표로 하기 때문에 DJango로는 부적합하다는 생각이다. 


마치며

그렇다고 DJango가 안좋다는 뜻은 아니다. Flask(FastAPI)와 DJango의 성능은 비슷하다고 알려저 있고, 나는 상황에 따라 두게 중에 하나를 선택해서 사용하고 있는 것 뿐이다.

특수한 경우가 아닌 이상 DJango를 사용하는 일은 없을 것같다. 나중에 웹 서비스 하나 시작해서 MicroService를 하나 구현할 계획이 있다고 하면 그땐 쓸 지도 모르겠지.

사실상 정 반대라고 보면 될 것같다. 일단 규모가 큰 경우는 확장성과 유연성이 높은 Flask/FastAPI 가 낫고 MicroService인 경우, DB와 상호작용을 자주한다면 DJango, 아니면 (혹은 더 가벼운 서버일 경우) Flask/FastAPI를 쓰는게 나을 거라 생각한다.

근데 사실 애초에 프로그래밍 언어나 프레임워크 자체를 서열을 정하는거 자체가 멍청한 일이다, 언어나 프레임워크들은 어디까지나 내가 어느 목표를 이루기 위한 도구일 뿐이다. 누가 좋네 마네 서열정리할 시간에 어느 도구가 나한테 맞는지 그리고 내가 진행하고 있는 프로젝트에 적합한지 그거에 대해 비교하는게 낫다.