원맨 밴드 개발 백엔드 스택 공유해요
현재 개발중인 사진공유 앱 '슈티'의 백엔드 스택을 공유합니다.
간단히 요약 하면, GraphQL + Prisma + Node.js(TypeScript + Fastify + Mercurius) + Pulumi 인데요.
...간단했나요? 이것저것 많지만 마라탕 토핑 넣듯이 만든 조합은 아닙니다. 각각의 선택이 긴밀히 연결되어 있거든요.
혼자서 다 해먹을수 있도록 하는것에 초점을 맞추어 구성했습니다. 이제 막 제품 개발을 시작하려는 메이커분들께 도움이 되었으면 좋겠습니다.
지금부터 하나씩 설명드릴게요.
원맨밴드 개발을 한다는건, 프론트엔드도 제가 해야한다는 뜻이겠죠?
그런데 프론트엔드 개발자 입장에서는 GraphQL 만한게 없습니다. REST는 비교하기도 민망할 정도고, trpc 등도 부분적으로 나은 점이 있을지 몰라도, GraphQL + Relay의 조합만큼 데이터 페칭을 우아하게 다루진 못하는걸로 압니다.
'슈티'의 프론트엔드는 React Native로 개발되고 있기 때문에, GraphQL 클라이언트로 Relay를 사용할 수 있었습니다
사실 GraphQL을 아주 자-알 쓰지 못하더라도요, GraphQL 생태계의 좋은 툴들이 스키마 관리를 즐거운 일로 만들어 주는 것만으로도 충분한 가치가 있습니다.
GraphQL을 쓰기로 결정한 순간, 나머지 스택들의 선택지가 크게 좁혀집니다.
일단 GraphQL 서버를 잘 만들기가 쉽지가 않아요. 백엔드 개발자들이 괜히 투덜대는게 아닙니다.
프로토타이핑 단계에선 Hasura가 눈에 들어왔습니다. Hasura는 RDBMS 스키마로부터 GraphQL 서버를 공짜로 만들어주는 SaaS 서비스입니다. PostgreSQL 서버를 띄우고 클릭 몇번만 하면 GraphQL API 서버가 생성됩니다.
하지만 이런류의 솔루션이 늘 그렇듯, 솔루션이 제공하는 기능을 벗어나면 문제가 발생합니다.
가령 이미지가 포함된 포스트 업로드를 구현할때, 이미지 업로드를 수행하는 서버를 별도로 만들고 Hasura 서버와 연동해야 합니다. 일종의 MSA가 강제되는 셈인데, 제가 당장 MSA를 잘해낼 자신이 없어서 다른 선택지를 찾게 되었습니다.
그래도 Hasura는 여전히 고려해볼만한 솔루션이니, 한번 살펴보시는걸 추천드립니다.
Prisma는 일종의 ORM인데요. 전통적인 ORM과 달리 n+1 문제에 대한 해결책인 Data Loader를 내장하고 있습니다.
이 Data Loader가 Prisma로 효율적인 GraphQL 서버를 개발하는것을 가능케합니다. 반대로 말해서, Data Loader가 없는 ORM을 사용해 GraphQL 서버를 개발하는 것은 매우 험난한 일이 됩니다.
RDBMS 데이터베이스를 소스로 하는 GraphQL 서버 개발에서 Prisma는 사실상 유일한 선택지라고 생각합니다.
여기서 또 Prisma가 어떤 언어를 써야할지에 대한 고민을 고맙게도(?) 크게 줄여줍니다.
Prisma 클라이언트가 지원하는 런타임이 현재 Node.js와 Go 밖에 없거든요.
바로 전직장까지 쓰던 손에 익은 TypeScript를 쓰기 위해 Node.js를 선택했습니다. 제가 Go를 잘 모르긴하지만 사실 무슨 언어를 쓰는지는 크게 상관은 없을 것으로 예상하는데요, 이유는 바로 밑에서 설명드리겠습니다.
Node.js엔 Express, Koa, NestJS, Fastify 등 다양한 웹 프레임워크가 있습니다.
저는 이중에 Fastify를 골랐습니다. 거기에 Fastify의 GraphQL 플러그인 Mercurius를 얹었죠.
그런데, 사실 뭘 고르건 개발엔 큰 상관이 없습니다! GraphQL 서버에선 웹 프레임워크의 역할이 크게 줄어들거든요.
보통 GraphQL 스키마를 바탕으로 코드를 생성하고, 거기에 빈칸를 채워넣는 식으로 개발을 진행합니다. 그런데 그 빈칸이 웹 프레임워크와 무관하게 똑같이 생겼습니다. 그래서 무슨 프레임워크를 고르건 짜게되는 코드는 대동소이할겁니다.
제가 Fastify + Mercurius를 고른건 취향의 문제에 가깝고요. 백엔드 개발 경험이 짧은 분께는 오히려 자료가 많은 NestJS를 추천합니다.
Pulumi는 Terraform, CDK와 같은 IaC 프로비저닝 툴입니다. 직접 EC2 인스턴스를 올렸다 내렸다 하지 않고, 필요한 자원을 코드로 기술하는 것만으로 인프라를 관리할 수 있습니다.
즉 현재 인프라에 어떤 변경을 가할 것인가의 문제에서 인프라가 어떤 상태가 되어야 하는가의 문제로 바뀌는 것이죠. 그럼 이제 어떤 변경을 가할지는 Pulumi 등의 툴이 알아서 계산하고 적용해줍니다.
처음에 배워야할게 좀 있긴 하지만, 작은 규모의 서비스라도 IaC를 구성해놓고 개발하는게 시간 낭비를 크게 줄여준다고 생각합니다
이 중 Terraform은 HCL이라는 설정 언어로 요구사항을 기술하는데 반해, Pulumi와 CDK는 TypeScript, Python 등의 프로그래밍 언어를 사용해서 같은 일을 합니다.
이는 Pulumi나 CDK를 쓸 경우엔 인프라 배포 단계에 다른 액션들을 자유롭게 넣을 수 있단걸 의미하는데요.
저는 CI/CD 파이프라인을 따로 작성하지 않고, 그냥 Pulumi 스크립트에서 Docker 이미지 빌드까지 포함시켰습니다. 그래서 배포는 pulumi up 이라는 CLI 명령어 하나로 끝납니다.
얼핏 무식한 방법처럼 보이지만, 캐시를 이용해 빌드를 효율적으로 수행하게만 한다면(아직 여기까진 안 했습니다) 오히려 기존 방법보다도 낫다고 생각합니다.
지금까지 말씀드린게 사실 RedwoodJS와 크게 다르지 않습니다.
RedwoodJS는 GitHub의 공동창업자 중 한명인 Tom Preston-Werner가 공개한 NestJS, Prisma, React로 구성된 풀스택 프레임워크인데요.
스타트업들이 최대한 고민없이 빠르게 개발을 시작할 수 있도록 설계되었습니다.
RedwoodJS로 서비스를 개발하면 투자 유치의 기회가 생긴다는 것도 매력적입니다.
어쩌다보니 RedwoodJS 광고가 되었는데요.
RedwoodJS를 선택하게 될 경우에도, 스택을 이루는 요소들이 어떤 목적으로 선택되었는지에 대해 이해하는데 이 메이커로그가 도움이 될것이라 생각합니다.